diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml index 6f3e8ebc523..486af2b239f 100644 --- a/.github/workflows/pr.yml +++ b/.github/workflows/pr.yml @@ -1110,7 +1110,6 @@ jobs: xvfb-run --auto-servernum pnpm exec vitest run --config config/vitest.config.ts --no-file-parallelism src/main/browser/client-pages/renderer/browser-client-page-renderer-lifecycle.electron.test.ts - src/main/browser/network/egress/browser-route-webrtc-egress.electron.test.ts src/main/persistence/profile-state/profile-state-writer-stall.electron.test.ts src/main/browser/cookies/browser-google-cookie-clear.electron.test.ts src/main/browser/guest-lifecycle/popups/popup-origin-chrome.electron.test.ts diff --git a/config/flaky-test-pattern-baseline.txt b/config/flaky-test-pattern-baseline.txt index c082c8341ec..0820dacad4e 100644 --- a/config/flaky-test-pattern-baseline.txt +++ b/config/flaky-test-pattern-baseline.txt @@ -620,7 +620,6 @@ 1 test-timeout-needs-reason:src/main/browser/cookies/browser-cookie-import-validated-partition.electron.test.ts 1 test-timeout-needs-reason:src/main/browser/guest-lifecycle/find/browser-find-frame-traversal.electron.test.ts 1 test-timeout-needs-reason:src/main/browser/identity/browser-session-ua-cloudflare-live.electron.test.ts -1 test-timeout-needs-reason:src/main/browser/network/egress/browser-route-webrtc-egress.electron.test.ts 1 test-timeout-needs-reason:src/main/claude/claude-background-run-isolation-real-cli.test.ts 1 test-timeout-needs-reason:src/main/claude/claude-child-exit-proof-ladder.test.ts 1 test-timeout-needs-reason:src/main/claude/claude-stop-ack-race.test.ts @@ -1316,7 +1315,6 @@ 4 test-no-wall-clock:src/main/updater/lifecycle/serve-supervisor-install-park.test.ts 4 test-no-wall-clock:src/renderer/src/components/cmd-j/palette-live-status.test.tsx 4 test-no-wall-clock:src/renderer/src/components/sidebar/workspace-card/surface/WorktreeCard.compact-hover.test.tsx -4 test-no-wall-clock:src/renderer/src/components/status-bar/provider-segment-balance.test.tsx 4 test-no-wall-clock:src/renderer/src/components/task-page/jira/task-page-jira-cache-selectors.test.ts 4 test-no-wall-clock:tests/e2e/cross-version-wire/release-checkout.unit.test.ts 4 test-timeout-needs-reason:src/main/claude/claude-agent-sdk-exit-proof.test.ts diff --git a/config/reliability-gates.jsonc b/config/reliability-gates.jsonc index e80f79aeaf0..2814ab17bf0 100644 --- a/config/reliability-gates.jsonc +++ b/config/reliability-gates.jsonc @@ -1660,7 +1660,7 @@ "providers": ["local", "remote-runtime"], "coveredPlatforms": ["linux"], "coveredProviders": [], - "coverageNotes": "Production SQLite and reducer tests on macOS. Execution-host-local storage behavior is shared by remote runtimes; no live SSH or Windows/Linux run. PTY, daemon, WSL process launch, transport framing and mobile rendering are unaffected. Test-value audit1293 removes src/main/native-chat/agent-session-wire/agent-session-history-forward-read-budget.test.ts (1 authored cases). Their isolated assertions no longer run; surviving tests are not claimed as equivalent coverage. Fresh execution against the candidate SHA is required. Audit1302: src/main/native-chat/agent-session-journal/journal-streaming-replay.test.ts: Keep actual SQLite replay classification of future versions, first sequence damage and missing epoch against independent literal state/damage expectations. Remove GC/heap thresholds and private reducer-call counters plus same-connection PASSIVE checkpoint busy checks, which do not demonstrate an independently blocked checkpoint. Lose assembled replay memory/snapshot 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. Audit1345: focused Linux unit execution now validates this primary selector on source head de234fc7b4a73eda58dd0084bc9e064b5d1191e0. Historical platform statements above describe earlier receipts. Removed fixture/matrix/call-count alarms are intentional losses with no equivalent replacement; no live SSH, physical-provider, p95 or cross-platform claim. Audit1371 removes synthetic receipt callback choreography and single/batch timestamp comparison from journal-row-writer.spec.ts. Actual SQLite duplicate-insert rollback/reuse and batch atomicity callbacks remain unchanged. The old directory-selected receipt is historical and does not validate this reduced selector. No equivalent replacement claim. Audit1372: current Linux unit selection passed on source head 9b07e8f1bea37bba70c78df2f9a238ea31d1be7e. Exact selectors and execution command are recorded in the new receipt. Intentional test losses remain in knownGaps; no replacement, live provider, native input, rendered E2E, performance threshold or soak coverage is claimed. Audit1379 changes src/main/native-chat/agent-session-journal/journal-atomic-settlement-oversized.spec.ts (1 authored registrations removed; 1 retained); src/main/native-chat/agent-session-journal/queued-message-edit-leases.test.ts (10 authored registrations removed; 1 retained); src/main/native-chat/agent-session-journal/journal-subagent-item.spec.ts (4 authored registrations removed; 1 retained). Intentional losses: Loses production batch-cap/cursor-count fallback expectations; retained case checks real database rollback and successful retry, with once-per-startup variants. Intentionally removes 10 authored registrations: 'a repeated acquire answers the same lease without extending it'; 'acquire refuses a card that is gone or whose text moved on'; 'reuses an edit key only after its previous target becomes invalid'; 'expires on its own and wakes the queue with no other activity'; 'renewal extends the deadline; a lapsed lease is not revived'; 'release frees only the exact caller and edit, never another editor on the same card'; 'every read re-derives: a card whose %s holds nothing'; 'a save retires every lease on the card; others stay'; 'reads the rows it is given inside a transaction'; 'dispose drops every lease and deadline; a closed handle grants none'. Retained focused cases are not claimed as equivalent replacement for these losses. Loses identity/schema/constant/field-bound and generic revision/turnScope persistence controls; retained recovery checks structured and legacy text twins. No equivalent replacement coverage is claimed. Existing exact primary-selector receipts and primary claims remain scoped to their unchanged sources; the broad optional selector has no new execution result. Audit1390: src/main/native-chat/agent-session-journal/journal-submission-positions.spec.ts reduced. Standalone history-page submitted-sequence projection alarm removed. 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. Audit1392: src/main/native-chat/agent-session-journal/journal-payload-bounds-retention.test.ts reduced. Retires hand-copied UTF-8 clipping implementation comparison loops and deterministic random differential replay. 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.", + "coverageNotes": "Production SQLite and reducer tests on macOS. Execution-host-local storage behavior is shared by remote runtimes; no live SSH or Windows/Linux run. PTY, daemon, WSL process launch, transport framing and mobile rendering are unaffected. Test-value audit1293 removes src/main/native-chat/agent-session-wire/agent-session-history-forward-read-budget.test.ts (1 authored cases). Their isolated assertions no longer run; surviving tests are not claimed as equivalent coverage. Fresh execution against the candidate SHA is required. Audit1302: src/main/native-chat/agent-session-journal/journal-streaming-replay.test.ts: Keep actual SQLite replay classification of future versions, first sequence damage and missing epoch against independent literal state/damage expectations. Remove GC/heap thresholds and private reducer-call counters plus same-connection PASSIVE checkpoint busy checks, which do not demonstrate an independently blocked checkpoint. Lose assembled replay memory/snapshot 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. Audit1345: focused Linux unit execution now validates this primary selector on source head de234fc7b4a73eda58dd0084bc9e064b5d1191e0. Historical platform statements above describe earlier receipts. Removed fixture/matrix/call-count alarms are intentional losses with no equivalent replacement; no live SSH, physical-provider, p95 or cross-platform claim. Audit1371 removes synthetic receipt callback choreography and single/batch timestamp comparison from journal-row-writer.spec.ts. Actual SQLite duplicate-insert rollback/reuse and batch atomicity callbacks remain unchanged. The old directory-selected receipt is historical and does not validate this reduced selector. No equivalent replacement claim. Audit1372: current Linux unit selection passed on source head 9b07e8f1bea37bba70c78df2f9a238ea31d1be7e. Exact selectors and execution command are recorded in the new receipt. Intentional test losses remain in knownGaps; no replacement, live provider, native input, rendered E2E, performance threshold or soak coverage is claimed. Audit1379 changes src/main/native-chat/agent-session-journal/journal-atomic-settlement-oversized.spec.ts (1 authored registrations removed; 1 retained); src/main/native-chat/agent-session-journal/queued-message-edit-leases.test.ts (10 authored registrations removed; 1 retained); src/main/native-chat/agent-session-journal/journal-subagent-item.spec.ts (4 authored registrations removed; 1 retained). Intentional losses: Loses production batch-cap/cursor-count fallback expectations; retained case checks real database rollback and successful retry, with once-per-startup variants. Intentionally removes 10 authored registrations: 'a repeated acquire answers the same lease without extending it'; 'acquire refuses a card that is gone or whose text moved on'; 'reuses an edit key only after its previous target becomes invalid'; 'expires on its own and wakes the queue with no other activity'; 'renewal extends the deadline; a lapsed lease is not revived'; 'release frees only the exact caller and edit, never another editor on the same card'; 'every read re-derives: a card whose %s holds nothing'; 'a save retires every lease on the card; others stay'; 'reads the rows it is given inside a transaction'; 'dispose drops every lease and deadline; a closed handle grants none'. Retained focused cases are not claimed as equivalent replacement for these losses. Loses identity/schema/constant/field-bound and generic revision/turnScope persistence controls; retained recovery checks structured and legacy text twins. No equivalent replacement coverage is claimed. Existing exact primary-selector receipts and primary claims remain scoped to their unchanged sources; the broad optional selector has no new execution result. Audit1390: src/main/native-chat/agent-session-journal/journal-submission-positions.spec.ts reduced. Standalone history-page submitted-sequence projection alarm removed. 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. Audit1392: src/main/native-chat/agent-session-journal/journal-payload-bounds-retention.test.ts reduced. Retires hand-copied UTF-8 clipping implementation comparison loops and deterministic random differential replay. 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/main/native-chat/agent-session-journal/command-receipt-schema.spec.ts reduced. Fresh receipt schema physical layout no longer independently enumerated. src/main/native-chat/agent-session-journal/journal-unsent-send-hold-queue.spec.ts reduced. Source-present versus source-absent folded property shape loses one separate assertion. 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.", "motivatingLinks": [ "https://github.com/stablyai/orca/blob/main/src/main/native-chat/agent-session-journal/journal-open.ts" ], @@ -1740,7 +1740,8 @@ "Historical receipt retired by audit1372 at 1f0e6b60b1cb31fc3aebaf573da5957297c8c613; original record unchanged, not current validation:\n{\n \"date\": \"2026-11-04\",\n \"runner\": \"local\",\n \"platform\": \"linux\",\n \"result\": \"passed\",\n \"command\": \"ORCA_BACKGROUND_LAUNCH=1 pnpm --config.verifyDepsBeforeRun=false test src/main/native-chat/agent-session-journal src/main/native-chat/agent-session-wire/agent-session-history-forward-read-budget.test.ts src/main/native-chat/agent-session-wire/agent-session-history-page.test.ts src/main/native-chat/agent-session-wire/structured-agent-session-subscribers.test.ts --maxWorkers=2\",\n \"currentCommand\": \"ORCA_BACKGROUND_LAUNCH=1 pnpm test src/main/native-chat/agent-session-journal src/main/native-chat/agent-session-wire/agent-session-history-page.test.ts src/main/native-chat/agent-session-wire/agent-session-history-forward-read-budget.test.ts src/main/native-chat/agent-session-wire/structured-agent-session-subscribers.test.ts\",\n \"durationSeconds\": 53.43,\n \"summary\": \"Test Files 50 passed (50); Tests 717 passed (717). One shared execution of the same selector set validates both journal gates. Focused Linux unit execution; no p95, physical-provider or cross-platform claim. Source head de234fc7b4a73eda58dd0084bc9e064b5d1191e0.\"\n }", "Audit1379 changes src/main/native-chat/agent-session-journal/journal-atomic-settlement-oversized.spec.ts (1 authored registrations removed; 1 retained); src/main/native-chat/agent-session-journal/queued-message-edit-leases.test.ts (10 authored registrations removed; 1 retained); src/main/native-chat/agent-session-journal/journal-subagent-item.spec.ts (4 authored registrations removed; 1 retained). Intentional losses: Loses production batch-cap/cursor-count fallback expectations; retained case checks real database rollback and successful retry, with once-per-startup variants. Intentionally removes 10 authored registrations: 'a repeated acquire answers the same lease without extending it'; 'acquire refuses a card that is gone or whose text moved on'; 'reuses an edit key only after its previous target becomes invalid'; 'expires on its own and wakes the queue with no other activity'; 'renewal extends the deadline; a lapsed lease is not revived'; 'release frees only the exact caller and edit, never another editor on the same card'; 'every read re-derives: a card whose %s holds nothing'; 'a save retires every lease on the card; others stay'; 'reads the rows it is given inside a transaction'; 'dispose drops every lease and deadline; a closed handle grants none'. Retained focused cases are not claimed as equivalent replacement for these losses. Loses identity/schema/constant/field-bound and generic revision/turnScope persistence controls; retained recovery checks structured and legacy text twins. No equivalent replacement coverage is claimed. Existing exact primary-selector receipts remain unchanged.", "Audit1390: src/main/native-chat/agent-session-journal/journal-submission-positions.spec.ts reduced. Standalone history-page submitted-sequence projection alarm removed. No equivalent replacement coverage is claimed. Existing exact primary-selector receipts remain unchanged.", - "Audit1392: src/main/native-chat/agent-session-journal/journal-payload-bounds-retention.test.ts reduced. Retires hand-copied UTF-8 clipping implementation comparison loops and deterministic random differential replay. No equivalent replacement coverage is claimed. Existing exact primary-selector receipts remain unchanged." + "Audit1392: src/main/native-chat/agent-session-journal/journal-payload-bounds-retention.test.ts reduced. Retires hand-copied UTF-8 clipping implementation comparison loops and deterministic random differential replay. No equivalent replacement coverage is claimed. Existing exact primary-selector receipts remain unchanged.", + "Audit1396: src/main/native-chat/agent-session-journal/command-receipt-schema.spec.ts reduced. Fresh receipt schema physical layout no longer independently enumerated. src/main/native-chat/agent-session-journal/journal-unsent-send-hold-queue.spec.ts reduced. Source-present versus source-absent folded property shape loses one separate assertion. No equivalent replacement coverage is claimed. Existing exact primary-selector receipts remain unchanged." ], "promotionCriteria": ["Retain red/green heap and value assertions and complete CI soak."], "demotionRule": "Keep experimental until cross-platform and soak evidence; investigate failures without weakening content or memory assertions." @@ -2961,7 +2962,7 @@ "providers": ["local", "daemon", "wsl", "ssh", "remote-runtime"], "coveredPlatforms": ["linux"], "coveredProviders": [], - "coverageNotes": "Audit1391 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): 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): macOS daemon-backed Electron journey verifies same PID and terminal identity plus rendered output. Focused renderer contracts pass on Linux, Windows and WSL. Neighboring SSH model and replay contracts pass locally; no new live SSH or paired-runtime journey. Audit1342: src/renderer/src/components/terminal-pane/pty-connection-unanswered-command-finish.spec.ts removed. Unanswered SSH shell finish keeps/drops mocked agent row matrix disappears. src/renderer/src/components/terminal-pane/pty-connection-split-cwd-resolution.test.ts reduced. Happy inherited-cwd wait and transport-option forwarding sample loses its individual alarm. src/renderer/src/components/terminal-pane/pty-connection-command-input-agent-identity.spec.ts removed. Typed Droid versus process/launch identity and SSH/runtime process-evidence matrices disappear. src/renderer/src/components/terminal-pane/pty-connection/reattach-success-settle.test.ts removed. Success/no-ID/expired/superseded attach-settlement bag alarms disappear. src/renderer/src/components/terminal-pane/pty-transport-user-input.test.ts reduced. Every-write kind forwarding and synchronous driving-attempt observer/rejection/ACK call counts disappear. src/renderer/src/components/terminal-pane/pty-connection-codex-restart-input-lookup.spec.ts removed. No global tab enumeration on ordinary/stale input and known restart notice input-block mock-call alarms. src/renderer/src/components/terminal-pane/pty-connection-layout-owner-wake.test.ts removed. Hidden-to-visible single-pane start request, attach-only argument and negative local wake-record alarms are intentionally dropped; no real server wake/ack occurs. 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. src/renderer/src/components/terminal-pane/pty-connection-command-finished-cleanup.spec.ts reduced. Driver registration/import of a whole-deleted selected spec is removed; directly authored cases remain unchanged. No extra authored removal credit. src/renderer/src/components/terminal-pane/pty-connection-terminal-input-gating.spec.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. Audit1361: src/renderer/src/components/terminal-pane/pty-connection/foreground-output-scan.test.ts reduced. Internal searched-character budget and ordinary/malformed no-op controls. 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. Audit1369: src/renderer/src/components/terminal-pane/pty-connection/disposed-spawn-retention.test.ts removed. Rootless/removed/moved tabs, missing leaves, queued deletion and host-qualified/legacy deletion eligibility fixtures. No equivalent replacement coverage is claimed. 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.", + "coverageNotes": "Audit1391 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): 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): macOS daemon-backed Electron journey verifies same PID and terminal identity plus rendered output. Focused renderer contracts pass on Linux, Windows and WSL. Neighboring SSH model and replay contracts pass locally; no new live SSH or paired-runtime journey. Audit1342: src/renderer/src/components/terminal-pane/pty-connection-unanswered-command-finish.spec.ts removed. Unanswered SSH shell finish keeps/drops mocked agent row matrix disappears. src/renderer/src/components/terminal-pane/pty-connection-split-cwd-resolution.test.ts reduced. Happy inherited-cwd wait and transport-option forwarding sample loses its individual alarm. src/renderer/src/components/terminal-pane/pty-connection-command-input-agent-identity.spec.ts removed. Typed Droid versus process/launch identity and SSH/runtime process-evidence matrices disappear. src/renderer/src/components/terminal-pane/pty-connection/reattach-success-settle.test.ts removed. Success/no-ID/expired/superseded attach-settlement bag alarms disappear. src/renderer/src/components/terminal-pane/pty-transport-user-input.test.ts reduced. Every-write kind forwarding and synchronous driving-attempt observer/rejection/ACK call counts disappear. src/renderer/src/components/terminal-pane/pty-connection-codex-restart-input-lookup.spec.ts removed. No global tab enumeration on ordinary/stale input and known restart notice input-block mock-call alarms. src/renderer/src/components/terminal-pane/pty-connection-layout-owner-wake.test.ts removed. Hidden-to-visible single-pane start request, attach-only argument and negative local wake-record alarms are intentionally dropped; no real server wake/ack occurs. 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. src/renderer/src/components/terminal-pane/pty-connection-command-finished-cleanup.spec.ts reduced. Driver registration/import of a whole-deleted selected spec is removed; directly authored cases remain unchanged. No extra authored removal credit. src/renderer/src/components/terminal-pane/pty-connection-terminal-input-gating.spec.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. Audit1361: src/renderer/src/components/terminal-pane/pty-connection/foreground-output-scan.test.ts reduced. Internal searched-character budget and ordinary/malformed no-op controls. 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. Audit1369: src/renderer/src/components/terminal-pane/pty-connection/disposed-spawn-retention.test.ts removed. Rootless/removed/moved tabs, missing leaves, queued deletion and host-qualified/legacy deletion eligibility fixtures. No equivalent replacement coverage is claimed. 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. Audit1396: src/renderer/src/components/terminal-pane/pty-connection-pending-spawn.spec.ts reduced. Lose standalone debug-state retention/log silence alarm. React StrictMode is not mounted; bindings manually simulate remount over mocked transport. 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.", "motivatingLinks": [ "https://github.com/user-attachments/assets/e8c6d1dc-6150-4c3d-b55a-3d12efefdd04", "https://github.com/user-attachments/assets/b0328f88-34ac-4d51-8119-9efe17072435" @@ -3039,7 +3040,8 @@ "Audit1369: src/renderer/src/components/terminal-pane/pty-connection/disposed-spawn-retention.test.ts removed. Rootless/removed/moved tabs, missing leaves, queued deletion and host-qualified/legacy deletion eligibility fixtures. No equivalent replacement coverage is claimed.", "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.", "Audit1391 retires this complete dated receipt because a source in its selector was deleted or reduced. It does not validate the reduced selector. Original receipt verbatim:\n{\n \"date\": \"2026-09-04\",\n \"runner\": \"local\",\n \"platform\": \"macos\",\n \"command\": \"pnpm test src/renderer/src/components/terminal-pane/pty-transport-connect-spawn.test.ts src/renderer/src/components/terminal-pane/pty-connection-deferred-reattach-live-output.test.ts src/renderer/src/components/terminal-pane/pty-connection-hidden-snapshot-live-overlap.test.ts src/renderer/src/components/terminal-pane/pty-connection-replay-payload-handling.test.ts\",\n \"result\": \"passed\",\n \"durationSeconds\": 2.34,\n \"summary\": \"5 suites / 48 tests pass. Focused 2-suite runs independently pass 27 tests on Linux, Windows and WSL.\",\n \"currentCommand\": \"pnpm test src/renderer/src/components/terminal-pane/pty-transport-connect-spawn.test.ts src/renderer/src/components/terminal-pane/pty-connection-hidden-replay.test.ts src/renderer/src/components/terminal-pane/pty-connection-visibility-and-exit.test.ts\"\n }", - "Audit1391 historical platform/provider scope before source changes: {\"coveredPlatforms\":[\"macos\",\"linux\"],\"coveredProviders\":[]}. Remaining provider scope applies only to unchanged-source receipts." + "Audit1391 historical platform/provider scope before source changes: {\"coveredPlatforms\":[\"macos\",\"linux\"],\"coveredProviders\":[]}. Remaining provider scope applies only to unchanged-source receipts.", + "Audit1396: src/renderer/src/components/terminal-pane/pty-connection-pending-spawn.spec.ts reduced. Lose standalone debug-state retention/log silence alarm. React StrictMode is not mounted; bindings manually simulate remount over mocked transport. No equivalent replacement coverage is claimed. Existing exact primary-selector receipts remain unchanged." ], "demotionRule": "Keep experimental or demote if adoption duplicates covered output, drops newer or unproven output, changes terminal ownership, or flakes without explanation." }, @@ -3151,14 +3153,14 @@ "platforms": ["macos", "linux", "windows"], "providers": ["local", "ssh", "remote-runtime"], "coveredPlatforms": ["macos"], - "coveredProviders": ["local", "ssh", "remote-runtime"], - "coverageNotes": "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): Deterministic candidate-builder contracts inject local and paired-runtime worktrees with the same bare id into the real aggregated tab buckets, including folder workspaces and a paired runtime transporting an SSH-owned worktree. A mirror contract preserves distinct same-id runtime tabs. A paired Electron journey verifies both visible browser rows and both visible simulator rows, then requires each selection to render its owning active tab and update backing host-qualified state. The test-value audit narrowed simulator-palette-search.test.ts to the recency tie, same-id host isolation, ambiguous legacy host rejection and oversized-input getter tripwire. Supplied timestamp, empty-query context ordering, host projection, alias/range, metadata matching, active-tab score, over-token/whitespace and malformed display-name fallback examples were removed. Their separate alarms are lost; retained candidate and activation contracts do not establish equivalent coverage. Historical run commands and results remain historical. The audit also removed the standalone workspace-tab-palette-search suite: supplied row, title, metadata, alias, range, ranking and ownership examples no longer provide their separate alarms. Other retained contracts are not replacement coverage. The mirror-identity fixture now retains same-id sibling-runtime isolation and the repeated-frame subscriber tripwire only. Supplied field, owner, alias and mapping examples are removed; their separate alarms are lost. The retained sibling-runtime case is not equivalent coverage for them. The test-value audit removed tabs-open-close-lifecycle host-stamping examples and ordinary tab/group operations from tabs-pane-layout-operations. Local-null, explicit-runtime and SSH-without-active-host stamp assertions are lost; retained mobile/MRU and subscriber/atomic split cases do not replace them. Candidate and live Cmd-J evidence retains its own scope. Historical commands, outcomes and narratives remain historical. Audit1308: src/renderer/src/lib/browser-palette-search.test.ts removed. Browser-palette token and pasted-query size-guard work alarms disappear. No equivalent replacement coverage is claimed. Audit1386: src/renderer/src/lib/open-mobile-emulator-tab.test.ts reduced. Duplicate attach count and null-tab creation retry/guard reset standalone alarms removed; no simulator device used. 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.", + "coveredProviders": [], + "coverageNotes": "Audit1396 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): 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): Deterministic candidate-builder contracts inject local and paired-runtime worktrees with the same bare id into the real aggregated tab buckets, including folder workspaces and a paired runtime transporting an SSH-owned worktree. A mirror contract preserves distinct same-id runtime tabs. A paired Electron journey verifies both visible browser rows and both visible simulator rows, then requires each selection to render its owning active tab and update backing host-qualified state. The test-value audit narrowed simulator-palette-search.test.ts to the recency tie, same-id host isolation, ambiguous legacy host rejection and oversized-input getter tripwire. Supplied timestamp, empty-query context ordering, host projection, alias/range, metadata matching, active-tab score, over-token/whitespace and malformed display-name fallback examples were removed. Their separate alarms are lost; retained candidate and activation contracts do not establish equivalent coverage. Historical run commands and results remain historical. The audit also removed the standalone workspace-tab-palette-search suite: supplied row, title, metadata, alias, range, ranking and ownership examples no longer provide their separate alarms. Other retained contracts are not replacement coverage. The mirror-identity fixture now retains same-id sibling-runtime isolation and the repeated-frame subscriber tripwire only. Supplied field, owner, alias and mapping examples are removed; their separate alarms are lost. The retained sibling-runtime case is not equivalent coverage for them. The test-value audit removed tabs-open-close-lifecycle host-stamping examples and ordinary tab/group operations from tabs-pane-layout-operations. Local-null, explicit-runtime and SSH-without-active-host stamp assertions are lost; retained mobile/MRU and subscriber/atomic split cases do not replace them. Candidate and live Cmd-J evidence retains its own scope. Historical commands, outcomes and narratives remain historical. Audit1308: src/renderer/src/lib/browser-palette-search.test.ts removed. Browser-palette token and pasted-query size-guard work alarms disappear. No equivalent replacement coverage is claimed. Audit1386: src/renderer/src/lib/open-mobile-emulator-tab.test.ts reduced. Duplicate attach count and null-tab creation retry/guard reset standalone alarms removed; no simulator device used. 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. Audit1396: src/renderer/src/lib/simulator-palette-search.test.ts reduced. Lose simulator-specific lastFocusedAt tie-order alarm; identity/routing and pre-scan size fence remain. No native simulator or real remote launch claim. No equivalent replacement coverage is claimed.", "motivatingLinks": [ "https://linear.app/stably/issue/STA-4965", "https://github.com/stablyai/orca/pull/15371" ], "invariant": "Candidate identity is the tuple of execution-host identity and worktree identity: same-id sibling hosts retain separate correct browser, simulator, and workspace tabs, each tab appears exactly once, and selection activates its owning host.", - "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 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): Aggregate two hosts' tabs under one intentionally colliding worktree id, stamp each unified tab with its execution host, and require exactly one candidate per owner. Repeat with an SSH physical owner transported by a paired-runtime alias. Independently require each browser and simulator Cmd-J selection to update activeWorkspaceExecutionHostId plus its owning workspace/group and render the selected tab active. Ambiguous legacy tabs without ownership fail closed.", + "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): 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): Aggregate two hosts' tabs under one intentionally colliding worktree id, stamp each unified tab with its execution host, and require exactly one candidate per owner. Repeat with an SSH physical owner transported by a paired-runtime alias. Independently require each browser and simulator Cmd-J selection to update activeWorkspaceExecutionHostId plus its owning workspace/group and render the selected tab active. Ambiguous legacy tabs without ownership fail closed.", "commands": [ "pnpm exec vitest run --config config/vitest.config.ts src/renderer/src/components/cmd-j/worktree-checks-review-index.test.ts", "pnpm exec vitest run --config config/vitest.config.ts src/renderer/src/components/worktree-jump/WorktreeJumpPalette.test.tsx", @@ -3222,12 +3224,6 @@ "same-id local and remote browser tabs appear once under their owning hosts" ] }, - { - "file": "src/renderer/src/lib/simulator-palette-search.test.ts", - "assertions": [ - "same-id local and remote simulator tabs appear once under their owning hosts" - ] - }, { "file": "tests/e2e/paired-cmd-j-host-qualified-tabs.spec.ts", "assertions": [ @@ -3251,16 +3247,6 @@ "summary": "The first review-round oracle failed 4/40: browser and simulator dropped the SSH worktree's paired-runtime-owned tab, generic workspace rows attributed the remote tab to local, and active local tab creation left ownership unstamped. Historical command and result are preserved after the test-value audit removed the tab-store assertions this run included. currentCommand is only an administrative link to the remaining gate command; the historical run has not validated that command or equivalent coverage.", "currentCommand": "pnpm exec vitest run --config config/vitest.config.ts src/renderer/src/lib/cmd-j-host-qualified-candidate-ownership.test.ts" }, - { - "date": "2026-08-20", - "runner": "local", - "platform": "macos", - "command": "pnpm exec vitest run --config config/vitest.config.ts src/renderer/src/lib/browser-palette-page-entries.test.ts src/renderer/src/lib/simulator-palette-search.test.ts", - "result": "failed", - "durationSeconds": 0.5, - "summary": "The byte-identical oracle on affected baseline 012e9f410c failed 4 tests and passed 24: each same-id browser and simulator tab was routed through both hosts, and ambiguous unstamped rows were duplicated instead of failing closed.", - "currentCommand": "pnpm exec vitest run --config config/vitest.config.ts src/renderer/src/lib/browser-palette-page-entries.test.ts src/renderer/src/lib/simulator-palette-search.test.ts" - }, { "date": "2026-08-20", "runner": "local", @@ -3281,16 +3267,6 @@ "summary": "A fresh electron-vite --mode e2e build passed the paired journey: the visible Cmd-J list retained one owning browser/simulator row and each selection changed the execution host plus its owning workspace or group.", "currentCommand": "pnpm exec playwright test tests/e2e/paired-cmd-j-host-qualified-tabs.spec.ts --config tests/playwright.config.ts --project electron-headless --workers=1" }, - { - "date": "2026-08-20", - "runner": "local", - "platform": "macos", - "command": "pnpm exec vitest run --config config/vitest.config.ts src/renderer/src/lib/browser-palette-page-entries.test.ts src/renderer/src/lib/simulator-palette-search.test.ts", - "result": "failed", - "durationSeconds": 0.5, - "summary": "The same oracle on fe45b73c20 with only browser-palette-page-entries.ts and simulator-palette-search.ts restored from origin/main failed the same 4 tests and passed 24; restored candidate files passed all 28.", - "currentCommand": "pnpm exec vitest run --config config/vitest.config.ts src/renderer/src/lib/browser-palette-page-entries.test.ts src/renderer/src/lib/simulator-palette-search.test.ts" - }, { "date": "2026-08-20", "runner": "local", @@ -3370,16 +3346,6 @@ "durationSeconds": 20.6, "summary": "After a fresh build exposed an unrelated mirrored terminal entering the broad backing-state diagnostic, the exact built candidate passed with that oracle scoped to the four seeded browser/simulator tabs; rendered active-tab and backing execution-host assertions remained green.", "currentCommand": "SKIP_BUILD=1 pnpm exec playwright test tests/e2e/paired-cmd-j-host-qualified-tabs.spec.ts --config tests/playwright.config.ts --project electron-headless --workers=1" - }, - { - "date": "2026-08-20", - "runner": "local", - "platform": "macos", - "command": "pnpm exec vitest run --config config/vitest.config.ts src/renderer/src/lib/browser-palette-page-entries.test.ts src/renderer/src/lib/simulator-palette-search.test.ts src/renderer/src/lib/ensure-simulator-tab-behavior.test.ts src/renderer/src/store/slices/tabs-open-close-lifecycle.test.ts src/renderer/src/store/slices/tabs-pane-layout-operations.test.ts src/shared/workspace-session-schema.test.ts", - "result": "passed", - "durationSeconds": 2.3, - "summary": "6 test files passed, 104 tests passed on the STA-4965 candidate. Historical command and result are preserved after the test-value audit removed the tab-store assertions this run included. currentCommand is only an administrative link to the remaining gate command; the historical run has not validated that command or equivalent coverage.", - "currentCommand": "pnpm exec vitest run --config config/vitest.config.ts src/renderer/src/lib/browser-palette-page-entries.test.ts src/renderer/src/lib/simulator-palette-search.test.ts src/renderer/src/lib/ensure-simulator-tab-behavior.test.ts src/shared/workspace-session/workspace-session-schema.test.ts" } ], "runtimeBudget": { @@ -3388,15 +3354,15 @@ }, "flakeHistory": { "status": "not-started", - "evidence": "Changed selectors have no new soak result after Audit1308; any retained unchanged-source receipts apply only to their own commands. Historical account: The candidate contract has no timers. Initial Electron attempts exposed seeded-worktree discovery and programmatic keyboard-selection harness races before the ownership assertions; soak history is not yet available." + "evidence": "Changed selectors have no new soak result after Audit1396; any retained unchanged-source receipts apply only to their own commands. Historical account: Changed selectors have no new soak result after Audit1308; any retained unchanged-source receipts apply only to their own commands. Historical account: The candidate contract has no timers. Initial Electron attempts exposed seeded-worktree discovery and programmatic keyboard-selection harness races before the ownership assertions; soak history is not yet available." }, "redGreenEvidence": { "status": "missing", - "evidence": "Audit1308 changed referenced test sources; no new red/green validation of the reduced set. Historical account: Byte-identical browser and simulator candidate tests failed 4/28 on affected baseline 012e9f410c, passed 28/28 on candidate fe45b73c20, failed the same 4/28 when only the two candidate filters were disabled, and returned to 28/28 after restoration. Review-round topology contracts failed 4/40 before the alias/generic/local-stamping follow-up; final-review contracts then failed 4/16 before folder/background/worktree-metadata hardening, 1/6 before paired repository-metadata hardening, 1/7 before unavailable-runtime fail-closed hardening, 1/12 with the component's physical-host bypass restored, 1/6 before review-cache ownership used the same resolver, 1/6 before the downstream document rejected a remote owner's unqualified legacy cache entry, and 1/6 when a host filter or scoped open-tab search hid the same-id sibling from legacy ambiguity detection. Manual local-provider ownership failed 1/12 before explicit host routing, and pre-hydration dedupe failed 1/13 before exact stamped-owner reuse; the combined contracts then passed 25/25 and the six-file review suite passed 73/73. The prior completed focused suite passed 225/225. Historical results include examples subsequently removed in the test-value audit; they do not establish current coverage for those removed assertions." + "evidence": "Audit1396 changed referenced test sources; no new red/green validation of the reduced set. Historical account: Audit1308 changed referenced test sources; no new red/green validation of the reduced set. Historical account: Byte-identical browser and simulator candidate tests failed 4/28 on affected baseline 012e9f410c, passed 28/28 on candidate fe45b73c20, failed the same 4/28 when only the two candidate filters were disabled, and returned to 28/28 after restoration. Review-round topology contracts failed 4/40 before the alias/generic/local-stamping follow-up; final-review contracts then failed 4/16 before folder/background/worktree-metadata hardening, 1/6 before paired repository-metadata hardening, 1/7 before unavailable-runtime fail-closed hardening, 1/12 with the component's physical-host bypass restored, 1/6 before review-cache ownership used the same resolver, 1/6 before the downstream document rejected a remote owner's unqualified legacy cache entry, and 1/6 when a host filter or scoped open-tab search hid the same-id sibling from legacy ambiguity detection. Manual local-provider ownership failed 1/12 before explicit host routing, and pre-hydration dedupe failed 1/13 before exact stamped-owner reuse; the combined contracts then passed 25/25 and the six-file review suite passed 73/73. The prior completed focused suite passed 225/225. Historical results include examples subsequently removed in the test-value audit; they do not establish current coverage for those removed assertions." }, "performanceBudget": { "required": true, - "evidence": "Historical account for the pre-audit test set; removed work-count alarms and changed selectors have no fresh measurement. Candidate construction adds bounded in-memory ownership filtering over the already-read worktree tab bucket and introduces no polling or network work." + "evidence": "Historical account for the pre-audit test set; removed work-count alarms and changed selectors have no fresh measurement. Historical account for the pre-audit test set; removed work-count alarms and changed selectors have no fresh measurement. Candidate construction adds bounded in-memory ownership filtering over the already-read worktree tab bucket and introduces no polling or network work." }, "promotionCriteria": [ "Collect paired Electron CI soak history without unexplained flakes.", @@ -3416,7 +3382,13 @@ "Audit1308 retires this complete dated receipt because a source in its selector was deleted or reduced. It does not validate the reduced selector. Original receipt verbatim:\n{\n \"date\": \"2026-08-21\",\n \"runner\": \"local\",\n \"platform\": \"macos\",\n \"command\": \"pnpm exec vitest run --config config/vitest.config.ts src/renderer/src/components/cmd-j/worktree-checks-review-index.test.ts src/renderer/src/components/WorktreeJumpPalette.test.tsx src/renderer/src/components/tab-bar/use-open-tab-search.test.ts src/renderer/src/lib/cmd-j-host-qualified-candidate-ownership.test.ts src/renderer/src/lib/palette-repo-resolution.test.ts src/renderer/src/lib/browser-palette-page-entries.test.ts src/renderer/src/lib/browser-palette-search.test.ts src/renderer/src/lib/simulator-palette-search.test.ts src/renderer/src/lib/workspace-tab-palette-search.test.ts src/renderer/src/lib/browser-page-palette-activation.test.ts src/renderer/src/lib/simulator-tab-palette-activation.test.ts src/renderer/src/lib/ensure-simulator-tab-behavior.test.ts src/renderer/src/runtime/web-session-tabs-sync-mirror-identity.test.ts src/renderer/src/store/slices/tabs-open-close-lifecycle.test.ts src/renderer/src/store/slices/tabs-pane-layout-operations.test.ts src/shared/workspace-session-schema.test.ts\",\n \"currentCommand\": \"pnpm exec vitest run --config config/vitest.config.ts src/renderer/src/components/cmd-j/worktree-checks-review-index.test.ts src/renderer/src/components/worktree-jump/WorktreeJumpPalette.test.tsx src/renderer/src/components/tab-bar/use-open-tab-search.test.ts src/renderer/src/lib/cmd-j-host-qualified-candidate-ownership.test.ts src/renderer/src/lib/browser-palette-page-entries.test.ts src/renderer/src/lib/browser-palette-search.test.ts src/renderer/src/lib/simulator-palette-search.test.ts src/renderer/src/lib/browser-page-palette-activation.test.ts src/renderer/src/lib/simulator-tab-palette-activation.test.ts src/renderer/src/lib/ensure-simulator-tab-behavior.test.ts src/renderer/src/runtime/web-session-tabs-sync-mirror-identity.test.ts src/shared/workspace-session-schema.test.ts\",\n \"result\": \"passed\",\n \"durationSeconds\": 24.3,\n \"summary\": \"The OpenCode-review-hardened candidate passed 225/225 focused contracts, including unfiltered ownership ambiguity under host filters and scoped open-tab search, review-cache ownership, rendered palette repo metadata, folder workspaces, inactive local simulator creation, paired SSH/runtime display metadata, and explicit-owner fail-closed resolution. Historical command preserved after the test-value audit removed the palette repo resolver suite. currentCommand is an administrative link to a remaining gate command; this historical run has not validated that command or equivalent coverage. Historical command and result are preserved after the test-value audit removed the tab-store assertions this run included. currentCommand is only an administrative link to the remaining gate command; the historical run has not validated that command or equivalent coverage.\"\n }", "Retired during audit1312 current-main refresh; original complete execution receipt (unchanged): {\"date\":\"2026-08-20\",\"runner\":\"local\",\"platform\":\"macos\",\"command\":\"pnpm exec vitest run --config config/vitest.config.ts src/renderer/src/lib/cmd-j-host-qualified-candidate-ownership.test.ts src/renderer/src/lib/palette-repo-resolution.test.ts src/renderer/src/lib/browser-palette-page-entries.test.ts src/renderer/src/lib/browser-palette-search.test.ts src/renderer/src/lib/simulator-palette-search.test.ts src/renderer/src/lib/workspace-tab-palette-search.test.ts src/renderer/src/lib/browser-page-palette-activation.test.ts src/renderer/src/lib/simulator-tab-palette-activation.test.ts src/renderer/src/lib/ensure-simulator-tab-behavior.test.ts src/renderer/src/runtime/web-session-tabs-sync-mirror-identity.test.ts src/renderer/src/store/slices/tabs-open-close-lifecycle.test.ts src/renderer/src/store/slices/tabs-pane-layout-operations.test.ts src/shared/workspace-session-schema.test.ts\",\"result\":\"passed\",\"durationSeconds\":10.5,\"summary\":\"The review-hardened candidate passed 189/189 focused contracts, including direct browser-result host fallback, SSH plus paired-runtime current-row aliases, explicit-owner fail-closed behavior, generic workspace rows, simulator reuse, local creation stamping, activation, and same-id distinct-runtime mirror preservation. Historical command preserved after the test-value audit removed the palette repo resolver suite. currentCommand is an administrative link to a remaining gate command; this historical run has not validated that command or equivalent coverage. Historical command and result are preserved after the test-value audit removed the tab-store assertions this run included. currentCommand is only an administrative link to the remaining gate command; the historical run has not validated that command or equivalent coverage.\",\"currentCommand\":\"pnpm exec vitest run --config config/vitest.config.ts src/renderer/src/lib/cmd-j-host-qualified-candidate-ownership.test.ts src/renderer/src/lib/browser-palette-page-entries.test.ts src/renderer/src/lib/browser-palette-search.test.ts src/renderer/src/lib/simulator-palette-search.test.ts src/renderer/src/lib/browser-page-palette-activation.test.ts src/renderer/src/lib/simulator-tab-palette-activation.test.ts src/renderer/src/lib/ensure-simulator-tab-behavior.test.ts src/renderer/src/runtime/web-session-tabs-sync-mirror-identity.test.ts src/shared/workspace-session/workspace-session-schema.test.ts\"}", "Retired during audit1312 current-main refresh; original complete execution receipt (unchanged): {\"date\":\"2026-08-21\",\"runner\":\"local\",\"platform\":\"macos\",\"command\":\"pnpm exec vitest run --config config/vitest.config.ts src/renderer/src/components/cmd-j/worktree-checks-review-index.test.ts src/renderer/src/components/WorktreeJumpPalette.test.tsx src/renderer/src/components/tab-bar/use-open-tab-search.test.ts src/renderer/src/lib/cmd-j-host-qualified-candidate-ownership.test.ts src/renderer/src/lib/palette-repo-resolution.test.ts src/renderer/src/lib/browser-palette-page-entries.test.ts src/renderer/src/lib/browser-palette-search.test.ts src/renderer/src/lib/simulator-palette-search.test.ts src/renderer/src/lib/workspace-tab-palette-search.test.ts src/renderer/src/lib/browser-page-palette-activation.test.ts src/renderer/src/lib/simulator-tab-palette-activation.test.ts src/renderer/src/lib/ensure-simulator-tab-behavior.test.ts src/renderer/src/runtime/web-session-tabs-sync-mirror-identity.test.ts src/renderer/src/store/slices/tabs-open-close-lifecycle.test.ts src/renderer/src/store/slices/tabs-pane-layout-operations.test.ts src/shared/workspace-session-schema.test.ts\",\"currentCommand\":\"pnpm exec vitest run --config config/vitest.config.ts src/renderer/src/components/cmd-j/worktree-checks-review-index.test.ts src/renderer/src/components/worktree-jump/WorktreeJumpPalette.test.tsx src/renderer/src/components/tab-bar/use-open-tab-search.test.ts src/renderer/src/lib/cmd-j-host-qualified-candidate-ownership.test.ts src/renderer/src/lib/browser-palette-page-entries.test.ts src/renderer/src/lib/browser-palette-search.test.ts src/renderer/src/lib/simulator-palette-search.test.ts src/renderer/src/lib/browser-page-palette-activation.test.ts src/renderer/src/lib/simulator-tab-palette-activation.test.ts src/renderer/src/lib/ensure-simulator-tab-behavior.test.ts src/renderer/src/runtime/web-session-tabs-sync-mirror-identity.test.ts src/shared/workspace-session/workspace-session-schema.test.ts\",\"result\":\"passed\",\"durationSeconds\":24.3,\"summary\":\"The OpenCode-review-hardened candidate passed 225/225 focused contracts, including unfiltered ownership ambiguity under host filters and scoped open-tab search, review-cache ownership, rendered palette repo metadata, folder workspaces, inactive local simulator creation, paired SSH/runtime display metadata, and explicit-owner fail-closed resolution. Historical command preserved after the test-value audit removed the palette repo resolver suite. currentCommand is an administrative link to a remaining gate command; this historical run has not validated that command or equivalent coverage. Historical command and result are preserved after the test-value audit removed the tab-store assertions this run included. currentCommand is only an administrative link to the remaining gate command; the historical run has not validated that command or equivalent coverage.\"}", - "Audit1386: src/renderer/src/lib/open-mobile-emulator-tab.test.ts reduced. Duplicate attach count and null-tab creation retry/guard reset standalone alarms removed; no simulator device used. No equivalent replacement coverage is claimed." + "Audit1386: src/renderer/src/lib/open-mobile-emulator-tab.test.ts reduced. Duplicate attach count and null-tab creation retry/guard reset standalone alarms removed; no simulator device used. No equivalent replacement coverage is claimed.", + "Audit1396: src/renderer/src/lib/simulator-palette-search.test.ts reduced. Lose simulator-specific lastFocusedAt tie-order alarm; identity/routing and pre-scan size fence remain. No native simulator or real remote launch claim. No equivalent replacement coverage is claimed.", + "Audit1396 retires this complete dated receipt because a source in its selector was deleted or reduced. It does not validate the reduced selector. Original receipt verbatim:\n{\n \"date\": \"2026-08-20\",\n \"runner\": \"local\",\n \"platform\": \"macos\",\n \"command\": \"pnpm exec vitest run --config config/vitest.config.ts src/renderer/src/lib/browser-palette-page-entries.test.ts src/renderer/src/lib/simulator-palette-search.test.ts\",\n \"result\": \"failed\",\n \"durationSeconds\": 0.5,\n \"summary\": \"The byte-identical oracle on affected baseline 012e9f410c failed 4 tests and passed 24: each same-id browser and simulator tab was routed through both hosts, and ambiguous unstamped rows were duplicated instead of failing closed.\",\n \"currentCommand\": \"pnpm exec vitest run --config config/vitest.config.ts src/renderer/src/lib/browser-palette-page-entries.test.ts src/renderer/src/lib/simulator-palette-search.test.ts\"\n }", + "Audit1396 retires this complete dated receipt because a source in its selector was deleted or reduced. It does not validate the reduced selector. Original receipt verbatim:\n{\n \"date\": \"2026-08-20\",\n \"runner\": \"local\",\n \"platform\": \"macos\",\n \"command\": \"pnpm exec vitest run --config config/vitest.config.ts src/renderer/src/lib/browser-palette-page-entries.test.ts src/renderer/src/lib/simulator-palette-search.test.ts\",\n \"result\": \"failed\",\n \"durationSeconds\": 0.5,\n \"summary\": \"The same oracle on fe45b73c20 with only browser-palette-page-entries.ts and simulator-palette-search.ts restored from origin/main failed the same 4 tests and passed 24; restored candidate files passed all 28.\",\n \"currentCommand\": \"pnpm exec vitest run --config config/vitest.config.ts src/renderer/src/lib/browser-palette-page-entries.test.ts src/renderer/src/lib/simulator-palette-search.test.ts\"\n }", + "Audit1396 retires this complete dated receipt because a source in its selector was deleted or reduced. It does not validate the reduced selector. Original receipt verbatim:\n{\n \"date\": \"2026-08-20\",\n \"runner\": \"local\",\n \"platform\": \"macos\",\n \"command\": \"pnpm exec vitest run --config config/vitest.config.ts src/renderer/src/lib/browser-palette-page-entries.test.ts src/renderer/src/lib/simulator-palette-search.test.ts src/renderer/src/lib/ensure-simulator-tab-behavior.test.ts src/renderer/src/store/slices/tabs-open-close-lifecycle.test.ts src/renderer/src/store/slices/tabs-pane-layout-operations.test.ts src/shared/workspace-session-schema.test.ts\",\n \"result\": \"passed\",\n \"durationSeconds\": 2.3,\n \"summary\": \"6 test files passed, 104 tests passed on the STA-4965 candidate. Historical command and result are preserved after the test-value audit removed the tab-store assertions this run included. currentCommand is only an administrative link to the remaining gate command; the historical run has not validated that command or equivalent coverage.\",\n \"currentCommand\": \"pnpm exec vitest run --config config/vitest.config.ts src/renderer/src/lib/browser-palette-page-entries.test.ts src/renderer/src/lib/simulator-palette-search.test.ts src/renderer/src/lib/ensure-simulator-tab-behavior.test.ts src/shared/workspace-session/workspace-session-schema.test.ts\"\n }", + "Audit1396 historical platform/provider scope before source changes: {\"coveredPlatforms\":[\"macos\"],\"coveredProviders\":[\"local\",\"ssh\",\"remote-runtime\"]}. Remaining provider scope applies only to unchanged-source receipts.", + "Audit1396 historical assertion inventory for deleted/reduced sources; not current validated assertions:\n[\n {\n \"file\": \"src/renderer/src/lib/simulator-palette-search.test.ts\",\n \"assertions\": [\n \"same-id local and remote simulator tabs appear once under their owning hosts\"\n ]\n }\n]" ], "demotionRule": "Cannot promote if a same-id sibling tab duplicates, disappears, activates another host, or if ambiguous unstamped ownership is guessed." }, @@ -7235,7 +7207,7 @@ "id": "browser-client-host.route-partition-policy", "title": "Client-hosted browser partitions apply route policy before attachment", "maturity": "experimental", - "protection": "partial", + "protection": "none", "owner": "browser-runtime", "layer": "electron-main-session-security-boundary", "surfaces": [ @@ -7246,109 +7218,35 @@ ], "platforms": ["macos", "linux", "windows"], "providers": ["remote-runtime", "ssh", "wsl"], - "coveredPlatforms": ["macos", "linux"], - "coveredProviders": ["ssh", "remote-runtime"], - "coverageNotes": "Deterministic main-process tests cover versioned delimiter-safe aggregate partition derivation, path-safe opaque names, durable collision metadata, oversized or corrupt metadata refusal, missing-sidecar Chromium-data refusal, bounded binding/live-page admission, immediate SOCKS5 setup with Chromium loopback bypass disabled, inherited-connection closure, exact proxy verification before allowlisting, concurrent setup coalescing, live proxy-retarget refusal, token-safe page replacement, existing browser-profile policy installation, active Orca-profile storage scoping, blank-only initial attachment, arbitrary initial-navigation denial, and fail-closed per-guest WebRTC policy through delayed or failed cleanup. The production client-page executor prepares, registers, and grants the exact route page. Archived standalone Electron experiments measured TCP routing and a forced persisted-worker wake using fixture-owned proxy setup; they did not exercise production route preparation and are no longer current test coverage. A separate capture proves the protected guest sends zero direct STUN packets. An archived standalone H3 experiment measured WebTransport and forced-QUIC controls through fixture-owned proxy setup and checked the shipped Direct Sockets feature list. It did not exercise Orca route preparation or admission and is no longer current product coverage. An archived DNS-prefetch experiment observed an accepted Electron 43 residual: prefetched hostnames resolved on the desktop outside the tunnel. It asserted the leak and was removed; current production-path TCP, persisted-worker, and DNS-prefetch egress coverage remains incomplete. Network-service restart and WSL provider journeys remain uncovered. Four Linux Docker SSH browser baseline scenarios passed: direct-host routing, unavailable-host local escape, forwarding refusal, and paired client-hosted reconnect. All four scenarios subsequently passed three repetitions each (12 passes, no skips or retries) in Linux CI run 34040309638, with unchanged assertions and timeouts.", + "coveredPlatforms": [], + "coveredProviders": [], + "coverageNotes": "Audit1396 intentionally deletes or reduces the listed sources. No fresh execution result validates their reduced selectors. Historical coverage account: Deterministic main-process tests cover versioned delimiter-safe aggregate partition derivation, path-safe opaque names, durable collision metadata, oversized or corrupt metadata refusal, missing-sidecar Chromium-data refusal, bounded binding/live-page admission, immediate SOCKS5 setup with Chromium loopback bypass disabled, inherited-connection closure, exact proxy verification before allowlisting, concurrent setup coalescing, live proxy-retarget refusal, token-safe page replacement, existing browser-profile policy installation, active Orca-profile storage scoping, blank-only initial attachment, arbitrary initial-navigation denial, and fail-closed per-guest WebRTC policy through delayed or failed cleanup. The production client-page executor prepares, registers, and grants the exact route page. Archived standalone Electron experiments measured TCP routing and a forced persisted-worker wake using fixture-owned proxy setup; they did not exercise production route preparation and are no longer current test coverage. A separate capture proves the protected guest sends zero direct STUN packets. An archived standalone H3 experiment measured WebTransport and forced-QUIC controls through fixture-owned proxy setup and checked the shipped Direct Sockets feature list. It did not exercise Orca route preparation or admission and is no longer current product coverage. An archived DNS-prefetch experiment observed an accepted Electron 43 residual: prefetched hostnames resolved on the desktop outside the tunnel. It asserted the leak and was removed; current production-path TCP, persisted-worker, and DNS-prefetch egress coverage remains incomplete. Network-service restart and WSL provider journeys remain uncovered. Four Linux Docker SSH browser baseline scenarios passed: direct-host routing, unavailable-host local escape, forwarding refusal, and paired client-hosted reconnect. All four scenarios subsequently passed three repetitions each (12 passes, no skips or retries) in Linux CI run 34040309638, with unchanged assertions and timeouts. Audit1396: src/main/browser/network/egress/browser-route-webrtc-egress.electron.test.ts removed. Deletes entire one-registration native Electron baseline/protected UDP probe, including fixture shell and packet observer. No equivalent actual network egress proof is claimed; actual Orca policy owner reviewed separately. No equivalent replacement coverage is claimed.", "motivatingLinks": [ "https://linear.app/stably/issue/STA-4150/refactor-remote-browser-to-client-hosted-electron-webviews", "https://github.com/stablyai/orca/actions/runs/34039986047", "https://github.com/stablyai/orca/actions/runs/34040309638" ], "invariant": "A client-hosted partition is derived only in main from stable Orca-profile, browser-profile, authority-connection, and execution-host identities. Raw identities and individually linkable component hashes never enter its path-safe partition name. Durable binding metadata must match and precede Chromium partition data before reuse. One live partition never changes execution host or proxy endpoint. Fixed SOCKS5 setup starts immediately after Session creation, before policy installation can yield or a persisted worker is awakened; no partition enters the webview allowlist until browser policy is installed, inherited connections are closed, and resolveProxy returns exactly that one listener. Initial route-partition attachment is blank-only. Its exact WebContents is quarantined before applying non-proxied WebRTC denial and remains navigation- and popup-denied if policy application or cleanup fails. Distinct live partitions, retained logical page generations, durable bindings, and binding-file reads remain bounded. No UDP transport a route-partition page can reach — WebRTC, WebTransport, or forced QUIC — emits a datagram to the desktop, the Direct Sockets constructors stay absent from every guest so no page can kill its renderer, and the process never enables a DoH host-resolver mode.", - "oracle": "Derive two delimiter-adversarial identities and require distinct full-digest path-safe partitions with no raw IDs or component hashes. Persist one binding, reload it, and reject replacement, malformed or oversized state, Chromium data without matching metadata, and the 513th binding. Prepare one partition and require setProxy with <-loopback> to be invoked immediately after getSession and before policy setup, then closeAllConnections and exact SOCKS5 resolveProxy while isAllowedPartition remains false; only then may it become live. Reject DIRECT, endpoint retargeting, and capacity overflow. Require quarantine before disable_non_proxied_udp and admission; under real Electron require the unprotected control to emit STUN and the protected guest to emit zero direct UDP packets.", - "commands": [ - "pnpm exec vitest run --config config/vitest.config.ts src/main/browser/routing/partitions/browser-route-identity.test.ts src/main/browser/routing/partitions/browser-route-partition-binding-store.test.ts src/main/browser/routing/sessions/browser-route-session-registry.test.ts src/main/browser/sessions/browser-session-registry.test.ts src/main/window/createMainWindow.test.ts", - "pnpm exec vitest run --config config/vitest.config.ts src/main/browser/routing/guest-authority/browser-route-webcontents-registry.test.ts src/main/browser/network/egress/browser-route-webrtc-egress.electron.test.ts" - ], - "testFiles": [ - "src/main/browser/routing/partitions/browser-route-identity.test.ts", - "src/main/browser/routing/partitions/browser-route-partition-binding-store.test.ts", - "src/main/browser/routing/sessions/browser-route-session-registry.test.ts", - "src/main/browser/network/egress/browser-route-webrtc-egress.electron.test.ts", - "src/main/browser/routing/guest-authority/browser-route-webcontents-registry.test.ts", - "src/main/browser/sessions/browser-session-registry.test.ts", - "src/main/window/createMainWindow.test.ts" - ], - "assertionRefs": [ - { - "file": "src/main/browser/routing/partitions/browser-route-identity.test.ts", - "assertions": [ - "aggregate partition and durable binding fingerprints are stable and opaque", - "delimiter-containing identities remain structurally distinct", - "individual component equality and Windows-illegal path separators are absent", - "empty and unbounded identity components are rejected" - ] - }, - { - "file": "src/main/browser/routing/partitions/browser-route-partition-binding-store.test.ts", - "assertions": [ - "opaque bindings survive restart", - "replacement and malformed metadata fail closed", - "oversized metadata and Chromium data without a binding fail closed", - "persisted binding count is bounded" - ] - }, - { - "file": "src/main/browser/routing/sessions/browser-route-session-registry.test.ts", - "assertions": [ - "policy, proxy, connection closure, and verification finish before allowlisting", - "DIRECT and endpoint retargeting are rejected", - "non-loopback listeners and invalid page generations are rejected before persistence", - "missing browser profiles consume no durable binding capacity", - "partial setup cleanup and concurrent setup settle exactly once", - "partition and page-generation counts are bounded", - "stale page cleanup cannot retire its replacement" - ] - }, - { - "file": "src/main/browser/network/egress/browser-route-webrtc-egress.electron.test.ts", - "assertions": [ - "the unprotected SOCKS-partition control emits direct STUN packets", - "disable_non_proxied_udp leaves the exact guest with zero direct UDP packets" - ] - }, - { - "file": "src/main/browser/routing/guest-authority/browser-route-webcontents-registry.test.ts", - "assertions": [ - "route guests apply non-proxied WebRTC denial before admission", - "policy failure remains quarantined through delayed or failed guest close" - ] - }, - { - "file": "src/main/window/createMainWindow.test.ts", - "assertions": [ - "a live route partition may attach only the normalized blank document", - "an arbitrary URL cannot be the initial route-partition document" - ] - } - ], - "evidenceRuns": [ - { - "date": "2026-08-16", - "runner": "local", - "platform": "macos", - "command": "pnpm exec vitest run --config config/vitest.config.ts src/main/browser/browser-route-webcontents-registry.test.ts src/main/browser/browser-route-webrtc-egress.electron.test.ts", - "currentCommand": "pnpm exec vitest run --config config/vitest.config.ts src/main/browser/routing/guest-authority/browser-route-webcontents-registry.test.ts src/main/browser/network/egress/browser-route-webrtc-egress.electron.test.ts", - "result": "passed", - "durationSeconds": 8, - "summary": "Two files passed 24 assertions; the real Electron control emitted direct STUN, while the exact per-guest policy emitted zero desktop UDP packets and policy failure remained quarantined through delayed or failed close." - } - ], + "oracle": "No current automated execution oracle is claimed for this gate after Audit1396 retired changed-source receipts. Historical oracle account: Derive two delimiter-adversarial identities and require distinct full-digest path-safe partitions with no raw IDs or component hashes. Persist one binding, reload it, and reject replacement, malformed or oversized state, Chromium data without matching metadata, and the 513th binding. Prepare one partition and require setProxy with <-loopback> to be invoked immediately after getSession and before policy setup, then closeAllConnections and exact SOCKS5 resolveProxy while isAllowedPartition remains false; only then may it become live. Reject DIRECT, endpoint retargeting, and capacity overflow. Require quarantine before disable_non_proxied_udp and admission; under real Electron require the unprotected control to emit STUN and the protected guest to emit zero direct UDP packets.", + "commands": [], + "testFiles": [], + "assertionRefs": [], + "evidenceRuns": [], "runtimeBudget": { "p95Seconds": 2, - "scope": "deterministic identity, binding-store, session-policy, and window-boundary tests; this unit-test budget excludes SSH browser journeys, whose CI p95 is not yet established" + "scope": "Historical prior-selector budget; reduced selector has no new runtime measurement." }, "flakeHistory": { "status": "unknown", - "evidence": "The deterministic suite passes locally. Linux SSH provider journeys passed four baseline cases and twelve repeated cases in CI runs 34039986047 and 34040309638 with zero skips or retries. Long-term and cross-platform soak history remains incomplete." + "evidence": "Audit1396 retired changed-source receipts; reduced selector has no new soak result. Historical account: The deterministic suite passes locally. Linux SSH provider journeys passed four baseline cases and twelve repeated cases in CI runs 34039986047 and 34040309638 with zero skips or retries. Long-term and cross-platform soak history remains incomplete." }, "redGreenEvidence": { - "status": "partial", - "evidence": "The TCP A/B control first reached all six target paths directly, while the fixed SOCKS arm routed the same HTTP, HTTPS, WebSocket, redirect, subresource, and download traffic plus remote DNS with zero direct target connections. The two-launch worker control reached desktop localhost before setProxy; immediate setProxy routed the same wake and later fetch through SOCKS. The WebRTC control emitted four direct STUN packets until disable_non_proxied_udp reduced the capture to zero. Non-WebRTC UDP and network-service restart remain explicit residuals. Historical receipts before removing the source-only tests, preserved verbatim with their originally executed commands (the narrowed commands were not rerun here): [{\"date\":\"2026-08-20\",\"runner\":\"local\",\"platform\":\"macos\",\"command\":\"pnpm exec vitest run --config config/vitest.config.ts src/main/browser/browser-route-h3-egress.electron.test.ts src/main/browser/browser-route-dns-prefetch.electron.test.ts src/main/startup/secure-dns-census.test.ts\",\"result\":\"passed\",\"durationSeconds\":21,\"summary\":\"The direct control emitted 5 WebTransport and 6 forced-QUIC datagrams while the SOCKS route partition emitted zero of each. With Direct Sockets explicitly enabled the control exposed TCPSocket/UDPSocket/TCPServerSocket and constructing one killed the guest renderer; the shipped disable-features list left all three undefined and the renderer alive. The dns-prefetch tripwire recorded HOST_RESOLVER_SYSTEM_TASK for the prefetched host with zero events for an unreferenced control host, documenting the accepted local-resolution residual.\",\"notes\":\"The dns-prefetch expectation intentionally asserts the leak. Flip it to an empty event list when the upstream Electron PrefetchDNS fix lands.\"}] Archived provider-experiment receipts after removing the standalone TCP and persisted-worker suites, preserved verbatim with their originally executed commands (remaining and narrowed commands were not rerun here): [\n {\n \"date\": \"2026-08-17\",\n \"runner\": \"local\",\n \"platform\": \"macos\",\n \"command\": \"pnpm exec vitest run --config config/vitest.config.ts src/main/browser/browser-route-tcp-egress.electron.test.ts\",\n \"result\": \"passed\",\n \"durationSeconds\": 1,\n \"summary\": \"The real Electron direct control reached all six TCP request surfaces; the fixed SOCKS session carried HTTP, HTTPS, WebSocket, redirects, subresources, downloads, and remote DNS with zero direct target connections.\"\n },\n {\n \"date\": \"2026-08-16\",\n \"runner\": \"local\",\n \"platform\": \"macos\",\n \"command\": \"pnpm exec vitest run --config config/vitest.config.ts src/main/browser/routing/sessions/browser-route-session-registry.test.ts src/main/browser/browser-route-persisted-worker-egress.electron.test.ts src/main/browser/network/egress/browser-route-webrtc-egress.electron.test.ts\",\n \"result\": \"passed\",\n \"durationSeconds\": 10,\n \"summary\": \"Three files passed 28 tests. The two-launch worker control reached desktop localhost before proxy setup; immediate setProxy invocation routed the same wake and a later worker fetch through SOCKS with path-attributed direct counts remaining zero. Both resolved and rejected proxy setup keep failed-policy retries joined through connection cleanup. The protected WebRTC guest emitted zero direct UDP packets.\"\n }\n ]" + "status": "missing", + "evidence": "Audit1396 changed referenced test sources; no new red/green validation. Historical account: The TCP A/B control first reached all six target paths directly, while the fixed SOCKS arm routed the same HTTP, HTTPS, WebSocket, redirect, subresource, and download traffic plus remote DNS with zero direct target connections. The two-launch worker control reached desktop localhost before setProxy; immediate setProxy routed the same wake and later fetch through SOCKS. The WebRTC control emitted four direct STUN packets until disable_non_proxied_udp reduced the capture to zero. Non-WebRTC UDP and network-service restart remain explicit residuals. Historical receipts before removing the source-only tests, preserved verbatim with their originally executed commands (the narrowed commands were not rerun here): [{\"date\":\"2026-08-20\",\"runner\":\"local\",\"platform\":\"macos\",\"command\":\"pnpm exec vitest run --config config/vitest.config.ts src/main/browser/browser-route-h3-egress.electron.test.ts src/main/browser/browser-route-dns-prefetch.electron.test.ts src/main/startup/secure-dns-census.test.ts\",\"result\":\"passed\",\"durationSeconds\":21,\"summary\":\"The direct control emitted 5 WebTransport and 6 forced-QUIC datagrams while the SOCKS route partition emitted zero of each. With Direct Sockets explicitly enabled the control exposed TCPSocket/UDPSocket/TCPServerSocket and constructing one killed the guest renderer; the shipped disable-features list left all three undefined and the renderer alive. The dns-prefetch tripwire recorded HOST_RESOLVER_SYSTEM_TASK for the prefetched host with zero events for an unreferenced control host, documenting the accepted local-resolution residual.\",\"notes\":\"The dns-prefetch expectation intentionally asserts the leak. Flip it to an empty event list when the upstream Electron PrefetchDNS fix lands.\"}] Archived provider-experiment receipts after removing the standalone TCP and persisted-worker suites, preserved verbatim with their originally executed commands (remaining and narrowed commands were not rerun here): [\n {\n \"date\": \"2026-08-17\",\n \"runner\": \"local\",\n \"platform\": \"macos\",\n \"command\": \"pnpm exec vitest run --config config/vitest.config.ts src/main/browser/browser-route-tcp-egress.electron.test.ts\",\n \"result\": \"passed\",\n \"durationSeconds\": 1,\n \"summary\": \"The real Electron direct control reached all six TCP request surfaces; the fixed SOCKS session carried HTTP, HTTPS, WebSocket, redirects, subresources, downloads, and remote DNS with zero direct target connections.\"\n },\n {\n \"date\": \"2026-08-16\",\n \"runner\": \"local\",\n \"platform\": \"macos\",\n \"command\": \"pnpm exec vitest run --config config/vitest.config.ts src/main/browser/routing/sessions/browser-route-session-registry.test.ts src/main/browser/browser-route-persisted-worker-egress.electron.test.ts src/main/browser/network/egress/browser-route-webrtc-egress.electron.test.ts\",\n \"result\": \"passed\",\n \"durationSeconds\": 10,\n \"summary\": \"Three files passed 28 tests. The two-launch worker control reached desktop localhost before proxy setup; immediate setProxy invocation routed the same wake and a later worker fetch through SOCKS with path-attributed direct counts remaining zero. Both resolved and rejected proxy setup keep failed-policy retries joined through connection cleanup. The protected WebRTC guest emitted zero direct UDP packets.\"\n }\n ]" }, "performanceBudget": { "required": true, - "evidence": "Derivation performs one bounded full SHA-256 aggregate partition hash and one binding hash. The registry admits at most 64 live or pending partitions, 64 logical page generations per partition, and 512 durable bindings per Orca profile. Concurrent preparation shares one promise and one Electron proxy application per partition. Binding-file reads are capped at 128 KiB. Writes are bounded synchronous profile-local JSON, fsync the temporary file before Windows-retried rename, and fsync the containing directory where supported." + "evidence": "Historical budget only after Audit1396 changed test sources; no new measurement. Previous account: Derivation performs one bounded full SHA-256 aggregate partition hash and one binding hash. The registry admits at most 64 live or pending partitions, 64 logical page generations per partition, and 512 durable bindings per Orca profile. Concurrent preparation shares one promise and one Electron proxy application per partition. Binding-file reads are capped at 128 KiB. Writes are bounded synchronous profile-local JSON, fsync the temporary file before Windows-retried rename, and fsync the containing directory where supported." }, "promotionCriteria": [ "Use the production paired journey to prove proxy application precedes the first popup and speculative request.", @@ -7369,7 +7267,14 @@ "Binding writes serialize in Electron main, and packaged hosts rely on Orca's per-userData single-instance lock. Activation still needs an explicit guard for dev instances that share userData or a cross-process CAS/lock.", "Sequential proxy or policy setup failures retain durable bindings and can exhaust the 512-binding ledger. Activation requires bounded tombstone recovery and partition garbage collection.", "Each preparePage synchronously reads and parses bounded binding metadata on Electron main; activation requires latency evidence or a safely invalidated cache before this becomes frequent.", - "New SSH browser journey evidence is limited to Linux CI with Docker; native macOS/Windows clients and WSL providers remain unverified by these scenarios." + "New SSH browser journey evidence is limited to Linux CI with Docker; native macOS/Windows clients and WSL providers remain unverified by these scenarios.", + "Audit1396: src/main/browser/network/egress/browser-route-webrtc-egress.electron.test.ts removed. Deletes entire one-registration native Electron baseline/protected UDP probe, including fixture shell and packet observer. No equivalent actual network egress proof is claimed; actual Orca policy owner reviewed separately. No equivalent replacement coverage is claimed.", + "Audit1396 retires this complete dated receipt because a source in its selector was deleted or reduced. It does not validate the reduced selector. Original receipt verbatim:\n{\n \"date\": \"2026-08-16\",\n \"runner\": \"local\",\n \"platform\": \"macos\",\n \"command\": \"pnpm exec vitest run --config config/vitest.config.ts src/main/browser/browser-route-webcontents-registry.test.ts src/main/browser/browser-route-webrtc-egress.electron.test.ts\",\n \"currentCommand\": \"pnpm exec vitest run --config config/vitest.config.ts src/main/browser/routing/guest-authority/browser-route-webcontents-registry.test.ts src/main/browser/network/egress/browser-route-webrtc-egress.electron.test.ts\",\n \"result\": \"passed\",\n \"durationSeconds\": 8,\n \"summary\": \"Two files passed 24 assertions; the real Electron control emitted direct STUN, while the exact per-guest policy emitted zero desktop UDP packets and policy failure remained quarantined through delayed or failed close.\"\n }", + "Audit1396 historical platform/provider scope before source changes: {\"coveredPlatforms\":[\"macos\",\"linux\"],\"coveredProviders\":[\"ssh\",\"remote-runtime\"]}. Remaining provider scope applies only to unchanged-source receipts.", + "Audit1396 retained but unvalidated assertion inventory pending a new run of the reduced command:\n[\n {\n \"file\": \"src/main/browser/routing/partitions/browser-route-identity.test.ts\",\n \"assertions\": [\n \"aggregate partition and durable binding fingerprints are stable and opaque\",\n \"delimiter-containing identities remain structurally distinct\",\n \"individual component equality and Windows-illegal path separators are absent\",\n \"empty and unbounded identity components are rejected\"\n ]\n },\n {\n \"file\": \"src/main/browser/routing/partitions/browser-route-partition-binding-store.test.ts\",\n \"assertions\": [\n \"opaque bindings survive restart\",\n \"replacement and malformed metadata fail closed\",\n \"oversized metadata and Chromium data without a binding fail closed\",\n \"persisted binding count is bounded\"\n ]\n },\n {\n \"file\": \"src/main/browser/routing/sessions/browser-route-session-registry.test.ts\",\n \"assertions\": [\n \"policy, proxy, connection closure, and verification finish before allowlisting\",\n \"DIRECT and endpoint retargeting are rejected\",\n \"non-loopback listeners and invalid page generations are rejected before persistence\",\n \"missing browser profiles consume no durable binding capacity\",\n \"partial setup cleanup and concurrent setup settle exactly once\",\n \"partition and page-generation counts are bounded\",\n \"stale page cleanup cannot retire its replacement\"\n ]\n },\n {\n \"file\": \"src/main/browser/routing/guest-authority/browser-route-webcontents-registry.test.ts\",\n \"assertions\": [\n \"route guests apply non-proxied WebRTC denial before admission\",\n \"policy failure remains quarantined through delayed or failed guest close\"\n ]\n },\n {\n \"file\": \"src/main/window/createMainWindow.test.ts\",\n \"assertions\": [\n \"a live route partition may attach only the normalized blank document\",\n \"an arbitrary URL cannot be the initial route-partition document\"\n ]\n }\n]", + "Audit1396 historical assertion inventory for deleted/reduced sources; not current validated assertions:\n[\n {\n \"file\": \"src/main/browser/network/egress/browser-route-webrtc-egress.electron.test.ts\",\n \"assertions\": [\n \"the unprotected SOCKS-partition control emits direct STUN packets\",\n \"disable_non_proxied_udp leaves the exact guest with zero direct UDP packets\"\n ]\n }\n]", + "Audit1396 archived original active assertionRefs verbatim after retirement of all passed receipts:\n[\n {\n \"file\": \"src/main/browser/routing/partitions/browser-route-identity.test.ts\",\n \"assertions\": [\n \"aggregate partition and durable binding fingerprints are stable and opaque\",\n \"delimiter-containing identities remain structurally distinct\",\n \"individual component equality and Windows-illegal path separators are absent\",\n \"empty and unbounded identity components are rejected\"\n ]\n },\n {\n \"file\": \"src/main/browser/routing/partitions/browser-route-partition-binding-store.test.ts\",\n \"assertions\": [\n \"opaque bindings survive restart\",\n \"replacement and malformed metadata fail closed\",\n \"oversized metadata and Chromium data without a binding fail closed\",\n \"persisted binding count is bounded\"\n ]\n },\n {\n \"file\": \"src/main/browser/routing/sessions/browser-route-session-registry.test.ts\",\n \"assertions\": [\n \"policy, proxy, connection closure, and verification finish before allowlisting\",\n \"DIRECT and endpoint retargeting are rejected\",\n \"non-loopback listeners and invalid page generations are rejected before persistence\",\n \"missing browser profiles consume no durable binding capacity\",\n \"partial setup cleanup and concurrent setup settle exactly once\",\n \"partition and page-generation counts are bounded\",\n \"stale page cleanup cannot retire its replacement\"\n ]\n },\n {\n \"file\": \"src/main/browser/network/egress/browser-route-webrtc-egress.electron.test.ts\",\n \"assertions\": [\n \"the unprotected SOCKS-partition control emits direct STUN packets\",\n \"disable_non_proxied_udp leaves the exact guest with zero direct UDP packets\"\n ]\n },\n {\n \"file\": \"src/main/browser/routing/guest-authority/browser-route-webcontents-registry.test.ts\",\n \"assertions\": [\n \"route guests apply non-proxied WebRTC denial before admission\",\n \"policy failure remains quarantined through delayed or failed guest close\"\n ]\n },\n {\n \"file\": \"src/main/window/createMainWindow.test.ts\",\n \"assertions\": [\n \"a live route partition may attach only the normalized blank document\",\n \"an arbitrary URL cannot be the initial route-partition document\"\n ]\n }\n ]", + "Audit1396 archived active selectors after retirement of all passed receipts: {\"commands\":[\"pnpm exec vitest run --config config/vitest.config.ts src/main/browser/routing/partitions/browser-route-identity.test.ts src/main/browser/routing/partitions/browser-route-partition-binding-store.test.ts src/main/browser/routing/sessions/browser-route-session-registry.test.ts src/main/browser/sessions/browser-session-registry.test.ts src/main/window/createMainWindow.test.ts\",\"pnpm exec vitest run --config config/vitest.config.ts src/main/browser/routing/guest-authority/browser-route-webcontents-registry.test.ts src/main/browser/network/egress/browser-route-webrtc-egress.electron.test.ts\"],\"testFiles\":[\"src/main/browser/routing/partitions/browser-route-identity.test.ts\",\"src/main/browser/routing/partitions/browser-route-partition-binding-store.test.ts\",\"src/main/browser/routing/sessions/browser-route-session-registry.test.ts\",\"src/main/browser/network/egress/browser-route-webrtc-egress.electron.test.ts\",\"src/main/browser/routing/guest-authority/browser-route-webcontents-registry.test.ts\",\"src/main/browser/sessions/browser-session-registry.test.ts\",\"src/main/window/createMainWindow.test.ts\"]}" ], "demotionRule": "Keep experimental or demote if raw identities enter a partition path, a durable binding mismatch is reused, a partition retargets to another execution host or live listener, a route partition becomes attachable before exact proxy verification, initial attachment can navigate beyond blank, stale cleanup retires a replacement, admission exceeds a declared cap, or any browser request reaches desktop DNS, TCP, UDP, localhost, or system proxy outside the selected route." }, @@ -9169,8 +9074,8 @@ "platforms": ["macos", "linux", "windows", "mobile"], "providers": ["local", "daemon", "ssh", "wsl", "remote-runtime"], "coveredPlatforms": ["macos"], - "coveredProviders": ["local", "ssh", "remote-runtime"], - "coverageNotes": "Local macOS evidence covers the shared missing-dir fallback policy, main pty:spawn recovery and metadata, no-flag and reattach strictness, renderer IPC flag routing, SSH-tagged and remote-runtime omission, and the visibility-gated terminal notice. Real Bash and Zsh node-pty tests replace the active cwd inode and exercise the shared POSIX OMP wrapper; audit829 removes generated-file snapshots and their current byte-parity claim. No equivalent replacement for generated-script byte checks is claimed. Daemon shares the same pre-provider main cwd decision but lacks a live daemon-provider run; WSL UNC paths are exempt from the spawn probe and lack a live run; native PowerShell is unaffected; Linux/Windows and mobile/API strictness remain gaps.", + "coveredProviders": [], + "coverageNotes": "Audit1396 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): Local macOS evidence covers the shared missing-dir fallback policy, main pty:spawn recovery and metadata, no-flag and reattach strictness, renderer IPC flag routing, SSH-tagged and remote-runtime omission, and the visibility-gated terminal notice. Real Bash and Zsh node-pty tests replace the active cwd inode and exercise the shared POSIX OMP wrapper; audit829 removes generated-file snapshots and their current byte-parity claim. No equivalent replacement for generated-script byte checks is claimed. Daemon shares the same pre-provider main cwd decision but lacks a live daemon-provider run; WSL UNC paths are exempt from the spawn probe and lack a live run; native PowerShell is unaffected; Linux/Windows and mobile/API strictness remain gaps. Audit1396: src/renderer/src/components/terminal-pane/pty-connection-pending-spawn.spec.ts reduced. Lose standalone debug-state retention/log silence alarm. React StrictMode is not mounted; bindings manually simulate remount over mocked transport. No equivalent replacement coverage is claimed.", "motivatingLinks": [ "https://github.com/stablyai/orca/issues/7239", "https://github.com/stablyai/orca/pull/7750", @@ -9179,7 +9084,7 @@ "https://github.com/stablyai/orca/pull/17128" ], "invariant": "A fresh local renderer terminal spawn may recover from a saved startup cwd whose directory no longer exists only by spawning at the selected workspace root and printing a generic in-terminal notice; existing directories — including ones outside the worktree (#7685) — spawn as requested, and reattach, SSH, remote-runtime, runtime/API, and mobile callers keep exact cwd semantics. A long-lived POSIX shell whose cwd inode was deleted and replaced may run a wrapped OMP command from the live path named by its logical PWD only in a subshell; the parent shell cwd, OMP argv, status extension, and exit status remain unchanged, while a genuinely unavailable path fails visibly without invoking OMP. A usable physical cwd remains authoritative when PWD is unset or names a removed logical symlink instead of being remapped to a workspace fallback.", - "oracle": "The shared resolver falls back to the workspace root only when the injected existence probe reports the resolved cwd missing and the workspace root present, and never probes floating terminals or a cwd equal to the root. The renderer sends cwdFallback only for fresh local IPC spawns, main honors it only when connectionId and sessionId are absent, WSL UNC paths never engage the probe-based fallback, main returns fallback metadata only after an actual fallback, the IPC transport preserves that metadata, and the connection layer writes a generic notice that omits the missing path. In real interactive Bash and Zsh PTYs, unset PWD and remove a logical symlink while the physical cwd stays usable, requiring OMP to remain there; then delete and recreate the active project path, require extension-enabled and extension-skipping invocations to observe the replacement inode with byte-exact argv and nonzero status preserved, require the parent shell to remain on the stale inode, unset PWD and require recovery through ORCA_WORKTREE_PATH, clear every logical fallback and require an actionable failure without invoking OMP, and finally delete the replacement and require the same for the missing PWD path.", + "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): The shared resolver falls back to the workspace root only when the injected existence probe reports the resolved cwd missing and the workspace root present, and never probes floating terminals or a cwd equal to the root. The renderer sends cwdFallback only for fresh local IPC spawns, main honors it only when connectionId and sessionId are absent, WSL UNC paths never engage the probe-based fallback, main returns fallback metadata only after an actual fallback, the IPC transport preserves that metadata, and the connection layer writes a generic notice that omits the missing path. In real interactive Bash and Zsh PTYs, unset PWD and remove a logical symlink while the physical cwd stays usable, requiring OMP to remain there; then delete and recreate the active project path, require extension-enabled and extension-skipping invocations to observe the replacement inode with byte-exact argv and nonzero status preserved, require the parent shell to remain on the stale inode, unset PWD and require recovery through ORCA_WORKTREE_PATH, clear every logical fallback and require an actionable failure without invoking OMP, and finally delete the replacement and require the same for the missing PWD path.", "commands": [ "pnpm exec vitest run --config config/vitest.config.ts src/shared/shell/launch/terminal-startup-cwd.test.ts", "pnpm exec vitest run --config config/vitest.config.ts src/main/ipc/pty/pty-spawn-cwd-fallback.test.ts", @@ -9280,16 +9185,6 @@ "durationSeconds": 0.34, "summary": "1 test file passed, 58 tests passed; covers cwdFallback forwarding only for local fresh spawns and metadata handoff." }, - { - "date": "2026-07-08", - "runner": "local", - "platform": "macos", - "command": "pnpm exec vitest run --config config/vitest.config.ts src/renderer/src/components/terminal-pane/pty-connection-runtime-owner-spawn-routing.test.ts", - "result": "passed", - "durationSeconds": 6.5, - "summary": "1 test file passed, 341 tests passed; covers local IPC marking, the generic terminal fallback notice, and remote-runtime omission.", - "currentCommand": "pnpm exec vitest run --config config/vitest.config.ts src/renderer/src/components/terminal-pane/pty-connection-spawn-and-startup.test.ts" - }, { "date": "2026-08-29", "runner": "local", @@ -9303,19 +9198,19 @@ ], "runtimeBudget": { "p95Seconds": 40, - "scope": "focused unit, IPC, and real Bash/Zsh PTY contracts" + "scope": "Historical pre-audit selector budget; changed selectors have no new runtime measurement. Unchanged receipts remain scoped to their own commands." }, "flakeHistory": { "status": "unknown", - "evidence": "New experimental gate added with local deterministic evidence only; needs CI soak before promotion." + "evidence": "Changed selectors have no new soak result after Audit1396; any retained unchanged-source receipts apply only to their own commands. Historical account: New experimental gate added with local deterministic evidence only; needs CI soak before promotion." }, "redGreenEvidence": { - "status": "partial", - "evidence": "The main IPC missing-cwd tests fail with the provider's 'Working directory ... does not exist.' error when the spawn fallback is removed and pass with it. Before the OMP recovery, the stale-inode reproduction makes the child process fail to resolve its cwd; the candidate rebinds to the recreated path in real Bash and Zsh PTYs. Full live Electron reproduction from a production persisted session is not captured." + "status": "missing", + "evidence": "Audit1396 changed referenced test sources; no new red/green validation of the reduced set. Historical account: The main IPC missing-cwd tests fail with the provider's 'Working directory ... does not exist.' error when the spawn fallback is removed and pass with it. Before the OMP recovery, the stale-inode reproduction makes the child process fail to resolve its cwd; the candidate rebinds to the recreated path in real Bash and Zsh PTYs. Full live Electron reproduction from a production persisted session is not captured." }, "performanceBudget": { "required": true, - "evidence": "The spawn recovery adds at most two statSync probes on the fresh-local spawn path (the provider already stats the same paths during validation) and one bounded terminal write only when fallback actually occurs. Each wrapped OMP launch adds a constant number of shell filesystem predicates; only a detected stale/unusable cwd creates one short recovery subshell. No polling, retry, provider listing, hidden-pane work, startup awaits, extra OMP subprocesses, or render-loop work was added." + "evidence": "Historical account for the pre-audit test set; removed work-count alarms and changed selectors have no fresh measurement. The spawn recovery adds at most two statSync probes on the fresh-local spawn path (the provider already stats the same paths during validation) and one bounded terminal write only when fallback actually occurs. Each wrapped OMP launch adds a constant number of shell filesystem predicates; only a detected stale/unusable cwd creates one short recovery subshell. No polling, retry, provider listing, hidden-pane work, startup awaits, extra OMP subprocesses, or render-loop work was added." }, "promotionCriteria": [ "Attach CI evidence for all declared test files.", @@ -9327,7 +9222,10 @@ "No live Electron fixture seeds a persisted tab whose startupCwd directory was deleted.", "Daemon coverage is via the shared pre-provider main cwd decision, not a live daemon provider spawn.", "The stale-inode OMP oracle is local macOS Bash/Zsh plus shared generated-file parity; it does not execute through live SSH, relay, Linux, or WSL providers.", - "WSL UNC paths bypass the spawn probe by design and have no live existence-recovery run; Linux, Windows, and mobile/API spawn strictness are not directly exercised." + "WSL UNC paths bypass the spawn probe by design and have no live existence-recovery run; Linux, Windows, and mobile/API spawn strictness are not directly exercised.", + "Audit1396: src/renderer/src/components/terminal-pane/pty-connection-pending-spawn.spec.ts reduced. Lose standalone debug-state retention/log silence alarm. React StrictMode is not mounted; bindings manually simulate remount over mocked transport. No equivalent replacement coverage is claimed.", + "Audit1396 retires this complete dated receipt because a source in its selector was deleted or reduced. It does not validate the reduced selector. Original receipt verbatim:\n{\n \"date\": \"2026-07-08\",\n \"runner\": \"local\",\n \"platform\": \"macos\",\n \"command\": \"pnpm exec vitest run --config config/vitest.config.ts src/renderer/src/components/terminal-pane/pty-connection-runtime-owner-spawn-routing.test.ts\",\n \"result\": \"passed\",\n \"durationSeconds\": 6.5,\n \"summary\": \"1 test file passed, 341 tests passed; covers local IPC marking, the generic terminal fallback notice, and remote-runtime omission.\",\n \"currentCommand\": \"pnpm exec vitest run --config config/vitest.config.ts src/renderer/src/components/terminal-pane/pty-connection-spawn-and-startup.test.ts\"\n }", + "Audit1396 historical platform/provider scope before source changes: {\"coveredPlatforms\":[\"macos\"],\"coveredProviders\":[\"local\",\"ssh\",\"remote-runtime\"]}. Remaining provider scope applies only to unchanged-source receipts." ], "demotionRule": "Demote or quarantine if the gate flakes without a product bug, if an existing directory is ever remapped away from the requested cwd, if a reattach/remote/API caller can engage the spawn fallback, or if OMP recovery remaps a usable cwd with unset PWD, changes argv/status/parent cwd, or invokes OMP when the logical path remains unavailable." }, @@ -9562,15 +9460,15 @@ "surfaces": ["Quick Launch agent inventory", "agent launch", "Floating Workspace"], "platforms": ["macos", "linux", "windows"], "providers": ["local", "wsl", "ssh", "remote-runtime"], - "coveredPlatforms": ["macos", "windows"], - "coveredProviders": ["local", "wsl"], - "coverageNotes": "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): A deterministic Windows-mode renderer oracle keeps an active WSL project and explicit-local Floating Workspace mounted together with disjoint agent inventories, then independently asserts discovery and launch authority. A headed physical Windows 2 journey with real Ubuntu WSL2 independently verified the rendered inventory and native PowerShell launch; SSH and paired-runtime behavior retain their existing contract coverage. The test-value audit removes standalone WSL/provider argument inventories and basic supplied-result/cache-clear examples from detected-agents.test.ts. It retains shared in-flight detection, cached-context return, repair/retry, stale-response fencing, SSH refresh/pruning and old-server fallback behavior. These retained tests do not replace the removed route-argument alarms or execute a live WSL/remote probe. Historical commands, counts and results remain unchanged. The later test-value audit removes narrow refusal/reset ledgers, a settled-project eviction/reacquisition example, a simple stale-success variant and redundant plain-text draft cleanup examples. local-detected-agent-state retains independent inventory completion orders, preference-change fencing, full-refresh loading settlement, refresh failure/retry, native-versus-WSL separation, subscriber broadcast bounds and in-flight project eviction. The removed settled-context sibling-preservation/reacquisition alarm is intentionally lost; retained cases are not equivalent replacements and no live provider execution is claimed. Historical commands, counts and results remain unchanged. The test-value audit retires the standalone project-runtime registrar from repos-project-mutations.test.ts. Its updateProject runtime-preference example no longer checks clearing detectedAgentIds, isDetectingAgents and isRefreshingAgents or dispatch of orca:installed-agent-skills-changed. This narrow invalidation alarm is lost; no equivalent replacement is claimed. Historical executed commands, counts and results remain unchanged. Audit1342: src/renderer/src/hooks/useAgentDetectionTarget.test.ts removed. Floating/runtime/hydration/stamped owner key selection, scanning count and malformed decode fallback matrices disappear. No equivalent replacement coverage is claimed.", + "coveredPlatforms": ["windows"], + "coveredProviders": [], + "coverageNotes": "Audit1396 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): A deterministic Windows-mode renderer oracle keeps an active WSL project and explicit-local Floating Workspace mounted together with disjoint agent inventories, then independently asserts discovery and launch authority. A headed physical Windows 2 journey with real Ubuntu WSL2 independently verified the rendered inventory and native PowerShell launch; SSH and paired-runtime behavior retain their existing contract coverage. The test-value audit removes standalone WSL/provider argument inventories and basic supplied-result/cache-clear examples from detected-agents.test.ts. It retains shared in-flight detection, cached-context return, repair/retry, stale-response fencing, SSH refresh/pruning and old-server fallback behavior. These retained tests do not replace the removed route-argument alarms or execute a live WSL/remote probe. Historical commands, counts and results remain unchanged. The later test-value audit removes narrow refusal/reset ledgers, a settled-project eviction/reacquisition example, a simple stale-success variant and redundant plain-text draft cleanup examples. local-detected-agent-state retains independent inventory completion orders, preference-change fencing, full-refresh loading settlement, refresh failure/retry, native-versus-WSL separation, subscriber broadcast bounds and in-flight project eviction. The removed settled-context sibling-preservation/reacquisition alarm is intentionally lost; retained cases are not equivalent replacements and no live provider execution is claimed. Historical commands, counts and results remain unchanged. The test-value audit retires the standalone project-runtime registrar from repos-project-mutations.test.ts. Its updateProject runtime-preference example no longer checks clearing detectedAgentIds, isDetectingAgents and isRefreshingAgents or dispatch of orca:installed-agent-skills-changed. This narrow invalidation alarm is lost; no equivalent replacement is claimed. Historical executed commands, counts and results remain unchanged. Audit1342: src/renderer/src/hooks/useAgentDetectionTarget.test.ts removed. Floating/runtime/hydration/stamped owner key selection, scanning count and malformed decode fallback matrices disappear. No equivalent replacement coverage is claimed. Audit1396: src/renderer/src/components/terminal-pane/pty-connection-pending-spawn.spec.ts reduced. Lose standalone debug-state retention/log silence alarm. React StrictMode is not mounted; bindings manually simulate remount over mocked transport. No equivalent replacement coverage is claimed.", "motivatingLinks": [ "https://linear.app/stably/issue/STA-4027/p1-floating-workspace-agent-detection-still-uses-active-wsl-project", "https://github.com/stablyai/orca/pull/13995" ], "invariant": "Every agent launch surface must advertise the provider set detected on its explicit execution authority and launch on that same authority; a local Floating Workspace must never inherit an unrelated active project's WSL, SSH, or paired-runtime context.", - "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): Model an active WSL project and a native-Windows Floating Workspace with WSL-only Claude and host-only Codex. Keep both detection consumers mounted, require their advertised sets to stay disjoint through refresh, require the legacy active-project inventory to remain unchanged, then independently assert the Floating launch call and PTY transport select local IPC with no WSL project runtime.", + "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): 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): Model an active WSL project and a native-Windows Floating Workspace with WSL-only Claude and host-only Codex. Keep both detection consumers mounted, require their advertised sets to stay disjoint through refresh, require the legacy active-project inventory to remain unchanged, then independently assert the Floating launch call and PTY transport select local IPC with no WSL project runtime.", "commands": [ "pnpm exec vitest run --config config/vitest.config.ts src/renderer/src/hooks/useDetectedAgents.test.tsx src/renderer/src/store/slices/detected-agents.test.ts src/renderer/src/store/slices/local-detected-agent-state.test.ts src/renderer/src/store/slices/repos-project-mutations.test.ts src/renderer/src/lib/launch-agent-in-new-tab.test.ts --reporter=verbose", "pnpm exec vitest run --config config/vitest.config.ts src/renderer/src/components/terminal-pane/pty-connection-spawn-and-startup.test.ts --reporter=dot", @@ -9620,16 +9518,6 @@ } ], "evidenceRuns": [ - { - "date": "2026-08-12", - "runner": "local", - "platform": "macos", - "command": "pnpm exec vitest run --config config/vitest.config.ts src/renderer/src/components/terminal-pane/pty-connection-fresh-spawn-guards.test.ts --reporter=dot", - "result": "passed", - "durationSeconds": 20, - "summary": "The full PTY connection suite passed the independent Floating native-host transport oracle.", - "currentCommand": "pnpm exec vitest run --config config/vitest.config.ts src/renderer/src/components/terminal-pane/pty-connection-spawn-and-startup.test.ts --reporter=dot" - }, { "date": "2026-08-12", "runner": "manual", @@ -9646,15 +9534,15 @@ }, "flakeHistory": { "status": "soaking", - "evidence": "Changed selectors have no new soak result after Audit1342; any retained unchanged-source receipts apply only to their own commands. Historical account: The deterministic oracle uses mocked provider results and React effect barriers. Exact-head PR run 31644438349 passed the full Node 24/26 matrix; an earlier patch-identical run's transient shard failures passed on rerun, but long-term soak history is not yet available." + "evidence": "Changed selectors have no new soak result after Audit1396; any retained unchanged-source receipts apply only to their own commands. Historical account: Changed selectors have no new soak result after Audit1342; any retained unchanged-source receipts apply only to their own commands. Historical account: The deterministic oracle uses mocked provider results and React effect barriers. Exact-head PR run 31644438349 passed the full Node 24/26 matrix; an earlier patch-identical run's transient shard failures passed on rerun, but long-term soak history is not yet available." }, "redGreenEvidence": { "status": "missing", - "evidence": "Audit1342 changed referenced test sources; no new red/green validation of the reduced set. Historical account: The byte-identical oracle fails on merged PR #13995 and the tested latest-main snapshot ebe5125476 because Floating receives the WSL-only Claude inventory while launch remains local. The candidate passes; disabling the scoped Floating target reproduces the mismatch." + "evidence": "Audit1396 changed referenced test sources; no new red/green validation of the reduced set. Historical account: Audit1342 changed referenced test sources; no new red/green validation of the reduced set. Historical account: The byte-identical oracle fails on merged PR #13995 and the tested latest-main snapshot ebe5125476 because Floating receives the WSL-only Claude inventory while launch remains local. The candidate passes; disabling the scoped Floating target reproduces the mismatch." }, "performanceBudget": { "required": true, - "evidence": "Historical account for the pre-audit test set; removed work-count alarms and changed selectors have no fresh measurement. Floating uses one stable host context key, retained tab bars do not rescan worktree indexes on store writes, concurrent probes deduplicate per context, and in-flight promise entries are deleted on settlement." + "evidence": "Historical account for the pre-audit test set; removed work-count alarms and changed selectors have no fresh measurement. Historical account for the pre-audit test set; removed work-count alarms and changed selectors have no fresh measurement. Floating uses one stable host context key, retained tab bars do not rescan worktree indexes on store writes, concurrent probes deduplicate per context, and in-flight promise entries are deleted on settlement." }, "promotionCriteria": [ "Collect 100 consecutive focused CI passes or 14 days of soak history.", @@ -9668,7 +9556,10 @@ "The test-value audit retires the standalone project-runtime registrar from repos-project-mutations.test.ts. Its updateProject runtime-preference example no longer checks clearing detectedAgentIds, isDetectingAgents and isRefreshingAgents or dispatch of orca:installed-agent-skills-changed. This narrow invalidation alarm is lost; no equivalent replacement is claimed. Historical executed commands, counts and results remain unchanged.", "Audit1342: src/renderer/src/hooks/useAgentDetectionTarget.test.ts removed. Floating/runtime/hydration/stamped owner key selection, scanning count and malformed decode fallback matrices disappear. No equivalent replacement coverage is claimed.", "Audit1342 retires this complete dated receipt because a source in its selector was deleted or reduced. It does not validate the reduced selector. Original receipt verbatim:\n{\n \"date\": \"2026-08-12\",\n \"runner\": \"local\",\n \"platform\": \"macos\",\n \"command\": \"pnpm exec vitest run --config config/vitest.config.ts src/renderer/src/hooks/useDetectedAgents.test.tsx src/renderer/src/hooks/useAgentDetectionTarget.test.ts src/renderer/src/store/slices/detected-agents.test.ts src/renderer/src/store/slices/local-detected-agent-state.test.ts src/renderer/src/store/slices/repos-remove-project-purge-leak.test.ts src/renderer/src/lib/local-preflight-context.test.ts src/renderer/src/lib/launch-agent-in-new-tab.test.ts --reporter=verbose\",\n \"result\": \"passed\",\n \"durationSeconds\": 2,\n \"summary\": \"Seven focused files passed independent WSL/local inventory, refresh isolation, cache lifecycle, project eviction, and Floating launch-call assertions. Historical command preserved after removal of the selector-only local-preflight suite in the test-value audit; currentCommand names the remaining suites only. This historical result has not been rerun for that command and does not claim equivalent coverage.\",\n \"currentCommand\": \"pnpm exec vitest run --config config/vitest.config.ts src/renderer/src/hooks/useDetectedAgents.test.tsx src/renderer/src/hooks/useAgentDetectionTarget.test.ts src/renderer/src/store/slices/detected-agents.test.ts src/renderer/src/store/slices/local-detected-agent-state.test.ts src/renderer/src/store/slices/repos-project-mutations.test.ts src/renderer/src/lib/launch-agent-in-new-tab.test.ts --reporter=verbose\"\n }", - "Audit1342 historical platform/provider scope before source changes: {\"coveredPlatforms\":[\"macos\",\"windows\"],\"coveredProviders\":[\"local\",\"wsl\"]}. Remaining provider scope applies only to unchanged-source receipts." + "Audit1342 historical platform/provider scope before source changes: {\"coveredPlatforms\":[\"macos\",\"windows\"],\"coveredProviders\":[\"local\",\"wsl\"]}. Remaining provider scope applies only to unchanged-source receipts.", + "Audit1396: src/renderer/src/components/terminal-pane/pty-connection-pending-spawn.spec.ts reduced. Lose standalone debug-state retention/log silence alarm. React StrictMode is not mounted; bindings manually simulate remount over mocked transport. No equivalent replacement coverage is claimed.", + "Audit1396 retires this complete dated receipt because a source in its selector was deleted or reduced. It does not validate the reduced selector. Original receipt verbatim:\n{\n \"date\": \"2026-08-12\",\n \"runner\": \"local\",\n \"platform\": \"macos\",\n \"command\": \"pnpm exec vitest run --config config/vitest.config.ts src/renderer/src/components/terminal-pane/pty-connection-fresh-spawn-guards.test.ts --reporter=dot\",\n \"result\": \"passed\",\n \"durationSeconds\": 20,\n \"summary\": \"The full PTY connection suite passed the independent Floating native-host transport oracle.\",\n \"currentCommand\": \"pnpm exec vitest run --config config/vitest.config.ts src/renderer/src/components/terminal-pane/pty-connection-spawn-and-startup.test.ts --reporter=dot\"\n }", + "Audit1396 historical platform/provider scope before source changes: {\"coveredPlatforms\":[\"macos\",\"windows\"],\"coveredProviders\":[\"local\",\"wsl\"]}. Remaining provider scope applies only to unchanged-source receipts." ], "demotionRule": "Demote if discovery and launch can resolve different authorities, a scoped Floating probe overwrites the active-project inventory, or local context selection adds recurring scans or retained in-flight entries." }, @@ -11347,13 +11238,13 @@ "providers": ["paired-runtime", "ssh"], "coveredPlatforms": ["macos", "linux"], "coveredProviders": ["paired-runtime"], - "coverageNotes": "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.", + "coverageNotes": "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.", "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": "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": "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/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", @@ -11918,7 +11809,8 @@ "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.", "Retired during audit1355 application of audit1346 at b176; complete original execution receipt unchanged, historical only:\n{\n \"date\": \"2026-08-10\",\n \"runner\": \"local\",\n \"platform\": \"macos\",\n \"command\": \"pnpm exec vitest run --config config/vitest.config.ts src/main/runtime/mobile-session-tabs-agent-status-heartbeat.test.ts src/main/runtime/orca-runtime-hook-agent-status-projection.test.ts tests/e2e/session-tabs-decorative-title-fanout.unit.test.ts tests/e2e/session-tabs-rich-status-boundaries.unit.test.ts --maxWorkers=1\",\n \"result\": \"passed\",\n \"durationSeconds\": 5.62,\n \"summary\": \"Forty-two deterministic contracts passed. The real-PTY cross-boundary contract delivered all 2,880 decorative spinner and bare-native redraw chunks with zero hot-path host publications, serialized bytes, renderer applies, or store mutations; continued past 30 minutes with one globally spaced freshness refresh per worktree every 15 minutes; kept all 24 mirrored statuses working; and preserved stable stateStartedAt. Exact-identical Pi and rotating normalized Pi/Grok frames renewed renderer-authored and retained-hook status without changing acknowledgement semantics. Rich status could not cross task completion, permission, provider-generation, or identity-owner boundaries, while neutral titles preserved same-task prompt and acknowledgement identity until authoritative shell evidence. Fresh renderer, retained OSC, and live-hook questions remained visible under the current shell title, but a later working interval, identity-only owner title, or provider generation stripped them; inherited tools and stale, resume-only, restored, or prior-interval questions stayed suppressed. After each rich source's original lease expired, title evidence kept only coarse working state and provider identity fresh while dropping unverifiable prompts. Unknown foreground ownership held publications at zero until resolution, then resumed the bounded heartbeat; tracked terminal state reset removed its delayed marker. The focused heartbeat contracts proved worktree deduplication, FIFO fairness, semantic bypass, PTY-source teardown, and timer cleanup. Causal-disable controls made ownership, teardown, retained source-switch, renderer rich-TTL, completion-transition, boundary-ordering, and live-question oracles fail; restoring the production corrections returned green.\",\n \"currentCommand\": \"pnpm exec vitest run --config config/vitest.config.ts src/main/runtime/mobile-session-tabs-agent-status-heartbeat.test.ts src/main/runtime/orca-runtime-hook-agent-status-projection.test.ts tests/e2e/session-tabs-decorative-title-fanout.unit.test.ts tests/e2e/session-tabs-rich-status-boundaries.unit.test.ts --maxWorkers=1\"\n }", "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.", - "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. Existing exact primary-selector receipts remain unchanged." + "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. Existing exact primary-selector receipts remain unchanged.", + "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. Historical removed assertion inventory: []" ], "demotionRule": "Keep experimental or demote to protection none if decorative title frequency changes paired client-event or session-tabs publication volume after convergence, a semantic fact is dropped, paired first paint is blank, passive work reconnects after manual disconnect, a capable hidden paired manager survives ordinary parking, a legacy hidden pane schedules raw xterm output or bypasses its fallback, reveal loses requested bounded history or live PTY identity, retained buffer cells do not fall, or either headed/headless scenario flakes." }, @@ -12711,7 +12603,7 @@ "providers": ["ssh", "remote-runtime", "wsl"], "coveredPlatforms": ["macos"], "coveredProviders": ["ssh", "remote-runtime"], - "coverageNotes": "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): 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): Deterministic renderer coverage proves retained native and runtime SSH payloads are admitted through production routes only with valid complete authority, stale cleanup cannot unregister a replacement runtime terminal, direct SSH Git and folder panes keep their PTY bindings through disconnect and reconnect, one authority chain stops after two automatic attempts even when each timeout exceeds the rolling window, rejected acknowledgements mutate no store maps, and one shared exact attempt admits every concurrent split-pane spawn and reattach while preserving the first PTY as the tab fallback. A later sibling failure rotates the tab once, stale callbacks from the prior attempt mutate no state, split remount activity suppression is counted per leaf, primary PTY exit promotes a bound survivor or preserves an empty continuation gap for a late sibling, and primary, non-primary, or null-PTY detach preserves exact authority on both resulting tabs. Intentional pane disposal cancels its settlement timer without breaking StrictMode remount timeout ownership. A mounted remote-runtime terminal survives repeated transport partitions without changing PTY identity. A real encrypted-WebSocket oracle proves a successful reachability probe can replace a pre-ready shared-control socket without rejecting or duplicating the waiting RPC. Direct SSH coordinator tests cover immediate terminal finalization, hydration correction, damping, bounded retry, and telemetry non-interference. Client/server heartbeat tests cover timer suspension, socket generations fence stale callbacks, cold restored-terminal attachment retries, cached pixels remain unhealthy until authoritative replay, automatic retries stop after one minute, manual reconnect preserves the PTY, and pane closure releases recovery UI state. Current-transport recovery clears every error that transport surfaced without clearing or displaying a sibling pane's errors, while transport suppression, pane retention, and the rendered surface are independently bounded. Current macOS Electron journeys against an ephemeral Linux Docker SSH target cover exact-authority repo/worktree hydration, live terminal recovery after disconnect/reconnect, and eager six-terminal remount after renderer reload. A Windows remote-runtime smoke covers reachability and PTY round-trip. Multi-target live fanout, paired-close, WSL, and patched live partition journeys remain gaps. The test-value audit narrowed resolved-worktree-execution-host.test.ts to cache reuse and invalidation. Its direct hydrated-worktree, missing-folder, restored-owner, host-stamp and empty-runtime selection alarms are lost. Other routing and recovery suites do not establish equivalent selector coverage; historical run commands and results remain historical. The test-value audit removes standalone unwritten/released renderer-authority and cross-scope request-count examples. The retained synthetic mirror/store/sidebar and pending-inventory checks do not establish real PTY byte ownership, SSH reconnection or equivalent replacement coverage; historical commands and results remain historical. The test-value audit removes direct join/label ledgers, immediate failure and negative-only stale/removal/partial-authority examples, plus the standalone retained/live event projection. Retained synthetic store/hydration compositions do not establish real SSH continuity, remote authority delivery or equivalent replacement alarms. Historical commands and results remain historical. The renderer scheduling fixture retains in-flight coalescing only. Its immediate publication and negative stale-SSH-cleanup call ledgers were removed; the retained SSH recovery evidence does not replace those alarms. The accepted-handle-events observer suite is removed, including its scoped listener and settlement-release assertions. These observer alarms are lost; retained SSH/transport journeys are not equivalent replacement coverage. The test-value audit removed repeated remote-handle rotation inventories and snapshot-first pending-restart migration examples from terminal-pty-identity-replacement.test.ts, plus direct foreground metadata setter, confirmation, evidence-enum, and prefix/worktree cleanup examples. Those rotation, pending-restart, snapshot-first migration, and foreground scalar-update alarms are lost. The retained replacement case observes pane and tab identities in one subscriber publication; it does not establish equivalent migration, process-evidence, or input-routing coverage. Historical commands and results remain historical. The test-value audit narrows terminals-hydration.test.ts to a real reconnect batch with one measured store publication and hydration-seeded history consumed by a subsequent visit/Back check. Direct split-layout, runtime/folder/floating restoration, dead-leaf focus, cold/known-absent SSH deferral, title, default-tab marker, navigation initialization, and hydrationSucceeded scalar examples are removed; those specific alarms are lost. No actual target-removal transition, live SSH connection, session writer, or persisted restart is exercised by the retained cases. The old target-scoped/snapshot assertion references are retired because the current file does not contain those oracles. Other suites and historical runs do not establish equivalent replacement coverage. Audit1299 removes tests/e2e/terminal-cold-activation-deferral.spec.ts. Private-state/work-budget/assembled-fixture alarms are intentionally lost; retained suites are not equivalent replacement coverage. No new runtime/platform evidence is claimed. 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. 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. Audit1342: src/renderer/src/hooks/ipc-events/direct-ssh-connect-reply-routing.test.ts removed. Initial and event-triggered partial-authority hydration fixture alarms disappear. src/renderer/src/components/terminal-pane/terminal-error-accumulation.test.ts removed. Multiline dedup/storm bounds, resource classification/collision, scoped clear/reconnect strip and1000-pane map count alarms disappear. No equivalent replacement coverage is claimed.", + "coverageNotes": "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): 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): Deterministic renderer coverage proves retained native and runtime SSH payloads are admitted through production routes only with valid complete authority, stale cleanup cannot unregister a replacement runtime terminal, direct SSH Git and folder panes keep their PTY bindings through disconnect and reconnect, one authority chain stops after two automatic attempts even when each timeout exceeds the rolling window, rejected acknowledgements mutate no store maps, and one shared exact attempt admits every concurrent split-pane spawn and reattach while preserving the first PTY as the tab fallback. A later sibling failure rotates the tab once, stale callbacks from the prior attempt mutate no state, split remount activity suppression is counted per leaf, primary PTY exit promotes a bound survivor or preserves an empty continuation gap for a late sibling, and primary, non-primary, or null-PTY detach preserves exact authority on both resulting tabs. Intentional pane disposal cancels its settlement timer without breaking StrictMode remount timeout ownership. A mounted remote-runtime terminal survives repeated transport partitions without changing PTY identity. A real encrypted-WebSocket oracle proves a successful reachability probe can replace a pre-ready shared-control socket without rejecting or duplicating the waiting RPC. Direct SSH coordinator tests cover immediate terminal finalization, hydration correction, damping, bounded retry, and telemetry non-interference. Client/server heartbeat tests cover timer suspension, socket generations fence stale callbacks, cold restored-terminal attachment retries, cached pixels remain unhealthy until authoritative replay, automatic retries stop after one minute, manual reconnect preserves the PTY, and pane closure releases recovery UI state. Current-transport recovery clears every error that transport surfaced without clearing or displaying a sibling pane's errors, while transport suppression, pane retention, and the rendered surface are independently bounded. Current macOS Electron journeys against an ephemeral Linux Docker SSH target cover exact-authority repo/worktree hydration, live terminal recovery after disconnect/reconnect, and eager six-terminal remount after renderer reload. A Windows remote-runtime smoke covers reachability and PTY round-trip. Multi-target live fanout, paired-close, WSL, and patched live partition journeys remain gaps. The test-value audit narrowed resolved-worktree-execution-host.test.ts to cache reuse and invalidation. Its direct hydrated-worktree, missing-folder, restored-owner, host-stamp and empty-runtime selection alarms are lost. Other routing and recovery suites do not establish equivalent selector coverage; historical run commands and results remain historical. The test-value audit removes standalone unwritten/released renderer-authority and cross-scope request-count examples. The retained synthetic mirror/store/sidebar and pending-inventory checks do not establish real PTY byte ownership, SSH reconnection or equivalent replacement coverage; historical commands and results remain historical. The test-value audit removes direct join/label ledgers, immediate failure and negative-only stale/removal/partial-authority examples, plus the standalone retained/live event projection. Retained synthetic store/hydration compositions do not establish real SSH continuity, remote authority delivery or equivalent replacement alarms. Historical commands and results remain historical. The renderer scheduling fixture retains in-flight coalescing only. Its immediate publication and negative stale-SSH-cleanup call ledgers were removed; the retained SSH recovery evidence does not replace those alarms. The accepted-handle-events observer suite is removed, including its scoped listener and settlement-release assertions. These observer alarms are lost; retained SSH/transport journeys are not equivalent replacement coverage. The test-value audit removed repeated remote-handle rotation inventories and snapshot-first pending-restart migration examples from terminal-pty-identity-replacement.test.ts, plus direct foreground metadata setter, confirmation, evidence-enum, and prefix/worktree cleanup examples. Those rotation, pending-restart, snapshot-first migration, and foreground scalar-update alarms are lost. The retained replacement case observes pane and tab identities in one subscriber publication; it does not establish equivalent migration, process-evidence, or input-routing coverage. Historical commands and results remain historical. The test-value audit narrows terminals-hydration.test.ts to a real reconnect batch with one measured store publication and hydration-seeded history consumed by a subsequent visit/Back check. Direct split-layout, runtime/folder/floating restoration, dead-leaf focus, cold/known-absent SSH deferral, title, default-tab marker, navigation initialization, and hydrationSucceeded scalar examples are removed; those specific alarms are lost. No actual target-removal transition, live SSH connection, session writer, or persisted restart is exercised by the retained cases. The old target-scoped/snapshot assertion references are retired because the current file does not contain those oracles. Other suites and historical runs do not establish equivalent replacement coverage. Audit1299 removes tests/e2e/terminal-cold-activation-deferral.spec.ts. Private-state/work-budget/assembled-fixture alarms are intentionally lost; retained suites are not equivalent replacement coverage. No new runtime/platform evidence is claimed. 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. 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. Audit1342: src/renderer/src/hooks/ipc-events/direct-ssh-connect-reply-routing.test.ts removed. Initial and event-triggered partial-authority hydration fixture alarms disappear. src/renderer/src/components/terminal-pane/terminal-error-accumulation.test.ts removed. Multiline dedup/storm bounds, resource classification/collision, scoped clear/reconnect strip and1000-pane map count alarms disappear. No equivalent replacement coverage is claimed. Audit1396: src/renderer/src/components/terminal-pane/pty-connection-pending-spawn.spec.ts reduced. Lose standalone debug-state retention/log silence alarm. React StrictMode is not mounted; bindings manually simulate remount over mocked transport. 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.", "motivatingLinks": [ "https://linear.app/stably/issue/STA-3107", "https://github.com/stablyai/orca/pull/12664", @@ -13055,7 +12947,8 @@ "Audit1342 retires this complete dated receipt because a source in its selector was deleted or reduced. It does not validate the reduced selector. Original receipt verbatim:\n{\n \"date\": \"2026-08-29\",\n \"runner\": \"local\",\n \"platform\": \"macos\",\n \"command\": \"pnpm exec vitest run --config config/vitest.config.ts src/renderer/src/components/terminal-pane/terminal-error-accumulation.test.ts src/renderer/src/components/terminal-pane/remote-runtime-pty-transport-attach-subscription.test.ts --reporter=dot\",\n \"result\": \"passed\",\n \"durationSeconds\": 4.3,\n \"summary\": \"Two focused files and 26 tests passed, including pane-scoped rendering and recovery, sibling-error retention, split-close pane-id churn cleanup, current-transport multi-error clearing, dismissal re-admission, multi-line deduplication, and 8-message/24-line/4,000-character bounds.\",\n \"currentCommand\": \"pnpm exec vitest run --config config/vitest.config.ts src/renderer/src/components/terminal-pane/terminal-error-accumulation.test.ts src/renderer/src/components/terminal-pane/remote-runtime-pty-transport-attach-subscription.test.ts --reporter=dot\"\n }", "Audit1342 retires this complete dated receipt because a source in its selector was deleted or reduced. It does not validate the reduced selector. Original receipt verbatim:\n{\n \"date\": \"2026-10-15\",\n \"runner\": \"local\",\n \"platform\": \"macos\",\n \"command\": \"pnpm exec vitest run --config config/vitest.config.ts src/renderer/src/hooks/ipc-events/direct-ssh-connect-reply-routing.test.ts src/renderer/src/store/slices/terminals-hydration.test.ts src/renderer/src/store/slices/repos-ssh-host-reconciliation.test.ts src/renderer/src/hooks/direct-ssh-state-routing.test.ts src/renderer/src/components/terminal-pane/pty-connection-pending-spawn.test.ts src/renderer/src/components/terminal-pane/terminal-pane-tab-detach.test.ts --reporter=dot\",\n \"result\": \"passed\",\n \"durationSeconds\": 3.3,\n \"summary\": \"Six direct-SSH files and 57 tests passed, including SSH tab bindings kept through connected pushes, disconnects and errors, partial-authority reconciliation, hydration, split-pane and detach contracts.\",\n \"currentCommand\": \"pnpm exec vitest run --config config/vitest.config.ts src/renderer/src/hooks/ipc-events/direct-ssh-connect-reply-routing.test.ts src/renderer/src/store/slices/terminals-hydration.test.ts src/renderer/src/store/slices/repos-ssh-host-reconciliation.test.ts src/renderer/src/components/terminal-pane/pty-connection-spawn-and-startup.test.ts src/renderer/src/components/terminal-pane/terminal-pane-tab-detach.test.ts --reporter=dot\"\n }", "Audit1342 historical platform/provider scope before source changes: {\"coveredPlatforms\":[\"macos\"],\"coveredProviders\":[\"ssh\",\"remote-runtime\"]}. Remaining provider scope applies only to unchanged-source receipts.", - "Audit1342 historical assertion inventory for deleted/reduced sources; not current validated assertions:\n[\n {\n \"file\": \"src/renderer/src/components/terminal-pane/terminal-error-accumulation.test.ts\",\n \"assertions\": [\n \"only the active pane's errors render and matching-pane recovery preserves sibling and unrelated messages\",\n \"individual messages and the joined surface remain within 24 lines and 4,000 characters\",\n \"a distinct error storm retains only the newest eight messages for one pane while whole-message dedup remains intact\",\n \"repeated split-pane close churn releases every closed pane id while preserving the live sibling\"\n ]\n },\n {\n \"file\": \"src/renderer/src/hooks/ipc-events/direct-ssh-connect-reply-routing.test.ts\",\n \"assertions\": [\n \"connected pushes, disconnects, and errors keep an SSH tab's PTY binding; loss of contact is not exit\"\n ]\n }\n]" + "Audit1342 historical assertion inventory for deleted/reduced sources; not current validated assertions:\n[\n {\n \"file\": \"src/renderer/src/components/terminal-pane/terminal-error-accumulation.test.ts\",\n \"assertions\": [\n \"only the active pane's errors render and matching-pane recovery preserves sibling and unrelated messages\",\n \"individual messages and the joined surface remain within 24 lines and 4,000 characters\",\n \"a distinct error storm retains only the newest eight messages for one pane while whole-message dedup remains intact\",\n \"repeated split-pane close churn releases every closed pane id while preserving the live sibling\"\n ]\n },\n {\n \"file\": \"src/renderer/src/hooks/ipc-events/direct-ssh-connect-reply-routing.test.ts\",\n \"assertions\": [\n \"connected pushes, disconnects, and errors keep an SSH tab's PTY binding; loss of contact is not exit\"\n ]\n }\n]", + "Audit1396: src/renderer/src/components/terminal-pane/pty-connection-pending-spawn.spec.ts reduced. Lose standalone debug-state retention/log silence alarm. React StrictMode is not mounted; bindings manually simulate remount over mocked transport. No equivalent replacement coverage is claimed. Existing exact primary-selector receipts remain unchanged." ], "demotionRule": "Cannot promote if provider failure can close panes or if the oracle is screenshot-only." }, @@ -13318,7 +13211,7 @@ "providers": ["local", "daemon", "ssh", "remote-runtime"], "coveredPlatforms": ["macos", "linux"], "coveredProviders": [], - "coverageNotes": "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.", + "coverageNotes": "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.", "motivatingLinks": [ "https://github.com/stablyai/orca/pull/6699", "https://github.com/stablyai/orca/pull/6682", @@ -13329,7 +13222,7 @@ "https://github.com/stablyai/orca/pull/17170" ], "invariant": "Composition, native text forwarding, synthetic input, paste, and platform keyboard bypass paths must not send preedit/control bytes before commit and must commit text exactly once to the intended PTY. A live preedit must stay visible across an IME-resumed transaction, preserve every committed row-tail cell hidden by its opaque overlay, mask only a structurally verified Codex or Claude placeholder and never a row carrying typed text, keep the themed insertion caret and native candidate anchor inside the terminal at the final column, and clean up without stale overlay children or ownership classes. Layout-native Option text must preserve negotiated CSI-u alternate/associated fields and lock modifiers, distinguish repeat from press, and resolve releases with the press's primary identity plus the actual keyup modifiers without exposing a partial or stale layout refresh. An in-focus macOS input-source change must invalidate both renderer authorities before the next key can use the old source. When the snapshot source proves kitty state, macOS Preview commits must use the application's effective flags after the sequenced snapshot and every proven replay/live transition, and a claimed press delivered under report_event_types must produce exactly one matching release regardless of whether keyup arrives before or after insertText and regardless of xterm's defensive internal reset.", - "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 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): 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): Exact outbound bytes from the REAL native-text forwarder behind installPreviewImeBridge after initial snapshot restore, replacement resync, live transitions, and both keydown->insertText->keyup and keydown->keyup->insertText orderings; a wiring-only mock is supporting evidence, not the oracle. The current renderer-unit slice asserts native text commits route to the intended PTY once, composition/preedit bookkeeping does not leak premature text, resumed Windows event traces reactivate the visible preedit, an opaque mid-line overlay reproduces the committed row tail it covers, arbitrary DIM output remains visible, and final-column layout keeps both caret and textarea candidate anchor within the available screen width. The retained candidate-anchor slice checks layout-read caching, deferred timer coalescing, recognized-screen follow-ups and refit refresh with deterministic DOM fixtures. A Codex frame whose prompt row is bold and whose dimmed model footer sits below a blank gap acquires the mask class while composing; the same placeholder text without that footer, a framed-versus-unframed Claude prompt, a row of arbitrary dimmed output, and a row carrying typed text do not. The class is gone after composition end, after a blur that is not followed by a session end, and after disposal. Rendered Electron/CDP geometry independently proves the overlay reproduces every committed cell it hides, that a real Codex placeholder at column 2 is masked rather than repeated, and that the final-column caret, preedit, and textarea right edges all land inside the screen. Input-source classification handles synthetic/native paths, paste/runtime forwarding avoids duplicate terminal payloads for covered fixtures, and real fcitx4/Sogou captures send only the committed candidate for Space/digit picks while ordinary typing, literal Space and digits stay available. A main/preload/renderer notification chain proves every live window invalidates immediately, the old native read settles before a coalesced refresh signal, and older refreshes are generation-fenced. The Electron/CDP live-PTY repro verifies Sogou-style Space and digit selectors submit only the committed Chinese text. Real legacy IME commit preservation, native candidate-window placement, and the full CJK/Vietnamese/Arabic/JIS-yen matrix run in follow-up platform soak where automation is possible.", + "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: 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): 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): Exact outbound bytes from the REAL native-text forwarder behind installPreviewImeBridge after initial snapshot restore, replacement resync, live transitions, and both keydown->insertText->keyup and keydown->keyup->insertText orderings; a wiring-only mock is supporting evidence, not the oracle. The current renderer-unit slice asserts native text commits route to the intended PTY once, composition/preedit bookkeeping does not leak premature text, resumed Windows event traces reactivate the visible preedit, an opaque mid-line overlay reproduces the committed row tail it covers, arbitrary DIM output remains visible, and final-column layout keeps both caret and textarea candidate anchor within the available screen width. The retained candidate-anchor slice checks layout-read caching, deferred timer coalescing, recognized-screen follow-ups and refit refresh with deterministic DOM fixtures. A Codex frame whose prompt row is bold and whose dimmed model footer sits below a blank gap acquires the mask class while composing; the same placeholder text without that footer, a framed-versus-unframed Claude prompt, a row of arbitrary dimmed output, and a row carrying typed text do not. The class is gone after composition end, after a blur that is not followed by a session end, and after disposal. Rendered Electron/CDP geometry independently proves the overlay reproduces every committed cell it hides, that a real Codex placeholder at column 2 is masked rather than repeated, and that the final-column caret, preedit, and textarea right edges all land inside the screen. Input-source classification handles synthetic/native paths, paste/runtime forwarding avoids duplicate terminal payloads for covered fixtures, and real fcitx4/Sogou captures send only the committed candidate for Space/digit picks while ordinary typing, literal Space and digits stay available. A main/preload/renderer notification chain proves every live window invalidates immediately, the old native read settles before a coalesced refresh signal, and older refreshes are generation-fenced. The Electron/CDP live-PTY repro verifies Sogou-style Space and digit selectors submit only the committed Chinese text. Real legacy IME commit preservation, native candidate-window placement, and the full CJK/Vietnamese/Arabic/JIS-yen matrix run in follow-up platform soak where automation is possible.", "commands": [ "pnpm exec vitest run --config config/vitest.config.ts src/renderer/src/components/terminal-pane/terminal-ime-native-text-forwarder.test.ts src/renderer/src/components/terminal-pane/terminal-ime-substituted-text-commit.test.ts src/renderer/src/components/terminal-pane/terminal-ime-macos-keybinding-dict-trace.test.ts src/renderer/src/components/terminal-pane/terminal-paste-runtime.test.ts src/renderer/src/components/terminal-pane/terminal-ime-composition-tracker.test.ts src/renderer/src/components/terminal-pane/terminal-ime-candidate-key-release-guard.test.ts src/renderer/src/components/terminal-pane/xterm-bypass-policy-non-mac.test.ts src/renderer/src/components/terminal-pane/xterm-bypass-policy.test.ts", "pnpm exec vitest run --config config/vitest.config.ts src/renderer/src/components/terminal-pane/terminal-ime-linux-fcitx4-trace-replay.test.ts", @@ -13462,12 +13355,6 @@ "candidate Space/digit selectors are suppressed only while candidate guards are active" ] }, - { - "file": "src/renderer/src/components/terminal-pane/xterm-bypass-policy.test.ts", - "assertions": [ - "macOS standalone Process key behavior and composition-owned key suppression stay intact" - ] - }, { "file": "tests/e2e/terminal-option-composed-ascii.spec.ts", "assertions": [ @@ -13687,7 +13574,8 @@ "Audit1361 retires this complete dated receipt because a source in its selector was deleted or reduced. It does not validate the reduced selector. Original receipt verbatim:\n{\n \"date\": \"2026-08-16\",\n \"runner\": \"local\",\n \"platform\": \"macos\",\n \"command\": \"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/preview-terminal-snapshot-replay.test.ts src/renderer/src/components/dashboard-popout/AgentTerminalPreview.test.tsx src/renderer/src/components/dashboard-popout/AgentTerminalPreview.option-dead-key.test.tsx src/shared/terminal-kitty-keyboard-mode-tracker.test.ts src/main/ipc/terminal-preview.test.ts src/main/ipc/terminal-preview-output-stream.test.ts src/renderer/src/runtime/remote-runtime-terminal-snapshot-kitty-flags.test.ts src/main/daemon/daemon-pty-adapter.test.ts\",\n \"currentCommand\": \"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/preview-terminal-snapshot-replay.test.ts src/renderer/src/components/dashboard-popout/AgentTerminalPreview.test.tsx src/renderer/src/components/dashboard-popout/AgentTerminalPreview.option-dead-key.test.tsx src/shared/terminal/protocol/input-modes/terminal-kitty-keyboard-mode-tracker.test.ts src/main/ipc/terminal-preview.test.ts src/main/ipc/terminal-preview-output-stream.test.ts src/main/daemon/daemon-pty-adapter.test.ts\",\n \"result\": \"passed\",\n \"durationSeconds\": 4.3,\n \"summary\": \"9 test files and 172 tests passed, covering the Preview kitty slice: real forwarder bytes at snapshot-proven flags, press/release pairing across both keyup/insertText orderings, native and configured-side dead-key release ownership, and flag provenance where a proven 0, a proven non-zero, and an absent field stay three distinct facts — asserted on the daemon snapshot and on a host-pushed remote snapshot that carries the flags with their sequence. The emitter-side rule that a remote SnapshotStart withholds flags without a seq has no direct assertion yet; the client-side gate is what these files pin.\"\n }", "Audit1361 historical platform/provider scope before source changes: {\"coveredPlatforms\":[\"macos\",\"linux\"],\"coveredProviders\":[]}. Remaining provider scope applies only to unchanged-source receipts.", "Audit1361 historical assertion inventory for deleted/reduced sources; not current validated assertions:\n[\n {\n \"file\": \"src/renderer/src/components/dashboard-popout/preview-terminal-snapshot-replay.test.ts\",\n \"assertions\": [\n \"a Preview opened on a snapshot carrying flags 8 adopts 8 before any live output\",\n \"a replacement snapshot whose ANSI carries no kitty bytes keeps its proven flags, and a proven 0 clears an earlier 8\",\n \"a missing field leaves the mirror unproven at the raw-text fallback and a later live negotiation still upgrades it\",\n \"restored flags land on the screen the snapshot scan selected\",\n \"a proven post-snapshot suffix advances the stack while redelivered bytes apply idempotently\"\n ]\n }\n]", - "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. Historical removed assertion inventory: [{\"file\":\"src/renderer/src/lib/pane-manager/terminal-ime-candidate-anchor.test.ts\",\"assertions\":[\"keeps forced layout at one read per composition across a Hangul burst\",\"coalesces the deferred Cursor Agent re-apply to one timer per burst\",\"keeps typed follow-ups anchored after recognizing the initial Cursor Agent screen\",\"refreshes the deferred metrics and anchor after a refit\"]},{\"file\":\"src/renderer/src/components/terminal-pane/terminal-kitty-csi-u-encoding.test.ts\",\"assertions\":[\"report-all Option events encode exact alternate, repeat, release, and associated-text fields\",\"report-all native commits preserve unresolved layout characters and distinguish functional from printable numpad identities\",\"associated text omits forbidden control codepoints\",\"release events omit associated text and sparse PC-101 alternates retain their parameter position\",\"CapsLock and NumLock occupy their protocol modifier bits\"]}]" + "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. Historical removed assertion inventory: [{\"file\":\"src/renderer/src/lib/pane-manager/terminal-ime-candidate-anchor.test.ts\",\"assertions\":[\"keeps forced layout at one read per composition across a Hangul burst\",\"coalesces the deferred Cursor Agent re-apply to one timer per burst\",\"keeps typed follow-ups anchored after recognizing the initial Cursor Agent screen\",\"refreshes the deferred metrics and anchor after a refit\"]},{\"file\":\"src/renderer/src/components/terminal-pane/terminal-kitty-csi-u-encoding.test.ts\",\"assertions\":[\"report-all Option events encode exact alternate, repeat, release, and associated-text fields\",\"report-all native commits preserve unresolved layout characters and distinguish functional from printable numpad identities\",\"associated text omits forbidden control codepoints\",\"release events omit associated text and sparse PC-101 alternates retain their parameter position\",\"CapsLock and NumLock occupy their protocol modifier bits\"]}]", + "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. Historical removed assertion inventory: [{\"file\":\"src/renderer/src/components/terminal-pane/xterm-bypass-policy.test.ts\",\"assertions\":[\"macOS standalone Process key behavior and composition-owned key suppression stay intact\"]}]" ], "demotionRule": "Cannot promote if success is based only on DOM text without PTY byte/cell evidence." }, @@ -15025,7 +14913,7 @@ "providers": ["local", "daemon", "ssh", "wsl", "remote-runtime"], "coveredPlatforms": ["macos"], "coveredProviders": [], - "coverageNotes": "Audit1379 changes src/main/runtime/orca-runtime-process-incarnation-liveness.test.ts (7 authored registrations removed; 0 retained). Intentional losses: Loses supplied host lists and record states mapped to liveness enums and routing counts; no live/dead process evidence. No equivalent replacement coverage is claimed. Remaining exact receipts validate only their unchanged selectors. Historical coverage account: Only Keep open keeps a worker; a phone or desktop names it by handle, pane or session, and older clients' typing reports record nothing. Host byte lanes perform zero orchestration SQL work; local and injected SSH Keep open tests fence release. Deterministic service tests cover release-versus-reuse ordering, transactional retain and takeover cancellation, exact host/pane/process identity, dead external/user-owned/transferred/stopped/abandoned reconciliation, host-partition persistence and legacy retirement replay with an absent web-terminal layout map, conservative unknown provider and legacy metadata handling, immutable transcript and bounded terminal archives, mutation restart, reset cleanup, replay idempotency, and 50-resource accounting. A macOS Electron journey invokes the freshly compiled worker-release CLI after the worker process disappears, then independently checks released SQLite state and coordinator liveness. Injected inventories cover local and SSH provider routing; live SSH, WSL, Windows, paired-runtime, and provider-close lost-ack journeys remain explicit gaps.", + "coverageNotes": "Audit1379 changes src/main/runtime/orca-runtime-process-incarnation-liveness.test.ts (7 authored registrations removed; 0 retained). Intentional losses: Loses supplied host lists and record states mapped to liveness enums and routing counts; no live/dead process evidence. No equivalent replacement coverage is claimed. Remaining exact receipts validate only their unchanged selectors. Historical coverage account: Only Keep open keeps a worker; a phone or desktop names it by handle, pane or session, and older clients' typing reports record nothing. Host byte lanes perform zero orchestration SQL work; local and injected SSH Keep open tests fence release. Deterministic service tests cover release-versus-reuse ordering, transactional retain and takeover cancellation, exact host/pane/process identity, dead external/user-owned/transferred/stopped/abandoned reconciliation, host-partition persistence and legacy retirement replay with an absent web-terminal layout map, conservative unknown provider and legacy metadata handling, immutable transcript and bounded terminal archives, mutation restart, reset cleanup, replay idempotency, and 50-resource accounting. A macOS Electron journey invokes the freshly compiled worker-release CLI after the worker process disappears, then independently checks released SQLite state and coordinator liveness. Injected inventories cover local and SSH provider routing; live SSH, WSL, Windows, paired-runtime, and provider-close lost-ack journeys remain explicit gaps. Audit1396: src/main/runtime/rpc/methods/orchestration/worker/worker-release-mobile-report.test.ts reduced. Retires unary/stream no-database-work call counts and three mobile RPC allowlist membership checks. Ancillary input delivery assertions in the budget registration intentionally lost without equivalence 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/12355", "https://github.com/stablyai/orca/issues/13860", @@ -15034,7 +14922,7 @@ "STA-5122" ], "invariant": "A settled Dispatch may close only its one coordinator-created terminal lease. Explicit reuse, Keep open, retain, identity or host change, ambiguity, and another resource for the same exact host/pane/process must fence closure. Once the authoritative owning provider positively excludes the resource's exact immutable process incarnation, even an external, user-owned, or transferred dead resource must converge to released without any process close. Unknown host scope, missing incarnation metadata, or unavailable inventory must remain retained. Exact terminal-close persistence must settle when a host partition omits renderer-owned layout state. Output preservation and the requested-to-releasing transition are atomic, archives remain readable without the provider file, retries resume idempotently, and orchestration reset removes archive and authority state.", - "oracle": "Surviving unchanged-source receipts are the current oracle; changed-source assertions await validation. Historical oracle account: Record release intent for a settled owner, attempt exact reuse before close, and require worker-start to fail with terminal_release_in_progress while the terminal stays open; then release the original owner exactly once. Race retain against a controlled archive promise and require no committed archive or close. Rebase a closed web-terminal host partition without terminalLayoutsByTabId and require the persistence write to complete while preserving host-authoritative membership; replay a valid legacy retirement under the same omission and require exact membership removal plus revision advancement. For retained external, user-owned, transferred, stopped, and abandoned resources, run one fresh inventory against the exact local/WSL or SSH provider: an exact live incarnation and every unknown inventory shape stay retained, while positive absence atomically sets ownership_state and release_state to released with processAction none and zero closeTerminal calls. Change host or process identity and inject duplicate resource evidence to require retention. Freeze a structured transcript, delete its source file, and require archived worker-read to return the same bounded redacted messages. Restart a pending mutation, reset orchestration state, and create 50 resources while asserting replay convergence, zero orphan rows, two-query worker listing, and no unrelated close.", + "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: Surviving unchanged-source receipts are the current oracle; changed-source assertions await validation. Historical oracle account: Record release intent for a settled owner, attempt exact reuse before close, and require worker-start to fail with terminal_release_in_progress while the terminal stays open; then release the original owner exactly once. Race retain against a controlled archive promise and require no committed archive or close. Rebase a closed web-terminal host partition without terminalLayoutsByTabId and require the persistence write to complete while preserving host-authoritative membership; replay a valid legacy retirement under the same omission and require exact membership removal plus revision advancement. For retained external, user-owned, transferred, stopped, and abandoned resources, run one fresh inventory against the exact local/WSL or SSH provider: an exact live incarnation and every unknown inventory shape stay retained, while positive absence atomically sets ownership_state and release_state to released with processAction none and zero closeTerminal calls. Change host or process identity and inject duplicate resource evidence to require retention. Freeze a structured transcript, delete its source file, and require archived worker-read to return the same bounded redacted messages. Restart a pending mutation, reset orchestration state, and create 50 resources while asserting replay convergence, zero orphan rows, two-query worker listing, and no unrelated close.", "commands": [ "ORCA_BACKGROUND_LAUNCH=1 pnpm exec vitest run --config config/vitest.config.ts src/main/runtime/rpc/methods/orchestration/worker/worker-release-mobile-report.test.ts src/main/runtime/orca-runtime-terminal-handle-incarnation.test.ts", "pnpm exec vitest run --config config/vitest.config.ts src/main/runtime/runtime-terminal-inventory-contracts.test.ts src/main/runtime/mobile-session-terminal-persistence-retirement.test.ts src/main/runtime/rpc/methods/orchestration/worker/worker-release.test.ts src/main/runtime/rpc/methods/orchestration/worker/worker-release-recovery.test.ts src/main/runtime/rpc/orchestration-mutation-ledger.test.ts src/main/runtime/orchestration/worker-transcript-read.test.ts --reporter=dot", @@ -15056,13 +14944,6 @@ "tests/e2e/orchestration-worker-settlement-release-cli.spec.ts" ], "assertionRefs": [ - { - "file": "src/main/runtime/rpc/methods/orchestration/worker/worker-release-mobile-report.test.ts", - "assertions": [ - "a handle-addressed phone Keep open fences %s worker release", - "mobile %s bytes do no orchestration database work" - ] - }, { "file": "src/main/runtime/runtime-terminal-inventory-contracts.test.ts", "assertions": [ @@ -15171,7 +15052,8 @@ "Audit1379 retires this complete actual-current dated receipt because its selector includes a deleted or reduced source. It does not validate the reduced selector. Original receipt verbatim:\n{\n \"date\": \"2026-08-27\",\n \"runner\": \"local\",\n \"platform\": \"macos\",\n \"command\": \"pnpm exec vitest run --config config/vitest.config.ts src/main/runtime/orca-runtime-process-incarnation-liveness.test.ts src/main/runtime/mobile-session-terminal-persistence-retirement.test.ts src/main/runtime/rpc/methods/orchestration/worker/worker-release.test.ts src/main/runtime/rpc/methods/orchestration/worker/worker-release-recovery.test.ts src/main/runtime/rpc/orchestration-mutation-ledger.test.ts src/main/runtime/orchestration/worker-transcript-read.test.ts --reporter=dot\",\n \"result\": \"passed\",\n \"durationSeconds\": 8.78,\n \"summary\": \"Seven deterministic files passed 78 tests, including red-green host-partition rebase and legacy-retirement regressions with an absent web-terminal layout map plus exact lease, reuse, takeover, recovery, restart, archive, and accounting contracts.\"\n }", "Audit1379 retires this complete actual-current dated receipt because its selector includes a deleted or reduced source. It does not validate the reduced selector. Original receipt verbatim:\n{\n \"date\": \"2026-08-11\",\n \"runner\": \"local\",\n \"platform\": \"macos\",\n \"command\": \"pnpm exec vitest run --config config/vitest.config.ts src/main/runtime/orca-runtime-process-incarnation-liveness.test.ts src/main/runtime/rpc/methods/orchestration/worker/worker-release.test.ts src/main/runtime/rpc/methods/orchestration/worker/worker-release-recovery.test.ts src/main/runtime/rpc/orchestration-mutation-ledger.test.ts src/main/runtime/orchestration/worker-transcript-read.test.ts --reporter=dot\",\n \"result\": \"passed\",\n \"durationSeconds\": 4.98,\n \"summary\": \"Six focused files passed 67 tests on the rebased candidate, covering dead external, user-owned, stopped, abandoned, and transferred reconciliation; exact local/WSL/SSH provider routing; malformed, missing, and unavailable inventory retention; zero process closes; existing lease, archive, recovery, mutation, and renderer-input contracts.\"\n }", "Audit1379 historical platform/provider scope: {\"coveredPlatforms\":[\"macos\"],\"coveredProviders\":[\"local\",\"ssh\"]}. Remaining evidence applies to unchanged-source commands only.", - "Audit1379 historical assertion inventory for reduced/deleted sources:\n[\n {\n \"file\": \"src/main/runtime/orca-runtime-process-incarnation-liveness.test.ts\",\n \"assertions\": [\n \"classifies only an exact incarnation as live on its owning provider\",\n \"keeps missing or malformed identity and unavailable inventory unknown\",\n \"does not inspect an unproven host scope\",\n \"uses the local provider inventory for local and WSL scopes\"\n ]\n }\n]" + "Audit1379 historical assertion inventory for reduced/deleted sources:\n[\n {\n \"file\": \"src/main/runtime/orca-runtime-process-incarnation-liveness.test.ts\",\n \"assertions\": [\n \"classifies only an exact incarnation as live on its owning provider\",\n \"keeps missing or malformed identity and unavailable inventory unknown\",\n \"does not inspect an unproven host scope\",\n \"uses the local provider inventory for local and WSL scopes\"\n ]\n }\n]", + "Audit1396: src/main/runtime/rpc/methods/orchestration/worker/worker-release-mobile-report.test.ts reduced. Retires unary/stream no-database-work call counts and three mobile RPC allowlist membership checks. Ancillary input delivery assertions in the budget registration intentionally lost without equivalence claim. No equivalent replacement coverage is claimed. Historical removed assertion inventory: [{\"file\":\"src/main/runtime/rpc/methods/orchestration/worker/worker-release-mobile-report.test.ts\",\"assertions\":[\"a handle-addressed phone Keep open fences %s worker release\",\"mobile %s bytes do no orchestration database work\"]}]" ], "demotionRule": "Keep experimental or demote if release can overlap exact reuse, close a conflicting lease, lose immutable output, retain reset archives, fan out per worker, or any focused ordering test flakes without a product or harness defect." }, @@ -18723,7 +18605,7 @@ "providers": ["local", "remote-runtime", "ssh", "wsl"], "coveredPlatforms": ["macos"], "coveredProviders": ["local"], - "coverageNotes": "The owner-boundary contract injects an isolated BrowserManager, offscreen WebContents, and AgentBrowserBridge. It proves explicit close removes the page from command routing before awaiting exact named-session retirement, runs retirement on unexpected renderer destruction, preserves unrelated pages, joins pending creation/retirement during shutdown, rejects command admission after terminal cleanup begins, retries Electron quit after a vetoed signal, preserves Windows shared-console graceful Ctrl-C, reserves the full renderer-acknowledgement and committed-teardown budgets before supervisor force-kill, bounds process-swap retirement to five seconds, and caps cleanup concurrency at four. A built-CLI macOS headless serve run used an isolated profile, folder workspace, socket directory, and port: exact PPID-1 helper/session identity disappeared within five seconds after page close and after Ctrl-C runtime shutdown, while an unrelated page survived close and reconnect. Linux cgroup and paired/SSH journeys remain gaps. Audit1379 changes src/main/browser/cdp/snapshots/snapshot-engine-iframe-sessions.test.ts (3 authored registrations removed; 0 retained); src/main/browser/client-hosts/file-transfers/browser-client-download-routing.test.ts (3 authored registrations removed; 4 retained). Intentional losses: Loses ref/string/Map.get-count projections; no real browser operation. Intentionally removes 3 authored registrations: 'cancels a client-hosted download whose owner cannot be resolved'; 'leaves ordinary browser guests on their desktop Downloads path'; 'stops routing to a composition once it starts closing'. Retained focused cases are not claimed as equivalent replacement for these losses. No equivalent replacement coverage is claimed. Existing exact primary-selector receipts and primary claims remain scoped to their unchanged sources; the broad optional selector has no new execution result. Audit1383: src/main/browser/guest-lifecycle/browser-guest-devtools-dock.test.ts reduced. Lose capture failure baseline and destroyed-host callback control. Fake Electron view models native lifecycle and custody; no native rendering/visible focus claimed. 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. Audit1390: src/main/browser/agent-browser/agent-browser-action-effects.test.ts reduced. No standalone baseline snapshot call-count alarm. src/main/browser/routing/partitions/browser-route-partition-stability.test.ts removed. Standalone assembled partition stability/separation/binding-count alarms across runtime/SSH/WSL/environment identity removed. No real browser cookie/session continuity or replacement equivalence is claimed. src/main/browser/element-grab/browser-grab-payload.test.ts reduced. Standalone annotation clamp/list-count/default preservation characterization removed. src/main/browser/cdp/input/guest-focused-frame-route.test.ts reduced. Attach-call count, default widget fallback and claimed detach-forgetting alarm removed; no actual Chromium frame/input delivery claimed. 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.", + "coverageNotes": "The owner-boundary contract injects an isolated BrowserManager, offscreen WebContents, and AgentBrowserBridge. It proves explicit close removes the page from command routing before awaiting exact named-session retirement, runs retirement on unexpected renderer destruction, preserves unrelated pages, joins pending creation/retirement during shutdown, rejects command admission after terminal cleanup begins, retries Electron quit after a vetoed signal, preserves Windows shared-console graceful Ctrl-C, reserves the full renderer-acknowledgement and committed-teardown budgets before supervisor force-kill, bounds process-swap retirement to five seconds, and caps cleanup concurrency at four. A built-CLI macOS headless serve run used an isolated profile, folder workspace, socket directory, and port: exact PPID-1 helper/session identity disappeared within five seconds after page close and after Ctrl-C runtime shutdown, while an unrelated page survived close and reconnect. Linux cgroup and paired/SSH journeys remain gaps. Audit1379 changes src/main/browser/cdp/snapshots/snapshot-engine-iframe-sessions.test.ts (3 authored registrations removed; 0 retained); src/main/browser/client-hosts/file-transfers/browser-client-download-routing.test.ts (3 authored registrations removed; 4 retained). Intentional losses: Loses ref/string/Map.get-count projections; no real browser operation. Intentionally removes 3 authored registrations: 'cancels a client-hosted download whose owner cannot be resolved'; 'leaves ordinary browser guests on their desktop Downloads path'; 'stops routing to a composition once it starts closing'. Retained focused cases are not claimed as equivalent replacement for these losses. No equivalent replacement coverage is claimed. Existing exact primary-selector receipts and primary claims remain scoped to their unchanged sources; the broad optional selector has no new execution result. Audit1383: src/main/browser/guest-lifecycle/browser-guest-devtools-dock.test.ts reduced. Lose capture failure baseline and destroyed-host callback control. Fake Electron view models native lifecycle and custody; no native rendering/visible focus claimed. 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. Audit1390: src/main/browser/agent-browser/agent-browser-action-effects.test.ts reduced. No standalone baseline snapshot call-count alarm. src/main/browser/routing/partitions/browser-route-partition-stability.test.ts removed. Standalone assembled partition stability/separation/binding-count alarms across runtime/SSH/WSL/environment identity removed. No real browser cookie/session continuity or replacement equivalence is claimed. src/main/browser/element-grab/browser-grab-payload.test.ts reduced. Standalone annotation clamp/list-count/default preservation characterization removed. src/main/browser/cdp/input/guest-focused-frame-route.test.ts reduced. Attach-call count, default widget fallback and claimed detach-forgetting alarm removed; no actual Chromium frame/input delivery claimed. 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/main/browser/network/egress/browser-route-webrtc-egress.electron.test.ts removed. Deletes entire one-registration native Electron baseline/protected UDP probe, including fixture shell and packet observer. No equivalent actual network egress proof is claimed; actual Orca policy owner reviewed separately. src/main/browser/cdp/screencast/browser-screencast-live-frame-allocation.test.ts reduced. Retires captured PNG/JPEG twelve-frame digest/metadata/ack replay bundled with zero copied-byte instrumentation; surviving retry ownership witness is not claimed equivalent format or sequence coverage. src/main/browser/extensions/context-menus/browser-extension-context-menu-model.test.ts reduced. Toolbar-context constant membership, wrapper submenu structure and click-data property projection no longer independently asserted. 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.", "motivatingLinks": [ "https://linear.app/stably/issue/STA-5400", "https://github.com/stablyai/orca/issues/16367" @@ -18820,7 +18702,8 @@ "The patch does not add startup stale-process recovery or an idle timeout; those require an exact persisted ownership ledger and are intentionally out of scope.", "Audit1379 changes src/main/browser/cdp/snapshots/snapshot-engine-iframe-sessions.test.ts (3 authored registrations removed; 0 retained); src/main/browser/client-hosts/file-transfers/browser-client-download-routing.test.ts (3 authored registrations removed; 4 retained). Intentional losses: Loses ref/string/Map.get-count projections; no real browser operation. Intentionally removes 3 authored registrations: 'cancels a client-hosted download whose owner cannot be resolved'; 'leaves ordinary browser guests on their desktop Downloads path'; 'stops routing to a composition once it starts closing'. Retained focused cases are not claimed as equivalent replacement for these losses. No equivalent replacement coverage is claimed. Existing exact primary-selector receipts remain unchanged.", "Audit1383: src/main/browser/guest-lifecycle/browser-guest-devtools-dock.test.ts reduced. Lose capture failure baseline and destroyed-host callback control. Fake Electron view models native lifecycle and custody; no native rendering/visible focus claimed. No equivalent replacement coverage is claimed.", - "Audit1390: src/main/browser/agent-browser/agent-browser-action-effects.test.ts reduced. No standalone baseline snapshot call-count alarm. src/main/browser/routing/partitions/browser-route-partition-stability.test.ts removed. Standalone assembled partition stability/separation/binding-count alarms across runtime/SSH/WSL/environment identity removed. No real browser cookie/session continuity or replacement equivalence is claimed. src/main/browser/element-grab/browser-grab-payload.test.ts reduced. Standalone annotation clamp/list-count/default preservation characterization removed. src/main/browser/cdp/input/guest-focused-frame-route.test.ts reduced. Attach-call count, default widget fallback and claimed detach-forgetting alarm removed; no actual Chromium frame/input delivery claimed. No equivalent replacement coverage is claimed. Existing exact primary-selector receipts remain unchanged." + "Audit1390: src/main/browser/agent-browser/agent-browser-action-effects.test.ts reduced. No standalone baseline snapshot call-count alarm. src/main/browser/routing/partitions/browser-route-partition-stability.test.ts removed. Standalone assembled partition stability/separation/binding-count alarms across runtime/SSH/WSL/environment identity removed. No real browser cookie/session continuity or replacement equivalence is claimed. src/main/browser/element-grab/browser-grab-payload.test.ts reduced. Standalone annotation clamp/list-count/default preservation characterization removed. src/main/browser/cdp/input/guest-focused-frame-route.test.ts reduced. Attach-call count, default widget fallback and claimed detach-forgetting alarm removed; no actual Chromium frame/input delivery claimed. No equivalent replacement coverage is claimed. Existing exact primary-selector receipts remain unchanged.", + "Audit1396: src/main/browser/network/egress/browser-route-webrtc-egress.electron.test.ts removed. Deletes entire one-registration native Electron baseline/protected UDP probe, including fixture shell and packet observer. No equivalent actual network egress proof is claimed; actual Orca policy owner reviewed separately. src/main/browser/cdp/screencast/browser-screencast-live-frame-allocation.test.ts reduced. Retires captured PNG/JPEG twelve-frame digest/metadata/ack replay bundled with zero copied-byte instrumentation; surviving retry ownership witness is not claimed equivalent format or sequence coverage. src/main/browser/extensions/context-menus/browser-extension-context-menu-model.test.ts reduced. Toolbar-context constant membership, wrapper submenu structure and click-data property projection no longer independently asserted. No equivalent replacement coverage is claimed. Existing exact primary-selector receipts remain unchanged." ], "demotionRule": "Demote if a closed or destroyed page leaves its exact named helper session, if quit can exit before helper retirement settles, if unrelated live pages are retired, or if cleanup regresses to a global process-name kill or timer-only sweep." }, @@ -19550,7 +19433,7 @@ "providers": ["local", "daemon", "ssh", "wsl", "remote-runtime"], "coveredPlatforms": ["macos"], "coveredProviders": ["local", "daemon", "ssh"], - "coverageNotes": "Temporary SQLite databases and fake-clock/provider contracts cover assignment repair, uncertain starts/stops, missing git/folder workspaces, owning SSH inventories, and rejection of foreign paired-runtime PTYs. Hidden macOS Electron journeys retain real daemon PTYs. A macOS client against a Debian ARM64 Docker SSH host proves historical repair preserves terminal ownership, the same live shell and background child, input and rendered output through app restart and SSH reconnect. Native Linux/Windows desktop, WSL and active-worker remote absence/deleted-folder journeys remain gaps; no execution commands or wire payloads change. 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. Audit1390: src/main/runtime/orchestration/db/hot-path-statement-compilation.test.ts removed. Retires compile-count, SQL-shape and self-derived repeated-publication alarms. No independent dispatch/task outcome asserted. 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.", + "coverageNotes": "Temporary SQLite databases and fake-clock/provider contracts cover assignment repair, uncertain starts/stops, missing git/folder workspaces, owning SSH inventories, and rejection of foreign paired-runtime PTYs. Hidden macOS Electron journeys retain real daemon PTYs. A macOS client against a Debian ARM64 Docker SSH host proves historical repair preserves terminal ownership, the same live shell and background child, input and rendered output through app restart and SSH reconnect. Native Linux/Windows desktop, WSL and active-worker remote absence/deleted-folder journeys remain gaps; no execution commands or wire payloads change. 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. Audit1390: src/main/runtime/orchestration/db/hot-path-statement-compilation.test.ts removed. Retires compile-count, SQL-shape and self-derived repeated-publication alarms. No independent dispatch/task outcome asserted. 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/main/runtime/orchestration/agent-facing-parity.test.ts reduced. Whole chat-versus-terminal preamble equality and incidental identity wording assertions no longer independently asserted. 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.", "motivatingLinks": [ "https://stablygroup.slack.com/archives/C0AK2T3JEF4/p1791225819076759", "https://github.com/stablyai/orca/pull/11271", @@ -19673,7 +19556,8 @@ "Retired during audit1312 selector cleanup; original complete execution receipt (unchanged): {\"date\":\"2026-10-05\",\"runner\":\"local\",\"platform\":\"macos\",\"command\":\"ORCA_BACKGROUND_LAUNCH=1 pnpm test src/main/runtime/orchestration src/main/runtime/runtime-legacy-worker-terminal-recovery-persistence.test.ts src/main/runtime/runtime-legacy-worker-terminal-recovery-retry.test.ts src/main/runtime/runtime-legacy-worker-missing-workspace-recovery.test.ts src/main/runtime/orca-runtime.test.ts src/main/runtime/rpc/orchestration-runtime-update-settlement.test.ts src/main/runtime/rpc/methods/orchestration/worker/composed-workers.test.ts src/main/runtime/rpc/methods/orchestration/messaging/check.test.ts src/main/runtime/rpc/methods/orchestration/federation/federation-lifecycle-settlement.test.ts src/main/runtime/rpc/methods/orchestration/federation/federation.test.ts src/cli/handlers/orchestration-lifecycle-rejection.test.ts src/cli/handlers/orchestration-migration.test.ts tests/e2e/cross-version-wire/orchestration-delivery-downgrade.unit.test.ts\",\"result\":\"passed\",\"durationSeconds\":21.07,\"summary\":\"48 focused tests passed after worker-first startup lookups and per-pass workspace deduplication. Reverting both optimizations produces four failures. The broader lifecycle command also passed 2,585 tests across 151 files in 21.07s, with seven tests and one file skipped.\"}", "Retired during audit1312 selector cleanup; original complete execution receipt (unchanged): {\"date\":\"2026-10-05\",\"runner\":\"local\",\"platform\":\"macos\",\"command\":\"ORCA_BACKGROUND_LAUNCH=1 pnpm test src/main/runtime/orchestration src/main/runtime/runtime-legacy-worker-terminal-recovery-persistence.test.ts src/main/runtime/runtime-legacy-worker-terminal-recovery-retry.test.ts src/main/runtime/runtime-legacy-worker-missing-workspace-recovery.test.ts src/main/runtime/orca-runtime.test.ts src/main/runtime/rpc/orchestration-runtime-update-settlement.test.ts src/main/runtime/rpc/methods/orchestration/worker/composed-workers.test.ts src/main/runtime/rpc/methods/orchestration/messaging/check.test.ts src/main/runtime/rpc/methods/orchestration/federation/federation-lifecycle-settlement.test.ts src/main/runtime/rpc/methods/orchestration/federation/federation.test.ts src/cli/handlers/orchestration-lifecycle-rejection.test.ts src/cli/handlers/orchestration-migration.test.ts tests/e2e/cross-version-wire/orchestration-delivery-downgrade.unit.test.ts\",\"result\":\"passed\",\"durationSeconds\":56.25,\"summary\":\"2,581 tests passed across 150 files; seven tests and one file skipped. Includes SQLite lifecycle, runtime recovery, federation, CLI and SQL downgrade contracts.\"}", "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.", - "Audit1390: src/main/runtime/orchestration/db/hot-path-statement-compilation.test.ts removed. Retires compile-count, SQL-shape and self-derived repeated-publication alarms. No independent dispatch/task outcome asserted. No equivalent replacement coverage is claimed. Existing exact primary-selector receipts remain unchanged." + "Audit1390: src/main/runtime/orchestration/db/hot-path-statement-compilation.test.ts removed. Retires compile-count, SQL-shape and self-derived repeated-publication alarms. No independent dispatch/task outcome asserted. No equivalent replacement coverage is claimed. Existing exact primary-selector receipts remain unchanged.", + "Audit1396: src/main/runtime/orchestration/agent-facing-parity.test.ts reduced. Whole chat-versus-terminal preamble equality and incidental identity wording assertions no longer independently asserted. No equivalent replacement coverage is claimed. Existing exact primary-selector receipts remain unchanged." ], "promotionCriteria": [ "Collect cross-platform and provider soak evidence with no weakened identity, transaction or bounded-work assertions." diff --git a/config/scripts/ci-shard-timings.json b/config/scripts/ci-shard-timings.json index e089fbc835b..4c9ac0c97ca 100644 --- a/config/scripts/ci-shard-timings.json +++ b/config/scripts/ci-shard-timings.json @@ -4268,7 +4268,6 @@ "src/main/persistence/terminal-topology/duplicate-pty-pane-repair.test.ts": 2087, "src/main/persistence/terminal-topology/editor-renames-across-partitions.test.ts": 526, "src/main/persistence/terminal-topology/editor-tab-layout-apply.test.ts": 2243, - "src/main/persistence/terminal-topology/layout-owner-written-session-golden.test.ts": 1961, "src/main/persistence/terminal-topology/open-in-new-group-layout-apply.test.ts": 1943, "src/main/persistence/terminal-topology/pane-last-grid.test.ts": 5642, "src/main/persistence/terminal-topology/persistence-binding-graft-placement.test.ts": 1949, @@ -5910,7 +5909,6 @@ "src/main/runtime/terminal/stop/exact-terminal-stop-liveness.test.ts": 4321, "src/main/runtime/terminal/stop/terminal-update-admission.test.ts": 48, "src/main/runtime/terminal/surface/closed-pane-exit-cleanup-main.test.ts": 4281, - "src/main/runtime/terminal/surface/main-default-pane-acquisition.test.ts": 5812, "src/main/runtime/terminal/surface/main-terminal-acquisition.test.ts": 4330, "src/main/runtime/terminal/surface/pty-pane-copies-untouched-main.test.ts": 4585, "src/main/runtime/terminal/surface/terminal-surface-replies-main.test.ts": 4324, @@ -7127,7 +7125,6 @@ "src/renderer/src/components/editor/EditorPanel.diff-rich-owner.test.tsx": 2823, "src/renderer/src/components/editor/EditorPanelHeaderPath.test.tsx": 2664, "src/renderer/src/components/editor/external-changes/ExternalFileChangeBanner.test.tsx": 2142, - "src/renderer/src/components/editor/ImageViewer.test.tsx": 1872, "src/renderer/src/components/editor/notebook/cells/IpynbCellEditor.external-sync.test.tsx": 1660, "src/renderer/src/components/editor/notebook/kernel/IpynbKernelToolbar.test.tsx": 736, "src/renderer/src/components/editor/notebook/document/IpynbRendering.test.tsx": 2143, @@ -7483,7 +7480,6 @@ "src/renderer/src/components/mobile/paired-mobile-devices.test.ts": 192, "src/renderer/src/components/mobile/use-mobile-page-paired-devices.test.ts": 475, "src/renderer/src/components/native-chat-launch-resume-decision.test.tsx": 1844, - "src/renderer/src/components/native-chat-restart-action-notifications.test.ts": 2125, "src/renderer/src/components/native-chat-resume-bulk-progress.test.tsx": 3261, "src/renderer/src/components/native-chat-resume-failed-chat-watch.test.ts": 1954, "src/renderer/src/components/native-chat-resume-failure-guidance.test.ts": 240, @@ -7802,7 +7798,6 @@ "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/NewWorkspaceComposerAgentSection.test.tsx": 895, "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/ProjectCombobox.test.tsx": 848, @@ -7905,9 +7900,7 @@ "src/renderer/src/components/right-sidebar/file-explorer/panel/FileExplorerToolbar.test.tsx": 1837, "src/renderer/src/components/right-sidebar/file-explorer/mutations/FileExplorerVirtualRows.row-handlers.test.tsx": 3415, "src/renderer/src/components/right-sidebar/hosted-review/folder-checks/FolderWorkspacePrChecksPanel.test.tsx": 863, - "src/renderer/src/components/right-sidebar/hosted-review/folder-checks/FolderWorkspacePrChecksRow.test.tsx": 293, "src/renderer/src/components/right-sidebar/FolderWorkspaceWorktreesPanel.test.tsx": 461, - "src/renderer/src/components/right-sidebar/hosted-review/provider-actions/HostedReviewActions.draft.test.tsx": 1587, "src/renderer/src/components/right-sidebar/plugin-panels/PluginPanel.test.tsx": 2710, "src/renderer/src/components/right-sidebar/PortsPanel.test.tsx": 1618, "src/renderer/src/components/right-sidebar/hosted-review/creation/PullRequestComposer.generate-tooltip.test.tsx": 1382, @@ -7995,7 +7988,6 @@ "src/renderer/src/components/right-sidebar/source-control/ai/runSourceControlAgentActionStart.test.ts": 1917, "src/renderer/src/components/right-sidebar/source-control/panel/source-control-branch-context-row.test.tsx": 790, "src/renderer/src/components/right-sidebar/source-control/review/source-control-create-pr-intent-flow.test.ts": 71, - "src/renderer/src/components/right-sidebar/source-control/review/source-control-dropdown-items.create-pr-intent.test.ts": 324, "src/renderer/src/components/right-sidebar/source-control/panel/source-control-dropdown-items.test.ts": 409, "src/renderer/src/components/right-sidebar/source-control/review/source-control-manual-review-url.test.ts": 77, "src/renderer/src/components/right-sidebar/source-control/review/source-control-primary-action.create-pr-intent.test.ts": 340, @@ -8197,7 +8189,6 @@ "src/renderer/src/components/sidebar/LocalGitToolchainScanBanner.test.tsx": 2025, "src/renderer/src/components/sidebar/MarkdownImageLightbox.test.tsx": 2284, "src/renderer/src/components/sidebar/project-admission/dialog/NonGitFolderDialog.test.tsx": 2473, - "src/renderer/src/components/sidebar/worktree-list/rows/NoticeHostGlyph.test.tsx": 2697, "src/renderer/src/components/sidebar/host-path-browser/RemoteFileBrowser.keyboard.test.tsx": 825, "src/renderer/src/components/sidebar/host-path-browser/RemoteFileBrowser.paste.test.tsx": 708, "src/renderer/src/components/sidebar/SetupGuideSidebarEntry.test.tsx": 386, @@ -8305,7 +8296,6 @@ "src/renderer/src/components/sidebar/workspace-board/search/workspace-kanban-search.test.ts": 176, "src/renderer/src/components/sidebar/workspace-board/drag/workspace-kanban-sidebar-drop.test.ts": 1891, "src/renderer/src/components/sidebar/workspace-actions/context-menu/workspace-lineage-menu-actions.test.ts": 235, - "src/renderer/src/components/sidebar/worktree-list/controls/workspace-options-menu-items.test.tsx": 630, "src/renderer/src/components/sidebar/workspace-card/review/workspace-reference-detail-read.test.ts": 662, "src/renderer/src/components/sidebar/workspace-card/review/workspace-reference-refresh-targets.test.ts": 945, "src/renderer/src/components/sidebar/worktree-list/parity/workspace-view-evaluator-parity.test.ts": 2374, @@ -8318,7 +8308,6 @@ "src/renderer/src/components/sidebar/workspace-card/activity/worktree-agent-row-selectors.test.ts": 376, "src/renderer/src/components/sidebar/workspace-card/activity/worktree-card-compact-agent-row.stable-message.test.tsx": 2166, "src/renderer/src/components/sidebar/workspace-card/activity/worktree-card-idle-child-agents.test.ts": 1899, - "src/renderer/src/components/sidebar/workspace-card/surface/worktree-card-option-items.test.tsx": 459, "src/renderer/src/components/sidebar/workspace-card/activity/worktree-card-status-inputs.test.ts": 55, "src/renderer/src/components/sidebar/workspace-actions/deletion/worktree-delete-host-qualification.test.ts": 1225, "src/renderer/src/components/sidebar/worktree-list/navigation/worktree-keyboard-cycle.test.ts": 131, @@ -8426,7 +8415,6 @@ "src/renderer/src/components/status-bar/owner-usage-rate-limits.test.ts": 260, "src/renderer/src/components/status-bar/ports-status-popover-rows.test.tsx": 710, "src/renderer/src/components/status-bar/provider-account-sync-key.test.ts": 310, - "src/renderer/src/components/status-bar/provider-segment-balance.test.tsx": 2245, "src/renderer/src/components/status-bar/provider-segment-monthly-window.test.tsx": 2106, "src/renderer/src/components/status-bar/provider-segment-stale-warning.test.tsx": 423, "src/renderer/src/components/status-bar/remote-host-connection-status.test.ts": 200, @@ -8656,7 +8644,6 @@ "src/renderer/src/components/terminal-pane/cache-timer-seeding.test.ts": 108, "src/renderer/src/components/terminal-pane/claude-detached-pane-restart.test.ts": 1938, "src/renderer/src/components/terminal-pane/claude-diff-panel-notice-banner.test.tsx": 1986, - "src/renderer/src/components/terminal-pane/claude-old-terminal-banner.test.tsx": 2146, "src/renderer/src/components/terminal-pane/claude-side-question-completion.test.ts": 2305, "src/renderer/src/components/terminal-pane/closed-terminal-leaf-notice-layout-owner.test.ts": 1032, "src/renderer/src/components/terminal-pane/codex-backfill-error-detector.test.ts": 26, @@ -8891,7 +8878,6 @@ "src/renderer/src/components/terminal-pane/split-right-white-screen.test.ts": 53, "src/renderer/src/components/terminal-pane/stale-document-visibility.test.ts": 88, "src/renderer/src/components/terminal-pane/terminal-agent-paste-bracketing.test.ts": 329, - "src/renderer/src/components/terminal-pane/terminal-agent-session-continuation.test.ts": 88, "src/renderer/src/components/terminal-pane/terminal-agent-session-fork.test.ts": 1377, "src/renderer/src/components/terminal-pane/terminal-alternate-screen-parse.test.ts": 70, "src/renderer/src/components/terminal-pane/terminal-appearance.test.ts": 339, @@ -9157,7 +9143,6 @@ "src/renderer/src/components/workspace-space/SpaceWindowStatePublisher.test.tsx": 2008, "src/renderer/src/components/worktree-creation/PendingNativeChatCreation.test.tsx": 4075, "src/renderer/src/components/worktree-jump/WorktreeJumpPalette.linear-url.test.tsx": 3372, - "src/renderer/src/components/worktree-jump/WorktreeJumpPalette.mount-gating.test.tsx": 3635, "src/renderer/src/components/worktree-jump/WorktreeJumpPalette.recent-tabs.behavior.test.tsx": 4445, "src/renderer/src/components/worktree-jump/WorktreeJumpPalette.recent-tabs.test.tsx": 9384, "src/renderer/src/components/worktree-jump/WorktreeJumpPalette.task-search.test.tsx": 10126, @@ -9903,7 +9888,6 @@ "src/renderer/src/store/repos/wsl-moved-repo-ui-retention.test.ts": 2920, "src/renderer/src/store/running-terminal-close-confirm.test.ts": 50, "src/renderer/src/store/runtime-status-subscriptions.test.ts": 182, - "src/renderer/src/store/selectors.test.ts": 1816, "src/renderer/src/store/slices/active-selection-owner-scoping.test.ts": 1909, "src/renderer/src/store/slices/agent-generated-tab-title.test.ts": 1976, "src/renderer/src/store/slices/agent-hibernation-live-anchor-e2e.test.ts": 1783, diff --git a/config/scripts/ci-unit-files.mjs b/config/scripts/ci-unit-files.mjs index cc5c6c0be9d..2bd224d6509 100644 --- a/config/scripts/ci-unit-files.mjs +++ b/config/scripts/ci-unit-files.mjs @@ -51,7 +51,6 @@ export const UNIT_EXCLUDE = [ 'src/main/browser/guest-lifecycle/popups/popup-origin-chrome.electron.test.ts', 'src/main/browser/guest-lifecycle/navigation/browser-link-materialization.electron.test.ts', 'src/main/browser/client-pages/renderer/browser-client-page-renderer-lifecycle.electron.test.ts', - 'src/main/browser/network/egress/browser-route-webrtc-egress.electron.test.ts', 'src/main/persistence/profile-state/profile-state-writer-stall.electron.test.ts', 'src/main/browser/guest-lifecycle/popups/browser-popup-compatibility.electron.test.ts', 'src/main/browser/guest-lifecycle/popups/browser-popup-adapter-compatibility.electron.test.ts', diff --git a/config/scripts/pr-package-test-paths.mjs b/config/scripts/pr-package-test-paths.mjs index c5967a17a74..5108491a36a 100644 --- a/config/scripts/pr-package-test-paths.mjs +++ b/config/scripts/pr-package-test-paths.mjs @@ -7,7 +7,6 @@ export const LINUX_PACKAGE_TESTS = [ 'src/main/browser/guest-lifecycle/popups/popup-origin-chrome.electron.test.ts', 'src/main/browser/guest-lifecycle/navigation/browser-link-materialization.electron.test.ts', 'src/main/browser/client-pages/renderer/browser-client-page-renderer-lifecycle.electron.test.ts', - 'src/main/browser/network/egress/browser-route-webrtc-egress.electron.test.ts', 'src/main/persistence/profile-state/profile-state-writer-stall.electron.test.ts', 'src/main/browser/guest-lifecycle/popups/browser-popup-compatibility.electron.test.ts', 'src/main/browser/guest-lifecycle/popups/browser-popup-adapter-compatibility.electron.test.ts', @@ -48,7 +47,6 @@ export const WINDOWS_BOUNDARY_TESTS = [ 'config/scripts/package-electron-runtime-contract.test.mjs', 'config/scripts/electron-builder-runtime-resources.test.mjs', 'src/main/browser/client-pages/renderer/browser-client-page-renderer-lifecycle.electron.test.ts', - 'src/main/browser/network/egress/browser-route-webrtc-egress.electron.test.ts', 'src/main/persistence/profile-state/profile-state-writer-stall.electron.test.ts', 'src/main/providers/windows-conpty-wide-char-duplication.node-pty.test.ts', 'src/main/providers/pty-repaint-wide-char-buffer.node-pty.test.ts', diff --git a/config/scripts/pr-unit-support-routing.test.mjs b/config/scripts/pr-unit-support-routing.test.mjs index 46af24ecaf2..b280c73a9fa 100644 --- a/config/scripts/pr-unit-support-routing.test.mjs +++ b/config/scripts/pr-unit-support-routing.test.mjs @@ -65,7 +65,7 @@ describe('unit support routing', () => { 'src/renderer/src/components/native-chat/NativeChatStructuredSession.next-test-harness.tsx', 'src/renderer/src/components/native-chat/NativeChatStructuredSession.tsx', 'src/main/providers/windows-conpty-wide-char-duplication.node-pty.test.ts', - 'src/main/browser/network/egress/browser-route-webrtc-egress.electron.test.ts' + 'src/main/browser/client-pages/renderer/browser-client-page-renderer-lifecycle.electron.test.ts' ]) { const result = classifyPrJobs([unitHarness, file]) expect(result.should_run, file).toBe(true) diff --git a/src/main/acp/acp-dialects/grok-subagents.test.ts b/src/main/acp/acp-dialects/grok-subagents.test.ts index 9c66db0ab75..f2570fbc937 100644 --- a/src/main/acp/acp-dialects/grok-subagents.test.ts +++ b/src/main/acp/acp-dialects/grok-subagents.test.ts @@ -17,58 +17,6 @@ function finished(update: Partial): ToolCallUpdate { } describe('Grok subagent session updates', () => { - it('reads a spawn with its description, its spawning turn and nothing it cannot show', () => { - expect( - grokSubagentNotification({ - sessionUpdate: 'subagent_spawned', - subagent_id: 'subagent-1', - child_session_id: 'subagent-1', - parent_prompt_id: 'prompt-1', - subagent_type: 'general-purpose', - description: 'Count note lines', - model: 'grok-4.7' - }) - ).toEqual({ id: 'subagent-1', state: 'working', label: 'Count note lines', turn: 'prompt-1' }) - }) - - it('labels a spawn with no description by its type', () => { - expect( - grokSubagentNotification({ - sessionUpdate: 'subagent_spawned', - subagent_id: 'subagent-1', - subagent_type: 'explore' - }) - ).toEqual({ id: 'subagent-1', state: 'working', label: 'explore' }) - }) - - it('reads progress as tokens only', () => { - expect( - grokSubagentNotification({ - sessionUpdate: 'subagent_progress', - subagent_id: 'subagent-1', - tokens_used: 4091 - }) - ).toEqual({ id: 'subagent-1', tokens: 4091 }) - }) - - it.each([ - ['completed', { state: 'completed', result: '4' }], - ['failed', { state: 'failed' }], - ['cancelled', { state: 'stopped' }], - ['something-new', {}] - ] as const)('reads a %s finish', (status, expected) => { - expect( - grokSubagentNotification({ - sessionUpdate: 'subagent_finished', - subagent_id: 'subagent-1', - status, - output: status === 'completed' ? '4' : undefined, - error: status === 'completed' ? undefined : 'Subagent was cancelled', - tokens_used: 0 - }) - ).toEqual({ id: 'subagent-1', ...expected }) - }) - it('ignores every other update', () => { expect(grokSubagentNotification({ sessionUpdate: 'turn_completed' })).toBeUndefined() expect(grokSubagentNotification({ sessionUpdate: 'subagent_spawned' })).toBeUndefined() @@ -96,21 +44,6 @@ describe('Grok subagent session updates', () => { describe('Grok subagent tool calls', () => { const spawnInput = { description: 'Count note lines', prompt: 'Read notes.txt' } - it('ends a foreground spawn with its reply, the machine blocks removed', () => { - expect( - grokToolSubagents( - finished({ - rawOutput: { - type: 'SubagentCompleted', - subagent_id: 'subagent-1', - output: `4${SUBAGENT_RESULT_TAIL}` - } - }), - tool('spawn_subagent', spawnInput) - ) - ).toEqual([{ id: 'subagent-1', state: 'completed', label: 'Count note lines', result: '4' }]) - }) - it('keeps a background spawn running from its acknowledgement, labelled by the prompt when undescribed', () => { expect( grokToolSubagents( @@ -177,13 +110,4 @@ describe('Grok subagent tool calls', () => { ]) expect(grokToolBackgroundTasks(update, tool('kill_command_or_subagent'))).toEqual([]) }) - - it('infers a kill with no result from the ids it named', () => { - expect( - grokToolSubagents( - finished({}), - tool('kill_command_or_subagent', { task_ids: ['subagent-1', 'subagent-1'] }) - ) - ).toEqual([{ id: 'subagent-1', state: 'stopped', knownOnly: true }]) - }) }) diff --git a/src/main/agent-launch/agent-launch-executor-cancel.test.ts b/src/main/agent-launch/agent-launch-executor-cancel.test.ts index 5a08f139b5b..4a63af5f5b1 100644 --- a/src/main/agent-launch/agent-launch-executor-cancel.test.ts +++ b/src/main/agent-launch/agent-launch-executor-cancel.test.ts @@ -175,15 +175,4 @@ describe('agent launch cancellation', () => { expect(result.prompt).toEqual({ delivery: 'submit', outcome: 'handed-to-terminal' }) }) - - it('hands its signal to the terminal prompt delivery', async () => { - const controller = new AbortController() - const h = harness({ lineCarriesPrompt: false }) - - await h.run(CREATE, controller.signal) - - expect(h.deliverTerminalPrompt).toHaveBeenCalledWith( - expect.objectContaining({ signal: controller.signal }) - ) - }) }) diff --git a/src/main/agent-launch/startup-agent-worktree-create.test.ts b/src/main/agent-launch/startup-agent-worktree-create.test.ts index 71be7006104..b5554c8a74c 100644 --- a/src/main/agent-launch/startup-agent-worktree-create.test.ts +++ b/src/main/agent-launch/startup-agent-worktree-create.test.ts @@ -55,128 +55,7 @@ const BASE: RuntimeManagedWorktreeCreateArgs = { lineage: { noParent: true } } -const FULL: RuntimeManagedWorktreeCreateArgs = { - ...BASE, - navigation: 'caller', - createdWithAgent: 'claude', - startupLaunchPreferences: { model: 'opus', effort: 'high' }, - startupAgentArgs: '--verbose', - startupExtraAgentArgs: '--extra', - startupCwd: 'packages/app', - startupPaneKey: 'tab-1:leaf-1', - pendingFirstAgentMessageRename: true, - automationProvenance: { - kind: 'created-by-automation', - automationId: 'auto-1', - automationNameSnapshot: 'Nightly', - automationRunId: 'run-1', - automationRunTitleSnapshot: 'Nightly run', - createdAt: 1, - executionTargetType: 'local', - executionTargetId: 'local', - projectId: 'project-1' - }, - cliProvenance: { kind: 'created-by-cli', createdAt: 1, startupAgent: 'claude' }, - lineage: { parentWorktree: 'id:parent', callerTerminalHandle: 'term_parent' }, - comment: 'from the CLI', - linkedIssue: 7 -} - describe('createWorktreeWithStartupAgent', () => { - it('forwards the startup agent and prompt to the create with the chat default on', async () => { - const { runtime, created, create } = harness() - const prompt = 'x'.repeat(3000) - const args = { - ...FULL, - startupAgent: 'claude' as const, - startupPrompt: prompt, - startupLaunchSource: 'cli' - } - - const result = await create(args) - - expect(result).toBe(created) - expect(runtime.createManagedWorktree).toHaveBeenCalledTimes(1) - expect(runtime.createManagedWorktree).toHaveBeenCalledWith(args) - expect(runtime.getStructuredAgentSessionCreateSupport).not.toHaveBeenCalled() - }) - - it('forwards a post-start agent and prompt to the create', async () => { - const { runtime, create } = harness() - const args = { ...FULL, startupAgent: 'aider' as const, startupPrompt: 'fix the bug' } - - await create(args) - - expect(runtime.createManagedWorktree).toHaveBeenCalledWith(args) - }) - - it('omits a blank startup prompt from the create request', async () => { - for (const startupPrompt of ['', ' \n']) { - const { runtime, create } = harness() - - await create({ ...FULL, startupAgent: 'codex', startupPrompt }) - - expect(runtime.createManagedWorktree).toHaveBeenCalledWith({ ...FULL, startupAgent: 'codex' }) - } - }) - - it('forwards a linked draft through `startupDraft` with its resolved agent', async () => { - const { runtime, create } = harness({ draftAgent: 'codex' }) - const { createdWithAgent: _requested, ...request } = FULL - const draft = 'https://github.com/o/r/issues/1' - - await create({ ...request, startupDraft: draft }) - - expect(runtime.resolveStartupDraftAgent).toHaveBeenCalledWith( - { id: 'repo-1', connectionId: null, executionHostId: null }, - undefined - ) - expect(runtime.createManagedWorktree).toHaveBeenCalledWith({ - ...request, - startupDraft: draft, - startupDraftAgent: 'codex' - }) - }) - - it('asks for the requested draft agent and keeps it as the request named it', async () => { - const { runtime, create } = harness() - - await create({ ...FULL, startupDraft: 'https://x/1' }) - - expect(runtime.resolveStartupDraftAgent).toHaveBeenCalledWith(expect.anything(), 'claude') - expect(runtime.createManagedWorktree).toHaveBeenCalledWith({ - ...FULL, - startupDraft: 'https://x/1', - startupDraftAgent: 'claude' - }) - }) - - it('creates without the draft when it resolves no agent, so the create does not detect again', async () => { - const { runtime, create } = harness({ draftAgent: null }) - - await create({ ...FULL, startupDraft: 'https://x/1' }) - - expect(runtime.resolveStartupDraftAgent).toHaveBeenCalledTimes(1) - expect(runtime.createManagedWorktree).toHaveBeenCalledWith(FULL) - }) - - it('passes a request with no agent, a blank draft, or a prebuilt command straight to the create', async () => { - for (const args of [ - BASE, - { ...BASE, startupDraft: ' ' }, - { - ...BASE, - startupAgent: 'claude' as const, - startup: { command: 'claude' } - } - ]) { - const { runtime, create } = harness() - await create(args) - expect(runtime.createManagedWorktree).toHaveBeenCalledWith(args) - expect(runtime.showRepo).not.toHaveBeenCalled() - } - }) - it('builds no second agent when the create spawned none here', async () => { const { runtime, created, create } = harness({ created: { @@ -194,13 +73,4 @@ describe('createWorktreeWithStartupAgent', () => { expect(result).toBe(created) expect(runtime.createManagedWorktree).toHaveBeenCalledTimes(1) }) - - it('surfaces a create failure unchanged', async () => { - const failure = new Error( - 'Selected agent is disabled. Choose an enabled agent before creating.' - ) - const { create } = harness({ createError: failure }) - - await expect(create({ ...BASE, startupAgent: 'claude' })).rejects.toBe(failure) - }) }) diff --git a/src/main/ai-vault-search/session-search-agent-compatibility.test.ts b/src/main/ai-vault-search/session-search-agent-compatibility.test.ts index 2f55dcc7b51..c1454e40054 100644 --- a/src/main/ai-vault-search/session-search-agent-compatibility.test.ts +++ b/src/main/ai-vault-search/session-search-agent-compatibility.test.ts @@ -5,11 +5,7 @@ import { searchHit, searchResults } from '../../shared/ai-vault/search/ai-vault-search-test-fixture' -import { - searchSessionService, - setSessionSearchService, - sessionSearchServiceStatus -} from './session-search-service-registry' +import { searchSessionService, setSessionSearchService } from './session-search-service-registry' import { encodeSessionSearchCursor, decodeSessionSearchCursor, @@ -19,24 +15,6 @@ import { afterEach(() => setSessionSearchService(null)) -test.each(['runtime', 'relay'] as const)( - 'publishes the current supported catalog independently of indexed data on %s', - async (transport) => { - expect(await sessionSearchServiceStatus({}, transport)).toMatchObject({ - enabled: false, - supportedAgents: [...AI_VAULT_AGENTS], - supportsQoderHistory: true, - supportsJcodeHistory: true - }) - const service = fakeSearchService() - setSessionSearchService(service) - expect((await sessionSearchServiceStatus({}, transport)).supportedAgents).toEqual( - AI_VAULT_AGENTS - ) - expect(service.status.mock.lastCall).toEqual([]) - } -) - test.each(['runtime', 'relay'] as const)( 'keeps an empty decoder catalog authoritative over both history flags on %s', async (transport) => { @@ -62,30 +40,6 @@ test.each(['runtime', 'relay'] as const)( } ) -test.each(['qoder', 'jcode'] as const)( - 'projects the independent %s history capability before retrieval', - async (agent) => { - const service = fakeSearchService() - setSessionSearchService(service) - const supportField = agent === 'qoder' ? 'supportsQoderHistory' : 'supportsJcodeHistory' - await searchSessionService({ query: 'proof', [supportField]: true }, 'relay') - expect(service.search).toHaveBeenCalledExactlyOnceWith( - { - query: 'proof', - limit: 20, - filters: { - agents: AI_VAULT_AGENTS.filter((candidate) => - agent === 'qoder' - ? !['jcode', 'kiro'].includes(candidate) - : !['codebuddy', 'zcode', 'qoder', 'kiro'].includes(candidate) - ) - } - }, - undefined - ) - } -) - test.each(['runtime', 'relay'] as const)( 'keeps an unsupported-only filter narrow through normal service checks on %s', async (transport) => { diff --git a/src/main/ai-vault/session-list-results.test.ts b/src/main/ai-vault/session-list-results.test.ts index 026d9d4f17c..c9059411dbd 100644 --- a/src/main/ai-vault/session-list-results.test.ts +++ b/src/main/ai-vault/session-list-results.test.ts @@ -51,18 +51,6 @@ describe('mergeAiVaultListResults', () => { vi.useRealTimers() }) - it('keeps the latest input scannedAt instead of restamping the merge', () => { - const merged = mergeAiVaultListResults( - [ - { ...listResult([]), scannedAt: '2026-08-02T00:00:00.000Z' }, - { ...listResult([]), scannedAt: '2026-08-02T00:00:05.000Z' } - ], - undefined - ) - - expect(merged.scannedAt).toBe('2026-08-02T00:00:05.000Z') - }) - it('ignores a future or unparsable remote stamp so the merge cannot pin the renderer guard', () => { vi.useFakeTimers() vi.setSystemTime(new Date('2026-08-02T00:00:10.000Z')) @@ -119,51 +107,6 @@ describe('mergeAiVaultListResults', () => { ).toBe('2026-08-02T00:00:04.000Z') }) - it('resolves an equal-instant tie the same way in either leg order', () => { - vi.useFakeTimers() - vi.setSystemTime(new Date('2026-08-02T00:00:10.000Z')) - const noMillis = { ...listResult([]), scannedAt: '2026-08-02T00:00:05Z' } - const withMillis = { ...listResult([]), scannedAt: '2026-08-02T00:00:05.000Z' } - - expect(mergeAiVaultListResults([noMillis, withMillis], undefined).scannedAt).toBe( - '2026-08-02T00:00:05.000Z' - ) - expect(mergeAiVaultListResults([withMillis, noMillis], undefined).scannedAt).toBe( - '2026-08-02T00:00:05.000Z' - ) - }) - - it('normalizes the merged stamp so a leg ISO variant cannot leak into it', () => { - vi.useFakeTimers() - vi.setSystemTime(new Date('2026-08-02T00:00:10.000Z')) - - expect( - mergeAiVaultListResults( - [{ ...listResult([]), scannedAt: '2026-08-01T19:00:05.000-05:00' }], - undefined - ).scannedAt - ).toBe('2026-08-02T00:00:05.000Z') - }) - - it('accepts the oldest representable stamp and rejects one millisecond beyond it', () => { - vi.useFakeTimers() - vi.setSystemTime(new Date('2026-08-02T00:00:10.000Z')) - - expect( - mergeAiVaultListResults( - [{ ...listResult([]), scannedAt: '-271821-04-20T00:00:00.000Z' }], - undefined - ).scannedAt - ).toBe('-271821-04-20T00:00:00.000Z') - - expect( - mergeAiVaultListResults( - [{ ...listResult([]), scannedAt: '-271821-04-19T23:59:59.999Z' }], - undefined - ).scannedAt - ).toBe('2026-08-02T00:00:10.000Z') - }) - it('does not cap an Unlimited all-host merge', () => { const sessions = Array.from({ length: 1001 }, (_, index) => session(index)) const merged = mergeAiVaultListResults([{ ...listResult([]), sessions }], undefined, true) diff --git a/src/main/browser/cdp/screencast/browser-screencast-live-frame-allocation.test.ts b/src/main/browser/cdp/screencast/browser-screencast-live-frame-allocation.test.ts index 880d8bffdb1..4e6a1cf0efe 100644 --- a/src/main/browser/cdp/screencast/browser-screencast-live-frame-allocation.test.ts +++ b/src/main/browser/cdp/screencast/browser-screencast-live-frame-allocation.test.ts @@ -73,70 +73,6 @@ async function settleAcknowledgements(): Promise { } describe('live screencast image allocation', () => { - it('replays captured PNG and JPEG bytes without copying the decoded image again', async () => { - const fixtures = [ - { - format: 'png', - path: 'docs/site/public/docs/remote-server-add-client.png', - width: 832, - height: 832 - }, - { - format: 'jpeg', - path: 'docs/site/public/docs/posters/file-drag.jpg', - width: 1200, - height: 682 - } - ] as const - const captures = fixtures.map((fixture) => { - const bytes = readFileSync(resolve(fixture.path)) - return { ...fixture, bytes, data: bytes.toString('base64'), expectedDigest: digest(bytes) } - }) - const nativeUint8Array = globalThis.Uint8Array - let imageCopyBytes = 0 - globalThis.Uint8Array = new Proxy(nativeUint8Array, { - construct(target, argumentsList, newTarget) { - const source: unknown = argumentsList[0] - const bytes: unknown = Reflect.construct(target, argumentsList, newTarget) - if (!(bytes instanceof nativeUint8Array)) { - throw new Error('Unexpected byte constructor result') - } - if (Buffer.isBuffer(source)) { - imageCopyBytes += bytes.byteLength - } - return bytes - } - }) - try { - for (const fixture of captures) { - const seen: number[] = [] - const route = createLiveFrameRoute(fixture.format, (wire) => { - const frame = decodeBrowserScreencastFrame(wire) - if (!frame) { - throw new Error('Captured frame was not encoded') - } - expect(frame.format).toBe(fixture.format) - expect(frame.metadata.imageWidth).toBe(fixture.width) - expect(frame.metadata.imageHeight).toBe(fixture.height) - expect(frame.image.byteLength).toBe(fixture.bytes.byteLength) - expect(digest(frame.image)).toBe(fixture.expectedDigest) - seen.push(frame.seq) - return true - }) - for (let sessionId = 1; sessionId <= 12; sessionId++) { - route.emit(fixture.data, sessionId) - } - await settleAcknowledgements() - expect(seen).toEqual(Array.from({ length: 12 }, (_, index) => index)) - expect(route.acknowledgements).toEqual(Array.from({ length: 12 }, (_, index) => index + 1)) - route.pacer.clearPending(true) - } - expect(imageCopyBytes).toBe(0) - } finally { - globalThis.Uint8Array = nativeUint8Array - } - }) - it('owns pooled and offset images through refused sends and keeps encoded bytes independent', async () => { vi.useFakeTimers() const bytes = readFileSync(resolve('resources/tray/orca-menu-barTemplate.png')) diff --git a/src/main/browser/extensions/context-menus/browser-extension-context-menu-model.test.ts b/src/main/browser/extensions/context-menus/browser-extension-context-menu-model.test.ts index 225b015c2eb..5fd4ae8f1c6 100644 --- a/src/main/browser/extensions/context-menus/browser-extension-context-menu-model.test.ts +++ b/src/main/browser/extensions/context-menus/browser-extension-context-menu-model.test.ts @@ -6,9 +6,6 @@ import { updateContextMenuItem } from './browser-extension-context-menu-model' import { - ACTION_MENU_CONTEXTS, - arrangeExtensionContextMenuGroup, - buildContextMenuOnClickData, contextMenuTargetFromParams, visibleContextMenuTree } from './browser-extension-context-menu-target' @@ -114,14 +111,6 @@ describe('what a right-click shows', () => { ]) }) - it('shows action items only for the toolbar menu', () => { - const toolbar = { - contexts: new Set(ACTION_MENU_CONTEXTS), - pageUrl: 'https://site.test/' - } - expect(visibleContextMenuTree(items, toolbar).map((item) => item.key)).toEqual(['"toolbar"']) - }) - it('drops a parent whose children are all filtered out', () => { const nested = build( { id: 'parent', title: 'Parent', contexts: ['all'] }, @@ -129,19 +118,6 @@ describe('what a right-click shows', () => { ) expect(visibleContextMenuTree(nested, pageClick)).toEqual([]) }) - - it('shows one item inline and several under a submenu named after the extension', () => { - const one = visibleContextMenuTree(build({ id: 'a', title: 'A' }), pageClick) - expect(arrangeExtensionContextMenuGroup('ext', 'Vault', one)?.items).toEqual(one) - const two = visibleContextMenuTree( - build({ id: 'a', title: 'A' }, { id: 'b', title: 'B' }), - pageClick - ) - expect(arrangeExtensionContextMenuGroup('ext', 'Vault', two)?.items).toEqual([ - { key: '', title: 'Vault', type: 'normal', checked: false, enabled: true, children: two } - ]) - expect(arrangeExtensionContextMenuGroup('ext', 'Vault', [])).toBeNull() - }) }) describe('clicking an item', () => { @@ -160,32 +136,4 @@ describe('clicking an item', () => { ['r2', true] ]) }) - - it("builds Chrome's OnClickData", () => { - const [item] = build({ id: 'fill', title: 'Fill', contexts: ['editable'], parentId: undefined }) - const target = contextMenuTargetFromParams( - { - isEditable: true, - linkURL: 'https://site.test/next', - srcURL: 'https://cdn.test/a.png', - frameURL: 'https://frame.test/', - selectionText: 'me@site.test' - }, - 'https://site.test/login', - true - ) - expect( - buildContextMenuOnClickData({ item, target, frameId: 12, editable: true, mediaType: 'image' }) - ).toEqual({ - menuItemId: 'fill', - mediaType: 'image', - linkUrl: 'https://site.test/next', - srcUrl: 'https://cdn.test/a.png', - pageUrl: 'https://site.test/login', - frameUrl: 'https://frame.test/', - frameId: 12, - selectionText: 'me@site.test', - editable: true - }) - }) }) diff --git a/src/main/browser/network/egress/browser-route-webrtc-egress.electron.test.ts b/src/main/browser/network/egress/browser-route-webrtc-egress.electron.test.ts deleted file mode 100644 index e25d366e3be..00000000000 --- a/src/main/browser/network/egress/browser-route-webrtc-egress.electron.test.ts +++ /dev/null @@ -1,199 +0,0 @@ -import { spawnSync } from 'node:child_process' -import { existsSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from 'node:fs' -import { createRequire } from 'node:module' -import { tmpdir } from 'node:os' -import { join } from 'node:path' -import { afterAll, describe, expect, it } from 'vitest' -import { resolveElectronProbeLaunch } from '../../electron-fixtures/electron-probe-display-launch' - -const electronBinary = createRequire(import.meta.url)('electron') as string -const fixtureRoots: string[] = [] - -type ProbeResult = { - packets: number - policy: string - resolvedProxy: string -} - -afterAll(() => { - const failures: unknown[] = [] - for (const root of fixtureRoots) { - try { - rmSync(root, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 }) - } catch (error) { - failures.push(error) - } - } - if (failures.length > 0) { - throw new AggregateError(failures, 'Failed to clean up WebRTC egress fixtures') - } -}) - -function probeMain(resultPath: string, protectedGuest: boolean): string { - return ` -const { app, BrowserWindow, session } = require('electron') -// This data-channel probe does not need Linux GPU initialization. -if (process.platform === 'linux') app.disableHardwareAcceleration() -const dgram = require('node:dgram') -const net = require('node:net') -const os = require('node:os') -const { writeFileSync } = require('node:fs') -let phase = 'app-ready' - -function enterPhase(next) { - phase = next - console.error('[webrtc-egress] ' + phase) -} - -function bind(socket, host) { - return new Promise((resolve, reject) => { - socket.once('error', reject) - socket.bind(0, host, () => { - socket.off('error', reject) - resolve(socket.address()) - }) - }) -} - -function listen(server, host) { - return new Promise((resolve, reject) => { - server.once('error', reject) - server.listen(0, host, () => { - server.off('error', reject) - resolve(server.address()) - }) - }) -} - -function viewerAddress() { - for (const entries of Object.values(os.networkInterfaces())) { - for (const entry of entries || []) { - if (entry.family === 'IPv4' && !entry.internal) return entry.address - } - } - return '127.0.0.1' -} - -async function probe() { - const udp = dgram.createSocket('udp4') - const tcp = net.createServer((socket) => socket.destroy()) - const packets = [] - udp.on('message', (message) => packets.push(message.length)) - enterPhase('bind-listeners') - const [udpAddress, tcpAddress] = await Promise.all([ - bind(udp, '0.0.0.0'), - listen(tcp, '127.0.0.1') - ]) - const partition = 'persist:webrtc-egress-${protectedGuest}-' + Date.now() - const routeSession = session.fromPartition(partition, { cache: false }) - enterPhase('configure-proxy') - await routeSession.setProxy({ - mode: 'fixed_servers', - proxyRules: 'socks5://127.0.0.1:' + tcpAddress.port, - proxyBypassRules: '<-loopback>' - }) - enterPhase('close-connections') - await routeSession.closeAllConnections() - enterPhase('resolve-proxy') - const resolvedProxy = await routeSession.resolveProxy('https://example.invalid/') - enterPhase('create-window') - const window = new BrowserWindow({ - show: false, - webPreferences: { partition, sandbox: true, nodeIntegration: false, contextIsolation: true } - }) - if (${protectedGuest}) { - window.webContents.setWebRTCIPHandlingPolicy('disable_non_proxied_udp') - } - const policy = window.webContents.getWebRTCIPHandlingPolicy() - enterPhase('load-page') - await window.loadURL('data:text/html,WebRTC egress probe') - const target = viewerAddress() - const script = \` - (async () => { - const peer = new RTCPeerConnection({ - iceServers: [{ urls: 'stun:\${target}:\${udpAddress.port}' }], - iceCandidatePoolSize: 0 - }) - globalThis.__webrtcEgressPeer = peer - peer.createDataChannel('probe') - const offer = await peer.createOffer() - await peer.setLocalDescription(offer) - })() - \` - enterPhase('renderer-webrtc') - await window.webContents.executeJavaScript(script) - // Hidden renderer timers may be throttled; the packet observation clock belongs to the host. - enterPhase('observe-packets') - await new Promise((resolve) => setTimeout(resolve, 3000)) - enterPhase('close-peer') - await window.webContents.executeJavaScript('globalThis.__webrtcEgressPeer.close()') - enterPhase('drain-packets') - await new Promise((resolve) => setTimeout(resolve, 500)) - enterPhase('cleanup') - window.destroy() - udp.close() - tcp.close() - return { packets: packets.length, policy, resolvedProxy } -} - -async function run() { - const timeout = setTimeout(() => { - writeFileSync(${JSON.stringify(resultPath)}, JSON.stringify({ - error: 'WebRTC egress probe timed out', phase, protectedGuest: ${protectedGuest} - })) - app.exit(2) - }, 20000) - await app.whenReady() - const result = await probe() - enterPhase('write-result') - writeFileSync(${JSON.stringify(resultPath)}, JSON.stringify(result)) - clearTimeout(timeout) - app.quit() -} - -run().catch((error) => { - writeFileSync(${JSON.stringify(resultPath)}, JSON.stringify({ error: String(error?.stack || error), phase, protectedGuest: ${protectedGuest} })) - app.exit(1) -}) -` -} - -function runProbe(protectedGuest: boolean): ProbeResult { - const root = mkdtempSync(join(tmpdir(), 'orca-browser-webrtc-egress-')) - fixtureRoots.push(root) - const mainPath = join(root, 'main.cjs') - const resultPath = join(root, 'result.json') - writeFileSync(mainPath, probeMain(resultPath, protectedGuest)) - const { ELECTRON_RUN_AS_NODE: _electronRunAsNode, ...env } = process.env - const electronArgs = [mainPath, `--user-data-dir=${join(root, 'profile')}`] - const { executable, args } = resolveElectronProbeLaunch({ - electronBinary, - electronArgs, - platform: process.platform, - display: env.DISPLAY - }) - const run = spawnSync(executable, args, { encoding: 'utf8', env, timeout: 30_000 }) - const rawResult = existsSync(resultPath) ? readFileSync(resultPath, 'utf8') : 'no result' - expect(run.error).toBeUndefined() - expect(run.status, `${rawResult}\n${run.stdout}\n${run.stderr}`).toBe(0) - if (rawResult === 'no result') { - throw new Error(`${rawResult}\n${run.stdout}\n${run.stderr}`) - } - const parsed = JSON.parse(rawResult) as ProbeResult | { error: string } - if ('error' in parsed) { - throw new Error(parsed.error) - } - return parsed -} - -describe('browser route WebRTC egress under Electron', () => { - it('blocks direct UDP after applying the exact guest policy', () => { - const baseline = runProbe(false) - const protectedGuest = runProbe(true) - - expect(protectedGuest.resolvedProxy).toMatch(/^SOCKS5 127\.0\.0\.1:\d+$/) - expect(baseline.packets).toBeGreaterThan(0) - expect(protectedGuest.policy).toBe('disable_non_proxied_udp') - expect(protectedGuest.packets).toBe(0) - }, 45_000) -}) diff --git a/src/main/claude-usage/claude-model-pricing.test.ts b/src/main/claude-usage/claude-model-pricing.test.ts index 7d044854e6b..7d616f471c5 100644 --- a/src/main/claude-usage/claude-model-pricing.test.ts +++ b/src/main/claude-usage/claude-model-pricing.test.ts @@ -1,6 +1,6 @@ import { describe, expect, it } from 'vitest' import type { ClaudeUsageTokenTotals } from './types' -import { MODEL_PRICING, estimateCostUsd, normalizeModelForPricing } from './claude-model-pricing' +import { estimateCostUsd, normalizeModelForPricing } from './claude-model-pricing' const MTOK = 1_000_000 @@ -19,10 +19,6 @@ function estimate(model: string, ...counts: number[]): number | null { } describe('model pricing name matching', () => { - it.each(Object.keys(MODEL_PRICING))('resolves the table key %s to its own row', (key) => { - expect(normalizeModelForPricing(key)).toBe(key) - }) - it.each([ [' ANTHROPIC/claude-opus-4.1-thinking ', 'claude-opus-4-1'], ['claude-opus-4-20250514', 'claude-opus-4'], @@ -65,34 +61,7 @@ describe('model pricing name matching', () => { }) }) -describe('point-release rates that differ from their major', () => { - it('bills Opus 5.5 below Opus 5 across every bucket', () => { - expect(estimate('claude-opus-5-5', 1_000_000, 1_000_000, 1_000_000, 0)).toBeCloseTo(24.2) - expect(estimate('claude-opus-5-5', 0, 0, 0, 1_000_000, 400_000)).toBeCloseTo(6.2) - }) - - it('bills Sonnet 5.5 cache reads at half of Sonnet 5', () => { - expect(estimate('claude-sonnet-5-5', 0, 0, 1_000_000, 0)).toBeCloseTo(0.1) - expect(estimate('claude-sonnet-5-5', 1_000_000, 1_000_000, 0, 1_000_000, 0)).toBeCloseTo(14.5) - expect(estimate('claude-sonnet-5-5', 0, 0, 0, 1_000_000, 1_000_000)).toBeCloseTo(4) - }) - - it('bills Fable 5.1 cache reads at a quarter of Fable 5', () => { - expect(estimate('claude-fable-5-1', 0, 0, 1_000_000, 0)).toBeCloseTo(0.25) - expect(estimate('claude-fable-5', 0, 0, 1_000_000, 0)).toBeCloseTo(1) - expect(estimate('claude-fable-5-1m', 0, 0, 1_000_000, 0)).toBeCloseTo(1) - }) -}) - describe('estimateCostUsd cache-write TTL rates', () => { - it('bills 5-minute cache writes at 1.25x base input', () => { - expect(estimate('claude-opus-5', 0, 0, 0, 1_000_000, 0)).toBeCloseTo(6.25) - }) - - it('bills 1-hour cache writes at 2x base input', () => { - expect(estimate('claude-opus-5', 0, 0, 0, 1_000_000, 1_000_000)).toBeCloseTo(10) - }) - it('splits a mixed write bucket without double-billing the 1-hour share', () => { expect(estimate('claude-opus-5', 0, 0, 0, 1_000_000, 400_000)).toBeCloseTo(7.75) }) @@ -101,10 +70,6 @@ describe('estimateCostUsd cache-write TTL rates', () => { expect(estimate('claude-opus-5', 0, 0, 0, 1_000, 5_000)).toBeCloseTo(0.01) }) - it('keeps Sonnet 4.6 one-hour writes flat across its full 1M window', () => { - expect(estimate('claude-sonnet-4-6', 0, 0, 0, 400_000, 400_000)).toBeCloseTo(2.4) - }) - it('still returns null for unknown models', () => { expect(estimate('gpt-5', 0, 0, 0, 1_000_000, 1_000_000)).toBeNull() }) @@ -125,15 +90,6 @@ describe('estimateCostUsd long-context requests', () => { ).toBeCloseTo(0.1 + 0.5) }) - it('bills Sonnet 4.5 long-context requests at its premium rates', () => { - const long = tokens(MTOK, MTOK, MTOK, MTOK, MTOK) - expect(estimateCostUsd('claude-sonnet-4-5', { ...long, longContext: long })).toBeCloseTo( - 6 + 22.5 + 0.6 + 12 - ) - // A large aggregate does not imply any individual request crossed the threshold. - expect(estimate('claude-sonnet-4-5', 5 * MTOK)).toBeCloseTo(15) - }) - it('ignores a long-context subset for a model with flat rates', () => { const long = tokens(MTOK) expect(estimateCostUsd('claude-sonnet-4-6', { ...long, longContext: long })).toBeCloseTo(3) @@ -147,27 +103,3 @@ describe('estimateCostUsd long-context requests', () => { }) // Rate source: platform.claude.com/docs/en/about-claude/pricing. -describe('estimateCostUsd Claude Sonnet 5 rates', () => { - it('bills base input at $2/MTok', () => { - expect(estimate('claude-sonnet-5', 1_000_000, 0, 0, 0, 0)).toBeCloseTo(2) - }) - - it('bills output at $10/MTok', () => { - expect(estimate('claude-sonnet-5', 0, 1_000_000, 0, 0, 0)).toBeCloseTo(10) - }) - - it('bills cache hits at $0.20/MTok', () => { - expect(estimate('claude-sonnet-5', 0, 0, 1_000_000, 0, 0)).toBeCloseTo(0.2) - }) - - it('bills 5-minute cache writes at $2.50/MTok and 1-hour writes at $4/MTok', () => { - expect(estimate('claude-sonnet-5', 0, 0, 0, 1_000_000, 0)).toBeCloseTo(2.5) - expect(estimate('claude-sonnet-5', 0, 0, 0, 1_000_000, 1_000_000)).toBeCloseTo(4) - }) - - it('keeps the full 1M window at flat rates with no long-context tier', () => { - expect(estimate('claude-sonnet-5', 1_000_000, 0, 0, 0, 0)).toBeCloseTo( - estimate('claude-sonnet-5', 500_000, 0, 0, 0, 0)! * 2 - ) - }) -}) diff --git a/src/main/claude/claude-agent-sdk-process-spawn.test.ts b/src/main/claude/claude-agent-sdk-process-spawn.test.ts index af4f743f7fd..a79821a55b3 100644 --- a/src/main/claude/claude-agent-sdk-process-spawn.test.ts +++ b/src/main/claude/claude-agent-sdk-process-spawn.test.ts @@ -35,70 +35,6 @@ function sdkOptions(overrides: Partial = {}): SdkSpawnOptions { } describe('claude agent SDK process spawn', () => { - it('routes the SDK spawn through Orca and retains the pid the lease adjudicates on', () => { - const process = fakeSpawn() - const spawn = createClaudeCodeProcessSpawn(process.spawnImpl, 'win32') - - expect(spawn.pid).toBeUndefined() - expect(spawn.child).toBeNull() - const child = spawn.spawn(sdkOptions()) - - expect(child).toBe(process.child) - expect(spawn.child).toBe(process.child) - expect(spawn.pid).toBe(4321) - expect(spawn.supervised).toBe(false) - expect(process.specs[0]).toEqual({ - program: '/usr/local/bin/claude', - args: ['--output-format', 'stream-json'], - cwd: '/work/repo', - env: { PATH: '/usr/bin', CLAUDE_CONFIG_DIR: '/accounts/one' }, - detached: false, - stdio: ['pipe', 'pipe', 'pipe'] - }) - }) - - it.each(['darwin', 'linux'] as const)( - 'starts Claude under the provider supervisor on %s, which is then the pid the lease records', - (platform) => { - const process = fakeSpawn() - const spawn = createClaudeCodeProcessSpawn(process.spawnImpl, platform) - spawn.spawn(sdkOptions()) - - const [spec] = process.specs - if (!spec) { - throw new Error('the spawner never built a spec') - } - expect(spawn.supervised).toBe(true) - expect(spawn.pid).toBe(4321) - expect(spec.program).toBe(globalThis.process.execPath) - expect(spec.args?.[0]).toBe('-e') - expect(spec.args?.slice(2)).toEqual([ - '--', - '/usr/local/bin/claude', - '--output-format', - 'stream-json' - ]) - expect(spec.detached).toBe(true) - expect(spec.cwd).toBe('/work/repo') - const supervisorSpec = JSON.parse( - Buffer.from(String(spec.env?.ORCA_PROVIDER_SUPERVISOR_SPEC), 'base64').toString() - ) - // Orca exit must trigger Claude's close ladder, including descendant cleanup. - expect(vi.mocked(createProviderSpawnSpec)).toHaveBeenLastCalledWith( - expect.anything(), - expect.anything(), - platform, - { closeRequest: 'stdin-end-and-sigterm' } - ) - expect(supervisorSpec).toMatchObject({ - closeRequest: 'stdin-end-and-sigterm', - cwd: '/work/repo', - ownerPid: globalThis.process.pid - }) - expect(spec.env).toMatchObject({ PATH: '/usr/bin', CLAUDE_CONFIG_DIR: '/accounts/one' }) - } - ) - it.each([ { platform: 'darwin', specSupervised: false }, { platform: 'win32', specSupervised: true } @@ -141,14 +77,6 @@ describe('claude agent SDK process spawn', () => { } ) - it('keeps the child out of the SDK abort path so exit proof stays Orca-owned', () => { - const process = fakeSpawn() - const controller = new AbortController() - createClaudeCodeProcessSpawn(process.spawnImpl).spawn(sdkOptions({ signal: controller.signal })) - - expect(process.specs[0]).not.toHaveProperty('signal') - }) - it('drains stderr into a bounded tail so an exit error still carries it', async () => { const process = fakeSpawn() const spawn = createClaudeCodeProcessSpawn(process.spawnImpl) diff --git a/src/main/claude/claude-hook-event-versions.test.ts b/src/main/claude/claude-hook-event-versions.test.ts index d5f4d202a0a..37e5b67d652 100644 --- a/src/main/claude/claude-hook-event-versions.test.ts +++ b/src/main/claude/claude-hook-event-versions.test.ts @@ -4,110 +4,13 @@ import { readManagedHookDetectionResult } from '../agent-hooks/managed-hook-dete import { getClaudeManagedHookPlan } from './claude-managed-hook-events' vi.mock('@orca/process-host', () => ({ runProcess: vi.fn() })) -import { compareAppVersions } from '../../shared/app-version' -import fixture from './__fixtures__/claude-hook-event-enums.json' + import { - CLAUDE_HOOK_EVENT_FIRST_VERSIONS, - CLAUDE_STATUS_LINE_FIRST_VERSION, - claudeKnowsHookEvent, - claudeKnowsStatusLine, parseClaudeCliVersion, parseClaudeCliVersionOutput, - probeClaudeCliVersion, - UNRESOLVED_CLAUDE_VERSION, - type ClaudeHookEventName + probeClaudeCliVersion } from './claude-hook-event-versions' -const enums: Record = fixture.enums -const previousPublished: Record = fixture.previousPublishedVersion -const topLevelSettings: Record = - fixture.topLevelSettings -const versions = Object.keys(enums).sort(compareAppVersions) -const isTableEvent = (name: string): name is ClaudeHookEventName => - name in CLAUDE_HOOK_EVENT_FIRST_VERSIONS -const tableEntries = Object.keys(CLAUDE_HOOK_EVENT_FIRST_VERSIONS) - .filter(isTableEvent) - .map((event): [ClaudeHookEventName, string] => [event, CLAUDE_HOOK_EVENT_FIRST_VERSIONS[event]]) - -describe('Claude hook event version table', () => { - it.each(tableEntries)('pins %s to the first release whose enum knows it', (event, first) => { - expect(versions.find((version) => enums[version].includes(event))).toBe(first) - for (const version of versions) { - expect(enums[version].includes(event), `${event} in ${version}`).toBe( - compareAppVersions(version, first) >= 0 - ) - } - const before = previousPublished[first] - expect(enums[before], `enum for ${before}`).toBeDefined() - expect(enums[before]).not.toContain(event) - }) -}) - -describe('Claude statusLine version floor', () => { - it('pins statusLine to the first release whose settings schema knows it', () => { - for (const [version, schema] of Object.entries(topLevelSettings)) { - expect(schema.keys.includes('statusLine'), version).toBe( - compareAppVersions(version, CLAUDE_STATUS_LINE_FIRST_VERSION) >= 0 - ) - } - const before = previousPublished[CLAUDE_STATUS_LINE_FIRST_VERSION] - expect(topLevelSettings[before].strict).toBe(true) - expect(topLevelSettings[before].keys).not.toContain('statusLine') - }) - - it.each([ - ['1.0.63', false], - ['1.0.64 (Claude Code)', true], - [undefined, true] - ] as const)('%s knows statusLine: %s', (version, expected) => { - expect(claudeKnowsStatusLine(version)).toBe(expected) - }) -}) - -describe('claudeKnowsHookEvent', () => { - it.each([ - ['2.1.77', 'StopFailure', false], - ['2.1.78 (Claude Code)', 'StopFailure', true], - ['2.1.75', 'PostCompact', false], - ['2.1.76', 'PostCompact', true], - ['2.1.32', 'TeammateIdle', false], - ['2.0.55', 'PostToolUseFailure', false], - ['2.0.44', 'PermissionRequest', false], - ['2.0.42', 'SubagentStart', false], - ['1.0.84', 'SessionEnd', false], - ['1.0.61', 'SessionStart', false], - ['1.0.62', 'SessionStart', true], - ['1.0.52', 'UserPromptSubmit', false], - ['1.0.40', 'SubagentStop', false], - ['1.0.30', 'Stop', false], - ['1.0.22', 'PreToolUse', false] - ] as const)('%s knows %s: %s', (version, event, expected) => { - expect(claudeKnowsHookEvent(version, event)).toBe(expected) - }) - - it.each([undefined, null, 'unknown'])( - 'grants an unresolved version (%s) only the events its assumed release knows', - (version) => { - const known = tableEntries - .filter(([event]) => claudeKnowsHookEvent(version, event)) - .map(([event]) => event) - expect(known.sort()).toEqual( - [ - 'PostToolUse', - 'PreToolUse', - 'SessionStart', - 'Stop', - 'SubagentStop', - 'UserPromptSubmit' - ].sort() - ) - for (const event of known) { - expect(enums[UNRESOLVED_CLAUDE_VERSION]).toContain(event) - } - } - ) -}) - describe('parseClaudeCliVersion', () => { it('extracts Claude Code version output', () => { expect(parseClaudeCliVersion('2.1.261 (Claude Code)')).toBe('2.1.261') diff --git a/src/main/claude/claude-streamed-text-checkpoints.test.ts b/src/main/claude/claude-streamed-text-checkpoints.test.ts index efb1f22640b..d1cd11f5969 100644 --- a/src/main/claude/claude-streamed-text-checkpoints.test.ts +++ b/src/main/claude/claude-streamed-text-checkpoints.test.ts @@ -98,18 +98,6 @@ describe('claude streamed text checkpoints', () => { expect(rows).toEqual([{ uuid: 'block-1', text: 'partial answer' }]) }) - it('keeps a block whose final frame arrived out of the settlement sweep', () => { - const { store } = checkpoints() - - store.append(identityOf('block-1'), 'one') - store.append(identityOf('block-2'), 'two') - store.forget('claude:claude-session:block-1') - - expect(store.pending).toBe(1) - store.settle() - expect(store.pending).toBe(0) - }) - it('flushes text the widening checkpoint interval has not written yet', () => { const { store, rows } = checkpoints() @@ -121,18 +109,6 @@ describe('claude streamed text checkpoints', () => { expect(rows).toHaveLength(1) }) - it("stamps a block streamed inside a child with that child's linkage", () => { - const producer = scriptedProducer() - producer.resolve(CHILD_LINKAGE) - const { store, rows, stamps, runWindow } = checkpoints(producer.source) - - store.append(identityOf('block-1'), 'hello', 'toolu_1') - runWindow() - - expect(rows).toEqual([{ uuid: 'block-1', text: 'hello' }]) - expect(stamps).toEqual([CHILD_LINKAGE]) - }) - it("writes no linkage keys for a block the session's own agent streamed", () => { const { store, stamps, runWindow } = checkpoints() @@ -162,22 +138,6 @@ describe('claude streamed text checkpoints', () => { expect(stamps.at(-1)).toEqual(CHILD_LINKAGE) }) - it('writes a held block under the raw reference when no announcement comes', () => { - const producer = scriptedProducer() - const { store, rows, stamps, runWindow } = checkpoints(producer.source) - - store.append(identityOf('block-1'), 'never announced', 'toolu_1') - runWindow() - - expect(rows).toEqual([{ uuid: 'block-1', text: 'never announced' }]) - expect(stamps).toEqual([ - { agentId: 'toolu_1', providerParentRef: 'toolu_1', producerKind: 'agent' } - ]) - - store.reattribute() - expect(rows).toHaveLength(1) - }) - it('re-resolves a block’s producer on every checkpoint', () => { const producer = scriptedProducer() producer.resolve(CHILD_LINKAGE) diff --git a/src/main/claude/post-compact-hook-command.test.ts b/src/main/claude/post-compact-hook-command.test.ts index b8d8ae6777e..1279b822a12 100644 --- a/src/main/claude/post-compact-hook-command.test.ts +++ b/src/main/claude/post-compact-hook-command.test.ts @@ -1,69 +1,19 @@ import { runProcess } from '@orca/process-host' -import { chmodSync, mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from 'node:fs' +import { chmodSync, mkdirSync, mkdtempSync, rmSync, writeFileSync } from 'node:fs' import { tmpdir } from 'node:os' import { join } from 'node:path' import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' -import { createAgentHookMemorySftp } from '../agent-hooks/agent-hook-memory-sftp.test-fixture' -import { wrapRuntimeHomePosixHookCommand } from '../agent-hooks/runtime-home-hook-command' -import { ClaudeHookService } from './hook-service' -import { - CLAUDE_HOOK_SETTINGS, - getManagedEventHook, - getManagedLifecycleHook, - getRemoteManagedCommand, - getRemoteManagedEventCommand -} from './hook-settings' + +import { getManagedEventHook } from './hook-settings' vi.mock('electron', () => ({ app: { getPath: () => '/tmp/userData' } })) const SCRIPT_PATH = '/home/test/.orca/agent-hooks/claude-hook.sh' -const CURRENT_CLAUDE = { claudeVersion: '2.1.261 (Claude Code)' } + // Claude echoes the full command after /compact, so keep it short. -const MAX_ECHOED_COMMAND_LENGTH = 400 afterEach(() => vi.restoreAllMocks()) -describe('PostCompact managed hook command', () => { - it.each(['darwin', 'linux'] as const)('is the short POSIX form on %s', (platform) => { - vi.spyOn(process, 'platform', 'get').mockReturnValue(platform) - const { command } = getManagedEventHook('PostCompact', SCRIPT_PATH) - - expect(command.length).toBeLessThan(MAX_ECHOED_COMMAND_LENGTH) - expect(command).toContain('if [ -n "${HOME-}" ] && ') - expect(command).toContain('/bin/sh "${HOME-}/.orca/agent-hooks/claude-hook.sh"') - expect(command).toContain('ORCA_HOOK_AGENT_PID="${PPID:-}"') - expect(command).not.toMatch(/powershell|OSTYPE|\/home\/test/i) - }) - - it('leaves every other event on the full wrapper', () => { - vi.spyOn(process, 'platform', 'get').mockReturnValue('linux') - expect(getManagedEventHook('Stop', SCRIPT_PATH)).toEqual( - getManagedLifecycleHook(SCRIPT_PATH, CLAUDE_HOOK_SETTINGS) - ) - }) - - it('keeps the Windows launcher on win32, whose bare script path is already short', () => { - vi.spyOn(process, 'platform', 'get').mockReturnValue('win32') - const windowsPath = 'C:\\Users\\alice\\.orca\\agent-hooks\\claude-hook.cmd' - expect(getManagedEventHook('PostCompact', windowsPath)).toEqual( - getManagedLifecycleHook(windowsPath, CLAUDE_HOOK_SETTINGS) - ) - }) - - it.each(['darwin', 'linux', 'win32'] as const)( - 'uses the short POSIX form only for remote PostCompact from %s', - (platform) => { - vi.spyOn(process, 'platform', 'get').mockReturnValue(platform) - expect(getRemoteManagedEventCommand('PostCompact', SCRIPT_PATH)).toBe( - wrapRuntimeHomePosixHookCommand('claude-hook') - ) - expect(getRemoteManagedEventCommand('Stop', SCRIPT_PATH)).toBe( - getRemoteManagedCommand(SCRIPT_PATH) - ) - } - ) -}) - describe.skipIf(process.platform === 'win32')('PostCompact command execution', () => { let home: string @@ -109,67 +59,4 @@ describe.skipIf(process.platform === 'win32')('PostCompact command execution', ( expect(result.stdout).toBe('') expect(result.stderr).toBe('') }) - - it.each([{}, { HOME: '' }])('skips script lookup without a runtime HOME: %j', async (env) => { - const lookupProbe = [ - 'probe_test() {', - ' case "$1" in -f|-r|-x) printf "unexpected script lookup: %s\\n" "$2" >&2; return 1 ;;', - ' *) command [ "$@" ;; esac', - '}', - 'alias [=probe_test', - getManagedEventHook('PostCompact', SCRIPT_PATH).command - ].join('\n') - - const result = await run(lookupProbe, env) - - expect(result.code).toBe(0) - expect(result.stdout).toBe('') - expect(result.stderr).toBe('') - }) -}) - -describe.skipIf(process.platform === 'win32')('ClaudeHookService PostCompact install', () => { - it('writes the short form, reports installed, and replaces an old full-wrapper entry', () => { - const home = mkdtempSync(join(tmpdir(), 'orca-post-compact-install-')) - vi.stubEnv('HOME', home) - try { - const settingsPath = join(home, '.claude', 'settings.json') - mkdirSync(join(home, '.claude'), { recursive: true }) - const fullWrapper = getManagedLifecycleHook(SCRIPT_PATH).command - writeFileSync( - settingsPath, - JSON.stringify({ - hooks: { - PostCompact: [{ hooks: [{ type: 'command', command: fullWrapper }] }] - } - }) - ) - - const status = new ClaudeHookService().install(CURRENT_CLAUDE) - - expect(status.state).toBe('installed') - const written = JSON.parse(readFileSync(settingsPath, 'utf-8')) - expect(written.hooks.PostCompact).toEqual([ - { hooks: [getManagedEventHook('PostCompact', SCRIPT_PATH)] } - ]) - expect(written.hooks.Stop[0].hooks[0].command).toBe(fullWrapper) - } finally { - vi.unstubAllEnvs() - rmSync(home, { recursive: true, force: true }) - } - }) -}) - -describe('ClaudeHookService remote PostCompact install', () => { - it('writes the short form for PostCompact and the full wrapper elsewhere', async () => { - const { sftp, fs } = createAgentHookMemorySftp() - await new ClaudeHookService().installRemote(sftp, '/home/dev', CURRENT_CLAUDE) - - const written = JSON.parse(fs.files.get('/home/dev/.claude/settings.json') ?? '{}') - const remoteScript = '/home/dev/.orca/agent-hooks/claude-hook.sh' - expect(written.hooks.PostCompact[0].hooks[0].command).toBe( - getRemoteManagedEventCommand('PostCompact', remoteScript) - ) - expect(written.hooks.Stop[0].hooks[0].command).toBe(getRemoteManagedCommand(remoteScript)) - }) }) diff --git a/src/main/claude/structured/claude-structured-reasoning-lifecycle.test.ts b/src/main/claude/structured/claude-structured-reasoning-lifecycle.test.ts index f6a19b625e2..01c7d06e8b6 100644 --- a/src/main/claude/structured/claude-structured-reasoning-lifecycle.test.ts +++ b/src/main/claude/structured/claude-structured-reasoning-lifecycle.test.ts @@ -134,28 +134,6 @@ describe('a streamed reasoning row the provider never finishes', () => { expect(translator.pendingStreamedBlocks).toBe(0) }) - it('is not rewritten by the token tally Claude sends after every thinking delta', () => { - const { translator, message, thinkingDelta, reasoningWrites } = setup() - for (let index = 0; index < 20; index += 1) { - thinkingDelta(`delta-${index}`, 'more words ', 1_000 + index) - translator.handle( - message( - { - type: 'system', - subtype: 'thinking_tokens', - estimated_tokens: index, - estimated_tokens_delta: 1, - uuid: `tokens-${index}` - }, - 1_000 + index - ) - ) - } - expect(reasoningWrites()).toEqual([]) - vi.advanceTimersByTime(100) - expect(reasoningWrites()).toHaveLength(1) - }) - // Timings of the first block in a captured 2.1.280 session with summarized display. it('spans from the block start to its final frame, though text arrives seconds later', () => { const { translator, message, thinkingDelta, reasoningWrites } = setup() diff --git a/src/main/claude/structured/claude-structured-reasoning.test.ts b/src/main/claude/structured/claude-structured-reasoning.test.ts index 12c7c93c6a1..e175f783d87 100644 --- a/src/main/claude/structured/claude-structured-reasoning.test.ts +++ b/src/main/claude/structured/claude-structured-reasoning.test.ts @@ -53,13 +53,6 @@ function reasoning(text: string, lifecycle: Record): AgentJourn afterEach(() => vi.useRealTimers()) describe('structured Claude reasoning', () => { - it.each(['', ' ', '\n\t'])('omits blank final thinking %j', (thinking) => { - const { rows, final, translator } = setup() - final('final-only', [{ type: 'thinking', thinking }]) - expect([...rows.values()]).toEqual([]) - translator.dispose() - }) - it('writes final-only thinking closed, with no span it never saw', () => { const { rows, final, translator } = setup() final('final-only', [{ type: 'thinking', thinking: 'Inspecting the request' }], 5_000) @@ -201,17 +194,4 @@ describe('structured Claude reasoning', () => { expect(scopes).toEqual([{ kind: 'turn', turnItemId: expect.stringContaining('turn') }]) translator.dispose() }) - - it('omits whitespace-only thinking deltas', () => { - vi.useFakeTimers() - const { rows, stream, translator } = setup() - stream('delta', { - type: 'content_block_delta', - index: 0, - delta: { type: 'thinking_delta', thinking: ' \n ' } - }) - translator.flush() - expect(rows.size).toBe(0) - translator.dispose() - }) }) diff --git a/src/main/claude/structured/launch/claude-structured-launch-visuals.test.ts b/src/main/claude/structured/launch/claude-structured-launch-visuals.test.ts index 64e5a4c4392..c9d98c36b95 100644 --- a/src/main/claude/structured/launch/claude-structured-launch-visuals.test.ts +++ b/src/main/claude/structured/launch/claude-structured-launch-visuals.test.ts @@ -3,14 +3,9 @@ import { describe, expect, it, vi } from 'vitest' import type { AgentSessionJournalIdentity } from '../../../../shared/agent-session/journal/agent-session-journal-identity' import { agentSessionRecordFixture } from '../../../native-chat/agent-session-record-test-fixture' import { LOCAL_EXECUTION_HOST_ID } from '../../../../shared/execution-host' +import type { NativeChatVisualsLaunch } from '../../../native-chat/native-chat-visuals-delivery' import { - NATIVE_CHAT_SCRATCH_DIR_ENV, - type NativeChatVisualsLaunch -} from '../../../native-chat/native-chat-visuals-delivery' -import { - CLAUDE_CLI_FLAG_PROBE_KILL_AFTER_MS, CLAUDE_PLUGIN_DIR_FLAG, - CLAUDE_THINKING_DISPLAY_FLAG, type ClaudeCliFlag, type ClaudeCliFlagFact } from '../../claude-cli-flag-support' @@ -59,80 +54,6 @@ function launch(options: { } describe('a Claude chat launch with inline visuals', () => { - it('loads the skill plugin, grants the chat folder beside the user directories, and names it', async () => { - const prepareVisuals = vi.fn(async () => VISUALS) - const resolved = await launch({ - prepareVisuals, - supports: async (flag) => flag === CLAUDE_PLUGIN_DIR_FLAG, - launchArgs: ['--add-dir', '/shared/notes'] - }) - expect(prepareVisuals).toHaveBeenCalledWith(SESSION_ID) - expect(resolved.options.plugins).toEqual([{ type: 'local', path: VISUALS.skill.pluginDir }]) - expect(resolved.options.additionalDirectories).toEqual(['/shared/notes', VISUALS.folder]) - expect(resolved.env?.[NATIVE_CHAT_SCRATCH_DIR_ENV]).toBe(VISUALS.folder) - }) - - it.each([ - { - name: 'prepared visuals', - visuals: VISUALS, - directories: ['/shared/notes', '/state/agent-session-attachments', VISUALS.folder] - }, - { - name: 'unavailable visuals', - visuals: null, - directories: ['/shared/notes', '/state/agent-session-attachments'] - } - ])('keeps user folders and attachment access with $name', async ({ visuals, directories }) => { - const resolved = await launch({ - prepareVisuals: async () => visuals, - supports: async (flag) => flag === CLAUDE_PLUGIN_DIR_FLAG, - launchArgs: ['--add-dir', '/shared/notes'], - attachmentDirectory: '/state/agent-session-attachments' - }) - expect(resolved.options.additionalDirectories).toEqual(directories) - }) - - it('still grants and names the folder when the CLI cannot load a plugin by path', async () => { - const resolved = await launch({ - prepareVisuals: async () => VISUALS, - supports: async () => false - }) - expect(resolved.options).not.toHaveProperty('plugins') - expect(resolved.options.additionalDirectories).toEqual([VISUALS.folder]) - expect(resolved.env?.[NATIVE_CHAT_SCRATCH_DIR_ENV]).toBe(VISUALS.folder) - }) - - it('without a prepared folder, grants nothing and drops a folder inherited from another chat', async () => { - const resolved = await launch({ - prepareVisuals: async () => null, - supports: async () => true, - env: { [NATIVE_CHAT_SCRATCH_DIR_ENV]: '/state/chat-scratch/other-chat' } - }) - expect(resolved.options).not.toHaveProperty('plugins') - expect(resolved.options).not.toHaveProperty('additionalDirectories') - expect(resolved.env).not.toHaveProperty(NATIVE_CHAT_SCRATCH_DIR_ENV) - }) - - it('never asks about the plugin flag on a host that delivers no visuals', async () => { - const supports = vi.fn(async (_flag: ClaudeCliFlag) => true) - const resolved = await launch({ supports }) - expect(supports.mock.calls.map(([flag]) => flag)).not.toContain(CLAUDE_PLUGIN_DIR_FLAG) - expect(resolved.options).not.toHaveProperty('plugins') - }) - - it('waits for the version up to the probe kill time for the plugin, never the short budget', async () => { - const supports = vi.fn( - async (_flag: ClaudeCliFlag, _launch: unknown, _budgetMs?: number) => true - ) - await launch({ prepareVisuals: async () => VISUALS, supports }) - expect(supports).toHaveBeenCalledWith( - CLAUDE_PLUGIN_DIR_FLAG, - expect.objectContaining({ command: '/usr/local/bin/claude' }), - CLAUDE_CLI_FLAG_PROBE_KILL_AFTER_MS - ) - }) - it('asks for readable thinking again once the plugin check has learned the version', async () => { let versionKnown = false const supports = vi.fn(async (flag: ClaudeCliFlag, _launch: unknown, budgetMs?: number) => { @@ -147,14 +68,4 @@ describe('a Claude chat launch with inline visuals', () => { expect(resolved.options.plugins).toHaveLength(1) expect(resolved.options.extraArgs).toMatchObject({ 'thinking-display': 'summarized' }) }) - - it('never re-asks for thinking on a host without visuals', async () => { - const supports = vi.fn( - async (_flag: ClaudeCliFlag, _launch: unknown, _budgetMs?: number) => false - ) - await launch({ supports }) - expect( - supports.mock.calls.filter(([flag]) => flag === CLAUDE_THINKING_DISPLAY_FLAG) - ).toHaveLength(1) - }) }) diff --git a/src/main/claude/windows-hook-files.test.ts b/src/main/claude/windows-hook-files.test.ts index 61b973d5d5a..c2a6f5aa7f2 100644 --- a/src/main/claude/windows-hook-files.test.ts +++ b/src/main/claude/windows-hook-files.test.ts @@ -8,10 +8,6 @@ import { readHooksJson, writeHooksJson } from '../agent-hooks/installer-utils' import { ClaudeHookService } from './hook-service' import { getWindowsManagedLifecycleHook } from './hook-settings' import { getWindowsClaudeHookEntry, getWindowsClaudeHookPayloadPath } from './windows-hook-files' -import { codebuddyHookService } from '../codebuddy/hook-service' -import { qoderHookService, qoderCnHookService } from '../qoder/hook-service' -import { qwenCodeHookService } from '../qwen-code/hook-service' -import { openClaudeHookService } from '../openclaude/hook-service' const { home } = vi.hoisted(() => ({ home: { path: '' } })) vi.mock('node:os', async (original) => ({ @@ -41,16 +37,6 @@ afterEach(() => { }) describe('Windows Claude hook files', () => { - it('installs a delegating entry and a payload that answers before bounded delivery', () => { - expect(service.install().state).toBe('installed') - expect(readFileSync(entry, 'utf8')).toBe(getWindowsClaudeHookEntry()) - const body = readFileSync(payload, 'utf8') - expect(body.indexOf('echo {}')).toBeLessThan(body.indexOf('curl.exe')) - expect(body).toContain('--connect-timeout 0.5 --max-time 1.5') - expect(body).toContain('--data-urlencode "payload@-" >nul 2>&1\r\nexit /b 0') - expect(body).toContain('DEVIN_PROJECT_DIR') - }) - it('repairs a missing payload from the surviving entry without touching settings', async () => { service.install() rmSync(payload) @@ -140,24 +126,4 @@ describe('Windows Claude hook files', () => { expect(config).toContain('echo user-hook') expect(service.getStatus().state).toBe('installed') }) - - it('keeps compatible agents on their existing single-file scripts', async () => { - for (const [name, compatible] of [ - ['qoder', qoderHookService], - ['qoder-cn', qoderCnHookService], - ['qwen-code', qwenCodeHookService], - ['codebuddy', codebuddyHookService], - ['openclaude', openClaudeHookService] - ] as const) { - expect(compatible.install().state).toBe('installed') - await compatible.refreshManagedScripts() - const path = join(home.path, '.orca', 'agent-hooks', `${name}-hook.cmd`) - const source = name === 'openclaude' ? 'claude' : name - expect(readFileSync(path, 'utf8')).toContain(`/hook/${source}`) - expect(readFileSync(path, 'utf8')).not.toContain('claude-hook-impl.cmd') - expect(existsSync(join(home.path, '.orca', 'agent-hooks', `${name}-hook-impl.cmd`))).toBe( - false - ) - } - }) }) diff --git a/src/main/codex/configuration/shared-server/codex-shared-server-fix.test.ts b/src/main/codex/configuration/shared-server/codex-shared-server-fix.test.ts index 7de9dd3a4b7..aa2b69c4911 100644 --- a/src/main/codex/configuration/shared-server/codex-shared-server-fix.test.ts +++ b/src/main/codex/configuration/shared-server/codex-shared-server-fix.test.ts @@ -57,10 +57,6 @@ describe('resolveCodexSharedServerBinary', () => { const legacy = installPackage('standalone', 'current') expect(await resolveCodexSharedServerBinary(home)).toBe(legacy) }) - - it('is null when no package is installed', async () => { - expect(await resolveCodexSharedServerBinary(home)).toBeNull() - }) }) describe('readFeatureEnabled', () => { diff --git a/src/main/daemon/history-reader-memory.test.ts b/src/main/daemon/history-reader-memory.test.ts index bb8be3f60e2..4194658b140 100644 --- a/src/main/daemon/history-reader-memory.test.ts +++ b/src/main/daemon/history-reader-memory.test.ts @@ -149,34 +149,4 @@ describe('terminal history restore memory limits', () => { expect(restore?.snapshotAnsi).toBe('safe checkpoint') expect(restore?.oscLinks).toHaveLength(oscLinks.length) }) - - // Call the reader directly so fallback behavior cannot hide a parsing regression. - it('parses link-dense checkpoints without a structural pre-scan budget', () => { - const { sessionPath } = createSession('link-dense-checkpoint') - const checkpointPath = join(sessionPath, 'checkpoint.json') - writeFileSync(checkpointPath, `{"snapshotAnsi":"","values":[${'0,'.repeat(2_000_000)}0]}`) - expect( - readTerminalHistoryJson<{ values: number[] }>( - checkpointPath, - TERMINAL_HISTORY_CHECKPOINT_MAX_BYTES - ).values - ).toHaveLength(2_000_001) - }) - - // V8 parses JSON iteratively; recursive parsing could abort the daemon instead of throwing. - it('parses deeply nested checkpoints without the retired nesting-depth cap', () => { - const { sessionPath } = createSession('deeply-nested-checkpoint') - const checkpointPath = join(sessionPath, 'checkpoint.json') - const depth = 50_000 - writeFileSync( - checkpointPath, - `{"snapshotAnsi":"","nested":${'['.repeat(depth)}${']'.repeat(depth)}}` - ) - expect( - readTerminalHistoryJson<{ nested: unknown[] }>( - checkpointPath, - TERMINAL_HISTORY_CHECKPOINT_MAX_BYTES - ).nested - ).toHaveLength(1) - }) }) diff --git a/src/main/git/wsl-windows-checkout-git-config.test.ts b/src/main/git/wsl-windows-checkout-git-config.test.ts index de84363997b..6c9a3fd9489 100644 --- a/src/main/git/wsl-windows-checkout-git-config.test.ts +++ b/src/main/git/wsl-windows-checkout-git-config.test.ts @@ -8,7 +8,6 @@ import { resetWslWindowsCheckoutGitConfigForTests, UNPROVEN_CHECKOUT_WRITE_GIT_CONFIG_ARGS, usesWindowsCheckoutGitConfig, - WINDOWS_CHECKOUT_GIT_CONFIG_ARGS, withWslWindowsCheckoutGitConfig, wslWindowsCheckoutKey, WSL_WINDOWS_CHECKOUT_TTL_MS @@ -93,18 +92,6 @@ describe('which commands run with Windows Git settings', () => { expect(withWslWindowsCheckoutGitConfig(read, CWD, 'Ubuntu', 'win32', NOW)).toEqual(read) }) - it('flags reads as soon as the host answers, before the sample', () => { - recordHostAutocrlf(KEY, 'true', NOW) - expect(withWslWindowsCheckoutGitConfig(read, CWD, 'Ubuntu', 'win32', NOW)).toEqual([ - ...WINDOWS_CHECKOUT_GIT_CONFIG_ARGS, - ...read - ]) - expect(withWslWindowsCheckoutGitConfig(write, CWD, 'Ubuntu', 'win32', NOW)).toEqual([ - ...UNPROVEN_CHECKOUT_WRITE_GIT_CONFIG_ARGS, - ...write - ]) - }) - it('stops flagging reads once the sample proves an LF checkout', () => { recordHostAutocrlf(KEY, 'true', NOW) recordLineEndingSample(KEY, 'not-windows', 'id', NOW) diff --git a/src/main/keybindings/keybinding-service.test.ts b/src/main/keybindings/keybinding-service.test.ts index 42e5892470a..bdadfec00fa 100644 --- a/src/main/keybindings/keybinding-service.test.ts +++ b/src/main/keybindings/keybinding-service.test.ts @@ -1,22 +1,13 @@ -import { existsSync, mkdtempSync, rmSync, writeFileSync } from 'node:fs' +import { 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 { - getEffectiveKeybindingsForAction, - keybindingMatchesAction, - LEGACY_TAB_SWITCH_BINDINGS, - type KeybindingActionId, - type KeybindingInput -} from '../../shared/keybindings' +import { getEffectiveKeybindingsForAction } from '../../shared/keybindings' import { getUserKeybindingsPath, writeKeybindingOverride } from './keybinding-file' import { KeybindingService } from './keybinding-service' // Verify effective bindings and keystroke matching for fresh/existing cohorts across all platforms. -const PLATFORMS: NodeJS.Platform[] = ['darwin', 'linux', 'win32'] -const SWAPPED_ACTIONS = Object.keys(LEGACY_TAB_SWITCH_BINDINGS) as KeybindingActionId[] - function makeCohort(pending: boolean): { controller: { isPending: () => boolean; markSeeded: () => void } seeded: () => boolean @@ -36,22 +27,6 @@ function makeCohort(pending: boolean): { } // Physical bracket codes are layout-independent; Mod maps to the platform modifier. -function bracketPress(opts: { - code: 'BracketLeft' | 'BracketRight' - alt: boolean - shift: boolean - platform: NodeJS.Platform -}): KeybindingInput { - const isMac = opts.platform === 'darwin' - return { - code: opts.code, - key: opts.code === 'BracketRight' ? ']' : '[', - meta: isMac, - control: !isMac, - alt: opts.alt, - shift: opts.shift - } -} describe('KeybindingService tab-switch cohort seeding', () => { let home: string @@ -64,84 +39,6 @@ describe('KeybindingService tab-switch cohort seeding', () => { rmSync(home, { recursive: true, force: true }) }) - it.each(PLATFORMS)( - 'fresh install (%s) adopts the swapped defaults and writes no file', - (platform) => { - const cohort = makeCohort(false) - const service = new KeybindingService({ - homePath: home, - platform, - legacyTabSwitchSeed: cohort.controller - }) - - expect(cohort.seeded()).toBe(false) - expect(existsSync(getUserKeybindingsPath(home))).toBe(false) - - const overrides = service.getOverrides() - expect(getEffectiveKeybindingsForAction('tab.nextAllTypes', platform, overrides)).toEqual([ - 'Mod+Shift+BracketRight' - ]) - expect(getEffectiveKeybindingsForAction('tab.nextSameType', platform, overrides)).toEqual([ - 'Mod+Alt+BracketRight' - ]) - - expect( - keybindingMatchesAction( - 'tab.nextAllTypes', - bracketPress({ code: 'BracketRight', alt: false, shift: true, platform }), - platform, - overrides - ) - ).toBe(true) - expect( - keybindingMatchesAction( - 'tab.nextSameType', - bracketPress({ code: 'BracketRight', alt: false, shift: true, platform }), - platform, - overrides - ) - ).toBe(false) - } - ) - - it.each(PLATFORMS)( - 'existing install (%s) keeps the pre-swap chords exactly, on a clean profile', - (platform) => { - const cohort = makeCohort(true) - const service = new KeybindingService({ - homePath: home, - platform, - legacyTabSwitchSeed: cohort.controller - }) - - expect(cohort.seeded()).toBe(true) - const overrides = service.getOverrides() - - for (const actionId of SWAPPED_ACTIONS) { - expect(getEffectiveKeybindingsForAction(actionId, platform, overrides)).toEqual( - LEGACY_TAB_SWITCH_BINDINGS[actionId] - ) - } - - // Existing installs keep the inverse of fresh-install tab-switch defaults. - const shiftRight = bracketPress({ code: 'BracketRight', alt: false, shift: true, platform }) - expect(keybindingMatchesAction('tab.nextSameType', shiftRight, platform, overrides)).toBe( - true - ) - expect(keybindingMatchesAction('tab.nextAllTypes', shiftRight, platform, overrides)).toBe( - false - ) - - const altLeft = bracketPress({ code: 'BracketLeft', alt: true, shift: false, platform }) - expect(keybindingMatchesAction('tab.previousAllTypes', altLeft, platform, overrides)).toBe( - true - ) - expect(keybindingMatchesAction('tab.previousSameType', altLeft, platform, overrides)).toBe( - false - ) - } - ) - it('existing install keeps a rebound action AND pins the pre-swap default on the rest', () => { const platform: NodeJS.Platform = 'darwin' writeKeybindingOverride(getUserKeybindingsPath(home), platform, 'tab.nextSameType', ['Mod+K']) @@ -168,30 +65,6 @@ describe('KeybindingService tab-switch cohort seeding', () => { ]) }) - it('is idempotent: a second launch after the seed changes nothing', () => { - const platform: NodeJS.Platform = 'linux' - const cohort = makeCohort(true) - const first = new KeybindingService({ - homePath: home, - platform, - legacyTabSwitchSeed: cohort.controller - }) - const afterFirst = first.getOverrides() - - // Reuse the cohort after markSeeded to model the persisted done flag. - const second = new KeybindingService({ - homePath: home, - platform, - legacyTabSwitchSeed: cohort.controller - }) - expect(second.getOverrides()).toEqual(afterFirst) - for (const actionId of SWAPPED_ACTIONS) { - expect(getEffectiveKeybindingsForAction(actionId, platform, second.getOverrides())).toEqual( - LEGACY_TAB_SWITCH_BINDINGS[actionId] - ) - } - }) - it('leaves the cohort pending when the seed write fails (retries next launch)', () => { // A file at homePath forces mkdir failure; the cohort must remain pending. const fileHome = join(home, 'not-a-dir') diff --git a/src/main/managed-server/install/orcad-remote-node-runtime.test.ts b/src/main/managed-server/install/orcad-remote-node-runtime.test.ts index bee808efc5d..9cfdb33dfb6 100644 --- a/src/main/managed-server/install/orcad-remote-node-runtime.test.ts +++ b/src/main/managed-server/install/orcad-remote-node-runtime.test.ts @@ -21,7 +21,6 @@ import { remoteNodeRuntimeDir } from './orcad-remote-node-runtime' import { - parseRemoteRuntimeExitReport, REMOTE_NODE_RUNTIME_MISSING, REMOTE_NODE_RUNTIME_READY } from '../../ssh/orcad-remote-node-runtime-report' @@ -81,18 +80,6 @@ describe.skipIf(process.platform === 'win32')('remote pinned-Node runtime store' ) }) - it('splits the runtime exit status from the loader output it reported', () => { - expect( - parseRemoteRuntimeExitReport( - 'ORCA_NODE_RUNTIME_SELFTEST_FAILED\nORCA_RUNTIME_EXIT=126\nsh: node: Permission denied\n' - ) - ).toEqual({ exitStatus: 126, output: 'sh: node: Permission denied' }) - expect(parseRemoteRuntimeExitReport('no report')).toEqual({ - exitStatus: null, - output: 'no report' - }) - }) - it('reports a runtime whose bytes do not hash to the pin as missing', () => { const root = mkdtempSync(join(tmpdir(), 'orcad-runtime-store-')) directories.push(root) diff --git a/src/main/managed-server/install/remote-install-model.test.ts b/src/main/managed-server/install/remote-install-model.test.ts index 6994d893271..4a3d0aa2f20 100644 --- a/src/main/managed-server/install/remote-install-model.test.ts +++ b/src/main/managed-server/install/remote-install-model.test.ts @@ -14,49 +14,15 @@ import { inventoryRemoteInstallDirs, ORCAD_INSTALL_MODEL, RELAY_INSTALL_MODEL, - remoteInstallDirName, remoteInstallDirOwner, remoteInstallGcPermits, - remoteInstallListingRegexSource, remoteInstallVersionDirRegex } from './remote-install-model' const RELAY_DIRS = ['relay-0.1.0+abcdef123456', 'relay-v0.1.0', 'relay-1.2.3'] const ORCAD_DIRS = ['orcad-0.1.0+abcdef123456', 'orcad-v0.1.0', 'orcad-1.2.3'] -it.each(['linux-x64', 'darwin-arm64', 'win32-x64'] as const)( - 'requires the runtime reference, node-pty and both profile workers on %s', - (platform) => { - const isWindows = platform.startsWith('win32') - const artifacts = ORCAD_INSTALL_MODEL.requiredArtifacts(getRemoteHostPlatform(platform)) - expect(artifacts).toContain('.runtime-node') - expect(artifacts).not.toContain('bun-runtime') - expect(artifacts).not.toContain('bun-runtime.exe') - expect(artifacts).toContain( - `node_modules/node-pty/build/Release/${isWindows ? 'conpty.node' : 'pty.node'}` - ) - expect(artifacts).toContain('profile-state-writer-worker-entry.js') - expect(artifacts).toContain('profile-state-backup-worker-entry.js') - expect(artifacts.includes('windows-process-tree.node')).toBe(isWindows) - } -) - describe('remote install namespace', () => { - it('names each model its own version dir', () => { - expect(remoteInstallDirName(RELAY_INSTALL_MODEL, '0.1.0+aa')).toBe('relay-0.1.0+aa') - expect(remoteInstallDirName(ORCAD_INSTALL_MODEL, '0.1.0+aa')).toBe('orcad-0.1.0+aa') - }) - - it("requires the host's own search binary in a completed standalone runtime install", () => { - for (const platform of ['linux-arm64', 'darwin-x64', 'win32-arm64'] as const) { - expect( - ORCAD_INSTALL_MODEL.requiredArtifacts(getRemoteHostPlatform(platform)).filter( - (file) => file.startsWith('ripgrep/') && !file.startsWith('ripgrep/licenses/') - ) - ).toEqual([orcadRipgrepArtifact(platform)]) - } - }) - it.skipIf(process.platform === 'win32')('rejects an install missing its search binary', () => { const dir = mkdtempSync(join(tmpdir(), 'orcad-remote-probe-')) try { @@ -76,13 +42,6 @@ describe('remote install namespace', () => { } }) - it('keeps the relay listing pattern byte-identical to the one it shipped with', () => { - // Preserve the shipped listing scope: widening it changes which existing directories GC can see. - expect(remoteInstallListingRegexSource(RELAY_INSTALL_MODEL)).toBe( - String.raw`^relay-(v?[0-9]+\.[0-9]+\.[0-9]+(\+[0-9a-f]+)?)(\.gc-tombstone\.[0-9]+\.[0-9]+)?$` - ) - }) - it('refuses a dir prefix that could escape a remote glob or quote', () => { const injected = { ...RELAY_INSTALL_MODEL, dirPrefix: "relay'; rm -rf ~" } expect(() => remoteInstallVersionDirRegex(injected)).toThrow('Unsafe remote install dir prefix') @@ -116,13 +75,6 @@ describe('GC ownership — each model collects only its own namespace', () => { } }) - it('groups a mixed listing without losing anything to the wrong owner', () => { - const inventory = inventoryRemoteInstallDirs([...RELAY_DIRS, ...ORCAD_DIRS, 'something-else']) - expect(inventory.relay).toEqual(RELAY_DIRS) - expect(inventory.orcad).toEqual(ORCAD_DIRS) - expect(inventory.unknown).toEqual(['something-else']) - }) - it('gives the shared runtime store its own owner that no version-dir GC may take', () => { expect(remoteInstallDirOwner('runtimes')).toBe('runtimes') expect(remoteInstallGcPermits(RELAY_INSTALL_MODEL, 'runtimes')).toBe(false) diff --git a/src/main/native-chat/agent-model-catalog/agent-model-catalog-efforts.test.ts b/src/main/native-chat/agent-model-catalog/agent-model-catalog-efforts.test.ts index 68e2c12fc5c..676511dba38 100644 --- a/src/main/native-chat/agent-model-catalog/agent-model-catalog-efforts.test.ts +++ b/src/main/native-chat/agent-model-catalog/agent-model-catalog-efforts.test.ts @@ -88,23 +88,6 @@ it.each([1, 10])( } ) -it('backs off a successful partial target, keeps saved-only reads passive, and retries after expiry', async () => { - const probe = vi.fn(async () => ({ - origin: 'probe', - models: menu('new') - })) - const { service, advance } = setup(probe) - const params = { agent: 'opencode2', sessionId: 'session-a', selectedModel: B } - await service.read({ ...params, savedOnly: true }) - expect(probe).not.toHaveBeenCalled() - await service.read({ ...params, waitForListing: true }) - await Promise.all(Array.from({ length: 10 }, () => service.demand(params))) - expect(probe).toHaveBeenCalledTimes(1) - advance() - await service.read({ ...params, waitForListing: true }) - expect(probe).toHaveBeenCalledTimes(2) -}) - it.each([ { n: 1, partial: false }, { n: 10, partial: false }, diff --git a/src/main/native-chat/agent-session-journal/command-receipt-schema.spec.ts b/src/main/native-chat/agent-session-journal/command-receipt-schema.spec.ts index d7d7f0d8881..c4885031f02 100644 --- a/src/main/native-chat/agent-session-journal/command-receipt-schema.spec.ts +++ b/src/main/native-chat/agent-session-journal/command-receipt-schema.spec.ts @@ -10,10 +10,7 @@ import { JOURNAL_DB_SCHEMA_VERSION } from './journal-database-schema' import { journalDatabasePath } from './journal-host-database' -import { - closeTestJournalHostDatabases, - openTestJournalHostDatabase -} from './journal-host-database-test-support' +import { closeTestJournalHostDatabases } from './journal-host-database-test-support' import { insertCommandReceiptIfAbsent, readCommandReceipt } from './command-receipt-table' import { commandReceiptScope } from './command-receipt-schema' import { @@ -47,49 +44,6 @@ export function registerCommandReceiptSchemaTests(): void { } describe('command receipt schema at writable open', () => { - it('creates the operation/caller primary key and session index on a fresh host database', () => { - const database = openTestJournalHostDatabase(root) - expect(hasReceiptTable(database.db)).toBe(true) - expect(journalPragmaNumber(database.db, 'user_version')).toBe(JOURNAL_DB_SCHEMA_VERSION) - expect( - database.db.prepare('PRAGMA table_info(agent_session_command_receipts)').all() - ).toEqual([ - expect.objectContaining({ name: 'operation_id', pk: 1 }), - expect.objectContaining({ name: 'session_id', pk: 0 }), - expect.objectContaining({ name: 'caller_key', pk: 2 }), - expect.objectContaining({ name: 'method', pk: 0 }), - expect.objectContaining({ name: 'fingerprint', pk: 0 }), - expect.objectContaining({ name: 'status', pk: 0 }), - expect.objectContaining({ name: 'result_json', pk: 0 }), - expect.objectContaining({ name: 'rejection_json', pk: 0 }), - expect.objectContaining({ name: 'accepted_at', pk: 0 }) - ]) - const indexes = database.db.prepare('PRAGMA index_list(agent_session_command_receipts)').all() - expect(indexes).toHaveLength(2) - expect(indexes).toEqual( - expect.arrayContaining([ - expect.objectContaining({ - name: 'sqlite_autoindex_agent_session_command_receipts_1', - origin: 'pk' - }), - expect.objectContaining({ name: 'agent_session_command_receipts_session', origin: 'c' }) - ]) - ) - expect( - database.db.prepare('PRAGMA index_info(agent_session_command_receipts_session)').all() - ).toEqual([expect.objectContaining({ name: 'session_id' })]) - expect( - database.db.prepare('PRAGMA foreign_key_list(agent_session_command_receipts)').all() - ).toEqual([ - expect.objectContaining({ - table: 'agent_session_records', - from: 'session_id', - to: 'session_id', - on_delete: 'CASCADE' - }) - ]) - }) - it.each([3, 4])('adds the table to an older-shaped version %i database', (version) => { const seeded = new Database(dbPath) seeded.exec(createJournalTablesSql()) diff --git a/src/main/native-chat/agent-session-journal/journal-unsent-send-hold-queue.spec.ts b/src/main/native-chat/agent-session-journal/journal-unsent-send-hold-queue.spec.ts index f481e0cd480..b494080f9ca 100644 --- a/src/main/native-chat/agent-session-journal/journal-unsent-send-hold-queue.spec.ts +++ b/src/main/native-chat/agent-session-journal/journal-unsent-send-hold-queue.spec.ts @@ -1,6 +1,6 @@ import type { createFixture } from './journal-unsent-send-hold-contract-test-fixture' import { describe, expect, it, vi } from 'vitest' -import type { AgentJournalSubmission } from '../../../shared/agent-session/journal/agent-session-journal-types' + import { USER_MESSAGE_SOURCE } from '../../../shared/agent-session/agent-session-message-source' export function registerJournalUnsentSendHoldQueueTests( @@ -136,15 +136,6 @@ export function registerJournalUnsentSendHoldQueueTests( }) describe('the source a send records', () => { - it('is folded from the row; a row without one folds without it', async () => { - const journal = await fixture.open() - await fixture.accept(journal, 'with-source', { origin: 'host', source: USER_MESSAGE_SOURCE }) - await fixture.accept(journal, 'without', { origin: 'client' }) - const folded: AgentJournalSubmission | undefined = journal.submission('with-source') - expect(folded?.source).toEqual({ kind: 'user' }) - expect(journal.submission('without')).not.toHaveProperty('source') - }) - it('keeps only the kind of an agent’s source, never its senders', async () => { const journal = await fixture.open() await fixture.accept(journal, 'mail', { origin: 'host', source: fixture.MAIL_SOURCE }) diff --git a/src/main/native-chat/agent-session-wire/hook-failure-provider-frame.test.ts b/src/main/native-chat/agent-session-wire/hook-failure-provider-frame.test.ts index ef13c063f48..f1159933ca8 100644 --- a/src/main/native-chat/agent-session-wire/hook-failure-provider-frame.test.ts +++ b/src/main/native-chat/agent-session-wire/hook-failure-provider-frame.test.ts @@ -74,36 +74,6 @@ describe('failed hook provider frames', () => { ) }) - it.each([undefined, null, 'invalid', [null], [{ kind: 'error', text: ' ' }]])( - 'keeps a named error visible when Codex entries are missing or malformed: %j', - (entries) => { - expect( - unhandledProviderFrameJournalItem('codex', CODEX_KIND, { - run: { eventName: 'stop', status: 'failed', entries } - })?.body - ).toMatchObject({ text: 'Codex Stop hook failed.', tone: 'error' }) - } - ) - - it('uses a readable fallback when a failed hook carries no name', () => { - expect( - unhandledProviderFrameJournalItem('codex', CODEX_KIND, { - run: { status: 'failed', entries: [{ kind: 'error', text: 'Timed out' }] } - })?.body.text - ).toBe('Codex hook failed: Timed out') - expect( - unhandledProviderFrameJournalItem('codex', CODEX_KIND, { - run: { status: 'failed' }, - message: 'Hook could not start' - })?.body.text - ).toBe('Codex hook failed: Hook could not start') - expect( - unhandledProviderFrameJournalItem('claude', CLAUDE_KIND, { - outcome: 'error' - })?.body.text - ).toBe('Claude hook failed.') - }) - it('shows Claude stderr and the exit code instead of the wire event name', () => { expect( unhandledProviderFrameJournalItem('claude', CLAUDE_KIND, { @@ -146,16 +116,6 @@ describe('failed hook provider frames', () => { ).toBe(`Claude SessionStart hook failed: ${expected}`) }) - it('retains a Claude exit code when there is no output', () => { - expect( - unhandledProviderFrameJournalItem('claude', CLAUDE_KIND, { - hook_name: 'SessionStart:startup', - outcome: 'error', - exit_code: 1 - })?.body.text - ).toBe('Claude SessionStart:startup hook failed (exit code 1).') - }) - it('keeps successful hooks hidden even when they produce diagnostic output', () => { expect( unhandledProviderFrameJournalItem('codex', CODEX_KIND, { @@ -187,14 +147,4 @@ describe('failed hook provider frames', () => { Buffer.byteLength(row?.body.providerFrame?.payload.head ?? '', 'utf8') ).toBeLessThanOrEqual(64) }) - - it('leaves other providers and unrelated frame kinds on their existing fallback', () => { - const payload = { outcome: 'error', hook_name: 'Other hook', stderr: 'Failure' } - expect( - unhandledProviderFrameJournalItem('future-provider', CLAUDE_KIND, payload)?.body.text - ).toBe(`future-provider · ${CLAUDE_KIND}`) - expect( - unhandledProviderFrameJournalItem('claude', 'message:future/event', payload)?.body.text - ).toBe('claude · message:future/event') - }) }) diff --git a/src/main/native-chat/agent-session-wire/structured-agent-session-queued-send-answer.test.ts b/src/main/native-chat/agent-session-wire/structured-agent-session-queued-send-answer.test.ts index adc2284dce7..968c3e1c361 100644 --- a/src/main/native-chat/agent-session-wire/structured-agent-session-queued-send-answer.test.ts +++ b/src/main/native-chat/agent-session-wire/structured-agent-session-queued-send-answer.test.ts @@ -176,21 +176,4 @@ describe('what a chat host answers for one send', () => { expect(result).toEqual({ kind }) expect(readReceipt).toHaveBeenCalledWith('mail') }) - - it.each([4, 40])('answers %i sends with no card left by key, over a long history', (count) => { - const j = journal() - for (let item = 0; item < 500; item += 1) { - j.submit(item * 3 + 1, `history-${item}`) - j.handOver(item * 3 + 2, `history-${item}`) - j.accept(item * 3 + 3, `history-${item}`) - } - let receipts = 0 - for (let send = 0; send < count; send += 1) { - const { result, readReceipt } = answer(j.state, `gone-${send}`) - expect(result).toEqual({ kind: 'absent' }) - receipts += readReceipt.mock.calls.length - } - // One receipt read per send, and no history walk (the reader above throws on one). - expect(receipts).toBe(count) - }) }) diff --git a/src/main/native-chat/agent-session-wire/structured-agent-session-status-feed-conversation-name.test.ts b/src/main/native-chat/agent-session-wire/structured-agent-session-status-feed-conversation-name.test.ts index 86abd582f6a..d3fa1f2e0fd 100644 --- a/src/main/native-chat/agent-session-wire/structured-agent-session-status-feed-conversation-name.test.ts +++ b/src/main/native-chat/agent-session-wire/structured-agent-session-status-feed-conversation-name.test.ts @@ -69,17 +69,6 @@ function lastName(events: readonly AgentSessionStatusEvent[]): string | undefine return event?.type === 'status' ? event.session.conversationName : undefined } -it('publishes a held chat its saved name once, and not again for an unchanged record', async () => { - const { feed, events, name } = await namedFeed() - expect(lastName(events)).toBeUndefined() - name('Explain the parser') - expect(lastName(events)).toBe('Explain the parser') - const count = events.length - feed.publish('session') - feed.publishConversationName('session') - expect(events).toHaveLength(count + 1) -}) - it('names a chat whose tab closed before naming finished, and keeps it for later subscribers', async () => { const { feed, events, sessions, name } = await namedFeed() feed.revokeLive('session') @@ -94,21 +83,6 @@ it('names a chat whose tab closed before naming finished, and keeps it for later }) }) -it('keeps the name on the summary a closing chat retains', async () => { - const { feed, events, name } = await namedFeed() - name('Explain the parser') - feed.close('session') - expect(lastName(events)).toBe('Explain the parser') -}) - -it('publishes naming activity without needing a chat summary', async () => { - const { feed, events } = await namedFeed() - const count = events.length - feed.publishConversationName('unknown-session') - expect(events).toHaveLength(count + 1) - expect(events.at(-1)).toEqual({ type: 'snapshot', sessions: [], namingActiveSessionIds: [] }) -}) - it('publishes cold names from durable records across a host restart without a status row', () => { let record = { ...agentSessionRecordFixture(), conversationName: 'Before' } const open = () => diff --git a/src/main/native-chat/agent-session-wire/structured-agent-session-subscriber-publication.test.ts b/src/main/native-chat/agent-session-wire/structured-agent-session-subscriber-publication.test.ts index bfb3bd69ff5..3cf2cdd21e7 100644 --- a/src/main/native-chat/agent-session-wire/structured-agent-session-subscriber-publication.test.ts +++ b/src/main/native-chat/agent-session-wire/structured-agent-session-subscriber-publication.test.ts @@ -20,31 +20,6 @@ import { journals } from './structured-agent-session-subscriber-contract-test-fixture' -it('reports every content publication to the journal hook, subscribed or not', async () => { - const journal = await journals.open({ - identity: { - sessionId: SESSION, - workspaceId: 'workspace-1', - hostId: 'local', - agent: 'codex', - providerHandle: codexProviderHandle('thread-1') - }, - stateDirectory: join(fixture.root, 'hook-journal') - }) - const published: string[] = [] - const subscribers = new AgentSessionSubscribers({ - onJournalPublished: (sessionId, published_journal) => { - expect(published_journal).toBe(journal) - published.push(sessionId) - } - }) - - subscribers.publish(SESSION, journal) - subscribers.snapshot(SESSION, journal, 1) - - expect(published).toEqual([SESSION, SESSION]) -}) - it('settles a session nobody is reading, from running to idle', async () => { // Never open a subscriber: status must publish independently of transcript readers. const journal = await journals.open({ diff --git a/src/main/persistence/terminal-topology/layout-owner-written-session-golden.test.ts b/src/main/persistence/terminal-topology/layout-owner-written-session-golden.test.ts deleted file mode 100644 index 1579279b204..00000000000 --- a/src/main/persistence/terminal-topology/layout-owner-written-session-golden.test.ts +++ /dev/null @@ -1,197 +0,0 @@ -import type * as NodeCrypto from 'node:crypto' -import { mkdtempSync, readFileSync, rmSync, writeFileSync } from 'node:fs' -import { tmpdir } from 'node:os' -import { join } from 'node:path' -import { afterEach, describe, expect, it, vi } from 'vitest' -import { - LAYOUT_OWNER_WRITTEN_SESSION_GOLDEN_PATH, - LAYOUT_OWNER_WRITTEN_SESSION_WORKTREE_ID as WORKTREE -} from '../../../shared/__fixtures__/layout-owner-written-session-golden-paths' -import type { LayoutCommand } from '../../../shared/workspace-layout/workspace-layout-command-types' -import type { WorkspaceSessionState } from '../../../shared/workspace-session/workspace-session-state-types' -import type { Store } from '../loading-store/store' -import { ProfileStateSqliteAuthority } from '../profile-state/profile-state-sqlite-authority' -import { windowViewSaveForMain } from '../window-save-view' -import { - emptyTerminalSessionProfile, - FIXTURE_GIT_WORKTREE_ID, - openTopologyStore -} from './terminal-topology-profile-fixture' - -vi.mock('../../../shared/layout-owner-switch', () => ({ ORCA_LAYOUT_OWNER_IS_MAIN: true })) -vi.mock('../../telemetry/client', () => ({ track: vi.fn() })) -vi.mock('../../ssh/config/ssh-config-parser', () => ({ - loadUserSshConfig: () => ({ hosts: [] }), - sshConfigHostsToTargets: () => [] -})) -// Deterministic minted IDs keep persisted bytes stable. -const minting = vi.hoisted(() => ({ on: false, next: 0 })) -vi.mock('node:crypto', async (importOriginal) => { - const actual = await importOriginal() - return { - ...actual, - randomUUID: () => - minting.on - ? `00000000-0000-4000-8000-${String(++minting.next).padStart(12, '0')}` - : actual.randomUUID() - } -}) - -const UPDATE_ENV = 'ORCA_UPDATE_LAYOUT_OWNER_GOLDEN' -const leaf = (n: number): string => `${n}${n}${n}${n}${n}${n}${n}${n}-1111-4111-8111-111111111111` -const on = { workspace: WORKTREE } -const directories: string[] = [] - -afterEach(() => { - minting.on = false - vi.useRealTimers() - for (const directory of directories.splice(0)) { - rmSync(directory, { recursive: true, force: true }) - } -}) - -const COMMANDS: LayoutCommand[] = [ - { ...on, type: 'createTerminalTab', tabId: 'tab-shell', leafId: leaf(1), createdAt: 1 }, - { ...on, type: 'createTerminalTab', tabId: 'tab-build', leafId: leaf(2), createdAt: 2 }, - { - ...on, - type: 'splitPane', - tabId: 'tab-shell', - leafId: leaf(1), - direction: 'vertical', - newLeafId: leaf(3) - }, - { ...on, type: 'renameTab', tabId: 'tab-build', kind: 'custom', title: 'Build' }, - { - ...on, - type: 'openEditorTab', - tabId: 'tab-readme', - fileId: '/fixture/local/README.md', - contentType: 'editor', - createdAt: 3, - file: { filePath: '/fixture/local/README.md', relativePath: 'README.md', language: 'markdown' } - }, - { - ...on, - type: 'openBrowserTab', - tabId: 'tab-docs', - browserTabId: 'browser-docs', - pageId: 'page-docs', - createdAt: 4, - browserCreatedAt: 4 - }, - { ...on, type: 'createTerminalTab', tabId: 'tab-scratch', leafId: leaf(4), createdAt: 5 } -] - -function windowSave(stored: WorkspaceSessionState): WorkspaceSessionState { - const page = { - id: 'page-docs', - workspaceId: 'browser-docs', - worktreeId: WORKTREE, - url: 'https://example.com/docs', - title: 'Docs', - loading: false, - faviconUrl: null, - canGoBack: false, - canGoForward: false, - loadError: null, - createdAt: 4 - } - return { - ...stored, - activeRepoId: 'repo-local', - activeWorktreeId: WORKTREE, - activeTabId: 'tab-shell', - browserPagesByWorkspace: { 'browser-docs': [page] }, - browserTabsByWorktree: { - [WORKTREE]: (stored.browserTabsByWorktree?.[WORKTREE] ?? []).map((tab) => ({ - ...tab, - url: page.url, - title: page.title - })) - } - } -} - -async function apply(store: Store, command: LayoutCommand): Promise { - const result = await store.applyLayoutCommand(command) - expect(result.ok, command.type).toBe(true) -} - -function writtenSession(directory: string): string { - const authority = new ProfileStateSqliteAuthority(join(directory, 'profile-state.db'), 'topology') - const serialized = authority.readSerializedState() - authority.close() - const profile: { workspaceSession: unknown } = JSON.parse(serialized ?? '{}') - return `${JSON.stringify(profile.workspaceSession, null, 2)}\n` -} - -// This probes current switch-off readers, not compatibility with older releases. -describe('the session a layout-owner build writes (downgrade probe, ledger L8)', () => { - it('is the checked-in golden, byte for byte', async () => { - expect(WORKTREE).toBe(FIXTURE_GIT_WORKTREE_ID) - const directory = mkdtempSync(join(tmpdir(), 'orca-layout-owner-golden-')) - directories.push(directory) - const store = await openTopologyStore(directory, emptyTerminalSessionProfile()) - vi.useFakeTimers({ toFake: ['Date'], now: 1_700_000_000_000 }) - minting.on = true - - for (const command of COMMANDS.slice(0, 4)) { - await apply(store, command) - } - const shellGroup = store.getWorkspaceSession().tabGroups?.[WORKTREE]?.[0]?.id - await apply(store, { - ...on, - type: 'splitGroup', - tabId: 'tab-build', - besideGroupId: shellGroup!, - direction: 'right' - }) - for (const command of COMMANDS.slice(4)) { - await apply(store, command) - } - await store.persistPtyBinding({ - worktreeId: WORKTREE, - tabId: 'tab-shell', - leafId: leaf(1), - ptyId: 'pty-shell', - incarnationId: 'inc-shell' - }) - // Retain closed terminals for exit events arriving after close. - await store.persistPtyBinding({ - worktreeId: WORKTREE, - tabId: 'tab-scratch', - leafId: leaf(4), - ptyId: 'pty-scratch', - incarnationId: 'inc-scratch' - }) - await apply(store, { - ...on, - type: 'closeTabs', - tabIds: ['tab-scratch'], - force: true, - recordClose: 'user' - }) - minting.on = false - // Simulate session:set with renderer selection and browser-page state. - const stored = store.getWorkspaceSession() - store.setWorkspaceSession(windowViewSaveForMain(stored, windowSave(stored), undefined)) - await store.flushPendingOrThrowAsync() - await store.freezeWritesAsync() - - const written = writtenSession(directory) - // Reject machine-specific paths; clocks and minted IDs are deterministic. - expect(written).not.toContain(tmpdir()) - if (process.env[UPDATE_ENV] === '1') { - writeFileSync(LAYOUT_OWNER_WRITTEN_SESSION_GOLDEN_PATH, written) - } - const golden = JSON.parse(readFileSync(LAYOUT_OWNER_WRITTEN_SESSION_GOLDEN_PATH, 'utf8')) - // Ignore formatter whitespace while retaining key order and values. - expect( - written, - `the switch-on save format changed: regenerate with ${UPDATE_ENV}=1, then the switch-off ` + - 'restore tests (layout-owner-written-session-switch-off-load, ' + - 'layout-owner-written-session-hydration) must still pass: they are the downgrade proof' - ).toBe(`${JSON.stringify(golden, null, 2)}\n`) - }) -}) diff --git a/src/main/providers/windows-powershell-executable.test.ts b/src/main/providers/windows-powershell-executable.test.ts index adcd3b75274..47d9c678b89 100644 --- a/src/main/providers/windows-powershell-executable.test.ts +++ b/src/main/providers/windows-powershell-executable.test.ts @@ -1,6 +1,5 @@ import { describe, expect, it } from 'vitest' import { - getWindowsCmdPath, resolveWindowsPowerShellExecutablePath, resolveWindowsPowerShellSpawnChain } from './windows-powershell-executable' @@ -13,7 +12,6 @@ const WIN_ENV: NodeJS.ProcessEnv = { ComSpec: 'C:\\Windows\\System32\\cmd.exe' } -const PWSH7 = 'C:\\Program Files\\PowerShell\\7\\pwsh.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). @@ -22,26 +20,6 @@ const PWSH_STORE_EXE = 'C:\\Program Files\\WindowsApps\\Microsoft.PowerShell_7.6.3.0_x64__8wekyb3d8bbwe\\pwsh.exe' describe('resolveWindowsPowerShellExecutablePath', () => { - it('returns null on non-Windows platforms', () => { - expect( - resolveWindowsPowerShellExecutablePath('pwsh.exe', { - platform: 'linux', - env: WIN_ENV, - isRealExecutable: () => true - }) - ).toBeNull() - }) - - it('resolves pwsh.exe to a real absolute path under Program Files', () => { - expect( - resolveWindowsPowerShellExecutablePath('pwsh.exe', { - platform: 'win32', - env: WIN_ENV, - isRealExecutable: (p) => p === PWSH7 - }) - ).toBe(PWSH7) - }) - it('repro: resolves pwsh.exe from a real PATH entry when standard roots miss', () => { expect( resolveWindowsPowerShellExecutablePath('pwsh.exe', { @@ -59,16 +37,6 @@ describe('resolveWindowsPowerShellExecutablePath', () => { ).toBe(PATH_PWSH7) }) - it('resolves powershell.exe to inbox System32 WindowsPowerShell', () => { - expect( - resolveWindowsPowerShellExecutablePath('powershell.exe', { - platform: 'win32', - env: WIN_ENV, - isRealExecutable: (p) => p === WINDOWS_POWERSHELL - }) - ).toBe(WINDOWS_POWERSHELL) - }) - 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', { @@ -108,28 +76,9 @@ describe('resolveWindowsPowerShellExecutablePath', () => { expect(resolved).toBeNull() } ) - - it('returns null for pwsh when no real executable is found', () => { - expect( - resolveWindowsPowerShellExecutablePath('pwsh.exe', { - platform: 'win32', - env: WIN_ENV, - isRealExecutable: () => false - }) - ).toBeNull() - }) }) describe('resolveWindowsPowerShellSpawnChain', () => { - it('orders pwsh -> Windows PowerShell -> cmd.exe when all resolve', () => { - const chain = resolveWindowsPowerShellSpawnChain('pwsh.exe', { - platform: 'win32', - env: WIN_ENV, - isRealExecutable: (p) => p === PWSH7 || p === WINDOWS_POWERSHELL - }) - expect(chain).toEqual([PWSH7, WINDOWS_POWERSHELL, WIN_ENV.ComSpec]) - }) - it('falls back to Windows PowerShell + cmd.exe when pwsh is only a Store alias', () => { const chain = resolveWindowsPowerShellSpawnChain('pwsh.exe', { platform: 'win32', @@ -139,57 +88,4 @@ describe('resolveWindowsPowerShellSpawnChain', () => { expect(chain).toEqual([WINDOWS_POWERSHELL, WIN_ENV.ComSpec]) expect(chain).not.toContain(PWSH_STORE_ALIAS) }) - - it('uses the real Store package executable ahead of Windows PowerShell', () => { - const chain = resolveWindowsPowerShellSpawnChain('pwsh.exe', { - platform: 'win32', - env: { - ...WIN_ENV, - Path: 'C:\\Users\\dev\\AppData\\Local\\Microsoft\\WindowsApps' - }, - isRealExecutable: (p) => p === PWSH_STORE_EXE || p === WINDOWS_POWERSHELL, - resolveAppExecutionAlias: (p) => (p === PWSH_STORE_ALIAS ? PWSH_STORE_EXE : null) - }) - expect(chain).toEqual([PWSH_STORE_EXE, WINDOWS_POWERSHELL, WIN_ENV.ComSpec]) - }) - - it('orders PATH-resolved pwsh before Windows PowerShell when standard roots miss', () => { - const chain = resolveWindowsPowerShellSpawnChain('pwsh.exe', { - platform: 'win32', - env: { - ...WIN_ENV, - Path: 'D:\\Tools\\PowerShell\\7' - }, - isRealExecutable: (p) => p === PATH_PWSH7 || p === WINDOWS_POWERSHELL - }) - expect(chain).toEqual([PATH_PWSH7, WINDOWS_POWERSHELL, WIN_ENV.ComSpec]) - }) - - it('always ends with cmd.exe even when no PowerShell resolves', () => { - const chain = resolveWindowsPowerShellSpawnChain('powershell.exe', { - platform: 'win32', - env: WIN_ENV, - isRealExecutable: () => false - }) - expect(chain).toEqual([WIN_ENV.ComSpec]) - }) - - it('does not duplicate cmd.exe when ComSpec is missing', () => { - const chain = resolveWindowsPowerShellSpawnChain('powershell.exe', { - platform: 'win32', - env: { SystemRoot: 'C:\\Windows' }, - isRealExecutable: () => false - }) - expect(chain).toEqual(['C:\\Windows\\System32\\cmd.exe']) - }) -}) - -describe('getWindowsCmdPath', () => { - it('prefers ComSpec when set', () => { - expect(getWindowsCmdPath(WIN_ENV)).toBe('C:\\Windows\\System32\\cmd.exe') - }) - - it('derives cmd.exe from SystemRoot when ComSpec is absent', () => { - expect(getWindowsCmdPath({ SystemRoot: 'D:\\Win' })).toBe('D:\\Win\\System32\\cmd.exe') - }) }) diff --git a/src/main/pty/process-retirement/windows-pty-root-identity.test.ts b/src/main/pty/process-retirement/windows-pty-root-identity.test.ts index f8c9c54550c..dd8dab314c3 100644 --- a/src/main/pty/process-retirement/windows-pty-root-identity.test.ts +++ b/src/main/pty/process-retirement/windows-pty-root-identity.test.ts @@ -1,19 +1,11 @@ -import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' +import { describe, expect, it, vi } from 'vitest' -const getAllProcessesMock = vi.fn() - -import { __setWindowsProcessTreeLoaderForTests } from '../../windows/windows-process-table' import { classifyWindowsTreeKillTarget, verifyWindowsTreeKillTarget, WINDOWS_ROOT_IDENTITY_TIMEOUT_MS } from './windows-pty-root-identity' // The native reader rejects snapshots that omit the querying process. -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 -] const ORCA_PID = 5000 @@ -34,12 +26,6 @@ describe('classifyWindowsTreeKillTarget', () => { expect(classifyWindowsTreeKillTarget(4242, rows, ORCA_PID)).toBe('own') }) - it('documents that a recycled PID under another Orca pane still classifies as own', () => { - // Recycled as a tool under another pane, PID 4242 still has Orca ancestry. - const rows = [link(ORCA_PID, 900), link(7000, ORCA_PID), link(7100, 7000), link(4242, 7100)] - expect(classifyWindowsTreeKillTarget(4242, rows, ORCA_PID)).toBe('own') - }) - it('rejects a recycled pid whose ancestry never reaches this process', () => { const rows = [...SYSTEM_CHAIN, link(ORCA_PID, 900), link(4242, 900)] expect(classifyWindowsTreeKillTarget(4242, rows, ORCA_PID)).toBe('foreign') @@ -72,10 +58,6 @@ describe('classifyWindowsTreeKillTarget', () => { expect(classifyWindowsTreeKillTarget(ORCA_PID, rows, ORCA_PID)).toBe('foreign') }) - it.each([0, -1, 1.5, Number.NaN])('rejects the invalid root pid %s', (rootPid) => { - expect(classifyWindowsTreeKillTarget(rootPid, [link(4242, ORCA_PID)], ORCA_PID)).toBe('foreign') - }) - it('treats duplicate rows for the root as unknown, not as ownership', () => { const rows = [link(4242, ORCA_PID), link(4242, 900)] expect(classifyWindowsTreeKillTarget(4242, rows, ORCA_PID)).toBe('unknown') @@ -88,21 +70,6 @@ describe('classifyWindowsTreeKillTarget', () => { }) describe('verifyWindowsTreeKillTarget', () => { - it('classifies a live win32 root against the fresh process table', async () => { - const readRows = vi.fn().mockResolvedValue([link(4242, ORCA_PID)]) - await expect( - verifyWindowsTreeKillTarget(4242, { readRows, ownerPid: ORCA_PID, platform: 'win32' }) - ).resolves.toBe('own') - expect(readRows).toHaveBeenCalledOnce() - }) - - it('detects the recycled-pid case that taskkill /T /F must not touch', async () => { - const readRows = vi.fn().mockResolvedValue([link(4242, 900), link(900, 4)]) - await expect( - verifyWindowsTreeKillTarget(4242, { readRows, ownerPid: ORCA_PID, platform: 'win32' }) - ).resolves.toBe('foreign') - }) - it('returns unknown when the Windows process table is unavailable', async () => { const readRows = vi.fn().mockResolvedValue(null) await expect( @@ -141,45 +108,4 @@ describe('verifyWindowsTreeKillTarget', () => { vi.useRealTimers() } }) - - it('skips the probe off Windows so POSIX teardown keeps its own guards', async () => { - const readRows = vi.fn() - await expect( - verifyWindowsTreeKillTarget(4242, { readRows, ownerPid: ORCA_PID, platform: 'darwin' }) - ).resolves.toBe('unknown') - expect(readRows).not.toHaveBeenCalled() - }) -}) - -describe('verifyWindowsTreeKillTarget scan volume', () => { - const NATIVE_ROWS = [ - { pid: ORCA_PID, ppid: 900, name: 'orca.exe', commandLine: 'orca.exe' }, - { pid: 4242, ppid: ORCA_PID, name: 'pwsh.exe', commandLine: 'pwsh.exe' } - ] - - beforeEach(() => { - getAllProcessesMock.mockReset() - getAllProcessesMock.mockImplementation((cb: (snapshot: unknown) => void) => { - cb(withSelf(NATIVE_ROWS)) - }) - __setWindowsProcessTreeLoaderForTests(() => ({ - ProcessDataFlag: { None: 0, Memory: 1, CommandLine: 2, CreationTime: 4 }, - getAllProcesses: getAllProcessesMock - })) - }) - - afterEach(() => { - __setWindowsProcessTreeLoaderForTests() - }) - - it('collapses a 32-wide teardown burst into a single process-table scan', async () => { - const verdicts = await Promise.all( - Array.from({ length: 32 }, () => - verifyWindowsTreeKillTarget(4242, { ownerPid: ORCA_PID, platform: 'win32' }) - ) - ) - - expect(getAllProcessesMock.mock.calls.length).toBe(1) - expect(new Set(verdicts)).toEqual(new Set(['own'])) - }) }) diff --git a/src/main/pty/shell-startup/fish-xdg-data-dirs-handoff.test.ts b/src/main/pty/shell-startup/fish-xdg-data-dirs-handoff.test.ts index d7674180df5..a7160bec47b 100644 --- a/src/main/pty/shell-startup/fish-xdg-data-dirs-handoff.test.ts +++ b/src/main/pty/shell-startup/fish-xdg-data-dirs-handoff.test.ts @@ -3,7 +3,7 @@ import { chmodSync, mkdirSync, mkdtempSync, rmSync, writeFileSync } from 'node:f import { tmpdir } from 'node:os' import { dirname, join } from 'node:path' import { afterEach, beforeEach, describe, expect, it } from 'vitest' -import { getFishCodexShellLaunchPreflight } from '../../../shared/codex-shell-function' + import { fishRequirementViolation, resolveFishBinary @@ -11,28 +11,12 @@ import { import { buildFishVendorConfWrapperFile, FISH_XDG_DATA_DIRS_PREFIX_ENV, - getFishVendorConfSnippet, getFishXdgDataDirsLaunchEnv } from './fish-xdg-data-dirs-handoff' const ROOT = '/orca/shell-wrappers/abc' -const DATA_DIR = `${ROOT}/fish-xdg-data` describe('getFishXdgDataDirsLaunchEnv', () => { - it('adds the XDG default after Orca when the variable is unset', () => { - const expected = `${DATA_DIR}:/usr/local/share:/usr/share` - expect(getFishXdgDataDirsLaunchEnv(ROOT, undefined)).toEqual({ - XDG_DATA_DIRS: expected, - [FISH_XDG_DATA_DIRS_PREFIX_ENV]: expected - }) - }) - - it('treats an empty XDG_DATA_DIRS as unset', () => { - expect(getFishXdgDataDirsLaunchEnv(ROOT, '')).toEqual( - getFishXdgDataDirsLaunchEnv(ROOT, undefined) - ) - }) - it.each([['-N'], ['--no-config'], ['--no-c'], ['-lN'], ['-Ni']])( 'skips fish args that disable config: %s', (arg) => { @@ -40,44 +24,11 @@ describe('getFishXdgDataDirsLaunchEnv', () => { } ) - it.for([['-l'], ['-i'], ['--login'], ['--no-execute'], ['--no-'], ['-c', 'echo N']])( - 'still hands off for fish args %j', - (args) => { - expect(getFishXdgDataDirsLaunchEnv(ROOT, undefined, args)).not.toEqual({}) - } - ) - - it('prepends to an existing value and records only its own entry', () => { - expect(getFishXdgDataDirsLaunchEnv(ROOT, '/opt/a:/opt/b')).toEqual({ - XDG_DATA_DIRS: `${DATA_DIR}:/opt/a:/opt/b`, - [FISH_XDG_DATA_DIRS_PREFIX_ENV]: DATA_DIR - }) - }) - - it('prepends again when the inherited value already names the dir', () => { - const inherited = `${DATA_DIR}:/opt/a` - expect(getFishXdgDataDirsLaunchEnv(ROOT, inherited).XDG_DATA_DIRS).toBe( - `${DATA_DIR}:${inherited}` - ) - }) - it('skips a dir that XDG_DATA_DIRS cannot represent', () => { expect(getFishXdgDataDirsLaunchEnv('/odd:root', undefined)).toEqual({}) }) }) -describe('fish vendor snippet', () => { - it('embeds the shared fish codex function verbatim', () => { - expect(getFishVendorConfSnippet()).toContain(getFishCodexShellLaunchPreflight()) - }) - - it('lives where fish looks for vendor snippets under the data dir', () => { - expect(buildFishVendorConfWrapperFile(ROOT)[0]).toBe( - `${DATA_DIR}/fish/vendor_conf.d/orca-shell-integration.fish` - ) - }) -}) - const fishLookup = resolveFishBinary(3) // The test shells run with a minimal PATH. const fish = fishLookup.available diff --git a/src/main/rate-limits/opencode-go-status-parsing.test.ts b/src/main/rate-limits/opencode-go-status-parsing.test.ts index b77f12f80c8..f6adb4173fe 100644 --- a/src/main/rate-limits/opencode-go-status-parsing.test.ts +++ b/src/main/rate-limits/opencode-go-status-parsing.test.ts @@ -52,31 +52,6 @@ describe('parseOpenCodeGoStatusPayload', () => { }) }) - it('accepts numeric microCents', () => { - const parsed = parseOpenCodeGoStatusPayload( - JSON.stringify({ - access: { - meters: { - fiveHour: { - resetsAt: '2026-09-18T12:42:04.962Z', - limitMicroCents: 100, - usedMicroCents: 25 - }, - week: { - resetsAt: '2026-09-21T00:00:00.000Z', - limitMicroCents: 200, - usedMicroCents: 50 - } - } - } - }) - ) - - expect(parsed?.session?.usedPercent).toBe(25) - expect(parsed?.weekly?.usedPercent).toBe(25) - expect(parsed?.monthly).toBeNull() - }) - it('caps usedPercent at 100 and floors at 0', () => { const parsed = parseOpenCodeGoStatusPayload( JSON.stringify({ @@ -100,26 +75,6 @@ describe('parseOpenCodeGoStatusPayload', () => { expect(parsed?.session?.usedPercent).toBe(100) expect(parsed?.weekly?.usedPercent).toBe(0) }) - - it('returns null for HTML and other non-JSON bodies', () => { - expect(parseOpenCodeGoStatusPayload('rollingUsage:{usagePercent:30}')).toBeNull() - expect(parseOpenCodeGoStatusPayload('')).toBeNull() - expect(parseOpenCodeGoStatusPayload('{not json')).toBeNull() - }) - - it('returns null when fiveHour or week meters are missing', () => { - expect( - parseOpenCodeGoStatusPayload( - JSON.stringify({ - access: { - meters: { - week: { limitMicroCents: '100', usedMicroCents: '10' } - } - } - }) - ) - ).toBeNull() - }) }) describe('parseOpenCodeGoBillingStatusPayload', () => { @@ -170,12 +125,6 @@ describe('parseOpenCodeGoBillingStatusPayload', () => { ])('rejects unsupported or unsafe billing payloads: $billingMode/$mode', (payload) => { expect(parseOpenCodeGoBillingStatusPayload(JSON.stringify(payload))).toBeNull() }) - - it('fails closed for malformed and non-object bodies', () => { - expect(parseOpenCodeGoBillingStatusPayload('{not json')).toBeNull() - expect(parseOpenCodeGoBillingStatusPayload('[]')).toBeNull() - expect(parseOpenCodeGoBillingStatusPayload('')).toBeNull() - }) }) describe('isOpenCodeGoExplicitNoAccessPayload', () => { @@ -212,14 +161,4 @@ describe('parseOpenCodeGoUsageApiPayload', () => { expect(parsed?.weekly.usedPercent).toBe(0) expect(parsed?.monthly).toBeNull() }) - - it('returns null for the console error bodies and other non-usage payloads', () => { - expect( - parseOpenCodeGoUsageApiPayload( - JSON.stringify({ type: 'error', error: { type: 'AuthError', message: 'Unauthorized' } }) - ) - ).toBeNull() - expect(parseOpenCodeGoUsageApiPayload('{not json')).toBeNull() - expect(parseOpenCodeGoUsageApiPayload('')).toBeNull() - }) }) diff --git a/src/main/remote-layout-snapshot/remote-layout-snapshot-recorder.test.ts b/src/main/remote-layout-snapshot/remote-layout-snapshot-recorder.test.ts index f65cbc2fd8a..516d53f820b 100644 --- a/src/main/remote-layout-snapshot/remote-layout-snapshot-recorder.test.ts +++ b/src/main/remote-layout-snapshot/remote-layout-snapshot-recorder.test.ts @@ -1,10 +1,8 @@ -import { join } from 'node:path' import { describe, expect, it } from 'vitest' import type { RemoteLayoutSnapshot } from '../../shared/remote-layout-snapshot' import type { RuntimeRpcResponse } from '../../shared/runtime-rpc-envelope' -import { scanSourceTree, stripComments } from '../../shared/source-scan/source-tree-scan' + import { - forgetRemoteLayoutSnapshot, recordRemoteLayoutFrame, type RemoteLayoutSnapshotStore } from './remote-layout-snapshot-recorder' @@ -113,33 +111,6 @@ describe('remote layout snapshot recorder', () => { expect(workspacesOf(snapshots)).toEqual({ a: tabs('a', 'vim') }) }) - it('saves at once only when tabs or groups change; title ticks ride the next save', () => { - const { store, saves } = memoryStore() - const census = (snapshots: unknown[]) => frame({ type: 'snapshots', snapshots }) - const update = (result: Record) => frame({ type: 'updated', ...result }) - recordRemoteLayoutFrame(store, 'env', METHOD, census([tabs('a')])) - recordRemoteLayoutFrame(store, 'env', METHOD, update(tabs('a', 'thinking.'))) - recordRemoteLayoutFrame(store, 'env', METHOD, update(tabs('a', 'thinking..'))) - const twoTabs = { - ...tabs('a'), - tabs: [...tabs('a').tabs, { type: 'terminal', id: 'second', title: 'shell' }] - } - recordRemoteLayoutFrame(store, 'env', METHOD, update(twoTabs)) - const grouped = { - ...twoTabs, - tabGroups: [{ id: 'g', activeTabId: null, tabOrder: ['second'] }] - } - recordRemoteLayoutFrame(store, 'env', METHOD, update(grouped)) - recordRemoteLayoutFrame( - store, - 'env', - METHOD, - update({ ...grouped, tabGroups: [{ id: 'g', activeTabId: 'second', tabOrder: ['second'] }] }) - ) - recordRemoteLayoutFrame(store, 'env', METHOD, update({ ...tabs('a'), tabs: [], removed: true })) - expect(saves).toEqual(['now', 'later', 'later', 'now', 'now', 'later', 'now']) - }) - it('keeps each server in its own entry', () => { const { store, snapshots } = memoryStore() recordRemoteLayoutFrame( @@ -169,22 +140,4 @@ describe('remote layout snapshot recorder', () => { recordRemoteLayoutFrame(store, 'env', METHOD, frame({ type: 'updated', worktree: 'a' })) expect(saves).toEqual([]) }) - - it('forgets a removed server', () => { - const { store, snapshots } = memoryStore() - recordRemoteLayoutFrame(store, 'env', METHOD, frame({ type: 'snapshots', snapshots: [] })) - forgetRemoteLayoutSnapshot(store, 'env') - expect(snapshots.has('env')).toBe(false) - }) - - it('is the only code that writes the snapshot', () => { - const src = join(__dirname, '../..') - const writers = scanSourceTree(src) - .filter(({ source }) => stripComments(source).includes('setRemoteLayoutSnapshot(')) - .map(({ relativePath }) => relativePath) - expect(writers.sort()).toEqual([ - 'main/remote-layout-snapshot/remote-layout-snapshot-persistence.ts', - 'main/remote-layout-snapshot/remote-layout-snapshot-recorder.ts' - ]) - }) }) diff --git a/src/main/runtime/desktop-browser-bridge/desktop-browser-bridge-registry.test.ts b/src/main/runtime/desktop-browser-bridge/desktop-browser-bridge-registry.test.ts index 9d51bdb270d..cc2170d60e7 100644 --- a/src/main/runtime/desktop-browser-bridge/desktop-browser-bridge-registry.test.ts +++ b/src/main/runtime/desktop-browser-bridge/desktop-browser-bridge-registry.test.ts @@ -105,16 +105,6 @@ describe('DesktopBrowserBridgeRegistry', () => { expect(MAX_PENDING_PER_ATTACHMENT).toBeLessThan(32) }) - it('gives the desktop a deadline that ends before orcad stops waiting', () => { - const registry = new DesktopBrowserBridgeRegistry() - const events: DesktopBrowserBridgeEvent[] = [] - registry.attach({ connectionId: 'c', pairedDeviceId: 'd', emit: (event) => events.push(event) }) - - registry.forward({ method: 'browser.click', params: {} }, { timeoutMs: 60_000 }).catch(() => {}) - - expect(events[0]).toMatchObject({ type: 'request', timeoutMs: 50_000 }) - }) - it('tells a cancel-capable desktop to drop a command the CLI cancelled', async () => { const registry = new DesktopBrowserBridgeRegistry() const events: DesktopBrowserBridgeEvent[] = [] diff --git a/src/main/runtime/folder-workspace-mixed-host-routing.test.ts b/src/main/runtime/folder-workspace-mixed-host-routing.test.ts index 7cd037af939..d230295e46f 100644 --- a/src/main/runtime/folder-workspace-mixed-host-routing.test.ts +++ b/src/main/runtime/folder-workspace-mixed-host-routing.test.ts @@ -71,13 +71,6 @@ describe('a folder whose project group mixes local and SSH repos (layout-owner b const { snapshot } = runtime.subscribeWorkspaceLayouts(() => {}) expect(snapshot.map((entry) => entry.key)).toEqual([GIT_KEY, FOLDER_KEY]) }) - - it('applies a layout command for it to the local partition', async () => { - const { runtime, applyLayoutCommand } = runtimeWithMixedFolder() - const reply = await runtime.applyLayoutCommand(RATIOS) - expect(reply.ok).toBe(true) - expect(applyLayoutCommand).toHaveBeenCalledWith(RATIOS, ['local'], {}) - }) }) describe('every folder kind routes where the window saves it (layout-owner build)', () => { @@ -88,15 +81,6 @@ describe('every folder kind routes where the window saves it (layout-owner build }) }) -describe('the same folder in a window-owned build', () => { - it('keeps refusing to route it', () => { - mocks.layoutOwnerIsMain = false - vi.spyOn(console, 'warn').mockImplementation(() => {}) - const { runtime } = runtimeWithMixedFolder() - expect(runtime.subscribeWorkspaceLayouts(() => {}).snapshot).toEqual([]) - }) -}) - describe('a workspace the router throws on', () => { function streamThrowingOn(key: string) { const session = localDesktopSession() @@ -151,21 +135,6 @@ describe('a workspace the router throws on', () => { ) }) - it('a routing warning never hides a later listener failure', async () => { - const warn = vi.spyOn(console, 'warn').mockImplementation(() => {}) - const { stream, route, write } = streamThrowingOn(FOLDER_KEY) - stream.subscribe(() => { - throw new Error('listener failed') - }) - route.throws = false - write() - await flush() - expect(warn.mock.calls.map((call) => call[0])).toEqual([ - '[workspace-layout] layout stream routing failed:', - '[workspace-layout] layout stream listener failed:' - ]) - }) - it('still fails the whole projection in a window-owned build', () => { mocks.layoutOwnerIsMain = false vi.spyOn(console, 'warn').mockImplementation(() => {}) diff --git a/src/main/runtime/orca-runtime-structured-status-sink-wiring.test.ts b/src/main/runtime/orca-runtime-structured-status-sink-wiring.test.ts index a743090f44e..4fde233fb4a 100644 --- a/src/main/runtime/orca-runtime-structured-status-sink-wiring.test.ts +++ b/src/main/runtime/orca-runtime-structured-status-sink-wiring.test.ts @@ -1,4 +1,4 @@ -import { afterEach, describe, expect, it, vi } from 'vitest' +import { describe, expect, it, vi } from 'vitest' import type { StructuredAgentSessionLogger } from '../native-chat/agent-session-wire/structured-agent-session-logger' const installed = vi.hoisted(() => { @@ -26,142 +26,6 @@ vi.mock('./agent-session/structured-agent-session-runtime', () => ({ })) import { OrcaRuntimeService } from './orca-runtime' -import { _resetTracerForTests, setActiveSink } from '../observability/tracer' -import type { StructuredAgentSessionStatusSink } from '../native-chat/agent-session-wire/structured-agent-session-status-feed' -import * as structuredMailTarget from './orchestration/structured-session-mail-target' -import { OrcaRuntimeWithRuntimeId } from './construction/runtime-identity-state' - -describe('structured status sink wiring', () => { - afterEach(() => { - _resetTracerForTests() - vi.restoreAllMocks() - }) - - it('hands the host the sink the runtime was constructed with', async () => { - installed.deps = null - const sink: StructuredAgentSessionStatusSink = { publish: vi.fn(), forget: vi.fn() } - const runtime = new OrcaRuntimeService(null, undefined, { structuredAgentStatusSink: sink }) - - await runtime.ensureStructuredAgentSessionHost() - - expect(installed.deps?.['statusSink']).toBe(sink) - }) - - it('hands the host the trace-file logger', async () => { - installed.logger = null - const push = vi.fn() - setActiveSink({ push, flush: () => {}, close: () => {} }) - vi.spyOn(console, 'warn').mockImplementation(() => undefined) - const runtime = new OrcaRuntimeService() - - await runtime.ensureStructuredAgentSessionHost() - const installedLogger = (): StructuredAgentSessionLogger | null => installed.logger - installedLogger()?.warn('renewing a chat lease failed', { - scope: 'lease-renewal', - sessionId: 'session-1', - error: new Error('database is locked') - }) - - expect(push).toHaveBeenCalledWith( - expect.objectContaining({ - name: 'agentSession.lease-renewal', - attributes: expect.objectContaining({ sessionId: 'session-1' }), - exit: expect.objectContaining({ _tag: 'Failure' }) - }) - ) - }) - - it('installs without a sink when none was provided', async () => { - installed.deps = null - const runtime = new OrcaRuntimeService() - - await runtime.ensureStructuredAgentSessionHost() - - expect(installed.deps).not.toBeNull() - expect('statusSink' in (installed.deps ?? {})).toBe(false) - }) -}) - -describe('mail dispatch through the current runtime', () => { - afterEach(() => vi.restoreAllMocks()) - - it('uses runtime journal and delivery overrides on the idle edge', () => { - const target = new OrcaRuntimeService() - vi.spyOn(structuredMailTarget, 'structuredSessionIdleEdgeMailboxes').mockReturnValue([ - 'term_mailbox' - ]) - const events: string[] = [] - Object.defineProperties(target, { - notifyStructuredSessionJournalActivity: { - value: function (this: OrcaRuntimeService, sessionId: string) { - expect(this).toBe(target) - expect(arguments.length).toBe(1) - events.push(`journal:${sessionId}`) - } - }, - deliverPendingMessagesForHandle: { - value: function (this: OrcaRuntimeService, handle: string) { - expect(this).toBe(target) - expect(arguments.length).toBe(1) - events.push(`delivery:${handle}`) - } - } - }) - - target.onStructuredSessionStatusForMail({ sessionId: 'session-mailbox', status: 'working' }) - expect(events).toEqual(['journal:session-mailbox']) - events.length = 0 - target.onStructuredSessionStatusForMail({ sessionId: 'session-mailbox', status: 'idle' }) - expect(events).toEqual(['journal:session-mailbox', 'delivery:term_mailbox']) - }) - - it('uses the runtime delivery override during repoint repair', () => { - const target = new OrcaRuntimeService() - const delivery = vi.fn(function (this: OrcaRuntimeService, handle: string) { - expect(this).toBe(target) - expect(arguments.length).toBe(1) - expect(handle).toBe('term_mailbox') - }) - Object.defineProperty(target, 'deliverPendingMessagesForHandle', { value: delivery }) - - target['repointPendingMessagesForHandle']('term_mailbox') - expect(delivery).toHaveBeenCalledOnce() - }) - - it('resolves a mailbox target through the runtime custody override', () => { - const target = new OrcaRuntimeService() - Object.defineProperty(target, 'structuredMailboxCustody', { - value: function (this: OrcaRuntimeService, handle: string) { - expect(this).toBe(target) - expect(arguments.length).toBe(1) - return handle === 'run:run-mailbox' - ? { sessionId: 'coordinator-session', dispatchId: null } - : 'elsewhere' - } - }) - - expect(target['resolveStructuredMailboxTarget']('run:run-mailbox')).toEqual({ - sessionId: 'coordinator-session', - dispatchId: null - }) - expect(target['resolveStructuredMailboxTarget']('term_mailbox')).toBeNull() - }) -}) - -abstract class MailSchedulerPhaseProbe extends OrcaRuntimeWithRuntimeId { - readonly pendingMailDuringConstruction = this.mailPointerRepointScheduler.pendingCount -} - -describe('input state before later runtime layers initialize', () => { - it('makes the scheduler available immediately after the original base layer', () => { - expect( - Object.getOwnPropertyDescriptor( - Reflect.construct(MailSchedulerPhaseProbe, []), - 'pendingMailDuringConstruction' - )?.value - ).toBe(0) - }) -}) describe('checked runtime construction', () => { it('installs subscription callbacks on the runtime with their real registry receiver', () => { @@ -173,17 +37,4 @@ describe('checked runtime construction', () => { runtime.cleanupSubscription('construction-subscription') expect(cleanup).toHaveBeenCalledOnce() }) - - it('keeps constructor callbacks lazy and the canonical status producer identity', () => { - const snapshot = vi.fn(() => []) - const stopped = vi.fn() - const runtime = new OrcaRuntimeService(null, undefined, { - getAgentStatusSnapshot: snapshot, - onPtyStopped: stopped - }) - expect(runtime.getAgentStatusSnapshotFn).toBe(snapshot) - expect(runtime['onPtyStopped']).toBe(stopped) - expect(snapshot).not.toHaveBeenCalled() - expect(stopped).not.toHaveBeenCalled() - }) }) diff --git a/src/main/runtime/orchestration/agent-facing-parity.test.ts b/src/main/runtime/orchestration/agent-facing-parity.test.ts index 30509bb7503..8d2cd0958b9 100644 --- a/src/main/runtime/orchestration/agent-facing-parity.test.ts +++ b/src/main/runtime/orchestration/agent-facing-parity.test.ts @@ -1,4 +1,3 @@ -/** Chat and terminal agents must receive equivalent preambles and pointers, apart from address and chat wording. */ import { afterAll, afterEach, describe, expect, it, vi } from 'vitest' import { getAppEnvironment, @@ -6,8 +5,7 @@ import { setAppEnvironment, type AppEnvironment } from '../../../shared/app-environment' -import type { OrcaRuntimeService } from '../orca-runtime' -import { deliverWorkerDispatchPreamble } from '../rpc/methods/orchestration/worker/deliver-worker-dispatch-preamble' + import { localOrchestrationCliCommand, resolveTerminalOrchestrationCliCommand, @@ -16,30 +14,15 @@ import { } from './cli-command' import { OrchestrationDb } from './db' import { OrchestrationStructuredMailboxPointerDelivery } from './structured-mailbox-pointer-delivery' -import { - CHAT_REDISPATCH_PARAGRAPH as CHAT_REDISPATCH, - TERMINAL_REDISPATCH_PARAGRAPH as TERMINAL_REDISPATCH -} from './preamble' - -const sent = vi.hoisted((): { preambles: string[] } => ({ preambles: [] })) -vi.mock('../rpc/methods/orchestration-structured-worker-session', () => ({ - sendStructuredWorkerPreamble: async (args: { preamble: string }) => { - sent.preambles.push(args.preamble) - } -})) const CHAT_SESSION = '4a1f6c2e-8b3d-4e7a-9c15-0d2b6e8f1a37' const CHAT_ADDRESS = `orca_session_id:${CHAT_SESSION}` -const TERMINAL_HANDLE = 'term_worker' -// The structured worker's hidden mailbox handle differs from its visible session address. -const CHAT_WORKER_HANDLE = 'structworker_1' const db = new OrchestrationDb(':memory:') -const SELF_LINE = `\nYour Orca session ID is: ${CHAT_ADDRESS}` + const previousEnvironment = hasAppEnvironment() ? getAppEnvironment() : null afterEach(() => { - sent.preambles = [] if (previousEnvironment) { setAppEnvironment(previousEnvironment) } @@ -61,52 +44,6 @@ function installApp(isPackaged: boolean): void { } satisfies AppEnvironment) } -function runtime(prompts: string[]): OrcaRuntimeService { - const fake: Pick< - OrcaRuntimeService, - 'getNestedWorkerMaxDepth' | 'getTerminalOrchestrationCliCommand' | 'sendTerminalAgentPrompt' - > & { - orchestrationSenderNames: Pick - } = { - orchestrationSenderNames: { snapshotOf: () => ({ name: null, agent: null }) }, - getNestedWorkerMaxDepth: () => 2, - getTerminalOrchestrationCliCommand: () => 'orca', - sendTerminalAgentPrompt: async (handle, text) => { - prompts.push(text) - return { handle, accepted: true, bytesWritten: text.length } - } - } - // oxlint-disable-next-line typescript/consistent-type-assertions -- SAFETY: preamble delivery reads only the members the fake implements. - return fake as OrcaRuntimeService -} - -type StructuredSession = Parameters[0]['structuredSession'] - -function structuredSession(): StructuredSession { - const session = { host: {}, identity: { sessionId: CHAT_SESSION, agent: 'codex' } } - // oxlint-disable-next-line typescript/consistent-type-assertions -- SAFETY: delivery reads only identity.sessionId, and the mocked send ignores host. - return session as unknown as StructuredSession -} - -async function renderPreamble(worker: 'chat' | 'terminal'): Promise { - const prompts: string[] = [] - await deliverWorkerDispatchPreamble({ - runtime: runtime(prompts), - db, - structuredSession: worker === 'chat' ? structuredSession() : null, - terminalHandle: worker === 'chat' ? CHAT_WORKER_HANDLE : TERMINAL_HANDLE, - dispatchId: 'ctx_1', - dispatchDepth: 1, - runId: 'run_1', - taskId: 'task_1', - taskSpec: 'do it', - coordinatorHandle: 'term_coord', - devMode: false, - requestId: 'req_1' - }) - return worker === 'chat' ? sent.preambles[0]! : prompts[0]! -} - /** Render what the structured lane hands a chat for one mailbox message. */ async function renderChatMail(mailbox: string): Promise<{ text: string; id: string }> { const texts: string[] = [] @@ -138,27 +75,6 @@ async function renderChatMail(mailbox: string): Promise<{ text: string; id: stri } describe('a chat agent and a terminal agent see the same text but for how each is named and called', () => { - it("teaches a chat worker a terminal worker's preamble but for its identity lines and the chat wording", async () => { - const chat = await renderPreamble('chat') - const terminal = await renderPreamble('terminal') - const withoutRedispatch = chat.replace(CHAT_REDISPATCH, TERMINAL_REDISPATCH) - - expect(terminal).not.toContain('Orca session ID') - expect(chat).toContain(`Your task ID is: task_1${SELF_LINE}\n`) - expect(chat).not.toContain(CHAT_WORKER_HANDLE) - expect(chat).not.toContain('this terminal') - expect(withoutRedispatch).not.toBe(chat) - expect(withoutRedispatch.split('this chat')).toHaveLength(2) - expect( - withoutRedispatch - .replace(SELF_LINE, '') - .split(CHAT_ADDRESS) - .join(TERMINAL_HANDLE) - .split('this chat') - .join('this terminal') - ).toBe(terminal) - }) - it.each([ ['a packaged app', true], ['a dev build', false] diff --git a/src/main/runtime/rpc/methods/orchestration/worker/closed-worker-terminal.test.ts b/src/main/runtime/rpc/methods/orchestration/worker/closed-worker-terminal.test.ts index f0eeeeeb474..4b2412d5fa7 100644 --- a/src/main/runtime/rpc/methods/orchestration/worker/closed-worker-terminal.test.ts +++ b/src/main/runtime/rpc/methods/orchestration/worker/closed-worker-terminal.test.ts @@ -62,7 +62,6 @@ describe('opening a terminal worker Orca closed', () => { } const open = (address: string) => h.call('orchestration.partyOpen', { address }) - const locate = (address: string) => h.call('orchestration.partyLocation', { address }) it('resumes its conversation in a new terminal, and a click after it reports reuses it', async () => { const { resource, resume, report } = await closedHelper() @@ -83,18 +82,6 @@ describe('opening a terminal worker Orca closed', () => { ) }) - it('keeps the lookup answer without a saved conversation', async () => { - const { resource, resume } = await closedHelper(false) - expect(await open(resource.terminal_handle)).toEqual(await locate(resource.terminal_handle)) - expect(resume).not.toHaveBeenCalled() - }) - - it('keeps the lookup answer when the conversation was recorded on another host', async () => { - const { resource, resume } = await closedHelper() - resume.mockResolvedValue({ ok: false, reason: 'no_resume_command' }) - expect(await open(resource.terminal_handle)).toEqual(await locate(resource.terminal_handle)) - }) - it('sends a later follow-up into the reopened terminal, not a second one', async () => { const { helper, resource, resume, report } = await closedHelper() await open(resource.terminal_handle) diff --git a/src/main/runtime/rpc/methods/orchestration/worker/worker-release-mobile-report.test.ts b/src/main/runtime/rpc/methods/orchestration/worker/worker-release-mobile-report.test.ts index a7dd79245e0..c53bf2bc5b3 100644 --- a/src/main/runtime/rpc/methods/orchestration/worker/worker-release-mobile-report.test.ts +++ b/src/main/runtime/rpc/methods/orchestration/worker/worker-release-mobile-report.test.ts @@ -1,8 +1,6 @@ import { afterEach, beforeEach, expect, it, vi } from 'vitest' import { createOrchestrationWorkerReleaseHarness } from './worker-release.test-support' -import { TERMINAL_SEND_METHODS } from '../../terminal/terminal-send-method' -import { sendTerminalStreamInput } from '../../terminal/terminal-input-delivery' -import { eraseRpcMethods, isStreamingMethod, type RpcMethod } from '../../../core' + import { registerBoundPty } from '../../../../bind-first-pane.test-fixture' const h = createOrchestrationWorkerReleaseHarness() @@ -61,60 +59,6 @@ it('an unknown handle does not fence another worker or access the database', asy ).resolves.toMatchObject({ state: 'released' }) }) -it.each(['unary', 'stream'])('mobile %s bytes do no orchestration database work', async (lane) => { - const worker = await h.startSettledWorker() - const runtime = h.runtime - runtime.registerPreAllocatedHandleForPty('pty-worker', 'term_worker') - registerBoundPty(runtime, 'pty-worker', 'repo::worktree', null, { - tabId: 'tab_worker', - leafId: 'bbbbbbbb-bbbb-4bbb-8bbb-bbbbbbbbbbbb', - incarnationId: 'runtime_test:term_worker:1' - }) - const write = vi.fn(() => true) - runtime.setPtyController({ write, kill: () => true, getForegroundProcess: async () => null }) - const commit = vi.fn(async () => {}) - vi.spyOn(runtime, 'beginMobileInputFloor').mockReturnValue({ commit, rollback: vi.fn() }) - const dbAccess = vi.spyOn(runtime, 'getOrchestrationDb') - const prepare = vi.spyOn(h.db.db, 'prepare') - const exec = vi.spyOn(h.db.db, 'exec') - const params = { - terminal: 'term_worker', - text: 'x', - client: { id: 'phone', type: 'mobile' as const } - } - if (lane === 'stream') { - await expect(sendTerminalStreamInput(runtime, { ...params, isMobile: true })).resolves.toBe( - 'delivered' - ) - } else { - const method = eraseRpcMethods(TERMINAL_SEND_METHODS).find( - (m): m is RpcMethod => m.name === 'terminal.send' && !isStreamingMethod(m) - )! - await expect( - method.handler(method.params!.parse(params) as never, { runtime } as never) - ).resolves.toMatchObject({ send: { accepted: true } }) - } - expect(write).toHaveBeenCalledWith('pty-worker', 'x', 'driving') - expect(commit).toHaveBeenCalledTimes(1) - expect(dbAccess).not.toHaveBeenCalled() - expect(prepare).not.toHaveBeenCalled() - expect(exec).not.toHaveBeenCalled() - expect(h.db.getWorkerTerminalResourceByOwner(worker.dispatchId)?.ownership_state).toBe('owned') -}) - -it('Keep open and the countdown are reachable from a mobile-scoped device token', async () => { - // An older phone's typing report must keep resolving too, or it would surface errors. - const { MOBILE_RPC_METHOD_ALLOWLIST } = - await import('../../../../runtime-rpc/runtime-rpc-mobile-method-allowlist') - for (const method of [ - 'orchestration.workerAutoClose', - 'orchestration.workerKeepOpen', - 'orchestration.workerTerminalUserInput' - ]) { - expect(MOBILE_RPC_METHOD_ALLOWLIST.has(method)).toBe(true) - } -}) - // Boot-wait Keep open must find custody written at terminal creation, or a later release closes a kept pane (#19337, #19608). it('a phone Keep open during the boot wait keeps the pane and fences the later release', async () => { const gate = h.deferred() diff --git a/src/main/runtime/runtime-reference-find.test.ts b/src/main/runtime/runtime-reference-find.test.ts index a97d56f7635..5a903f72f80 100644 --- a/src/main/runtime/runtime-reference-find.test.ts +++ b/src/main/runtime/runtime-reference-find.test.ts @@ -143,39 +143,4 @@ describe('stored reference lookup', () => { expect(result.matches[1].agents[0].terminal).toBe('remote-handle') expect(result.matches[1].workspace.hostId).toBe('ssh:build') }) - - it('handles 1000 workspaces with 23 references each without discovering agents for misses', async () => { - const workspaces = Array.from({ length: 1000 }, (_, i) => - workspace( - String(i), - Array.from({ length: 23 }, (_, n) => `https://github.com/acme/api/pull/${i * 23 + n + 1}`) - ) - ) - const agents = vi.fn(() => new Map()) - const source = { workspaces, agents } - expect((await findWorkspaceReferences({ query: 'MISSING-1' }, source)).matches).toEqual([]) - expect(agents).not.toHaveBeenCalled() - expect( - (await findWorkspaceReferences({ query: 'https://github.com/acme/api/pull/23000' }, source)) - .matches[0].workspace.id - ).toBe('999') - expect(agents).toHaveBeenCalledTimes(1) - }) - - it.each([0, -1, 1.5, Infinity])('rejects invalid limits %s', async (limit) => { - await expect( - findWorkspaceReferences({ query: linear, limit }, { workspaces: [], agents: () => new Map() }) - ).rejects.toThrow('positive integer') - }) - - it('rejects bare numbers and conflicting filters before inspecting agents', async () => { - const source = { workspaces: [], agents: vi.fn(() => new Map()) } - await expect(findWorkspaceReferences({ query: '5123' }, source)).rejects.toThrow( - 'full reference URL' - ) - await expect( - findWorkspaceReferences({ query: linear, worktree: 'one', repo: 'api' }, source) - ).rejects.toThrow('either --worktree or --repo') - expect(source.agents).not.toHaveBeenCalled() - }) }) diff --git a/src/main/runtime/session-tabs/session-tabs-phone-layout-moves.test.ts b/src/main/runtime/session-tabs/session-tabs-phone-layout-moves.test.ts index c66bbdf1a57..33caa5f7fdc 100644 --- a/src/main/runtime/session-tabs/session-tabs-phone-layout-moves.test.ts +++ b/src/main/runtime/session-tabs/session-tabs-phone-layout-moves.test.ts @@ -92,17 +92,6 @@ describe("a phone's layout change on a windowless server (switch on)", () => { ) }) - it("sends nothing for a group's last tab dropped on its neighbour's facing edge", async () => { - const { runtime } = await headlessRuntime() - const split = { kind: 'split', tabId: SECOND_TAB_ID, targetGroupId: SAVED_GROUP_ID } as const - await runtime.moveMobileSessionTab(WORKTREE, { ...split, splitDirection: 'right' }) - const apply = vi.spyOn(runtime, 'applyLayoutCommand') - - await runtime.moveMobileSessionTab(WORKTREE, { ...split, splitDirection: 'right' }) - - expect(apply).not.toHaveBeenCalled() - }) - it("answers a split main refuses with main's refusal code", async () => { const { runtime } = await headlessRuntime() vi.spyOn(runtime, 'applyLayoutCommand').mockResolvedValue({ ok: false, code: 'invalid_params' }) diff --git a/src/main/runtime/terminal-run-facts.test.ts b/src/main/runtime/terminal-run-facts.test.ts index 3789e98ab60..6ddcb5fb80a 100644 --- a/src/main/runtime/terminal-run-facts.test.ts +++ b/src/main/runtime/terminal-run-facts.test.ts @@ -17,12 +17,6 @@ describe('terminal run facts', () => { facts.recordSpawnCommit({ id: 'pending', incarnationId: 'inc-3' }) expect(facts.read('pending', 'inc-3').firstUserInputAt).toBeNull() }) - it('reads a run main never saw committed as not fresh', () => { - expect(new TerminalRunFactsRegister().read('pty-1', 'inc-1')).toEqual({ - freshSpawn: false, - firstUserInputAt: null - }) - }) it('keeps the first user input across later input and a re-registration of the same process', () => { const facts = new TerminalRunFactsRegister() @@ -72,14 +66,6 @@ describe('terminal run facts', () => { expect(facts.readProcessStartedAt('pty-2', 'inc-9')).toBeNull() }) - it('never reads a reattached process as fresh', () => { - const facts = new TerminalRunFactsRegister() - - facts.recordSpawnCommit({ id: 'pty-1', incarnationId: 'inc-1', isReattach: true }) - - expect(facts.read('pty-1', 'inc-1').freshSpawn).toBe(false) - }) - it('starts clean when a new process commits without an incarnation', () => { const facts = new TerminalRunFactsRegister() facts.recordSpawnCommit({ id: 'pty-1' }) diff --git a/src/main/runtime/terminal/surface/closed-pane-exit-cleanup-main.test.ts b/src/main/runtime/terminal/surface/closed-pane-exit-cleanup-main.test.ts index 10dc4df0f51..4e072c5add8 100644 --- a/src/main/runtime/terminal/surface/closed-pane-exit-cleanup-main.test.ts +++ b/src/main/runtime/terminal/surface/closed-pane-exit-cleanup-main.test.ts @@ -105,19 +105,6 @@ for (const host of ['no window', 'a window'] as const) { expect(await phoneShowsPane(harness.runtime, HEADLESS_SECOND_LEAF_ID)).toBe(false) }) - it(`the exit drops the closed-pane record in its one retirement write: ${route}, with ${host}`, async () => { - const harness = closingHost(host) - await close(harness.runtime) - // The fixture's setter may already be a mock carrying the close's own writes. - const writes = vi.spyOn(harness.runtime['store']!, 'setWorkspaceSession') - writes.mockClear() - - harness.runtime.onPtyExit('pty-b', 0, 'inc-b') - - expect(writes).toHaveBeenCalledTimes(1) - expect(writes.mock.calls[0]![0].closedTerminalPanesByPaneKey?.[PANE_B]).toBeUndefined() - }) - it(`exit then close retires them too: ${route}, with ${host}`, async () => { const harness = closingHost(host) diff --git a/src/main/runtime/terminal/surface/main-default-pane-acquisition.test.ts b/src/main/runtime/terminal/surface/main-default-pane-acquisition.test.ts deleted file mode 100644 index 39383678a4f..00000000000 --- a/src/main/runtime/terminal/surface/main-default-pane-acquisition.test.ts +++ /dev/null @@ -1,183 +0,0 @@ -import '../../orca-runtime-test-mocks.spec' -import { mkdtempSync, rmSync } from 'node:fs' -import { tmpdir } from 'node:os' -import { join } from 'node:path' -import { expect, it, vi } from 'vitest' -import { OrcaRuntimeService } from '../../orca-runtime' -import { OrchestrationDb } from '../../orchestration/db' -import { - openTopologyStoreWithAuthority, - emptyTerminalSessionProfile, - FIXTURE_GIT_WORKTREE_ID -} from '../../../persistence/terminal-topology/terminal-topology-profile-fixture' -import { WindowSession } from '../../../persistence/terminal-topology/terminal-topology-window-session-fixture' -import { getDefaultWorkspaceSession } from '../../../../shared/constants' -import * as loader from '../../../../shared/workspace-layout/workspace-layout-load' -import * as paneQuery from './pty-pane-query' -import * as surfaceIndex from './terminal-surface-index' -import type { ResolvedWorktree } from '../../runtime-worktree-path-identity' - -vi.mock('../../../../shared/layout-owner-switch', () => ({ ORCA_LAYOUT_OWNER_IS_MAIN: true })) -const leaf = (i: number) => `aaaaaaaa-aaaa-4aaa-8aaa-${String(i).padStart(12, '0')}` -const pane = `tab-main-0:${leaf(0)}` - -class DefaultPaneRuntime extends OrcaRuntimeService { - protected override async getResolvedWorktreeMap(): Promise> { - const worktree = this.buildResolvedWorktreeFromId(FIXTURE_GIT_WORKTREE_ID) - return new Map(worktree ? [[worktree.id, worktree]] : []) - } - captures = 0 - protected captureTerminalPaneReader(): paneQuery.PtyPaneOf { - this.captures++ - return super.captureTerminalPaneReader() - } - seed(id: string, handle: string, spawnPaneKey?: string, connected = true): void { - this.recordPtyWorktree(id, FIXTURE_GIT_WORKTREE_ID, { connected, spawnPaneKey }) - this.terminalIdentity.bindPty(id, handle) - this.terminalIdentity.setHandle(handle, { - handle, - runtimeId: this.runtimeId, - rendererGraphEpoch: this.rendererGraphEpoch, - worktreeId: FIXTURE_GIT_WORKTREE_ID, - tabId: `pty:${id}`, - leafId: `pty:${id}`, - ptyId: id, - ptyGeneration: 0 - }) - } -} - -async function fixture(population: number) { - const directory = mkdtempSync(join(tmpdir(), 'orca-pr34890-finite-')) - const { store } = await openTopologyStoreWithAuthority(directory, emptyTerminalSessionProfile()) - const db = new OrchestrationDb(':memory:') - const saved = new WindowSession(getDefaultWorkspaceSession()) - for (let i = 0; i < population; i++) { - saved.addTab(FIXTURE_GIT_WORKTREE_ID, `tab-main-${i}`, leaf(i)) - saved.bind(`tab-main-${i}`, leaf(i), `pty-main-${i}`) - } - store.setWorkspaceSession(saved.snapshot(), 'local') - await store.flushPendingOrThrowAsync() - const runtime = new DefaultPaneRuntime(store) - runtime.setOrchestrationDb(db) - runtime.seed('coord', 'term_coord') - for (let i = 0; i < population; i++) { - runtime.seed(`pty-main-${i}`, `term_main_${i}`) - } - runtime.seed('replacement', 'term_replacement') - const run = db.createRun({ - objective: 'Finite acquisition', - coordinatorHandle: 'term_coord', - coordinatorPaneKey: 'coord-pane' - }) - const task = db.createTask({ runId: run.id, spec: 'Main worker' }) - db.createDispatchContext({ - taskId: task.id, - assigneeHandle: 'term_missing_stored', - assigneePaneKey: pane, - creator: { kind: 'system' }, - maxDepth: 10 - }) - const stop = runtime.watchOrchestrationCrew(() => {}) - const rebind = async () => { - const next = structuredClone(store.getWorkspaceSession()) - next.terminalLayoutsByTabId['tab-main-0'].ptyIdsByLeafId = { [leaf(0)]: 'replacement' } - store.setWorkspaceSession(next, 'local') - await store.flushPendingOrThrowAsync() - } - return { - runtime, - rebind, - close: async () => { - stop() - db.close() - await store.freezeWritesAsync() - rmSync(directory, { recursive: true, force: true }) - } - } -} - -it('bounds the real public pane lookup and captured Crew read at N and 10N', async () => { - vi.useFakeTimers({ toFake: ['Date'] }) - vi.setSystemTime(new Date('2026-10-29T12:00:00Z')) - try { - for (const population of [3, 30]) { - const f = await fixture(population) - try { - const loads = vi.spyOn(loader, 'loadWorkspaceLayoutWithoutReport') - const capture = vi.spyOn(paneQuery, 'captureMainTerminalPaneLookup') - const qualify = vi.spyOn(surfaceIndex, 'terminalHoldsPane') - const measure = (read: () => unknown) => { - loads.mockClear() - capture.mockClear() - qualify.mockClear() - const value = read() - return { - value, - loads: loads.mock.calls.length, - surfaceCandidates: capture.mock.calls.reduce( - (sum, [surfaces]) => sum + surfaces.leaves.length, - 0 - ), - factCopies: capture.mock.calls.reduce( - (sum, [surfaces]) => sum + surfaces.paneFacts.size, - 0 - ), - qualificationCalls: qualify.mock.calls.length - } - } - expect(f.runtime.getTerminalHandleForPaneKey(pane)).toBe('term_main_0') - f.runtime.getOrchestrationCrewSnapshot('term_coord') - await f.rebind() - const next = measure(() => f.runtime.getTerminalHandleForPaneKey(pane)) - expect(next.value).toBe('term_replacement') - expect(next.loads).toBe(2) - expect(next.surfaceCandidates).toBe(population) - expect(next.factCopies).toBe(population) - expect(next.qualificationCalls).toBeLessThanOrEqual(population + 3) - const crew = measure(() => f.runtime.getOrchestrationCrewSnapshot('term_coord')) - expect(crew.loads).toBe(1) - expect(crew.surfaceCandidates).toBe(population) - loads.mockRestore() - capture.mockRestore() - qualify.mockRestore() - } finally { - await f.close() - } - } - } finally { - vi.useRealTimers() - } -}) - -it('keeps absent and exact/reminted connected public points outside global capture', () => { - const runtime = new DefaultPaneRuntime(null) - expect(runtime.getTerminalHandleForPaneKey(pane)).toBeNull() - expect(runtime.captures).toBe(0) - runtime.seed('fast', 'term_fast') - runtime.attachWindow(1) - runtime.syncWindowGraph(1, { - tabs: [ - { - tabId: 'tab-main-0', - worktreeId: FIXTURE_GIT_WORKTREE_ID, - title: 'Terminal', - activeLeafId: leaf(0), - layout: null - } - ], - leaves: [ - { - tabId: 'tab-main-0', - worktreeId: FIXTURE_GIT_WORKTREE_ID, - leafId: leaf(0), - ptyId: 'fast', - paneRuntimeId: 1 - } - ] - }) - const before = runtime.captures - expect(runtime.getTerminalHandleForPaneKey(pane)).toBe('term_fast') - expect(runtime.getTerminalHandleForPaneKey(`tab-moved:${leaf(0)}`)).toBe('term_fast') - expect(runtime.captures).toBe(before) -}) diff --git a/src/main/skills/skill-wsl-posix-semantics.integration.test.ts b/src/main/skills/skill-wsl-posix-semantics.integration.test.ts index 9f0d387b321..f003c33b6ea 100644 --- a/src/main/skills/skill-wsl-posix-semantics.integration.test.ts +++ b/src/main/skills/skill-wsl-posix-semantics.integration.test.ts @@ -14,7 +14,7 @@ import { installSharedSkill } from './skill-install-service' import { createSkillBundleArchive } from './skill-bundle-creation' import { installSkillBundle } from './skill-bundle-install-service' import { writeSkillTarGzip } from './skill-package-tar' -import { detectSkillProvidersInWsl } from './skill-wsl-provider-detection' + import { createWslSkillInstallFilesystem } from './skill-wsl-install-filesystem' const execFileAsync = promisify(execFile) @@ -66,10 +66,6 @@ describe.runIf(RUN_REAL_WSL)('real WSL POSIX skill semantics', () => { } }) - it('detects providers from the selected distro', async () => { - await expect(detectSkillProvidersInWsl(DISTRO)).resolves.toContain('codex') - }) - it('preserves case and applies owner-private executable modes', async () => { const markdown = Buffer.from( '---\nname: wsl-mode-skill\ndescription: WSL modes\n---\n\n# WSL\n' diff --git a/src/main/ssh/orcad-migration-blob-coordinator.test.ts b/src/main/ssh/orcad-migration-blob-coordinator.test.ts index ff4ce14987c..68a00dc563b 100644 --- a/src/main/ssh/orcad-migration-blob-coordinator.test.ts +++ b/src/main/ssh/orcad-migration-blob-coordinator.test.ts @@ -119,12 +119,6 @@ describe('orcad migration blob sender', () => { ).rejects.toThrow('orcad_migration_blob_descriptor_conflict:terminal-restore-hints') expect(destination.stageChunk).not.toHaveBeenCalled() }) - - it('sends nothing and asks nothing when there are no blobs', async () => { - const destination = realDestination([]) - await transferOrcadMigrationBlobs({ source: source(), manifest, blobs: [], destination }) - expect(destination.readCapabilities).not.toHaveBeenCalled() - }) }) function descriptor( diff --git a/src/main/startup/profile-state-write-failure.test.ts b/src/main/startup/profile-state-write-failure.test.ts index 7749f75f5af..19e950039af 100644 --- a/src/main/startup/profile-state-write-failure.test.ts +++ b/src/main/startup/profile-state-write-failure.test.ts @@ -92,16 +92,6 @@ it('attaches the alert to the visible main window and explains an unconfirmed wr expect(recordProfileStateWriteFailureReport).toHaveBeenCalledWith(workerExit, 'dialog') }) -it('tells the user saved changes are safe when no write was left unresolved', async () => { - fixture.state.mainWindow = new FakeWindow(true) - reportProfileStateWriteFailure(conflict) - await vi.waitFor(() => expect(fixture.show).toHaveBeenCalledOnce()) - expect(fixture.show.mock.calls[0][1]).toMatchObject({ - detail: - 'Changes saved before this point are safe. New changes will not be saved until you restart Orca.' - }) -}) - it('waits for a hidden window to be shown by the user instead of revealing it', async () => { const window = new FakeWindow(false) fixture.state.mainWindow = window diff --git a/src/main/text-generation/commit-message-text-generation-branch-name.test.ts b/src/main/text-generation/commit-message-text-generation-branch-name.test.ts index d2769728008..78115918849 100644 --- a/src/main/text-generation/commit-message-text-generation-branch-name.test.ts +++ b/src/main/text-generation/commit-message-text-generation-branch-name.test.ts @@ -100,39 +100,6 @@ describe('generateBranchNameFromContext', () => { }) }) - it('does not persist stdout-only branch failure detail that may echo the prompt', async () => { - const result = await generateBranchNameFromContext( - { firstPrompt: 'Customer secret in the first prompt' }, - { - agentId: 'custom', - model: '', - customAgentCommand: 'agent' - }, - { - kind: 'remote', - cwd: '/repo', - missingBinaryLocation: 'remote PATH', - execute: async () => ({ - stdout: 'Customer secret in the first prompt', - stderr: '', - exitCode: 1, - timedOut: false - }) - } - ) - - expect(result).toMatchObject({ - success: false, - error: 'agent CLI command failed with code 1.', - failureOutput: { - label: 'agent', - exitCode: 1, - stdout: 'Customer secret in the first prompt', - stderr: '' - } - }) - }) - it('describes a signal-terminated generator without a null exit code', async () => { const result = await generateBranchNameFromContext( { firstPrompt: 'Fix login flow' }, @@ -159,40 +126,6 @@ describe('generateBranchNameFromContext', () => { }) }) - it('keeps branch-name guidance first without dropping the output contract', async () => { - let prompt = '' - await generateBranchNameFromContext( - { firstPrompt: 'Fix login flow' }, - { - agentId: 'custom', - model: '', - customAgentCommand: 'agent', - customPrompt: 'Prefer auth terminology.', - commandInputTemplate: 'Prefer auth terminology.\n\n{basePrompt}' - }, - { - kind: 'remote', - cwd: '/repo', - missingBinaryLocation: 'remote PATH', - execute: async (plan) => { - prompt = plan.stdinPayload ?? '' - return { - stdout: 'fix-login-flow\n', - stderr: '', - exitCode: 0, - timedOut: false - } - } - } - ) - - expect(prompt.startsWith('Prefer auth terminology.')).toBe(true) - expect(prompt).toContain('Prefer auth terminology.') - expect(prompt).not.toContain('Additional user prompt:') - expect(prompt).toContain('Generate a short git branch name') - expect(prompt).toContain('Output ONLY the branch name on a single line') - }) - 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/window/updater-package-recovery-ipc.test.ts b/src/main/window/updater-package-recovery-ipc.test.ts index 7327ea8477a..52ae7cb43e7 100644 --- a/src/main/window/updater-package-recovery-ipc.test.ts +++ b/src/main/window/updater-package-recovery-ipc.test.ts @@ -139,21 +139,6 @@ describe('updater linux package recovery IPC handlers', () => { vi.unstubAllEnvs() }) - it('removes and re-registers both recovery channels', () => { - for (const channel of RECOVERY_CHANNELS) { - expect(removeHandlerMock).toHaveBeenCalledWith(channel) - expect(handleMock.mock.calls.filter(([name]) => name === channel)).toHaveLength(1) - } - }) - - it('routes both channels through the trusted-renderer guard', () => { - isTrustedUIRendererMock.mockReturnValue(false) - - expectBothChannelsRejected(webContents({})) - - expect(isTrustedUIRendererMock).toHaveBeenCalledTimes(2) - }) - it('serves the current main UI renderer', async () => { const event = senderEvent(webContents({})) diff --git a/src/main/windows/windows-pty-job.test.ts b/src/main/windows/windows-pty-job.test.ts index cff619e4295..a6149250db2 100644 --- a/src/main/windows/windows-pty-job.test.ts +++ b/src/main/windows/windows-pty-job.test.ts @@ -64,19 +64,6 @@ describe('terminatePtyJob', () => { expect(terminatePtyJob(ptyWithHandle(id))).toBe('unavailable') }) - it('passes the shell pid so an id from another backend cannot match', () => { - // winpty/ConPTY IDs can collide; always send shell PID so native code can refuse mismatches. - const terminateJob = vi.fn().mockReturnValue(true) - __setConptyJobNativeForTests(() => ({ - terminateJob, - listJobProcessIds: vi.fn(), - assignCurrentProcessToJob: vi.fn().mockReturnValue(true) - })) - - terminatePtyJob(ptyWithHandle(3, 9001)) - expect(terminateJob).toHaveBeenCalledWith(3, 9001) - }) - it('reports unavailable when the shell pid is unusable', () => { __setConptyJobNativeForTests(() => ({ terminateJob: vi.fn().mockReturnValue(true), @@ -106,16 +93,6 @@ describe('terminatePtyJob', () => { }) describe('listPtyJobProcessIds', () => { - it('returns the live pids, including a detached grandchild', () => { - // Detached/reparented grandchildren escape console and parent-PID walks but remain job members. - __setConptyJobNativeForTests(() => ({ - terminateJob: vi.fn(), - listJobProcessIds: vi.fn().mockReturnValue([107184, 91480]), - assignCurrentProcessToJob: vi.fn().mockReturnValue(true) - })) - expect(listPtyJobProcessIds(ptyWithHandle(1))).toEqual([107184, 91480]) - }) - it('reports null when there is no answer to give', () => { // Null is unverifiable, including after shell exit closes the job; it never proves death. __setConptyJobNativeForTests(() => null) @@ -131,16 +108,6 @@ describe('listPtyJobProcessIds', () => { }) describe('assignHostProcessToKillOnCloseJob', () => { - it('reports whether the host process is now kill-on-close', () => { - const assignCurrentProcessToJob = vi.fn().mockReturnValue(true) - __setConptyJobNativeForTests(() => ({ - terminateJob: vi.fn(), - listJobProcessIds: vi.fn(), - assignCurrentProcessToJob - })) - expect(assignHostProcessToKillOnCloseJob()).toBe(true) - }) - it('degrades to false when the OS refuses the assignment', () => { // An outer job that forbids nesting; orphan behaviour is then simply what // it was before, rather than a crash. @@ -165,24 +132,6 @@ describe('PTY Job Object teardown breadcrumbs', () => { })) } - it('records the shell pid whose job it terminated', () => { - withNative(() => true) - - expect(terminatePtyJob(ptyWithHandle(7, 4242))).toBe('terminated') - expect(recordSelfInitiatedTreeKillMock).toHaveBeenCalledWith({ - pid: 4242, - site: 'windows-pty-job-teardown', - scope: 'win-pty-job' - }) - }) - - it('records nothing when the native side refused', () => { - withNative(() => false) - - expect(terminatePtyJob(ptyWithHandle(7))).toBe('unavailable') - expect(recordSelfInitiatedTreeKillMock).not.toHaveBeenCalled() - }) - it('never downgrades a real termination to unavailable because diagnostics failed', () => { // Breadcrumb failure must not downgrade termination to unavailable and escalate taskkill. withNative(() => true) diff --git a/src/renderer/src/attention/hidden-workspace-notification-mute.test.ts b/src/renderer/src/attention/hidden-workspace-notification-mute.test.ts index e44c8bbbf25..d28cdf516d2 100644 --- a/src/renderer/src/attention/hidden-workspace-notification-mute.test.ts +++ b/src/renderer/src/attention/hidden-workspace-notification-mute.test.ts @@ -52,16 +52,6 @@ beforeEach(() => { }) describe('isWorkspaceNotificationMutedBySidebarFilters', () => { - it('mutes a workspace a Filters switch hides while the setting is off', () => { - expect(muted(CLI_ID)).toBe(true) - expect(muted(PLAIN_ID)).toBe(false) - }) - - it('mutes nothing when Notify about hidden workspaces is on', () => { - setNotifyHiddenWorkspaces(true) - expect(muted(CLI_ID)).toBe(false) - }) - it('fails open before settings or UI state hydrate, and for an unknown workspace', () => { expect(muted('repo1::/tmp/unknown')).toBe(false) store.setState({ persistedUIReady: false }) @@ -133,11 +123,6 @@ describe('isWorkspaceNotificationMutedBySidebarFilters', () => { store.setState({ hideWorkspacesFromOtherDevices: true }) expect(muted(folderWorkspaceKey('f1'))).toBe(true) }) - - it('ignores the project filter, which is not a Filters switch', () => { - store.setState({ hideCliCreatedWorkspaces: false, filterRepoIds: ['other-repo'] }) - expect(muted(PLAIN_ID)).toBe(false) - }) }) describe('muted workspace attention', () => { diff --git a/src/renderer/src/components/editor/ImageViewer.test.tsx b/src/renderer/src/components/editor/ImageViewer.test.tsx deleted file mode 100644 index eb7ddc5ce27..00000000000 --- a/src/renderer/src/components/editor/ImageViewer.test.tsx +++ /dev/null @@ -1,223 +0,0 @@ -import type * as ReactModule from 'react' -import { beforeEach, describe, expect, it, vi } from 'vitest' - -const reactHookRuntime = vi.hoisted(() => ({ - states: [] as unknown[], - index: 0 -})) - -vi.mock('react', async () => { - const actual = await vi.importActual('react') - return { - ...actual, - useMemo(factory: () => T) { - return factory() - }, - useCallback unknown>(callback: T) { - return callback - }, - useEffect() { - return undefined - }, - useContext() { - return false - }, - useRef(initial: T) { - return { current: initial } - }, - useState(initial: T | (() => T)) { - const stateIndex = reactHookRuntime.index++ - if (!(stateIndex in reactHookRuntime.states)) { - reactHookRuntime.states[stateIndex] = - typeof initial === 'function' ? (initial as () => T)() : initial - } - const setState = (next: T | ((previous: T) => T)): void => { - reactHookRuntime.states[stateIndex] = - typeof next === 'function' - ? (next as (previous: T) => T)(reactHookRuntime.states[stateIndex] as T) - : next - } - return [reactHookRuntime.states[stateIndex] as T, setState] as const - } - } -}) - -vi.mock('lucide-react', () => ({ - Image: function Image(props: Record) { - return { type: 'Image', props } - }, - RotateCcw: function RotateCcw(props: Record) { - return { type: 'RotateCcw', props } - }, - X: function X(props: Record) { - return { type: 'X', props } - }, - ZoomIn: function ZoomIn(props: Record) { - return { type: 'ZoomIn', props } - }, - ZoomOut: function ZoomOut(props: Record) { - return { type: 'ZoomOut', props } - } -})) - -vi.mock('@/components/ui/dialog', () => ({ - Dialog: function Dialog(props: { children?: unknown }) { - return { type: 'Dialog', props } - }, - DialogContent: function DialogContent(props: { children?: unknown }) { - return { type: 'DialogContent', props } - }, - DialogDescription: function DialogDescription(props: { children?: unknown }) { - return { type: 'DialogDescription', props } - }, - DialogTitle: function DialogTitle(props: { children?: unknown }) { - return { type: 'DialogTitle', props } - } -})) - -vi.mock('./PdfViewer', () => ({ - default: function PdfViewer(props: Record) { - return { type: 'PdfViewer', props } - } -})) - -type ReactElementLike = { - type: unknown - props: Record -} - -function expandNode(node: unknown): unknown { - if (node == null || typeof node === 'string' || typeof node === 'number') { - return node - } - if (Array.isArray(node)) { - return node.map(expandNode) - } - const el = node as ReactElementLike - if (typeof el.type === 'function') { - return expandNode(el.type(el.props)) - } - return { - ...el, - props: { - ...el.props, - children: expandNode(el.props?.children) - } - } -} - -function findElementsByType(node: unknown, typeName: string): ReactElementLike[] { - const results: ReactElementLike[] = [] - const visit = (current: unknown): void => { - if (current == null || typeof current === 'string' || typeof current === 'number') { - return - } - if (Array.isArray(current)) { - for (const child of current) { - visit(child) - } - return - } - const el = current as ReactElementLike - if (el.type === typeName) { - results.push(el) - } - visit(el.props?.children) - } - visit(node) - return results -} - -function findPreviewImage(node: unknown): ReactElementLike { - const image = findElementsByType(node, 'img').find((element) => element.props.onError) - if (!image) { - throw new Error('preview image not found') - } - return image -} - -async function renderExpandedImageViewer(content: string): Promise { - reactHookRuntime.index = 0 - const module = await import('./ImageViewer') - return expandNode( - module.default({ - content, - filePath: '/repo/preview.png', - mimeType: 'image/png' - }) - ) -} - -function pngBase64(width: number): string { - const bytes = Buffer.alloc(24) - Buffer.from([137, 80, 78, 71, 13, 10, 26, 10]).copy(bytes) - bytes.writeUInt32BE(13, 8) - bytes.write('IHDR', 12, 'ascii') - bytes.writeUInt32BE(width, 16) - bytes.writeUInt32BE(1, 20) - return bytes.toString('base64') -} - -describe('ImageViewer preview source retry', () => { - beforeEach(() => { - reactHookRuntime.states = [] - reactHookRuntime.index = 0 - vi.clearAllMocks() - }) - - it('retries an earlier failed source after a later source loads successfully', async () => { - const failedContent = pngBase64(1) - const loadedContent = pngBase64(2) - - const firstRender = await renderExpandedImageViewer(failedContent) - const firstImage = findPreviewImage(firstRender) - expect(firstImage.props.src).toBe(`data:image/png;base64,${failedContent}`) - - ;(firstImage.props.onError as () => void)() - const failedRender = await renderExpandedImageViewer(failedContent) - expect(findElementsByType(failedRender, 'Image')).toHaveLength(1) - expect(findElementsByType(failedRender, 'img')).toHaveLength(0) - - const loadedRender = await renderExpandedImageViewer(loadedContent) - const loadedImage = findPreviewImage(loadedRender) - ;( - loadedImage.props.onLoad as (event: { - currentTarget: { naturalWidth: number; naturalHeight: number } - }) => void - )({ currentTarget: { naturalWidth: 12, naturalHeight: 10 } }) - - const retryRender = await renderExpandedImageViewer(failedContent) - const retryImage = findPreviewImage(retryRender) - expect(retryImage.props.src).toBe(`data:image/png;base64,${failedContent}`) - }) - - it('shows a failure instead of loading an unsafe raster forever', async () => { - const rendered = await renderExpandedImageViewer(pngBase64(32_769)) - - expect(findElementsByType(rendered, 'Image')).toHaveLength(1) - expect(findElementsByType(rendered, 'img')).toHaveLength(0) - }) -}) - -describe('ImageViewer reading its bytes by URL', () => { - beforeEach(() => { - reactHookRuntime.states = [] - reactHookRuntime.index = 0 - vi.clearAllMocks() - }) - - it('shows an image from its orca-media URL, sized from the grant', async () => { - reactHookRuntime.index = 0 - const module = await import('./ImageViewer') - const rendered = expandNode( - module.default({ - src: 'orca-media://file/token?revision=r1', - byteSize: 2048, - filePath: '/repo/preview.png', - mimeType: 'image/png' - }) - ) - expect(findPreviewImage(rendered).props.src).toBe('orca-media://file/token?revision=r1') - expect(JSON.stringify(rendered)).toContain('2.0 KB') - }) -}) diff --git a/src/renderer/src/components/editor/diff/sections/DiffSectionBody.word-wrap-lifecycle.test.tsx b/src/renderer/src/components/editor/diff/sections/DiffSectionBody.word-wrap-lifecycle.test.tsx index dd92ee4169d..4033b0af57b 100644 --- a/src/renderer/src/components/editor/diff/sections/DiffSectionBody.word-wrap-lifecycle.test.tsx +++ b/src/renderer/src/components/editor/diff/sections/DiffSectionBody.word-wrap-lifecycle.test.tsx @@ -195,15 +195,4 @@ describe('combined diff word-wrap lifecycle', () => { expect(syncWordWrap).toHaveBeenLastCalledWith(mountedEditors[1]?.editor, false) }) - - // The Monaco wrapper calls updateOptions on every options identity change, once per mounted section. - it('keeps editor options stable across re-renders that change no option', () => { - const view = render() - view.rerender() - expect(renderedOptions.at(-1)).toBe(renderedOptions[0]) - - view.rerender() - expect(renderedOptions.at(-1)).not.toBe(renderedOptions[0]) - expect(renderedOptions.at(-1)).toMatchObject({ renderSideBySide: false }) - }) }) diff --git a/src/renderer/src/components/editor/markdown-preview/search/markdown-preview-search-crash.test.tsx b/src/renderer/src/components/editor/markdown-preview/search/markdown-preview-search-crash.test.tsx index 53f2afd768d..52a21fbd61f 100644 --- a/src/renderer/src/components/editor/markdown-preview/search/markdown-preview-search-crash.test.tsx +++ b/src/renderer/src/components/editor/markdown-preview/search/markdown-preview-search-crash.test.tsx @@ -48,14 +48,6 @@ describe('markdown preview search highlighting keeps react-owned DOM intact (cra return body } - it('applies highlights without mutating the react-owned subtree', () => { - const body = render(['hello world']) - const matches = applyMarkdownPreviewSearchHighlights(instance, body, 'lo') - expect(matches.length).toBe(1) - expect(body.querySelector('mark')).toBeNull() - expect(body.textContent).toBe('hello world') - }) - it('survives a streamed re-render while highlights are active', () => { const body = render(['alpha ', 'beta']) applyMarkdownPreviewSearchHighlights(instance, body, 'beta') @@ -108,15 +100,6 @@ describe('markdown preview search painting with the CSS Custom Highlight API', ( return div } - it('paints many matches without a constructor-arg spread', () => { - const instance = {} - const body = bodyWithText('a a a a a a a a') - expect(() => applyMarkdownPreviewSearchHighlights(instance, body, 'a')).not.toThrow() - expect(registry.entries.get(SEARCH_HIGHLIGHT_NAME)?.ranges.size).toBe(8) - clearMarkdownPreviewSearchHighlights(instance) - body.remove() - }) - it('unions highlights across previews so a second Find does not clobber the first', () => { const a = {} const b = {} @@ -140,20 +123,6 @@ describe('markdown preview search painting with the CSS Custom Highlight API', ( bodyB.remove() }) - it('navigation repaints only the active highlight, not the full match set', () => { - const instance = {} - const body = bodyWithText('a a a a') - const matches = applyMarkdownPreviewSearchHighlights(instance, body, 'a') - const paintedAfterApply = registry.entries.get(SEARCH_HIGHLIGHT_NAME) - setActiveMarkdownPreviewSearchMatch(instance, matches, 0) - setActiveMarkdownPreviewSearchMatch(instance, matches, 1) - // Navigation must retain the search highlight while moving the active match. - expect(registry.entries.get(SEARCH_HIGHLIGHT_NAME)).toBe(paintedAfterApply) - expect(registry.entries.get(ACTIVE_SEARCH_HIGHLIGHT_NAME)?.ranges.has(matches[1])).toBe(true) - clearMarkdownPreviewSearchHighlights(instance) - body.remove() - }) - it('re-apply drops the active highlight until the caller repaints it (same-count rerender)', () => { const instance = {} const body = bodyWithText('one two one two') diff --git a/src/renderer/src/components/editor/model-lifetime/retention/closed-editor-tab-disposal.test.ts b/src/renderer/src/components/editor/model-lifetime/retention/closed-editor-tab-disposal.test.ts index a8e91414f47..6b4b849c472 100644 --- a/src/renderer/src/components/editor/model-lifetime/retention/closed-editor-tab-disposal.test.ts +++ b/src/renderer/src/components/editor/model-lifetime/retention/closed-editor-tab-disposal.test.ts @@ -129,24 +129,6 @@ function buildScenario(): { } describe('disposeClosedEditorModels', () => { - it('scans the model registry once per batch instead of twice per closed diff tab', () => { - const batched = buildScenario() - const batchedRegistry = createRegistry(batched.models) - disposeClosedEditorModels(batchedRegistry, batched.closedTabs) - - const perTab = buildScenario() - const perTabRegistry = createRegistry(perTab.models) - disposeByPrefixPerTab(perTabRegistry, perTab.prefixes) - - expect(perTabRegistry.counters.getModelsCalls).toBe(CLOSED_DIFF_TAB_COUNT * 2) - expect(perTabRegistry.counters.uriToStringCalls).toBe( - CLOSED_DIFF_TAB_COUNT * 2 * perTab.models.length * 2 - ) - - expect(batchedRegistry.counters.getModelsCalls).toBe(1) - expect(batchedRegistry.counters.uriToStringCalls).toBeLessThanOrEqual(batched.models.length * 2) - }) - it('disposes exactly the models the per-tab sweep disposed', () => { const batched = buildScenario() disposeClosedEditorModels(createRegistry(batched.models), batched.closedTabs) @@ -187,11 +169,4 @@ describe('disposeClosedEditorModels', () => { closed.modifiedModelPath ]) }) - - it('is a no-op when nothing closed', () => { - const registry = createRegistry([createModel('diff:original:tab-1:tab-1')]) - disposeClosedEditorModels(registry, []) - expect(registry.counters.getModelsCalls).toBe(0) - expect(registry.models[0].disposed).toBe(false) - }) }) diff --git a/src/renderer/src/components/editor/rich-markdown/clipboard/rich-markdown-cut.test.ts b/src/renderer/src/components/editor/rich-markdown/clipboard/rich-markdown-cut.test.ts index 2cfcddca68b..2137bdeeab9 100644 --- a/src/renderer/src/components/editor/rich-markdown/clipboard/rich-markdown-cut.test.ts +++ b/src/renderer/src/components/editor/rich-markdown/clipboard/rich-markdown-cut.test.ts @@ -8,7 +8,7 @@ import { TaskList, TaskItem } from '@tiptap/extension-list' import { Table, TableCell, TableHeader, TableRow } from '@tiptap/extension-table' import { createIsolatedMarkdownExtensionForTests } from '../schema/isolated-markdown-extension-for-tests' import { handleRichMarkdownCut } from './rich-markdown-cut-handler' -import { normalizeEmptyListItems, normalizeSoftBreaks } from '../schema/rich-markdown-normalize' +import { normalizeEmptyListItems } from '../schema/rich-markdown-normalize' const showCutLimitErrorMock = vi.hoisted(() => vi.fn()) @@ -75,23 +75,6 @@ function createClipboardEventMock(options?: { failReadback?: boolean }): { } describe('rich markdown cut handler behavior', () => { - it('soft-break normalization still creates visible paragraph breaks', () => { - const editor = createEditor('Line one\nLine two\nLine three\n') - normalizeSoftBreaks(editor) - - expect(countParagraphs(editor)).toBe(3) - const paragraphs: string[] = [] - editor.state.doc.forEach((node) => { - if (node.type.name === 'paragraph') { - expect(node.textContent).not.toContain('\n') - paragraphs.push(node.textContent) - } - }) - expect(paragraphs).toEqual(['Line one', 'Line two', 'Line three']) - - editor.destroy() - }) - it('Cmd+X cuts only a visual line inside a hard-wrapped paragraph', () => { const editor = createEditor('Alpha segment stays\nMiddle segment is cut\nOmega segment stays') try { diff --git a/src/renderer/src/components/editor/rich-markdown/details/details-markdown-html.test.ts b/src/renderer/src/components/editor/rich-markdown/details/details-markdown-html.test.ts index 2f4c85d4893..77c93dd7e1c 100644 --- a/src/renderer/src/components/editor/rich-markdown/details/details-markdown-html.test.ts +++ b/src/renderer/src/components/editor/rich-markdown/details/details-markdown-html.test.ts @@ -1,11 +1,7 @@ import { afterEach, describe, expect, it, vi } from 'vitest' import { - extractDetailsSummaryHtml, isEditableDetailsHtmlBlock, matchDetailsHtmlBlock, - normalizeDetailsOpeningTag, - parseDetailsAttributes, - parseToggleHeadingVariant, type DetailsHtmlBlock } from './details-markdown-html' @@ -27,110 +23,6 @@ afterEach(() => { }) describe('details markdown html', () => { - it.each([ - ['
', '
'], - ['
', '
'], - ['
', '
'], - ["
", '
'], - ['
', '
'], - [ - "
", - '
' - ] - ])('normalizes supported opening tag %s like the serializer', (input, expected) => { - expect(normalizeDetailsOpeningTag(input)).toBe(expected) - }) - - it.each([ - '
', - '
', - '
', - "
", - '
', - '
', - '
', - '', - '
', - '', - '' - ])('leaves noncanonical or unrelated fragment %s unchanged', (fragment) => { - expect(normalizeDetailsOpeningTag(fragment)).toBe(fragment) - }) - - it.each(['ORCA-DETAILS', 'Orca-Details'])( - 'keeps case-sensitive class %s out of editable details nodes', - (className) => { - expect( - isEditableHtml(`
ToggleBody
`) - ).toBe(false) - } - ) - - it('extracts leading summary html without regex capture', () => { - const matchSpy = vi.spyOn(String.prototype, 'match') - const inner = `\n${'Heading line\n'.repeat(1_000)}

Body

` - - const summary = extractDetailsSummaryHtml(inner) - - expect(summary?.content).toContain('Heading line') - expect(summary?.rawLength).toBe(inner.indexOf('

Body

')) - const usedSummaryCapture = matchSpy.mock.calls.some( - ([pattern]) => - pattern instanceof RegExp && - pattern.source.startsWith('^\\s* { - const matchSpy = vi.spyOn(String.prototype, 'match') - const block: DetailsHtmlBlock = { - raw: '', - openingAttributes: '', - inner: `${'Heading line\n'.repeat(1_000)}

Body

` - } - - expect(isEditableDetailsHtmlBlock(block)).toBe(true) - const usedSummaryCapture = matchSpy.mock.calls.some( - ([pattern]) => - pattern instanceof RegExp && - pattern.source.startsWith('^\\s* { - expect(parseToggleHeadingVariant('heading-5')).toBe('heading-5') - expect(parseToggleHeadingVariant('heading-6')).toBeNull() - expect(parseDetailsAttributes(' data-orca-toggle="heading-5"')).toMatchObject({ - variant: 'heading-5' - }) - expect(parseDetailsAttributes(' data-orca-toggle="heading-6"')).toMatchObject({ - variant: null - }) - - const editableHeading5: DetailsHtmlBlock = { - raw: '', - openingAttributes: ' data-orca-toggle="heading-5"', - inner: 'Toggle

Body

' - } - const unsupportedHeading6: DetailsHtmlBlock = { - raw: '', - openingAttributes: ' data-orca-toggle="heading-6"', - inner: 'Toggle

Body

' - } - - expect(isEditableDetailsHtmlBlock(editableHeading5)).toBe(true) - expect(isEditableDetailsHtmlBlock(unsupportedHeading6)).toBe(false) - }) - - it('accepts nested toggles whose descendants are all editable', () => { - expect(isEditableHtml(nestedToggles(2))).toBe(true) - expect(isEditableHtml(nestedToggles(16))).toBe(true) - }) - it('bounds each of many sibling nested toggles independently', () => { const siblings = (count: number, extra = ''): string => Array.from( @@ -146,16 +38,6 @@ describe('details markdown html', () => { expect(isEditableHtml(wrap(`${siblings(20)}\n\n${siblings(1, ' id="x"')}`))).toBe(false) }) - it('rejects a toggle whose nested toggle is not itself editable', () => { - const block: DetailsHtmlBlock = { - raw: '', - openingAttributes: ' class="orca-details"', - inner: 'Outer
Inner

Body

' - } - - expect(isEditableDetailsHtmlBlock(block)).toBe(false) - }) - it('rejects nesting past the recursion guard instead of recursing without bound', () => { expect(isEditableHtml(nestedToggles(17))).toBe(false) expect(isEditableHtml(nestedToggles(400))).toBe(false) diff --git a/src/renderer/src/components/editor/useEditorPanelFloatingTabContentState.test.tsx b/src/renderer/src/components/editor/useEditorPanelFloatingTabContentState.test.tsx index 2696c9b3e6c..2d002a5d856 100644 --- a/src/renderer/src/components/editor/useEditorPanelFloatingTabContentState.test.tsx +++ b/src/renderer/src/components/editor/useEditorPanelFloatingTabContentState.test.tsx @@ -136,21 +136,6 @@ describe('restored client-local editor tabs', () => { } ) - it('reads a local tab stored outside its own project as user-named', async () => { - const filePath = '/Users/me/notes/audit.md' - const activeFile = createFloatingFile(filePath, { - relativePath: filePath, - worktreeId: 'repo-local::/Users/me/project' - }) - - await act(async () => root?.render()) - - await vi.waitFor(() => expect(latestFileContents[activeFile.id]?.content).toBe('# local')) - expect(mocks.readRuntimeFileContent).toHaveBeenCalledWith( - expect.objectContaining({ filePath, access: { kind: 'user-file' } }) - ) - }) - it('keeps an absolute floating file in its panel when a project also owns the path', async () => { const filePath = '/project/readme.md' const activeFile = createFloatingFile(filePath, { relativePath: filePath }) @@ -168,18 +153,4 @@ describe('restored client-local editor tabs', () => { expect.objectContaining({ filePath, access: { kind: 'user-file' } }) ) }) - - it('keeps a project tab inside its root', async () => { - const activeFile = createFloatingFile('/Users/me/project/README.md', { - relativePath: 'README.md', - worktreeId: 'repo::/Users/me/project' - }) - - await act(async () => root?.render()) - - await vi.waitFor(() => expect(latestFileContents[activeFile.id]?.content).toBe('# local')) - expect(mocks.readRuntimeFileContent).toHaveBeenCalledWith( - expect.objectContaining({ access: undefined }) - ) - }) }) diff --git a/src/renderer/src/components/floating-terminal/FloatingTerminalToggleButton.test.tsx b/src/renderer/src/components/floating-terminal/FloatingTerminalToggleButton.test.tsx index f273d760a4c..a3945ac096a 100644 --- a/src/renderer/src/components/floating-terminal/FloatingTerminalToggleButton.test.tsx +++ b/src/renderer/src/components/floating-terminal/FloatingTerminalToggleButton.test.tsx @@ -101,7 +101,6 @@ vi.mock('@/store', () => ({ useAppStore: (selector: (state: typeof storeState) => T): T => selector(storeState) })) -// Isolate dot visibility here; store/selectors.test.ts covers the real unread selector. vi.mock('@/store/selectors', () => ({ selectFloatingWorkspaceHasUnread: (state: typeof storeState): boolean => state.hasFloatingUnread })) diff --git a/src/renderer/src/components/native-chat-restart-action-notifications.test.ts b/src/renderer/src/components/native-chat-restart-action-notifications.test.ts deleted file mode 100644 index 937aebe36f4..00000000000 --- a/src/renderer/src/components/native-chat-restart-action-notifications.test.ts +++ /dev/null @@ -1,182 +0,0 @@ -import { isValidElement } from 'react' -import { renderToStaticMarkup } from 'react-dom/server' -import { toast } from 'sonner' -import { beforeEach, expect, it, vi } from 'vitest' -import { announceRestartResults } from './native-chat-restart-action-notifications' -import { lastToastShow } from './native-chat-resume-toast.test-support' - -vi.mock('sonner', async () => - (await import('./native-chat-resume-sonner-mock.test-support')).resumeSonnerMock() -) - -const show = vi.fn() -const refusedBoth = [ - { sessionId: 'a', outcome: 'refused' as const }, - { sessionId: 'b', outcome: 'refused' as const } -] - -beforeEach(() => vi.clearAllMocks()) - -function titles(): unknown[] { - return vi.mocked(toast).mock.calls.map(([text]) => text) -} - -it.each([true, false])('offers View chats on a success toast with a failure list: %s', (listed) => { - announceRestartResults( - ['a', 'b'], - [ - { sessionId: 'a', outcome: 'continued' }, - { sessionId: 'b', outcome: 'continued' } - ], - listed ? [] : undefined, - show - ) - expect(titles()).toEqual(['Resumed 2 chats']) - expect(toast.success).toHaveBeenCalledTimes(1) - expect(vi.mocked(toast).mock.calls[0]?.[1]?.action).toEqual({ - label: 'View chats', - onClick: expect.any(Function) - }) - expect(lastToastShow()).toBeDefined() -}) - -it('offers Review on an error toast for chats a resume could not carry on', () => { - announceRestartResults(['a', 'b'], refusedBoth, refusedBoth, show) - expect(titles()).toEqual(['2 chats couldn’t be resumed']) - const options = vi.mocked(toast).mock.calls[0]?.[1] - expect(options).not.toHaveProperty('description') - expect(toast.error).toHaveBeenCalledTimes(1) - expect(options?.action).toEqual({ label: 'Review', onClick: expect.any(Function) }) - lastToastShow()?.() - expect(show).toHaveBeenCalledTimes(1) -}) - -it('offers Review on a warning toast when a resume could not be confirmed', () => { - announceRestartResults( - ['a', 'b'], - [ - { sessionId: 'a', outcome: 'continued' }, - { sessionId: 'b', outcome: 'unknown' } - ], - [{ sessionId: 'b', outcome: 'unconfirmed' }], - show - ) - expect(titles()).toEqual(['Couldn’t confirm 1 chat was resumed']) - expect(toast.warning).toHaveBeenCalledTimes(1) - expect(vi.mocked(toast).mock.calls[0]?.[1]?.action).toEqual({ - label: 'Review', - onClick: expect.any(Function) - }) -}) - -it('reports a mixed resume in one toast', () => { - announceRestartResults( - ['a', 'b', 'c'], - [ - { sessionId: 'a', outcome: 'continued' }, - { sessionId: 'b', outcome: 'continued' }, - { sessionId: 'c', outcome: 'refused' } - ], - [{ sessionId: 'c', outcome: 'refused' }], - show - ) - expect(vi.mocked(toast).mock.calls).toEqual([ - ['1 chat couldn’t be resumed', expect.objectContaining({ description: 'Resumed 2 chats' })] - ]) -}) - -it('puts each extra count on its own line when a resume had all three outcomes', () => { - announceRestartResults( - ['a', 'b', 'c'], - [ - { sessionId: 'a', outcome: 'continued' }, - { sessionId: 'b', outcome: 'refused' }, - { sessionId: 'c', outcome: 'unknown' } - ], - [ - { sessionId: 'b', outcome: 'refused' }, - { sessionId: 'c', outcome: 'unconfirmed' } - ], - show - ) - expect(toast).toHaveBeenCalledTimes(1) - const [title, options] = vi.mocked(toast).mock.calls[0]! - expect(title).toBe('1 chat couldn’t be resumed') - const description = options?.description - if (!isValidElement(description)) { - throw new Error('Expected one line per count') - } - expect(renderToStaticMarkup(description)).toBe( - 'Couldn’t confirm 1 other chat was resumed' + - 'Resumed 1 chat' - ) -}) - -it('counts only the requested chats the host still lists as failed', () => { - announceRestartResults(['a', 'b'], refusedBoth, [{ sessionId: 'a', outcome: 'refused' }], show) - expect(titles()).toEqual(['1 chat couldn’t be resumed']) -}) - -it('says nothing when the host lists none of them as failed', () => { - announceRestartResults(['a', 'b'], refusedBoth, [], show) - expect(toast).not.toHaveBeenCalled() -}) - -it('counts every chat not carried on when no failure list was read, with nothing to open', () => { - announceRestartResults(['a', 'b'], [], undefined, show) - expect(titles()).toEqual(['2 chats couldn’t be resumed']) - expect(vi.mocked(toast).mock.calls[0]?.[1]).not.toHaveProperty('action') -}) - -it('counts every chat as unconfirmed when the answer carried no outcomes', () => { - announceRestartResults(['a', 'b'], undefined, undefined, show) - expect(titles()).toEqual(['Couldn’t confirm 2 chats were resumed']) -}) - -// The host retires an unconfirmed send once the agent is seen carrying on it; the resume must still -// report the chat, and as resumed, not as a failure the list can no longer show. -it('preserves normalized uncertainty when a pending-list read omits the chat', () => { - announceRestartResults(['a'], [{ sessionId: 'a', outcome: 'unknown' }], [], show) - expect(titles()).toEqual(['Couldn’t confirm 1 chat was resumed']) -}) - -// Unconfirmed means the agent may well be working; "couldn't be resumed" would invite a second send. -// The normalized action result takes precedence over a different host observation. -it('counts normalized uncertainty separately from matching refusal', () => { - announceRestartResults( - ['a', 'b'], - [ - { sessionId: 'a', outcome: 'refused' }, - { sessionId: 'b', outcome: 'unknown' } - ], - [ - { sessionId: 'a', outcome: 'unconfirmed' }, - { sessionId: 'b', outcome: 'refused' } - ], - show - ) - expect(vi.mocked(toast).mock.calls).toEqual([ - [ - '1 chat couldn’t be resumed', - expect.objectContaining({ description: 'Couldn’t confirm 1 other chat was resumed' }) - ] - ]) -}) - -it('leads with the unconfirmed count when nothing was refused', () => { - announceRestartResults( - ['a', 'b'], - [ - { sessionId: 'a', outcome: 'unknown' }, - { sessionId: 'b', outcome: 'pending' } - ], - undefined, - show - ) - expect(vi.mocked(toast).mock.calls).toEqual([ - [ - 'Couldn’t confirm 2 chats were resumed', - expect.not.objectContaining({ description: expect.anything() }) - ] - ]) -}) diff --git a/src/renderer/src/components/native-chat/NativeChatMessageList.queued-turn-order.test.tsx b/src/renderer/src/components/native-chat/NativeChatMessageList.queued-turn-order.test.tsx index 12907958a70..6580a2a9061 100644 --- a/src/renderer/src/components/native-chat/NativeChatMessageList.queued-turn-order.test.tsx +++ b/src/renderer/src/components/native-chat/NativeChatMessageList.queued-turn-order.test.tsx @@ -163,33 +163,5 @@ describe.each([['states each row’s turn', true]])( screen.getByText('SECOND DONE') ) }) - - it('keeps that order once both turns settle', () => { - render( - - ) - - expectInOrder( - screen.getByText('Run the first job'), - bar('Worked for 17s'), - screen.getByText('FIRST DONE'), - screen.getByText('Then run the second job'), - bar('Worked for 51s'), - screen.getByText('SECOND DONE') - ) - }) } ) diff --git a/src/renderer/src/components/native-chat/use-structured-agent-session-context-usage.test.tsx b/src/renderer/src/components/native-chat/use-structured-agent-session-context-usage.test.tsx index 63dbc52988c..d0ac410def8 100644 --- a/src/renderer/src/components/native-chat/use-structured-agent-session-context-usage.test.tsx +++ b/src/renderer/src/components/native-chat/use-structured-agent-session-context-usage.test.tsx @@ -145,21 +145,6 @@ describe('useStructuredAgentSession context usage', () => { expect(optionReads()).toBe(1) }) - it('asks the host again when a live turn revision fell outside the loaded window', async () => { - answerOptions([ - { used: estimate(150_000), window: WINDOW }, - { used: estimate(170_000), window: WINDOW } - ]) - const { result, rerender } = render() - await waitFor(() => expect(result.current.contextUsage).toMatchObject({ usedTokens: 150_000 })) - - unloadedTurnRevisions = 1 - rerender() - - await waitFor(() => expect(result.current.contextUsage).toMatchObject({ usedTokens: 170_000 })) - expect(optionReads()).toBe(2) - }) - it('keeps one refresh in flight and one behind it however many revisions it missed', async () => { const replies: ((used: number) => void)[] = [] mocks.call.mockImplementation((_target, method) => diff --git a/src/renderer/src/components/native-chat/use-structured-agent-session-question-answers.test.tsx b/src/renderer/src/components/native-chat/use-structured-agent-session-question-answers.test.tsx index 6ac6447c713..2cf509f12b9 100644 --- a/src/renderer/src/components/native-chat/use-structured-agent-session-question-answers.test.tsx +++ b/src/renderer/src/components/native-chat/use-structured-agent-session-question-answers.test.tsx @@ -179,22 +179,4 @@ describe('desktop structured prompt answers', () => { }) expect(respondCall()).not.toHaveProperty('answers') }) - - it('sends an approval decision without probing the host', async () => { - const { result } = renderHook(() => - useStructuredAgentSession({ - sessionId: 'session-1', - target, - agent: 'claude', - isVisible: true - }) - ) - - await act(async () => { - await result.current.respond(approval, { kind: 'option', optionId: 'allow' }) - }) - - expect(mocks.questionAnswersSupported).not.toHaveBeenCalled() - expect(respondCall()).toMatchObject({ itemId: 'approval-1', optionId: 'allow' }) - }) }) diff --git a/src/renderer/src/components/new-workspace/NewWorkspaceComposerAgentSection.test.tsx b/src/renderer/src/components/new-workspace/NewWorkspaceComposerAgentSection.test.tsx deleted file mode 100644 index cd5d34a9ff1..00000000000 --- a/src/renderer/src/components/new-workspace/NewWorkspaceComposerAgentSection.test.tsx +++ /dev/null @@ -1,140 +0,0 @@ -// @vitest-environment happy-dom - -import { cleanup, fireEvent, render, screen } from '@testing-library/react' -import { afterEach, describe, expect, it, vi } from 'vitest' -import { TooltipProvider } from '@/components/ui/tooltip' -import { AGENT_CATALOG } from '@/lib/agent-catalog' -import { NewWorkspaceComposerAgentSection } from './NewWorkspaceComposerAgentSection' - -afterEach(cleanup) - -describe('NewWorkspaceComposerAgentSection agent picker', () => { - it.each([ - { kind: 'project', selectedRepoIsGit: true, matched: true }, - { kind: 'project', selectedRepoIsGit: false, matched: false }, - { kind: 'project-group', selectedRepoIsGit: true, matched: false } - ] as const)( - 'keeps Blank Terminal and scopes menu width to Create worktree: $kind / $selectedRepoIsGit', - ({ kind, selectedRepoIsGit, matched }) => { - const work: { size: number; initial: number; update: number; targetChange: number }[] = [] - for (const size of [32, 320]) { - let optionReads = 0 - const projectOptions = Array.from({ length: size }, (_, index) => { - const optionKind = index === 0 ? (kind === 'project' ? 'project-group' : 'project') : kind - return { - get id() { - optionReads++ - return index === size - 1 ? 'target' : index === 0 ? 'alternate' : `option-${index}` - }, - displayName: `Target ${index}`, - badgeColor: '', - detail: `/target-${index}`, - ...(optionKind === 'project' - ? { kind: optionKind, projectId: `target-${index}` } - : { - kind: optionKind, - projectGroupId: `target-${index}`, - parentPath: `/target-${index}`, - connectionId: null - }) - } - }) - const onQuickAgentChange = vi.fn() - const section = (selectedProjectId: string, advancedOpen: boolean) => ( - - ['codex'] - } - }, - selection: { ready: true, cli: false }, - contextKey: 'test', - onPick: vi.fn() - }} - onQuickAgentChange={onQuickAgentChange} - onOpenAgentSettings={vi.fn()} - createDisabled={false} - onCreate={vi.fn()} - advancedOpen={advancedOpen} - onToggleAdvanced={vi.fn()} - visibleQuickAgents={AGENT_CATALOG} - defaultTuiAgent="blank" - handleSetDefaultAgent={vi.fn()} - /> - - ) - const { rerender, unmount } = render(section('target', false)) - const initial = optionReads - expect(initial).toBeLessThanOrEqual(selectedRepoIsGit ? size : 0) - expect(initial).toBeGreaterThanOrEqual(selectedRepoIsGit ? 1 : 0) - - expect(screen.getByRole('button', { name: 'Select agent' }).textContent).not.toContain( - 'Chat' - ) - expect(screen.getByRole('button', { name: 'Select agent' }).textContent).not.toContain( - '(CLI)' - ) - fireEvent.pointerDown(screen.getByRole('button', { name: 'Select agent' }), { - button: 0, - ctrlKey: false - }) - const blankTerminal = screen.getByRole('menuitem', { name: 'Blank Terminal' }) - const menu = screen.getByRole('menu') - expect(menu.classList.contains('w-(--radix-dropdown-menu-trigger-width)')).toBe(matched) - expect(menu.classList.contains('min-w-(--radix-dropdown-menu-trigger-width)')).toBe(matched) - expect(menu.classList.contains('min-w-64')).toBe(!matched) - expect(blankTerminal.querySelector('svg')).not.toBeNull() - expect(screen.queryByRole('menuitem', { name: 'Setup only' })).toBeNull() - expect(screen.getByRole('menuitem', { name: 'Agent settings…' })).toBeTruthy() - fireEvent.click(blankTerminal) - - expect(onQuickAgentChange).toHaveBeenCalledWith(null) - - optionReads = 0 - rerender(section('target', true)) - const update = optionReads - expect(update).toBeLessThanOrEqual(selectedRepoIsGit ? size : 0) - expect(update).toBeGreaterThanOrEqual(selectedRepoIsGit ? 1 : 0) - - optionReads = 0 - rerender(section('alternate', true)) - const targetChange = optionReads - expect(targetChange).toBeLessThanOrEqual(selectedRepoIsGit ? 1 : 0) - expect(targetChange).toBeGreaterThanOrEqual(selectedRepoIsGit ? 1 : 0) - fireEvent.pointerDown(screen.getByRole('button', { name: 'Select agent' }), { - button: 0, - ctrlKey: false - }) - const changedMenu = screen.getByRole('menu') - const changedMatched = selectedRepoIsGit && kind === 'project-group' - expect(changedMenu.classList.contains('w-(--radix-dropdown-menu-trigger-width)')).toBe( - changedMatched - ) - expect(changedMenu.classList.contains('min-w-(--radix-dropdown-menu-trigger-width)')).toBe( - changedMatched - ) - expect(changedMenu.classList.contains('min-w-64')).toBe(!changedMatched) - expect(onQuickAgentChange).toHaveBeenCalledExactlyOnceWith(null) - work.push({ size, initial, update, targetChange }) - unmount() - } - expect(work[1].initial).toBeLessThanOrEqual(10 * work[0].initial) - expect(work[1].update).toBeLessThanOrEqual(10 * work[0].update) - expect(work[1].targetChange).toBeLessThanOrEqual(work[0].targetChange) - console.info('project option reads', { kind, selectedRepoIsGit, work }) - } - ) -}) diff --git a/src/renderer/src/components/new-workspace/SetProjectLocationDialog.test.tsx b/src/renderer/src/components/new-workspace/SetProjectLocationDialog.test.tsx index 5d697639cbf..e9b7564e596 100644 --- a/src/renderer/src/components/new-workspace/SetProjectLocationDialog.test.tsx +++ b/src/renderer/src/components/new-workspace/SetProjectLocationDialog.test.tsx @@ -84,31 +84,6 @@ afterEach(() => { }) describe('SetProjectLocationDialog', () => { - it('keeps the parent caller in charge of close and saves an existing folder', async () => { - const onClose = vi.fn() - const onReady = vi.fn() - const user = renderDialog({ onClose, onReady }) - - expect(screen.getByTestId('set-project-location-dialog')).toBeTruthy() - expect(screen.getByText('Set project location')).toBeTruthy() - expect(screen.getByText('Choose where orca lives on openclaw.')).toBeTruthy() - - await user.click(screen.getByRole('button', { name: /Browse folder/ })) - await user.click(screen.getByRole('button', { name: 'Browse host filesystem' })) - await user.click(screen.getByRole('button', { name: 'Select remote folder' })) - await user.click(screen.getByRole('button', { name: 'Set location' })) - - expect(storeMocks.setupProjectExistingFolder).toHaveBeenCalledWith({ - projectId: 'project-orca', - hostId: 'ssh:openclaw', - path: '/remote/orca', - kind: 'git', - displayName: 'orca' - }) - expect(onReady).toHaveBeenCalledWith(JSON.stringify(['setup-openclaw', 'ssh:openclaw'])) - expect(onClose).not.toHaveBeenCalled() - }) - it('backs out of the host browser on Escape instead of discarding the form', async () => { const onClose = vi.fn() const user = renderDialog({ onClose }) diff --git a/src/renderer/src/components/onboarding/onboarding-feature-setup.test.ts b/src/renderer/src/components/onboarding/onboarding-feature-setup.test.ts index fbd0c5ed308..0b11839d45d 100644 --- a/src/renderer/src/components/onboarding/onboarding-feature-setup.test.ts +++ b/src/renderer/src/components/onboarding/onboarding-feature-setup.test.ts @@ -3,30 +3,15 @@ import type { ComputerUsePermissionSetupResult, ComputerUsePermissionStatusResult } from '../../../../shared/computer-use-permissions-types' -import { - COMPUTER_USE_SKILL_NAME, - ORCA_CLI_SKILL_NAME, - ORCA_LINEAR_SKILL_NAME, - ORCHESTRATION_SKILL_NAME -} from '@/lib/agent-feature-skill-names' +import { ORCHESTRATION_SKILL_NAME } from '@/lib/agent-feature-skill-names' import { BROWSER_USE_ENABLED_STORAGE_KEY } from '@/lib/browser-use-setup-state' -import { - ORCHESTRATION_ENABLED_STORAGE_KEY, - ORCHESTRATION_SETUP_DISMISSED_STORAGE_KEY -} from '@/lib/orchestration-setup-state' +import { ORCHESTRATION_ENABLED_STORAGE_KEY } from '@/lib/orchestration-setup-state' import { runOnboardingFeatureSetup, type OnboardingFeatureSetupDeps, type OnboardingFeatureSetupSelection } from './onboarding-feature-setup' -const ALL_SKILL_NAMES = [ - ORCA_CLI_SKILL_NAME, - COMPUTER_USE_SKILL_NAME, - ORCHESTRATION_SKILL_NAME, - ORCA_LINEAR_SKILL_NAME -] - const GRANTED_COMPUTER_USE_STATUS: ComputerUsePermissionStatusResult = { platform: 'darwin', helperAppPath: '/Applications/Orca Computer Use.app', @@ -70,40 +55,6 @@ describe('onboarding feature setup runner', () => { vi.unstubAllGlobals() }) - it('runs selected feature setup through injected deps only', async () => { - const deps = createDeps({ - getComputerUsePermissionStatus: vi.fn( - async (): Promise => ({ - platform: 'darwin', - helperAppPath: '/Applications/Orca Computer Use.app', - helperUnavailableReason: null, - permissions: [ - { id: 'accessibility', status: 'not-granted' }, - { id: 'screenshots', status: 'granted' } - ] - }) - ) - }) - - const result = await runOnboardingFeatureSetup( - { browserUse: true, computerUse: true, orchestration: true, linearTickets: true }, - deps - ) - - expect(result).toEqual({ - selectedIds: ['browserUse', 'computerUse', 'orchestration', 'linearTickets'], - skillNames: ALL_SKILL_NAMES, - computerUsePermissionsOpened: true, - warnings: [] - }) - expect(deps.getComputerUsePermissionStatus).toHaveBeenCalledTimes(1) - expect(deps.openComputerUsePermissionSetup).toHaveBeenCalledTimes(1) - expect(deps.storage.get(BROWSER_USE_ENABLED_STORAGE_KEY)).toBe('1') - expect(deps.storage.get(ORCHESTRATION_ENABLED_STORAGE_KEY)).toBe('1') - expect(deps.removeStorageItem).toHaveBeenCalledWith(ORCHESTRATION_SETUP_DISMISSED_STORAGE_KEY) - expect(deps.notifyOrchestrationStateChanged).toHaveBeenCalledTimes(1) - }) - it('keeps invasive Browser Use and Computer Use setup untouched when only Orchestration is selected', async () => { const deps = createDeps() const selection: OnboardingFeatureSetupSelection = { @@ -124,25 +75,6 @@ describe('onboarding feature setup runner', () => { expect(deps.storage.get(ORCHESTRATION_ENABLED_STORAGE_KEY)).toBe('1') }) - it('clears feature markers when no setup items are selected', async () => { - const deps = createDeps() - - const result = await runOnboardingFeatureSetup( - { browserUse: false, computerUse: false, orchestration: false, linearTickets: false }, - deps - ) - - expect(result).toEqual({ - selectedIds: [], - skillNames: [], - computerUsePermissionsOpened: false, - warnings: [] - }) - expect(deps.storage.get(BROWSER_USE_ENABLED_STORAGE_KEY)).toBe('0') - expect(deps.storage.get(ORCHESTRATION_ENABLED_STORAGE_KEY)).toBe('0') - expect(deps.getComputerUsePermissionStatus).not.toHaveBeenCalled() - }) - it('keeps main’s skill selection but skips permissions when the Computer Use helper is unavailable', async () => { // Missing helpers report every permission ungranted; do not call openSetup, which would throw. const unavailableStatus: ComputerUsePermissionStatusResult = { diff --git a/src/renderer/src/components/right-sidebar/ai-vault/scanning/ai-vault-panel-session-list-request.test.tsx b/src/renderer/src/components/right-sidebar/ai-vault/scanning/ai-vault-panel-session-list-request.test.tsx index 2544da05e50..ce0470b0c3a 100644 --- a/src/renderer/src/components/right-sidebar/ai-vault/scanning/ai-vault-panel-session-list-request.test.tsx +++ b/src/renderer/src/components/right-sidebar/ai-vault/scanning/ai-vault-panel-session-list-request.test.tsx @@ -17,7 +17,7 @@ import { import { aiVaultSearchHitToSession } from '../search/ai-vault-search-session' import type { AiVaultSessionVirtualList } from '../session-list/AiVaultSessionVirtualList' import type { AiVaultResumeInChatEligibility } from '../session-actions/ai-vault-session-resume-in-chat' -import { resolveAiVaultPanelSessionListRequest } from './ai-vault-panel-session-list-request' + import { writeAiVaultViewOptions, readAiVaultViewOptions @@ -120,20 +120,6 @@ afterEach(() => { window.localStorage.clear() }) -it('builds the exact request the Session History panel sends for the active workspace', async () => { - const { default: AiVaultPanel } = await import('../panel/AiVaultPanel') - render() - - const request = resolveAiVaultPanelSessionListRequest(useAppStore.getState(), WORKTREE_ID) - expect(request.scopePaths).toEqual(['/repo/wt']) - expect(request.sessionLimit).toBe(500) - expect(panelRequests.at(-1)).toEqual(request) - fireEvent.click(screen.getByRole('radio', { name: /^All$/ })) - expect(panelRequests.at(-1)?.scopePaths).toEqual(['/repo/wt', '/repo/sibling', '/repos/repo-1']) - fireEvent.click(screen.getByRole('radio', { name: /^Workspace$/ })) - expect(panelRequests.at(-1)).toEqual(request) -}) - it.each([false, true])( 'delivers alias rows to native resume in Workspace and All (bound: %s)', async (bound) => { diff --git a/src/renderer/src/components/right-sidebar/ai-vault/scope/ai-vault-host-scope.test.ts b/src/renderer/src/components/right-sidebar/ai-vault/scope/ai-vault-host-scope.test.ts index 109d24df65b..2457f6226f7 100644 --- a/src/renderer/src/components/right-sidebar/ai-vault/scope/ai-vault-host-scope.test.ts +++ b/src/renderer/src/components/right-sidebar/ai-vault/scope/ai-vault-host-scope.test.ts @@ -5,11 +5,7 @@ import { createRoot, type Root } from 'react-dom/client' import { afterEach, describe, expect, it } from 'vitest' import { TEST_REPO, makeWorktree } from '@/store/slices/worktrees-slice-test-fixtures' import type { AiVaultSessionResumeTargetState } from '../session-actions/ai-vault-session-resume' -import { - buildAiVaultHostScopeOptions, - buildRuntimeAiVaultHostScopeOptions, - useAiVaultExecutionHostScope -} from './ai-vault-host-scope' +import { useAiVaultExecutionHostScope } from './ai-vault-host-scope' import type { ExecutionHostId, ExecutionHostScope } from '../../../../../../shared/execution-host' type HostScopeResult = ReturnType @@ -80,33 +76,6 @@ afterEach(() => { }) describe('useAiVaultExecutionHostScope', () => { - it('defaults SSH worktrees to their SSH execution host', async () => { - await renderHook({ - activeWorktreeId: 'repo-1::/remote/repo', - resumeTargetState: stateForWorktree({ - worktreeId: 'repo-1::/remote/repo', - repoId: 'repo-1', - hostId: 'ssh:dev-box' - }) - }) - - expect(latest?.executionHostScope).toBe('ssh:dev-box') - expect(latest?.activeExecutionHostScope).toBe('ssh:dev-box') - }) - - it('defaults local worktrees to local history', async () => { - await renderHook({ - activeWorktreeId: 'repo-1::/local/repo', - resumeTargetState: stateForWorktree({ - worktreeId: 'repo-1::/local/repo', - repoId: 'repo-1' - }) - }) - - expect(latest?.executionHostScope).toBe('local') - expect(latest?.activeExecutionHostScope).toBeNull() - }) - it('does not claim local history when the active workspace host cannot be resolved (#13713)', async () => { await renderHook({ activeWorktreeId: 'repo-1::/remote/repo', @@ -122,35 +91,6 @@ describe('useAiVaultExecutionHostScope', () => { expect(latest?.executionHostScope).toBe('all') }) - it('keeps local history when no workspace is selected at all', async () => { - await renderHook({ - activeWorktreeId: null, - resumeTargetState: { - folderWorkspaces: [], - projectGroups: [], - repos: [], - worktreesByRepo: {} - } - }) - - expect(latest?.executionHostScope).toBe('local') - }) - - it('defaults runtime worktrees to their runtime execution host', async () => { - await renderHook({ - activeWorktreeId: 'repo-1::/runtime/repo', - resumeTargetState: stateForWorktree({ - worktreeId: 'repo-1::/runtime/repo', - repoId: 'repo-1', - executionHostId: 'runtime:remote-server' - }), - availableExecutionHostScopes: ['runtime:remote-server'] as const - }) - - expect(latest?.executionHostScope).toBe('runtime:remote-server') - expect(latest?.activeExecutionHostScope).toBe('runtime:remote-server') - }) - it('preserves manual host scope changes across unrelated rerenders', async () => { const props = { activeWorktreeId: 'repo-1::/remote/repo', @@ -189,36 +129,3 @@ describe('useAiVaultExecutionHostScope', () => { expect(latest?.executionHostScope).toBe('runtime:remote-server') }) }) - -describe('buildAiVaultHostScopeOptions', () => { - it('adds saved runtime hosts between active SSH and all hosts', () => { - const runtimeHostOptions = buildRuntimeAiVaultHostScopeOptions([ - { id: 'remote-server', name: 'VPS Orca Server' } - ]) - - expect( - buildAiVaultHostScopeOptions({ - activeExecutionHostScope: 'ssh:dev-box', - runtimeHostOptions - }) - ).toEqual([ - { id: 'local', label: expect.any(String) }, - { id: 'ssh:dev-box', label: 'dev-box' }, - { id: 'runtime:remote-server', label: 'VPS Orca Server' }, - { id: 'all', label: 'All hosts' } - ]) - }) - - it('keeps a runtime active host visible even before the saved environment list hydrates', () => { - expect( - buildAiVaultHostScopeOptions({ - activeExecutionHostScope: 'runtime:remote-server', - runtimeHostOptions: [] - }) - ).toEqual([ - { id: 'local', label: expect.any(String) }, - { id: 'runtime:remote-server', label: 'remote-server' }, - { id: 'all', label: 'All hosts' } - ]) - }) -}) diff --git a/src/renderer/src/components/right-sidebar/ai-vault/search/ai-vault-search-session.test.ts b/src/renderer/src/components/right-sidebar/ai-vault/search/ai-vault-search-session.test.ts index 70e0599f50d..67a32cc05ed 100644 --- a/src/renderer/src/components/right-sidebar/ai-vault/search/ai-vault-search-session.test.ts +++ b/src/renderer/src/components/right-sidebar/ai-vault/search/ai-vault-search-session.test.ts @@ -1,6 +1,6 @@ import { afterEach, describe, expect, it, vi } from 'vitest' import { searchHit } from '../../../../../../shared/ai-vault/search/ai-vault-search-test-fixture' -import * as correspondence from '../../../../../../shared/ai-vault/scanning/ai-vault-session-cwd-aliases' + import { resolveAiVaultSessionResumeInChatEligibility } from '../session-actions/ai-vault-session-resume-in-chat' import { aiVaultSearchHitToSession, @@ -12,43 +12,6 @@ import { afterEach(() => vi.restoreAllMocks()) describe('withAiVaultHistoryPreviews', () => { - it('bounds identity indexing and correspondence work when history adds aliases at N and 10N', () => { - const copy = vi.spyOn(correspondence, 'withAiVaultSessionCwdAliases') - for (const size of [100, 1000]) { - let identityReads = 0 - const hits = Array.from({ length: size }, (_, index) => { - const row = aiVaultSearchHitToSession( - { ...searchHit(), sessionId: `session-${index}`, cwd: '/private/var/example' }, - 'local' - ) - return { - ...row, - get sessionId() { - identityReads += 1 - return row.sessionId - } - } - }) - const history = hits.map((hit) => ({ ...hit })) - expect(withAiVaultHistoryPreviews(hits, history).every((row) => !row.cwdAliases)).toBe(true) - const updated = history.map((row) => ({ - ...row, - cwdAliases: ['/var/example'], - get sessionId() { - identityReads += 1 - return row.sessionId - } - })) - identityReads = 0 - copy.mockClear() - const hydrated = withAiVaultHistoryPreviews(hits, updated) - expect(hydrated.every((row) => row.cwdAliases?.[0] === '/var/example')).toBe(true) - expect(copy).toHaveBeenCalledTimes(size) - expect(identityReads).toBeGreaterThanOrEqual(2 * size) - expect(identityReads).toBeLessThanOrEqual(4 * size) - } - }) - it('restores resume correspondence when history arrives after a search hit, even with an existing preview', () => { const hit = aiVaultSearchHitToSession( { ...searchHit(), agent: 'claude', cwd: '/private/var/example' }, @@ -81,35 +44,6 @@ describe('withAiVaultHistoryPreviews', () => { expect(withAiVaultHistoryPreviews([hit], rows)[0].cwdAliases).toBeUndefined() } }) - - it("shows a paired server hit's conversation preview from its scanned history row", () => { - // A relay hit arrives without its transcript path, so its row id differs from the scanned row's. - const hitSession = aiVaultSearchHitToSession( - { ...searchHit(), source: { presence: 'present' as const } }, - 'runtime:env-1' - ) - const turns = [ - { role: 'user' as const, text: 'Fix the flaky test', timestamp: null }, - { role: 'assistant' as const, text: 'Done', timestamp: null } - ] - - const [hydrated] = withAiVaultHistoryPreviews( - [hitSession], - [ - { - ...hitSession, - id: 'runtime:env-1:codex:host-session:/host/transcript.jsonl', - filePath: '/host/transcript.jsonl', - previewMessages: turns, - firstUserPrompt: 'Fix the flaky test' - } - ] - ) - - expect(hydrated.previewMessages).toEqual(turns) - expect(hydrated.firstUserPrompt).toBe('Fix the flaky test') - expect(hydrated.resumeCommand).toBe('host-resume-command') - }) }) describe('aiVaultSearchHitToSession', () => { @@ -131,20 +65,6 @@ describe('aiVaultSearchHitToSession', () => { expect(session.accountWslDistro).toBe('Ubuntu') }) - it('does not invent a path or resume command when transport withholds them', () => { - const hit = { - ...searchHit(), - source: { presence: 'unverifiable' as const }, - resumeCommand: undefined - } - const session = aiVaultSearchHitToSession(hit, 'runtime:cloud') - - expect(session.filePath).toBe('') - expect(session.resumeCommand).toBe('') - expect(canResumeAiVaultSearchHit(hit)).toBe(false) - expect(hasAiVaultSearchHitPath(hit)).toBe(false) - }) - it('opens a native chat hit through its owner when transport withholds the command', () => { const hit = { ...searchHit(), @@ -156,11 +76,4 @@ describe('aiVaultSearchHitToSession', () => { expect(canResumeAiVaultSearchHit(hit)).toBe(true) expect(hasAiVaultSearchHitPath(hit)).toBe(false) }) - - it('gates resume and path actions on their own source fields', () => { - const withoutResume = { ...searchHit(), resumeCommand: undefined } - - expect(canResumeAiVaultSearchHit(withoutResume)).toBe(false) - expect(hasAiVaultSearchHitPath(withoutResume)).toBe(true) - }) }) diff --git a/src/renderer/src/components/right-sidebar/ai-vault/session-actions/ai-vault-session-resume-in-chat-workspace.test.ts b/src/renderer/src/components/right-sidebar/ai-vault/session-actions/ai-vault-session-resume-in-chat-workspace.test.ts index b9d2f90a68d..3210d7d713b 100644 --- a/src/renderer/src/components/right-sidebar/ai-vault/session-actions/ai-vault-session-resume-in-chat-workspace.test.ts +++ b/src/renderer/src/components/right-sidebar/ai-vault/session-actions/ai-vault-session-resume-in-chat-workspace.test.ts @@ -107,12 +107,6 @@ describe('resuming a conversation from history in a chat', () => { expect(resumeInChat()).toEqual({ available: false, reason: 'workspace' }) }) - it('is offered in a workspace on the machine that recorded it', () => { - mocks.owner.mockReturnValue('local') - - expect(resumeInChat()).toEqual({ available: true, workspaceId: PAIRED_WORKSPACE }) - }) - it('asks the host that would run it whether it can resume, not another', () => { mocks.owner.mockReturnValue('local') mocks.localCapabilities.mockReturnValue([]) @@ -128,13 +122,6 @@ describe('paired runtime conversation ownership', () => { mocks.owner.mockReturnValue('runtime:server-1') }) - it('offers a conversation on its recording runtime', () => { - expect(resumeInChat(remoteSession)).toEqual({ - available: true, - workspaceId: PAIRED_WORKSPACE - }) - }) - it.each(['local', 'runtime:server-2', null])( 'refuses moving the remote conversation to %s', (owner) => { diff --git a/src/renderer/src/components/right-sidebar/checks-panel/active-checks-status.test.ts b/src/renderer/src/components/right-sidebar/checks-panel/active-checks-status.test.ts index 33ab3ce22de..a18d4d6114d 100644 --- a/src/renderer/src/components/right-sidebar/checks-panel/active-checks-status.test.ts +++ b/src/renderer/src/components/right-sidebar/checks-panel/active-checks-status.test.ts @@ -1,9 +1,5 @@ import { beforeEach, describe, expect, it } from 'vitest' -import { - ACTIVE_CHECKS_STATUS_INPUT_KEYS, - clearActiveChecksStatusCacheForTests, - getActiveChecksStatus -} from './active-checks-status' +import { clearActiveChecksStatusCacheForTests, getActiveChecksStatus } from './active-checks-status' import type { AppState } from '../../../store/types' import type { PRInfo } from '../../../../../shared/github/pull-request-types' @@ -46,44 +42,6 @@ describe('getActiveChecksStatus', () => { expect(getActiveChecksStatus(state)).toBe('success') }) - it('uses GitLab MR pipeline status when the active branch has no GitHub PR cache entry', () => { - const state = { - activeWorktreeId: 'wt-1', - repos: [{ id: 'repo-1', path: '/repo' }], - worktreesByRepo: { - 'repo-1': [ - { - id: 'wt-1', - repoId: 'repo-1', - branch: 'refs/heads/feature/gitlab', - linkedGitLabMR: 7 - } - ] - }, - prCache: {}, - hostedReviewCache: { - 'local::repo-1::feature/gitlab': { - data: { - provider: 'gitlab', - number: 7, - title: 'GitLab MR', - state: 'open', - url: 'https://gitlab.com/acme/orca/-/merge_requests/7', - status: 'success', - updatedAt: '2026-05-20T00:00:00Z', - mergeable: 'MERGEABLE' - }, - fetchedAt: 2 - } - } - } as unknown as Pick< - AppState, - 'activeWorktreeId' | 'repos' | 'worktreesByRepo' | 'prCache' | 'hostedReviewCache' - > - - expect(getActiveChecksStatus(state)).toBe('success') - }) - it('does not show stale GitHub PR status for a linked GitLab MR while MR status is loading', () => { const state = { activeWorktreeId: 'wt-1', @@ -109,91 +67,4 @@ describe('getActiveChecksStatus', () => { expect(getActiveChecksStatus(state)).toBeNull() }) - - it('hides the matching suppressed GitHub PR status', () => { - const state = { - activeWorktreeId: 'wt-1', - repos: [{ id: 'repo-1', path: '/repo' }], - worktreesByRepo: { - 'repo-1': [ - { - id: 'wt-1', - repoId: 'repo-1', - branch: 'refs/heads/feature/test', - linkedPR: null, - suppressedGitHubPR: 12 - } - ] - }, - prCache: { - 'repo-1::feature/test': { data: makePR('failure'), fetchedAt: 2 } - } - } as unknown as Pick - - expect(getActiveChecksStatus(state)).toBeNull() - }) -}) - -describe('getActiveChecksStatus caching', () => { - beforeEach(() => { - clearActiveChecksStatusCacheForTests() - }) - - function makeState(prCache: Record) { - return { - activeWorktreeId: 'wt-1', - repos: [{ id: 'repo-1', path: '/repo' }], - worktreesByRepo: { - 'repo-1': [{ id: 'wt-1', repoId: 'repo-1', branch: 'refs/heads/feature/test' }] - }, - prCache - } as unknown as Pick - } - - it('reuses the cached status when every input reference is unchanged', () => { - const prCache = { 'repo-1::feature/test': { data: makePR('success'), fetchedAt: 2 } } - const state = makeState(prCache) - - expect(getActiveChecksStatus(state)).toBe('success') - // A different state object carrying the same field references must still hit the cache. - expect(getActiveChecksStatus({ ...state })).toBe('success') - }) - - it('recomputes when a keyed input reference changes', () => { - expect( - getActiveChecksStatus( - makeState({ 'repo-1::feature/test': { data: makePR('success'), fetchedAt: 2 } }) - ) - ).toBe('success') - expect( - getActiveChecksStatus( - makeState({ 'repo-1::feature/test': { data: makePR('failure'), fetchedAt: 3 } }) - ) - ).toBe('failure') - }) - - it('reads no store field the cache is not keyed on', () => { - // Every property read must invalidate the cache, even if a cast bypasses its derived input type. - const reads = new Set() - const keyed = new Set(ACTIVE_CHECKS_STATUS_INPUT_KEYS) - const state = new Proxy( - makeState({ 'repo-1::feature/test': { data: makePR('success'), fetchedAt: 2 } }), - { - get(target, prop, receiver) { - reads.add(prop) - // oxlint-disable-next-line anti-slop/no-reflect-get -- Proxy get trap default forward. - return Reflect.get(target, prop, receiver) - }, - has(target, prop) { - reads.add(prop) - return Reflect.has(target, prop) - } - } - ) - - expect(getActiveChecksStatus(state)).toBe('success') - expect([...reads].filter((prop) => !keyed.has(prop))).toEqual([]) - // Require a nonempty read set so the guard cannot pass vacuously. - expect(reads.has('prCache')).toBe(true) - }) }) diff --git a/src/renderer/src/components/right-sidebar/file-explorer/mutations/file-explorer-operation-generation.test.ts b/src/renderer/src/components/right-sidebar/file-explorer/mutations/file-explorer-operation-generation.test.ts index 21ec757de45..53053a8d42a 100644 --- a/src/renderer/src/components/right-sidebar/file-explorer/mutations/file-explorer-operation-generation.test.ts +++ b/src/renderer/src/components/right-sidebar/file-explorer/mutations/file-explorer-operation-generation.test.ts @@ -2,14 +2,10 @@ import { afterEach, describe, expect, it } from 'vitest' import type { Worktree } from '../../../../../../shared/worktree/types' import { useAppStore } from '@/store/index' import { folderWorkspaceKey } from '../../../../../../shared/workspace-scope' -import { - FLOATING_TERMINAL_WORKTREE_ID, - getDefaultSettings -} from '../../../../../../shared/constants' + import { captureFileExplorerOperationGuard, - getFileExplorerOperationOwner, - getFileExplorerOperationOwnerFromState + getFileExplorerOperationOwner } from '../navigation/file-explorer-operation-owner' const initialState = useAppStore.getInitialState() @@ -31,24 +27,6 @@ afterEach(() => { }) describe('file explorer operation generations', () => { - it('routes floating file ownership and mutations locally while a runtime is focused', () => { - useAppStore.setState({ - settings: { ...getDefaultSettings('/home/me'), activeRuntimeEnvironmentId: 'focused-env' }, - activeWorktreeId: FLOATING_TERMINAL_WORKTREE_ID, - activeWorkspaceExecutionHostId: 'runtime:focused-env' - }) - const owner = getFileExplorerOperationOwner(FLOATING_TERMINAL_WORKTREE_ID) - const guard = captureFileExplorerOperationGuard(FLOATING_TERMINAL_WORKTREE_ID, owner) - - expect(owner).toEqual({ kind: 'local' }) - expect( - getFileExplorerOperationOwnerFromState(useAppStore.getState(), FLOATING_TERMINAL_WORKTREE_ID) - ).toEqual(owner) - expect(guard.route.target).toEqual({ kind: 'local' }) - expect(guard.route.expectedExecutionHostId).toBe('local') - expect(() => guard.assertCurrent()).not.toThrow() - }) - it('invalidates a nested SSH mutation when that target reconnects', () => { useAppStore.setState({ repos: [], diff --git a/src/renderer/src/components/right-sidebar/file-explorer/watching/file-explorer-stale-retry.test.tsx b/src/renderer/src/components/right-sidebar/file-explorer/watching/file-explorer-stale-retry.test.tsx index 440def1a125..927a9978048 100644 --- a/src/renderer/src/components/right-sidebar/file-explorer/watching/file-explorer-stale-retry.test.tsx +++ b/src/renderer/src/components/right-sidebar/file-explorer/watching/file-explorer-stale-retry.test.tsx @@ -70,27 +70,6 @@ it('runs one bounded retry across root and expanded-wave loading transitions', a expect(props.refreshTree).toHaveBeenCalledTimes(1) expect(props.loadDir).not.toHaveBeenCalled() }) -it.each(['fresh-empty', 'directory-error', 'root-error'])('does not loop on %s', async (state) => { - const { props, stale } = params() - if (state === 'fresh-empty') { - stale.clear() - } - if (state === 'directory-error') { - props.dirCache['/repo/link'].error = 'ENOENT' - } - if (state === 'root-error') { - props.rootError = 'ETIMEDOUT' - } - const hook = renderHook(useFileExplorerTreeLoadEffects, { initialProps: props }) - for (let i = 0; i < 3; i++) { - hook.rerender({ ...props, loadingDirPaths: new Set() }) - await act(async () => { - await Promise.resolve() - }) - } - expect(props.refreshTree).not.toHaveBeenCalled() - expect(props.loadDir).not.toHaveBeenCalled() -}) it.each(['root', 'directory'])('stops after a retry records a %s error', async (failure) => { const { props } = params() const pending = pendingRefresh() @@ -136,12 +115,6 @@ it('does not let an old workspace completion release a new workspace retry', asy }) expect(second.props.refreshTree).toHaveBeenCalledTimes(1) }) -it("only the user's own switch to a workspace lets its open read start a stopped WSL distro", () => { - const { props } = params() - noteUserWorkspaceSwitch(props.workspaceKey) - renderHook(useFileExplorerTreeLoadEffects, { initialProps: props }) - expect(props.resetAndLoad).toHaveBeenCalledExactlyOnceWith({ wakeWslDistro: true }) -}) it('an old switch no consumer used is not redeemed by a later mount (E9)', () => { vi.useFakeTimers() try { diff --git a/src/renderer/src/components/right-sidebar/hosted-review/folder-checks/FolderWorkspacePrChecksRow.test.tsx b/src/renderer/src/components/right-sidebar/hosted-review/folder-checks/FolderWorkspacePrChecksRow.test.tsx deleted file mode 100644 index a2ad233b7ad..00000000000 --- a/src/renderer/src/components/right-sidebar/hosted-review/folder-checks/FolderWorkspacePrChecksRow.test.tsx +++ /dev/null @@ -1,233 +0,0 @@ -// @vitest-environment happy-dom - -import { act, type ReactNode } from 'react' -import { createRoot, type Root } from 'react-dom/client' -import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' -import type { CheckStatus } from '../../../../../../shared/github/pull-request-types' -import type { Repo } from '../../../../../../shared/repo-types' -import type { Worktree } from '../../../../../../shared/worktree/types' -import type { ParentPrChecksRow, ParentPrChecksRowStatus } from './parent-pr-checks-row-types' - -vi.mock('@/i18n/i18n', () => ({ - translate: (_key: string, fallback: string, values?: Record) => - values ? fallback.replace('{{value0}}', String(values.value0)) : fallback -})) - -vi.mock('@/lib/app-http-link-open', () => ({ - openAppHttpLink: vi.fn() -})) - -vi.mock('@/components/ui/tooltip', () => ({ - Tooltip: ({ children }: { children: ReactNode }) => <>{children}, - TooltipContent: ({ children }: { children: ReactNode }) => {children}, - TooltipTrigger: ({ children }: { children: ReactNode }) => <>{children} -})) - -vi.mock('../../checks-panel/presentation/check-presentation', () => ({ - CHECK_COLOR: { - success: 'success-color', - failure: 'failure-color', - pending: 'pending-color', - neutral: 'neutral-color' - }, - CHECK_ICON: { - success: (props: { className?: string }) => , - failure: (props: { className?: string }) => , - pending: (props: { className?: string }) => , - neutral: (props: { className?: string }) => - }, - PullRequestIcon: (props: { className?: string }) => , - prStateColor: () => 'state-color' -})) - -vi.mock('../../checks-panel/presentation/checks-list', () => ({ - ChecksList: () =>
-})) - -import { FolderWorkspacePrChecksRow } from './FolderWorkspacePrChecksRow' - -let container: HTMLDivElement -let root: Root - -function makeWorktree(): Worktree { - return { - id: 'repo-1::/child', - path: '/child', - head: 'abc', - branch: 'refs/heads/feature', - isBare: false, - isMainWorktree: false, - repoId: 'repo-1', - displayName: 'Enable child worktrees', - comment: '', - linkedIssue: null, - linkedPR: 12, - linkedLinearIssue: null, - linkedGitLabMR: null, - linkedGitLabIssue: null, - isArchived: false, - isUnread: false, - isPinned: false, - sortOrder: 0, - lastActivityAt: 0 - } -} - -function makeRepo(): Repo { - return { - id: 'repo-1', - path: '/repo', - displayName: 'Repo', - badgeColor: '#fff', - addedAt: 1, - kind: 'git' - } -} - -function makeRow( - overrides: Partial & { - status?: ParentPrChecksRowStatus - checkTone?: CheckStatus - } = {} -): ParentPrChecksRow { - return { - id: 'row-1', - refreshIdentity: 'row-1::feature', - worktree: makeWorktree(), - repo: makeRepo(), - branch: 'feature', - status: 'success', - group: 'passing', - checkTone: 'success', - title: 'Review title', - reviewNumber: 12, - reviewLabel: '#12', - reviewUrl: 'https://example.test/pr/12', - reviewState: 'open', - reviewStatus: 'success', - provider: 'github', - summary: 'Checks passing', - detailNames: [], - checks: [], - isRefreshing: false, - hasLinkedReview: true, - ...overrides - } -} - -function renderRow(row: ParentPrChecksRow): void { - act(() => { - root.render( - null)} - /> - ) - }) -} - -function iconNames(): string[] { - return [...container.querySelectorAll('[data-icon]')].map( - (icon) => icon.dataset.icon ?? '' - ) -} - -describe('FolderWorkspacePrChecksRow', () => { - beforeEach(() => { - container = document.createElement('div') - document.body.appendChild(container) - root = createRoot(container) - }) - - afterEach(() => { - act(() => root.unmount()) - container.remove() - }) - - it.each([ - { - status: 'success' as const, - checkTone: 'success' as const, - icon: 'success', - color: 'success-color', - summary: 'Checks passing' - }, - { - status: 'failing' as const, - checkTone: 'failure' as const, - icon: 'failure', - color: 'failure-color', - summary: 'Checks failing' - } - ])('shows $icon status as summary metadata after the review identity', (state) => { - renderRow( - makeRow({ - status: state.status, - checkTone: state.checkTone, - summary: state.summary - }) - ) - - expect(iconNames()).toEqual(['review', state.icon]) - expect(container.textContent).toContain('Enable child worktrees') - expect(container.textContent).toContain(state.summary) - expect(container.querySelector(`[data-icon="${state.icon}"]`)?.className).toContain(state.color) - expect(container.querySelector(`[data-icon="${state.icon}"]`)?.className).not.toContain( - 'animate-spin' - ) - }) - - it('spins pending summary status like regular pending check rows', () => { - renderRow( - makeRow({ - status: 'pending', - group: 'pending', - checkTone: 'pending', - summary: 'Checks pending' - }) - ) - - expect(iconNames()).toEqual(['review', 'pending']) - expect(container.querySelector('[data-icon="pending"]')?.className).toContain('pending-color') - expect(container.querySelector('[data-icon="pending"]')?.className).toContain('animate-spin') - }) - - it('spins loading summary status because it uses the pending check icon', () => { - renderRow( - makeRow({ - status: 'loading', - group: 'unavailable', - checkTone: 'pending', - reviewLabel: null, - reviewUrl: null, - reviewState: null, - provider: null, - summary: 'Checking review status...' - }) - ) - - expect(iconNames()).toEqual(['review', 'pending']) - expect(container.querySelector('[data-icon="pending"]')?.className).toContain('animate-spin') - }) - - it('does not render a dashed neutral status glyph for rows without a known check state', () => { - renderRow( - makeRow({ - status: 'noReview', - group: 'noPr', - checkTone: 'neutral', - reviewLabel: null, - reviewUrl: null, - reviewState: null, - provider: null, - summary: 'No PR linked' - }) - ) - - expect(iconNames()).toEqual(['review']) - expect(container.querySelector('[data-icon="neutral"]')).toBeNull() - expect(container.textContent).toContain('No PR linked') - }) -}) diff --git a/src/renderer/src/components/right-sidebar/hosted-review/provider-actions/HostedReviewActions.draft.test.tsx b/src/renderer/src/components/right-sidebar/hosted-review/provider-actions/HostedReviewActions.draft.test.tsx deleted file mode 100644 index e135e865ba4..00000000000 --- a/src/renderer/src/components/right-sidebar/hosted-review/provider-actions/HostedReviewActions.draft.test.tsx +++ /dev/null @@ -1,204 +0,0 @@ -import type { ReactNode } from 'react' -import { renderToStaticMarkup } from 'react-dom/server' -import { describe, expect, it, vi, type Mock } from 'vitest' -import type { Repo } from '../../../../../../shared/repo-types' -import type { Worktree } from '../../../../../../shared/worktree/types' -import HostedReviewActions from './HostedReviewActions' -import type { HostedReviewActionInfo } from './use-hosted-review-actions' -import type { PRInfo } from '../../../../../../shared/github/pull-request-types' -import { hostedReviewInfoFromGitHubPRInfo as gitHubPRToChecksPanelReview } from '../../../../../../shared/source-control/hosted-review/hosted-review-github' - -const actionMocks = vi.hoisted( - (): { - actionError: string | null - handleMarkReadyForReview: Mock - handleCloseReview: Mock - } => ({ - actionError: null, - handleMarkReadyForReview: vi.fn(), - handleCloseReview: vi.fn() - }) -) - -vi.mock('@/store/index', () => ({ useAppStore: () => false })) -vi.mock('./use-hosted-review-actions', () => ({ - useHostedReviewActions: () => ({ - merging: false, - readying: false, - stateUpdating: null, - actionError: actionMocks.actionError, - handleMerge: vi.fn(), - handleAutoMerge: vi.fn(), - handleMarkReadyForReview: actionMocks.handleMarkReadyForReview, - handleCloseReview: actionMocks.handleCloseReview, - handleReopenReview: vi.fn() - }) -})) -vi.mock('@/components/ui/dropdown-menu', () => ({ - DropdownMenu: ({ children }: { children: ReactNode }) => <>{children}, - DropdownMenuTrigger: ({ children }: { children: ReactNode }) => <>{children}, - DropdownMenuContent: ({ children }: { children: ReactNode }) =>
{children}
, - DropdownMenuItem: ({ children }: { children: ReactNode }) =>
{children}
, - DropdownMenuSeparator: () =>
-})) - -const repo = { id: 'repo-1', path: '/repo' } as Repo -const worktree = { id: 'worktree-1' } as Worktree - -function renderDraft(provider: HostedReviewActionInfo['provider']): string { - return renderToStaticMarkup( - - ) -} - -describe('HostedReviewActions draft state', () => { - it.each([ - ['github', 'PR'], - ['gitlab', 'MR'] - ] as const)('renders Ready as primary and Close as secondary for %s', (provider, shortLabel) => { - const markup = renderDraft(provider) - - expect(markup).toContain('Mark ready for review') - expect(markup).toContain(`Close ${shortLabel}`) - expect(markup).not.toContain('Merge') - expect(markup).not.toContain('auto-merge') - }) - - it.each(['azure-devops', 'gitea'] as const)( - 'does not misroute unsupported %s drafts through GitHub', - (provider) => { - expect(renderDraft(provider)).toBe('') - } - ) -}) - -describe('HostedReviewActions GitHub merge selection', () => { - it.each([ - { - mergeable: 'MERGEABLE', - mergeStateStatus: 'UNSTABLE', - status: 'pending', - label: 'Squash and merge', - autoMerge: false - }, - { - mergeable: 'MERGEABLE', - mergeStateStatus: 'UNSTABLE', - status: 'failure', - label: 'Squash and merge', - autoMerge: false - }, - { - mergeable: 'UNKNOWN', - mergeStateStatus: 'UNKNOWN', - status: 'pending', - label: 'Checking', - autoMerge: false - }, - { mergeable: 'UNKNOWN', status: 'pending', label: 'Checking', autoMerge: false }, - { - mergeable: 'MERGEABLE', - mergeStateStatus: 'BLOCKED', - status: 'pending', - label: 'Enable auto-merge', - autoMerge: true - }, - { - mergeable: 'MERGEABLE', - mergeStateStatus: 'BLOCKED', - reviewDecision: 'REVIEW_REQUIRED', - status: 'success', - label: 'Enable auto-merge', - autoMerge: true - }, - { - mergeable: 'MERGEABLE', - mergeStateStatus: 'BLOCKED', - autoMergeEnabled: true, - status: 'pending', - label: 'Disable auto-merge', - autoMerge: true - }, - { - mergeable: 'MERGEABLE', - mergeStateStatus: 'UNSTABLE', - mergeQueueRequired: true, - status: 'pending', - label: 'Merge when ready', - autoMerge: false - }, - { - mergeable: 'CONFLICTING', - mergeStateStatus: 'DIRTY', - mergeQueueRequired: true, - status: 'pending', - label: 'Conflicts', - autoMerge: false - } - ] as const)('renders $label for $mergeStateStatus / $status', (scenario) => { - const pr: PRInfo = { - number: 31259, - title: 'fix(i18n): restore localized editor safety copy', - state: 'open', - url: 'https://github.com/stablyai/orca/pull/31259', - updatedAt: '2026-10-21T16:31:00Z', - headSha: 'e4353ead012043474fc723e50aea198a76d32979', - autoMergeAllowed: true, - checksStatus: scenario.status, - ...scenario - } - const markup = renderToStaticMarkup( - - ) - expect(markup).toContain(scenario.label) - expect(markup.includes('Enable auto-merge') || markup.includes('Disable auto-merge')).toBe( - scenario.autoMerge - ) - if (scenario.label === 'Checking' || scenario.label === 'Conflicts') { - expect(markup).toMatch(/]*disabled=""/) - } - }) -}) - -describe('HostedReviewActions merged state', () => { - it('keeps a failed merge error visible when the reread shows the review merged', () => { - actionMocks.actionError = 'Connection lost' - try { - const markup = renderToStaticMarkup( - - ) - expect(markup).toContain('Delete Workspace') - expect(markup).toContain('Connection lost') - } finally { - actionMocks.actionError = null - } - }) -}) diff --git a/src/renderer/src/components/right-sidebar/source-control/review/source-control-dropdown-items.create-pr-intent.test.ts b/src/renderer/src/components/right-sidebar/source-control/review/source-control-dropdown-items.create-pr-intent.test.ts deleted file mode 100644 index a8f68b03387..00000000000 --- a/src/renderer/src/components/right-sidebar/source-control/review/source-control-dropdown-items.create-pr-intent.test.ts +++ /dev/null @@ -1,188 +0,0 @@ -import { describe, expect, it } from 'vitest' -import { resolveDropdownItems } from '../panel/source-control-dropdown-items' -import type { - DropdownActionInputs, - DropdownItem -} from '../../../../../../shared/source-control/dropdown/source-control-dropdown-item-types' - -function inputs(overrides: Partial = {}): DropdownActionInputs { - return { - stagedCount: 0, - hasUnstagedChanges: false, - hasStageableChanges: false, - hasPartiallyStagedChanges: false, - hasMessage: false, - hasUnresolvedConflicts: false, - isCommitting: false, - isRemoteOperationActive: false, - upstreamStatus: undefined, - ...overrides - } -} - -describe('resolveDropdownItems Create PR intent', () => { - it('enables the push-before-PR recovery action when review creation is only blocked by unpushed commits', () => { - const items = resolveDropdownItems( - inputs({ - upstreamStatus: { hasUpstream: true, ahead: 2, behind: 0 }, - hostedReviewCreation: { - provider: 'github', - review: null, - canCreate: false, - blockedReason: 'needs_push', - nextAction: 'push', - reviewLookupOutcome: 'not_found' - } - }) - ) - const byKind = Object.fromEntries( - items.filter((e) => e.kind !== 'separator').map((e) => [e.kind, e]) - ) - expect(byKind.create_pr.disabled).toBe(false) - expect(byKind.create_pr.hint).toBe('Push first') - expect(byKind.push_create_pr.label).toBe('Push before PR') - expect(byKind.push_create_pr.disabled).toBe(false) - }) - - it.each([ - { - name: 'push', - provider: 'github' as const, - upstreamStatus: { hasUpstream: true as const, ahead: 2, behind: 0 }, - blockedReason: 'needs_push' as const, - expectedTitle: 'Push local commits before creating a pull request' - }, - { - name: 'force push', - provider: 'gitlab' as const, - upstreamStatus: { - hasUpstream: true as const, - upstreamName: 'origin/feature', - ahead: 2, - behind: 1, - behindCommitsArePatchEquivalent: true - }, - blockedReason: 'needs_sync' as const, - expectedTitle: 'Force push with lease before creating a merge request' - } - ])( - 'keeps $name-before-review recovery available when review lookup is unavailable', - ({ provider, upstreamStatus, blockedReason, expectedTitle }) => { - const items = resolveDropdownItems( - inputs({ - branchCommitsAhead: 2, - upstreamStatus, - hostedReviewCreation: { - provider, - review: null, - canCreate: false, - blockedReason, - nextAction: blockedReason === 'needs_push' ? 'push' : 'sync', - reviewLookupOutcome: 'unavailable' - } - }) - ) - const pushCreate = items.find((item): item is DropdownItem => item.kind === 'push_create_pr') - - expect(pushCreate?.disabled).toBe(false) - expect(pushCreate?.title).toBe(expectedTitle) - expect(pushCreate?.hint).toBeUndefined() - } - ) - - it('uses GitLab MR copy for create and push-before-create rows', () => { - const items = resolveDropdownItems( - inputs({ - upstreamStatus: { hasUpstream: true, ahead: 2, behind: 0 }, - hostedReviewCreation: { - provider: 'gitlab', - review: null, - canCreate: false, - blockedReason: 'needs_push', - nextAction: 'push', - reviewLookupOutcome: 'not_found' - } - }) - ) - const byKind = Object.fromEntries( - items.filter((e) => e.kind !== 'separator').map((e) => [e.kind, e]) - ) - expect(byKind.create_pr.label).toBe('Create MR') - expect(byKind.create_pr.hint).toBe('Push first') - expect(byKind.create_pr.disabled).toBe(false) - expect(byKind.push_create_pr.label).toBe('Push before MR') - expect(byKind.push_create_pr.title).toBe('Push local commits before creating a merge request') - expect(byKind.push_create_pr.disabled).toBe(false) - }) - - it.each(['azure-devops', 'gitea'] as const)( - 'enables push-before-PR recovery for %s review creation', - (provider) => { - const items = resolveDropdownItems( - inputs({ - upstreamStatus: { hasUpstream: true, ahead: 2, behind: 0 }, - hostedReviewCreation: { - provider, - review: null, - canCreate: false, - blockedReason: 'needs_push', - nextAction: 'push', - reviewLookupOutcome: 'not_found' - } - }) - ) - const byKind = Object.fromEntries( - items.filter((e) => e.kind !== 'separator').map((e) => [e.kind, e]) - ) - expect(byKind.create_pr.label).toBe('Create PR') - expect(byKind.create_pr.hint).toBe('Push first') - expect(byKind.create_pr.disabled).toBe(false) - expect(byKind.push_create_pr.label).toBe('Push before PR') - expect(byKind.push_create_pr.title).toBe('Push local commits before creating a pull request') - expect(byKind.push_create_pr.disabled).toBe(false) - } - ) - - it.each([ - ['azure-devops', 'Set ORCA_AZURE_DEVOPS_TOKEN in this environment'], - ['gitea', 'Set ORCA_GITEA_TOKEN in this environment'] - ] as const)('uses token auth copy when %s PR creation needs authentication', (provider, hint) => { - const items = resolveDropdownItems( - inputs({ - upstreamStatus: { hasUpstream: true, ahead: 0, behind: 0 }, - hostedReviewCreation: { - provider, - review: null, - canCreate: false, - blockedReason: 'auth_required', - nextAction: 'authenticate', - reviewLookupOutcome: 'not_found' - } - }) - ) - const byKind = Object.fromEntries( - items.filter((e) => e.kind !== 'separator').map((e) => [e.kind, e]) - ) - expect(byKind.create_pr.hint).toBe(hint) - }) - - it('uses GitLab auth copy when MR creation needs authentication', () => { - const items = resolveDropdownItems( - inputs({ - upstreamStatus: { hasUpstream: true, ahead: 0, behind: 0 }, - hostedReviewCreation: { - provider: 'gitlab', - review: null, - canCreate: false, - blockedReason: 'auth_required', - nextAction: 'authenticate', - reviewLookupOutcome: 'not_found' - } - }) - ) - const byKind = Object.fromEntries( - items.filter((e) => e.kind !== 'separator').map((e) => [e.kind, e]) - ) - expect(byKind.create_pr.hint).toBe('Run glab auth login in this environment') - }) -}) diff --git a/src/renderer/src/components/settings/chat/ChatSettingsSection.navigation.test.tsx b/src/renderer/src/components/settings/chat/ChatSettingsSection.navigation.test.tsx index 35a47c2dc43..33c97355d59 100644 --- a/src/renderer/src/components/settings/chat/ChatSettingsSection.navigation.test.tsx +++ b/src/renderer/src/components/settings/chat/ChatSettingsSection.navigation.test.tsx @@ -130,23 +130,4 @@ describe('Chat settings deep links', () => { ) await waitFor(() => expect(scrollIntoView.mock.contexts).toContain(visuals)) }) - - it('activates Chat and scrolls to the separate naming section for its deep link', async () => { - const scrollIntoView = vi.spyOn(HTMLElement.prototype, 'scrollIntoView') - const { container } = render( - - ) - await waitFor(() => { - expect(screen.getByRole('status', { name: 'Selected settings page' }).textContent).toBe( - 'chat' - ) - }) - const names = container.querySelector('#chat-names') - const appearance = container.querySelector('#chat-appearance') - expect(names?.parentElement).toBe(appearance?.parentElement) - expect(names?.querySelector('[role="switch"]')).toBe( - screen.getByRole('switch', { name: 'Name chats automatically' }) - ) - await waitFor(() => expect(scrollIntoView.mock.contexts).toContain(names)) - }) }) diff --git a/src/renderer/src/components/settings/virtual-machines/recipes/EphemeralVmsPane.test.tsx b/src/renderer/src/components/settings/virtual-machines/recipes/EphemeralVmsPane.test.tsx index f380e4ff083..12d5c61ddb1 100644 --- a/src/renderer/src/components/settings/virtual-machines/recipes/EphemeralVmsPane.test.tsx +++ b/src/renderer/src/components/settings/virtual-machines/recipes/EphemeralVmsPane.test.tsx @@ -123,53 +123,6 @@ describe('EphemeralVmsPane', () => { document.body.replaceChildren() }) - it('renders the skill panel and recipe, and opens the composer with the recipe selected', async () => { - const container = await renderPane() - - await vi.waitFor(() => expect(container.textContent).toContain('Cloud Sandbox')) - await vi.waitFor(() => expect(container.textContent).toContain('Cloud VM setup skill')) - expect(container.textContent).toContain('What the skill does, with you') - const useButton = [...container.querySelectorAll('button')].find( - (button) => button.textContent === 'Use in workspace' - ) - expect(useButton).toBeDefined() - - await act(async () => { - useButton?.dispatchEvent(new MouseEvent('click', { bubbles: true })) - }) - - expect(storeMocks.openModal).toHaveBeenCalledWith('new-workspace-composer', { - initialRepoId: 'repo-1', - initialEphemeralVmRecipeId: 'cloud-sandbox', - telemetrySource: 'settings' - }) - }) - - it('does not schedule a reset when clipboard completion arrives after unmount', async () => { - let finishClipboard!: () => void - vi.mocked(window.api.ui.writeClipboardText).mockReturnValueOnce( - new Promise((resolve) => { - finishClipboard = resolve - }) - ) - const container = await renderPane() - const setTimeout = vi.spyOn(window, 'setTimeout') - try { - await act(async () => { - container.querySelector('button[aria-label="Copy"]')?.click() - }) - await act(async () => roots.pop()?.unmount()) - setTimeout.mockClear() - await act(async () => { - finishClipboard() - await Promise.resolve() - }) - expect(setTimeout.mock.calls.filter(([, delay]) => delay === 1500)).toHaveLength(0) - } finally { - setTimeout.mockRestore() - } - }) - it('ignores an older refresh that finishes after a plugin-content refresh', async () => { const listRecipeCatalog = window.api.ephemeralVm.listRecipeCatalog as ReturnType let resolveInitial!: (catalog: unknown[]) => void 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 index 56afe79b5c1..e85a10ded0c 100644 --- 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 @@ -48,53 +48,6 @@ function makeState(overrides: Partial): AddRepoSki } describe('finalizeImportedRepoAfterSkip', () => { - it('keeps skipped imported worktrees visible without activating a worktree', () => { - const state = makeState({ - activeRepoId: 'repo-old', - filterRepoIds: ['repo-old'], - showActiveOnly: true, - hideDefaultBranchWorkspace: false, - worktreesByRepo: { - 'repo-new': [makeWorktree({ id: 'repo-new::/repo/feature', repoId: 'repo-new' })] - } - }) - - finalizeImportedRepoAfterSkip(state, 'repo-new') - - expect(state.setActiveRepo).toHaveBeenCalledWith('repo-new') - expect(state.setFilterRepoIds).toHaveBeenCalledWith(['repo-old', 'repo-new']) - expect(state.setShowActiveOnly).toHaveBeenCalledWith(false) - expect(state.setHideDefaultBranchWorkspace).not.toHaveBeenCalled() - }) - - it('leaves the project filter off when the import lands with no filter', () => { - const state = makeState({ filterRepoIds: [] }) - - finalizeImportedRepoAfterSkip(state, 'repo-new') - - expect(state.setFilterRepoIds).not.toHaveBeenCalled() - }) - - it('clears default-branch hiding when it would hide every imported worktree', () => { - const state = makeState({ - hideDefaultBranchWorkspace: true, - worktreesByRepo: { - 'repo-new': [ - makeWorktree({ - id: 'repo-new::/repo/main', - repoId: 'repo-new', - isMainWorktree: true, - branch: 'refs/heads/main' - }) - ] - } - }) - - finalizeImportedRepoAfterSkip(state, 'repo-new') - - expect(state.setHideDefaultBranchWorkspace).toHaveBeenCalledWith(false) - }) - it('preserves default-branch hiding when importing a folder project with only its root', () => { const state = makeState({ hideDefaultBranchWorkspace: true, @@ -178,42 +131,4 @@ describe('finalizeImportedRepoAfterSkip', () => { expect(state.setAlwaysShowDefaultBranchWorkspace).toHaveBeenCalledWith(true) }) - - it('leaves the default-branch exemption alone when sleeping workspaces are shown', () => { - const state = makeState({ - showSleepingWorkspaces: true, - 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).not.toHaveBeenCalled() - }) - - it('still reveals the imported repo when it has no discovered worktrees yet', () => { - const state = makeState({ - activeRepoId: 'repo-old', - filterRepoIds: ['repo-old'], - showActiveOnly: true, - hideDefaultBranchWorkspace: true, - worktreesByRepo: { 'repo-new': [] } - }) - - finalizeImportedRepoAfterSkip(state, 'repo-new') - - expect(state.setActiveRepo).toHaveBeenCalledWith('repo-new') - expect(state.setFilterRepoIds).toHaveBeenCalledWith(['repo-old', 'repo-new']) - expect(state.setShowActiveOnly).toHaveBeenCalledWith(false) - expect(state.setHideDefaultBranchWorkspace).not.toHaveBeenCalled() - }) }) 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 9d586fa4f22..2fc6b6f777d 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 @@ -69,19 +69,6 @@ async function review(): Promise { } describe('nested deletion confirmation', () => { - it('lists hidden Git registrations and warns about lost changes before any deletion', async () => { - render( - - ) - await review() - expect(mocks.preview).toHaveBeenCalledWith({ worktreeId: target.id, hostId: 'local' }) - expect(screen.getByRole('dialog')).toHaveTextContent('including uncommitted changes') - expect(screen.getAllByRole('listitem')).toHaveLength(2) - expect(mocks.remove).not.toHaveBeenCalled() - fireEvent.click(screen.getByRole('button', { name: 'Cancel' })) - expect(mocks.remove).not.toHaveBeenCalled() - }) - it('sends only the reviewed set to the confirmed host and refreshes after success', async () => { const dismissToast = vi.fn() const onDeleted = vi.fn() diff --git a/src/renderer/src/components/sidebar/workspace-actions/deletion/delete-worktree-always-force-preference.test.tsx b/src/renderer/src/components/sidebar/workspace-actions/deletion/delete-worktree-always-force-preference.test.tsx index 54a62aadf5c..a58316e0eba 100644 --- a/src/renderer/src/components/sidebar/workspace-actions/deletion/delete-worktree-always-force-preference.test.tsx +++ b/src/renderer/src/components/sidebar/workspace-actions/deletion/delete-worktree-always-force-preference.test.tsx @@ -81,18 +81,6 @@ afterEach(() => { vi.clearAllMocks() }) -it('keeps the main Force Delete button a one-time action', async () => { - const savePreference = vi.fn().mockResolvedValue(undefined) - const { container, onForceDelete } = renderRecovery(savePreference) - expect(container.querySelector('button[aria-label="More force delete options"]')).not.toBeNull() - expect(document.querySelector('[role="menuitem"]')).toBeNull() - - await act(async () => click(container, 'button[data-variant="destructive"]')) - - expect(savePreference).not.toHaveBeenCalled() - expect(onForceDelete).toHaveBeenCalledOnce() -}) - it('keeps portaled menu focus from triggering toast focus restoration', async () => { const { container, onOutsideBlur } = renderRecovery(vi.fn()) const trigger = container.querySelector('button[aria-label="More force delete options"]') diff --git a/src/renderer/src/components/sidebar/workspace-actions/deletion/delete-worktree-dirty-change-hydration.test.tsx b/src/renderer/src/components/sidebar/workspace-actions/deletion/delete-worktree-dirty-change-hydration.test.tsx index 85d5d88035b..4c2513aabd0 100644 --- a/src/renderer/src/components/sidebar/workspace-actions/deletion/delete-worktree-dirty-change-hydration.test.tsx +++ b/src/renderer/src/components/sidebar/workspace-actions/deletion/delete-worktree-dirty-change-hydration.test.tsx @@ -100,28 +100,6 @@ afterEach(cleanup) beforeEach(() => vi.clearAllMocks()) describe('loaded deletion disclosure and existing hydration', () => { - it('shows pending and failed detail checks alongside a known dirty warning', async () => { - let failRead: (error: Error) => void = () => { - throw new Error('Status read has not started') - } - vi.mocked(getRuntimeGitStatus).mockImplementationOnce( - () => - new Promise((_resolve, reject) => { - failRead = reject - }) - ) - render() - expect(screen.getByText('Uncommitted or untracked changes')).toBeVisible() - expect(screen.getByText('· Checking…')).toBeVisible() - await act(async () => { - failRead(new Error('Details unavailable')) - }) - expect(screen.getByText('Uncommitted or untracked changes')).toBeVisible() - expect(screen.getByText('· Details unavailable')).toBeVisible() - expect(screen.queryByRole('button')).not.toBeInTheDocument() - expect(screen.queryByText(/No uncommitted|0 changes/)).not.toBeInTheDocument() - }) - it('expands and collapses a hydrated snapshot without requesting status again', async () => { vi.mocked(getRuntimeGitStatus).mockResolvedValue({ entries: [ diff --git a/src/renderer/src/components/sidebar/workspace-card/surface/WorktreeCardStatusSlot.gitlab-checks.test.tsx b/src/renderer/src/components/sidebar/workspace-card/surface/WorktreeCardStatusSlot.gitlab-checks.test.tsx deleted file mode 100644 index 0e30a3dbef7..00000000000 --- a/src/renderer/src/components/sidebar/workspace-card/surface/WorktreeCardStatusSlot.gitlab-checks.test.tsx +++ /dev/null @@ -1,156 +0,0 @@ -import { renderToStaticMarkup } from 'react-dom/server' -import type { ReactNode } from 'react' -import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' -import { i18n } from '@/i18n/i18n' -import { WorktreeCardStatusSlot } from './WorktreeCardStatusSlot' -import { getWorktreeCardPrDisplay } from '../review/worktree-card-pr-display' -import { gitlabMrCheckCases } from '../../../../../../shared/source-control/checks/gitlab/gitlab-mr-checks.test-fixture' -import { buildParentPrChecksProjection } from '../../../right-sidebar/hosted-review/folder-checks/parent-pr-checks-rows' -import { FolderWorkspacePrChecksRow } from '../../../right-sidebar/hosted-review/folder-checks/FolderWorkspacePrChecksRow' -import { presentGitLabMRMergeState } from '../../../right-sidebar/hosted-review/provider-actions/gitlab-mr-merge-state' -import { getActiveChecksStatus } from '../../../right-sidebar/checks-panel/active-checks-status' -import { - ReviewChecksBadge, - ReviewChecksIndicator -} from '../metadata/WorktreeCardMetadataStatusBadges' -import { WorktreeReferenceStack } from '../review/WorktreeReferenceStack' -import type { WorkspaceReferenceDetailsMap } from '../review/workspace-reference-details' -import { getFolderWorkspaceCardPrDisplay } from '../review/folder-workspace-card-pr-display' -import { formatHostedReviewLabel } from '../../../status-bar/hosted-review-label' -import { reviewRefreshIntervalMs } from '../../../../../../shared/review-refresh-policy' -import { summarizeWorkspaceReviewChecks } from '../../../../../../shared/workspace-review-checks' -import { getWorkspaceAttachmentKey } from '../../../../../../shared/workspace-attachments/workspace-attachment-normalization' -import { - referenceRepo, - referenceWorkspace -} from '../review/workspace-reference-fixtures.test-support' - -const mocks = vi.hoisted(() => ({ - status: 'active', - sleeping: false -})) - -vi.mock('@/components/ui/tooltip', () => ({ - Tooltip: ({ children }: { children: ReactNode }) => {children}, - TooltipContent: ({ children }: { children: ReactNode }) => ( - {children} - ), - TooltipTrigger: ({ children }: { children: ReactNode }) => <>{children} -})) - -vi.mock('../activity/use-worktree-activity-status', () => ({ - useWorktreeActivityStatus: () => mocks.status -})) - -vi.mock('../../workspace-actions/sleep/use-worktree-sleep-state', () => ({ - useIsSleepingWorktree: () => mocks.sleeping -})) - -describe('WorktreeCardStatusSlot', () => { - afterEach(async () => { - await i18n.changeLanguage('en') - }) - beforeEach(() => { - mocks.status = 'active' - mocks.sleeping = false - }) - - it.each(gitlabMrCheckCases)( - 'presents the normalized $name without claiming passing', - ({ review: mapped, cardLabel, tone, rowSummary, mergeLabel, directMergeAvailable }) => { - const { status } = mapped - const worktree = { ...referenceWorkspace, linkedPR: null, linkedGitLabMR: 7 } - const hostedReviewCache = { - 'local::repo::feature': { data: mapped, fetchedAt: 1, linkedReviewHintKey: 'gitlab:7' } - } - const context = { - worktrees: [worktree], - repos: [referenceRepo], - settings: null, - hostedReviewCache, - prCache: {}, - checksCache: {} - } - const projection = buildParentPrChecksProjection(context) - const card = renderToStaticMarkup( - - ) - expect(card).toContain(cardLabel) - expect(card).toContain(tone) - expect(card).not.toContain('Passing') - const row = renderToStaticMarkup( - null} - /> - ) - expect(row).toContain(rowSummary) - expect(row).not.toMatch(/Checks passing|lucide-circle-check/) - expect(projection.summary.passing).toBe(0) - expect(presentGitLabMRMergeState(mapped)).toMatchObject({ - label: mergeLabel, - directMergeAvailable - }) - expect( - getActiveChecksStatus({ - ...context, - activeWorktreeId: worktree.id, - worktreesByRepo: { repo: [worktree] } - }) - ).toBe(status) - const item = { provider: 'gitlab', type: 'mr', number: 7 } as const - const details = { - [getWorkspaceAttachmentKey(item)]: { title: mapped.title, url: mapped.url, review: mapped } - } satisfies WorkspaceReferenceDetailsMap - expect(summarizeWorkspaceReviewChecks([item], details)).toMatchObject({ - state: status, - passed: 0 - }) - expect( - renderToStaticMarkup() - ).toContain(tone) - const checkLabel = { failure: 'Failing', pending: 'Pending', neutral: '' }[status] - for (const Badge of [ReviewChecksBadge, ReviewChecksIndicator]) { - const badge = renderToStaticMarkup() - expect(badge).toEqual(checkLabel ? expect.stringContaining(`Checks: ${checkLabel}`) : '') - } - expect( - getFolderWorkspaceCardPrDisplay({ - ...context, - folderWorkspaceId: 'folder', - worktreeMap: new Map([[worktree.id, worktree]]), - repoMap: new Map([['repo', referenceRepo]]), - worktreeLineageById: {}, - workspaceLineageByChildKey: { - child: { - childWorkspaceKey: `worktree:${worktree.id}`, - parentWorkspaceKey: 'folder:folder', - origin: 'cli', - capture: { source: 'env-workspace', confidence: 'inferred' }, - createdAt: 1 - } - } - }) - ).toMatchObject({ state: 'open', status }) - expect(formatHostedReviewLabel(mapped)).toBe(`MR !7 Open, ${status}`) - expect(reviewRefreshIntervalMs({ state: mapped.state, checksStatus: mapped.status })).toBe( - 120_000 - ) - expect(reviewRefreshIntervalMs({ state: 'merged', checksStatus: mapped.status })).toBe( - status === 'pending' ? 60_000 : null - ) - } - ) -}) diff --git a/src/renderer/src/components/sidebar/workspace-card/surface/worktree-card-option-items.test.tsx b/src/renderer/src/components/sidebar/workspace-card/surface/worktree-card-option-items.test.tsx deleted file mode 100644 index 91612243765..00000000000 --- a/src/renderer/src/components/sidebar/workspace-card/surface/worktree-card-option-items.test.tsx +++ /dev/null @@ -1,224 +0,0 @@ -// @vitest-environment happy-dom -import { act, type ReactNode } from 'react' -import { createRoot, type Root } from 'react-dom/client' -import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' -import { - WorktreeCardDisplayMenuItem, - WorktreeCardLayoutMenuItem, - WorktreeCardPropertiesMenuItem -} from './worktree-card-option-items' - -;(globalThis as { IS_REACT_ACT_ENVIRONMENT?: boolean }).IS_REACT_ACT_ENVIRONMENT = true - -const setWorktreeCardMode = vi.fn() -const setWorktreeCardProperties = vi.fn() -const setAgentActivityDisplayMode = vi.fn() -const setShowAgentModels = vi.fn() -let showAgentModels: boolean | undefined - -let settings = { compactWorktreeCards: false, experimentalNewWorktreeCardStyle: false } -let projectGroups: unknown[] = [] -let worktreeCardProperties = [ - 'status', - 'unread', - 'issue', - 'linear-issue', - 'jira-issue', - 'pr', - 'automation', - 'comment', - 'ports', - 'inline-agents' -] - -vi.mock('@/store', () => ({ - useAppStore: (selector: (state: unknown) => unknown) => - selector({ - agentActivityDisplayMode: 'compact', - persistedUIReady: true, - showAgentModels, - setShowAgentModels, - projectGroups, - setAgentActivityDisplayMode, - setWorktreeCardMode, - setWorktreeCardProperties, - settings, - worktreeCardProperties - }) -})) - -vi.mock('@/components/ui/dropdown-menu', async () => { - const ReactModule = await import('react') - type RadioItemProps = { - children: ReactNode - onSelect?: (event: { preventDefault: () => void }) => void - onValueChange?: (value: string) => void - value: string - } - return { - DropdownMenuCheckboxItem: ({ - children, - checked, - onCheckedChange - }: { - children: ReactNode - checked?: boolean - onCheckedChange?: (checked: boolean) => void - }) => ( - - ), - DropdownMenuLabel: ({ children }: { children: ReactNode }) =>
{children}
, - DropdownMenuRadioGroup: ({ - children, - onValueChange, - value - }: { - children: ReactNode - onValueChange?: (value: string) => void - value?: string - }) => ( -
- {ReactModule.Children.map(children, (child) => - ReactModule.isValidElement(child) - ? ReactModule.cloneElement(child, { onValueChange }) - : child - )} -
- ), - DropdownMenuRadioItem: ({ children, onSelect, onValueChange, value }: RadioItemProps) => ( - - ), - DropdownMenuSeparator: () =>
, - DropdownMenuSub: ({ children }: { children: ReactNode }) => <>{children}, - DropdownMenuSubContent: ({ children }: { children: ReactNode }) =>
{children}
, - DropdownMenuSubTrigger: ({ children }: { children: ReactNode }) =>
{children}
- } -}) - -let root: Root | null = null -let container: HTMLDivElement | null = null - -function renderMenu(): void { - container = document.createElement('div') - document.body.appendChild(container) - root = createRoot(container) - act(() => { - root?.render( - settings.experimentalNewWorktreeCardStyle ? ( - - ) : ( - <> - - - - ) - ) - }) -} - -beforeEach(() => { - settings = { compactWorktreeCards: false, experimentalNewWorktreeCardStyle: false } - projectGroups = [] - worktreeCardProperties = [ - 'status', - 'unread', - 'issue', - 'linear-issue', - 'jira-issue', - 'pr', - 'automation', - 'comment', - 'ports', - 'inline-agents' - ] - showAgentModels = undefined - setShowAgentModels.mockReset() - setAgentActivityDisplayMode.mockReset() - setWorktreeCardMode.mockReset() - setWorktreeCardProperties.mockReset() -}) - -afterEach(() => { - act(() => { - root?.unmount() - }) - root = null - container?.remove() - container = null - document.body.innerHTML = '' -}) - -describe('worktree card menu items', () => { - it.each([false, true])( - 'toggles model badges in the existing property menu (new style: %s)', - (newStyle) => { - settings.experimentalNewWorktreeCardStyle = newStyle - renderMenu() - const button = [...document.querySelectorAll('button')].find( - (candidate) => candidate.textContent === 'Agent model' - ) - expect(button?.dataset.checked).toBe('true') - act(() => button?.click()) - expect(setShowAgentModels).toHaveBeenCalledWith(false) - showAgentModels = false - act(() => root?.unmount()) - root = null - renderMenu() - const hiddenButton = [...document.querySelectorAll('button')].find( - (candidate) => - candidate.textContent === 'Agent model' && candidate.dataset.checked === 'false' - ) - act(() => hiddenButton?.click()) - expect(setShowAgentModels).toHaveBeenLastCalledWith(true) - } - ) - - it('applies the compact card mode preset from the visible card layout menu', () => { - renderMenu() - - const compactLayoutButton = document.querySelector( - '[data-radio-group-value="detailed"] [data-radio-item-value="compact"]' - ) - expect(compactLayoutButton).not.toBeNull() - - act(() => { - compactLayoutButton?.click() - }) - - expect(setWorktreeCardMode).toHaveBeenCalledWith('Compact') - }) - - it('keeps branch-only copy when project groups are unavailable', () => { - settings = { compactWorktreeCards: false, experimentalNewWorktreeCardStyle: true } - - renderMenu() - - expect(container?.textContent).toContain('Branch name') - expect(container?.textContent).not.toContain('Branch / folder path') - }) - - it('mentions folder paths when project groups can create folder workspaces', () => { - settings = { compactWorktreeCards: false, experimentalNewWorktreeCardStyle: true } - projectGroups = [{ id: 'group-1' }] - - renderMenu() - - expect(container?.textContent).toContain('Branch / folder path') - expect(container?.textContent).not.toContain('Branch name') - }) -}) diff --git a/src/renderer/src/components/sidebar/workspace-creation/folder/folder-workspace-composer-submit-host-platform.test.ts b/src/renderer/src/components/sidebar/workspace-creation/folder/folder-workspace-composer-submit-host-platform.test.ts index df2750da591..a0e7cea9243 100644 --- a/src/renderer/src/components/sidebar/workspace-creation/folder/folder-workspace-composer-submit-host-platform.test.ts +++ b/src/renderer/src/components/sidebar/workspace-creation/folder/folder-workspace-composer-submit-host-platform.test.ts @@ -19,42 +19,6 @@ describe('submitFolderWorkspaceCreate on an Orca server', () => { beforeEach(() => { mocks.activateAndRevealFolderWorkspace.mockReturnValue({ primaryTabId: 'tab-1' }) }) - it('preserves SSH group ownership when creating and activating a folder workspace', async () => { - const projectGroup = { - ...makeProjectGroup(), - connectionId: 'ssh-1', - executionHostId: 'ssh:ssh-1' - } - const createFolderWorkspace = vi.fn(async () => makeFolderWorkspace({ connectionId: 'ssh-1' })) - const onOpenChange = vi.fn() - - await submitFolderWorkspaceCreate({ - projectGroup, - name: 'SSH workspace', - lastAutoName: '', - linkedWorkItem: null, - note: '', - quickAgent: null, - autoRenameBranchFromWork: false, - agentCmdOverrides: {}, - isRemote: true, - runtimeEnvironmentId: null, - createFolderWorkspace, - onOpenChange - }) - - expect(createFolderWorkspace).toHaveBeenCalledWith({ - projectGroupId: 'group-1', - name: 'SSH workspace', - connectionId: 'ssh-1', - linkedTask: null - }) - expect(onOpenChange).toHaveBeenCalledWith(false) - expect(mocks.activateAndRevealFolderWorkspace).toHaveBeenCalledWith('folder-workspace-1', { - agent: null, - runtimeEnvironmentId: null - }) - }) it('quotes quick-agent startup for POSIX when the folder group is a local WSL UNC path', async () => { const createFolderWorkspace = vi.fn(async () => makeFolderWorkspace()) diff --git a/src/renderer/src/components/sidebar/worktree-list/controls/workspace-options-menu-items.test.tsx b/src/renderer/src/components/sidebar/worktree-list/controls/workspace-options-menu-items.test.tsx deleted file mode 100644 index c32820cfc0a..00000000000 --- a/src/renderer/src/components/sidebar/worktree-list/controls/workspace-options-menu-items.test.tsx +++ /dev/null @@ -1,195 +0,0 @@ -// @vitest-environment happy-dom -import { act, type ReactNode } from 'react' -import { createRoot, type Root } from 'react-dom/client' -import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' -import { - WORKSPACE_GROUP_BY_CHOICES, - WORKSPACE_VIEW_OPTIONS, - WORKSPACE_VIEW_SECTION_LABELS, - getShownWorkspaceViewSections, - type WorkspaceViewOptionContext, - type WorkspaceViewOptionKey -} from '../../../../../../shared/workspace-view/workspace-view-options' - -;(globalThis as { IS_REACT_ACT_ENVIRONMENT?: boolean }).IS_REACT_ACT_ENVIRONMENT = true - -const mocks = vi.hoisted(() => { - const state: Record = {} - const hostOptions: { id: string }[] = [] - return { state, hostOptions } -}) - -vi.mock('@/store', () => ({ - useAppStore: Object.assign( - (selector: (state: Record) => unknown) => selector(mocks.state), - { getState: () => mocks.state } - ) -})) - -vi.mock('./use-sidebar-host-scope-options', () => ({ - useSidebarHostScopeOptions: () => ({ hostOptions: mocks.hostOptions, hostScopeOptions: [] }) -})) -vi.mock('./sidebar-host-options', () => ({ - shouldShowHostScopeControls: (hosts: { id: string }[]) => hosts.some((h) => h.id !== 'local'), - getSidebarHostVisibilityLabel: () => 'All hosts' -})) -vi.mock('./SidebarHostScopeMenuSection', () => ({ - SidebarHostScopeMenuSection: () =>
Hosts
-})) -vi.mock('./SidebarRepositoryFilterSection', () => ({ - default: () =>
Projects
-})) -vi.mock('@/components/ui/tooltip', () => ({ - Tooltip: ({ children }: { children: ReactNode }) => <>{children}, - TooltipTrigger: ({ children }: { children: ReactNode }) => <>{children}, - TooltipContent: () => null -})) -vi.mock('@/components/ui/dropdown-menu', () => { - const Pass = ({ children }: { children?: ReactNode }) =>
{children}
- return { - DropdownMenuCheckboxItem: Pass, - DropdownMenuLabel: Pass, - DropdownMenuRadioGroup: Pass, - DropdownMenuRadioItem: Pass, - DropdownMenuSeparator: () =>
, - DropdownMenuShortcut: Pass, - DropdownMenuSub: Pass, - DropdownMenuSubContent: () => null, - DropdownMenuSubTrigger: Pass, - DropdownMenuItem: Pass - } -}) - -import { WorkspaceOptionsMenuItems } from './workspace-options-menu-items' - -const BASE_CONTEXT: WorkspaceViewOptionContext = { - groupBy: 'none', - hideSleepingWorkspaces: false, - hideWorkspacesFromOtherDevices: false, - newCardStyle: false, - hasRemoteHosts: false, - projectCount: 1, - mayHaveServerHosts: false -} - -function setState(context: WorkspaceViewOptionContext, catalogHydrated = true): void { - mocks.hostOptions = context.hasRemoteHosts ? [{ id: 'local' }, { id: 'ssh:box' }] : [] - const noop = vi.fn() - mocks.state = { - repos: Array.from({ length: context.projectCount }, (_, i) => ({ id: `repo-${i}` })), - groupBy: context.groupBy, - setGroupBy: noop, - sortBy: 'name', - setSortBy: noop, - projectOrderBy: 'manual', - setProjectOrderBy: noop, - showSleepingWorkspaces: !context.hideSleepingWorkspaces, - setShowSleepingWorkspaces: noop, - alwaysShowDefaultBranchWorkspace: true, - setAlwaysShowDefaultBranchWorkspace: noop, - hideDefaultBranchWorkspace: false, - setHideDefaultBranchWorkspace: noop, - hideAutomationGeneratedWorkspaces: false, - setHideAutomationGeneratedWorkspaces: noop, - hideCliCreatedWorkspaces: false, - setHideCliCreatedWorkspaces: noop, - hideDetachedHeadWorkspaces: false, - setHideDetachedHeadWorkspaces: noop, - hideWorkspacesFromOtherDevices: context.hideWorkspacesFromOtherDevices, - setHideWorkspacesFromOtherDevices: noop, - runtimeEnvironments: context.mayHaveServerHosts ? [{ id: 'server' }] : [], - runtimeEnvironmentCatalogHydrated: catalogHydrated, - settings: { - compactWorktreeCards: false, - experimentalNewWorktreeCardStyle: context.newCardStyle - }, - worktreeCardProperties: ['status'], - setWorktreeCardProperties: noop, - setWorktreeCardMode: noop, - agentActivityDisplayMode: 'compact', - setAgentActivityDisplayMode: noop, - projectGroups: [], - visibleWorkspaceHostIds: null, - setVisibleWorkspaceHostIds: noop, - setWorkspaceHostScope: noop - } -} - -let container: HTMLDivElement -let root: Root - -beforeEach(() => { - container = document.createElement('div') - document.body.appendChild(container) - root = createRoot(container) -}) - -afterEach(() => { - act(() => root.unmount()) - container.remove() -}) - -// Probe the control’s own text because Group by’s heading belongs to the whole section. -const PROBE: Partial> = { - groupBy: WORKSPACE_GROUP_BY_CHOICES[0].label -} - -function renderedOptionKeys(): WorkspaceViewOptionKey[] { - const text = container.textContent ?? '' - return WORKSPACE_VIEW_OPTIONS.map((option) => ({ - key: option.key, - at: text.indexOf(PROBE[option.key] ?? option.label) - })) - .filter((option) => option.at >= 0) - .sort((a, b) => a.at - b.at) - .map((option) => option.key) -} - -const CONTEXTS: WorkspaceViewOptionContext[] = [ - BASE_CONTEXT, - { - ...BASE_CONTEXT, - groupBy: 'repo', - hideSleepingWorkspaces: true, - hasRemoteHosts: true, - projectCount: 3, - mayHaveServerHosts: true - }, - { ...BASE_CONTEXT, newCardStyle: true, hideWorkspacesFromOtherDevices: true } -] - -describe('Workspace options menu renders from the shared option list', () => { - for (const context of CONTEXTS) { - it(`shows one control per shown option, in order: ${JSON.stringify(context)}`, () => { - setState(context) - act(() => root.render()) - const sections = getShownWorkspaceViewSections(context) - expect(renderedOptionKeys()).toEqual( - sections.flatMap(({ options }) => options.map((option) => option.key)) - ) - for (const { section } of sections) { - const heading = WORKSPACE_VIEW_SECTION_LABELS[section] - if (heading) { - expect(container.textContent).toContain(heading) - } - } - }) - } - - it('renders every option in some context', () => { - const everShown = new Set( - CONTEXTS.flatMap((context) => - getShownWorkspaceViewSections(context).flatMap(({ options }) => - options.map((option) => option.key) - ) - ) - ) - expect([...everShown].sort()).toEqual(WORKSPACE_VIEW_OPTIONS.map((o) => o.key).sort()) - }) - - it('keeps the other-client filter while the server catalog is still loading', () => { - setState(BASE_CONTEXT, false) - act(() => root.render()) - expect(renderedOptionKeys()).toContain('hideWorkspacesFromOtherDevices') - }) -}) diff --git a/src/renderer/src/components/sidebar/worktree-list/drag/project-group-header-drop.test.ts b/src/renderer/src/components/sidebar/worktree-list/drag/project-group-header-drop.test.ts index 4a22fa9d4e6..a3443f73c36 100644 --- a/src/renderer/src/components/sidebar/worktree-list/drag/project-group-header-drop.test.ts +++ b/src/renderer/src/components/sidebar/worktree-list/drag/project-group-header-drop.test.ts @@ -4,13 +4,10 @@ import { describe, expect, it } from 'vitest' import { computeProjectGroupHeaderDropPreview, getProjectGroupHeaderDragBucketKey, - getProjectGroupTabOrderUpdatesForSidebarDrop, - getSidebarOrderedProjectGroupHeaderIdsByBucket, - mapSidebarProjectGroupDropIndexToSiblingInsertIndex + getProjectGroupTabOrderUpdatesForSidebarDrop } from './project-group-header-drop' -import type { Row } from '../../../../../../shared/sidebar-list/row-types' + import type { ProjectGroup } from '../../../../../../shared/project-group-types' -import type { Repo } from '../../../../../../shared/repo-types' function group(id: string, overrides: Partial = {}): ProjectGroup { return { @@ -29,21 +26,6 @@ function group(id: string, overrides: Partial = {}): ProjectGroup } describe('getProjectGroupHeaderDragBucketKey', () => { - it('uses root for top-level groups', () => { - expect(getProjectGroupHeaderDragBucketKey(group('root'))).toBe('root') - }) - - it('scopes child groups to their parent bucket', () => { - const root = group('root') - const child = group('child', { parentGroupId: root.id }) - const groupsById = new Map([ - [root.id, root], - [child.id, child] - ]) - - expect(getProjectGroupHeaderDragBucketKey(child, groupsById)).toBe('parent:root') - }) - it('falls back to root when persisted parent metadata is missing', () => { const orphan = group('orphan', { parentGroupId: 'missing' }) @@ -51,83 +33,6 @@ describe('getProjectGroupHeaderDragBucketKey', () => { }) }) -describe('getSidebarOrderedProjectGroupHeaderIdsByBucket', () => { - it('groups Project Group headers by effective parent bucket', () => { - const rootA = group('root-a') - const rootB = group('root-b') - const childA = group('child-a', { parentGroupId: rootA.id }) - const repo = { id: 'repo-a', projectGroupId: rootA.id } as Repo - const groupsById = new Map([ - [rootA.id, rootA], - [rootB.id, rootB], - [childA.id, childA] - ]) - const rows: Row[] = [ - { - type: 'header', - key: 'project-group:root-a', - label: 'A', - count: 0, - icon: { kind: 'project' }, - projectGroup: rootA - }, - { - type: 'header', - key: 'repo:repo-a', - label: 'repo', - count: 0, - icon: { kind: 'project' }, - repo - }, - { - type: 'header', - key: 'project-group:child-a', - label: 'A child', - count: 0, - icon: { kind: 'project' }, - projectGroup: childA - }, - { - type: 'header', - key: 'project-group:root-b', - label: 'B', - count: 0, - icon: { kind: 'project' }, - projectGroup: rootB - } - ] - - expect(getSidebarOrderedProjectGroupHeaderIdsByBucket(rows, groupsById)).toEqual( - new Map([ - ['root', ['root-a', 'root-b']], - ['parent:root-a', ['child-a']] - ]) - ) - }) -}) - -describe('mapSidebarProjectGroupDropIndexToSiblingInsertIndex', () => { - it('keeps upward drops at the same target index after removing the source', () => { - expect( - mapSidebarProjectGroupDropIndexToSiblingInsertIndex({ - sidebarDropIndex: 0, - sourceIndex: 2, - siblingCount: 2 - }) - ).toBe(0) - }) - - it('shifts downward drops because the source header is removed first', () => { - expect( - mapSidebarProjectGroupDropIndexToSiblingInsertIndex({ - sidebarDropIndex: 3, - sourceIndex: 0, - siblingCount: 2 - }) - ).toBe(2) - }) -}) - describe('computeProjectGroupHeaderDropPreview', () => { it('uses row-model header indices instead of mounted subset order', () => { const preview = computeProjectGroupHeaderDropPreview({ @@ -167,27 +72,6 @@ describe('computeProjectGroupHeaderDropPreview', () => { expect(preview).toBeNull() }) - - it('uses the whole Project Group section for the final boundary slot', () => { - const preview = computeProjectGroupHeaderDropPreview({ - pointerY: 400, - containerTop: 0, - scrollTop: 0, - sidebarProjectGroupHeaderIds: ['a', 'b', 'c'], - rects: [ - { - groupId: 'c', - bucketKey: 'root', - headerIndex: 2, - top: 300, - bottom: 328, - sectionBottom: 380 - } - ] - }) - - expect(preview).toEqual({ dropIndex: 3, dropIndicatorY: 383 }) - }) }) describe('getProjectGroupTabOrderUpdatesForSidebarDrop', () => { @@ -208,18 +92,4 @@ describe('getProjectGroupTabOrderUpdatesForSidebarDrop', () => { { groupId: 'c', tabOrder: 3 } ]) }) - - it('returns no updates when the drop keeps the group in the same slot', () => { - const groups = [group('a', { tabOrder: 0 }), group('b', { tabOrder: 1 })] - const projectGroupById = new Map(groups.map((entry) => [entry.id, entry])) - - expect( - getProjectGroupTabOrderUpdatesForSidebarDrop({ - sidebarProjectGroupHeaderIds: ['a', 'b'], - draggedGroupId: 'a', - sidebarDropIndex: 1, - projectGroupById - }) - ).toEqual([]) - }) }) diff --git a/src/renderer/src/components/sidebar/worktree-list/grouping/host-section-reveal.test.tsx b/src/renderer/src/components/sidebar/worktree-list/grouping/host-section-reveal.test.tsx index e31f4e70bb9..aea46b0141d 100644 --- a/src/renderer/src/components/sidebar/worktree-list/grouping/host-section-reveal.test.tsx +++ b/src/renderer/src/components/sidebar/worktree-list/grouping/host-section-reveal.test.tsx @@ -147,24 +147,5 @@ for (const hostId of ['ssh:builder', 'runtime:builder'] as const) { expect(after.some((row) => row.type === 'item' && row.worktree.id === remote.id)).toBe(true) expect(after.some((row) => row.type === 'item' && row.worktree.id === local.id)).toBe(false) }) - it(`${hostId} ${groupBy}: combined view retains its single section collapse key`, () => { - const { local, remote } = setup(hostId) - const worktrees = [local, remote] - const rows = sections(hostId, groupBy, worktrees, new Set([laneKey]), {}, null) - expect(rows.filter((row) => row.type === 'header' && row.key === laneKey)).toHaveLength(1) - expect(rows.some((row) => row.type === 'item')).toBe(false) - const expanded = sections( - hostId, - groupBy, - worktrees, - new Set([`${laneKey}:host:${hostId}`]), - {}, - null - ) - expect(expanded.filter((row) => row.type === 'item')).toHaveLength(2) - expect(expanded.some((row) => row.type === 'header' && row.collapseKey !== undefined)).toBe( - false - ) - }) } } diff --git a/src/renderer/src/components/sidebar/worktree-list/navigation/use-keyboard.host-identity.test.tsx b/src/renderer/src/components/sidebar/worktree-list/navigation/use-keyboard.host-identity.test.tsx index f2b8f1a81d4..c1cd43da360 100644 --- a/src/renderer/src/components/sidebar/worktree-list/navigation/use-keyboard.host-identity.test.tsx +++ b/src/renderer/src/components/sidebar/worktree-list/navigation/use-keyboard.host-identity.test.tsx @@ -125,30 +125,6 @@ describe('worktree keyboard cycling with a resolved active host', () => { executionHostId: 'local' }) }) - - it('steps to the previous row when the active host resolved to local', () => { - renderProbe('b', 'local') - - press('up') - - expect(activateAndRevealWorktree).toHaveBeenCalledWith('a', { - navigationIntent: 'user-open', - revealInSidebar: false, - executionHostId: 'local' - }) - }) - - it('still steps normally when the active host is unqualified', () => { - renderProbe('b', null) - - press('down') - - expect(activateAndRevealWorktree).toHaveBeenCalledWith('c', { - navigationIntent: 'user-open', - revealInSidebar: false, - executionHostId: 'local' - }) - }) }) describe('workspace keyboard cycling after closing the final tab', () => { diff --git a/src/renderer/src/components/sidebar/worktree-list/navigation/worktree-keyboard-cycle.test.ts b/src/renderer/src/components/sidebar/worktree-list/navigation/worktree-keyboard-cycle.test.ts index 5b903eb40f7..48919f036b0 100644 --- a/src/renderer/src/components/sidebar/worktree-list/navigation/worktree-keyboard-cycle.test.ts +++ b/src/renderer/src/components/sidebar/worktree-list/navigation/worktree-keyboard-cycle.test.ts @@ -1,13 +1,7 @@ import { describe, expect, it } from 'vitest' import type { HostSectionRow } from '../../../../../../shared/sidebar-list/host-section-rows' import type { FolderWorkspaceRow } from '../../../../../../shared/sidebar-list/row-types' -import { - getCyclableRowIdentity, - getCyclableWorktreeIds, - getCyclableWorktrees, - resolveActiveCycleIdentity, - resolveCycledWorktreeId -} from './worktree-keyboard-cycle' +import { getCyclableRowIdentity, getCyclableWorktreeIds } from './worktree-keyboard-cycle' import { getRenderedWorkspaceRowsInSidebarOrder as getCyclableWorktreeRows } from './worktree-sidebar-row-preference' const folderRow: FolderWorkspaceRow = { @@ -44,46 +38,6 @@ const folderRow: FolderWorkspaceRow = { groupDepth: 0 } -describe('resolveCycledWorktreeId', () => { - const worktreeIds = ['a', 'b', 'c'] - - it('steps to the next and previous worktree', () => { - expect(resolveCycledWorktreeId({ worktreeIds, activeWorktreeId: 'a', direction: 'down' })).toBe( - 'b' - ) - expect(resolveCycledWorktreeId({ worktreeIds, activeWorktreeId: 'b', direction: 'up' })).toBe( - 'a' - ) - }) - - it('wraps around at both ends', () => { - expect(resolveCycledWorktreeId({ worktreeIds, activeWorktreeId: 'c', direction: 'down' })).toBe( - 'a' - ) - expect(resolveCycledWorktreeId({ worktreeIds, activeWorktreeId: 'a', direction: 'up' })).toBe( - 'c' - ) - }) - - it('enters from the matching end when the active worktree is not cyclable', () => { - expect( - resolveCycledWorktreeId({ worktreeIds, activeWorktreeId: 'hidden', direction: 'down' }) - ).toBe('a') - expect( - resolveCycledWorktreeId({ worktreeIds, activeWorktreeId: 'hidden', direction: 'up' }) - ).toBe('c') - expect( - resolveCycledWorktreeId({ worktreeIds, activeWorktreeId: null, direction: 'down' }) - ).toBe('a') - }) - - it('has nothing to cycle to when every group is collapsed', () => { - expect( - resolveCycledWorktreeId({ worktreeIds: [], activeWorktreeId: 'a', direction: 'down' }) - ).toBe(null) - }) -}) - describe('getCyclableWorktreeIds', () => { const repo = { id: 'repo-1', @@ -107,67 +61,6 @@ describe('getCyclableWorktreeIds', () => { } } - it('keeps a pinned worktree cyclable when only its natural group is collapsed', () => { - const rows: HostSectionRow[] = [worktree('pinned-a', true), worktree('plain-b')] - - expect(getCyclableWorktreeIds(rows, 'single-location')).toEqual(['pinned-a', 'plain-b']) - }) - - it('counts a duplicated pinned worktree once', () => { - const rows: HostSectionRow[] = [ - worktree('dup', true), - { ...worktree('dup'), rowKey: 'row:dup-natural' }, - worktree('plain-b') - ] - - expect(getCyclableWorktreeIds(rows, 'duplicate-in-groups')).toEqual(['dup', 'plain-b']) - }) - - it('keeps same-id rows on different hosts independently cyclable', () => { - const rows: HostSectionRow[] = [ - { - ...worktree('shared'), - worktree: { id: 'shared', repoId: repo.id, hostId: 'local' } as never - }, - { - ...worktree('shared'), - rowKey: 'row:shared:ssh', - worktree: { id: 'shared', repoId: repo.id, hostId: 'ssh:host-b' } as never - } - ] - - expect(getCyclableWorktrees(rows, 'single-location').map((item) => item.hostId)).toEqual([ - 'local', - 'ssh:host-b' - ]) - }) - - it('includes folder workspaces between git worktrees in visible order', () => { - const rows = [worktree('a'), folderRow, worktree('b')] - - expect(getCyclableWorktreeIds(rows, 'single-location')).toEqual(['a', 'folder:folder-1', 'b']) - }) - - it('anchors both directions on the active folder workspace', () => { - const rows = getCyclableWorktreeRows( - [worktree('a'), folderRow, worktree('b')], - 'single-location' - ) - const activeWorktreeId = resolveActiveCycleIdentity({ - rows, - activeWorktreeId: 'folder:folder-1', - activeWorkspaceExecutionHostId: 'local' - }) - const worktreeIds = rows.map(getCyclableRowIdentity) - - expect(resolveCycledWorktreeId({ worktreeIds, activeWorktreeId, direction: 'up' })).toBe( - getCyclableRowIdentity(rows[0]) - ) - expect(resolveCycledWorktreeId({ worktreeIds, activeWorktreeId, direction: 'down' })).toBe( - getCyclableRowIdentity(rows[2]) - ) - }) - it('keeps folder placement while preferring a pinned worktree natural row', () => { const rows = [ worktree('dup', true), @@ -199,23 +92,4 @@ describe('getCyclableWorktreeIds', () => { 'ssh:host-b|folder:folder-1' ]) }) - - it('drops worktrees the sidebar elided inside a collapsed host section', () => { - const rows: HostSectionRow[] = [ - { - type: 'host-header', - key: 'host:local', - hostId: 'local' as never, - kind: 'local', - label: 'This computer', - detail: '', - health: 'local', - collapsed: true, - count: 1 - }, - worktree('visible-after-host') - ] - - expect(getCyclableWorktreeIds(rows, 'single-location')).toEqual(['visible-after-host']) - }) }) diff --git a/src/renderer/src/components/sidebar/worktree-list/rows/NoticeHostGlyph.test.tsx b/src/renderer/src/components/sidebar/worktree-list/rows/NoticeHostGlyph.test.tsx deleted file mode 100644 index a79c30af44a..00000000000 --- a/src/renderer/src/components/sidebar/worktree-list/rows/NoticeHostGlyph.test.tsx +++ /dev/null @@ -1,167 +0,0 @@ -// @vitest-environment happy-dom - -import { act, cloneElement, type ReactElement, type ReactNode } from 'react' -import { createRoot, type Root } from 'react-dom/client' -import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' - -import NoticeHostGlyph from './NoticeHostGlyph' -import en from '../../../../i18n/locales/en.json' -import es from '../../../../i18n/locales/es.json' -import ja from '../../../../i18n/locales/ja.json' -import ko from '../../../../i18n/locales/ko.json' -import zh from '../../../../i18n/locales/zh.json' -import { useAppStore } from '@/store' -import type { RuntimeEnvironmentStatus } from '../../../../../../shared/remote-runtime/host-status/runtime-host-status' -import { createCompatibleRuntimeStatusResponse } from '@/runtime/runtime-compatibility-test-fixture' - -const original = useAppStore.getState() -let runtimeStatusByEnvironmentId = new Map() - -vi.mock('@/components/ui/tooltip', () => ({ - Tooltip: ({ children }: { children: ReactNode }) => <>{children}, - TooltipTrigger: ({ children }: { children: ReactElement<{ 'data-testid'?: string }> }) => - cloneElement(children, { 'data-testid': 'tooltip-trigger' }), - TooltipContent: ({ children }: { children: ReactNode }) => ( - {children} - ) -})) - -const roots: Root[] = [] - -async function render( - hostId: string, - hostLabel = 'openclaw', - keyboardFocusable = false -): Promise { - const container = document.createElement('div') - document.body.appendChild(container) - const root = createRoot(container) - roots.push(root) - await act(async () => { - root.render( - - ) - }) - return container -} - -describe('NoticeHostGlyph', () => { - beforeEach(() => { - globalThis.IS_REACT_ACT_ENVIRONMENT = true - runtimeStatusByEnvironmentId = new Map() - useAppStore.setState({ runtimeStatusByEnvironmentId }) - }) - - afterEach(() => { - roots.splice(0).forEach((root) => act(() => root.unmount())) - document.body.replaceChildren() - useAppStore.setState(original, true) - }) - - it('names the SSH host it would act on', async () => { - const container = await render('ssh:openclaw-target') - - expect(container.querySelector('[data-notice-host-kind="ssh"]')).not.toBeNull() - expect(container.querySelector('[data-testid="tooltip"]')?.textContent).toBe( - 'Project on SSH host openclaw' - ) - }) - - it('names the paired runtime separately from the SSH host of the same name', async () => { - const response = createCompatibleRuntimeStatusResponse() - if (!response.ok) { - throw new Error('expected a compatible status') - } - runtimeStatusByEnvironmentId.set('openclaw-env', { status: response.result, checkedAt: 1 }) - const container = await render('runtime:openclaw-env') - - expect(container.querySelector('[data-notice-host-kind="runtime"]')).not.toBeNull() - expect(container.querySelector('[data-testid="tooltip"]')?.textContent).toBe( - 'Project on openclaw' - ) - }) - - it('marks a paired runtime a probe found unreachable as disconnected', async () => { - runtimeStatusByEnvironmentId.set('openclaw-env', { status: null, checkedAt: 1 }) - const container = await render('runtime:openclaw-env') - - expect(container.querySelector('[data-testid="tooltip"]')?.textContent).toBe( - 'openclaw disconnected' - ) - }) - - it('does not call a host disconnected before its first probe answers', async () => { - // An absent first probe is not evidence of disconnection; avoid destructive styling during launch. - const container = await render('runtime:openclaw-env') - - expect(container.querySelector('[data-testid="tooltip"]')?.textContent).toBe( - 'Project on openclaw' - ) - expect(container.querySelector('svg')?.getAttribute('class')).not.toContain('text-destructive') - }) - - it('gives the local host the monitor glyph the run-target rows use', async () => { - const container = await render('local', 'Local Mac') - - expect(container.querySelector('[data-notice-host-kind="local"]')).not.toBeNull() - expect(container.querySelector('[data-testid="tooltip"]')?.textContent).toBe( - 'Project on this host' - ) - }) - - it('makes a passive row glyph keyboard reachable with an accessible name', async () => { - const container = await render('ssh:openclaw-target', 'openclaw', true) - const trigger = container.querySelector('[data-testid="tooltip-trigger"]') - - expect(trigger?.getAttribute('tabindex')).toBe('0') - expect(trigger?.getAttribute('role')).toBe('img') - expect(trigger?.getAttribute('aria-label')).toBe('Project on SSH host openclaw') - }) - - it('does not add a nested tab stop when the glyph is inside a button', async () => { - const container = await render('ssh:openclaw-target') - - expect( - container.querySelector('[data-testid="tooltip-trigger"]')?.hasAttribute('tabindex') - ).toBe(false) - }) - - it('draws one glyph vocabulary: a monitor for local, a server for remote', async () => { - const local = await render('local', 'Local Mac') - const remote = await render('ssh:openclaw-target') - - const glyph = (container: HTMLDivElement): string => - container.querySelector('svg')?.getAttribute('class') ?? '' - expect(glyph(local)).toContain('lucide-monitor') - expect(glyph(remote)).toContain('lucide-server') - const tokens = (value: string): string[] => - value.split(' ').filter((entry) => !entry.startsWith('lucide')) - expect(tokens(glyph(local))).toEqual(tokens(glyph(remote))) - }) - - it('keeps its copy in the English catalog', async () => { - // Component-only fallback keys never reach translators. - expect(en.auto.components.sidebar.NoticeHostGlyph).toMatchObject({ - hostDisconnected: '{{hostName}} disconnected', - sshHostProject: 'Project on SSH host {{hostName}}', - localHostProject: 'Project on this host', - runtimeHostProject: 'Project on {{hostName}}' - }) - }) - - it.each(Object.entries({ es, ja, ko, zh }))( - 'keeps its copy in the %s catalog', - (_locale, catalog) => { - expect(catalog.auto.components.sidebar.NoticeHostGlyph).toMatchObject({ - hostDisconnected: expect.stringContaining('{{hostName}}'), - sshHostProject: expect.stringContaining('{{hostName}}'), - localHostProject: expect.any(String), - runtimeHostProject: expect.stringContaining('{{hostName}}') - }) - } - ) -}) diff --git a/src/renderer/src/components/status-bar/SshStatusSegment.large-host-list.test.tsx b/src/renderer/src/components/status-bar/SshStatusSegment.large-host-list.test.tsx index 92a630aa8a5..3a756b77fe5 100644 --- a/src/renderer/src/components/status-bar/SshStatusSegment.large-host-list.test.tsx +++ b/src/renderer/src/components/status-bar/SshStatusSegment.large-host-list.test.tsx @@ -97,11 +97,6 @@ describe('the status menu with a very large imported SSH host list', () => { expect(screen.queryByText('node-13691')).toBeNull() }) - it('keeps an idle host that owns a project, even deep in the config', () => { - render() - expect(screen.getByText('node-12345')).toBeTruthy() - }) - it('offers a search for the hidden hosts that connects the exact host typed', async () => { const connect = vi.fn(async () => undefined) vi.stubGlobal('api', { ssh: { connect } }) diff --git a/src/renderer/src/components/status-bar/provider-segment-balance.test.tsx b/src/renderer/src/components/status-bar/provider-segment-balance.test.tsx deleted file mode 100644 index e176ca44987..00000000000 --- a/src/renderer/src/components/status-bar/provider-segment-balance.test.tsx +++ /dev/null @@ -1,212 +0,0 @@ -import { renderToStaticMarkup } from 'react-dom/server' -import { afterEach, describe, expect, it, vi } from 'vitest' -import type { ProviderRateLimits } from '../../../../shared/rate-limit-types' - -const compactMessages = vi.hoisted(() => ({ balance: '' })) - -afterEach(() => { - compactMessages.balance = '' -}) - -vi.mock('@/i18n/i18n', () => ({ - i18n: { language: 'en' }, - translate: (_key: string, fallback: string, values?: Record) => { - let result = - _key === 'auto.components.status.bar.StatusBar.4fba7dc1e7' && compactMessages.balance - ? compactMessages.balance - : fallback - for (const [key, value] of Object.entries(values ?? {})) { - result = result.replace(`{{${key}}}`, value) - } - return result - } -})) - -vi.mock('@/lib/agent-catalog', () => ({ - AgentIcon: () => null -})) - -vi.mock('../../store', () => ({ - useAppStore: (selector: (state: { usagePercentageDisplay: 'used' | 'remaining' }) => unknown) => - selector({ usagePercentageDisplay: 'used' }) -})) - -function openCodeGo(sessionUsedPercent: number, balance = 12.4): ProviderRateLimits { - return { - provider: 'opencode-go', - session: { - usedPercent: sessionUsedPercent, - windowMinutes: 300, - resetsAt: null, - resetDescription: null - }, - weekly: { usedPercent: 40, windowMinutes: 10080, resetsAt: null, resetDescription: null }, - extraUsage: { - balance, - unit: 'currency', - currencyCode: 'USD', - enabled: true, - disabledReason: null, - spent: null, - spendLimit: null, - spentPercent: null, - resetsAt: null - }, - updatedAt: Date.now(), - error: null, - status: 'ok' - } -} - -function claudeUnknownBalance(): ProviderRateLimits { - return { - provider: 'claude', - session: { usedPercent: 100, windowMinutes: 300, resetsAt: null, resetDescription: null }, - weekly: { usedPercent: 40, windowMinutes: 10080, resetsAt: null, resetDescription: null }, - extraUsage: { - balance: null, - unit: 'currency', - currencyCode: 'EUR', - enabled: true, - disabledReason: null, - spent: 50, - spendLimit: 2000, - spentPercent: 2.5, - resetsAt: null - }, - updatedAt: Date.now(), - error: null, - status: 'ok' - } -} - -function codexCredits(sessionUsedPercent: number): ProviderRateLimits { - return { - provider: 'codex', - session: { - usedPercent: sessionUsedPercent, - windowMinutes: 300, - resetsAt: null, - resetDescription: null - }, - weekly: { usedPercent: 40, windowMinutes: 10080, resetsAt: null, resetDescription: null }, - extraUsage: { - balance: 500, - unit: 'credits', - unlimited: false, - enabled: true, - disabledReason: null, - resetsAt: null - }, - updatedAt: Date.now(), - error: null, - status: 'ok' - } -} - -function claudeDisabledCredits(): ProviderRateLimits { - return { - provider: 'claude', - session: { usedPercent: 100, windowMinutes: 300, resetsAt: null, resetDescription: null }, - weekly: { usedPercent: 80, windowMinutes: 10080, resetsAt: null, resetDescription: null }, - extraUsage: { - balance: 0, - unit: 'currency', - currencyCode: 'EUR', - enabled: false, - disabledReason: 'out_of_credits', - spent: 0, - spendLimit: 2000, - spentPercent: 0, - resetsAt: null - }, - updatedAt: Date.now(), - error: null, - status: 'ok' - } -} - -describe('ProviderSegment extra-usage balance token', () => { - it.each([ - ['Spanish', 'saldo {{value0}}', 'saldo $12.40'], - ['French', 'solde {{value0}}', 'solde $12.40'] - ])('lets %s put the balance label before its amount', async (_locale, message, expected) => { - compactMessages.balance = message - const { ProviderSegment } = await import('./StatusBar') - const markup = renderToStaticMarkup( - - ) - expect(markup).toContain(expected) - expect(markup).not.toContain('{{value0}}') - }) - - it('reveals the remaining balance once a plan window is capped', async () => { - const { ProviderSegment } = await import('./StatusBar') - - const markup = renderToStaticMarkup( - - ) - - expect(markup).toContain('$12.40') - expect(markup).toContain('bal') - }) - - it.each([0, -1.25])( - 'hides a depleted currency balance of %s from the compact segment', - async (balance) => { - const { ProviderSegment } = await import('./StatusBar') - - const markup = renderToStaticMarkup( - - ) - - expect(markup).not.toContain('bal') - expect(markup).not.toContain('$0.00') - expect(markup).not.toContain('-$1.25') - } - ) - - it('hides the balance while plan windows still have headroom', async () => { - const { ProviderSegment } = await import('./StatusBar') - - const markup = renderToStaticMarkup( - - ) - - expect(markup).not.toContain('$12.40') - expect(markup).not.toContain('bal') - }) - - it('does not reveal an unavailable balance as zero when a window is capped', async () => { - const { ProviderSegment } = await import('./StatusBar') - - const markup = renderToStaticMarkup( - - ) - - expect(markup).not.toContain('€0.00') - expect(markup).not.toContain('bal') - }) - - it('does not reveal a disabled balance even when a window is capped', async () => { - const { ProviderSegment } = await import('./StatusBar') - - const markup = renderToStaticMarkup( - - ) - - expect(markup).not.toContain('bal') - }) - - it('reveals a Codex credit count (not a currency amount) when a window caps', async () => { - const { ProviderSegment } = await import('./StatusBar') - - const markup = renderToStaticMarkup( - - ) - - expect(markup).toContain('500 credits') - expect(markup).not.toContain('$500') - expect(markup).not.toContain('bal') - }) -}) diff --git a/src/renderer/src/components/status-bar/use-resource-usage-derived-model.test.tsx b/src/renderer/src/components/status-bar/use-resource-usage-derived-model.test.tsx index 9e5d12a5ec8..2aedfe57845 100644 --- a/src/renderer/src/components/status-bar/use-resource-usage-derived-model.test.tsx +++ b/src/renderer/src/components/status-bar/use-resource-usage-derived-model.test.tsx @@ -153,12 +153,6 @@ describe('Resource Manager folder ownership', () => { } ) - it('does not replace a sampled project name with a same-id group from another host', () => { - const foreignGroup = { ...group, name: 'Foreign project', executionHostId: 'runtime:paired' } - const groups = derive([local], [], sampled, [group, foreignGroup]) - expect(groups[0].repoName).toBe('Local project') - }) - it('keeps git snapshot identity when the catalog contains a different host', () => { const row = { ...sampled, worktreeId: 'repo::/notes', repoId: 'repo' } const foreign = { diff --git a/src/renderer/src/components/terminal-pane/agent-question-answered-inference.test.ts b/src/renderer/src/components/terminal-pane/agent-question-answered-inference.test.ts index d1a795f91eb..7655c3d14c4 100644 --- a/src/renderer/src/components/terminal-pane/agent-question-answered-inference.test.ts +++ b/src/renderer/src/components/terminal-pane/agent-question-answered-inference.test.ts @@ -40,36 +40,6 @@ function makeInference(entry: AgentStatusEntry | undefined) { } describe('agent question-answered inference', () => { - it('reports the baseline when Enter is sent to a waiting question pane', () => { - const { inference, inferQuestionAnswered } = makeInference(makeWaitingQuestionEntry()) - - inference.observeSentTerminalInput('\r') - - expect(inferQuestionAnswered).toHaveBeenCalledExactlyOnceWith({ - paneKey: PANE_KEY, - baselineUpdatedAt: 1_000, - baselineStateStartedAt: 900, - baselinePrompt: 'pick a color', - baselineAgentType: 'claude' - }) - }) - - it('accepts kitty-keyboard Enter encodings', () => { - const { inference, inferQuestionAnswered } = makeInference(makeWaitingQuestionEntry()) - - inference.observeSentTerminalInput('\x1b[13u') - - expect(inferQuestionAnswered).toHaveBeenCalledOnce() - }) - - it('accepts a bare digit quick-select, which submits without Enter', () => { - const { inference, inferQuestionAnswered } = makeInference(makeWaitingQuestionEntry()) - - inference.observeSentTerminalInput('2') - - expect(inferQuestionAnswered).toHaveBeenCalledOnce() - }) - it('keeps waiting when a digit only advances or toggles a partial answer', () => { const prompts = [ JSON.stringify({ @@ -151,21 +121,6 @@ describe('agent question-answered inference', () => { expect(inferQuestionAnswered).toHaveBeenCalledOnce() }) - it('does not read status for ordinary terminal input', () => { - const getStatusEntry = vi.fn(() => makeWaitingQuestionEntry()) - const inferQuestionAnswered = vi.fn() - const inference = createAgentQuestionAnsweredInference({ - paneKey: PANE_KEY, - getStatusEntry, - inferQuestionAnswered - }) - - inference.observeSentTerminalInput('ordinary typing') - - expect(getStatusEntry).not.toHaveBeenCalled() - expect(inferQuestionAnswered).not.toHaveBeenCalled() - }) - it('ignores non-submit input, batched keystrokes, and pastes', () => { const { inference, inferQuestionAnswered } = makeInference(makeWaitingQuestionEntry()) diff --git a/src/renderer/src/components/terminal-pane/claude-old-terminal-banner.test.tsx b/src/renderer/src/components/terminal-pane/claude-old-terminal-banner.test.tsx deleted file mode 100644 index bea0930ed97..00000000000 --- a/src/renderer/src/components/terminal-pane/claude-old-terminal-banner.test.tsx +++ /dev/null @@ -1,178 +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 { useAppStore } from '@/store' -import type { GlobalSettings } from '../../../../shared/global-settings-types' -import { FLOATING_TERMINAL_WORKTREE_ID, getDefaultSettings } from '../../../../shared/constants' -import type { ClaudeManagedAccount } from '../../../../shared/managed-account-types' -import type { Worktree } from '../../../../shared/worktree/types' -import { makePaneKey } from '../../../../shared/workspace-layout/stable-pane-id' -import { ClaudeOldTerminalBanner } from './ClaudeOldTerminalBanner' - -const routing = vi.hoisted(() => ({ activateAndRevealWorkspace: vi.fn(() => ({})) })) -vi.mock('@/lib/worktree-activation', () => ({ - activateAndRevealWorkspace: routing.activateAndRevealWorkspace -})) - -globalThis.IS_REACT_ACT_ENVIRONMENT = true - -const TAB_ID = 'tab-1' -const LEAF_ID = '11111111-1111-4111-8111-111111111111' -const PANE_KEY = makePaneKey(TAB_ID, LEAF_ID) -const TITLE = "This terminal was opened before Orca's Claude account update" -const ACCOUNT: ClaudeManagedAccount = { - id: 'a', - email: 'a@example.test', - authMethod: 'subscription-oauth', - managedAuthPath: '/unused', - createdAt: 0, - updatedAt: 0, - lastAuthenticatedAt: 0 -} -let paneElement: HTMLDivElement -let root: Root -let openedBeforeClaudeAccounts: ReturnType< - typeof vi.fn<(id: string, target: { runtime: string }) => Promise> -> -let nextPtyId = 0 -let ptyId: string - -class ResizeObserverStub { - observe(): void {} - disconnect(): void {} -} - -const FLOATING_TAB = { - id: 'unified-floating', - entityId: TAB_ID, - groupId: 'group-1', - worktreeId: FLOATING_TERMINAL_WORKTREE_ID, - contentType: 'terminal' as const, - label: 'Terminal', - customLabel: null, - color: null, - sortOrder: 0, - createdAt: 0 -} - -function setState(settings: Partial, agent: 'claude' | 'codex' = 'claude'): void { - useAppStore.setState({ - settings: { ...getDefaultSettings('/home/me'), ...settings }, - paneForegroundAgentByPaneKey: { [PANE_KEY]: { agent, shellForeground: false } }, - // A floating terminal runs on the host. - unifiedTabsByWorktree: { [FLOATING_TERMINAL_WORKTREE_ID]: [FLOATING_TAB] } - }) -} - -const SELECTED = { claudeManagedAccounts: [ACCOUNT], activeClaudeManagedAccountId: 'a' } - -async function renderBanner(): Promise { - await act(async () => { - root.render() - }) -} - -function button(label: string): HTMLButtonElement { - const match = Array.from(document.querySelectorAll('button')).find( - (candidate) => candidate.textContent?.trim() === label - ) - if (!match) { - throw new Error(`missing ${label} button`) - } - return match -} - -beforeEach(() => { - vi.stubGlobal('ResizeObserver', ResizeObserverStub) - useAppStore.setState(useAppStore.getInitialState(), true) - ptyId = `pty-${(nextPtyId += 1)}` - paneElement = document.createElement('div') - document.body.appendChild(paneElement) - root = createRoot(paneElement) - openedBeforeClaudeAccounts = vi.fn(() => Promise.resolve(true)) - Object.defineProperty(window, 'api', { - configurable: true, - value: { pty: { openedBeforeClaudeAccounts } } - }) -}) - -afterEach(() => { - act(() => root.unmount()) - paneElement.remove() - vi.unstubAllGlobals() - useAppStore.setState(useAppStore.getInitialState(), true) -}) - -describe('ClaudeOldTerminalBanner', () => { - it('says Claude in an older terminal uses System default, with a new terminal and Learn more', async () => { - setState(SELECTED) - await renderBanner() - expect(openedBeforeClaudeAccounts).toHaveBeenCalledWith(ptyId, { runtime: 'host' }) - expect(paneElement.textContent).toContain(TITLE) - expect(paneElement.textContent).toContain("so claude here uses System default's login.") - button('Open new terminal') - expect(() => button("Don't show again")).toThrow() - - await act(async () => button('Learn more').click()) - expect(document.body.textContent).toContain('Open a new terminal to use the selected account.') - }) - - it('opens a new terminal beside the tab from the banner', async () => { - const openNewTerminalTabInActiveWorkspace = vi.fn(() => Promise.resolve()) - setState(SELECTED) - useAppStore.setState({ - unifiedTabsByWorktree: { - 'wt-1': [ - { - id: 'unified-1', - entityId: TAB_ID, - groupId: 'group-2', - worktreeId: 'wt-1', - contentType: 'terminal', - label: 'Terminal', - customLabel: null, - color: null, - sortOrder: 0, - createdAt: 0 - } - ] - }, - // oxlint-disable-next-line typescript/consistent-type-assertions -- SAFETY: the banner reads only a worktree's id and path. - worktreesByRepo: { 'repo-1': [{ id: 'wt-1', path: '/repos/wt-1' } as Worktree] }, - openNewTerminalTabInActiveWorkspace - }) - await renderBanner() - await act(async () => button('Open new terminal').click()) - expect(openNewTerminalTabInActiveWorkspace).toHaveBeenCalledWith('group-2') - }) - - it('stays hidden for a terminal from this version or after Dismiss', async () => { - setState(SELECTED) - openedBeforeClaudeAccounts.mockResolvedValue(false) - await renderBanner() - expect(paneElement.textContent).toBe('') - - openedBeforeClaudeAccounts.mockResolvedValue(true) - ptyId = `pty-${(nextPtyId += 1)}` - await renderBanner() - await act(async () => button('Dismiss').click()) - expect(paneElement.textContent).toBe('') - }) - - it('never asks without Claude in the pane or a selected account', async () => { - setState(SELECTED, 'codex') - await renderBanner() - setState({ claudeManagedAccounts: [ACCOUNT], activeClaudeManagedAccountId: null }) - await renderBanner() - setState({ claudeManagedAccounts: [], activeClaudeManagedAccountId: 'a' }) - await renderBanner() - // A pane whose tab is unknown has no runtime to judge it by. - setState(SELECTED) - useAppStore.setState({ unifiedTabsByWorktree: {} }) - await renderBanner() - expect(openedBeforeClaudeAccounts).not.toHaveBeenCalled() - expect(paneElement.textContent).toBe('') - }) -}) diff --git a/src/renderer/src/components/terminal-pane/floating-file-link-selection.test.ts b/src/renderer/src/components/terminal-pane/floating-file-link-selection.test.ts index 55d8ab538a9..f1288949d9b 100644 --- a/src/renderer/src/components/terminal-pane/floating-file-link-selection.test.ts +++ b/src/renderer/src/components/terminal-pane/floating-file-link-selection.test.ts @@ -8,7 +8,7 @@ import { registerWorkspaceHttpLinkBrowserOpener } from '@/lib/http-link-routing' import { FLOATING_TERMINAL_WORKTREE_ID, getDefaultSettings } from '../../../../shared/constants' -import { openDetectedFilePath } from './terminal-file-open-routing' + import { buildFileLinkActions, handleTerminalFileLink } from './terminal-file-link-actions' const mocks = vi.hoisted(() => ({ @@ -156,19 +156,6 @@ describe('floating file links preserve main-window selection', () => { expect(mainSelection()).toEqual(before) }) - it('opens a source file directly within the floating workspace', async () => { - const before = mainSelection() - openDetectedFilePath('/Users/me/floating/app.ts', null, null, deps) - await flushOpen() - expect(openFileForLink).toHaveBeenCalledWith( - expect.objectContaining({ worktreeId: FLOATING_TERMINAL_WORKTREE_ID }), - { forceContentReload: true }, - null, - expect.any(Function) - ) - expect(mainSelection()).toEqual(before) - }) - it('reveals a closed floating panel through workspace activation without selecting it in main', () => { useAppStore.setState({ floatingWorkspacePanelOpen: false }) const before = mainSelection() diff --git a/src/renderer/src/components/terminal-pane/pty-connection-pending-spawn.spec.ts b/src/renderer/src/components/terminal-pane/pty-connection-pending-spawn.spec.ts index 2dca36843e8..dcb16842491 100644 --- a/src/renderer/src/components/terminal-pane/pty-connection-pending-spawn.spec.ts +++ b/src/renderer/src/components/terminal-pane/pty-connection-pending-spawn.spec.ts @@ -57,31 +57,6 @@ export function registerPtyConnectionPendingSpawnTests(): void { await restoreTerminalTestGlobals() }) - it('does not retain PTY connect diagnostics unless e2e debug state is enabled', async () => { - const { connectPanePty } = await import('./pty-connection') - const agentTitleMocks = - await vi.importMock('@/lib/agent-status') - const logSpy = vi.spyOn(console, 'log').mockImplementation(() => {}) - const transport = createMockTransport() - transportFactoryQueue.push(transport) - mockStoreState = { - ...mockStoreState, - tabsByWorktree: { 'wt-1': [{ id: 'tab-1', ptyId: null }] }, - ptyIdsByTabId: { 'tab-1': [] } - } - - connectPanePty( - createPane(1) as never, - createManager(1) as never, - bindPaneAgentTitleMocks(createDeps() as never, agentTitleMocks) - ) - await flushAsyncTicks() - - expect((globalThis as Record).__ptyConnectDiag).toBeUndefined() - expect(logSpy).not.toHaveBeenCalledWith(expect.stringContaining('[pty-connect]')) - logSpy.mockRestore() - }, 30_000) - it('does not hold a successor behind a canceled spawn serializer declaration', async () => { const { connectPanePty } = await import('./pty-connection') const agentTitleMocks2 = diff --git a/src/renderer/src/components/terminal-pane/terminal-agent-paste-bracketing.test.ts b/src/renderer/src/components/terminal-pane/terminal-agent-paste-bracketing.test.ts index a7c0cc59e81..d36f37bc8f1 100644 --- a/src/renderer/src/components/terminal-pane/terminal-agent-paste-bracketing.test.ts +++ b/src/renderer/src/components/terminal-pane/terminal-agent-paste-bracketing.test.ts @@ -54,27 +54,6 @@ describe('resolveProtectedMultilinePasteOptionsForPane', () => { expect(decide({ leafId: SHELL_LEAF })).toBeUndefined() }) - it('gates per leaf, not per tab', () => { - expect(decide({ tabId: 'other-tab' })).toBeUndefined() - }) - - it('keeps the existing Windows-client behaviour for non-agent panes', () => { - expect(decide({ isWindowsClient: true, agentStatusByPaneKey: {}, leafId: SHELL_LEAF })).toEqual( - { forceBracketedPasteForMultiline: true } - ) - }) - - it('brackets on a process-confirmed agent even with no status row', () => { - expect( - decide({ - agentStatusByPaneKey: {}, - paneForegroundAgentByPaneKey: { - [AGENT_PANE_KEY]: foregroundEntry({ agent: 'codex', shellForeground: false }) - } - }) - ).toEqual({ forceBracketedPasteForMultiline: true }) - }) - it('vetoes a row rehydrated from disk across an app restart', () => { expect( decide({ @@ -85,17 +64,6 @@ describe('resolveProtectedMultilinePasteOptionsForPane', () => { ).toBeUndefined() }) - it('still brackets a long-idle agent parked at done', () => { - // Idle live agents can stay done past the freshness TTL and still require paste brackets. - expect( - decide({ - agentStatusByPaneKey: { - [AGENT_PANE_KEY]: agentEntry({ state: 'done', updatedAt: 0, stateStartedAt: 0 }) - } - }) - ).toEqual({ forceBracketedPasteForMultiline: true }) - }) - it('degrades to the pre-fix path instead of throwing on a malformed pane key', () => { // Malformed pane keys throw before the paste handler’s catch; require graceful fallback rather than a silent no-op. expect(() => decide({ leafId: 'not-a-uuid' })).not.toThrow() @@ -114,17 +82,6 @@ describe('resolveProtectedMultilinePasteOptionsForPane', () => { }) ).toEqual({ forceBracketedPasteForMultiline: true }) }) - - it('does not change unverified Windows agent paste protocols', () => { - expect( - decide({ - hostPlatform: 'win32', - agentStatusByPaneKey: { - [AGENT_PANE_KEY]: agentEntry({ agentType: 'pi' }) - } - }) - ).toEqual({ forceBracketedPasteForMultiline: true }) - }) }) describe('leading-newline paste into a remote agent pane', () => { @@ -169,16 +126,4 @@ describe('leading-newline paste into a remote agent pane', () => { expect(terminal.input).toHaveBeenCalledWith(`\x1b\r${PASTED.slice(1)}`) expect(terminal.paste).not.toHaveBeenCalled() }) - - it('a single-line paste stays on the direct path', () => { - const plan = planTerminalPaste({ - text: 'no newline here', - source: 'keyboard', - target: remoteWindowsTarget(), - terminalBracketedPasteMode: false, - ...decide({ hostPlatform: 'win32' }) - }) - - expect(plan.mode).toBe('direct') - }) }) diff --git a/src/renderer/src/components/terminal-pane/terminal-agent-session-continuation.test.ts b/src/renderer/src/components/terminal-pane/terminal-agent-session-continuation.test.ts deleted file mode 100644 index 19900d18763..00000000000 --- a/src/renderer/src/components/terminal-pane/terminal-agent-session-continuation.test.ts +++ /dev/null @@ -1,184 +0,0 @@ -import { beforeEach, describe, expect, it, vi } from 'vitest' -import type { ManagedPane } from '@/lib/pane-manager/pane-manager' -import { - buildAgentSessionContinuationPrompt, - hasFullAgentSessionContext -} from '@/lib/agent-session-continuation' -import { - canContinueAgentSessionInNewSession, - prepareAgentSessionContinuationFromPane -} from './terminal-agent-session-continuation' - -const LEAF_ID = '11111111-1111-4111-8111-111111111111' -const store = { - agentStatusByPaneKey: {} as Record< - string, - { - agentType?: string - prompt?: string - lastAssistantMessage?: string - providerSession?: { transcriptPath?: string } - } - >, - tabsByWorktree: {} as Record -} - -vi.mock('@/store', () => ({ useAppStore: { getState: () => store } })) -vi.mock('@/i18n/i18n', () => ({ - translate: (_key: string, fallback: string) => fallback -})) -vi.mock('sonner', () => ({ toast: { error: vi.fn() } })) - -function makePane(capturedText: string): ManagedPane { - return { - leafId: LEAF_ID, - serializeAddon: { serialize: vi.fn(() => capturedText) }, - terminal: { focus: vi.fn() } - } as unknown as ManagedPane -} - -describe('buildAgentSessionContinuationPrompt', () => { - it('supports focused and full modes from a saved transcript', () => { - const source = { - capturedText: 'unused fallback', - sourceAgent: 'claude' as const, - transcriptPath: '/home/u/.claude/projects/repo/session.jsonl', - lastPrompt: 'finish the auth refactor' - } - - const focused = buildAgentSessionContinuationPrompt(source, 'focused') - const full = buildAgentSessionContinuationPrompt(source, 'full') - - expect(focused).toContain('Continue work from the prior Orca session') - expect(focused).toContain('The prior provider session is read-only context') - expect(focused).not.toContain('Start a fresh, independent agent session') - expect(focused).toContain('If the prior task appears complete, say so and wait') - expect(focused).toContain('Read only the transcript sections needed') - expect(full).toContain('Read the complete original session transcript') - expect(full).toContain('/home/u/.claude/projects/repo/session.jsonl') - expect(full).not.toContain('unused fallback') - }) - - it('falls back to bounded terminal context only in focused mode', () => { - const source = { - capturedText: 'User: update settings\nAssistant: editing the form', - sourceAgent: null - } - - expect(buildAgentSessionContinuationPrompt(source, 'focused')).toContain( - 'bounded recent terminal capture' - ) - expect(buildAgentSessionContinuationPrompt(source, 'full')).toBeNull() - }) - it.each(['opencode', 'opencode2'] as const)( - 'uses captured context for %s SQLite references', - (sourceAgent) => { - const source = { - sourceAgent, - transcriptPath: '/home/u/.local/share/opencode/opencode.db#ses_proof', - capturedText: 'user: fix the parser\nassistant: tests remain' - } - expect(hasFullAgentSessionContext(source)).toBe(false) - expect(buildAgentSessionContinuationPrompt(source, 'focused')).toContain('tests remain') - expect(buildAgentSessionContinuationPrompt(source, 'focused')).not.toContain('opencode.db#') - expect(buildAgentSessionContinuationPrompt(source, 'full')).toBeNull() - } - ) - - it('preserves real transcript paths containing a hash', () => { - const source = { - sourceAgent: 'opencode' as const, - transcriptPath: '/home/u/project#one/session.jsonl', - capturedText: 'fallback' - } - expect(hasFullAgentSessionContext(source)).toBe(true) - expect(buildAgentSessionContinuationPrompt(source, 'full')).toContain(source.transcriptPath) - }) -}) - -describe('prepareAgentSessionContinuationFromPane', () => { - it('does not enable session continuation for recognition-only DeepSeek Build', () => { - expect(canContinueAgentSessionInNewSession('dsb')).toBe(false) - }) - - beforeEach(() => { - vi.clearAllMocks() - store.agentStatusByPaneKey = { - [`tab-1:${LEAF_ID}`]: { - agentType: 'claude', - prompt: 'finish the auth refactor', - lastAssistantMessage: 'The tests still need updating.', - providerSession: { transcriptPath: '/home/u/.claude/session.jsonl' } - } - } - store.tabsByWorktree = { 'wt-1': [{ id: 'tab-1', launchAgent: 'claude' }] } - }) - - it('prepares a generic request without serializing when a transcript exists', () => { - const pane = makePane('unused scrollback') - const request = prepareAgentSessionContinuationFromPane({ - pane, - tabId: 'tab-1', - worktreeId: 'wt-1', - groupId: 'group-1', - workspacePath: '/repo/worktree', - initialCwd: '/repo/worktree/packages/app' - }) - - expect(pane.serializeAddon.serialize).not.toHaveBeenCalled() - expect(request).toMatchObject({ - worktreeId: 'wt-1', - groupId: 'group-1', - workspacePath: '/repo/worktree', - initialCwd: '/repo/worktree/packages/app', - source: { - sourceAgent: 'claude', - transcriptPath: '/home/u/.claude/session.jsonl' - } - }) - }) - - it.each(['opencode', 'opencode2'] as const)( - 'captures scrollback for a %s SQLite session', - (sourceAgent) => { - store.agentStatusByPaneKey[`tab-1:${LEAF_ID}`] = { - agentType: sourceAgent, - providerSession: { transcriptPath: '/tmp/opencode.db#ses_proof' } - } - const pane = makePane('latest terminal context') - const request = prepareAgentSessionContinuationFromPane({ - pane, - tabId: 'tab-1', - worktreeId: 'wt-1', - groupId: null, - workspacePath: '/repo', - initialCwd: '/repo' - }) - expect(pane.serializeAddon.serialize).toHaveBeenCalledWith({ scrollback: 800 }) - expect(request?.source.capturedText).toBe('latest terminal context') - expect(buildAgentSessionContinuationPrompt(request!.source, 'focused')).toContain( - 'latest terminal context' - ) - } - ) - - it('falls back to terminal capture when the provider transcript path is blank', () => { - store.agentStatusByPaneKey[`tab-1:${LEAF_ID}`]!.providerSession = { transcriptPath: ' ' } - const pane = makePane('latest terminal context') - - const request = prepareAgentSessionContinuationFromPane({ - pane, - tabId: 'tab-1', - worktreeId: 'wt-1', - groupId: null, - workspacePath: '/repo/worktree', - initialCwd: '/repo/worktree' - }) - - expect(pane.serializeAddon.serialize).toHaveBeenCalledWith({ scrollback: 800 }) - expect(request?.source).toMatchObject({ - capturedText: 'latest terminal context', - transcriptPath: null - }) - }) -}) diff --git a/src/renderer/src/components/terminal-pane/terminal-cold-park-timer-rearm.test.tsx b/src/renderer/src/components/terminal-pane/terminal-cold-park-timer-rearm.test.tsx index 07239dc2ef0..68c491a9736 100644 --- a/src/renderer/src/components/terminal-pane/terminal-cold-park-timer-rearm.test.tsx +++ b/src/renderer/src/components/terminal-pane/terminal-cold-park-timer-rearm.test.tsx @@ -50,7 +50,7 @@ import { useTerminalTabColdParking } from './use-terminal-tab-cold-parking' const TAB_IDS = ['tab-a', 'tab-b', 'tab-c', 'tab-d', 'tab-e'] as const const EMPTY_ASSIGNMENTS = new Map() const EMPTY_PORTALS: never[] = [] -const TITLE_FLOOD_WRITES = 40 + const TICK_MS = 10_000 type ParkingStoreState = { tabsByWorktree: Record } @@ -146,20 +146,6 @@ function firstParkedElapsedMs(options: { publishTitles: boolean }): number { } describe('cold-park recheck timers under a background title flood', () => { - it('re-arms no park timer for tab-model writes that move no deadline', () => { - root = createRoot(container) - act(() => root?.render()) - - const setTimeoutSpy = vi.spyOn(window, 'setTimeout') - const clearTimeoutSpy = vi.spyOn(window, 'clearTimeout') - act(() => root?.render()) - - // Per-write effect runs preserve watcher-coverage wakeups (terminal-cold-park-verdict-loop). - expect(park.effectRuns).toBeGreaterThanOrEqual(TITLE_FLOOD_WRITES) - expect(setTimeoutSpy.mock.calls.length).toBe(0) - expect(clearTimeoutSpy.mock.calls.length).toBe(0) - }) - it('parks at the same instant with and without a title flood', () => { const quiet = firstParkedElapsedMs({ publishTitles: false }) act(() => root?.unmount()) diff --git a/src/renderer/src/components/terminal-pane/terminal-delivery-watchdog.test.ts b/src/renderer/src/components/terminal-pane/terminal-delivery-watchdog.test.ts index 95b00313a97..231d36c546d 100644 --- a/src/renderer/src/components/terminal-pane/terminal-delivery-watchdog.test.ts +++ b/src/renderer/src/components/terminal-pane/terminal-delivery-watchdog.test.ts @@ -164,19 +164,4 @@ describe('terminal delivery watchdog', () => { expect(countHeals()).toBe(2) expect(reattachMock).toHaveBeenCalledTimes(2) }) - - it('never starts without the invoke heal lane (web client, partial mocks)', async () => { - ;(window.api.pty as { reportRendererDeliveryState?: unknown }).reportRendererDeliveryState = - undefined - const watchdog = await import('./terminal-delivery-watchdog') - watchdog.startTerminalDeliveryWatchdog({ - reattachPushListeners: reattachMock, - hasAttachedPtys: () => true - }) - - await vi.advanceTimersByTimeAsync(INTERVAL_MS * 3) - - expect(reattachMock).not.toHaveBeenCalled() - expect(vi.getTimerCount()).toBe(0) - }) }) diff --git a/src/renderer/src/components/terminal-pane/terminal-drop-image-path.test.ts b/src/renderer/src/components/terminal-pane/terminal-drop-image-path.test.ts index 0c98eb491f3..9f8fa95f98b 100644 --- a/src/renderer/src/components/terminal-pane/terminal-drop-image-path.test.ts +++ b/src/renderer/src/components/terminal-pane/terminal-drop-image-path.test.ts @@ -1,197 +1,10 @@ import { describe, expect, it } from 'vitest' -import { - canPasteImageDropPathRaw, - formatImageDropPasteText, - isImageDropPath -} from './terminal-drop-image-path' - -describe('isImageDropPath', () => { - it('detects common image extensions case-insensitively', () => { - for (const path of [ - '/repo/shot.png', - '/repo/shot.PNG', - '/repo/a.jpg', - '/repo/a.jpeg', - '/repo/a.gif', - '/repo/icon.svg', - '/repo/a.webp', - '/repo/a.bmp', - '/repo/a.ico', - 'C:\\Users\\me\\Pictures\\diagram.PnG' - ]) { - expect(isImageDropPath(path)).toBe(true) - } - }) - - it('rejects non-image and extension-less paths', () => { - for (const path of [ - '/repo/index.ts', - '/repo/notes.md', - '/repo/archive.tar.gz', - '/repo/Makefile', - '/repo/.gitignore' - ]) { - expect(isImageDropPath(path)).toBe(false) - } - }) - - it('does not classify directory components with dots as images', () => { - expect(isImageDropPath('/home/jane.png/photo')).toBe(false) - expect(isImageDropPath('/home/jane.doe/screenshot')).toBe(false) - }) -}) - -describe('canPasteImageDropPathRaw', () => { - it('pastes plain names raw', () => { - expect(canPasteImageDropPathRaw('/tmp/orca-paste-1-abc.png', 'posix')).toBe(true) - expect(canPasteImageDropPathRaw('C:\\Temp\\orca-paste-1-abc.png', 'windows')).toBe(true) - }) - - it('treats an ASCII space as unsafe but leaves U+202F alone', () => { - expect( - canPasteImageDropPathRaw('/t/Screenshot 2026-09-28 at 4.03.11\u202fPM.png', 'posix') - ).toBe(false) - expect(canPasteImageDropPathRaw('C:\\My Pictures\\shot.png', 'windows')).toBe(false) - expect(canPasteImageDropPathRaw('/t/4.03.11\u202fPM.png', 'posix')).toBe(true) - }) -}) - -// Mirror agent path parsers so escaping is checked against their accepted syntax. - -// Claude Code 2.1.285 S → x strips outer quotes, unescapes pairs and retains one backslash from a doubled pair. -function claudeCodeRecoverPath(pasted: string): string { - const unquoted = /^(['"]).*\1$/s.test(pasted) ? pasted.slice(1, -1) : pasted - return unquoted.replace(/\\(.)/gs, '$1') -} - -// Codex skips shlex for unquoted drive/UNC paths; other paths must shlex-split into one token. -function codexRecoverPath(pasted: string): string | null { - const unquoted = pasted.replace(/^(['"])(.*)\1$/s, '$2') - if (/^([a-z]:[\\/]|\\\\)/i.test(unquoted)) { - return unquoted - } - const tokens: string[] = [] - let current: string | null = null - let quote: "'" | '"' | null = null - for (let i = 0; i < pasted.length; i += 1) { - const char = pasted[i] - if (quote === "'") { - if (char === "'") { - quote = null - } else { - current += char - } - } else if (quote === '"') { - if (char === '"') { - quote = null - } else if (char === '\\' && /["\\$`]/.test(pasted[i + 1] ?? '')) { - current += pasted[++i] - } else { - current += char - } - } else if (char === ' ' || char === '\t' || char === '\n') { - if (current !== null) { - tokens.push(current) - current = null - } - } else { - current ??= '' - if (char === '\\') { - current += pasted[++i] ?? '' - } else if (char === "'" || char === '"') { - quote = char - } else { - current += char - } - } - } - if (quote !== null) { - return null - } - if (current !== null) { - tokens.push(current) - } - return tokens.length === 1 ? tokens[0] : null -} +import { formatImageDropPasteText } from './terminal-drop-image-path' describe('formatImageDropPasteText', () => { - it('backslash-escapes unsafe POSIX characters and double-quotes on Windows', () => { - expect(formatImageDropPasteText('/t/shot.png', 'posix')).toBe('/t/shot.png') - expect(formatImageDropPasteText("/t/it's (1).png", 'posix')).toBe("/t/it\\'s\\ \\(1\\).png") - expect(formatImageDropPasteText('C:\\My Pictures\\shot.png', 'windows')).toBe( - '"C:\\My Pictures\\shot.png"' - ) - }) - it('refuses paths a paste frame would alter', () => { expect(formatImageDropPasteText('/t/a\nb.png', 'posix')).toBeNull() expect(formatImageDropPasteText('/t/a\u001bb.png', 'posix')).toBeNull() expect(formatImageDropPasteText('C:\\t\\a\u0007b.png', 'windows')).toBeNull() }) }) - -// pi-image-paste 1.0.0 bare tokens preserve escape pairs, stop at JS whitespace (including U+202F), then shell-unescape and extend up to eight words if missing. -function piImagePasteRecoverPath(pasted: string, exists: (path: string) => boolean): string | null { - const unescape = (text: string): string => text.replace(/\\(.)/gs, '$1') - let index = 0 - let raw = '' - while (index < pasted.length && !/\s/.test(pasted[index])) { - if (pasted[index] === '\\' && index + 1 < pasted.length) { - raw += pasted.slice(index, index + 2) - index += 2 - } else { - raw += pasted[index++] - } - } - let value = unescape(raw) - for (let step = 0; step < 8 && !exists(value); step += 1) { - let wordStart = index - while ( - wordStart < pasted.length && - /\s/.test(pasted[wordStart]) && - pasted[wordStart] !== '\n' - ) { - wordStart += 1 - } - let wordEnd = wordStart - while (wordEnd < pasted.length && !/\s/.test(pasted[wordEnd])) { - wordEnd += 1 - } - if (wordStart === index || wordEnd === wordStart) { - break - } - value += pasted.slice(index, wordStart) + unescape(pasted.slice(wordStart, wordEnd)) - index = wordEnd - } - return exists(value) ? value : null -} - -describe('agent recovery of pasted image paths', () => { - const screenshot = '/t/Screenshot 2026-09-28 at 4.03.11\u202fPM.png' - - it.each([ - screenshot, - "/t/it's.png", - '/t/download (1).png', - '/t/a.png; touch /tmp/pwned #.png', - '/t/"q" $HOME `x` \\ [1] {2} *?!&|<>~.png' - ])('recovers %j in Claude Code, Codex and Pi', (path) => { - const pasted = formatImageDropPasteText(path, 'posix') ?? '' - expect(claudeCodeRecoverPath(pasted)).toBe(path) - expect(codexRecoverPath(pasted)).toBe(path) - expect(piImagePasteRecoverPath(pasted, (candidate) => candidate === path)).toBe(path) - }) - - it("shows why the quoted `'\\''` form was not used: Claude Code corrupts it", () => { - expect(claudeCodeRecoverPath("'/t/it'\\''s.png'")).toBe("/t/it'''s.png") - }) - - it('recovers a double-quoted spaced Windows path in Codex', () => { - const path = 'C:\\Users\\me\\My Pictures\\shot.png' - expect(codexRecoverPath(formatImageDropPasteText(path, 'windows') ?? '')).toBe(path) - }) - - it('shows why a raw spaced path is not enough for Codex', () => { - expect(codexRecoverPath(screenshot)).toBeNull() - }) -}) diff --git a/src/renderer/src/components/terminal-pane/terminal-parked-watcher-reconciliation.test.ts b/src/renderer/src/components/terminal-pane/terminal-parked-watcher-reconciliation.test.ts index 161aa0d7d2e..e073150d65c 100644 --- a/src/renderer/src/components/terminal-pane/terminal-parked-watcher-reconciliation.test.ts +++ b/src/renderer/src/components/terminal-pane/terminal-parked-watcher-reconciliation.test.ts @@ -4,10 +4,7 @@ import { pruneParkedTerminalWatchers, retireParkedTerminalTab } from './terminal-parked-watcher-registry' -import { - reconcileParkedWatcherPtyIds, - resolveParkedTerminalPaneCandidates -} from './terminal-parked-watcher-reconciliation' +import { resolveParkedTerminalPaneCandidates } from './terminal-parked-watcher-reconciliation' import { readTerminalScrollIntentKeyRetention, writeKeyedTerminalScrollIntent @@ -70,25 +67,6 @@ describe('paired parked-watcher reconciliation', () => { } ]) }) - - it('surgically reconciles a reminted split leaf without restarting its sibling', () => { - expect( - reconcileParkedWatcherPtyIds({ - currentTabPtyId: FIRST_PTY_ID, - entryTabPtyId: FIRST_PTY_ID, - paneIdByPtyId: new Map([ - [FIRST_PTY_ID, 1], - [OLD_SECOND_PTY_ID, 2] - ]), - expectedPtyIds: new Set([FIRST_PTY_ID, NEW_SECOND_PTY_ID]) - }) - ).toEqual({ - restartAll: false, - addedPtyIds: [NEW_SECOND_PTY_ID], - retainedPtyIds: [FIRST_PTY_ID], - retiredPaneIds: [2] - }) - }) }) it('releases captured scroll-intent keys when a parked tab is closed', () => { @@ -139,33 +117,6 @@ describe('untouchedFreshSpawn carry through the layout-fallback rescue', () => { } } - it('keeps the captured fact for the same PTY when the rescue runs', () => { - // Stale capture (an extra pane the layout no longer has) forces the rescue path. - captureParkedTerminalPaneCandidates(TAB_ID, WORKTREE_ID, [ - { - ptyId: FIRST_PTY_ID, - paneId: 1, - leafId: FIRST_LEAF_ID, - drivesTabTitle: true, - untouchedFreshSpawn: true, - workspaceOwner: { executionHostId: 'ssh:qa', runtimeEnvironmentId: 'env-1' } - }, - { ptyId: OLD_SECOND_PTY_ID, paneId: 2, leafId: SECOND_LEAF_ID, drivesTabTitle: false } - ]) - - const panes = resolveParkedTerminalPaneCandidates( - { id: TAB_ID, ptyId: FIRST_PTY_ID }, - soleLeafState - ) - - expect(panes).toHaveLength(1) - expect(panes[0].untouchedFreshSpawn).toBe(true) - expect(panes[0].workspaceOwner).toEqual({ - executionHostId: 'ssh:qa', - runtimeEnvironmentId: 'env-1' - }) - }) - it('drops the fact when the leaf re-minted a different PTY', () => { captureParkedTerminalPaneCandidates(TAB_ID, WORKTREE_ID, [ { diff --git a/src/renderer/src/components/terminal-pane/use-terminal-scroll-visibility-memory.test.ts b/src/renderer/src/components/terminal-pane/use-terminal-scroll-visibility-memory.test.ts index 3cd22968f4b..f779e03753c 100644 --- a/src/renderer/src/components/terminal-pane/use-terminal-scroll-visibility-memory.test.ts +++ b/src/renderer/src/components/terminal-pane/use-terminal-scroll-visibility-memory.test.ts @@ -99,39 +99,6 @@ describe('useTerminalScrollVisibilityMemory', () => { } }) - it('bounds follow-output flushes when applying pending requests', () => { - const terminal = { - onScroll: vi.fn(() => ({ dispose: vi.fn() })), - scrollToBottom: vi.fn() - } - const manager = { - getPanes: vi.fn(() => [{ id: 1, terminal }]) - } - const animationFrames: FrameRequestCallback[] = [] - globalThis.requestAnimationFrame = vi.fn((callback: FrameRequestCallback) => { - animationFrames.push(callback) - return animationFrames.length - }) - - beginHookRender() - const visibilityMemory = useTerminalScrollVisibilityMemory({ - managerRef: { current: manager as never }, - isVisibleRef: { current: true }, - visibleResumeCompleteRef: { current: true }, - paneCount: 1 - }) - - visibilityMemory.scheduleFollowOutputIfNeeded(1) - animationFrames.shift()?.(16) - animationFrames.shift()?.(32) - - expect(mocks.flushTerminalOutput).toHaveBeenCalledWith(terminal, { - maxChars: 256 * 1024 - }) - expect(terminal.scrollToBottom).toHaveBeenCalled() - expect(mocks.markTerminalFollowOutput).toHaveBeenCalledWith(terminal) - }) - it('does not turn a pinned viewport into follow-output when pending focus requests catch up', () => { mocks.getTerminalScrollIntentKind.mockReturnValue('pinnedViewport') const terminal = { @@ -166,31 +133,4 @@ describe('useTerminalScrollVisibilityMemory', () => { expect(mocks.markTerminalFollowOutput).not.toHaveBeenCalled() expect(terminal.scrollToBottom).not.toHaveBeenCalled() }) - - it('cancels pending follow-output frames on cleanup', () => { - const terminal = { - onScroll: vi.fn(() => ({ dispose: vi.fn() })), - scrollToBottom: vi.fn() - } - const manager = { - getPanes: vi.fn(() => [{ id: 1, terminal }]) - } - const cancelAnimationFrame = vi.fn() - globalThis.requestAnimationFrame = vi.fn(() => 7) - globalThis.cancelAnimationFrame = cancelAnimationFrame - - beginHookRender() - const visibilityMemory = useTerminalScrollVisibilityMemory({ - managerRef: { current: manager as never }, - isVisibleRef: { current: true }, - visibleResumeCompleteRef: { current: true }, - paneCount: 1 - }) - - visibilityMemory.scheduleFollowOutputIfNeeded(1) - runEffectCleanups() - - expect(cancelAnimationFrame).toHaveBeenCalledWith(7) - expect(mocks.flushTerminalOutput).not.toHaveBeenCalled() - }) }) diff --git a/src/renderer/src/components/terminal-pane/xterm-bypass-policy.test.ts b/src/renderer/src/components/terminal-pane/xterm-bypass-policy.test.ts index 74122546a5f..dabdce0abc0 100644 --- a/src/renderer/src/components/terminal-pane/xterm-bypass-policy.test.ts +++ b/src/renderer/src/components/terminal-pane/xterm-bypass-policy.test.ts @@ -61,21 +61,6 @@ describe('shouldBypassXtermKeyboardEvent — macOS', () => { ).toBe(true) }) - it('does not bubble already-handled Ctrl chords on macOS', () => { - expect( - shouldBypassXtermKeyboardEvent( - event({ key: 'c', code: 'KeyC', defaultPrevented: true, ctrlKey: true }), - opts - ) - ).toBe(false) - }) - - it('bubbles Shift+non-ASCII printable text so the active keyboard layout wins', () => { - expect( - shouldBypassXtermKeyboardEvent(event({ key: 'Ф', code: 'KeyA', shiftKey: true }), opts) - ).toBe(true) - }) - it('bubbles Shift+non-ASCII keyup so kitty does not emit a Latin release sequence', () => { expect( shouldBypassXtermKeyboardEvent( @@ -93,12 +78,6 @@ describe('shouldBypassXtermKeyboardEvent — macOS', () => { ) ).toBe(false) }) - - it('leaves ordinary Shift+Space available to the terminal', () => { - expect( - shouldBypassXtermKeyboardEvent(event({ key: ' ', code: 'Space', shiftKey: true }), opts) - ).toBe(false) - }) }) describe('shouldSuppressTerminalImeKeyboardEvent — macOS', () => { @@ -117,15 +96,6 @@ describe('shouldSuppressTerminalImeKeyboardEvent — macOS', () => { pendingCandidateKeyReleaseActive: false } - it('suppresses keyboard events while Chromium reports active IME composition', () => { - expect( - shouldSuppressTerminalImeKeyboardEvent( - event({ key: 'Backspace', code: 'Backspace', isComposing: true }), - idle - ) - ).toBe(true) - }) - it('lets standalone Process keys reach xterm so its CompositionHelper can diff text', () => { expect( shouldSuppressTerminalImeKeyboardEvent( @@ -153,36 +123,6 @@ describe('shouldSuppressTerminalImeKeyboardEvent — macOS', () => { ).toBe(true) }) - it('suppresses Process keys while the terminal composition tracker is active', () => { - expect( - shouldSuppressTerminalImeKeyboardEvent( - event({ key: 'Process', code: 'KeyN', keyCode: 229 }), - composing - ) - ).toBe(true) - }) - - it('suppresses IME-owned editing keys while composition is active', () => { - expect( - shouldSuppressTerminalImeKeyboardEvent( - event({ key: 'Backspace', code: 'Backspace' }), - composing - ) - ).toBe(true) - expect( - shouldSuppressTerminalImeKeyboardEvent( - event({ key: 'ArrowDown', code: 'ArrowDown' }), - composing - ) - ).toBe(true) - }) - - it('does not suppress ordinary text keys solely because composition is active', () => { - expect( - shouldSuppressTerminalImeKeyboardEvent(event({ key: 'a', code: 'KeyA' }), composing) - ).toBe(false) - }) - it('does not suppress keypress events because they carry committed text', () => { expect( shouldSuppressTerminalImeKeyboardEvent( diff --git a/src/renderer/src/components/terminal/tab-type-cycle.test.ts b/src/renderer/src/components/terminal/tab-type-cycle.test.ts index 3db556cb086..29364a2ccdd 100644 --- a/src/renderer/src/components/terminal/tab-type-cycle.test.ts +++ b/src/renderer/src/components/terminal/tab-type-cycle.test.ts @@ -1,106 +1,7 @@ import { describe, expect, it } from 'vitest' -import { - getNextTabAcrossAllTypes, - getNextTabWithinActiveType, - type TypeCyclableTab -} from './tab-type-cycle' - -const mixedTabs: TypeCyclableTab[] = [ - { type: 'terminal', id: 'term-1' }, - { type: 'editor', id: 'file-1', tabId: 'tab-file-1' }, - { type: 'browser', id: 'browser-1' }, - { type: 'terminal', id: 'term-2' }, - { type: 'editor', id: 'file-2', tabId: 'tab-file-2' }, - { type: 'browser', id: 'browser-2' } -] +import { getNextTabAcrossAllTypes, getNextTabWithinActiveType } from './tab-type-cycle' describe('getNextTabWithinActiveType', () => { - it('cycles only terminal tabs when a terminal is active', () => { - expect( - getNextTabWithinActiveType({ - tabs: mixedTabs, - activeTabType: 'terminal', - activeTabId: 'term-1', - activeFileId: 'file-1', - activeBrowserTabId: 'browser-1', - direction: 1 - }) - ).toEqual({ type: 'terminal', id: 'term-2' }) - }) - - it('cycles only editor tabs when an editor is active', () => { - expect( - getNextTabWithinActiveType({ - tabs: mixedTabs, - activeTabType: 'editor', - activeTabId: 'term-1', - activeFileId: 'file-1', - activeBrowserTabId: 'browser-1', - direction: 1 - }) - ).toEqual({ type: 'editor', id: 'file-2', tabId: 'tab-file-2' }) - }) - - it('cycles only browser tabs when a browser is active', () => { - expect( - getNextTabWithinActiveType({ - tabs: mixedTabs, - activeTabType: 'browser', - activeTabId: 'term-1', - activeFileId: 'file-1', - activeBrowserTabId: 'browser-1', - direction: 1 - }) - ).toEqual({ type: 'browser', id: 'browser-2' }) - }) - - it('returns null when the active type has a single tab', () => { - expect( - getNextTabWithinActiveType({ - tabs: [ - { type: 'terminal', id: 'term-1' }, - { type: 'editor', id: 'file-1' }, - { type: 'browser', id: 'browser-1' } - ], - activeTabType: 'browser', - activeTabId: 'term-1', - activeFileId: 'file-1', - activeBrowserTabId: 'browser-1', - direction: 1 - }) - ).toBeNull() - }) - - it('uses direction-aware fallback when the active tab is missing from the active type', () => { - const terminalTabs: TypeCyclableTab[] = [ - { type: 'terminal', id: 'term-1' }, - { type: 'terminal', id: 'term-2' }, - { type: 'terminal', id: 'term-3' } - ] - - expect( - getNextTabWithinActiveType({ - tabs: terminalTabs, - activeTabType: 'terminal', - activeTabId: null, - activeFileId: 'file-1', - activeBrowserTabId: 'browser-1', - direction: -1 - }) - ).toEqual({ type: 'terminal', id: 'term-3' }) - - expect( - getNextTabWithinActiveType({ - tabs: terminalTabs, - activeTabType: 'terminal', - activeTabId: null, - activeFileId: 'file-1', - activeBrowserTabId: 'browser-1', - direction: 1 - }) - ).toEqual({ type: 'terminal', id: 'term-1' }) - }) - it('uses the active group tab id for split editor duplicates', () => { expect( getNextTabWithinActiveType({ @@ -122,58 +23,6 @@ describe('getNextTabWithinActiveType', () => { }) describe('getNextTabAcrossAllTypes', () => { - it('cycles to the next tab regardless of type when a terminal is active', () => { - expect( - getNextTabAcrossAllTypes({ - tabs: mixedTabs, - activeTabType: 'terminal', - activeTabId: 'term-1', - activeFileId: 'file-1', - activeBrowserTabId: 'browser-1', - direction: 1 - }) - ).toEqual({ type: 'editor', id: 'file-1', tabId: 'tab-file-1' }) - }) - - it('wraps from the last tab back to the first', () => { - expect( - getNextTabAcrossAllTypes({ - tabs: mixedTabs, - activeTabType: 'browser', - activeTabId: 'term-1', - activeFileId: 'file-1', - activeBrowserTabId: 'browser-2', - direction: 1 - }) - ).toEqual({ type: 'terminal', id: 'term-1' }) - }) - - it('cycles backward across types', () => { - expect( - getNextTabAcrossAllTypes({ - tabs: mixedTabs, - activeTabType: 'editor', - activeTabId: 'term-1', - activeFileId: 'file-1', - activeBrowserTabId: 'browser-1', - direction: -1 - }) - ).toEqual({ type: 'terminal', id: 'term-1' }) - }) - - it('returns null when only one tab exists total', () => { - expect( - getNextTabAcrossAllTypes({ - tabs: [{ type: 'terminal', id: 'term-1' }], - activeTabType: 'terminal', - activeTabId: 'term-1', - activeFileId: null, - activeBrowserTabId: null, - direction: 1 - }) - ).toBeNull() - }) - it('prefers the active group tab id to disambiguate split duplicates', () => { expect( getNextTabAcrossAllTypes({ @@ -192,28 +41,4 @@ describe('getNextTabAcrossAllTypes', () => { }) ).toEqual({ type: 'browser', id: 'browser-1', tabId: 'tab-browser-1' }) }) - - it('uses direction-aware fallback when the active tab id is missing', () => { - expect( - getNextTabAcrossAllTypes({ - tabs: mixedTabs, - activeTabType: 'terminal', - activeTabId: 'nonexistent', - activeFileId: null, - activeBrowserTabId: null, - direction: 1 - }) - ).toEqual(mixedTabs[0]) - - expect( - getNextTabAcrossAllTypes({ - tabs: mixedTabs, - activeTabType: 'terminal', - activeTabId: 'nonexistent', - activeFileId: null, - activeBrowserTabId: null, - direction: -1 - }) - ).toEqual(mixedTabs.at(-1)) - }) }) diff --git a/src/renderer/src/components/worktree-jump/WorktreeJumpPalette.mount-gating.test.tsx b/src/renderer/src/components/worktree-jump/WorktreeJumpPalette.mount-gating.test.tsx deleted file mode 100644 index 42ebb07236e..00000000000 --- a/src/renderer/src/components/worktree-jump/WorktreeJumpPalette.mount-gating.test.tsx +++ /dev/null @@ -1,198 +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 * as ReactI18Next from 'react-i18next' -import { useAppStore } from '@/store/index' -import WorktreeJumpPalette from './WorktreeJumpPalette' - -const contentProbe = vi.hoisted(() => ({ - renders: vi.fn(), - storeNotifications: vi.fn(), - subscriptions: vi.fn(), - unsubscriptions: vi.fn() -})) - -vi.mock('react-i18next', async (importOriginal) => { - const actual = await importOriginal() - return { - ...actual, - useTranslation: () => ({ - t: (_key: string, fallback?: string) => fallback ?? _key - }) - } -}) - -vi.mock('sonner', () => ({ - toast: { - success: vi.fn(), - error: vi.fn(), - info: vi.fn(), - warning: vi.fn(), - message: vi.fn() - } -})) - -vi.mock('@/hooks/useSettingsNavigationMetadata', async () => { - const React = await import('react') - return { - useSettingsNavigationMetadata: () => { - contentProbe.renders() - React.useEffect(() => { - contentProbe.subscriptions() - const unsubscribe = useAppStore.subscribe(() => contentProbe.storeNotifications()) - return () => { - contentProbe.unsubscriptions() - unsubscribe() - } - }, []) - return [] - } - } -}) - -vi.mock('@/components/sidebar/workspace-card/surface/StatusIndicator', () => ({ - default: () => -})) - -vi.mock('@/components/repo/RepoBadgeLabel', () => ({ - RepoBadgeMark: () => -})) - -vi.mock('@/components/cmd-j/palette-host-badge', () => ({ - getPaletteHostBadge: () => null -})) - -vi.mock('@/components/ui/command', async () => { - const React = await import('react') - return { - Command: ({ children }: { children: React.ReactNode }) =>
{children}
, - CommandGroup: ({ children }: { children: React.ReactNode }) =>
{children}
, - CommandDialog: ({ children, open }: { children: React.ReactNode; open?: boolean }) => - open ?
{children}
: null, - CommandInput: React.forwardRef(function CommandInput( - _props: Record, - ref: React.ForwardedRef - ) { - return - }), - CommandList: React.forwardRef(function CommandList( - { children }: { children: React.ReactNode }, - ref: React.ForwardedRef - ) { - return
{children}
- }), - CommandEmpty: ({ children }: { children: React.ReactNode }) =>
{children}
, - CommandItem: ({ children }: { children: React.ReactNode }) => ( - - ) - } -}) - -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('WorktreeJumpPalette 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() - 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('mounts content on demand and unmounts it after the close linger', 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('worktree-palette') - }) - await flushEffects() - - expect(testContainer.querySelector('[data-command-dialog="true"]')).not.toBeNull() - expect(contentProbe.renders).toHaveBeenCalled() - expect(activeContentSubscriptions()).toBe(1) - - await act(async () => { - useAppStore.getState().closeModal() - }) - - expect(testContainer.querySelector('[data-command-dialog="true"]')).toBeNull() - expect(activeContentSubscriptions()).toBe(1) - - await act(async () => vi.advanceTimersByTimeAsync(299)) - expect(activeContentSubscriptions()).toBe(1) - - const lingerNotifications = contentProbe.storeNotifications.mock.calls.length - await act(async () => useAppStore.setState({ activeWorktreeId: 'during-close-linger' })) - expect(contentProbe.storeNotifications).toHaveBeenCalledTimes(lingerNotifications + 1) - - 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(100) - - 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('worktree-palette')) - await flushEffects() - - await act(async () => useAppStore.getState().closeModal()) - await act(async () => vi.advanceTimersByTimeAsync(299)) - await act(async () => useAppStore.getState().openModal('worktree-palette')) - await act(async () => vi.advanceTimersByTimeAsync(1_000)) - - expect(testContainer.querySelector('[data-command-dialog="true"]')).not.toBeNull() - expect(activeContentSubscriptions()).toBe(1) - }) -}) diff --git a/src/renderer/src/hooks/ipc-events/browser-request-ipc-bridge.split.test.ts b/src/renderer/src/hooks/ipc-events/browser-request-ipc-bridge.split.test.ts index aec3b74e5ef..826b3e654ab 100644 --- a/src/renderer/src/hooks/ipc-events/browser-request-ipc-bridge.split.test.ts +++ b/src/renderer/src/hooks/ipc-events/browser-request-ipc-bridge.split.test.ts @@ -76,38 +76,6 @@ describe('browser tab create with a split pane', () => { registerBrowserRequestIpcBridge([]) }) - it("opens the tab in a new pane beside the caller's terminal without taking focus", () => { - mocks.createListener?.({ - requestId: 'r1', - worktreeId: WT, - url: 'https://example.com', - split: 'right', - splitFromTabId: 'term-1' - }) - - const options = mocks.createBrowserTab.mock.calls[0]?.[2] - expect(options).toMatchObject({ - split: { sourceGroupId: 'g-terminal', splitDirection: 'right', activate: false }, - activate: false - }) - expect(options).not.toHaveProperty('targetGroupId') - expect(mocks.replyTabCreate).toHaveBeenCalledWith({ requestId: 'r1', browserPageId: 'ws-new' }) - }) - - it('splits beside the active pane when the caller tab is unknown', () => { - mocks.createListener?.({ - requestId: 'r2', - worktreeId: WT, - url: 'about:blank', - split: 'down', - splitFromTabId: 'stale-tab' - }) - - expect(mocks.createBrowserTab.mock.calls[0]?.[2]).toMatchObject({ - split: { sourceGroupId: 'g-active', splitDirection: 'down' } - }) - }) - it("opens in the caller's workspace when the CLI names none", () => { mocks.getState.mockReturnValue({ ...storeState(), activeWorktreeId: 'other-workspace' }) diff --git a/src/renderer/src/hooks/use-terminal-quick-command-hosts.test.ts b/src/renderer/src/hooks/use-terminal-quick-command-hosts.test.ts index e4949d955cc..49d648aa885 100644 --- a/src/renderer/src/hooks/use-terminal-quick-command-hosts.test.ts +++ b/src/renderer/src/hooks/use-terminal-quick-command-hosts.test.ts @@ -154,32 +154,4 @@ describe('useTerminalQuickCommandHosts', () => { expect(testState.loadRuntimeTerminalQuickCommands).toHaveBeenCalledWith('build') } ) - - it('keeps mutations pending until remote capability ownership resolves', async () => { - await act(async () => root.render(createElement(Probe))) - - expect(renderedHosts.map((host) => host.hostId)).toEqual(['local']) - expect(remoteHostPending).toBe(true) - }) - - it('distinguishes an unresolved host failure from active loading', async () => { - testState.runtimeTerminalQuickCommands = new Map([ - [ - 'build', - { - commands: [], - connectionGeneration: 4, - error: 'offline', - loading: false, - ready: false, - supported: null - } - ] - ]) - - await act(async () => root.render(createElement(Probe))) - - expect(remoteHostPending).toBe(true) - expect(remoteHostLoadFailed).toBe(true) - }) }) diff --git a/src/renderer/src/lib/agent-launch-route-registered-agents.test.ts b/src/renderer/src/lib/agent-launch-route-registered-agents.test.ts index 82507bf456a..6d1cd0e87c5 100644 --- a/src/renderer/src/lib/agent-launch-route-registered-agents.test.ts +++ b/src/renderer/src/lib/agent-launch-route-registered-agents.test.ts @@ -95,12 +95,6 @@ beforeEach(() => { }) describe('structured launch of a host-registered agent', () => { - it('opens an unchosen chat with empty native installation inventory', async () => { - await loadHostStructuredAgents('local', REGISTERED, null) - - expect(routeFor(store(), 'grok')).toBe('structured-native-chat') - }) - it('never offers it from a host without the registered-agents capability', async () => { await loadHostStructuredAgents('local', REGISTERED, null) mocks.readLocalRuntimeCapabilitiesOrUnknown.mockReturnValue(STRUCTURED) diff --git a/src/renderer/src/lib/browser-history-match.test.ts b/src/renderer/src/lib/browser-history-match.test.ts index e770d86a559..02b843f7aea 100644 --- a/src/renderer/src/lib/browser-history-match.test.ts +++ b/src/renderer/src/lib/browser-history-match.test.ts @@ -50,103 +50,6 @@ describe('browser history match', () => { { tier: 'url-tail', url: 'https://example.com/?ref=git' } ]) }) - - it('drops url-tail matches for the omnibox and keeps them for the address bar', () => { - expect(match(GIT_CORPUS, 'git', { includeUrlTail: false }).map((row) => row.tier)).toEqual([ - 'host-prefix', - 'host-substring', - 'title' - ]) - expect(match(GIT_CORPUS, 'git', { includeUrlTail: true })).toHaveLength(4) - }) - - it('ranks by frecency inside a tier and clamps the visit-count bonus at 50', () => { - const rows = match( - [ - entry({ - url: 'https://acme.dev/clamped', - visitCount: 5000, - lastVisitedAt: NOW - 23 * HOUR - }), - entry({ url: 'https://acme.dev/fresh', visitCount: 50, lastVisitedAt: NOW }) - ], - 'acme' - ) - - expect(rows.map((row) => row.url)).toEqual([ - 'https://acme.dev/fresh', - 'https://acme.dev/clamped' - ]) - }) - - it('floors the recency contribution at zero rather than penalising old entries', () => { - const rows = match( - [ - entry({ - url: 'https://acme.dev/ancient', - visitCount: 3, - lastVisitedAt: NOW - 10_000 * HOUR - }), - entry({ url: 'https://acme.dev/old', visitCount: 2, lastVisitedAt: NOW - 100 * HOUR }) - ], - 'acme' - ) - - expect(rows.map((row) => row.url)).toEqual(['https://acme.dev/ancient', 'https://acme.dev/old']) - }) - - it('breaks ties on lastVisitedAt so ordering cannot wobble between renders', () => { - const tied = [ - entry({ url: 'https://acme.dev/a', visitCount: 1, lastVisitedAt: NOW - 200 * HOUR }), - entry({ url: 'https://acme.dev/b', visitCount: 1, lastVisitedAt: NOW - 100 * HOUR }) - ] - const first = match(tied, 'acme') - - expect(first.map((row) => row.url)).toEqual(['https://acme.dev/b', 'https://acme.dev/a']) - expect(match(tied, 'acme')).toEqual(first) - expect(match(tied.toReversed(), 'acme')).toEqual(first) - }) - - it('breaks a full tie on the url so a reordered snapshot keeps the same order', () => { - const tied = [ - entry({ url: 'https://acme.dev/b', visitCount: 3, lastVisitedAt: NOW - 5 * HOUR }), - entry({ url: 'https://acme.dev/a', visitCount: 3, lastVisitedAt: NOW - 5 * HOUR }) - ] - - const expected = ['https://acme.dev/a', 'https://acme.dev/b'] - expect(match(tied, 'acme').map((row) => row.url)).toEqual(expected) - expect(match(tied.toReversed(), 'acme').map((row) => row.url)).toEqual(expected) - }) - - it('survives an unparseable url by falling back to url and title matching', () => { - const broken = entry({ url: 'not a url at all', title: 'Broken' }) - - expect(match([broken], 'url at')).toEqual([{ tier: 'url-tail', url: 'not a url at all' }]) - expect(match([broken], 'brok')).toEqual([{ tier: 'title', url: 'not a url at all' }]) - }) - - it('promotes a path prefix to the top tier when the entry has no host', () => { - const doc = entry({ url: '/repo/docs/report.html', title: 'Quarterly Report' }) - - expect(match([doc], '/repo/docs')).toEqual([ - { tier: 'host-prefix', url: '/repo/docs/report.html' } - ]) - expect(match([doc], 'docs/report')).toEqual([ - { tier: 'url-tail', url: '/repo/docs/report.html' } - ]) - }) - - it('clamps the recency bonus so a future lastVisitedAt cannot outrank a fresh visit', () => { - const rows = match( - [ - entry({ url: 'https://acme.dev/future', visitCount: 1, lastVisitedAt: NOW + 500 * HOUR }), - entry({ url: 'https://acme.dev/now', visitCount: 2, lastVisitedAt: NOW }) - ], - 'acme' - ) - - expect(rows.map((row) => row.url)).toEqual(['https://acme.dev/now', 'https://acme.dev/future']) - }) }) describe('rankBrowserTopSites', () => { diff --git a/src/renderer/src/lib/ime-text-field.test.tsx b/src/renderer/src/lib/ime-text-field.test.tsx index 4f2d5681e40..92748ea9b8e 100644 --- a/src/renderer/src/lib/ime-text-field.test.tsx +++ b/src/renderer/src/lib/ime-text-field.test.tsx @@ -117,18 +117,6 @@ describe.each([Input, Textarea, ImeInput, ImeTextarea])('IME text field %s', (Fi } }) - it('preserves ordinary keys, Shift+Enter, and modifier submits after a confirmation', () => { - const action = vi.fn() - const { getByRole } = render() - const field = getByRole('textbox') - fireEvent.keyDown(field, { ...enter, keyCode: 229 }) - expect(fireEvent.keyDown(field, { ...enter, shiftKey: true })).toBe(true) - expect(fireEvent.keyDown(field, { ...enter, ctrlKey: true })).toBe(true) - fireEvent.keyDown(field, { key: 'Escape' }) - fireEvent.keyDown(field, { key: 'a' }) - expect(action).toHaveBeenCalledTimes(4) - }) - it('does not carry ownership into a replaced field', () => { const action = vi.fn() const { getByRole, rerender } = render() diff --git a/src/renderer/src/lib/pane-manager/pane-viewport-present-lifetime.test.ts b/src/renderer/src/lib/pane-manager/pane-viewport-present-lifetime.test.ts index 82e26db86c6..6dd6c24e7f7 100644 --- a/src/renderer/src/lib/pane-manager/pane-viewport-present-lifetime.test.ts +++ b/src/renderer/src/lib/pane-manager/pane-viewport-present-lifetime.test.ts @@ -10,7 +10,7 @@ import { isTerminalLeafId } from '../../../../shared/workspace-layout/stable-pan import type { ManagedPaneInternal } from './pane-manager-types' import { disposePane } from './pane-lifecycle' import { toPublicPane } from './pane-public-view' -import { cancelPendingTerminalViewportPresents, presentPaneViewport } from './pane-viewport-present' +import { presentPaneViewport } from './pane-viewport-present' import { requestFullViewportPresent } from './terminal-render-pause-release' vi.mock('./terminal-render-pause-release', () => ({ @@ -151,39 +151,4 @@ describe('pane viewport retry lifetime', () => { expect(requestFullViewportPresent).toHaveBeenCalledOnce() expect(requestFullViewportPresent).toHaveBeenCalledWith(live.terminal) }) - - it('deduplicates retries for the same wrapper', () => { - const pane = createPane() - const view = toPublicPane(pane) - presentPaneViewport(view) - presentPaneViewport(view) - expect(frames.size).toBe(1) - pane.container.style.display = 'block' - flushFrame() - expect(requestFullViewportPresent).toHaveBeenCalledOnce() - }) - - it('preserves the sixteen-frame budget for a live collapsed pane', () => { - const pane = createPane() - presentPaneViewport(toPublicPane(pane)) - for (let tick = 0; tick < 15; tick++) { - flushFrame() - expect(frames.size).toBe(1) - } - flushFrame() - expect(frames.size).toBe(0) - pane.container.style.display = 'block' - presentPaneViewport(toPublicPane(pane)) - expect(requestFullViewportPresent).toHaveBeenCalledOnce() - }) - - it('allows a cancelled live wrapper to schedule again', () => { - const pane = createPane() - const view = toPublicPane(pane) - presentPaneViewport(view) - cancelPendingTerminalViewportPresents(pane.terminal) - expect(frames.size).toBe(0) - presentPaneViewport(view) - expect(frames.size).toBe(1) - }) }) diff --git a/src/renderer/src/lib/shutdown-checkpoint-guard.test.ts b/src/renderer/src/lib/shutdown-checkpoint-guard.test.ts index cad4c2f70b0..70f1ca354a8 100644 --- a/src/renderer/src/lib/shutdown-checkpoint-guard.test.ts +++ b/src/renderer/src/lib/shutdown-checkpoint-guard.test.ts @@ -7,7 +7,6 @@ import { } from './shutdown-checkpoint-guard' import { consumeShutdownCheckpointFailureReason, - ORCA_RENDERER_SHUTDOWN_CHECKPOINT_FAILED_EVENT, ORCA_RENDERER_UNLOAD_PREVENTED_EVENT } from '../../../shared/renderer-shutdown-events' @@ -18,47 +17,6 @@ describe('createShutdownCheckpointGuard', () => { consumeShutdownCheckpointFailureReason() }) - it('dedupes the synthetic and native unload events in one close attempt', () => { - const persist = vi.fn() - const guard = createShutdownCheckpointGuard(persist) - - expect(guard.persistOnce()).toBe(true) - expect(guard.persistOnce()).toBe(true) - - expect(persist).toHaveBeenCalledTimes(1) - }) - - it('publishes the failure cause and records a crash breadcrumb (STA-5505)', () => { - const recordBreadcrumb = vi.fn() - ;(window as unknown as { api: unknown }).api = { crashReports: { recordBreadcrumb } } - vi.spyOn(console, 'error').mockImplementation(() => {}) - const guard = createShutdownCheckpointGuard(() => { - throw new Error('sendSync payload rejected') - }) - - expect(guard.persistOnce()).toBe(false) - - expect(consumeShutdownCheckpointFailureReason()).toBe('sendSync payload rejected') - expect(recordBreadcrumb).toHaveBeenCalledWith({ - name: 'renderer_shutdown_checkpoint_failed', - data: { message: 'sendSync payload rejected' } - }) - }) - - it('clears a stale failure cause once a later checkpoint succeeds (STA-5505)', () => { - vi.spyOn(console, 'error').mockImplementation(() => {}) - const guard = createShutdownCheckpointGuard( - vi.fn().mockImplementationOnce(() => { - throw new Error('disk full') - }) - ) - - expect(guard.persistOnce()).toBe(false) - expect(guard.persistOnce()).toBe(true) - - expect(consumeShutdownCheckpointFailureReason()).toBeNull() - }) - it('keeps failure reporting non-throwing for unstringifiable thrown values', () => { vi.spyOn(console, 'error').mockImplementation(() => {}) const guard = createShutdownCheckpointGuard(() => { @@ -69,19 +27,6 @@ describe('createShutdownCheckpointGuard', () => { expect(consumeShutdownCheckpointFailureReason()).toBe('Unknown shutdown checkpoint failure') }) - it('reports checkpoint failure separately from the unload verdict', () => { - const eventTarget = new EventTarget() - const failed = vi.fn() - const guard = createShutdownCheckpointGuard(() => { - throw new Error('invalid session') - }) - eventTarget.addEventListener(ORCA_RENDERER_SHUTDOWN_CHECKPOINT_FAILED_EVENT, failed) - eventTarget.addEventListener('beforeunload', createShutdownCheckpointBeforeUnloadHandler(guard)) - - expect(eventTarget.dispatchEvent(new Event('beforeunload', { cancelable: true }))).toBe(false) - expect(failed).toHaveBeenCalledTimes(1) - }) - it('reports only browser-initiated unloads vetoed by checkpoint failure (#21017)', () => { vi.spyOn(console, 'error').mockImplementation(() => {}) const eventTarget = new EventTarget() @@ -104,23 +49,6 @@ describe('createShutdownCheckpointGuard', () => { expect(onNativeUnloadVetoed).toHaveBeenCalledTimes(1) }) - it('retries after a prevented reload resets the completed checkpoint', () => { - const eventTarget = new EventTarget() - const persist = vi.fn() - const guard = createShutdownCheckpointGuard(persist) - const checkpoint = createShutdownCheckpointBeforeUnloadHandler(guard) - const preventReload = (event: Event): void => event.preventDefault() - eventTarget.addEventListener('beforeunload', checkpoint) - eventTarget.addEventListener('beforeunload', preventReload) - - expect(eventTarget.dispatchEvent(new Event('beforeunload', { cancelable: true }))).toBe(false) - guard.abandonAttempt() - eventTarget.removeEventListener('beforeunload', preventReload) - expect(eventTarget.dispatchEvent(new Event('beforeunload', { cancelable: true }))).toBe(true) - - expect(persist).toHaveBeenCalledTimes(2) - }) - it('cancels unload when persistence fails and remains retryable', () => { const eventTarget = new EventTarget() const persist = vi.fn().mockImplementationOnce(() => { diff --git a/src/renderer/src/lib/simulator-palette-search.test.ts b/src/renderer/src/lib/simulator-palette-search.test.ts index 8b1fef923c3..e6f6d114c4f 100644 --- a/src/renderer/src/lib/simulator-palette-search.test.ts +++ b/src/renderer/src/lib/simulator-palette-search.test.ts @@ -4,16 +4,12 @@ import type { Worktree } from '../../../shared/worktree/types' import { getWorktreeHostIdentity } from '../../../shared/worktree/host-qualified-identity' import { buildPaletteTabDocument } from '../../../shared/palette-match/tab-document' import '../../../shared/palette-match/palette-query' -import { - resolveWorktreeBranchLabel, - resolveWorktreeDisplayName -} from './worktree-default-display-name' + import { SIMULATOR_PALETTE_QUERY_MAX_BYTES, SIMULATOR_TYPE_SEARCH_ALIASES, buildSearchableSimulatorTabs, searchSimulatorTabs, - simulatorPaletteTabTitle, type SearchableSimulatorTab } from './simulator-palette-search' @@ -57,43 +53,6 @@ function makeTab(overrides: Partial = {}): Tab { } /** Mirrors buildSearchableSimulatorTabs so hand-built rows index the same fields. */ -function makeEntry(entry: Omit): SearchableSimulatorTab { - return { - ...entry, - document: buildPaletteTabDocument({ - id: entry.tab.id, - title: simulatorPaletteTabTitle(entry.tab), - secondaryTexts: [], - worktreeName: resolveWorktreeDisplayName(entry.worktree), - branch: resolveWorktreeBranchLabel(entry.worktree), - repoName: entry.repoName, - typeAliases: SIMULATOR_TYPE_SEARCH_ALIASES - }) - } -} - -describe('simulator-palette-search lastActiveAt', () => { - function entryFor(overrides: Partial): SearchableSimulatorTab { - return makeEntry({ - tab: makeTab(overrides), - worktree: makeWorktree(), - repoName: 'repo/mobile', - worktreeSortIndex: 0, - isCurrentTab: false, - isCurrentWorktree: true - }) - } - - it('breaks a rank tie between equally-matching tabs by recency', () => { - // Ids are ordered so an id-only tiebreak would flip this expectation. - const older = entryFor({ id: 'sim-a-older', lastFocusedAt: 1000 }) - const newer = entryFor({ id: 'sim-z-newer', lastFocusedAt: 5000 }) - - const results = searchSimulatorTabs([older, newer], 'emulator') - - expect(results.map((result) => result.tabId)).toEqual(['sim-z-newer', 'sim-a-older']) - }) -}) describe('simulator-palette-search', () => { it('keeps same-id simulator tabs isolated by execution host', () => { diff --git a/src/renderer/src/lib/user-opened-local-path.test.ts b/src/renderer/src/lib/user-opened-local-path.test.ts index b8cc7422cf0..06e2af3de3c 100644 --- a/src/renderer/src/lib/user-opened-local-path.test.ts +++ b/src/renderer/src/lib/user-opened-local-path.test.ts @@ -60,20 +60,6 @@ describe('statUserOpenedPath', () => { mocks.statRuntimePath.mockReset() }) - it('keeps a project file that stays in the project contained', async () => { - answer(true, true) - - await expect(statUserOpenedPath(project, '/repo/src/a.ts')).resolves.toEqual({ - isDirectory: false, - escapesWorktree: false - }) - expect(mocks.statRuntimePath).toHaveBeenCalledTimes(1) - // Opening a path is a user act, so its stat may start a stopped WSL distro. - expect(mocks.statRuntimePath).toHaveBeenCalledWith(project, '/repo/src/a.ts', undefined, { - wakeWslDistro: true - }) - }) - it.each([false, true])( 'reports a project link that leads out of the project (directory: %s)', async (isDirectory) => { diff --git a/src/renderer/src/lib/worktree-palette-search.test.ts b/src/renderer/src/lib/worktree-palette-search.test.ts index c786a76e78b..f075456e56b 100644 --- a/src/renderer/src/lib/worktree-palette-search.test.ts +++ b/src/renderer/src/lib/worktree-palette-search.test.ts @@ -73,26 +73,6 @@ describe('worktree-palette-search', () => { }) }) - it('returns a truncated comment snippet with the highlighted match range', () => { - const results = searchWorktrees( - [ - makeWorktree({ - comment: - 'This worktree carries the quick jump refresh implementation details for the new palette.' - }) - ], - 'implementation', - repoMap - ) - - expect(results).toHaveLength(1) - expect(results[0].supportingText?.labelKind).toBe('comment') - expect(results[0].supportingText?.accessibilityLabel).toBe('Workspace comment') - expect(results[0].supportingText?.text).toContain('implementation') - const [range] = results[0].supportingText?.matchRanges ?? [] - expect(results[0].supportingText?.text.slice(range.start, range.end)).toBe('implementation') - }) - it('does not search stale GitHub cache metadata when Checks selected another review', () => { const staleWorktree = makeWorktree({ branch: 'refs/heads/feature/palette-refresh', @@ -157,13 +137,6 @@ describe('worktree-palette-search', () => { expect(results[0].branchRanges).toEqual([{ start: 0, end: 4 }]) }) - it('requires every token of a multi-token query to land somewhere', () => { - const worktree = makeWorktree({ comment: 'waiting on infra' }) - - expect(searchWorktrees([worktree], 'jump palette', repoMap)).toHaveLength(1) - expect(searchWorktrees([worktree], 'jump nowhere', repoMap)).toEqual([]) - }) - it('matches a pasted GitHub issue URL to the linked worktree instead of the URL text', () => { const results = searchWorktrees( [ diff --git a/src/renderer/src/store/selectors.test.ts b/src/renderer/src/store/selectors.test.ts deleted file mode 100644 index d229c35fad1..00000000000 --- a/src/renderer/src/store/selectors.test.ts +++ /dev/null @@ -1,226 +0,0 @@ -import { beforeEach, describe, expect, it } from 'vitest' - -import type { TerminalTab } from '../../../shared/workspace-layout/terminal-tab-types' - -import { FLOATING_TERMINAL_WORKTREE_ID } from '../../../shared/constants' -import '../../../shared/execution-host' -import '../../../shared/repo-kind' -import type { AppState } from './types' -import { - resetFloatingVisibleTabCountSelectorCacheForTest, - resetFloatingWorkspaceUnreadSelectorCacheForTest, - selectFloatingVisibleTabCount, - selectFloatingWorkspaceHasUnread -} from './selectors' -import './active-terminal-chrome-selector' - -describe('store selectors', () => { - beforeEach(() => { - resetFloatingVisibleTabCountSelectorCacheForTest() - }) - - it('reuses the floating tab-count projection across unrelated store ticks', () => { - const worktreeId = FLOATING_TERMINAL_WORKTREE_ID - const terminalTabs = [ - { - id: 'term-1', - ptyId: null, - worktreeId, - title: 'Terminal', - customTitle: null, - color: null, - sortOrder: 0, - createdAt: 1 - } - ] as AppState['tabsByWorktree'][string] - const browserTabs = [{ id: 'browser-1' }] as AppState['browserTabsByWorktree'][string] - let openFileScans = 0 - const fileEntries = [ - { - id: 'file-1', - filePath: '/tmp/file-1.ts', - relativePath: 'file-1.ts', - worktreeId, - language: 'typescript', - mode: 'edit', - isDirty: false - }, - { - id: 'file-2', - filePath: '/tmp/file-2.ts', - relativePath: 'file-2.ts', - worktreeId: 'repo::/elsewhere', - language: 'typescript', - mode: 'edit', - isDirty: false - } - ] as AppState['openFiles'] - const openFiles = [...fileEntries] as AppState['openFiles'] - Object.defineProperty(openFiles, Symbol.iterator, { - value: function* () { - openFileScans += 1 - for (const entry of fileEntries) { - yield entry - } - } - }) - // oxlint-disable-next-line typescript/consistent-type-assertions -- SAFETY: the selector reads only each tab's contentType and entityId, which every fixture entry sets; the omitted Tab fields are never read. - const unifiedTabs = [ - { - id: 'unified-term-1', - entityId: 'term-1', - worktreeId, - contentType: 'terminal', - label: 'Terminal', - sortOrder: 0, - createdAt: 1 - }, - { - id: 'unified-browser-1', - entityId: 'browser-1', - worktreeId, - contentType: 'browser', - label: 'Browser', - sortOrder: 1, - createdAt: 2 - }, - { - id: 'unified-file-1', - entityId: 'file-1', - worktreeId, - contentType: 'editor', - label: 'file-1.ts', - sortOrder: 2, - createdAt: 3 - }, - { - id: 'simulator-1', - entityId: 'simulator-1', - worktreeId, - contentType: 'simulator', - label: 'Mobile Emulator', - sortOrder: 3, - createdAt: 4 - }, - { - // A structured chat is its own backing record, so it counts like a simulator. - id: 'chat-1', - entityId: 'session-1', - worktreeId, - contentType: 'agent-session', - label: 'Claude Chat', - sortOrder: 4, - createdAt: 5 - }, - { - id: 'unified-stale-terminal', - entityId: 'missing-term', - worktreeId, - contentType: 'terminal', - label: 'Stale', - sortOrder: 4, - createdAt: 5 - } - ] as AppState['unifiedTabsByWorktree'][string] - const state = { - tabsByWorktree: { [worktreeId]: terminalTabs }, - browserTabsByWorktree: { [worktreeId]: browserTabs }, - openFiles, - unifiedTabsByWorktree: { [worktreeId]: unifiedTabs } - } satisfies Parameters[0] - - expect(selectFloatingVisibleTabCount(state)).toBe(5) - expect(openFileScans).toBe(1) - - expect(selectFloatingVisibleTabCount({ ...state })).toBe(5) - expect(openFileScans).toBe(1) - }) -}) - -describe('selectFloatingWorkspaceHasUnread', () => { - const FLOATING = FLOATING_TERMINAL_WORKTREE_ID - - type UnreadState = Parameters[0] - - beforeEach(() => { - resetFloatingWorkspaceUnreadSelectorCacheForTest() - }) - - function makeState(overrides: Partial): UnreadState { - return { - tabsByWorktree: {}, - unreadTerminalTabs: {}, - unreadAgentCompletionPanes: {}, - ...overrides - } as UnreadState - } - - it('shares one scan across consumers and skips 1,000 unrelated writes', () => { - let tabIdReads = 0 - let terminalUnreadReads = 0 - let completionKeyScans = 0 - const tabs = [ - { - get id() { - tabIdReads += 1 - return 'ft1' - }, - title: 'floating', - ptyId: null - } as unknown as TerminalTab - ] - const unreadTerminalTabs = new Proxy>( - {}, - { - get(target, property, receiver) { - if (typeof property === 'string') { - terminalUnreadReads += 1 - } - // oxlint-disable-next-line anti-slop/no-reflect-get -- Proxy `get` trap: only Reflect.get forwards a raw string|symbol key with the proxy receiver. - return Reflect.get(target, property, receiver) - } - } - ) - const unreadAgentCompletionPanes = new Proxy>( - { 'main-tab:leaf-a': true }, - { - ownKeys(target) { - completionKeyScans += 1 - return Reflect.ownKeys(target) - } - } - ) - const state = makeState({ - tabsByWorktree: { [FLOATING]: tabs }, - unreadTerminalTabs, - unreadAgentCompletionPanes - }) - - expect(selectFloatingWorkspaceHasUnread(state)).toBe(false) - const countsAfterFirstConsumer = { - tabIdReads, - terminalUnreadReads, - completionKeyScans - } - expect(selectFloatingWorkspaceHasUnread(state)).toBe(false) - for (let write = 0; write < 1_000; write += 1) { - expect( - selectFloatingWorkspaceHasUnread({ - ...state, - unrelatedStoreRevision: write - } as unknown as UnreadState) - ).toBe(false) - } - expect({ tabIdReads, terminalUnreadReads, completionKeyScans }).toEqual( - countsAfterFirstConsumer - ) - - expect( - selectFloatingWorkspaceHasUnread({ - ...state, - unreadTerminalTabs: { ft1: true } - }) - ).toBe(true) - expect(tabIdReads).toBeGreaterThan(countsAfterFirstConsumer.tabIdReads) - }) -}) diff --git a/src/renderer/src/store/slices/editor-close-file-cleanup.test.ts b/src/renderer/src/store/slices/editor-close-file-cleanup.test.ts index 48ea73d0fcd..6ab78726d1f 100644 --- a/src/renderer/src/store/slices/editor-close-file-cleanup.test.ts +++ b/src/renderer/src/store/slices/editor-close-file-cleanup.test.ts @@ -56,49 +56,7 @@ function mirroredEditorUnifiedTab(id: string, entityId: string, worktreeId: stri } } -describe('createEditorSlice pending editor reveal', () => { - it('clears pending reveal when closing all files in the active worktree', () => { - const store = createEditorStore() - - store.getState().openFile({ - filePath: '/repo/src/file.ts', - relativePath: 'src/file.ts', - worktreeId: 'wt-1', - language: 'typescript', - mode: 'edit' - }) - store.getState().setPendingEditorReveal({ - filePath: '/repo/src/file.ts', - line: 42, - column: 7, - matchLength: 5 - }) - - store.getState().closeAllFiles() - - expect(store.getState().openFiles).toEqual([]) - expect(store.getState().pendingEditorReveal).toBeNull() - }) -}) - describe('createEditorSlice editor drafts', () => { - it('clears draft buffers when closing the file', () => { - const store = createEditorStore() - - store.getState().openFile({ - filePath: '/repo/src/file.ts', - relativePath: 'src/file.ts', - worktreeId: 'wt-1', - language: 'typescript', - mode: 'edit' - }) - store.getState().setEditorDraft('/repo/src/file.ts', 'edited') - - store.getState().closeFile('/repo/src/file.ts') - - expect(store.getState().editorDrafts).toEqual({}) - }) - it('drops replaced preview drafts so hidden preview state cannot linger', () => { const store = createEditorStore() diff --git a/src/shared/__fixtures__/layout-owner-written-session-golden-paths.ts b/src/shared/__fixtures__/layout-owner-written-session-golden-paths.ts deleted file mode 100644 index f5122e4ba31..00000000000 --- a/src/shared/__fixtures__/layout-owner-written-session-golden-paths.ts +++ /dev/null @@ -1,11 +0,0 @@ -// Layout-owner main writes this command fixture; switch-off builds must restore it unchanged. - -import { join } from 'node:path' - -export const LAYOUT_OWNER_WRITTEN_SESSION_GOLDEN_PATH = join( - __dirname, - 'layout-owner-written-session.json' -) - -/** The fixture profile's registered git worktree (terminal-topology-profile-fixture.ts). */ -export const LAYOUT_OWNER_WRITTEN_SESSION_WORKTREE_ID = 'repo-local::/fixture/local' diff --git a/src/shared/__fixtures__/layout-owner-written-session.json b/src/shared/__fixtures__/layout-owner-written-session.json deleted file mode 100644 index c8a74caef7a..00000000000 --- a/src/shared/__fixtures__/layout-owner-written-session.json +++ /dev/null @@ -1,226 +0,0 @@ -{ - "activeRepoId": "repo-local", - "activeWorktreeId": "repo-local::/fixture/local", - "activeTabId": "tab-shell", - "tabsByWorktree": { - "repo-local::/fixture/local": [ - { - "id": "tab-shell", - "ptyId": "pty-shell", - "title": "Terminal 1", - "defaultTitle": "Terminal 1", - "worktreeId": "repo-local::/fixture/local", - "customTitle": null, - "color": null, - "sortOrder": 0, - "createdAt": 1 - }, - { - "id": "tab-build", - "ptyId": null, - "title": "Terminal 2", - "defaultTitle": "Terminal 2", - "worktreeId": "repo-local::/fixture/local", - "customTitle": "Build", - "color": null, - "sortOrder": 1, - "createdAt": 2 - } - ] - }, - "terminalLayoutsByTabId": { - "tab-shell": { - "root": { - "type": "split", - "direction": "vertical", - "first": { - "type": "leaf", - "leafId": "11111111-1111-4111-8111-111111111111" - }, - "second": { - "type": "leaf", - "leafId": "33333333-1111-4111-8111-111111111111" - } - }, - "activeLeafId": "11111111-1111-4111-8111-111111111111", - "expandedLeafId": null, - "ptyIdsByLeafId": { - "11111111-1111-4111-8111-111111111111": "pty-shell" - } - }, - "tab-build": { - "root": { - "type": "leaf", - "leafId": "22222222-1111-4111-8111-111111111111" - }, - "activeLeafId": "22222222-1111-4111-8111-111111111111", - "expandedLeafId": null, - "ptyIdsByLeafId": {} - } - }, - "openFilesByWorktree": { - "repo-local::/fixture/local": [ - { - "filePath": "/fixture/local/README.md", - "relativePath": "README.md", - "language": "markdown", - "worktreeId": "repo-local::/fixture/local" - } - ] - }, - "markdownFrontmatterVisible": {}, - "browserTabsByWorktree": { - "repo-local::/fixture/local": [ - { - "id": "browser-docs", - "sessionProfileId": null, - "pageIds": ["page-docs"], - "createdAt": 4, - "worktreeId": "repo-local::/fixture/local", - "url": "https://example.com/docs", - "title": "Docs", - "loading": false, - "faviconUrl": null, - "canGoBack": false, - "canGoForward": false, - "loadError": null - } - ] - }, - "browserPagesByWorkspace": { - "browser-docs": [ - { - "id": "page-docs", - "workspaceId": "browser-docs", - "worktreeId": "repo-local::/fixture/local", - "url": "https://example.com/docs", - "title": "Docs", - "loading": false, - "faviconUrl": null, - "canGoBack": false, - "canGoForward": false, - "loadError": null, - "createdAt": 4 - } - ] - }, - "activeBrowserTabIdByWorktree": {}, - "activeFileIdByWorktree": {}, - "activeTabTypeByWorktree": {}, - "browserUrlHistory": [], - "defaultTerminalTabsAppliedByWorktreeId": { - "repo-local::/fixture/local": true - }, - "unifiedTabs": { - "repo-local::/fixture/local": [ - { - "id": "tab-shell", - "entityId": "tab-shell", - "groupId": "aa34db2e-c345-5d9d-a3e5-ddd4ac94492b", - "worktreeId": "repo-local::/fixture/local", - "executionHostId": "local", - "contentType": "terminal", - "label": "Terminal 1", - "customLabel": null, - "color": null, - "sortOrder": 0, - "createdAt": 1 - }, - { - "id": "tab-readme", - "entityId": "/fixture/local/README.md", - "groupId": "aa34db2e-c345-5d9d-a3e5-ddd4ac94492b", - "worktreeId": "repo-local::/fixture/local", - "executionHostId": "local", - "contentType": "editor", - "label": "", - "customLabel": null, - "color": null, - "sortOrder": 1, - "createdAt": 3 - }, - { - "id": "tab-docs", - "entityId": "browser-docs", - "groupId": "aa34db2e-c345-5d9d-a3e5-ddd4ac94492b", - "worktreeId": "repo-local::/fixture/local", - "executionHostId": "local", - "contentType": "browser", - "label": "", - "customLabel": null, - "color": null, - "sortOrder": 2, - "createdAt": 4 - }, - { - "id": "tab-build", - "entityId": "tab-build", - "groupId": "00000000-0000-4000-8000-000000000001", - "worktreeId": "repo-local::/fixture/local", - "executionHostId": "local", - "contentType": "terminal", - "label": "Terminal 2", - "customLabel": "Build", - "color": null, - "sortOrder": 0, - "createdAt": 2 - } - ] - }, - "tabGroups": { - "repo-local::/fixture/local": [ - { - "id": "aa34db2e-c345-5d9d-a3e5-ddd4ac94492b", - "worktreeId": "repo-local::/fixture/local", - "activeTabId": "tab-shell", - "tabOrder": ["tab-shell", "tab-readme", "tab-docs"] - }, - { - "id": "00000000-0000-4000-8000-000000000001", - "worktreeId": "repo-local::/fixture/local", - "activeTabId": "tab-build", - "tabOrder": ["tab-build"] - } - ] - }, - "tabGroupLayouts": { - "repo-local::/fixture/local": { - "type": "split", - "direction": "horizontal", - "first": { - "type": "leaf", - "groupId": "aa34db2e-c345-5d9d-a3e5-ddd4ac94492b" - }, - "second": { - "type": "leaf", - "groupId": "00000000-0000-4000-8000-000000000001" - }, - "ratio": 0.5 - } - }, - "terminalTopologyRevisionByRepoId": { - "repo-local": 5 - }, - "terminalPtyIncarnationsByPaneKey": { - "tab-shell:11111111-1111-4111-8111-111111111111": "inc-shell" - }, - "closedTerminalTabTombstonesByTabId": { - "tab-scratch": { - "reason": "user", - "closedAt": 1700000000000, - "worktreeId": "repo-local::/fixture/local" - } - }, - "closedTerminalPanesByPaneKey": { - "tab-scratch:44444444-1111-4111-8111-111111111111": { - "ptyId": "pty-scratch", - "incarnationId": "inc-scratch", - "worktreeId": "repo-local::/fixture/local", - "closedAt": 1700000000000 - } - }, - "terminalSurfaceTombstonesByPaneKey": {}, - "activeTabIdByWorktree": { - "repo-local::/fixture/local": "tab-shell" - } -}