Remove 129 low-value test cases and 45 whole suites (#38233)

* Remove low-value renderer and main tests

129 registrations removed; 45 whole suites deleted.

* Clean deleted-suite CI reference and retained type import

---------

Co-authored-by: Codex <codex@openai.com>
This commit is contained in:
OrcaWin
2026-11-04 13:44:42 -08:00
committed by GitHub
co-authored by Codex
parent e7801630fa
commit bcb6dd0102
78 changed files with 126 additions and 5203 deletions
+1 -3
View File
@@ -629,9 +629,7 @@ jobs:
export GIT_EXEC_PATH="$HOME/.cache/orca-git-compat/git-$ORCA_GIT_COMPAT_VERSION"
fi
pnpm exec vitest run --config config/vitest.config.ts \
src/shared/git/compatibility/git-binary-compatibility.test.ts \
src/main/git/worktrees/creation/worktree-safety-real-git.test.ts \
src/main/git/worktrees/removal/worktree-rebase-update-refs-real-git.test.ts
src/shared/git/compatibility/git-binary-compatibility.test.ts
# Run pinned real Codex: thread/read must repair an unindexed rollout’s threads row; stub-only units cannot detect upstream removal of this behavior.
codex_index_heal_contract:
-3
View File
@@ -548,7 +548,6 @@
1 test-no-wall-clock:src/main/skills/skill-placement-reconciliation.test.ts
1 test-no-wall-clock:src/main/skills/skill-placement-transaction.test.ts
1 test-no-wall-clock:src/main/ssh/legacy-relay/legacy-relay-bridge-runtime-exit.test.ts
1 test-no-wall-clock:src/main/ssh/ssh-agent-hook-interrupt-reconciliation.test.ts
1 test-no-wall-clock:src/main/star-nag/service-force-show-races.test.ts
1 test-no-wall-clock:src/main/startup/login-shell-environment.test.ts
1 test-no-wall-clock:src/main/window/desktop-integration/macos-press-and-hold-default.defaults-domain.test.ts
@@ -1009,7 +1008,6 @@
2 test-no-wall-clock:src/main/git/command-runner/git-command-resolution-windows-checkout.test.ts
2 test-no-wall-clock:src/main/git/refs/maintenance/repo-ref-maintenance-real-git.test.ts
2 test-no-wall-clock:src/main/git/runner-gh-account-binding.test.ts
2 test-no-wall-clock:src/main/git/source-control/settled-diff-cache-bounds.test.ts
2 test-no-wall-clock:src/main/git/worktrees/catalog/worktree-created-description-real-git.test.ts
2 test-no-wall-clock:src/main/github/client-pr-fallback-number.test.ts
2 test-no-wall-clock:src/main/github/pr-refresh-coordinator-error-cadence.test.ts
@@ -1245,7 +1243,6 @@
3 test-timeout-needs-reason:src/main/daemon/daemon-shutdown-final-checkpoint-caller-deadline.test.ts
3 test-timeout-needs-reason:src/main/daemon/node-pty-windows-input-error.win32.test.ts
3 test-timeout-needs-reason:src/main/native-chat/agent-model-catalog/agent-model-catalog-probe-runner.test.ts
1 test-timeout-needs-reason:src/main/providers/pty-repaint-wide-char-buffer.node-pty.test.ts
3 test-timeout-needs-reason:src/main/runtime/orchestration-cli-subprocess.test.ts
3 test-timeout-needs-reason:src/main/runtime/terminal-interactive-wait-visibility.test.ts
3 test-timeout-needs-reason:src/main/runtime/tui-idle-name-only-real-pty.integration.test.ts
File diff suppressed because one or more lines are too long
-41
View File
@@ -1089,7 +1089,6 @@
"src/main/ai-vault/scanners/opencode/reader-transport/session-scanner-opencode-sqlite-process.test.ts": 3815,
"src/main/ai-vault/scanners/opencode/sqlite/session-scanner-opencode-sqlite-scan-scope.test.ts": 54,
"src/main/ai-vault/scanners/opencode/reader-transport/session-scanner-opencode-sqlite-worker-client.test.ts": 181,
"src/main/ai-vault/scanners/opencode/reader-transport/session-scanner-opencode-sqlite-worker-spawn.test.ts": 180,
"src/main/ai-vault/scanners/opencode/sqlite/session-scanner-opencode-sqlite.test.ts": 410,
"src/main/ai-vault/scanners/opencode/reader-transport/session-scanner-opencode-wsl-routing.test.ts": 200,
"src/main/ai-vault/scanners/opencode2/session-scanner-opencode2-message.test.ts": 138,
@@ -1200,7 +1199,6 @@
"src/main/automations/runtime-terminal-run-observer-evidence.test.ts": 230,
"src/main/automations/runtime-terminal-run-observer.test.ts": 4026,
"src/main/automations/schedule-drift-report.test.ts": 83,
"src/main/automations/service-lifecycle.test.ts": 1952,
"src/main/automations/service-precheck.test.ts": 2057,
"src/main/automations/service.test.ts": 2970,
"src/main/azure-devops/client.test.ts": 411,
@@ -1235,7 +1233,6 @@
"src/main/browser/certificate-trust/browser-certificate-trust-controller.test.ts": 496,
"src/main/browser/client-hosts/file-transfers/browser-client-download-relay.test.ts": 124,
"src/main/browser/client-hosts/file-transfers/browser-client-download-routing.test.ts": 124,
"src/main/browser/client-hosts/file-transfers/browser-client-file-channel-negotiation.test.ts": 116,
"src/main/browser/client-hosts/file-transfers/browser-client-file-channel-reconnect.test.ts": 556,
"src/main/browser/client-hosts/lifecycle/browser-client-host-authority-replacement.test.ts": 47,
"src/main/browser/client-hosts/commands/browser-client-host-command-dispatcher.test.ts": 156,
@@ -1260,7 +1257,6 @@
"src/main/browser/cookies/client-route/browser-client-route-cookie-import-partition-parity.test.ts": 374,
"src/main/browser/cookies/client-route/browser-client-route-cookie-import.test.ts": 101,
"src/main/browser/client-hosts/file-transfers/browser-client-upload-buffer-retention.test.ts": 736,
"src/main/browser/client-hosts/file-transfers/browser-client-upload-command.test.ts": 115,
"src/main/browser/client-hosts/file-transfers/browser-client-upload-staging.test.ts": 140,
"src/main/browser/client-hosts/file-transfers/browser-client-upload-transfer.test.ts": 93,
"src/main/browser/cdp/input/browser-element-functions.test.ts": 228,
@@ -1471,7 +1467,6 @@
"src/main/browser/routing/local-host-partitions/local-ssh-browser-route.test.ts": 92,
"src/main/browser/backends/offscreen-browser-backend-lifecycle.test.ts": 341,
"src/main/browser/client-hosts/lifecycle/paired-runtime-browser-client-host-composition.test.ts": 165,
"src/main/browser/client-hosts/lifecycle/paired-runtime-browser-client-host-identity-wiring.test.ts": 1368,
"src/main/browser/client-hosts/lifecycle/paired-runtime-browser-client-host-reconnect.test.ts": 568,
"src/main/browser/client-hosts/lifecycle/paired-runtime-browser-client-host-registry.test.ts": 53,
"src/main/browser/client-hosts/lifecycle/paired-runtime-browser-client-host-route-identity.test.ts": 1404,
@@ -1490,7 +1485,6 @@
"src/main/browser/network/tunnels/system-ssh-socks-client-socket.test.ts": 53,
"src/main/browser/network/execution-routes/wsl-browser-network-execution-route.test.ts": 678,
"src/main/browser/network/execution-routes/wsl-browser-network-relay-launch.test.ts": 146,
"src/main/browser/routing/local-host-partitions/wsl-host-browser-jar-identity.test.ts": 806,
"src/main/browser/routing/local-host-partitions/wsl-host-browser-route-partition.test.ts": 753,
"src/main/claude-accounts/claude-account-folder.test.ts": 153,
"src/main/claude-accounts/claude-account-service.test.ts": 1207,
@@ -1755,7 +1749,6 @@
"src/main/codex-accounts/runtime-home-windows-profile-ownership.test.ts": 359,
"src/main/codex-accounts/runtime-home-wsl-managed-accounts.test.ts": 1199,
"src/main/codex-accounts/runtime-home-wsl-session-bridge.test.ts": 1018,
"src/main/codex-accounts/runtime-home-wsl-sibling-bridge.test.ts": 545,
"src/main/codex-accounts/runtime-home-wsl-system-default.test.ts": 758,
"src/main/codex-accounts/service-abandoned-login-supersede.test.ts": 780,
"src/main/codex-accounts/service-account-add-login.test.ts": 757,
@@ -2175,7 +2168,6 @@
"src/main/daemon/daemon-session-owner-resolution.test.ts": 114,
"src/main/daemon/daemon-session-scrollback-window.test.ts": 554,
"src/main/daemon/daemon-severed-restart.test.ts": 629,
"src/main/daemon/daemon-shared-owner-incarnation-retention.test.ts": 1025,
"src/main/daemon/daemon-shutdown-final-checkpoint-caller-deadline.test.ts": 2543,
"src/main/daemon/daemon-socket-endpoint-path.test.ts": 529,
"src/main/daemon/daemon-spawner.test.ts": 708,
@@ -2408,7 +2400,6 @@
"src/main/git/command-runner/git-admission-output-parity.test.ts": 271,
"src/main/git/command-runner/git-admission-storm-measurement.test.ts": 264,
"src/main/git/command-runner/git-command-resolution-windows-checkout.test.ts": 133,
"src/main/git/command-runner/git-command-timeout-behavior.test.ts": 2006,
"src/main/git/command-runner/git-exec-admission-lifetime.test.ts": 718,
"src/main/git/command-runner/git-operation-executor.test.ts": 59,
"src/main/git/command-runner/git-spawn-wsl-admission.test.ts": 153,
@@ -2479,7 +2470,6 @@
"src/main/git/runner-wsl-network-ssh-policy.test.ts": 260,
"src/main/git/runner.test.ts": 284,
"src/main/git/saved-clone-target.test.ts": 890,
"src/main/git/source-control/settled-diff-cache-bounds.test.ts": 71,
"src/main/git/source-control/blob-absence-real-git.test.ts": 629,
"src/main/git/source-control/complete-merge-real-git.test.ts": 958,
"src/main/git/source-control/diff-textconv-real-git.test.ts": 520,
@@ -2525,7 +2515,6 @@
"src/main/git/worktrees/creation/worktree-base-refresh-lock-real-git.test.ts": 1588,
"src/main/git/worktrees/creation/worktree-checkout-progress-real-git.test.ts": 769,
"src/main/git/worktrees/creation/worktree-configured-paths-concurrency.test.ts": 80,
"src/main/git/worktrees/creation/worktree-create-git-executor-real-git.test.ts": 528,
"src/main/git/worktrees/creation/worktree-create-preparation-real-git.test.ts": 1858,
"src/main/git/worktrees/creation/worktree-create-preparation-real-wsl.test.ts": 382,
"src/main/git/worktrees/catalog/worktree-created-description-real-git.test.ts": 1119,
@@ -2554,10 +2543,8 @@
"src/main/git/worktrees/creation/worktree-preparation-tip-refresh-real-git.test.ts": 844,
"src/main/git/worktrees/creation/worktree-preparation-tip-refresh.test.ts": 113,
"src/main/git/worktrees/removal/worktree-prune-guard.test.ts": 159,
"src/main/git/worktrees/removal/worktree-rebase-update-refs-real-git.test.ts": 284,
"src/main/git/worktrees/removal/worktree-removal-real-git.test.ts": 3365,
"src/main/git/worktrees/removal/worktree-remove-branch-deletion.test.ts": 353,
"src/main/git/worktrees/creation/worktree-safety-real-git.test.ts": 549,
"src/main/git/worktrees/catalog/worktree-scan-cache-annotation-reuse.test.ts": 361,
"src/main/git/worktrees/catalog/worktree-separate-git-dir.test.ts": 689,
"src/main/git/worktrees/content-paths/worktree-shared-directories.test.ts": 1045,
@@ -2770,7 +2757,6 @@
"src/main/ipc/created-worktree-root-prune.test.ts": 494,
"src/main/ipc/dashboard-payload-validation.test.ts": 188,
"src/main/ipc/dashboard-popout.test.ts": 124,
"src/main/ipc/browser/desktop-browser-bridge-wiring.test.ts": 1211,
"src/main/ipc/developer-permissions.test.ts": 147,
"src/main/ipc/diagnostics.test.ts": 124,
"src/main/ipc/doc-preview-grant-ipc.test.ts": 58,
@@ -2869,7 +2855,6 @@
"src/main/ipc/folder-repo-git-upgrade-lifetime.test.ts": 123,
"src/main/ipc/folder-repo-git-upgrade.test.ts": 2302,
"src/main/ipc/worktrees/git/git-status-upstream-ref-watch-request.test.ts": 405,
"src/main/ipc/source-control/github-pr-refresh-document-lifetime.test.ts": 120,
"src/main/ipc/source-control/github-pr-refresh-routing.test.ts": 715,
"src/main/ipc/source-control/github-repo-access-guards.test.ts": 805,
"src/main/ipc/source-control/github-repo-routing-twin-host.test.ts": 124,
@@ -2958,12 +2943,10 @@
"src/main/ipc/pty/host-env/spawn-cwd-hung-folder.test.ts": 650,
"src/main/ipc/pty/host-env/spawn-cwd-wsl-move.test.ts": 817,
"src/main/ipc/pty/ipc/claude-old-terminal.test.ts": 47,
"src/main/ipc/pty/ipc/closed-pane-spawn-refusal.test.ts": 242,
"src/main/ipc/pty/ipc/spawn-commit-run-facts.test.ts": 1661,
"src/main/ipc/pty/ipc/spawn-options-codex-isolation.test.ts": 1115,
"src/main/ipc/pty/ipc/spawn-push-target-materialization-real-git.test.ts": 1316,
"src/main/ipc/pty/ipc/spawn-reattach-size-cache.test.ts": 1459,
"src/main/ipc/pty/ipc/terminal-view-attributes-ipc.test.ts": 516,
"src/main/ipc/pty/ipc/write-input-user-input.test.ts": 84,
"src/main/ipc/pty/managed-data-account-spawn-environment.test.ts": 1150,
"src/main/ipc/pty/opencode-legacy-spawn-deletions.test.ts": 1214,
@@ -3010,7 +2993,6 @@
"src/main/ipc/pty/pty-serializer-settlement-mapping.test.ts": 4245,
"src/main/ipc/pty/pty-session-liveness-and-ownership.test.ts": 3423,
"src/main/ipc/pty/pty-spawn-codex-home-unavailable.test.ts": 4208,
"src/main/ipc/pty/pty-spawn-cwd-fallback-hung-folder.test.ts": 3949,
"src/main/ipc/pty/pty-spawn-cwd-fallback.test.ts": 4680,
"src/main/ipc/pty/pty-spawn-env-agent-overlays.test.ts": 3981,
"src/main/ipc/pty/pty-spawn-env-codex-home-routing.test.ts": 6383,
@@ -3317,7 +3299,6 @@
"src/main/workspaces/removal/local-worktree-filesystem-wsl-banner.wsl.test.ts": 84,
"src/main/workspaces/removal/local-worktree-filesystem.test.ts": 4284,
"src/main/workspaces/catalog/local-worktree-metadata-prune-gate.test.ts": 41,
"src/main/workspaces/catalog/local-worktree-path-presence.test.ts": 52,
"src/main/workspaces/removal/local-worktree-removal-recovery-live.test.ts": 465,
"src/main/workspaces/removal/local-worktree-removal-recovery.test.ts": 351,
"src/main/ports/workspace-labels/localhost-worktree-label-proxy.test.ts": 120,
@@ -3421,9 +3402,7 @@
"src/main/memory/collector.test.ts": 925,
"src/main/memory/host-malloc-tunables.test.ts": 41,
"src/main/memory/host-memory.test.ts": 80,
"src/main/memory/hydrate-local-pty-registry-folder-workspaces.test.ts": 535,
"src/main/memory/hydrate-local-pty-registry.test.ts": 1970,
"src/main/memory/memory-snapshot-buckets.test.ts": 113,
"src/main/memory/verified-local-folder-workspaces.test.ts": 74,
"src/main/menu/register-app-menu.test.ts": 280,
"src/main/mimo/hook-service.test.ts": 115,
@@ -3526,7 +3505,6 @@
"src/main/native-chat/agent-session-wire/agent-session-history-page.test.ts": 1049,
"src/main/native-chat/agent-session-wire/agent-session-item-wire-form.test.ts": 2246,
"src/main/native-chat/agent-session-wire/agent-session-stop-note-paging.test.ts": 1337,
"src/main/native-chat/agent-session-wire/agent-session-stop-note-refresh.test.ts": 213,
"src/main/native-chat/agent-session-wire/at-rest-command-demand.test.ts": 43,
"src/main/native-chat/agent-session-wire/hook-failure-provider-frame.test.ts": 234,
"src/main/native-chat/agent-session-wire/hook-outcome-rows.test.ts": 2040,
@@ -3565,7 +3543,6 @@
"src/main/native-chat/agent-session-wire/structured-agent-session-codex-applied-inputs.test.ts": 2594,
"src/main/native-chat/agent-session-wire/structured-agent-session-codex-held-goal.test.ts": 2081,
"src/main/native-chat/agent-session-wire/structured-agent-session-codex-input-host.test.ts": 19298,
"src/main/native-chat/agent-session-wire/structured-agent-session-codex-version-recovery.test.ts": 2227,
"src/main/native-chat/agent-session-wire/structured-agent-session-command-publication.test.ts": 1350,
"src/main/native-chat/agent-session-wire/structured-agent-session-command-readers.test.ts": 567,
"src/main/native-chat/agent-session-wire/structured-agent-session-conversations.test.ts": 2096,
@@ -3615,7 +3592,6 @@
"src/main/native-chat/agent-session-wire/structured-agent-session-omp-menu-persistence.test.ts": 1063,
"src/main/native-chat/agent-session-wire/structured-agent-session-operation-settlement.test.ts": 1852,
"src/main/native-chat/agent-session-wire/structured-agent-session-operation-start-refusal.test.ts": 1210,
"src/main/native-chat/agent-session-wire/structured-agent-session-options-deadline.test.ts": 1036,
"src/main/native-chat/agent-session-wire/structured-agent-session-options-read-scaling.test.ts": 1006,
"src/main/native-chat/agent-session-wire/structured-agent-session-options-read.accepted-inputs.test.ts": 1063,
"src/main/native-chat/agent-session-wire/structured-agent-session-options-read.test.ts": 1405,
@@ -3650,7 +3626,6 @@
"src/main/native-chat/agent-session-wire/structured-agent-session-received-output.test.ts": 2740,
"src/main/native-chat/agent-session-wire/structured-agent-session-receiving-cancellation.test.ts": 5834,
"src/main/native-chat/agent-session-wire/structured-agent-session-receiving-catalog.test.ts": 5757,
"src/main/native-chat/agent-session-wire/structured-agent-session-recovered-queue-idle.test.ts": 1857,
"src/main/native-chat/agent-session-wire/structured-agent-session-recovered-turn-clock.test.ts": 1539,
"src/main/native-chat/agent-session-wire/structured-agent-session-recovery-exits.test.ts": 2112,
"src/main/native-chat/agent-session-wire/structured-agent-session-recovery-read.test.ts": 918,
@@ -3665,7 +3640,6 @@
"src/main/native-chat/agent-session-wire/structured-agent-session-restart-cap.test.ts": 731,
"src/main/native-chat/agent-session-wire/structured-agent-session-restart-child-work.test.ts": 1898,
"src/main/native-chat/agent-session-wire/structured-agent-session-restart-context-clear.test.ts": 538,
"src/main/native-chat/agent-session-wire/structured-agent-session-restart-continuation-wait.test.ts": 191,
"src/main/native-chat/agent-session-wire/structured-agent-session-restart-continuation.test.ts": 533,
"src/main/native-chat/agent-session-wire/structured-agent-session-restart-failure-filing.test.ts": 2350,
"src/main/native-chat/agent-session-wire/structured-agent-session-restart-offer-after-reattach.test.ts": 1903,
@@ -3739,7 +3713,6 @@
"src/main/native-chat/agent-session-wire/structured-agent-session-unexpected-exit.test.ts": 430,
"src/main/native-chat/agent-session-wire/structured-agent-session-unnamed-stop-races.test.ts": 2959,
"src/main/native-chat/agent-session-wire/structured-agent-session-unopened-send-withdrawal.test.ts": 156,
"src/main/native-chat/agent-session-wire/structured-agent-session-unopened-tab.test.ts": 1885,
"src/main/native-chat/agent-session-wire/structured-agent-session-unrun-send-hold.test.ts": 2343,
"src/main/native-chat/agent-session-wire/structured-agent-session-unsent-send-hold.test.ts": 6694,
"src/main/native-chat/agent-session-wire/structured-agent-session-view-start-after-failed-start.test.ts": 5757,
@@ -3877,7 +3850,6 @@
"src/main/opencode/opencode-acp-connection.test.ts": 1205,
"src/main/opencode/opencode-cli-version.test.ts": 54,
"src/main/opencode/opencode-client-sweep.test.ts": 124,
"src/main/opencode/opencode-credential-backend.test.ts": 84,
"src/main/opencode/opencode-go-api-key-store.test.ts": 128,
"src/main/opencode/opencode-launch-model-config.test.ts": 87,
"src/main/opencode/opencode-launch-model-context-cleanup.test.ts": 1204,
@@ -4077,7 +4049,6 @@
"src/main/persistence/loading-store/secret-sentinel-substitution.test.ts": 90,
"src/main/persistence/loading-store/state-write-round-trip.test.ts": 1983,
"src/main/persistence/loading-store/store-host-partition-before-unload.test.ts": 1670,
"src/main/persistence/loading-store/store-prune-gate-signals.test.ts": 1787,
"src/main/persistence/loading-store/store-runtime-authored-session-writes.test.ts": 1757,
"src/main/persistence/loading-store/store-workspace-session-host-removal.test.ts": 1738,
"src/main/persistence/loading-store/terminal-close-async-durability.test.ts": 5719,
@@ -4568,7 +4539,6 @@
"src/main/providers/pty-default-cwd.test.ts": 54,
"src/main/providers/pty-process-inspection.test.ts": 45,
"src/main/providers/pty-process-list-admission.test.ts": 138,
"src/main/providers/pty-repaint-wide-char-buffer.node-pty.test.ts": 2018,
"src/main/providers/stable-foreground-process.test.ts": 77,
"src/main/providers/startup-line-typed-length.live-shell.test.ts": 316,
"src/main/providers/terminal-path-existence-batch.integration.test.ts": 337,
@@ -4707,7 +4677,6 @@
"src/main/runtime/agent-session-record-store-damaged.test.ts": 1557,
"src/main/runtime/agent-session-saved-roster.test.ts": 3035,
"src/main/runtime/agent-session-store-load-scale.test.ts": 732,
"src/main/runtime/agent-session-tab-layout-command.test.ts": 279,
"src/main/runtime/agent-session-tab-layout-queue.test.ts": 4061,
"src/main/runtime/agent-session/agent-session-conversation-clear-commit.test.ts": 957,
"src/main/runtime/agent-session/agent-session-death-evidence-persistence.test.ts": 1113,
@@ -4860,7 +4829,6 @@
"src/main/runtime/device-registry-own-quarantine.test.ts": 123,
"src/main/runtime/device-registry-startup-offer.test.ts": 102,
"src/main/runtime/dsh-launch-prompt-resubmit.test.ts": 6527,
"src/main/runtime/dsh-readiness-transcript.test.ts": 71,
"src/main/runtime/e2ee-keypair.test.ts": 77,
"src/main/runtime/editor-view-layout-apply.test.ts": 6870,
"src/main/runtime/editor-view-partition-changes.test.ts": 5531,
@@ -5236,7 +5204,6 @@
"src/main/runtime/renderer-browser-session-reconciliation.test.ts": 3882,
"src/main/runtime/renderer-owned-tab-properties.test.ts": 15027,
"src/main/runtime/repo-icon-fork-backfill.test.ts": 4159,
"src/main/runtime/repo-worktree-admin-fingerprint.test.ts": 668,
"src/main/runtime/repo-worktree-row-resolution.test.ts": 664,
"src/main/runtime/restored-editor-move-restart.test.ts": 5390,
"src/main/runtime/restored-pane-hook-store-resume.test.ts": 4423,
@@ -5408,7 +5375,6 @@
"src/main/runtime/rpc/methods/orchestration/worker/worker-run-dispatch-contracts.test.ts": 5136,
"src/main/runtime/rpc/methods/orchestration/worker/worker-start-budgets.test.ts": 45,
"src/main/runtime/rpc/methods/orchestration/worker/worker-start-launch-executor-parity.test.ts": 1137,
"src/main/runtime/rpc/methods/orchestration/worker/worker-start-prompt-budget.test.ts": 100,
"src/main/runtime/rpc/methods/orchestration/worker/worker-start-prompt-contract.test.ts": 6279,
"src/main/runtime/rpc/methods/orchestration/worker/worker-stop-exit-race.test.ts": 4508,
"src/main/runtime/rpc/methods/orchestration/worker/worker-stop-liveness-verdict.test.ts": 5279,
@@ -5667,7 +5633,6 @@
"src/main/runtime/runtime-provisioned-root-adoption.test.ts": 537,
"src/main/runtime/runtime-reference-agents.test.ts": 207,
"src/main/runtime/runtime-reference-find.test.ts": 245,
"src/main/runtime/runtime-reference-integration.test.ts": 4136,
"src/main/runtime/runtime-remote-fetch-ref-maintenance.test.ts": 419,
"src/main/runtime/runtime-repo-update-worktree-refresh.test.ts": 4375,
"src/main/runtime/runtime-repository-clone-host-dedupe.test.ts": 874,
@@ -5890,13 +5855,11 @@
"src/main/runtime/terminal/identity/main-pane-record-arrival.test.ts": 5024,
"src/main/runtime/terminal/identity/terminal-identity-placement-schedules.test.ts": 4255,
"src/main/runtime/terminal/identity/terminal-identity-schedules.test.ts": 4334,
"src/main/runtime/terminal/identity/terminal-identity-source.test.ts": 3963,
"src/main/runtime/terminal/input/agent-prompt-permission-hold.test.ts": 48,
"src/main/runtime/terminal/input/terminal-send-plugin-hook.test.ts": 169,
"src/main/runtime/terminal/launch/mobile-terminal-chat-fork.test.ts": 3900,
"src/main/runtime/terminal/launch/terminal-split-layout-owner.test.ts": 4503,
"src/main/runtime/terminal/launch/terminal-split-retry.test.ts": 4038,
"src/main/runtime/terminal/launch/window-tab-start-layout-owner.test.ts": 4124,
"src/main/runtime/terminal/launch/window-tab-start-switch-off.test.ts": 4475,
"src/main/runtime/terminal/layout/agent-generated-tab-titles.test.ts": 5572,
"src/main/runtime/terminal/lifecycle/main-pane-liveness.test.ts": 4871,
@@ -5956,7 +5919,6 @@
"src/main/runtime/worktree-teardown-daemon-inventory.test.ts": 463,
"src/main/runtime/worktree-teardown-unstopped-pty.test.ts": 419,
"src/main/runtime/worktree-teardown.test.ts": 862,
"src/main/runtime/worktree-terminal-mutation-lock.test.ts": 55,
"src/main/runtime/worktree-terminal-sleep-host-twin.test.ts": 4345,
"src/main/runtime/wsl-distro-loss-exit-retention.test.ts": 5519,
"src/main/runtime/wsl-guest-drive-mountinfo.test.ts": 38,
@@ -6011,7 +5973,6 @@
"src/main/skills/skill-delete/wsl-enumeration-protocol.test.ts": 52,
"src/main/skills/skill-discovery-concurrency.test.ts": 222,
"src/main/skills/skill-discovery-target.test.ts": 124,
"src/main/skills/skill-discovery-wsl-alias-sharing.test.ts": 165,
"src/main/skills/skill-discovery-wsl-bash-filter.test.ts": 2105,
"src/main/skills/skill-discovery-wsl-script-roundtrip.test.ts": 136,
"src/main/skills/skill-discovery-wsl.test.ts": 104,
@@ -6185,7 +6146,6 @@
"src/main/ssh/orcad-migration-delta-move.test.ts": 3812,
"src/main/ssh/orcad-migration-delta-snapshot.test.ts": 2611,
"src/main/ssh/orcad-migration-manifest-export.test.ts": 613,
"src/main/ssh/orcad-migration-path-rewrite.test.ts": 175,
"src/main/ssh/orcad-migration-repository-link.test.ts": 4281,
"src/main/ssh/orcad-migration-snapshot-coordinator.test.ts": 309,
"src/main/ssh/orcad-migration-snapshot-resume.test.ts": 1878,
@@ -6221,7 +6181,6 @@
"src/main/ssh/sftp-stream-late-error.test.ts": 209,
"src/main/ssh/sftp-upload-pipeline.test.ts": 480,
"src/main/ssh/sftp-upload.test.ts": 533,
"src/main/ssh/ssh-agent-hook-interrupt-reconciliation.test.ts": 610,
"src/main/ssh/ssh-agent-identity-filter.test.ts": 54,
"src/main/ssh/ssh-channel-multiplexer-backpressure.test.ts": 102,
"src/main/ssh/ssh-channel-multiplexer-saturation-wedge.test.ts": 60,
-2
View File
@@ -49,7 +49,6 @@ export const WINDOWS_BOUNDARY_TESTS = [
'src/main/browser/client-pages/renderer/browser-client-page-renderer-lifecycle.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',
'src/shared/__tests__/process-host/windows-command-line.win32.test.ts',
'src/shared/__tests__/process-host/windows-cmd-shim-resolution.test.ts',
'src/shared/__tests__/process-host/windows-cmd-shim-resolution.win32.test.ts',
@@ -95,7 +94,6 @@ export const WINDOWS_BOUNDARY_TESTS = [
'src/main/startup/windows-app-data-path.test.ts',
'src/main/startup/windows-std-handle-inheritance.win32.test.ts',
'src/main/startup/windows-isolated-spawner.win32.test.ts',
'src/main/runtime/repo-worktree-admin-fingerprint.test.ts',
'src/main/runtime/worktree-scan-admin-fingerprint-gate.test.ts',
'src/shared/secure-file-fsync-flags.test.ts',
'src/shared/secure-path-windows-acl.win32.test.ts',
@@ -5,10 +5,6 @@ import { afterEach, describe, expect, it, vi } from 'vitest'
import type { AiVaultScanWork } from '../../shared/ai-vault/scanning/ai-vault-scan-progress'
import {
AI_VAULT_SCAN_PROGRESS_INTERVAL_MS,
noteAiVaultScanFileFound,
aiVaultScanWork,
openAiVaultScanSource,
noteAiVaultScanWorkChanged,
trackAiVaultScanProgress
} from './ai-vault-scan-progress'
import { scanAiVaultSessions } from './session-scanner'
@@ -30,37 +26,7 @@ async function tempRoot(): Promise<string> {
return root
}
function codexFile(index: number): { path: string; dev: number; ino: number } {
return { path: `/sessions/${index}.jsonl`, dev: 1, ino: index + 1 }
}
describe('scan progress reporting', () => {
it('reports at most once per interval, with the latest tally trailing', () => {
vi.useFakeTimers()
const signal = new AbortController().signal
const files: number[] = []
const stop = trackAiVaultScanProgress(
signal,
(work) => files.push(work.agents[0]?.files ?? 0),
() => Date.now()
)
noteAiVaultScanFileFound(signal, 'codex', codexFile(0))
noteAiVaultScanWorkChanged(signal)
for (let index = 1; index < 16; index++) {
noteAiVaultScanFileFound(signal, 'codex', codexFile(index))
noteAiVaultScanWorkChanged(signal)
}
expect(files).toEqual([1])
vi.advanceTimersByTime(AI_VAULT_SCAN_PROGRESS_INTERVAL_MS)
expect(files).toEqual([1, 16])
stop()
noteAiVaultScanWorkChanged(signal)
vi.advanceTimersByTime(AI_VAULT_SCAN_PROGRESS_INTERVAL_MS)
expect(files).toEqual([1, 16])
})
it('reports discovery and reading for its own scan, never for an unrelated one', async () => {
const root = await tempRoot()
const roots = isolatedScanRoots(root)
@@ -104,19 +70,3 @@ describe('scan progress reporting', () => {
expect(reports.at(-1)).toMatchObject({ stage: 'reading', read: { done: 3, total: 3 } })
})
})
it('keeps source ownership and final facts independent of immediate and trailing sink faults', () => {
vi.useFakeTimers()
const signal = new AbortController().signal
const sink = vi.fn(() => {
throw new Error('Synthetic reporting sink fault')
})
const stop = trackAiVaultScanProgress(signal, sink, () => Date.now())
const close = openAiVaultScanSource(signal, 'claude')
expect(aiVaultScanWork(signal)?.agents[0]?.walking).toBe(true)
close()
expect(() => vi.advanceTimersByTime(AI_VAULT_SCAN_PROGRESS_INTERVAL_MS)).not.toThrow()
expect(sink).toHaveBeenCalledTimes(2)
expect(aiVaultScanWork(signal)?.agents).toEqual([])
stop()
})
@@ -1,44 +0,0 @@
import { expect, it, vi } from 'vitest'
import type * as OpenCodeWslClient from './session-scanner-opencode-wsl-client'
import type { OpenCodeSqliteWorkerClient } from './session-scanner-opencode-sqlite-worker-client'
import type { AiVaultScanIssue } from '../../../../../shared/ai-vault/contracts/ai-vault-types'
import { listOpenCodeSqliteSessionsViaWorker } from './session-scanner-opencode-sqlite-worker-spawn'
const mocks = vi.hoisted(() => ({ list: vi.fn<OpenCodeSqliteWorkerClient['list']>() }))
vi.mock('./session-scanner-opencode-wsl-client', async (importOriginal) => {
const { parseWslUncPath } = await import('../../../../../shared/wsl-paths')
return {
...(await importOriginal<typeof OpenCodeWslClient>()),
openCodeWslPath: parseWslUncPath,
openCodeWslClient: async () => ({ list: mocks.list })
}
})
it('routes scoped SQLite reads only to the owning distro and reports a scoped-only failure', async () => {
const ubuntu = String.raw`\\wsl.localhost\Ubuntu\home\ada\opencode.db`
const debian = String.raw`\\wsl.localhost\Debian\home\ada\opencode.db`
const issues: AiVaultScanIssue[] = []
mocks.list.mockImplementation(async (args) => {
if (args.scopePaths?.length) {
expect(args.scopePaths).toEqual(['/home/ada/repo'])
args.issues.push({
agent: 'opencode',
kind: 'scope',
path: args.dbPaths[0],
message: 'Scoped read timed out'
})
}
return []
})
await listOpenCodeSqliteSessionsViaWorker({ dbPaths: [ubuntu], limit: 1, issues })
await listOpenCodeSqliteSessionsViaWorker({
dbPaths: [ubuntu, debian],
limit: 1,
issues,
scopePaths: [String.raw`\\wsl.localhost\Ubuntu\home\ada\repo`]
})
expect(mocks.list).toHaveBeenCalledTimes(2)
expect(issues).toEqual([
{ agent: 'opencode', kind: 'scope', path: ubuntu, message: 'Scoped read timed out' }
])
})
@@ -1,10 +1,9 @@
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
import type { Dirent } from 'node:fs'
import type * as NodeFsPromisesModule from 'node:fs/promises'
import type { AiVaultScanIssue } from '../../../../shared/ai-vault/contracts/ai-vault-types'
const WSL_HOME = '\\\\wsl.localhost\\Ubuntu\\home\\ada'
const WSL_DATA_DIR = `${WSL_HOME}/.local/share/opencode`
const mocks = vi.hoisted(() => ({ readdir: vi.fn(), stat: vi.fn() }))
@@ -58,31 +57,6 @@ afterEach(async () => {
})
describe('OpenCode source discovery with a stalled WSL data directory', () => {
it('reports the stalled home as an issue and still resolves every discovery', async () => {
mocks.readdir.mockImplementation((dir: string) =>
dir === WSL_DATA_DIR ? stalls() : Promise.resolve([] as Dirent[])
)
const issues: AiVaultScanIssue[] = []
const discoveries = Promise.all(
opencodeDiscoveries(
{ opencodeStorageDir: '/home/ada/.local/share/opencode/storage' },
[WSL_HOME],
10,
issues
)
)
// The storage scan queues before the data-dir timeout marks the route stuck; both reach their deadlines.
await vi.advanceTimersByTimeAsync(WSL_TRANSCRIPT_FS_SCAN_TIMEOUT_MS * 2 + 2)
// A scan issue distinguishes an unreachable source from an uninstalled agent.
const resolved = await discoveries
expect(resolved).toHaveLength(4)
expect(mocks.readdir.mock.calls.filter(([path]) => path === WSL_DATA_DIR)).toHaveLength(1)
expect(resolved.every((discovery) => discovery.files.length === 0)).toBe(true)
expect(issues.some((issue) => issue.path === WSL_DATA_DIR)).toBe(true)
expect(issues.every((issue) => issue.agent === 'opencode')).toBe(true)
})
it('reports a refused primary data directory instead of scanning clean', async () => {
const previousXdg = process.env.XDG_DATA_HOME
const previousDb = process.env.OPENCODE_DB
@@ -10,7 +10,7 @@ import { copyStructuredChatConversation } from './structured-chat-conversation-c
import { installCopyingChat as installChat } from './structured-session-ownership.test-support'
const OMP_ID = '019fd611-2b3c-7d4e-9f50-6b7c8d9e0f1a'
const OPENCODE_ID = 'ses_2f9aB3cD4eF5gH6iJ7kL8mN9oP'
const OMP_FILE = `/sessions/${OMP_ID}.jsonl`
const acp = (agent: 'opencode' | 'omp', nativeId: string): AgentSessionProviderHandle => ({
@@ -34,31 +34,6 @@ const fork = (
afterEach(() => setStructuredAgentSessionHost(null))
describe('copying a chat conversation for Resume in New CLI', () => {
it.each([
{ agent: 'omp' as const, handle: acp('omp', OMP_ID), sessionId: OMP_ID },
{ agent: 'opencode2' as const, handle: acp('opencode', OPENCODE_ID), sessionId: OPENCODE_ID }
])(
"has the chat's host copy a $agent row's conversation",
async ({ agent, handle, sessionId }) => {
const copy = vi.fn(async () => ({
sessionId: 'copy-1',
filePath: '/sessions/copy-1.jsonl'
}))
installChat(handle, copy)
await expect(
copyStructuredChatConversation(fork({ agent, sessionId, filePath: `/x/${sessionId}` }))
).resolves.toEqual({
useRealCodexHome: false,
forkedSession: {
sessionId: 'copy-1',
filePath: '/sessions/copy-1.jsonl',
agent: handle.agent
}
})
}
)
it.each(['source', 'owned'])('rejects a %s fork id on the execution host', async (returned) => {
const lease = agentSessionLeaseFixture()
const fixture = agentSessionRecordFixture(lease)
@@ -1,73 +0,0 @@
import { it, expect, vi } from 'vitest'
import { mkdtempSync, rmSync } from 'node:fs'
import { join } from 'node:path'
import { tmpdir } from 'node:os'
import { createStore, closeTestStores, testState } from '../persistence/persistence-test-harness'
import { buildProfileStateCutoverFixture } from '../persistence/profile-state-cutover-fixture'
import { AutomationRunLaunchError } from './headless-dispatch'
import { AutomationService } from './service'
vi.mock('electron', () => ({
app: { getPath: () => testState.dir },
safeStorage: {
isEncryptionAvailable: () => true,
encryptString: (plaintext: string) => Buffer.from(`encrypted:${plaintext}`, 'utf-8'),
decryptString: (ciphertext: Buffer) => ciphertext.toString('utf-8').slice('encrypted:'.length)
}
}))
it.each(['chat', 'terminal'] as const)(
'durably saves an unsuccessful published %s target without a resend',
async (kind) => {
testState.dir = mkdtempSync(join(tmpdir(), 'orca-34332-failed-target-'))
const store = await createStore()
try {
store.addRepo({
...buildProfileStateCutoverFixture('/repo').repos[0],
id: 'r1',
path: '/repo'
})
const automation = store.createAutomation({
name: 'Failed delivery',
prompt: 'check',
agentId: 'claude',
launchSetupPatch: { surface: 'chat' },
projectId: 'r1',
workspaceMode: 'existing',
workspaceId: 'r1::/repo',
timezone: 'UTC',
rrule: 'FREQ=DAILY',
dtstart: 1
})
const published = {
workspaceId: 'actually-created',
sessionKind: kind,
chatSessionId: kind === 'chat' ? 'claude_actual' : null,
terminalSessionId: kind === 'terminal' ? 'actual-tab' : null,
terminalPaneKey:
kind === 'terminal' ? 'actual-tab:11111111-1111-4111-8111-111111111111' : null,
terminalPtyId: kind === 'terminal' ? 'actual-pty' : null
}
const dispatch = vi.fn(async () => {
throw new AutomationRunLaunchError(
'Claude could not confirm delivery. Open the run.',
published
)
})
const service = new AutomationService(store, { chatDispatcher: dispatch })
service.setWebContents({ isDestroyed: () => false, send: vi.fn() })
service.setRendererReady()
const failed = await service.runNow(automation.id)
expect(failed).toMatchObject({ ...published, status: 'dispatch_failed' })
expect(store.listAutomationRuns()[0]).toMatchObject({
...published,
status: 'dispatch_failed'
})
expect(dispatch).toHaveBeenCalledOnce()
service.stop()
} finally {
await closeTestStores()
rmSync(testState.dir, { recursive: true, force: true })
}
}
)
@@ -1,16 +0,0 @@
import { describe, expect, it } from 'vitest'
import { BrowserClientHostAttachParams } from '../../../../shared/browser/client-hosting/browser-client-host-protocol'
describe('browser file channel negotiation', () => {
it('rejects an attach that asks for the file channel without command negotiation', () => {
expect(
BrowserClientHostAttachParams.safeParse({
authorityRuntimeId: 'runtime-1',
browserHostClientId: 'host-1',
hostCapabilities: ['webview'],
fileChannelProtocolVersion: 1
}).success
).toBe(false)
})
})
@@ -1,91 +0,0 @@
import { mkdtemp, realpath, rm } from 'node:fs/promises'
import { tmpdir } from 'node:os'
import path from 'node:path'
import { afterEach, beforeEach, describe, expect, it } from 'vitest'
import type { BrowserClientHostCommandEvent } from '../../../../shared/browser/client-hosting/browser-client-host-protocol'
import { BrowserClientFileChannelTransport } from './browser-client-file-channel-transport'
import { executeBrowserClientUploadCommand } from './browser-client-upload-command'
import { BrowserClientUploadStaging } from './browser-client-upload-staging'
let stagingRoot = ''
beforeEach(async () => {
stagingRoot = await realpath(await mkdtemp(path.join(tmpdir(), 'orca-upload-command-')))
})
afterEach(async () => {
await rm(stagingRoot, { recursive: true, force: true })
})
function uploadEvent(files: unknown): BrowserClientHostCommandEvent {
return {
type: 'command',
pageCommandProtocolVersion: 1,
authorityRuntimeId: 'runtime-1',
authorityEpoch: 'epoch-1',
browserHostClientId: 'host-1',
browserHostGeneration: 2,
browserPageId: 'page-1',
pageHostGeneration: 3,
commandSequence: 1,
commandId: 'command-1',
command: { type: 'automation', method: 'browser.upload', params: { element: '#f', files } }
} as BrowserClientHostCommandEvent
}
function transportReturning(
responses: Map<string, { contentBase64: string; bytesRead: number; eof: boolean }[]>
): { transport: BrowserClientFileChannelTransport; calls: unknown[] } {
const calls: unknown[] = []
const transport = new BrowserClientFileChannelTransport()
transport.bind({
fileChannelNegotiated: true,
fileChannelAvailability: 'negotiated' as const,
sendFileChannelRequest: async (_method, params) => {
calls.push(params)
const requested = (params as { workspaceRelativePath: string }).workspaceRelativePath
const queue = responses.get(requested)
if (!queue?.length) {
return { ok: false, error: { code: 'not_found', message: 'missing' }, _meta: {} } as never
}
const next = queue.shift()
return {
ok: true,
result: { ...next, totalBytes: next?.bytesRead ?? 0 },
_meta: {}
} as never
}
})
return { transport, calls }
}
describe('executeBrowserClientUploadCommand', () => {
it('reports the upload failure, not the cleanup failure, when both fail', async () => {
const staging = new BrowserClientUploadStaging(stagingRoot, {
mkdir: async () => {},
writeFile: async () => {},
removeDirectorySync: () => {},
removeDirectory: async () => {
throw new Error('EBUSY: resource busy or locked')
}
})
const { transport } = transportReturning(
new Map([['a.txt', [{ contentBase64: '', bytesRead: 0, eof: true }]]])
)
await expect(
executeBrowserClientUploadCommand({
event: uploadEvent(['a.txt']),
params: { element: '#f', files: ['a.txt'] },
fileChannel: transport,
staging,
run: async () => {
throw new Error('element not found')
}
})
).rejects.toThrow('element not found')
// A later page release needs the retained directory record to retry cleanup.
expect(staging.activeStagingCount()).toBe(1)
})
})
@@ -1,124 +0,0 @@
import { mkdtempSync } from 'node:fs'
import { tmpdir } from 'node:os'
import { join } from 'node:path'
import { afterEach, describe, expect, it, vi } from 'vitest'
import type { KnownRuntimeEnvironment } from '../../../../shared/remote-runtime/host-catalog/runtime-environments'
vi.mock('electron', () => ({
app: { getPath: () => tmpdir() },
ipcMain: { on: vi.fn(), removeListener: vi.fn() }
}))
type CreateHost = (
next: { pairing: unknown; authorityRuntimeId: string },
callbacks: Record<string, unknown>
) => unknown
const createHostCallbacks: CreateHost[] = []
const hostOptions: { browserHostClientId: string }[] = []
vi.mock('./paired-runtime-browser-client-host', () => ({
PairedRuntimeBrowserClientHost: class {
constructor(options: { browserHostClientId: string }) {
hostOptions.push(options)
}
}
}))
vi.mock('../file-transfers/browser-client-file-channel-transport', () => ({
BrowserClientFileChannelTransport: class {
bind(): void {}
}
}))
vi.mock('./paired-runtime-browser-client-host-composition', () => ({
PairedRuntimeBrowserClientHostComposition: class {
constructor(options: { createHost: CreateHost }) {
createHostCallbacks.push(options.createHost)
}
start(): Promise<unknown> {
return Promise.resolve({ authority: 'lease-a' })
}
close(): Promise<boolean> {
return Promise.resolve(true)
}
whenClosed(): Promise<void> {
return Promise.resolve()
}
}
}))
function pairedEnvironment(id: string): KnownRuntimeEnvironment {
return {
id,
name: `Environment ${id}`,
createdAt: 1,
updatedAt: 1,
lastUsedAt: null,
runtimeId: null,
preferredEndpointId: 'endpoint-a',
endpoints: [
{
id: 'endpoint-a',
endpoint: 'ws://127.0.0.1:9999',
deviceToken: 'token-a',
publicKeyB64: 'key-a'
}
]
} as KnownRuntimeEnvironment
}
async function launch(profileDirectory: string): Promise<void> {
vi.resetModules()
createHostCallbacks.length = 0
hostOptions.length = 0
const { initializeBrowserClientHostId } = await import('./browser-client-host-id')
initializeBrowserClientHostId(profileDirectory)
}
async function hostingIdentity(): Promise<string> {
const runtime = await import('./paired-runtime-browser-client-host-runtime')
runtime.configurePairedRuntimeBrowserClientHostsForOrcaProfile({ orcaProfileId: 'profile-a' })
await runtime.startPairedRuntimeBrowserClientHost({
environment: pairedEnvironment('environment-a'),
authorityRuntimeId: 'runtime-a'
})
const createHost = createHostCallbacks[0]
if (!createHost) {
throw new Error('client host composition never asked for a host')
}
createHost({ pairing: {}, authorityRuntimeId: 'runtime-a' }, {})
const options = hostOptions[0]
if (!options) {
throw new Error('client host was never constructed')
}
return options.browserHostClientId
}
async function hostingIdentityForLaunch(profileDirectory: string): Promise<string> {
await launch(profileDirectory)
return await hostingIdentity()
}
afterEach(() => {
vi.resetModules()
})
describe('client host hosting identity wiring', () => {
it('attaches under the same identity after the desktop relaunches', async () => {
const profileDirectory = mkdtempSync(join(tmpdir(), 'orca-host-wiring-'))
expect(await hostingIdentityForLaunch(profileDirectory)).toBe(
await hostingIdentityForLaunch(profileDirectory)
)
})
it('attaches under a different identity for a different Orca profile', async () => {
expect(
await hostingIdentityForLaunch(mkdtempSync(join(tmpdir(), 'orca-host-wiring-')))
).not.toBe(await hostingIdentityForLaunch(mkdtempSync(join(tmpdir(), 'orca-host-wiring-'))))
})
})
@@ -9,26 +9,6 @@ describe('browser client page inventory', () => {
expect(prepareBrowserClientPageInventoryForAttach([page, page])).toBeUndefined()
})
it('uses codepoint order to break equal URL-compaction ties across input order', () => {
const pageIds = [
'ä-page',
'z-page',
...Array.from({ length: 254 }, (_, index) => `page-${index.toString().padStart(3, '0')}`)
]
const inventory = pageIds.map(inventoryPage)
const forward = prepareBrowserClientPageInventoryForAttach(inventory)
const reversed = prepareBrowserClientPageInventoryForAttach(inventory.toReversed())
if (!forward || !reversed) {
throw new Error('expected encodable inventory')
}
const omitted = omittedPageIds(forward)
const expected = [...pageIds].sort(compareCodepoints).slice(0, omitted.length)
expect(omitted.length).toBeGreaterThan(0)
expect(omitted).toEqual(expected)
expect(omittedPageIds(reversed)).toEqual(expected)
})
it('declines the optional snapshot instead of dropping an unencodable page', () => {
expect(
prepareBrowserClientPageInventoryForAttach([
@@ -53,14 +33,3 @@ function inventoryPage(browserPageId: string): BrowserClientHostedPageInventory
currentUrl: `https://remote.internal/${'x'.repeat(4096)}`
}
}
function omittedPageIds(pages: readonly BrowserClientHostedPageInventory[]): string[] {
return pages
.filter((page) => page.currentUrl === undefined)
.map((page) => page.browserPageId)
.sort(compareCodepoints)
}
function compareCodepoints(left: string, right: string): number {
return left < right ? -1 : left > right ? 1 : 0
}
@@ -72,12 +72,6 @@ describe('BrowserExtensionInstallStore', () => {
store.save(record({ id: STORE_ID }))
expect(store.get(STORE_ID)?.installTime).toBeGreaterThanOrEqual(updated ?? 0)
})
it('refuses install directories that could escape the root', () => {
const store = new BrowserExtensionInstallStore(root)
expect(() => store.installDirectory('../evil', '1.0')).toThrow()
expect(() => store.installDirectory(STORE_ID, '../../1.0')).toThrow()
})
})
describe('readBrowserExtensionManifest', () => {
@@ -1,106 +0,0 @@
/**
* R45: which in-app browser jar a WSL host's pages get, pinned by what can change about the host.
* The jar follows the environment (named by the distro) and orcad's key; the distro's registry id
* is deliberately not part of it (#29610).
*/
import { mkdtempSync, realpathSync, rmSync } from 'node:fs'
import { tmpdir } from 'node:os'
import { join } from 'node:path'
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
import type { KnownRuntimeEnvironment } from '../../../../shared/remote-runtime/host-catalog/runtime-environments'
import { defaultWslOrcadEnvironmentId } from '../../../wsl/orcad/wsl-orcad-deploy'
const bindings = vi.hoisted(() => ({ filePath: '' }))
vi.mock('electron', () => ({
app: { getPath: () => tmpdir() },
ipcMain: { on: vi.fn(), removeListener: vi.fn() }
}))
vi.mock('../partitions/browser-route-partition-binding-runtime', async () => {
const { BrowserRoutePartitionBindingStore } =
await import('../partitions/browser-route-partition-binding-store')
return {
currentBrowserRoutePartitionBindingStore: () =>
new BrowserRoutePartitionBindingStore({ filePath: bindings.filePath })
}
})
vi.mock('../partitions/browser-route-partition-storage-dependencies', () => ({
releaseEvictedBrowserRoutePartitionStorage: vi.fn(async () => {})
}))
import {
bindWslHostBrowserRoutePartition,
wslHostBrowserRouteStorage
} from './wsl-host-browser-route-partition'
const ORCA_PROFILE = 'profile-a'
function wslHost(
distro: string,
hostKey: string,
pairing = { revision: 1, device: 'device-a' }
): KnownRuntimeEnvironment {
return {
id: defaultWslOrcadEnvironmentId(distro),
name: distro,
createdAt: 1,
updatedAt: 1,
lastUsedAt: null,
runtimeId: null,
pairingRevision: pairing.revision,
pairedDeviceId: pairing.device,
orcadDeployment: { kind: 'wsl', distro, localPort: 9999, remotePort: 7777 },
preferredEndpointId: 'endpoint-a',
endpoints: [
{
id: 'endpoint-a',
kind: 'websocket',
label: distro,
endpoint: 'ws://127.0.0.1:9999',
deviceToken: `token-${pairing.device}`,
publicKeyB64: hostKey
}
]
}
}
function jar(environment: KnownRuntimeEnvironment): string {
const result = wslHostBrowserRouteStorage(ORCA_PROFILE, environment)
if (result.kind !== 'stable') {
throw new Error(`expected a stable partition, got ${result.reason}`)
}
return bindWslHostBrowserRoutePartition(result.storage, 'default')
}
let directory: string
beforeEach(() => {
directory = realpathSync(mkdtempSync(join(realpathSync(tmpdir()), 'wsl-host-jar-')))
bindings.filePath = join(directory, 'bindings.json')
})
afterEach(() => {
rmSync(directory, { recursive: true, force: true })
})
describe('WSL host browser jar identity', () => {
it('starts a new jar when the distro runs a newly deployed orcad (a new key)', () => {
const before = jar(wslHost('Ubuntu', 'orcad-key-a'))
expect(jar(wslHost('Ubuntu', 'orcad-key-b'))).not.toBe(before)
// The old jar is still the old key's; nothing of it moved to the new one.
expect(jar(wslHost('Ubuntu', 'orcad-key-a'))).toBe(before)
})
it('starts a new jar for a renamed distro, even one that kept its orcad key', () => {
const before = jar(wslHost('Ubuntu', 'orcad-key-a'))
expect(jar(wslHost('Ubuntu-work', 'orcad-key-a'))).not.toBe(before)
})
// Accepted (R45, Low): the same name and key is the same host to this desktop.
it('keeps the jar for a distro re-imported under its old name with its old key', () => {
const before = jar(wslHost('Ubuntu', 'orcad-key-a'))
expect(jar(wslHost('Ubuntu', 'orcad-key-a', { revision: 7, device: 'device-b' }))).toBe(before)
})
})
@@ -1,116 +0,0 @@
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
import { join } from 'node:path'
import { createSettings } from './runtime-home-settings-test-fixtures'
import {
createStore,
setupRuntimeHomeTest,
teardownRuntimeHomeTest,
testState
} from './runtime-home-service-test-harness'
vi.mock('electron', () => ({
app: {
getPath: () => testState.userDataDir
}
}))
vi.mock(import('node:os'), async (importOriginal) => {
const actual = await importOriginal()
return {
...actual,
homedir: () => testState.fakeHomeDir
}
})
const bridge = vi.hoisted(() => ({
start: vi.fn<(target: Record<string, string>) => Promise<void>>(() => Promise.resolve())
}))
vi.mock('../codex/history/bridges/wsl-codex-session-bridge', () => ({
startWslCodexSessionBridgeInBackground: bridge.start
}))
let wslHome = ''
vi.mock('../wsl/discovery/distro-catalog', () => ({
getDefaultWslDistro: () => 'Ubuntu',
getWslHome: () => wslHome
}))
function accountsRoot(): string {
return join(wslHome, '.local', 'share', 'orca', 'codex-accounts')
}
function wslAccount(id: string, distro: string) {
return {
id,
email: `${id}@example.com`,
managedHomePath: join(accountsRoot(), id, 'home'),
managedHomeRuntime: 'wsl' as const,
wslDistro: distro,
wslLinuxHomePath: join(accountsRoot(), id, 'home'),
providerAccountId: null,
workspaceLabel: null,
workspaceAccountId: null,
createdAt: 1,
updatedAt: 1,
lastAuthenticatedAt: 1
}
}
async function createService(selectedUbuntuAccount: string | null) {
const store = createStore(
createSettings({
codexManagedAccounts: [
wslAccount('a', 'Ubuntu'),
wslAccount('b', 'Ubuntu'),
wslAccount('other-distro', 'Debian')
],
activeCodexManagedAccountIdsByRuntime: {
host: null,
wsl: { Ubuntu: selectedUbuntuAccount }
}
})
)
const { CodexRuntimeHomeService } = await import('./runtime-home-service')
// oxlint-disable-next-line typescript/consistent-type-assertions -- SAFETY: the harness store implements the getSettings/updateSettings surface this service reads.
return new CodexRuntimeHomeService(store as never)
}
describe('WSL sibling account session bridging', () => {
let originalPlatform: PropertyDescriptor | undefined
beforeEach(() => {
setupRuntimeHomeTest()
originalPlatform = Object.getOwnPropertyDescriptor(process, 'platform')
Object.defineProperty(process, 'platform', { configurable: true, value: 'win32' })
wslHome = join(testState.userDataDir, 'ubuntu-home')
bridge.start.mockReset()
bridge.start.mockImplementation(() => Promise.resolve())
})
afterEach(() => {
if (originalPlatform) {
Object.defineProperty(process, 'platform', originalPlatform)
}
teardownRuntimeHomeTest()
})
it('lets a concurrent launch join the sibling loop already bridging that home', async () => {
let releaseFirst: () => void = () => {}
bridge.start.mockImplementationOnce(
() =>
new Promise<void>((resolve) => {
releaseFirst = resolve
})
)
const service = await createService('b')
service.prepareForCodexLaunch({ runtime: 'wsl', wslDistro: 'Ubuntu' })
service.prepareForCodexLaunch({ runtime: 'wsl', wslDistro: 'Ubuntu' })
await vi.waitFor(() => expect(bridge.start).toHaveBeenCalledTimes(1))
releaseFirst()
await vi.waitFor(() => expect(bridge.start).toHaveBeenCalledTimes(2))
await Promise.resolve()
expect(bridge.start).toHaveBeenCalledTimes(2)
})
})
@@ -1,99 +0,0 @@
import { afterEach, describe, expect, it, vi } from 'vitest'
import { join } from 'node:path'
import { tmpdir } from 'node:os'
import { DaemonPtyAdapter } from './daemon-pty-adapter'
import { DegradedDaemonPtyProvider } from './degraded-daemon-pty-provider'
import { LocalPtyProvider } from '../providers/local-pty/local-pty-provider'
import type { PtyProcessInfo } from '../providers/pty-provider-contract'
import { TerminalSessionOwnerUnverifiedError } from './daemon-errors'
const cleanups: (() => void)[] = []
afterEach(() => {
for (const cleanup of cleanups.splice(0)) {
cleanup()
}
vi.restoreAllMocks()
})
function adapterFixture(label: string, pid: number) {
const adapter = new DaemonPtyAdapter({
socketPath: join(tmpdir(), `unused-${label}.sock`),
tokenPath: join(tmpdir(), `unused-${label}.token`)
})
const client = adapter['client']
vi.spyOn(client, 'ensureConnected').mockResolvedValue()
vi.spyOn(client, 'ensureConnectedWithin').mockResolvedValue()
const identity = vi.spyOn(client, 'getDaemonIdentity')
const request = vi.spyOn(client, 'request').mockResolvedValue({ sessions: [] })
const spawn = vi.spyOn(adapter, 'spawn').mockImplementation(async (opts) => ({
id: opts.sessionId ?? 'unexpected-fresh-spawn',
incarnationId: opts.expectedIncarnationId,
isReattach: true
}))
return {
adapter,
request,
spawn,
setProcesses(processes: PtyProcessInfo[]) {
request.mockResolvedValue({
sessions: processes.map((process) => ({
sessionId: process.id,
incarnationId: process.incarnationId,
cwd: process.cwd,
isAlive: true
}))
})
},
async publishIdentity(generation: number) {
identity.mockReturnValue({
pid,
startedAtMs: generation + 1,
launchNonce: label + generation
})
await adapter.establishLifecycleLease()
}
}
}
async function fixture() {
const current = adapterFixture('current', 999_999_997)
const legacy = adapterFixture('legacy', 999_999_998)
const fallback = new LocalPtyProvider()
vi.spyOn(fallback, 'listProcesses').mockResolvedValue([])
const provider = new DegradedDaemonPtyProvider({
current: current.adapter,
legacy: [legacy.adapter],
fallback
})
cleanups.push(() => provider.dispose())
await current.publishIdentity(0)
await legacy.publishIdentity(0)
const recovery = provider['ownerRecovery']
return { current, legacy, provider, recovery }
}
function processInfo(id: string, incarnationId: string): PtyProcessInfo {
return { id, incarnationId, cwd: '', title: 'shell' }
}
describe('shared daemon owner incarnation retirement', () => {
it('retains authoritative incarnation mismatch refusal after another daemon changes', async () => {
const { current, legacy, provider } = await fixture()
legacy.setProcesses([processInfo('live', 'current-incarnation')])
await provider.discoverDaemonSessions()
await current.publishIdentity(1)
await expect(
provider.spawn({
sessionId: 'live',
attachOnly: true,
cols: 80,
rows: 24,
expectedIncarnationId: 'retired-incarnation',
expectedIncarnationIsAuthoritative: true
})
).rejects.toBeInstanceOf(TerminalSessionOwnerUnverifiedError)
expect(current.spawn).not.toHaveBeenCalled()
expect(legacy.spawn).not.toHaveBeenCalled()
})
})
@@ -46,39 +46,6 @@ describe('tracked gh capture lifetime', () => {
vi.restoreAllMocks()
})
it.each([false, true])(
'keeps grace settlement separate from termination (abort: %s)',
async (abort) => {
const value = child(1234)
spawnMock.mockReturnValue(value)
const owner = createGhLogicalLifetime()
const controller = new AbortController()
const result = ghExecFileAsync(
['api', 'user'],
{ timeout: 10, ...(abort ? { signal: controller.signal } : {}) },
owner.hooks
)
const rejection = expect(result).rejects.toThrow()
await vi.advanceTimersByTimeAsync(0)
if (abort) {
controller.abort('private-reason')
}
await vi.advanceTimersByTimeAsync(12_010)
await rejection
owner.seal()
let terminated = false
void owner.terminated.then(() => {
terminated = true
})
await vi.advanceTimersByTimeAsync(0)
expect(terminated).toBe(false)
expect(spawnMock).toHaveBeenCalledOnce()
value.emit('close', null, 'SIGKILL')
await vi.advanceTimersByTimeAsync(0)
expect(terminated).toBe(true)
}
)
it('waits for a PID error close before retry and ignores its stale callback', async () => {
const values = [child(11), child(12)]
spawnMock.mockReturnValueOnce(values[0]).mockReturnValueOnce(values[1])
@@ -1,103 +0,0 @@
import { chmod, mkdtemp, mkdir, rm, writeFile } from 'node:fs/promises'
import { tmpdir } from 'node:os'
import path from 'node:path'
import { afterEach, describe, expect, it } from 'vitest'
import { gitExecFileAsync, gitExecFileAsyncBuffer } from './git-exec-file'
import { GitCommandTimeoutError } from '../../../shared/git/commands/git-command-timeout'
import { gitStreamStdout } from './git-stream-stdout'
const tempRoots: string[] = []
afterEach(async () => {
await Promise.all(tempRoots.splice(0).map((root) => rm(root, { recursive: true, force: true })))
})
async function createTimedGitFixture(): Promise<{
cwd: string
env: NodeJS.ProcessEnv
}> {
const root = await mkdtemp(path.join(tmpdir(), 'orca-git-timeout-'))
tempRoots.push(root)
const binDir = path.join(root, 'bin')
const cwd = path.join(root, 'repo')
await mkdir(binDir)
await mkdir(cwd)
const script = path.join(binDir, 'git')
await writeFile(
script,
`#!/usr/bin/env node
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms))
async function run() {
if (process.env.ORCA_STUB_PROGRESSIVE === '1') {
const remaining = Math.max(0, Number(process.env.ORCA_STUB_EXIT_AT_MS) - Date.now())
const step = remaining / 3
await sleep(step)
process.stdout.write('one')
await sleep(step)
process.stdout.write('two')
await sleep(step)
process.stdout.write('three')
return
}
await sleep(Number(process.env.ORCA_STUB_SLEEP_MS))
process.stdout.write('done')
}
void run()
`
)
await chmod(script, 0o755)
return {
cwd,
env: { ...process.env, PATH: `${binDir}${path.delimiter}${process.env.PATH ?? ''}` }
}
}
describe.skipIf(process.platform === 'win32')('git read timeout behavior', () => {
it('rejects a silent read at 1.1 times the deadline with a typed error', async () => {
const fixture = await createTimedGitFixture()
await expect(
gitExecFileAsync(['status', '--porcelain=v2'], {
...fixture,
env: { ...fixture.env, ORCA_STUB_SLEEP_MS: '110' },
timeoutMsForTest: 100
})
).rejects.toBeInstanceOf(GitCommandTimeoutError)
})
it('times out a silent streaming read with the same typed error', async () => {
const fixture = await createTimedGitFixture()
await expect(
gitStreamStdout(['status'], {
...fixture,
env: { ...fixture.env, ORCA_STUB_SLEEP_MS: '110' },
timeoutMsForTest: 100,
onStdout: () => {}
})
).rejects.toBeInstanceOf(GitCommandTimeoutError)
})
it('applies the read default to binary blob reads', async () => {
const fixture = await createTimedGitFixture()
await expect(
gitExecFileAsyncBuffer(['show', 'HEAD:file.bin'], {
...fixture,
env: { ...fixture.env, ORCA_STUB_SLEEP_MS: '110' },
timeoutMsForTest: 100
})
).rejects.toBeInstanceOf(GitCommandTimeoutError)
})
it.each([['fetch'], ['checkout'], ['unrecognized-command']])(
'does not default-timeout the fail-safe %s class',
async (subcommand) => {
const fixture = await createTimedGitFixture()
await expect(
gitExecFileAsync([subcommand], {
...fixture,
env: { ...fixture.env, ORCA_STUB_SLEEP_MS: '110' },
timeoutMsForTest: 100
})
).resolves.toMatchObject({ stdout: 'done' })
}
)
})
@@ -1,11 +1,7 @@
import { afterEach, describe, expect, it } from 'vitest'
import { resolveGitAdmissionTier } from './git-operation-executor'
import {
acquireGitAdmission,
_resetGitAdmissionForTests,
_gitAdmissionSnapshotForTests
} from './git-subprocess-admission'
import { GitAdmissionScheduler } from '../../../shared/git/admission/git-admission-scheduler'
import { _resetGitAdmissionForTests } from './git-subprocess-admission'
import {
worktreeCreateGit,
worktreePreparationGit
@@ -34,44 +30,4 @@ describe('Git operation execution policy', () => {
await create
expect(resolveGitAdmissionTier()).toBe('status')
})
it.each([false, true])(
'expires inherited policy after completion (failure: %s)',
async (fail) => {
const finishDetached = Promise.withResolvers<void>()
let detached: Promise<string> | undefined
const create = worktreeCreateGit.run(async () => {
detached = finishDetached.promise.then(() => resolveGitAdmissionTier())
if (fail) {
throw new Error('create failed')
}
})
await (fail ? expect(create).rejects.toThrow('create failed') : create)
finishDetached.resolve()
await expect(detached).resolves.toBe('status')
}
)
it('admits nested commands without priority options while preparation work stays queued', async () => {
_resetGitAdmissionForTests(new GitAdmissionScheduler({ generalCap: 1, generalHeadroom: 1 }))
const blocker = await acquireGitAdmission({ args: ['status'], cwd: '/repo' })
const preparation = worktreePreparationGit.run(() =>
acquireGitAdmission({ args: ['status'], cwd: '/repo' })
)
try {
await worktreeCreateGit.run(async () => {
const grant = await acquireGitAdmission({ args: ['rev-parse', 'HEAD'], cwd: '/repo' })
expect(_gitAdmissionSnapshotForTests()).toMatchObject({
queued: 1,
budgets: { general: { baseUsed: 1, headroomUsed: 1 } }
})
grant.release()
})
} finally {
blocker.release()
const grant = await preparation
grant.release()
}
expect(_gitAdmissionSnapshotForTests().queued).toBe(0)
})
})
@@ -1,56 +0,0 @@
import { describe, expect, it } from 'vitest'
import type { GitDiffResult } from '../../../shared/git/diff/git-diff-compare-types'
import { MAX_SETTLED_DIFF_CACHE_RESULT_CHARACTERS, SettledDiffCache } from './settled-diff-cache'
import type { WorktreeDiffStamp } from './worktree-diff-stamp'
function settledStamp(value: string): WorktreeDiffStamp {
// Old enough that the racy-write margin is satisfied.
return { value, newestMtimeMs: Date.now() - 60_000, capturedAtMs: Date.now() }
}
function diffOfSize(characters: number): GitDiffResult {
return {
kind: 'text',
originalContent: '',
modifiedContent: 'x'.repeat(characters),
originalIsBinary: false,
modifiedIsBinary: false
}
}
describe('SettledDiffCache bounds', () => {
it('declines a single result larger than the per-entry cap', () => {
const cache = new SettledDiffCache()
cache.set(
'huge',
settledStamp('huge-stamp'),
diffOfSize(MAX_SETTLED_DIFF_CACHE_RESULT_CHARACTERS + 1),
cache.beginRead()
)
expect(cache.stats().entries).toBe(0)
expect(cache.stats().retainedCharacters).toBe(0)
})
it('releases retained characters when a key is overwritten', () => {
const cache = new SettledDiffCache()
cache.set('key', settledStamp('first'), diffOfSize(1_000), cache.beginRead())
cache.set('key', settledStamp('second'), diffOfSize(10), cache.beginRead())
expect(cache.stats().entries).toBe(1)
expect(cache.stats().retainedCharacters).toBe(10)
expect(cache.get('key', settledStamp('first'))).toBeUndefined()
expect(cache.get('key', settledStamp('second'))).toBeDefined()
})
it('drops everything and refuses in-flight stores after a clear', () => {
const cache = new SettledDiffCache()
const readGeneration = cache.beginRead()
cache.clear()
cache.set('key', settledStamp('stamp'), diffOfSize(1), readGeneration)
expect(cache.stats().entries).toBe(0)
expect(cache.stats().invalidatedDuringRead).toBe(1)
})
})
@@ -1,108 +0,0 @@
import { mkdtemp, readFile, rm, writeFile } from 'node:fs/promises'
import { tmpdir } from 'node:os'
import { join } from 'node:path'
import { afterEach, expect, it } from 'vitest'
import { gitExecFileAsync } from '../../command-runner/git-exec-file'
import { addWorktree } from './worktree-add'
import { listWorktrees } from '../catalog/worktree-scan-cache'
import { finalizePreparedWorktree } from './worktree-create-preparation'
import { worktreeCreateGit } from './worktree-create-git-executor'
import {
_resetPreparationPoolForTests,
listPreparations,
startPreparation,
takePreparation
} from '../../../workspaces/creation/preparation/worktree-create-preparation-pool'
import {
acquireGitAdmission,
_resetGitAdmissionForTests
} from '../../command-runner/git-subprocess-admission'
import { GitAdmissionScheduler } from '../../../../shared/git/admission/git-admission-scheduler'
import type { GitAdmissionEvent } from '../../../../shared/git/admission/git-admission-state'
const roots: string[] = []
afterEach(async () => {
_resetGitAdmissionForTests()
await _resetPreparationPoolForTests()
await Promise.all(roots.splice(0).map((root) => rm(root, { recursive: true, force: true })))
})
it('creates cold and prepared worktrees with real Git while status capacity is occupied', async () => {
const root = await mkdtemp(join(tmpdir(), 'orca-create-policy-'))
roots.push(root)
const repo = join(root, 'repo')
await gitExecFileAsync(['init', '--quiet', repo], { cwd: root })
await gitExecFileAsync(['symbolic-ref', 'HEAD', 'refs/heads/main'], { cwd: repo })
await writeFile(join(repo, 'file.txt'), 'workspace content\n')
await gitExecFileAsync(['add', '.'], { cwd: repo })
await gitExecFileAsync(
['-c', 'user.name=Test', '-c', 'user.email=test@example.com', 'commit', '-qm', 'fixture'],
{ cwd: repo }
)
const events: GitAdmissionEvent[] = []
_resetGitAdmissionForTests(
new GitAdmissionScheduler({
generalCap: 1,
generalHeadroom: 1,
onAdmissionEvent: (event) => {
if (event.phase === 'grant') {
events.push(event)
}
}
})
)
await worktreeCreateGit.run(() =>
startPreparation({
repoPath: repo,
workspaceRoot: root,
baseBranch: 'main',
canonicalBase: 'refs/heads/main',
options: {}
})
)
expect(events.length).toBeGreaterThan(0)
expect(events.every((event) => event.tier === 'status')).toBe(true)
const [prepared] = listPreparations()
expect(prepared).toBeDefined()
takePreparation(prepared)
const blocker = await acquireGitAdmission({ args: ['status'], cwd: repo })
events.length = 0
try {
await worktreeCreateGit.run(async () => {
await gitExecFileAsync(['fetch', repo, 'main'], {
cwd: repo,
useConfiguredSshCommandForNetwork: true,
env: { ...process.env, GIT_SSH_COMMAND: '' }
})
await addWorktree(repo, join(root, 'cold'), 'cold', 'main')
await finalizePreparedWorktree(
repo,
prepared.preparedPath,
join(root, 'warm'),
'warm',
'main',
false,
{},
prepared.lockReason
)
expect(await listWorktrees(repo)).toHaveLength(3)
})
expect(
events.filter((event) => event.args[0] === 'config').map((event) => event.args)
).toContainEqual(['config', '--null', '--get-regexp', '^(core\\.sshcommand|ssh\\.variant)$'])
expect(events.some((event) => event.args.includes('fetch'))).toBe(true)
expect(events.every((event) => event.tier === 'interactive')).toBe(true)
expect(
events
.filter((event) => event.admissionClass === 'general')
.every((event) => event.slotKind === 'headroom')
).toBe(true)
for (const name of ['cold', 'warm']) {
expect(await readFile(join(root, name, 'file.txt'), 'utf8')).toBe('workspace content\n')
}
} finally {
blocker.release()
}
})
@@ -1,107 +0,0 @@
import { mkdir, mkdtemp, realpath, rm, writeFile } from 'node:fs/promises'
import { tmpdir } from 'node:os'
import { join } from 'node:path'
import { afterAll, afterEach, beforeAll, beforeEach, describe, expect, it, vi } from 'vitest'
const { runner } = vi.hoisted(() => ({ runner: vi.fn() }))
vi.mock('../../command-runner/git-exec-file', async (importOriginal) => ({
...(await importOriginal<Record<string, unknown>>()),
gitExecFileAsync: runner
}))
vi.mock('../../command-runner/git-exec-file-sync', async (importOriginal) => ({
...(await importOriginal<Record<string, unknown>>()),
gitExecFileSync: vi.fn()
}))
vi.mock('../../command-runner/wsl-path-translation', async (importOriginal) => ({
...(await importOriginal<Record<string, unknown>>()),
translateWslOutputPaths: (value: string) => value
}))
vi.mock('../../source-control/git-read-cache-invalidation', () => ({
runWithGitReadCacheInvalidation: (run: () => unknown) => run()
}))
import { getBranchConflictKind, getBranchConflictKindViaExec } from '../../repo-branch-conflict'
import { clearGitCapabilityStateForTests } from '../../command-runner/git-capability-state'
import { GitBinaryTestFixture } from '../../../../shared/git/compatibility/git-binary.test-fixture'
const expectedVersion = process.env.ORCA_GIT_COMPAT_VERSION
let fixture: GitBinaryTestFixture
let root = ''
let repo = ''
async function git(args: string[], cwd = repo): Promise<{ stdout: string; stderr: string }> {
return fixture.exec(args, cwd)
}
async function initializeRepo(cwd: string): Promise<void> {
await mkdir(cwd, { recursive: true })
await git(['init', '-q', '-b', 'main'], cwd).catch(() => git(['init', '-q'], cwd))
await git(['config', 'user.name', 'Worktree Safety'], cwd)
await git(['config', 'user.email', 'safety@example.invalid'], cwd)
await git(['config', 'commit.gpgSign', 'false'], cwd)
await git(['config', 'core.hooksPath', '.git/no-hooks'], cwd)
await git(['config', 'gc.auto', '0'], cwd)
await writeFile(join(cwd, 'seed'), 'seed\n')
await git(['add', 'seed'], cwd)
await git(['commit', '-qm', 'seed'], cwd)
}
beforeAll(async () => {
root = await realpath(await mkdtemp(join(tmpdir(), 'orca-worktree-safety-')))
fixture = new GitBinaryTestFixture(root)
await fixture.start()
const { stdout } = await fixture.exec(['--version'])
expect(stdout).toContain(expectedVersion ? `git version ${expectedVersion}` : 'git version ')
})
afterAll(async () => {
try {
await fixture.stop()
} finally {
await rm(root, { recursive: true, force: true })
}
})
beforeEach(async () => {
repo = await mkdtemp(join(root, 'repo-'))
await initializeRepo(repo)
clearGitCapabilityStateForTests()
runner.mockImplementation((args: string[], options: { cwd?: string }) => git(args, options.cwd))
})
afterEach(async () => {
await rm(repo, { recursive: true, force: true })
})
describe('worktree safety with the real Git binary', () => {
it.each([
['feature', 'Feature'],
['Ä', 'ä'],
['K', 'K'],
['ß', 'ẞ']
])(
'protects packed %s from the alias %s on native and SSH hosts',
async (existing, candidate) => {
await git(['branch', existing])
await git(['pack-refs', '--all'])
const before = (await git(['rev-parse', `refs/heads/${existing}`])).stdout
await writeFile(join(repo, 'seed'), 'advanced base\n')
await git(['commit', '-qam', 'advance'])
for (const setting of ['true', 'false', 'unset']) {
await git(
setting === 'unset'
? ['config', '--unset', 'core.ignoreCase']
: ['config', 'core.ignoreCase', setting]
)
await expect(getBranchConflictKind(repo, candidate)).resolves.toBe('local')
await expect(getBranchConflictKindViaExec((args) => git(args), candidate)).resolves.toBe(
'local'
)
expect((await git(['rev-parse', `refs/heads/${existing}`])).stdout).toBe(before)
}
},
90_000
)
})
@@ -1,109 +0,0 @@
import { mkdir, mkdtemp, readFile, realpath, rm, writeFile } from 'node:fs/promises'
import { tmpdir } from 'node:os'
import { join } from 'node:path'
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
import { GitBinaryTestFixture } from '../../../../shared/git/compatibility/git-binary.test-fixture'
vi.mock('../../command-runner/git-exec-file', async (importOriginal) => ({
...(await importOriginal<Record<string, unknown>>()),
gitExecFileAsync: vi.fn()
}))
vi.mock('../../command-runner/git-exec-file-sync', async (importOriginal) => ({
...(await importOriginal<Record<string, unknown>>()),
gitExecFileSync: vi.fn()
}))
vi.mock('../../refs/maintenance/local-repo-ref-maintenance', () => ({
withRepoRefMaintenancePaused: (_reason: string, run: () => unknown) => run()
}))
import { forceDeleteLocalBranch } from './worktree-branch-removal'
let fixture: GitBinaryTestFixture
let root = ''
let repo = ''
let checkout = ''
async function git(args: string[], cwd = repo): Promise<{ stdout: string; stderr: string }> {
return fixture.exec(args, cwd, { GIT_CONFIG_GLOBAL: join(root, 'empty-config') })
}
beforeEach(async () => {
root = await realpath(await mkdtemp(join(tmpdir(), 'orca-rebase-reservation-')))
fixture = new GitBinaryTestFixture(root)
await fixture.start()
repo = join(root, 'repo')
checkout = join(root, 'checkout')
await mkdir(repo)
await writeFile(join(root, 'empty-config'), '')
await git(['init', '-q'])
await git(['symbolic-ref', 'HEAD', 'refs/heads/main'])
for (const [key, value] of [
['user.name', 'Reservation Safety'],
['user.email', 'safety@example.invalid'],
['commit.gpgSign', 'false'],
['core.hooksPath', '.git/no-hooks'],
['gc.auto', '0']
]) {
await git(['config', key, value])
}
await writeFile(join(repo, 'seed'), 'seed\n')
await git(['add', 'seed'])
await git(['commit', '-qm', 'seed'])
})
afterEach(async () => {
try {
await fixture.stop()
} finally {
await rm(root, { recursive: true, force: true })
}
})
describe('rebase branch reservations with real Git', () => {
it.for(['native'] as const)(
'%s retains auxiliary branches reserved by a paused real rebase --update-refs',
{ timeout: 120_000 },
async (_host, context) => {
await git(['worktree', 'add', '-q', '-b', 'feature', checkout])
await writeFile(join(checkout, 'first'), 'first\n')
await git(['add', 'first'], checkout)
await git(['commit', '-qm', 'first'], checkout)
await git(['branch', 'auxiliary'], checkout)
const expected = (await git(['rev-parse', 'refs/heads/auxiliary'])).stdout.trim()
await writeFile(join(checkout, 'second'), 'second\n')
await git(['add', 'second'], checkout)
await git(['commit', '-qm', 'second'], checkout)
await writeFile(join(repo, 'base'), 'advanced main\n')
await git(['add', 'base'])
await git(['commit', '-qm', 'advance main'])
try {
await git(['rebase', '--update-refs', '--exec', 'false', 'main'], checkout)
throw new Error('Rebase did not pause')
} catch (error) {
if (error instanceof Error && /unknown option[^\n]*update-refs/.test(error.message)) {
context.skip()
}
if (!(error instanceof Error) || !/execution failed/.test(error.message)) {
throw error
}
}
const gitDir = (await git(['rev-parse', '--absolute-git-dir'], checkout)).stdout.trim()
expect(await readFile(join(gitDir, 'rebase-merge', 'update-refs'), 'utf8')).toContain(
'refs/heads/auxiliary\n'
)
await expect(git(['branch', '-D', 'auxiliary'])).rejects.toThrow(
/checked out|used by worktree/
)
const cleanupGit = vi.fn((args: string[], cwd: string) => git(args, cwd))
const remove = () => forceDeleteLocalBranch(repo, 'auxiliary', expected, cleanupGit)
await expect(remove()).rejects.toThrow('checked out in another worktree')
expect(cleanupGit.mock.calls.map(([args]) => args)).toEqual([
['worktree', 'list', '--porcelain']
])
expect((await git(['rev-parse', 'refs/heads/auxiliary'])).stdout.trim()).toBe(expected)
await git(['rebase', '--abort'], checkout)
await expect(remove()).resolves.toBeUndefined()
}
)
})
@@ -1,96 +1,8 @@
import { describe, expect, it } from 'vitest'
import { isMap, parseDocument } from 'yaml'
import { enablePlugin, updateConfigContent } from './hermes-config-yaml'
function expectInsertion(source: string, expected: unknown, eol: string): string {
const original = parseDocument(source)
expect(original.errors).toEqual([])
expect(original.warnings).toEqual([])
const plugins = original.get('plugins', true)
const parent = isMap(plugins) ? plugins : original.contents
if (!isMap(parent) || !parent.range) {
throw new Error('Fixture must contain a ranged mapping')
}
const position = parent.range[1] - 1
const result = updateConfigContent(source, enablePlugin)
expect(result.detail).toBeUndefined()
const output = result.content ?? ''
const document = parseDocument(output)
expect(document.errors).toEqual([])
expect(document.warnings).toEqual([])
expect(document.toJS()).toEqual(expected)
expect(output.startsWith(source.slice(0, position))).toBe(true)
expect(output.endsWith(source.slice(position))).toBe(true)
expect(updateConfigContent(output, enablePlugin)).toEqual({ content: output })
if (eol === '\r\n') {
expect(output.replaceAll(eol, '')).not.toContain('\n')
}
return output
}
describe.each(['\n', '\r\n'])('Hermes flow insertion with tab separation (%j)', (eol) => {
it.each([
{
name: 'minimal tab',
source: 'plugins: {custom: keep # note\n\t}',
padding: '\n '
},
{
name: 'deeper escaped quoted key',
source: ' "plu\\u0067ins": {custom: keep # note\n \t}',
padding: '\n '
},
{
name: 'mixed spaces and tabs below required indentation',
source: ' plugins: {custom: keep # note\n \t \t }',
padding: '\n '
},
{
name: 'whitespace-only closing line',
source: 'plugins: {custom: keep # note\n\t \t }',
padding: '\n '
},
{
name: 'sufficient spaces before a tab',
source: 'plugins: {custom: keep # note\n \t}',
padding: ''
},
{
name: 'deeper sufficient spaces before mixed separation',
source: ' plugins: {custom: keep # note\n \t \t}',
padding: ''
},
{
name: 'root flow with zero required indentation',
source: ' {plugins: {custom: keep # note\n\t}}',
padding: ''
},
{
name: 'tab after content on the same line',
source: 'plugins: {custom: keep\t}',
padding: ''
},
{
name: 'explicit plugins key',
source: '? "plugins"\n: {custom: keep # note\n\t}',
padding: '\n '
},
{
name: 'deeper split explicit key',
source: ' ?\n "plugins"\n : {custom: keep # note\n \t}',
padding: '\n '
}
])('preserves source around $name', ({ source: input, padding }) => {
const source = input.replaceAll('\n', eol)
const output = expectInsertion(
source,
{ plugins: { custom: 'keep', enabled: ['orca-status'] } },
eol
)
const insertion = `${padding}, enabled: [ orca-status ]`.replaceAll('\n', eol)
expect(output).toBe(source.replace('}', `${insertion}}`))
})
it('rejects an unrelated value change after repairing tab-separated insertion', () => {
const source = 'model: "001"\nplugins: {custom: keep # note\n\t}'.replaceAll('\n', eol)
expect(
@@ -1,106 +0,0 @@
import type * as RuntimeEnvironmentConnectivityHandlersModule from '../runtime-environments/runtime-environment-connectivity-handlers'
import type * as RuntimeEnvironmentManualDisconnectModule from '../runtime-environments/runtime-environment-manual-disconnect'
import { beforeEach, describe, expect, it, vi } from 'vitest'
import type { GlobalSettings } from '../../../shared/global-settings-types'
import type { SshConnectionState, SshTarget } from '../../../shared/ssh-types'
const { targets, settings } = vi.hoisted(() => {
const targets: SshTarget[] = []
const settings: { current: Partial<GlobalSettings> } = { current: {} }
return { targets, settings }
})
vi.mock('electron', () => ({ webContents: { fromId: () => null } }))
vi.mock('../../browser/browser-manager', () => ({ browserManager: {} }))
vi.mock('../../runtime/rpc/methods/index', () => ({ ALL_RPC_METHODS: [] }))
vi.mock('../ssh/ssh-ipc-context', () => ({
persistedStore: { getSettings: () => settings.current }
}))
vi.mock('../../managed-server/lifecycle/orcad-managed-tunnel', () => ({
ensureOrcadManagedTunnel: vi.fn()
}))
vi.mock('../../ssh/targets/ssh-target-registry', async (importOriginal) => ({
...(await importOriginal<Record<string, unknown>>()),
getSshTargetRegistryStore: () => ({ listTargets: () => targets })
}))
vi.mock('../../../shared/managed-orcad-ssh-owner', async (importOriginal) => ({
...(await importOriginal<Record<string, unknown>>()),
getManagedOrcadFenceEnvironmentId: (target: SshTarget) => `env-${target.id}`
}))
vi.mock('../runtime-environments/runtime-environment-connectivity-handlers', () => ({
isRuntimeEnvironmentManuallyDisconnected: () => false
}))
vi.mock('../runtime-environments/runtime-environment-manual-disconnect', async (importOriginal) => {
const facadeMocks = await vi.importMock<typeof RuntimeEnvironmentConnectivityHandlersModule>(
'../runtime-environments/runtime-environment-connectivity-handlers'
)
return {
...(await importOriginal<typeof RuntimeEnvironmentManualDisconnectModule>()),
isRuntimeEnvironmentManuallyDisconnected: facadeMocks.isRuntimeEnvironmentManuallyDisconnected
}
})
vi.mock(
'../../../shared/remote-runtime/host-store/runtime-environment-store',
async (importOriginal) => ({
...(await importOriginal<Record<string, unknown>>()),
resolveEnvironment: (_userDataPath: string, environmentId: string) => ({ environmentId })
})
)
vi.mock(
'../../../shared/remote-runtime/host-catalog/runtime-environments',
async (importOriginal) => ({
...(await importOriginal<Record<string, unknown>>()),
getOrcadSshDeployment: ({ environmentId }: { environmentId: string }) => ({
sshTargetId: environmentId.slice('env-'.length)
})
})
)
import { listDesktopBrowserBridgeCandidates } from './desktop-browser-bridge-wiring'
const host = (id: string, extra: Partial<SshTarget> = {}): SshTarget => ({
id,
label: id,
host: `${id}.example.com`,
port: 22,
username: 'me',
...extra
})
const connected = {
getState: (targetId: string): SshConnectionState => ({
targetId,
status: 'connected',
error: null,
reconnectAttempt: 0
})
}
beforeEach(() => {
targets.length = 0
settings.current = {}
})
describe('which managed hosts may attach the desktop browser bridge', () => {
it('attaches a host with no saved switch or an explicit true, never an explicit false', () => {
targets.push(
host('absent'),
host('on', { allowRemoteCliControl: true }),
host('off', { allowRemoteCliControl: false })
)
const candidates = listDesktopBrowserBridgeCandidates('/user-data', connected)
expect(candidates.map((candidate) => candidate.targetId)).toEqual(['absent', 'on'])
})
it('holds back a host whose pages browse from this device', () => {
targets.push(host('routed'), host('opted-out'))
settings.current = { browserSshWorkspaceRoutingDisabledTargetIds: ['opted-out'] }
expect(
listDesktopBrowserBridgeCandidates('/user-data', connected).map((c) => c.targetId)
).toEqual(['routed'])
settings.current = { browserSshWorkspaceRoutingEnabled: false }
expect(listDesktopBrowserBridgeCandidates('/user-data', connected)).toEqual([])
})
})
@@ -1,69 +0,0 @@
import { afterEach, describe, expect, it, vi } from 'vitest'
import { getDefaultWorkspaceSession } from '../../../../shared/constants'
import type { WorkspaceSessionState } from '../../../../shared/workspace-session/workspace-session-state-types'
import type { Store } from '../../../persistence/loading-store/store'
import {
_resetClosedPaneFencesForTests,
fencedIfLayoutOwner,
partitionsFence
} from '../../../persistence/terminal-topology/closed-pane-binding-fence'
import { refuseClosedPaneSpawn } from './closed-pane-spawn-refusal'
vi.mock('../../../../shared/layout-owner-switch', () => ({ ORCA_LAYOUT_OWNER_IS_MAIN: true }))
function session(leafIds: string[]): WorkspaceSessionState {
const [first, second] = leafIds
return {
...getDefaultWorkspaceSession(),
terminalLayoutsByTabId: {
t: {
root: second
? {
type: 'split',
direction: 'vertical',
first: { type: 'leaf', leafId: first! },
second: { type: 'leaf', leafId: second }
}
: { type: 'leaf', leafId: first! },
activeLeafId: first!,
expandedLeafId: null
}
}
}
}
function storeHolding(current: { value: WorkspaceSessionState }): Store {
// oxlint-disable-next-line typescript/consistent-type-assertions -- SAFETY: refuseClosedPaneSpawn reads only these two Store members.
return {
getWorkspaceSessionHostIds: () => ['local'],
getWorkspaceSession: () => current.value
} as unknown as Store
}
describe('a spawn for a pane main closed while it waited', () => {
afterEach(() => _resetClosedPaneFencesForTests())
it('starts nothing once main took the pane back', () => {
const current = { value: session(['a', 'b']) }
fencedIfLayoutOwner(
() => partitionsFence(['local'], () => current.value, true),
() => ((current.value = session(['a'])), { value: null })
)()
expect(() => refuseClosedPaneSpawn(storeHolding(current), 't', 'b')).toThrow(
'terminal_pane_closed'
)
})
it('starts a pane main never closed, or one it holds again', () => {
const current = { value: session(['a', 'b']) }
expect(() => refuseClosedPaneSpawn(storeHolding(current), 't', 'b')).not.toThrow()
fencedIfLayoutOwner(
() => partitionsFence(['local'], () => current.value, true),
() => ((current.value = session(['a'])), { value: null })
)()
current.value = session(['a', 'b'])
expect(() => refuseClosedPaneSpawn(storeHolding(current), 't', 'b')).not.toThrow()
})
})
@@ -1,113 +0,0 @@
import { afterEach, describe, expect, it } from 'vitest'
import { getDefaultSettings } from '../../../../shared/constants'
import type { TerminalOscColorQueryReplyColors } from '../../../../shared/terminal/protocol/queries/terminal-osc-color-reply'
import { LocalPtyProvider } from '../../../providers/local-pty/local-pty-provider'
import {
_resetTerminalViewAttributesForTest,
getTerminalViewerColors,
setPairedViewerColors,
setTerminalViewAttributes,
setTerminalViewerColorsListener
} from '../../../runtime/terminal-view-attribute-store'
import type { TerminalViewAttributes } from '../../../../shared/terminal/protocol/queries/terminal-view-attributes'
import {
_resetColorQueryReplyColorsForTest,
getLocalPtyProvider,
setLocalPtyProvider
} from '../provider/registry'
import { installTerminalViewAttributesIpc } from './terminal-view-attributes-ipc'
class RecordingProvider extends LocalPtyProvider {
readonly pushes: TerminalOscColorQueryReplyColors[] = []
override setColorQueryReplyColors(colors: TerminalOscColorQueryReplyColors): void {
this.pushes.push(colors)
}
}
const DESKTOP_ATTRIBUTES: TerminalViewAttributes = {
foreground: [0, 0, 0],
background: [0x12, 0x34, 0x56],
cursor: [0, 0, 0],
ansi: Array.from({ length: 256 }, (): [number, number, number] => [0, 0, 0]),
colorSchemeMode: 'light',
cursorStyle: 'block',
cursorBlink: false
}
const DESKTOP = { foreground: '#000000', background: '#123456' }
const CLIENT = { foreground: '#2e3434', background: '#ffffff' }
describe('seeding PTY owner colours from saved settings', () => {
const originalLocal = getLocalPtyProvider()
const settings = getDefaultSettings('/tmp')
afterEach(() => {
_resetTerminalViewAttributesForTest()
_resetColorQueryReplyColorsForTest()
setLocalPtyProvider(originalLocal)
})
it('seeds a host with no display from its saved dark theme', () => {
const owner = new RecordingProvider()
setLocalPtyProvider(owner)
installTerminalViewAttributesIpc({ getSettings: () => ({ ...settings, theme: 'dark' }) })
expect(owner.pushes).toEqual([{ foreground: '#ffffff', background: '#282c34' }])
})
it('publishes colours a renderer already pushed instead of the saved theme', () => {
setTerminalViewAttributes(DESKTOP_ATTRIBUTES)
const owner = new RecordingProvider()
setLocalPtyProvider(owner)
installTerminalViewAttributesIpc({ getSettings: () => settings })
expect(owner.pushes).toEqual([DESKTOP])
})
})
describe('one viewer colour value for every PTY owner', () => {
const originalLocal = getLocalPtyProvider()
const settings = getDefaultSettings('/tmp')
const SEED = { foreground: '#ffffff', background: '#282c34' }
afterEach(() => {
_resetTerminalViewAttributesForTest()
_resetColorQueryReplyColorsForTest()
setLocalPtyProvider(originalLocal)
})
function install(): RecordingProvider {
const owner = new RecordingProvider()
setLocalPtyProvider(owner)
installTerminalViewAttributesIpc({ getSettings: () => ({ ...settings, theme: 'dark' }) })
return owner
}
it("switches to this host's window theme once its renderer pushes", () => {
const owner = install()
setPairedViewerColors(CLIENT)
setTerminalViewAttributes(DESKTOP_ATTRIBUTES)
setPairedViewerColors({ foreground: '#111111', background: '#eeeeee' })
expect(getTerminalViewerColors()).toEqual(DESKTOP)
expect(owner.pushes).toEqual([SEED, CLIENT, DESKTOP])
})
it('notifies once per change of the answered colours', () => {
install()
const published: unknown[] = []
setTerminalViewerColorsListener((colors) => published.push(colors))
setPairedViewerColors(CLIENT)
setPairedViewerColors({ ...CLIENT })
setTerminalViewAttributes(DESKTOP_ATTRIBUTES)
setTerminalViewAttributes({ ...DESKTOP_ATTRIBUTES, cursorBlink: true })
setPairedViewerColors({ foreground: '#111111', background: '#eeeeee' })
expect(published).toEqual([CLIENT, DESKTOP])
})
})
@@ -3,12 +3,7 @@ import { TerminalRunFactsRegister } from '../../../runtime/terminal-run-facts'
import { ptyOwnership } from '../provider/ownership-state'
import { createPtyWriteInput } from './write-input'
import {
WRITE_ACCEPTED,
writeRefused,
writeUnverifiable,
type WriteSettlement
} from '../../../../shared/terminal/pty/lifecycle/pty-write-settlement'
import type { WriteSettlement } from '../../../../shared/terminal/pty/lifecycle/pty-write-settlement'
const PTY_ID = 'pty-user-input'
@@ -52,41 +47,6 @@ afterEach(() => {
})
describe('renderer PTY writes: input kind', () => {
it.each(['local', 'ssh:ssh-connection'] as const)(
'observes a settled write only after acceptance on the execution host (%s)',
async (owner) => {
ptyOwnership.set(PTY_ID, owner)
const observe = vi.fn()
const input = createWriteInput(new TerminalRunFactsRegister(), observe)
let finish: (settlement: WriteSettlement) => void = () => {}
provider.writeWithSettlement.mockReturnValueOnce(
new Promise((resolve) => {
finish = resolve
})
)
const args = {
id: PTY_ID,
data: '\x1b[27u',
inputKind: 'driving' as const,
requireWriteSettlement: true as const
}
const pending = input.writePtyInputAccepted(args)
expect(observe).not.toHaveBeenCalled()
finish(WRITE_ACCEPTED)
expect(await pending).toBe(true)
expect(observe).toHaveBeenCalledTimes(owner === 'local' ? 1 : 0)
observe.mockClear()
provider.writeWithSettlement.mockReturnValueOnce(writeRefused('endpoint_disconnected'))
expect(await input.writePtyInputAccepted(args)).toBe(false)
provider.writeWithSettlement.mockResolvedValueOnce(
writeUnverifiable('transport_settlement_lost', true)
)
await expect(input.writePtyInputAccepted(args)).rejects.toThrow('acknowledgment unavailable')
expect(observe).not.toHaveBeenCalled()
expect(provider.write).not.toHaveBeenCalled()
}
)
it.each(['writePtyInput', 'writePtyInputAccepted'] as const)(
'%s records driving input before the provider write',
async (writer) => {
@@ -1,99 +0,0 @@
import type * as FsPromises from 'node:fs/promises'
import { beforeEach, describe, expect, it, vi } from 'vitest'
import { spawnMock } from './pty-ipc-mock-registry'
import { setupPtyIpcSuite } from './pty-ipc-test-harness'
import { registerPtyHandlers } from './register-handlers'
type DirectoryStats = { isDirectory(): boolean }
const statMock = vi.hoisted(() => vi.fn<(path: string) => Promise<DirectoryStats>>())
vi.mock('node:fs/promises', async (importOriginal) => ({
...(await importOriginal<typeof FsPromises>()),
stat: statMock
}))
vi.mock('electron', () => import('./pty-ipc-mock-registry').then((m) => m.electronModuleMock()))
vi.mock('fs', () => import('./pty-ipc-mock-registry').then((m) => m.fsModuleMock()))
vi.mock('node-pty', () => import('./pty-ipc-mock-registry').then((m) => m.nodePtyModuleMock()))
vi.mock('node:child_process', async (importOriginal) =>
(await import('./pty-ipc-mock-registry')).childProcessModuleMock(await importOriginal())
)
vi.mock('../../opencode/hook-service', () =>
import('./pty-ipc-mock-registry').then((m) => m.openCodeHookServiceModuleMock())
)
vi.mock('../../mimo/hook-service', () =>
import('./pty-ipc-mock-registry').then((m) => m.mimoHookServiceModuleMock())
)
vi.mock('../../agent-hooks/server', () =>
import('./pty-ipc-mock-registry').then((m) => m.agentHookServerModuleMock())
)
vi.mock('../../pi/titlebar-extension-service', () =>
import('./pty-ipc-mock-registry').then((m) => m.piTitlebarExtensionModuleMock())
)
vi.mock('../../pty/shell-startup/pwsh', () =>
import('./pty-ipc-mock-registry').then((m) => m.pwshModuleMock())
)
vi.mock('../../wsl/filesystem/unc-path-access', async (importOriginal) =>
(await import('./pty-ipc-mock-registry')).wslModuleMock(await importOriginal())
)
vi.mock('../../telemetry/client', () =>
import('./pty-ipc-mock-registry').then((m) => m.telemetryClientModuleMock())
)
vi.mock('../../telemetry/classify-error', () =>
import('./pty-ipc-mock-registry').then((m) => m.classifyErrorModuleMock())
)
vi.mock('../../cli/linux-terminal-orca-cli-shim', () =>
import('./pty-ipc-mock-registry').then((m) => m.linuxCliShimModuleMock())
)
vi.mock('../../memory/pty-registry', () =>
import('./pty-ipc-mock-registry').then((m) => m.ptyRegistryModuleMock())
)
vi.mock('../../agent-hooks/migration-unsupported-pty-state', () =>
import('./pty-ipc-mock-registry').then((m) => m.migrationUnsupportedPtyModuleMock())
)
vi.mock('../../codex/configuration/homes/codex-pane-account-registry', () =>
import('./pty-ipc-mock-registry').then((m) => m.codexPaneAccountRegistryModuleMock())
)
vi.mock('../../codex/history/index/codex-state-db-backfill-recovery', () =>
import('./pty-ipc-mock-registry').then((m) => m.codexBackfillRecoveryModuleMock())
)
const WORKTREE = '/Volumes/nas/repo'
const directoryStats = { isDirectory: () => true }
function spawnWithWorktreeFallback(cwd: string): Promise<unknown> {
return Promise.resolve(
handlers.get('pty:spawn')!(null, {
cols: 80,
rows: 24,
cwd,
cwdFallback: 'worktree',
worktreeId: `repo-1::${WORKTREE}`
})
)
}
const { handlers, mainWindow } = setupPtyIpcSuite()
beforeEach(() => {
statMock.mockReset()
// Provider validation uses sync stat; folder-workspace validation uses async stat.
statMock.mockImplementation(async () => directoryStats)
})
describe('pty:spawn with the worktree-folder fallback on a mount that stopped answering', () => {
it('never asks the worktree folder once the saved folder answers', async () => {
const outsideCwd = '/Users/alice/scratch'
statMock.mockImplementation(async (path) =>
path === outsideCwd ? directoryStats : new Promise<DirectoryStats>(() => {})
)
registerPtyHandlers(mainWindow as never)
await spawnWithWorktreeFallback(outsideCwd)
expect(statMock.mock.calls.map(([path]) => path)).toEqual([outsideCwd])
const [, , options] = spawnMock.mock.calls.at(-1) as [string, string[], { cwd: string }]
expect(options.cwd).toBe(outsideCwd)
})
})
@@ -67,25 +67,4 @@ describe('runtime environment removal storage clearing', () => {
finishTeardown()
await vi.waitFor(() => expect(clearStorageMock).toHaveBeenCalledWith('environment-a'))
})
it('retries a partition the first pass refused as live', async () => {
clearStorageMock
.mockResolvedValueOnce({ clearedPartitions: [], livePartitions: ['persist:one'] })
.mockResolvedValueOnce({ clearedPartitions: ['persist:one'], livePartitions: [] })
registerRuntimeEnvironmentConnectivityHandlers({
// oxlint-disable-next-line typescript/consistent-type-assertions -- SAFETY: remove reads only settings and the session-host prune.
store: {
getSettings: () => ({}),
removeWorkspaceSessionHost: vi.fn(),
setRemoteLayoutSnapshot: vi.fn(),
setHostRepoCatalogSnapshot: vi.fn()
} as never,
getUserDataPath: () => '/tmp/orca-user-data',
invalidateTransport: () => Promise.resolve()
})
removeHandler()(null, { selector: 'environment-a' })
await vi.waitFor(() => expect(clearStorageMock).toHaveBeenCalledTimes(2), { timeout: 2_000 })
})
})
@@ -1,99 +0,0 @@
import type * as ClientModule from '../../github/client'
import type * as GetPrForBranchModule from '../../github/client/lookup/get-pr-for-branch'
import { EventEmitter } from 'node:events'
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
import type { PRRefreshOutcome } from '../../../shared/github/pull-request-refresh-types'
const { handlers, lookup, contents } = vi.hoisted(() => ({
handlers: new Map<string, (...args: unknown[]) => unknown>(),
lookup: vi.fn(),
contents: new Map<number, EventEmitter & { id: number; isDestroyed: () => boolean }>()
}))
vi.mock('electron', () => ({
ipcMain: {
handle: (channel: string, handler: (...args: unknown[]) => unknown) =>
handlers.set(channel, handler)
},
webContents: { fromId: (id: number) => contents.get(id) }
}))
vi.mock('../../github/client', () => ({ getPRForBranch: vi.fn(), getPRForBranchOutcome: lookup }))
vi.mock('../../github/client/lookup/get-pr-for-branch', async (importOriginal) => {
const facadeMocks = await vi.importMock<typeof ClientModule>('../../github/client')
return {
...(await importOriginal<typeof GetPrForBranchModule>()),
getPRForBranch: facadeMocks.getPRForBranch
}
})
vi.mock('../../github/pr-refresh-rate-limit-gate', () => ({
prRefreshRateLimitPausedUntil: async () => null
}))
vi.mock('../../wsl/filesystem/wsl-unc-access-gate', () => ({
wslDistroForPath: () => null,
blockedWslDistroForPath: async () => null
}))
vi.mock('../../crash-reporting/crash-breadcrumb-store', () => ({
recordCoalescedCrashBreadcrumb: () => {}
}))
vi.mock('../ui', () => ({ sendToTrustedUIRenderer: () => {} }))
vi.mock('./github-repo-routing', () => ({
validateAutomaticPRRefreshCandidate: (candidate: unknown) => ({ kind: 'ok', candidate }),
isPRRefreshCandidateRegistered: () => true
}))
import { makeCandidate, makePR } from '../../github/pr-refresh-coordinator-test-harness'
function sender(id: number) {
return Object.assign(new EventEmitter(), { id, isDestroyed: () => false })
}
function found(): PRRefreshOutcome {
return { kind: 'found', pr: makePR({ checksStatus: 'pending' }), fetchedAt: Date.now() }
}
async function setup() {
const { registerGitHubPRRefreshHandlers } = await import('./github-pr-refresh-handlers')
const coordinator = await import('../../github/pr-refresh-coordinator')
const owner = sender(17)
const other = sender(18)
contents.set(owner.id, owner)
contents.set(other.id, other)
// oxlint-disable-next-line typescript/consistent-type-assertions -- SAFETY: Visible refresh registration reads only this store fixture's repo list.
const store = { getRepos: () => [] } as never
// oxlint-disable-next-line typescript/consistent-type-assertions -- SAFETY: Every PR is already counted, so the observer reads only this stats method.
const stats = { hasCountedPR: () => true } as never
registerGitHubPRRefreshHandlers(store, stats)
const report = (target = owner) =>
handlers.get('gh:reportVisiblePRRefreshCandidates')!(
{ sender: target },
{ candidates: [makeCandidate()], generation: Date.now() }
)
return { owner, other, report, coordinator }
}
describe('automatic PR refresh document ownership', () => {
beforeEach(() => {
vi.resetModules()
vi.useFakeTimers()
vi.setSystemTime(1_700_000_000_000)
handlers.clear()
contents.clear()
lookup.mockReset().mockImplementation(async () => found())
})
afterEach(() => {
vi.clearAllTimers()
vi.useRealTimers()
})
it('does not requeue a late lookup after document retirement', async () => {
const pending = Promise.withResolvers<PRRefreshOutcome>()
lookup.mockImplementationOnce(() => pending.promise)
const { owner, report, coordinator } = await setup()
report()
await vi.advanceTimersByTimeAsync(0)
owner.emit('render-process-gone')
pending.resolve(found())
await vi.advanceTimersByTimeAsync(360_000)
expect(lookup).toHaveBeenCalledTimes(1)
expect(coordinator._getVisiblePRRefreshWindowCountForTests()).toBe(0)
expect(coordinator._getPRRefreshQueueSizeForTests()).toBe(0)
})
})
@@ -1,68 +0,0 @@
import { beforeEach, expect, it, vi } from 'vitest'
import type { FolderWorkspace } from '../../shared/folder-workspaces/folder-workspace-types'
import type { SessionInfo } from '../daemon/types'
import type { Store } from '../persistence/loading-store/store'
const getDaemonProviderMock = vi.fn()
vi.mock('../daemon/daemon-provider-state', () => ({
getDaemonProvider: () => getDaemonProviderMock()
}))
vi.mock('../workspaces/execution-host/project-runtime-git-options', () => ({
getLocalProjectWorktreeGitOptions: () => ({})
}))
const listLocalRepoWorktreesStrictMock = vi.fn()
vi.mock('../workspaces/catalog/repo-worktrees', () => ({
listLocalRepoWorktreesStrict: (...args: unknown[]) => listLocalRepoWorktreesStrictMock(...args)
}))
function makeStore(folderWorkspaces: FolderWorkspace[]): Store {
const store: Partial<Store> = {
getRepos: () => [],
getFolderWorkspaces: () => folderWorkspaces,
getProjectGroups: () => [],
getAllWorktreeMeta: () => ({}),
getAllWorktreeMetaForHost: () => ({})
}
// oxlint-disable-next-line typescript/consistent-type-assertions -- SAFETY: hydration reads only the store members stubbed here.
return store as Store
}
// Reset both memoized hydration and the module-scoped registry.
async function loadFresh() {
vi.resetModules()
const hydrateMod = await import('./hydrate-local-pty-registry')
const registryMod = await import('./pty-registry')
return {
hydrate: hydrateMod.hydrateLocalPtyRegistryAtBoot,
listRegisteredPtys: registryMod.listRegisteredPtys
}
}
beforeEach(() => {
getDaemonProviderMock.mockReset()
listLocalRepoWorktreesStrictMock.mockReset()
})
it.each(['deleted', 'remote', 'ssh'] as const)(
'rechecks folder ownership after inventory when the catalog becomes %s',
async (change) => {
const { hydrate, listRegisteredPtys } = await loadFresh()
// oxlint-disable-next-line typescript/consistent-type-assertions -- SAFETY: ownership reads only id and executionHostId.
const folders = [{ id: 'folder-1', executionHostId: 'local' } as FolderWorkspace]
getDaemonProviderMock.mockReturnValue({
listSessions: vi.fn().mockImplementation(async (): Promise<Partial<SessionInfo>[]> => {
if (change === 'deleted') {
folders.splice(0)
} else {
folders[0].executionHostId = change === 'ssh' ? 'ssh:target-1' : 'runtime:environment-1'
}
return [{ sessionId: 'folder:folder-1@@cafebabe', pid: 4242 }]
})
})
await hydrate(makeStore(folders))
expect(listRegisteredPtys()).toEqual([])
expect(listLocalRepoWorktreesStrictMock).not.toHaveBeenCalled()
}
)
@@ -1,71 +0,0 @@
import { describe, expect, it, vi } from 'vitest'
import type { FolderWorkspace } from '../../shared/folder-workspaces/folder-workspace-types'
import type { MemorySnapshotStore } from './collector'
import { resolveWorktreeMemoryNames } from './memory-snapshot-buckets'
const folder: FolderWorkspace = {
id: 'notes-folder',
projectGroupId: 'documentation',
name: 'Release notes',
folderPath: '/notes',
linkedTask: null,
comment: '',
isArchived: false,
isUnread: false,
isPinned: false,
sortOrder: 0,
lastActivityAt: 0,
createdAt: 0,
updatedAt: 0
}
const group = { id: 'documentation', name: 'Documentation' }
function makeStore(): MemorySnapshotStore &
Required<Pick<MemorySnapshotStore, 'getFolderWorkspace' | 'getProjectGroups'>> {
return {
getRepo: vi.fn(),
getWorktreeMeta: vi.fn(),
getFolderWorkspace: vi.fn((id) => (id === folder.id ? folder : undefined)),
getProjectGroups: vi.fn(() => [group])
}
}
describe('memory snapshot workspace names', () => {
it('resolves a folder workspace and its project group from persisted metadata', () => {
const store = makeStore()
expect(resolveWorktreeMemoryNames('folder:notes-folder', store)).toEqual({
worktreeName: 'Release notes',
repoId: 'folder-workspace:documentation',
repoName: 'Documentation'
})
expect(store.getRepo).not.toHaveBeenCalled()
})
it('reads updated folder and project-group names on the next snapshot', () => {
const store = makeStore()
resolveWorktreeMemoryNames('folder:notes-folder', store)
vi.mocked(store.getFolderWorkspace).mockReturnValue({ ...folder, name: 'Changelog' })
vi.mocked(store.getProjectGroups).mockReturnValue([{ ...group, name: 'Docs' }])
expect(resolveWorktreeMemoryNames('folder:notes-folder', store)).toMatchObject({
worktreeName: 'Changelog',
repoName: 'Docs'
})
})
it('keeps a readable folder label if its project-group metadata is missing', () => {
const store = makeStore()
vi.mocked(store.getProjectGroups).mockReturnValue([])
expect(resolveWorktreeMemoryNames('folder:notes-folder', store)).toMatchObject({
worktreeName: 'Release notes',
repoName: 'Release notes'
})
})
it('keeps unknown folder ids identifiable without assigning them to a different folder', () => {
expect(resolveWorktreeMemoryNames('folder:missing', makeStore())).toEqual({
worktreeName: 'folder:missing',
repoId: 'folder:missing',
repoName: 'folder:missing'
})
})
})
@@ -1,82 +0,0 @@
import type { createFixture } from './journal-atomic-settlement-contract-test-fixture'
import { expect, it } from 'vitest'
import {
AGENT_JOURNAL_THREAD_SCOPE,
type AgentJournalSnapshot
} from '../../../shared/agent-session/journal/agent-session-journal-types'
import { settleObservedExitForTest } from '../agent-session-wire/structured-agent-session-observed-exit.test-fixture'
import { openTestJournalHostDatabase } from './journal-host-database-test-support'
import { MAX_JOURNAL_LIFECYCLE_BATCH_BYTES } from './journal-row-schema'
export function registerJournalAtomicSettlementOversizedTests(
fixture: ReturnType<typeof createFixture>
): void {
it.each(['startup', 'stop'] as const)(
'rolls back the %s submission verdict with its oversized item',
async (mode) => {
await fixture.journal.appendSubmission({
clientMessageId: 'unanswered',
payloadFingerprint: 'unanswered',
fence: 7,
handoverRecorded: true,
body: { kind: 'message', role: 'user', blocks: [{ type: 'text', text: 'next' }] }
})
await fixture.journal.resolveDispatch({
clientMessageId: 'unanswered',
state: 'pending',
fence: 7,
turnScope: AGENT_JOURNAL_THREAD_SCOPE
})
if (mode === 'stop') {
await fixture.journal.appendStopEvent({ reason: 'user-stop' }, 7)
}
await fixture.journal.appendItem(
{ provider: 'orca', clientMessageId: 'oversized-call' },
{
kind: 'tool-call',
name: 'Write',
input: 'x'.repeat(MAX_JOURNAL_LIFECYCLE_BATCH_BYTES),
state: 'running'
},
{ fence: 7, turnScope: AGENT_JOURNAL_THREAD_SCOPE }
)
const input = {
journal: fixture.journal,
sessionId: fixture.SESSION,
fence: 7,
settlementId: `${mode}:7`,
verdict: { state: 'interrupted' as const, completedAt: 1_000 },
pendingSubmissionReason: 'owner exited',
showUnexpectedExitOutcome: mode === 'startup',
...(mode === 'startup' ? { exitedDuringStartup: { generation: 'start-7' } } : {})
}
const database = openTestJournalHostDatabase(fixture.root)
const before = fixture.journal.snapshot()
const diskBefore = fixture.rows()
const published: AgentJournalSnapshot[] = []
fixture.journal.observeCommits(() => published.push(fixture.journal.snapshot()))
database.db.exec(`CREATE TEMP TRIGGER reject_submission_settlement_item
BEFORE INSERT ON journal_rows
WHEN NEW.session_id = '${fixture.SESSION}' AND json_extract(NEW.row_json, '$.recovered') = 1
AND json_extract(NEW.row_json, '$.kind') = 'item'
BEGIN SELECT RAISE(ABORT, 'item rejected after submission'); END`)
expect(await settleObservedExitForTest(input)).toMatchObject({ ok: false })
expect(fixture.journal.snapshot()).toEqual(before)
expect(fixture.rows()).toEqual(diskBefore)
expect(published).toEqual([])
database.db.exec('DROP TRIGGER reject_submission_settlement_item')
expect(await settleObservedExitForTest(input)).toMatchObject({ ok: true })
expect(fixture.journal.submission('unanswered')).toMatchObject({
dispatchState: mode === 'stop' ? 'unknown' : 'rejected',
recovered: true,
...(mode === 'stop' ? {} : { rejection: { kind: 'providerStartFailed' } })
})
const committed = fixture.rows().slice(diskBefore.length)
expect(committed.filter((row) => row.kind === 'dispatch')).toHaveLength(1)
expect(published).toEqual([fixture.journal.snapshot()])
expect(await settleObservedExitForTest(input)).toMatchObject({ ok: true })
expect(fixture.rows().slice(diskBefore.length)).toEqual(committed)
expect(published).toHaveLength(1)
}
)
}
@@ -1,9 +1,7 @@
import { createFixture } from './journal-atomic-settlement-contract-test-fixture'
import { registerJournalAtomicSettlementRollbackTests } from './journal-atomic-settlement-rollback.spec'
import { registerJournalAtomicSettlementOversizedTests } from './journal-atomic-settlement-oversized.spec'
export function registerJournalAtomicTerminalSettlementTests(): void {
const fixture = createFixture()
registerJournalAtomicSettlementRollbackTests(fixture)
registerJournalAtomicSettlementOversizedTests(fixture)
}
@@ -1,98 +0,0 @@
// Rows frames must restate current work or clients interpret them as legacy-host frames.
import { describe, expect, it } from 'vitest'
import { agentJournalItemKey } from '../../../shared/agent-session/journal/agent-session-journal-item-key'
import type { AgentJournalRenderItem } from '../../../shared/agent-session/journal/agent-session-journal-types'
import type { AgentSessionSubscribeEvent } from '../../../shared/agent-session/agent-session-wire'
import type { AgentSessionJournal } from '../agent-session-journal/journal-store'
import { refreshDerivedStopNotes } from './agent-session-stop-note-refresh'
import { structuredAgentSessionStopNoteIdentity } from './structured-agent-session-command-turn'
import { StructuredAgentSessionCurrentWork } from './structured-agent-session-current-work'
import type { Subscriber } from './structured-agent-session-subscribers'
const staleTurn: AgentJournalRenderItem = {
itemId: 'turn-old',
revision: 1,
sequence: 1,
observedAt: 1,
body: { kind: 'turn', turnId: 't-old', state: 'running', startedAt: 1 }
}
const stalePrompt: AgentJournalRenderItem = {
itemId: 'prompt-old',
revision: 1,
sequence: 2,
observedAt: 2,
body: {
kind: 'approval',
title: 'Run ls',
detail: null,
options: [{ id: 'allow', label: 'Allow' }],
resolution: { state: 'pending', selectedOptionId: null, resolvedBy: null, resolvedAt: null }
}
}
const noteId = agentJournalItemKey(structuredAgentSessionStopNoteIdentity('t-old'))
const unconfirmed = {
kind: 'status' as const,
text: 'x',
failure: { kind: 'cancelUnconfirmed' as const }
}
const projectedNote: AgentJournalRenderItem = {
itemId: noteId,
revision: 2,
sequence: 3,
observedAt: 3,
body: { kind: 'status', text: 'Stopped.' }
}
const items = [staleTurn, stalePrompt, projectedNote]
describe('the stop-note refresh frame', () => {
it("restates the host's current work", () => {
const emitted: AgentSessionSubscribeEvent[] = []
const journal = {
visitItems: (visit: (itemId: string, sequence: number, body: unknown) => void) =>
visit(noteId, 3, unconfirmed),
itemsRead: () => ({
sessionId: 's',
cursor: { epoch: 'e', sequence: 3 },
items,
submissions: []
}),
itemBody: (itemId: string) => (itemId === noteId ? unconfirmed : null)
}
const work = new StructuredAgentSessionCurrentWork(
{
cursor: () => ({ epoch: 'e', sequence: 3 }),
item: (id) => items.find((item) => item.itemId === id) ?? null,
runningTurn: () => ({ item: staleTurn, turnId: 't-old' }),
itemFence: () => 1,
visitItems: (visit) => {
for (const item of items) {
visit(item.itemId, item.sequence, item.body)
}
},
submissions: () => [],
inputOrder: { recoverableSubmissions: () => [] },
wroteBeforeOpen: () => false
},
2
)
const subscriber = { sessionId: 's', cursor: { epoch: 'e', sequence: 3 }, fence: 2 }
refreshDerivedStopNotes(
{ emit: (_subscriber, event) => emitted.push(event), isActive: () => true },
// oxlint-disable-next-line typescript/consistent-type-assertions -- SAFETY: the refresh reads only the subscriber's session, cursor and fence.
subscriber as unknown as Subscriber,
// oxlint-disable-next-line typescript/consistent-type-assertions -- SAFETY: the refresh reads only visitItems, snapshot and itemBody, all supplied.
journal as unknown as AgentSessionJournal,
10,
work
)
expect(emitted).toHaveLength(1)
const frame = emitted[0]
if (frame?.type !== 'batch') {
throw new Error('expected a batch')
}
expect(frame.batch.items.length).toBeGreaterThan(0)
expect(frame).toMatchObject({ working: false, actionablePromptIds: [], latestTurn: null })
})
})
@@ -1,96 +0,0 @@
import { afterEach, beforeEach, expect, it } from 'vitest'
import { AgentCliInstallationError } from '../agent-cli-installation-error'
import { isStructuredAgentSessionStartFailureRow } from '../../../shared/agent-session/failures/structured-agent-session-start-failure-row-key'
import { projectQueuedMessageCards } from '../../../shared/agent-session/view/structured-agent-session-queued-cards'
import { HOST_TEST_SESSION, hostTestMessage } from './structured-agent-session-host-test-data'
import {
createQueuedMessageTestRig,
eventually,
type QueuedMessageTestRig
} from './structured-agent-session-queued-message-rig.test-fixture'
let rig: QueuedMessageTestRig
beforeEach(async () => {
rig = await createQueuedMessageTestRig({
restartable: true,
idleSweep: { idleMs: () => 0, intervalMs: 60 * 60 * 1000 }
})
})
afterEach(() => rig.dispose())
async function retainedCards() {
const result = await rig.host.history({ sessionId: HOST_TEST_SESSION, direction: 'tail' })
if (!result.ok) {
throw new Error('History refused')
}
const { page } = result
const cards = projectQueuedMessageCards(page.queuedMessages, page.submissions, {
queuePaused: Boolean(page.queuePause)
})
return { page, cards }
}
async function retainedDraft() {
const working = await rig.workingSend()
const queued = await rig.send('retained instruction', 'queue-if-active').result
if (!queued.ok || !('queued' in queued.value)) {
throw new Error('Expected a queued draft')
}
await rig.stop()
await rig.settleAccepted(working, 'earlier-turn')
await rig.host.collaboratorsForTests().lifetime.idleSweep.tick()
rig.starts.mockImplementationOnce(() => {
throw new AgentCliInstallationError({
agent: 'codex',
installedVersion: '0.135.0',
minimumVersion: '0.136.0'
})
})
return queued.value.queued.messageId
}
it.each(['returned', 'held'] as const)(
'uses per-message Send after an existing conversation restart is version-refused with a %s draft and no pause',
async (kind) => {
let id = await retainedDraft()
expect(await rig.resume()).toMatchObject({ ok: true, value: { resumed: true } })
await eventually(async () => expect((await rig.handoff(id))?.dispatchState).toBe('rejected'))
const rejected = await rig.handoffId(id)
if (kind === 'held') {
await rig.deleteQueued(id)
id = 'held-draft'
const journal = rig.host.collaboratorsForTests().sessions.get(HOST_TEST_SESSION)?.journal
if (!journal) {
throw new Error('Missing journal')
}
await journal.queuedMessages.insert({
messageId: id,
body: hostTestMessage('retained instruction'),
holdReason: 'send_failed'
})
}
const { page, cards } = await retainedCards()
expect(page.queuePause ?? null).toBeNull()
expect(cards).toMatchObject([{ messageId: id, hold: kind === 'held' ? 'paused' : 'returned' }])
expect(
page.items.filter((item) => isStructuredAgentSessionStartFailureRow(item.itemId))
).toMatchObject([
{
body: {
text: 'Codex 0.135.0 is too old for chats. Update to 0.136.0 or newer.',
tone: 'error',
failure: { refusal: { details: { codexInstallation: { installedVersion: '0.135.0' } } } }
}
}
])
expect(await rig.resume()).toMatchObject({ ok: true, value: { resumed: false } })
const attempts = rig.starts.mock.calls.length
expect((await rig.submission(rejected))?.handedOverAt).toBeUndefined()
expect(rig.starts).toHaveBeenCalledTimes(attempts)
expect(await rig.sendNow(id)).toMatchObject({ ok: true })
await eventually(async () => expect((await rig.handoff(id))?.handedOverAt).toBeDefined())
expect(await rig.handoffId(id)).not.toBe(rejected)
expect(rig.starts).toHaveBeenCalledTimes(attempts + 1)
expect((await rig.submission(rejected))?.dispatchState).toBe('rejected')
}
)
@@ -1,100 +0,0 @@
import { agentSessionConversationView } from '../../../shared/agent-session/lifecycle/agent-session-conversation-view'
import { it, expect, vi } from 'vitest'
import { createClaudeModelCatalogProbe } from '../../claude/claude-model-catalog-probe'
import { createAgentModelCatalogService } from '../agent-model-catalog/agent-model-catalog-service'
import { AgentModelCatalogStore } from '../agent-model-catalog/agent-model-catalog-store'
import { agentSessionRecordFixture } from '../agent-session-record-test-fixture'
import { claudeAndCodexAgents } from './structured-agent-session-adapter-router-test-support'
import type { StructuredAgentSessionMutationContext } from './structured-agent-session-host-mutations'
import { readStructuredAgentSessionOptions } from './structured-agent-session-options-read'
const SESSION = 'session-1'
function restingJournal() {
return {
threadGoal: () => null,
visitItems: () => {},
inputOrder: { hasInputs: () => false },
statusRead: () => ({ items: [], submissions: [] }),
contextUsage: () => null,
context: { floor: () => null }
}
}
it('keeps one legacy deadline across an optional join and current resting read', async () => {
vi.useFakeTimers()
const args = Promise.withResolvers<string[]>()
const resolving = vi.fn(() => args.promise)
const environment = vi.fn(async () => ({}))
const record = agentSessionRecordFixture({
sessionId: SESSION,
provider: 'claude',
permissionMode: 'auto',
accountHome: { variable: 'CLAUDE_CONFIG_DIR', path: '/fixture/account' }
})
const catalog = createAgentModelCatalogService({
store: new AgentModelCatalogStore(),
getConversation: () => {
const held = record
return held ? agentSessionConversationView(held) : undefined
},
getRecord: () => record,
drivesRecord: () => true,
resolveAccountHome: async () => record.accountHome,
recordWorkspacePath: async () => '/fixture/workspace',
probes: {
claude: createClaudeModelCatalogProbe({
resolveLaunchArgs: resolving,
resolveInheritedEnv: environment,
resolveCommand: () => ({ command: '/fixture/claude', prefixArgs: [] }),
resolveAuthPolicy: () => ({ account: 'system' })
})
}
})
const completion = Promise.withResolvers<void>()
const optionsReady = vi.fn(() => completion.promise)
let child: { phase: 'ready'; fence: number } | null = { phase: 'ready', fence: 1 }
const resting = { child: null, params: { provider: 'claude' }, journal: restingJournal() }
// oxlint-disable-next-line typescript/consistent-type-assertions -- SAFETY: the full read only uses these context members.
const context = {
deps: {
adapter: { readOptions: async () => ({ models: [], current: {} }) },
agents: claudeAndCodexAgents(),
store: {
getConversation: () => {
const held = record
return held ? agentSessionConversationView(held) : undefined
},
getRecord: () => record
},
modelCatalog: catalog
},
deliveryOrder: { optionsReady },
serialize: (_id: string, task: () => Promise<unknown>) => task(),
openConversation: async () => ({ ...resting, child }),
conversation: async () => resting
} as unknown as StructuredAgentSessionMutationContext
try {
const answer = readStructuredAgentSessionOptions(context, SESSION, {
readsWithoutModel: false
})
await vi.advanceTimersByTimeAsync(0)
expect(resolving).not.toHaveBeenCalled()
await vi.advanceTimersByTimeAsync(10_000)
child = null
completion.resolve()
await vi.advanceTimersByTimeAsync(0)
expect(optionsReady).toHaveBeenCalledExactlyOnceWith(SESSION)
expect(resolving).toHaveBeenCalledOnce()
await vi.advanceTimersByTimeAsync(20_001)
await expect(answer).resolves.toMatchObject({ current: { permissionMode: 'auto' } })
expect(resolving).toHaveBeenCalledOnce()
args.resolve([])
await vi.advanceTimersByTimeAsync(0)
expect(environment).not.toHaveBeenCalled()
} finally {
completion.resolve()
args.resolve([])
catalog.stop()
vi.useRealTimers()
}
})
@@ -1,75 +0,0 @@
import { expect, it } from 'vitest'
import { projectStructuredAgentSessionMessages } from '../../../shared/agent-session/view/structured-agent-session-message-projection'
import { agentJournalSubmissionKey } from '../../../shared/agent-session/journal/agent-session-journal-item-key'
import {
createQueuedMessageTestRig,
eventually
} from './structured-agent-session-queued-message-rig.test-fixture'
import {
HOST_TEST_NOW,
HOST_TEST_SESSION as SESSION
} from './structured-agent-session-host-test-data'
import { STRUCTURED_AGENT_SESSION_IDLE_MS as IDLE_MS } from './structured-agent-session-idle-sweep'
it('keeps a recovered handoff consumed through natural idle unload and a history reopen', async () => {
let now = HOST_TEST_NOW
const rig = await createQueuedMessageTestRig({
restartable: true,
now: () => now,
idleSweep: { idleMs: () => IDLE_MS, intervalMs: 2_000_000_000 }
})
try {
const first = await rig.workingSend()
await rig.send('handed-over card', 'queue-if-active').result
await rig.send('still waiting', 'queue-if-active').result
const cards = await rig.drafts()
const consumedId = cards[0]!.messageId
const waitingId = cards[1]!.messageId
await rig.settleAccepted(first, 'initial')
await eventually(async () =>
expect((await rig.handoff(consumedId))?.handedOverAt).toBeDefined()
)
const handoffId = await rig.handoffId(consumedId)
const dispatches = rig.dispatch.mock.calls.length
// Simulate process loss without a graceful close settling the handed-over row.
rig.host.collaboratorsForTests().lifetime.idleSweep.dispose()
rig.crashRestartHostProcess()
const read = async () => {
const result = await rig.host.history({ sessionId: SESSION, direction: 'tail' })
if (!result.ok) {
throw new Error('history refused')
}
return result.page
}
const recovered = await read()
expect(recovered.submissions.find((row) => row.clientMessageId === handoffId)).toMatchObject({
queuedMessageId: consumedId,
dispatchState: 'unknown',
recovered: true
})
expect(recovered.queuedMessages?.map((card) => card.messageId)).toEqual([waitingId])
const messages = () =>
read().then((page) => projectStructuredAgentSessionMessages(page.items, [], page.submissions))
const bubble = (await messages()).filter(
(message) => message.id === agentJournalSubmissionKey(handoffId)
)
expect(bubble).toHaveLength(1)
expect(bubble[0]?.unsent).toBeUndefined()
expect(bubble[0]?.queued).toBeUndefined()
now += IDLE_MS + 1
await rig.host.collaboratorsForTests().lifetime.idleSweep.tick()
expect(rig.host.collaboratorsForTests().sessions.has(SESSION)).toBe(true)
expect(await rig.deleteQueued(waitingId)).toMatchObject({ ok: true })
now += IDLE_MS + 1
await rig.host.collaboratorsForTests().lifetime.idleSweep.tick()
expect(rig.host.collaboratorsForTests().sessions.has(SESSION)).toBe(false)
const reopened = await read()
expect(reopened.queuedMessages ?? []).toEqual([])
expect(
(await messages()).filter((message) => message.id === agentJournalSubmissionKey(handoffId))
).toEqual(bubble)
expect(rig.dispatch).toHaveBeenCalledTimes(dispatches)
} finally {
await rig.dispose()
}
})
@@ -1,6 +1,6 @@
import { afterEach, beforeEach, describe, expect, it, vi, type Mock } from 'vitest'
import { AGENT_SESSION_MAX_NEW_OPERATION_AGE_MS } from '../../../shared/agent-session/agent-session-host-authority'
import type { AgentSessionJournal } from '../agent-session-journal/journal-store'
import { openTestJournalHostDatabase } from '../agent-session-journal/journal-host-database-test-support'
import type { StructuredAgentSessionAdapter } from './structured-agent-session-adapter'
import type { StructuredAgentSessionHost } from './structured-agent-session-host'
@@ -10,11 +10,7 @@ import {
envelope,
hostTestState
} from './structured-agent-session-host-test-harness'
import {
HOST_TEST_NOW as NOW,
HOST_TEST_SESSION as SESSION,
hostTestMessage
} from './structured-agent-session-host-test-data'
import { HOST_TEST_NOW as NOW, hostTestMessage } from './structured-agent-session-host-test-data'
let root: string
let host: StructuredAgentSessionHost
@@ -26,12 +22,6 @@ beforeEach(() => {
afterEach(() => vi.restoreAllMocks())
function hostJournal(): AgentSessionJournal {
return (
host as unknown as { sessions: Map<string, { journal: AgentSessionJournal }> }
).sessions.get(SESSION)!.journal
}
function sendParams(text: string, clientOperationId?: string) {
const body = hostTestMessage(text)
return {
@@ -72,39 +62,6 @@ describe('send without ledger policy', () => {
})
describe('a /clear in flight', () => {
it('answers a resend from the attempt queued ahead of it, delivering once', async () => {
await attach()
const journal = hostJournal()
const append = journal.appendSubmission.bind(journal)
const held = Promise.withResolvers<void>()
// Hold the earlier send so clear arrives before the queued retry runs.
vi.spyOn(journal, 'appendSubmission').mockImplementationOnce(async (...args) => {
await held.promise
return append(...args)
})
const earlier = host.send(CALLER, sendParams('holds the queue'))
const params = sendParams('queued ahead of the clear')
const id = params.envelope.clientOperationId
const attempt = host.send(CALLER, params)
await vi.waitFor(() => expect(journal.appendSubmission).toHaveBeenCalledTimes(1))
const clearing = clear()
const resent = host.send(CALLER, params)
held.resolve()
const [first, second] = await Promise.all([attempt, resent, earlier, clearing])
expect(first).toMatchObject({ ok: true, replayed: false })
expect(second).toMatchObject({
ok: true,
replayed: true,
value: { submission: { clientMessageId: id } }
})
expect(journal.submissions().filter((entry) => entry.clientMessageId === id)).toHaveLength(1)
expect(
dispatch.mock.calls.filter(([input]) => input.clientMessageId === id).length
).toBeLessThanOrEqual(1)
})
it('refuses an old id only for the clear in flight', async () => {
await attach()
const clearing = clear()
@@ -1,110 +0,0 @@
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
import type { AgentJournalSubmission } from '../../../shared/agent-session/journal/agent-session-journal-types'
import { structuredAgentSessionRestartResumeSurfaces } from './structured-agent-session-restart-resume-wiring'
import { StructuredAgentSessionSendSettlement } from './structured-agent-session-send-settlement'
const SESSION = 'session-1'
const MESSAGE = 'continuation-1'
let submission: AgentJournalSubmission
const journal = {
submission: (id: string): AgentJournalSubmission | undefined =>
id === MESSAGE ? submission : undefined,
cursor: () => ({ epoch: 'epoch-1', sequence: 1 }),
activeTurnId: () => null
}
beforeEach(() => {
vi.useFakeTimers()
submission = {
clientMessageId: MESSAGE,
fence: 2,
payloadFingerprint: 'fingerprint',
dispatchState: 'pending',
providerItemId: null,
reason: null,
submittedAt: 1,
resolvedAt: null,
handoverRecorded: true
}
})
afterEach(() => {
vi.useRealTimers()
})
describe('the restart continuation waits for its delivery (W18)', () => {
it('reaches accepted after a 40 s cold start, with nothing filed as unconfirmed', async () => {
const settlement = new StructuredAgentSessionSendSettlement(() => journal)
const surfaces = structuredAgentSessionRestartResumeSurfaces(
{
revealSession: async () => ({ readable: true }),
send: async () => {
throw new Error('not used')
},
waitForSendSettlement: settlement.wait
},
() => 0
)
const verdict = surfaces.awaitSendSettlement(SESSION, MESSAGE)
await vi.advanceTimersByTimeAsync(40_000)
submission = { ...submission, dispatchState: 'accepted', providerItemId: 'item-1' }
settlement.publish(SESSION, journal)
await expect(verdict).resolves.toMatchObject({
value: { submission: { dispatchState: 'accepted' } }
})
})
})
describe('a restart batch holds a chat until its continuation is handed over', () => {
function surfacesOver(settlement: StructuredAgentSessionSendSettlement) {
return structuredAgentSessionRestartResumeSurfaces(
{
revealSession: async () => ({ readable: true }),
send: async () => {
throw new Error('not used')
},
waitForSendSettlement: settlement.wait
},
() => 0
)
}
it('keeps waiting through a slow start and resolves once the agent takes the message', async () => {
const settlement = new StructuredAgentSessionSendSettlement(() => journal)
let released = false
const handedOver = surfacesOver(settlement)
.awaitSendHandedOver(SESSION, MESSAGE)
.then((result) => {
released = true
return result
})
await vi.advanceTimersByTimeAsync(5 * 60_000)
expect(released).toBe(false)
submission = { ...submission, handedOverAt: 2 }
settlement.publish(SESSION, journal)
await expect(handedOver).resolves.toMatchObject({
value: { submission: { dispatchState: 'pending', handedOverAt: 2 } }
})
})
it('resolves once the start fails and the message is rejected', async () => {
const settlement = new StructuredAgentSessionSendSettlement(() => journal)
const handedOver = surfacesOver(settlement).awaitSendHandedOver(SESSION, MESSAGE)
submission = { ...submission, dispatchState: 'rejected', reason: 'Codex could not start.' }
settlement.publish(SESSION, journal)
await expect(handedOver).resolves.toMatchObject({
value: { submission: { dispatchState: 'rejected' } }
})
})
it('resolves undefined when the session closes first', async () => {
const settlement = new StructuredAgentSessionSendSettlement(() => journal)
const handedOver = surfacesOver(settlement).awaitSendHandedOver(SESSION, MESSAGE)
settlement.closeSession(SESSION)
await expect(handedOver).resolves.toBeUndefined()
})
})
@@ -1,116 +0,0 @@
import { cp, rm } from 'node:fs/promises'
import { afterEach, beforeEach, expect, it, vi } from 'vitest'
import { openTestAgentSessionRecordStore } from '../../runtime/agent-session/records/agent-session-record-store-test-harness'
import { JOURNAL_OUTCOME_MARKER_VERSION } from '../agent-session-journal/journal-outcome-format'
import type Database from '../../sqlite/sync-database'
import {
closeTestJournalHostDatabases,
liveTestJournalRows,
openTestJournalHostDatabase,
updateTestJournalRowJson
} from '../agent-session-journal/journal-host-database-test-support'
import { StructuredAgentSessionHost } from './structured-agent-session-host'
import {
adapter,
attach,
CALLER,
envelope,
hostTestState,
replaceHostTestState
} from './structured-agent-session-host-test-harness'
import {
HOST_TEST_NOW as NOW,
HOST_TEST_SESSION as SESSION,
hostTestMessage
} from './structured-agent-session-host-test-data'
import { createStructuredAgentSessionLogger } from './structured-agent-session-logger'
import { NO_STRUCTURED_AGENTS } from './structured-agent-session-adapter-router-test-support'
const relaunchedRoots: string[] = []
beforeEach(() => {
vi.spyOn(console, 'warn').mockImplementation(() => undefined)
})
afterEach(async () => {
vi.restoreAllMocks()
await closeTestJournalHostDatabases()
await Promise.all(
relaunchedRoots.splice(0).map((dir) => rm(dir, { recursive: true, force: true }))
)
})
async function relaunchWith(
edit: (db: Database.Database) => void
): Promise<StructuredAgentSessionHost> {
const before = hostTestState()
await attach()
await before.host.flushStreamedEvents(SESSION)
await before.store.renewLeases([])
const relaunched = `${before.root}-relaunched`
relaunchedRoots.push(relaunched)
// Simulate process death without copying its live lock.
await cp(before.root, relaunched, {
recursive: true,
filter: (source) => !source.includes('.lock')
})
edit(openTestJournalHostDatabase(relaunched).db)
const store = await openTestAgentSessionRecordStore(relaunched, {
legacySessionTabIds: [SESSION]
})
const host = new StructuredAgentSessionHost({
agents: NO_STRUCTURED_AGENTS,
logger: createStructuredAgentSessionLogger(),
store,
adapter: adapter(),
journalDatabase: openTestJournalHostDatabase(relaunched),
claimKeyId: 'key-1',
mintSpawnToken: () => 'spawn-next',
now: () => NOW
})
replaceHostTestState({ store, host })
return host
}
const damaged = (db: Database.Database) => updateTestJournalRowJson(db, SESSION, 1, '}{')
const savedByNewerOrca = (db: Database.Database) => {
const epochRow = liveTestJournalRows(db, SESSION).find((row) => row.seq === 1)!
const newer = { ...JSON.parse(epochRow.rowJson), v: JOURNAL_OUTCOME_MARKER_VERSION + 1 }
updateTestJournalRowJson(db, SESSION, 1, JSON.stringify(newer))
}
it.each([
['damaged', damaged, 'journalCorrupt'],
['saved by a newer Orca', savedByNewerOrca, 'journalWrittenByNewerOrca']
] as const)(
'lists a restored chat that could not be opened (%s), and its read and send are refused with why',
async (_why, edit, reason) => {
const host = await relaunchWith(edit)
const workspaceId = hostTestState().store.getRecord(SESSION)?.location.workspaceId
const refusal = { code: 'agent_session_journal_unreadable', details: { reason } }
await host.seedSessionTabs([SESSION])
expect(host.hasSession(SESSION)).toBe(false)
expect(host.listSessionTabs()).toEqual([
{ sessionId: SESSION, workspaceId, agent: expect.any(String) }
])
for (let read = 0; read < 3; read += 1) {
await expect(host.history({ sessionId: SESSION, direction: 'tail' })).rejects.toMatchObject({
refusal
})
}
const readFailureLogs = vi
.mocked(console.warn)
.mock.calls.filter(([line]) => String(line).includes('open-for-read'))
expect(readFailureLogs).toHaveLength(1)
const body = hostTestMessage('sent to a chat that cannot load')
await expect(
host.send(CALLER, { envelope: envelope('agentSession.send', { body }), body })
).resolves.toMatchObject({ ok: false, refusal })
await host.setSessionTabVisibility(SESSION, false)
expect(host.listSessionTabs()).toEqual([])
await host.flushAllStreamedEvents()
}
)
@@ -61,12 +61,6 @@ async function lookup(cwd = '/repo/orca', homeDir = join(root, 'home'), temp = j
}
describe('OMP known-workspace transcript lookup', () => {
it('does not search unrelated workspaces when the known cwd has no transcript', async () => {
await record('--unrelated-project--')
expect(await lookup()).toBeNull()
expect(io.open).not.toHaveBeenCalled()
})
it('finds the reverted SHA256 bucket used by OMP 17.2.5–17.2.8', async () => {
const windows = process.platform === 'win32'
const digest = windows
@@ -88,28 +82,4 @@ describe('OMP known-workspace transcript lookup', () => {
expect(await lookup(alias, home)).toBe(expected)
}
)
it('never descends into session artifacts even when a child carries the wanted ID', async () => {
const dir = join(sessionsDir, '--repo-orca--')
const artifact = FILE.slice(0, -'.jsonl'.length)
await mkdir(join(dir, artifact), { recursive: true })
await writeFile(join(dir, artifact, FILE), '{}\n')
expect(await lookup()).toBeNull()
expect(io.open.mock.calls.map(([path]) => path)).toEqual([dir])
})
it.skipIf(process.platform === 'win32')('rejects symlinked groups and files', async () => {
const outside = join(root, 'outside')
await mkdir(outside, { recursive: true })
await mkdir(sessionsDir, { recursive: true })
await writeFile(join(outside, FILE), '{}\n')
const group = join(sessionsDir, '--repo-orca--')
await symlink(outside, group)
expect(await lookup()).toBeNull()
expect(io.open).not.toHaveBeenCalled()
await rm(group)
await mkdir(group)
await symlink(join(outside, FILE), join(group, FILE))
expect(await lookup()).toBeNull()
})
})
@@ -1,45 +0,0 @@
import { beforeEach, describe, expect, it, vi } from 'vitest'
import { runProcess } from '@orca/process-host'
import { resolveCommandOnLocalPath } from '../ipc/command-path/command-path-resolver'
import {
detectOpenCodeCredentialBackend,
resetOpenCodeCredentialBackendProbes
} from './opencode-credential-backend'
const files = vi.hoisted(() => ({ realpath: vi.fn(), stat: vi.fn() }))
vi.mock('@orca/process-host', () => ({ runProcess: vi.fn() }))
vi.mock('../ipc/command-path/command-path-resolver', () => ({ resolveCommandOnLocalPath: vi.fn() }))
vi.mock('node:fs/promises', () => files)
describe('OpenCode credential execution backend', () => {
beforeEach(() => {
vi.resetAllMocks()
resetOpenCodeCredentialBackendProbes()
files.realpath.mockImplementation(async (path: string) => path)
files.stat.mockResolvedValue({ dev: 1, ino: 2, size: 3, mtimeMs: 4, ctimeMs: 5 })
vi.mocked(resolveCommandOnLocalPath).mockResolvedValue('/task/bin/opencode')
vi.mocked(runProcess).mockResolvedValue({
code: 0,
signal: null,
stdout: '1.18.30\n',
stderr: '',
timedOut: false
})
})
it('reprobes when the resolved binary is replaced', async () => {
expect(await detectOpenCodeCredentialBackend()).toBe('v1')
files.stat.mockResolvedValue({ dev: 1, ino: 6, size: 3, mtimeMs: 7, ctimeMs: 8 })
vi.mocked(runProcess).mockResolvedValue({
code: 0,
signal: null,
stdout: 'opencode v2.0.16',
stderr: '',
timedOut: false
})
expect(await detectOpenCodeCredentialBackend()).toBe('v2')
expect(runProcess).toHaveBeenCalledTimes(2)
})
})
@@ -5,10 +5,8 @@ import { afterEach, describe, expect, it } from 'vitest'
import {
activationFenceExists,
createOrcadIdleProbes,
resolveOrcadManagedIdleExit,
type OrcadManagedIdleExitPorts
} from './orcad-managed-idle-exit'
import { ORCAD_E2E_IDLE_TIMEOUT_ENV } from '../../shared/orcad-idle-exit'
const config = { timeoutMs: 1_000, activationRoot: '/home/u/.orca-remote/.fence' }
@@ -34,41 +32,7 @@ async function verdicts(ports: OrcadManagedIdleExitPorts): Promise<Record<string
return Object.fromEntries(entries)
}
describe('resolveOrcadManagedIdleExit', () => {
it('stays off for a server the user started or paired', () => {
expect(resolveOrcadManagedIdleExit({})).toBeNull()
expect(resolveOrcadManagedIdleExit({ [ORCAD_E2E_IDLE_TIMEOUT_ENV]: '50' })).toBeNull()
})
})
describe('createOrcadIdleProbes', () => {
it.each<[string, Partial<OrcadManagedIdleExitPorts>, string]>([
[
'an open client socket',
{
readClientActivity: () => ({ openConnections: 1, requestsInFlight: 0, lastRequestAt: 0 })
},
'clients'
],
[
'a request still running',
{
readClientActivity: () => ({ openConnections: 0, requestsInFlight: 1, lastRequestAt: 0 })
},
'clients'
],
['an in-process terminal', { listTerminals: async () => [{ id: 'pty-1' }] }, 'terminals'],
['a live daemon session', { countDaemonSessions: async () => 2 }, 'terminals'],
['a working agent', { agentStates: () => [{ state: 'working' }] }, 'agents'],
['a loaded chat provider', { hasChatProviders: () => true }, 'chat-providers'],
['a staged migration', { hasStagedMigration: () => true }, 'migration'],
['an enabled or running automation', { automationsBusy: () => true }, 'automations'],
['an Orca-started emulator', { emulatorsBusy: async () => true }, 'emulators'],
['a held activation fence', { activationFenceExists: async () => true }, 'activation']
])('reads %s as busy', async (_label, override, probe) => {
expect((await verdicts({ ...idlePorts(), ...override }))[probe]).toBe('busy')
})
it('treats a daemon that did not answer as unverifiable, not as no terminals', async () => {
const result = await verdicts({ ...idlePorts(), countDaemonSessions: async () => null })
expect(result.terminals).toBe('unverifiable')
@@ -1,114 +0,0 @@
import { hostWorktreeTarget } from '../../../shared/worktree/host-qualified-identity'
import { closeTestStores, createSqliteTestStore } from '../persistence-test-harness'
// Drive real Store writers so disconnected prune-gate call sites cannot pass unnoticed.
import { mkdtempSync, realpathSync } from 'node:fs'
import { tmpdir } from 'node:os'
import { join } from 'node:path'
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
import type { WorkspaceSessionState } from '../../../shared/workspace-session/workspace-session-state-types'
import { removeWorktreeMetaRow } from '../host-qualified-worktree-meta'
vi.mock('electron', () => ({
app: {
getPath: () => tmpdir(),
getName: () => 'orca-test',
getVersion: () => '0.0.0-test',
isPackaged: false,
on: () => {},
whenReady: () => Promise.resolve()
},
safeStorage: {
isEncryptionAvailable: () => false,
encryptString: (value: string) => Buffer.from(value),
decryptString: (value: Buffer) => value.toString()
},
ipcMain: { on: () => {}, handle: () => {} },
BrowserWindow: { getAllWindows: () => [] }
}))
const { Store } = await import('./store')
const {
__resetLocalWorktreeMetadataPruneGateForTests,
isLocalWorktreeMetadataPruneDue,
markLocalWorktreeMetadataPruneStarted
} = await import('../../workspaces/catalog/local-worktree-metadata-prune-gate')
const REPO_ID = 'repo-1'
const WORKTREE_ID = `${REPO_ID}::/tmp/worktree-a`
const stores: InstanceType<typeof Store>[] = []
beforeEach(() => {
__resetLocalWorktreeMetadataPruneGateForTests()
})
afterEach(async () => {
// Freeze debounced writes before their temporary directories disappear.
for (const store of stores.splice(0)) {
store.freezeWrites()
}
await closeTestStores()
vi.restoreAllMocks()
})
function createStore(): InstanceType<typeof Store> {
const dir = realpathSync(mkdtempSync(join(tmpdir(), 'orca-store-prune-gate-')))
const store = createSqliteTestStore(Store, { dataFile: join(dir, 'orca-data.json') })
stores.push(store)
return store
}
function parkGate(): void {
markLocalWorktreeMetadataPruneStarted(REPO_ID)
expect(isLocalWorktreeMetadataPruneDue(REPO_ID)).toBe(false)
}
describe('store signals to the worktree metadata prune gate', () => {
it('re-arms the gate when a session releases its claim on a worktree', () => {
const store = createStore()
store.setWorkspaceSession({
activeRepoId: REPO_ID,
activeWorktreeId: WORKTREE_ID,
activeTabId: 'tab-1',
tabsByWorktree: { [WORKTREE_ID]: [{ id: 'tab-1', worktreeId: WORKTREE_ID }] },
terminalLayoutsByTabId: {}
} as unknown as WorkspaceSessionState)
parkGate()
store.removeWorkspaceSessionStateForWorktree(
hostWorktreeTarget({ id: WORKTREE_ID, hostId: 'local' })
)
expect(isLocalWorktreeMetadataPruneDue(REPO_ID)).toBe(true)
})
it('re-arms the gate when a metadata row is removed', () => {
const store = createStore()
store.setWorktreeMeta(hostWorktreeTarget({ id: WORKTREE_ID, hostId: 'local' }), {
displayName: 'a',
hostId: 'local'
})
parkGate()
removeWorktreeMetaRow(store, WORKTREE_ID, undefined)
expect(isLocalWorktreeMetadataPruneDue(REPO_ID)).toBe(true)
})
it('leaves the gate parked for writes that only add or update a claim', () => {
const store = createStore()
parkGate()
store.setWorktreeMeta(hostWorktreeTarget({ id: WORKTREE_ID, hostId: 'local' }), {
displayName: 'a',
hostId: 'local'
})
store.setWorktreeMeta(hostWorktreeTarget({ id: WORKTREE_ID, hostId: 'local' }), {
displayName: 'b',
hostId: 'local'
})
// Ordinary scan writes must not rearm pruning and recreate the scan storm.
expect(isLocalWorktreeMetadataPruneDue(REPO_ID)).toBe(false)
})
})
@@ -3,10 +3,7 @@ import { tmpdir } from 'node:os'
import { join } from 'node:path'
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
import type * as FsPromises from 'node:fs/promises'
import {
renamePluginFileWithWindowsRetry,
writePluginFileAtomically
} from './plugin-atomic-file-write'
import { writePluginFileAtomically } from './plugin-atomic-file-write'
// Windows AV/indexer locks cannot be provoked on CI, so queue the errno codes instead.
const locks = vi.hoisted(() => ({ codes: [] as string[] }))
@@ -89,38 +86,3 @@ describe('writePluginFileAtomically', () => {
expect(await readFile(target, 'utf8')).toMatch(/^value-\d$/)
})
})
describe('renamePluginFileWithWindowsRetry', () => {
describe('on Windows', () => {
let dir: string
let restorePlatform: () => void
beforeEach(async () => {
dir = await mkdtemp(join(tmpdir(), 'orca-plugin-rename-win-'))
restorePlatform = withPlatform('win32')
})
afterEach(async () => {
restorePlatform()
locks.codes = []
await rm(dir, { recursive: true, force: true })
})
it.each(['EPERM', 'EACCES', 'EBUSY'])('retries past a transient %s lock', async (code) => {
await writePluginFileAtomically(join(dir, 'from'), 'payload')
locks.codes = [code, code]
await renamePluginFileWithWindowsRetry(join(dir, 'from'), join(dir, 'to'))
expect(await readFile(join(dir, 'to'), 'utf8')).toBe('payload')
expect(locks.codes).toEqual([])
})
it('rethrows a non-retryable code without retrying', async () => {
await writePluginFileAtomically(join(dir, 'from'), 'payload')
locks.codes = ['ENOSPC', 'ENOSPC']
await expect(
renamePluginFileWithWindowsRetry(join(dir, 'from'), join(dir, 'to'))
).rejects.toMatchObject({ code: 'ENOSPC' })
expect(locks.codes).toEqual(['ENOSPC'])
})
})
})
@@ -1,13 +1,5 @@
import { describe, expect, it } from 'vitest'
import { resolvePluginModuleSpecifier } from './plugin-frame-module-resolution'
import { createPluginSourceModuleCompiler } from './plugin-frame-module-transform'
import { transpilePluginSource } from './plugin-source-transpiler'
// The real transform, in process; the worker hop is covered by plugin-source-transpile-client.
const inProcess = () =>
createPluginSourceModuleCompiler({
transpile: async (path, source) => transpilePluginSource(path, source)
})
const FILES = new Set([
'src/panel.tsx',
@@ -18,7 +10,6 @@ const FILES = new Set([
'src/my file.ts'
])
const exists = async (path: string) => FILES.has(path)
const encode = (text: string) => new TextEncoder().encode(text)
describe('resolvePluginModuleSpecifier', () => {
it('refuses bare packages, escapes and missing files', async () => {
@@ -36,31 +27,3 @@ describe('resolvePluginModuleSpecifier', () => {
})
})
})
describe('createPluginSourceModuleCompiler', () => {
it('strips types, compiles JSX for orca and rewrites relative imports, keeping lines', async () => {
const compile = inProcess()
const source = [
"import { Stack, type Atom } from 'orca'",
"import { add } from './util'",
'type Props = { n: number }',
'export default function Panel({ n }: Props) {',
' return <Stack gap="md">{add(n, 1)}</Stack>',
'}',
"export const later = () => import('./list')"
].join('\n')
const code = await compile({ relativePath: 'src/panel.tsx', bytes: encode(source), exists })
expect(code).toContain('from "orca/jsx-runtime"')
expect(code).toContain("from './util.ts'")
expect(code).toContain('import("./list/index.tsx")')
expect(code).not.toContain('Props')
expect(code).not.toContain('Atom')
// Line-preserving: a guest error's line number is the author's line number.
const lines = code.split('\n')
expect(lines[4]).toContain('_jsx(Stack')
const mapLine = lines.at(-2) ?? ''
expect(mapLine).toMatch(/^\/\/# sourceMappingURL=data:application\/json/)
const map = JSON.parse(Buffer.from(mapLine.split('base64,')[1], 'base64').toString('utf8'))
expect(map).toMatchObject({ sources: ['panel.tsx'], sourcesContent: [source] })
})
})
@@ -67,23 +67,3 @@ describe('worker panel requests', () => {
await pending
})
})
describe('workspace.changed in workers', () => {
it('replays the current context to a handler as it subscribes', async () => {
let orca!: PluginWorkerOrcaApi
const { send, runtime } = await activate((api) => (orca = api))
const handler = vi.fn()
const disposed = vi.fn()
orca.events.on('workspace.changed', handler)
orca.events.on('workspace.changed', disposed).dispose()
const reads = send.mock.calls
.map(([message]) => message)
.filter((message) => message.method === 'workspace.readContext')
expect(reads).toHaveLength(2)
for (const read of reads) {
await runtime.handleMessage({ orca: 1, kind: 'res', id: read.id, result: { branch: 'main' } })
}
await vi.waitFor(() => expect(handler).toHaveBeenCalledWith({ context: { branch: 'main' } }))
expect(disposed).not.toHaveBeenCalled()
})
})
@@ -1,114 +0,0 @@
/** #15192: replay ConPTY bytes/resizes into the host emulator and assert buffer cells, since valid repaint bytes can still duplicate glyphs.
* Fixture/control assertions also run on POSIX; only bundled-vs-system ConPTY comparison requires Windows. */
import { describe, expect, it } from 'vitest'
import type { Terminal } from '@xterm/headless'
import { HeadlessEmulator } from '../daemon/headless-emulator'
import { readWrappedLineGlyphs } from '../daemon/__fixtures__/terminal-wide-cell-grid'
const KOREAN_LINE = '안녕하세요 오르카 테스트입니다. 결론부터 말씀드리면 시각적 피로도'
const LATIN_LINE = 'roadmap/complete-overhaul-backlog-history.md (1.75) R-08)'
const LINE_REPEATS = 8
const COLS = 40
const ROWS = 12
const glyphsOf = (line: string): string => line.replace(/\s+/g, '')
const KOREAN_GLYPHS = glyphsOf(KOREAN_LINE)
const LATIN_GLYPHS = glyphsOf(LATIN_LINE)
type Event = { data: string } | { resizeTo: number }
type RunOptions = {
useConptyDll: boolean
/** Milliseconds after spawn at which to resize, paired with the target width. */
resizes?: { atMs: number; cols: number }[]
/** How long the child stays alive after its last write, so a resize can trigger a repaint. */
holdMs: number
}
// Node writes deterministic UTF-8; cmd.exe output depends on the machine codepage.
function childScript(holdMs: number): string {
return [
`const ko=${JSON.stringify(KOREAN_LINE)};`,
`const la=${JSON.stringify(LATIN_LINE)};`,
`let i=0;`,
`const t=setInterval(()=>{process.stdout.write(ko+"\\r\\n"+la+"\\r\\n");`,
`if(++i>=${LINE_REPEATS}){clearInterval(t);setTimeout(()=>process.exit(0),${holdMs});}},25);`
].join('')
}
async function recordConpty(options: RunOptions): Promise<Event[]> {
const nodePty = await import('node-pty')
const proc = nodePty.spawn(process.execPath, ['-e', childScript(options.holdMs)], {
name: 'xterm-256color',
cols: COLS,
rows: ROWS,
cwd: process.cwd(),
env: process.env as Record<string, string>,
...(options.useConptyDll ? { useConptyDll: true } : {})
})
// Preserve resize/output order for faithful Session.resize replay.
const events: Event[] = []
proc.onData((chunk) => {
events.push({ data: chunk })
})
const timers = (options.resizes ?? []).map((resize) =>
setTimeout(() => {
events.push({ resizeTo: resize.cols })
try {
proc.resize(resize.cols, ROWS)
} catch {
/* the child may already have exited */
}
}, resize.atMs)
)
await new Promise<void>((resolve) => {
proc.onExit(() => resolve())
})
for (const timer of timers) {
clearTimeout(timer)
}
return events
}
function replayIntoEmulator(events: Event[]): string[] {
const emulator = new HeadlessEmulator({ cols: COLS, rows: ROWS })
for (const event of events) {
if ('resizeTo' in event) {
// Session.resize sizes the emulator before the pty; keep that order.
emulator.resize(event.resizeTo, ROWS)
} else {
emulator.writeSync(event.data)
}
}
const terminal = (emulator as unknown as { terminal: Terminal }).terminal
const lines = readWrappedLineGlyphs(terminal).filter((line) => line.length > 0)
emulator.dispose()
return lines
}
/** Adjacent identical wide glyphs. Neither fixture contains any, so a hit is duplication. */
function expectFixtureLinesOnly(lines: string[]): void {
expect(lines.length).toBeGreaterThan(0)
expect(lines.filter((line) => line === KOREAN_GLYPHS)).toHaveLength(LINE_REPEATS)
// Latin is the control for script-specific corruption.
expect(lines.filter((line) => line === LATIN_GLYPHS)).toHaveLength(LINE_REPEATS)
expect(lines.filter((line) => line !== KOREAN_GLYPHS && line !== LATIN_GLYPHS)).toEqual([])
}
describe('pty repaint fidelity in the terminal buffer (#15192)', () => {
it('survives the resize the reporter used as a workaround, after output settles', async () => {
// Resize a quiet child so only ConPTY repaint can change the settled text.
const events = await recordConpty({
useConptyDll: true,
holdMs: 900,
resizes: [
{ atMs: 450, cols: 31 },
{ atMs: 650, cols: 47 }
]
})
expectFixtureLinesOnly(replayIntoEmulator(events))
}, 60_000)
})
@@ -1,129 +0,0 @@
import { describe, expect, it } from 'vitest'
import type { Tab } from '../../shared/tab-types'
import type { WorkspaceSessionState } from '../../shared/workspace-session/workspace-session-state-types'
import { chatTabLayoutCommand, loadChatTabLayout } from './agent-session/agent-session-tab-layout'
const WT = 'repo-1::/tmp/worktree-a'
function chatTab(sessionId: string, id: string): Tab {
return {
id,
entityId: sessionId,
groupId: 'group-1',
worktreeId: WT,
contentType: 'agent-session',
label: 'Chat',
customLabel: null,
color: null,
sortOrder: 0,
createdAt: 1
}
}
function session(tabs: Tab[], activeGroupId?: string): WorkspaceSessionState {
return {
activeRepoId: 'repo-1',
activeWorktreeId: WT,
activeTabId: null,
tabsByWorktree: {},
terminalLayoutsByTabId: {},
unifiedTabs: { [WT]: tabs },
tabGroups: {
[WT]: [{ id: 'group-1', worktreeId: WT, activeTabId: null, tabOrder: tabs.map((t) => t.id) }]
},
...(activeGroupId ? { activeGroupIdByWorktree: { [WT]: activeGroupId } } : {})
}
}
function commandOn(
stored: WorkspaceSessionState,
change: Parameters<typeof chatTabLayoutCommand>[3]
) {
const { loaded, activeGroupId } = loadChatTabLayout('local', stored, WT)
return chatTabLayoutCommand(loaded.layout.workspaces[WT], activeGroupId, WT, change)
}
const open = (sessionId: string) => ({
open: { sessionId, agent: 'codex' as const, tabId: `agent-session:${sessionId}` }
})
describe("a host's chat tab change as main's layout command", () => {
it('names no group the workspace no longer holds, nor one with no layout loaded', () => {
expect(commandOn(session([chatTab('a', 'saved-tab')], 'gone'), open('b'))).toEqual(
expect.not.objectContaining({ groupId: expect.anything() })
)
expect(chatTabLayoutCommand(undefined, 'group-1', WT, open('b'))).toEqual(
expect.not.objectContaining({ groupId: expect.anything() })
)
})
describe("a paired client's chosen place (L270)", () => {
function twoGroups(): WorkspaceSessionState {
const tabs = [
chatTab('a', 'tab-a'),
chatTab('c', 'tab-c'),
{ ...chatTab('d', 'tab-d'), groupId: 'group-2' }
]
return {
...session(tabs, 'group-1'),
tabGroups: {
[WT]: [
{ id: 'group-1', worktreeId: WT, activeTabId: null, tabOrder: ['tab-a', 'tab-c'] },
{ id: 'group-2', worktreeId: WT, activeTabId: null, tabOrder: ['tab-d'] }
]
}
}
}
const placed = (placement: { groupId?: string; afterTabId?: string }) => ({
open: { ...open('b').open, placement }
})
it('opens the chat in the named group, after the named tab', () => {
expect(commandOn(twoGroups(), placed({ groupId: 'group-1', afterTabId: 'tab-a' }))).toEqual({
type: 'openAgentSessionTab',
workspace: WT,
tabId: 'agent-session:b',
sessionId: 'b',
agent: 'codex',
groupId: 'group-1',
afterTabId: 'tab-a'
})
})
it('prefers the named group to the remembered active one', () => {
expect(commandOn(twoGroups(), placed({ groupId: 'group-2' }))).toMatchObject({
groupId: 'group-2'
})
})
it('falls back to the active group for a group the workspace no longer holds', () => {
expect(
commandOn(twoGroups(), placed({ groupId: 'gone', afterTabId: 'tab-a' }))
).toMatchObject({
groupId: 'group-1',
afterTabId: 'tab-a'
})
})
it('drops an anchor tab that is not in the chosen group', () => {
expect(commandOn(twoGroups(), placed({ groupId: 'group-2', afterTabId: 'tab-a' }))).toEqual(
expect.not.objectContaining({ afterTabId: expect.anything() })
)
})
})
it('opens nothing for a chat a tab already shows', () => {
expect(commandOn(session([chatTab('a', 'saved-tab')], 'group-1'), open('a'))).toBeNull()
})
it('closes the tab that shows the chat, under its saved id, forced', () => {
expect(
commandOn(session([chatTab('a', 'saved-tab')]), { close: 'a', tabId: 'saved-tab' })
).toEqual({
type: 'closeTabs',
workspace: WT,
tabIds: ['saved-tab'],
force: true
})
})
})
@@ -23,19 +23,6 @@ describe('browser execution host key resolution', () => {
).toMatchObject({ kind: 'native', runtimeId: runtime.getRuntimeId() })
})
it('holds a resolvable workspace whose execution host cannot be minted yet', async () => {
const runtime = createRuntime()
Object.defineProperty(runtime, 'resolveBrowserNetworkExecutionHostForWorktree', {
value: async () => {
throw new Error('Host provider is not ready')
}
})
await expect(runtime.resolveBrowserExecutionHostKeyForWorkspace(WORKSPACE_ID)).resolves.toEqual(
{ status: 'unavailable' }
)
})
it('settles a workspace that no longer resolves', async () => {
const runtime = createRuntime()
@@ -1,63 +0,0 @@
import { readFileSync } from 'node:fs'
import { join } from 'node:path'
import { describe, expect, it } from 'vitest'
import { createDraftPasteReadyScanner } from '../../shared/draft-paste-ready-scanner'
import {
getAgentLabel,
isGeminiTerminalTitle,
resolveExplicitTerminalTitleAgentType
} from '../../shared/agent-status/title/terminal-title-agent-type'
// Raw dsh-tui 0.10.2 / @deepseek-ai/dsh 0.1.5-rc.1 capture; agent-pty-transcript-capture.md.
const TRANSCRIPT = readFileSync(join(__dirname, '__fixtures__', 'dsh-tui-ready-no-key.txt'), 'utf8')
const ESC = String.fromCharCode(27)
const BEL = String.fromCharCode(7)
function readFirstOscTitle(data: string): string {
const open = data.indexOf(`${ESC}]0;`)
const bodyStart = open === -1 ? -1 : open + 4
const terminator = bodyStart === -1 ? -1 : data.indexOf(BEL, bodyStart)
if (terminator === -1) {
throw new Error('the captured transcript carries no BEL-terminated OSC 0 title')
}
return data.slice(bodyStart, terminator)
}
describe('DSH-TUI readiness from captured terminal bytes', () => {
it('fires the composer-ready signal well before the transcript ends', () => {
const scanner = createDraftPasteReadyScanner('dsh-composer-prompt')
let readyAt = -1
for (let offset = 0; offset < TRANSCRIPT.length; offset += 1024) {
const chunk = TRANSCRIPT.slice(offset, offset + 1024)
if (scanner.observe(chunk).ready && readyAt === -1) {
readyAt = offset + chunk.length
}
}
// Composer arrives near byte 5910; do not wait for the remaining ~70KB whale animation.
expect(readyAt).toBeGreaterThan(0)
expect(readyAt).toBeLessThan(8192)
})
it('arms the quiet-window fallback from DECSET 2004 as well', () => {
const scanner = createDraftPasteReadyScanner('dsh-composer-prompt')
expect(scanner.observe(TRANSCRIPT.slice(0, 40)).armQuietTimer).toBe(true)
})
it('identifies the pane from DSH’s own title, not Gemini’s', () => {
const title = readFirstOscTitle(TRANSCRIPT)
expect(title).toContain('✦')
expect(title).toContain('\u{1F40B}')
expect(isGeminiTerminalTitle(title)).toBe(false)
expect(getAgentLabel(title)).toBe('DeepSeek Harness')
expect(resolveExplicitTerminalTitleAgentType(title)).toBe('dsh')
})
it('keeps a working DSH title out of Claude’s braille-spinner lane', () => {
// DSH working braille overlaps Claude's spinner glyphs.
for (const frame of ['⠂', '⠐']) {
const working = `${frame} \u{1F40B} fix the flaky test`
expect(getAgentLabel(working)).toBe('DeepSeek Harness')
}
})
})
@@ -1,43 +0,0 @@
import { describe, expect, it } from 'vitest'
import { OrcaRuntimeService } from '../orca-runtime-test-mocks.spec'
import {
HEADLESS_LEAF_ID,
HEADLESS_SECOND_LEAF_ID,
TEST_WORKTREE_ID,
makeHeadlessTerminalLayout,
makeRuntimeStoreWithWorkspaceSession,
makeWorkspaceSessionWithHeadlessTerminal
} from '../orca-runtime-test-fixtures.spec'
import { planDuplicatePtyPaneRepair } from '../../persistence/terminal-topology/duplicate-pty-pane-repair'
import type { WorkspaceSessionState } from '../../../shared/workspace-session/workspace-session-state-types'
// Windowless listing reads stored layout; duplicate PTY bindings need durable repair to avoid duplicate tabs.
const duplicate = makeWorkspaceSessionWithHeadlessTerminal({
terminalLayoutsByTabId: {
'host-tab': makeHeadlessTerminalLayout({
[HEADLESS_LEAF_ID]: 'persisted-pty',
[HEADLESS_SECOND_LEAF_ID]: 'persisted-pty'
})
}
})
async function terminalRows(session: WorkspaceSessionState): Promise<number> {
const { runtimeStore } = makeRuntimeStoreWithWorkspaceSession(session)
const runtime = new OrcaRuntimeService(runtimeStore)
runtime.setPtyController({
write: () => true,
kill: () => true,
getForegroundProcess: async () => null,
listProcesses: async () => []
})
const snapshots = await runtime.listAllMobileSessionTabs()
return (snapshots.find((snapshot) => snapshot.worktree === TEST_WORKTREE_ID)?.tabs ?? []).filter(
(tab) => tab.type === 'terminal'
).length
}
describe('windowless mobile list of a terminal stored in two panes', () => {
it('lists one row once main has repaired the stored tree', async () => {
await expect(terminalRows(planDuplicatePtyPaneRepair(duplicate)!.session)).resolves.toBe(1)
})
})
@@ -1,100 +0,0 @@
import { describe, expect, it, vi } from 'vitest'
import { OrcaRuntimeService } from '../orca-runtime-test-mocks.spec'
import type { WorkspaceSessionState } from '../../../shared/workspace-session/workspace-session-state-types'
import {
HEADLESS_LEAF_ID,
HEADLESS_SECOND_LEAF_ID,
TEST_WORKTREE_ID,
makeHeadlessTerminalLayout,
makeRuntimeStoreWithWorkspaceSession,
makeWorkspaceSessionWithHeadlessTerminal
} from '../orca-runtime-test-fixtures.spec'
// Model a moved/restored tab with an agent first pane, shell sibling and no live terminals; launchAgent applies tab-wide.
function splitAgentTabSession(withRecord: boolean): WorkspaceSessionState {
const base = makeWorkspaceSessionWithHeadlessTerminal()
const tab = base.tabsByWorktree[TEST_WORKTREE_ID]?.[0]
return {
...base,
tabsByWorktree: {
[TEST_WORKTREE_ID]: tab ? [{ ...tab, ptyId: null, launchAgent: 'codex' }] : []
},
terminalLayoutsByTabId: {
'host-tab': makeHeadlessTerminalLayout({
[HEADLESS_LEAF_ID]: undefined,
[HEADLESS_SECOND_LEAF_ID]: undefined
})
},
sleepingAgentSessionsByPaneKey: withRecord
? {
[`host-tab:${HEADLESS_LEAF_ID}`]: {
paneKey: `host-tab:${HEADLESS_LEAF_ID}`,
tabId: 'host-tab',
worktreeId: TEST_WORKTREE_ID,
agent: 'codex',
providerSession: { key: 'session_id', id: 'codex-session-1' },
prompt: 'do the thing',
state: 'done',
capturedAt: 1,
updatedAt: 1,
origin: 'worktree-sleep'
}
}
: {}
}
}
function startRuntime(session: WorkspaceSessionState): {
runtime: OrcaRuntimeService
spawn: ReturnType<typeof vi.fn>
} {
const { runtimeStore } = makeRuntimeStoreWithWorkspaceSession(session)
const spawn = vi.fn().mockResolvedValue({ id: 'started-pty' })
// oxlint-disable-next-line typescript/consistent-type-assertions -- SAFETY: the fixture store implements the runtime store methods these tests reach.
const runtime = new OrcaRuntimeService(runtimeStore as never)
runtime.setPtyController({
spawn,
write: () => true,
kill: () => true,
getForegroundProcess: async () => null,
listProcesses: async () => []
})
runtime.syncWindowGraph(0, { tabs: [], leaves: [] })
return { runtime, spawn }
}
function spawnCommand(spawn: ReturnType<typeof vi.fn>): string {
const args: unknown = spawn.mock.calls[0]?.[0]
return typeof args === 'object' && args !== null && 'command' in args
? String(args.command ?? '')
: ''
}
const activate = (runtime: OrcaRuntimeService, leafId: string) =>
runtime.activateMobileSessionTab(`id:${TEST_WORKTREE_ID}`, 'host-tab', leafId, {
notifyClients: false,
navigation: 'caller',
intent: 'user'
})
describe('OrcaRuntimeService', () => {
describe('a split agent tab starting with no live terminal', () => {
it('starts the split sibling as a plain shell, not as a fresh agent', async () => {
const { runtime, spawn } = startRuntime(splitAgentTabSession(true))
await activate(runtime, HEADLESS_SECOND_LEAF_ID)
expect(spawn).toHaveBeenCalledOnce()
expect(spawnCommand(spawn)).not.toContain('codex')
expect(spawn.mock.calls[0]?.[0]).not.toMatchObject({ launchAgent: 'codex' })
})
it('still resumes the agent in the pane that recorded it', async () => {
const { runtime, spawn } = startRuntime(splitAgentTabSession(true))
await activate(runtime, HEADLESS_LEAF_ID)
expect(spawnCommand(spawn)).toContain('codex-session-1')
})
})
})
@@ -1,111 +0,0 @@
import { describe, expect, it, vi } from 'vitest'
import { getDefaultWorkspaceSession } from '../../../shared/constants'
import { WorktreeCreate } from '../../../shared/rpc-contract/worktree-create-params'
import {
buildRuntimeWorktreeCreateParams,
type WorktreeCreateRequest
} from '../../../renderer/src/store/slices/worktrees/create/worktree-create-payload'
import { buildManagedWorktreeCreateArgs } from '../rpc/methods/worktree-create-args'
import {
OrcaRuntimeService,
computeWorktreePathMock,
createSetupRunnerScript,
ensurePathWithinWorkspaceMock,
getDefaultTabsLaunch,
getEffectiveHooks,
listWorktrees
} from '../orca-runtime-test-mocks.spec'
import {
makeRuntimeStoreWithWorkspaceSession,
spawnedPtyEnv,
store
} from '../orca-runtime-test-fixtures.spec'
const WORKTREE_PATH = '/tmp/workspaces/native-chat-setup'
const RUNNER = '/tmp/repo/.git/orca/setup-runner.sh'
const SETUP_ENV = { ORCA_WORKTREE_PATH: WORKTREE_PATH, SETUP_INPUT: 'preserved' }
async function createFromRendererPayload(options: {
agent?: WorktreeCreateRequest['createdWithAgent']
mode?: 'new-tab' | 'split-horizontal' | 'split-vertical'
activate?: boolean
runHooks?: boolean
withSetup?: boolean
defaultTabs?: boolean
startup?: boolean
}) {
const { runtimeStore } = makeRuntimeStoreWithWorkspaceSession(getDefaultWorkspaceSession())
const runtime = new OrcaRuntimeService({
...runtimeStore,
getSettings: () => ({
...store.getSettings(),
...(options.mode ? { setupScriptLaunchMode: options.mode } : {})
})
})
const spawn = vi.fn(async (_args: { command?: string; env?: Record<string, string> }) => ({
id: `pty-native-chat-${spawn.mock.calls.length}`
}))
runtime.setPtyController({
spawn,
write: () => true,
kill: () => true,
getForegroundProcess: async () => null
})
computeWorktreePathMock.mockReturnValue(WORKTREE_PATH)
ensurePathWithinWorkspaceMock.mockReturnValue(WORKTREE_PATH)
vi.mocked(listWorktrees).mockResolvedValue([
{
path: WORKTREE_PATH,
head: 'def',
branch: 'native-chat-setup',
isBare: false,
isMainWorktree: false
}
])
vi.mocked(getEffectiveHooks).mockReturnValue({ scripts: { setup: 'pnpm install' } })
vi.mocked(createSetupRunnerScript).mockResolvedValue({
runnerScriptPath: RUNNER,
envVars: SETUP_ENV
})
if (options.defaultTabs) {
vi.mocked(getDefaultTabsLaunch).mockReturnValue({
runCommands: true,
tabs: [{ title: 'Dev', command: 'pnpm dev' }]
})
}
const params = WorktreeCreate.parse({
...buildRuntimeWorktreeCreateParams(
{
repoId: 'repo-1',
name: 'native-chat-setup',
setupDecision: options.withSetup === false ? 'skip' : 'run',
...(options.agent ? { createdWithAgent: options.agent } : {}),
...(options.startup ? { startup: { command: 'codex' } } : {})
},
{ name: 'native-chat-setup' }
),
...(options.activate ? { activate: true } : {}),
...(options.runHooks ? { runHooks: true } : {})
})
const args = buildManagedWorktreeCreateArgs(params, {}, { clientKind: 'runtime' })
expect(args.createdWithAgent).toBe(options.agent)
expect(args.startup?.command).toBe(options.startup ? 'codex' : undefined)
expect(args.activate).toBe(options.activate === true || options.startup === true)
expect(args.runHooks).toBe(options.runHooks === true)
const result = await runtime.createManagedWorktree({ ...args, awaitTerminalProvisioning: true })
return { result, spawn }
}
describe('native-chat setup through renderer payload, RPC and execution host', () => {
it.each(['split-horizontal', 'split-vertical'] as const)(
'gives the requested startup and Setup separate tabs despite retired location %s',
async (mode) => {
const { spawn } = await createFromRendererPayload({ agent: 'codex', mode, startup: true })
expect(spawn.mock.calls.map(([args]) => args.command)).toEqual(['codex', `bash ${RUNNER}`])
const startupTabId = spawnedPtyEnv(spawn.mock.calls[0][0]).ORCA_TAB_ID
expect(startupTabId).toEqual(expect.any(String))
expect(startupTabId).not.toBe('')
expect(startupTabId).not.toBe(spawnedPtyEnv(spawn.mock.calls[1][0]).ORCA_TAB_ID)
}
)
})
-3
View File
@@ -91,9 +91,7 @@ await import('./orca-runtime-tests/mobile-session-tabs-part-13.spec')
await import('./orca-runtime-tests/mobile-session-tabs-part-14.spec')
await import('./orca-runtime-tests/mobile-session-tabs-cold-serve-hydrate.spec')
await import('./orca-runtime-tests/mobile-session-tabs-empty-headless-snapshot.spec')
await import('./orca-runtime-tests/mobile-session-tabs-duplicate-pty-repair.spec')
await import('./orca-runtime-tests/mobile-session-tabs-headless-chat-rebuild.spec')
await import('./orca-runtime-tests/mobile-session-tabs-split-agent-tab.spec')
await import('./orca-runtime-tests/mobile-session-tabs-windowless-sleep-resume.spec')
await import('./orca-runtime-tests/mobile-session-tabs-targeted-agent-resume.spec')
await import('./orca-runtime-tests/mobile-session-tabs-headless-wake-report.spec')
@@ -122,7 +120,6 @@ await import('./orca-runtime-tests/worktree-setup-and-startup-part-02.spec')
await import('./orca-runtime-tests/worktree-setup-and-startup-part-03.spec')
await import('./orca-runtime-tests/worktree-setup-and-startup-part-04.spec')
await import('./orca-runtime-tests/worktree-setup-single-creator.spec')
await import('./orca-runtime-tests/worktree-native-chat-setup.spec')
await import('./orca-runtime-tests/gitlab-and-pr-bases.spec')
await import('./orca-runtime-tests/gitlab-and-pr-bases-part-02.spec')
await import('./orca-runtime-tests/gitlab-and-pr-bases-part-03.spec')
@@ -1,94 +0,0 @@
// Use real Git to verify admin fingerprints, including Windows path/CRLF/move/lock semantics that Linux mocks cannot cover.
import { execFile } from 'node:child_process'
import { mkdir, mkdtemp, realpath, writeFile } from 'node:fs/promises'
import { removeTree } from '../../shared/windows-transient-lock-removal'
import { tmpdir } from 'node:os'
import { join } from 'node:path'
import { promisify } from 'node:util'
import { afterEach, beforeEach, describe, expect, it } from 'vitest'
import { readRepoWorktreeAdminFingerprint } from './repo-worktree-admin-fingerprint'
const execFileAsync = promisify(execFile)
let scratchDir = ''
let repoPath = ''
let worktreePath = ''
async function git(args: string[], cwd: string): Promise<string> {
const { stdout } = await execFileAsync('git', args, { cwd })
return stdout
}
async function fingerprint(path = repoPath): Promise<string | null> {
return await readRepoWorktreeAdminFingerprint(path)
}
beforeEach(async () => {
// realpath: macOS hands out /var/... temp paths while Git records /private/var/... in `gitdir`.
scratchDir = await realpath(await mkdtemp(join(tmpdir(), 'orca-worktree-fingerprint-')))
repoPath = join(scratchDir, 'repo')
worktreePath = join(scratchDir, 'trees', 'feature')
await mkdir(repoPath, { recursive: true })
await mkdir(join(scratchDir, 'trees'), { recursive: true })
await git(['init', '-q'], repoPath)
await git(['config', 'user.email', 'fingerprint@example.invalid'], repoPath)
await git(['config', 'user.name', 'Fingerprint'], repoPath)
await writeFile(join(repoPath, 'seed.txt'), 'seed\n')
await git(['add', '-A'], repoPath)
await git(['commit', '-qm', 'seed'], repoPath)
await git(['worktree', 'add', '-q', worktreePath, '-b', 'feature'], repoPath)
})
afterEach(async () => {
await removeTree(scratchDir)
})
describe('readRepoWorktreeAdminFingerprint', () => {
it('changes when a worktree directory is deleted outside Git', async () => {
// The admin dir is untouched by `rm -rf`, but the row's `prunable` flag flips.
const before = await fingerprint()
await removeTree(worktreePath)
expect(await fingerprint()).not.toBe(before)
})
it('changes when an existing worktree lock is replaced', async () => {
await git(
['worktree', 'lock', '--reason', 'orca-create-preparation:v1:12345:lease', worktreePath],
repoPath
)
const before = await fingerprint()
const gitDir = (await git(['rev-parse', '--absolute-git-dir'], worktreePath)).trim()
await writeFile(join(gitDir, 'locked'), 'different user-owned lock\n')
expect(await fingerprint()).not.toBe(before)
})
it('changes when a linked worktree records a commit', async () => {
// Committing rewrites refs/heads/feature and leaves HEAD alone, but moves the reported HEAD oid.
const before = await fingerprint()
await writeFile(join(worktreePath, 'work.txt'), 'work\n')
await git(['add', '-A'], worktreePath)
await git(['commit', '-qm', 'work'], worktreePath)
expect(await fingerprint()).not.toBe(before)
})
it('still tracks a tip whose loose ref has been packed away', async () => {
await git(['pack-refs', '--all'], repoPath)
const before = await fingerprint()
await writeFile(join(worktreePath, 'packed.txt'), 'packed\n')
await git(['add', '-A'], worktreePath)
await git(['commit', '-qm', 'packed'], worktreePath)
expect(await fingerprint()).not.toBe(before)
})
it('reads a bare repo through its own gitdir', async () => {
const barePath = join(scratchDir, 'bare.git')
await git(['clone', '-q', '--bare', repoPath, barePath], scratchDir)
const before = await fingerprint(barePath)
expect(before).not.toBeNull()
await git(
['worktree', 'add', '-q', join(scratchDir, 'trees', 'from-bare'), '-b', 'bare-tree'],
barePath
)
expect(await fingerprint(barePath)).not.toBe(before)
})
})
+2 -76
View File
@@ -1,14 +1,10 @@
import { describe, expect, it, vi } from 'vitest'
import { describe, expect, it } from 'vitest'
import nacl from 'tweetnacl'
import {
generateKeyPair,
deriveSharedKey,
encrypt,
decrypt,
encryptBytes,
decryptBytes,
MAX_E2EE_ENCRYPTED_BASE64_CHARACTERS,
publicKeyFromBase64
decryptBytes
} from '../../../shared/remote-runtime/e2ee/node/e2ee-crypto'
import { MOBILE_E2EE_LEGACY_FIXTURE } from '../../../shared/remote-runtime/e2ee/mobile-e2ee-legacy-fixtures'
@@ -28,78 +24,8 @@ describe('e2ee-crypto', () => {
)
})
it('decrypt returns null with wrong key', () => {
const server = generateKeyPair()
const client = generateKeyPair()
const attacker = generateKeyPair()
const clientShared = deriveSharedKey(client.secretKey, server.publicKey)
const attackerShared = deriveSharedKey(attacker.secretKey, server.publicKey)
const encrypted = encrypt('secret data', clientShared)
expect(decrypt(encrypted, attackerShared)).toBeNull()
})
it('each encryption produces unique ciphertext (random nonce)', () => {
const server = generateKeyPair()
const client = generateKeyPair()
const shared = deriveSharedKey(client.secretKey, server.publicKey)
const message = 'same message'
const a = encrypt(message, shared)
const b = encrypt(message, shared)
expect(a).not.toBe(b)
})
it('handles unicode content', () => {
const server = generateKeyPair()
const client = generateKeyPair()
const shared = deriveSharedKey(client.secretKey, server.publicKey)
const message = '日本語テスト 🎉 émojis'
const encrypted = encrypt(message, shared)
expect(decrypt(encrypted, shared)).toBe(message)
})
it('decrypt returns null for truncated data', () => {
const shared = deriveSharedKey(generateKeyPair().secretKey, generateKeyPair().publicKey)
expect(decrypt('dG9vc2hvcnQ=', shared)).toBeNull()
})
it('rejects oversized encoded inputs before base64 decoding', () => {
const shared = deriveSharedKey(generateKeyPair().secretKey, generateKeyPair().publicKey)
const decode = vi.spyOn(Buffer, 'from')
expect(() => publicKeyFromBase64('A'.repeat(45))).toThrow('encoded value is too large')
expect(decrypt('A'.repeat(MAX_E2EE_ENCRYPTED_BASE64_CHARACTERS + 1), shared)).toBeNull()
expect(decode).not.toHaveBeenCalled()
decode.mockRestore()
})
it('decrypt returns null for tampered data', () => {
const server = generateKeyPair()
const client = generateKeyPair()
const shared = deriveSharedKey(client.secretKey, server.publicKey)
const encrypted = encrypt('hello', shared)
const bytes = Buffer.from(encrypted, 'base64')
bytes[bytes.length - 1] ^= 0xff
const tampered = bytes.toString('base64')
expect(decrypt(tampered, shared)).toBeNull()
})
it('encrypt/decrypt round-trips raw bytes for binary terminal frames', () => {
const server = generateKeyPair()
const client = generateKeyPair()
const serverShared = deriveSharedKey(server.secretKey, client.publicKey)
const clientShared = deriveSharedKey(client.secretKey, server.publicKey)
const payload = new Uint8Array([0, 1, 2, 127, 128, 255])
const encrypted = encryptBytes(payload, clientShared)
const decrypted = decryptBytes(encrypted, serverShared)
expect(decrypted).toEqual(payload)
})
})
@@ -1,43 +0,0 @@
import { describe, expect, it } from 'vitest'
import { getTerminalPasteIngestMs } from '../../../../../../shared/agent-launch/startup/agent-prompt-injection'
import { ORCHESTRATION_WORKER_START_CLIENT_GRACE_MS } from '../../../../../../shared/orchestration-timing-budgets'
import {
isWorkerStartTaskSpecTooLarge,
ORCHESTRATION_WORKER_START_PROMPT_MAX_BYTES,
ORCHESTRATION_WORKER_START_TASK_SPEC_MAX_BYTES
} from '../../../../../../shared/orchestration-worker-start-prompt-budget'
import { getTerminalInputByteLength } from '../../../../../../shared/terminal/input/terminal-input'
import { buildDispatchPreamble } from '../../../../orchestration/preamble'
describe('worker-start prompt budget', () => {
it('refuses an 8 MiB Task spec whose fake-Windows ingest outlives RPC grace', async () => {
const spec = 'x'.repeat(8 * 1024 * 1024)
const prompt = buildDispatchPreamble({
taskId: 'task_test',
dispatchId: 'ctx_test',
taskSpec: spec,
coordinatorHandle: 'term_coordinator',
workerHandle: 'term_worker'
})
expect(getTerminalPasteIngestMs('win32', getTerminalInputByteLength(prompt))).toBeGreaterThan(
ORCHESTRATION_WORKER_START_CLIENT_GRACE_MS
)
await expect(isWorkerStartTaskSpecTooLarge(spec)).resolves.toBe(true)
})
it('keeps a maximum legal composition under the derived full-prompt ceiling', () => {
const prompt = buildDispatchPreamble({
taskId: `task_${'a'.repeat(32)}`,
dispatchId: `ctx_${'b'.repeat(32)}`,
taskSpec: 'x'.repeat(ORCHESTRATION_WORKER_START_TASK_SPEC_MAX_BYTES),
coordinatorHandle: `term_${'d'.repeat(256)}`,
workerHandle: `term_${'e'.repeat(256)}`,
canDispatchSubWorkers: true
})
expect(getTerminalInputByteLength(prompt)).toBeLessThanOrEqual(
ORCHESTRATION_WORKER_START_PROMPT_MAX_BYTES
)
})
})
@@ -1,37 +1,10 @@
import { gunzipSync } from 'node:zlib'
import { describe, expect, it, vi } from 'vitest'
import {
COMPRESSED_FRAMES_CLIENT_CAPABILITY,
readCompressedFrame
} from '../../../shared/remote-runtime/e2ee/paired-e2ee-compressed-frame'
import { COMPRESSED_FRAMES_CLIENT_CAPABILITY } from '../../../shared/remote-runtime/e2ee/paired-e2ee-compressed-frame'
import { compressMobileE2EEV2Item } from './mobile-e2ee-v2-compressed-frame'
const capable = [COMPRESSED_FRAMES_CLIENT_CAPABILITY]
function inflate(item: ReturnType<typeof compressMobileE2EEV2Item>) {
const frame = item.kind === 'binary' ? readCompressedFrame(item.plaintext) : null
return frame && { payloadKind: frame.payloadKind, bytes: gunzipSync(frame.gzipped) }
}
describe('compressMobileE2EEV2Item', () => {
it('sends large text to a capable phone as a compressed frame that inflates to the same text', () => {
const plaintext = JSON.stringify({ rows: Array.from({ length: 200 }, (_, i) => `ü-row-${i}`) })
const inflated = inflate(compressMobileE2EEV2Item({ kind: 'text', plaintext }, capable))
expect(inflated?.payloadKind).toBe('text')
expect(inflated?.bytes.toString('utf8')).toBe(plaintext)
})
it('compresses binary only when its sender marks it compressible', () => {
const terminal = { kind: 'binary' as const, plaintext: new Uint8Array(4096).fill(0x74) }
expect(compressMobileE2EEV2Item(terminal, capable)).toBe(terminal)
const inflated = inflate(compressMobileE2EEV2Item(terminal, capable, { compressible: true }))
expect(inflated?.payloadKind).toBe('binary')
expect(new Uint8Array(inflated!.bytes)).toEqual(terminal.plaintext)
})
it('leaves frames alone for phones without the capability and for small frames', () => {
const large = { kind: 'text' as const, plaintext: 'x'.repeat(4096) }
const small = { kind: 'text' as const, plaintext: '{"id":"1"}' }
@@ -1,89 +0,0 @@
import { afterEach, describe, expect, it, vi } from 'vitest'
import {
OrcaRuntimeService,
listWorktrees,
listWorktreesStrict,
listWorktreesSharedStrict
} from './orca-runtime-test-mocks.spec'
import { store, TEST_WORKTREE_ID } from './orca-runtime-test-fixtures.spec'
import { parseWorkspaceReferenceUrl } from '../../shared/workspace-attachments/workspace-reference-identity'
vi.mock('./agent-session/records/agent-session-record-store-slot', () => ({
peekOpenedAgentSessionRecordStore: () => null,
openAgentSessionRecordStoreOnce: async () => ({
store: { listConversations: () => [], getConversation: () => null }
})
}))
const linear = 'https://linear.app/acme/issue/STA-1234'
afterEach(() => vi.restoreAllMocks())
function setup(Runtime: typeof OrcaRuntimeService = OrcaRuntimeService) {
const base = store.getAllWorktreeMeta()[TEST_WORKTREE_ID]
const linkedItems = [parseWorkspaceReferenceUrl(linear)]
const runtimeStore = {
...store,
getAllWorktreeMeta: () => ({
[TEST_WORKTREE_ID]: { ...base, linkedItems },
'repo-2::/elsewhere': { ...base, displayName: 'other', linkedItems }
}),
getRepos: () => [
...store.getRepos(),
{ id: 'repo-2', path: '/elsewhere', displayName: 'other', badgeColor: '', addedAt: 0 }
]
}
vi.mocked(listWorktrees).mockClear()
vi.mocked(listWorktreesStrict).mockClear()
vi.mocked(listWorktreesSharedStrict).mockClear()
return new Runtime(runtimeStore)
}
describe('reference find runtime integration', () => {
it('searches cold saved metadata across repos without a Git scan', async () => {
const runtime = setup()
const result = await runtime.findWorkspaceReferences({ query: 'STA-1234' })
expect(result.matches.map(({ workspace }) => workspace.id)).toEqual([
TEST_WORKTREE_ID,
'repo-2::/elsewhere'
])
expect(listWorktrees).not.toHaveBeenCalled()
expect(listWorktreesStrict).not.toHaveBeenCalled()
expect(listWorktreesSharedStrict).not.toHaveBeenCalled()
})
it('filters explicit repo and workspace selectors without populating the Git cache', async () => {
const runtime = setup()
expect(
(await runtime.findWorkspaceReferences({ query: linear, repo: 'id:repo-2' })).matches.map(
({ workspace }) => workspace.id
)
).toEqual(['repo-2::/elsewhere'])
expect(
(await runtime.findWorkspaceReferences({ query: linear, worktree: 'name:foo' })).matches.map(
({ workspace }) => workspace.id
)
).toEqual([TEST_WORKTREE_ID])
expect(listWorktrees).not.toHaveBeenCalled()
expect(listWorktreesStrict).not.toHaveBeenCalled()
expect(listWorktreesSharedStrict).not.toHaveBeenCalled()
})
it('warms the worktree cache when a branch selector misses cold metadata', async () => {
const discovered = {
path: '/tmp/worktree-a',
head: 'abc',
branch: 'refs/heads/feat',
isBare: false,
isMainWorktree: false
}
class ScannedRuntime extends OrcaRuntimeService {
protected override async listRepoWorktreesForResolution(repo: { id: string }) {
return { ok: true as const, worktrees: repo.id === 'repo-2' ? [] : [discovered] }
}
}
const runtime = setup(ScannedRuntime)
const result = await runtime.findWorkspaceReferences({ query: linear, worktree: 'branch:feat' })
expect(result.matches.map(({ workspace }) => workspace.id)).toEqual([TEST_WORKTREE_ID])
})
})
@@ -76,14 +76,6 @@ export function registerRuntimeRemoveProjectHostScopeTests(): void {
}
describe('repo.rm with the same repo id on two execution hosts', () => {
it('removes only the resolved row when a path selector picks the SSH host copy', async () => {
const { runtime, repos } = createRuntime()
await runtime.removeProject('path:/remote/dup')
expect(repos.map((repo) => repo.path)).toEqual(['/laptop/dup'])
})
it('refuses a bare duplicated id rather than guessing a host', async () => {
const { runtime, repos, removeProjectForHost } = createRuntime()
@@ -118,24 +110,4 @@ export function registerRuntimeRemoveProjectHostScopeTests(): void {
expect(repos.map((repo) => repo.path)).toEqual(['/laptop/dup'])
})
})
describe('projectGroup.moveProject with the same repo id on two execution hosts', () => {
it('moves the SSH row a paired client names by its connection', async () => {
const { runtime, repos } = createRuntime()
await runtime.moveProjectToGroup('dup', 'group-1', undefined, 'ssh-1')
expect(repos.map((repo) => repo.projectGroupId)).toEqual([undefined, 'group-1'])
})
})
describe('repo.update with the same repo id on two execution hosts', () => {
it('writes the SSH row a path selector picked, not its local sibling', async () => {
const { runtime, repos } = createRuntime()
await runtime.updateRepo('path:/remote/dup', { displayName: 'Renamed Remote' })
expect(repos.map((repo) => repo.displayName)).toEqual(['Dup Local', 'Renamed Remote'])
})
})
}
@@ -1,59 +1,5 @@
import { expect, it, vi } from 'vitest'
import { createTranscriptPane, TRANSCRIPT_PANE_PTY_ID } from './agent-transcript-pane-test-harness'
import { registerTerminalPtyExitWaiterTests } from './terminal-pty-exit-waiter.spec'
import { registerClaudeAgentTeamsPtyExitLeakTests } from './claude-agent-teams-pty-exit-leak.spec'
registerTerminalPtyExitWaiterTests()
registerClaudeAgentTeamsPtyExitLeakTests()
it('reports an already-exited terminal from the idle wait command', async () => {
const originalExitCode = process.exitCode
const { runtime, handle } = await createTranscriptPane({
paneTitle: 'shell',
foregroundProcess: 'bash',
data: ''
})
runtime.onPtyExit(TRANSCRIPT_PANE_PTY_ID, 137, 'inc-1', { providerExitObserved: true })
const callMock = vi.fn(
async (method: string, params: { terminal: string; for: 'tui-idle'; timeoutMs: number }) => {
expect(method).toBe('terminal.wait')
return runtime.waitForTerminal(params.terminal, {
condition: params.for,
timeoutMs: params.timeoutMs
})
}
)
vi.doMock('../../cli/runtime-client', async (importOriginal) => ({
...(await importOriginal<Record<string, unknown>>()),
RuntimeClient: class {
call = callMock
}
}))
try {
process.exitCode = undefined
vi.stubEnv('ORCA_ENVIRONMENT', '')
const errorSpy = vi.spyOn(console, 'error').mockImplementation(() => {})
const cliPath = '../../cli/index'
const { main }: { main(argv: string[], cwd: string): Promise<void> } = await import(cliPath)
await main(
['terminal', 'wait', '--terminal', handle, '--for', 'tui-idle', '--timeout-ms', '25'],
'/tmp/repo'
)
expect(callMock).toHaveBeenCalledExactlyOnceWith(
'terminal.wait',
{ terminal: handle, for: 'tui-idle', timeoutMs: 25 },
{ timeoutMs: 5025 }
)
expect(process.exitCode).toBe(1)
expect(errorSpy.mock.calls.flat().join('\n')).toContain('terminal_exited')
expect(runtime['terminalWaiters'].forPty(TRANSCRIPT_PANE_PTY_ID)).toHaveLength(0)
} finally {
process.exitCode = originalExitCode
vi.doUnmock('../../cli/runtime-client')
vi.unstubAllEnvs()
vi.restoreAllMocks()
vi.resetModules()
}
})
@@ -1,92 +0,0 @@
import { expect, it, vi } from 'vitest'
import { identityFixture, LEAF, PANE } from '../query/terminal-identity.test-fixture'
import {
makeRuntimeStoreWithWorkspaceSession,
makeWorkspaceSessionWithHeadlessTerminal,
TEST_WORKTREE_ID
} from '../../orca-runtime-test-fixtures.spec'
import { recordPtySurface, forgetPtySurface } from '../../pty-recorded-surface-topology'
import type { TerminalLeafMoveResult } from '../../../../shared/workspace-layout/terminal-leaf-move'
it('retires before dependent callbacks, keeps connected output idle and preserves partial writes when a callback fails', () => {
const f = identityFixture()
const pty = f.owner.recordPtyWorktree('pty-owned', TEST_WORKTREE_ID)
const changes: (string | null | undefined)[] = []
const stop = f.owner.crewChanges.subscribe(() => changes.push(pty.paneKey))
recordPtySurface(f.owner.source, pty, 'tab-original', PANE, 3)
expect(changes).toEqual([PANE])
recordPtySurface(f.owner.source, pty, 'tab-original', PANE, 2)
expect(changes).toEqual([PANE])
f.owner.source.replaceLeaves(f.owner.leaves)
f.owner.source.bindPty('pty-owned', 'term_owned')
const before = changes.length
f.owner.source.bindPty('pty-owned', 'term_owned')
for (let i = 0; i < 30; i++) {
f.runtime.onPtyData('pty-owned', 'output', 500 + i)
}
expect(changes).toHaveLength(before)
const fail = f.owner.crewChanges.subscribe(() => {
throw new Error('consumer_failure')
})
expect(() => forgetPtySurface(f.owner.source, pty)).not.toThrow()
expect(pty.paneKey).toBeNull()
expect(changes.at(-1)).toBeNull()
fail()
expect(() => {
recordPtySurface(f.owner.source, pty, 'tab-original', PANE, 4)
throw new Error('later_operation_failure')
}).toThrow('later_operation_failure')
expect(changes.at(-1)).toBe(PANE)
stop()
})
it('wakes for preallocation before any leaf or incarnation and for a real pane move only after its awaited commit', async () => {
const harness = makeRuntimeStoreWithWorkspaceSession(makeWorkspaceSessionWithHeadlessTerminal())
const f = identityFixture(undefined, harness.runtimeStore)
const wake = vi.fn()
const stop = f.owner.crewChanges.subscribe(wake)
const old = f.runtime.createTerminalIdentityReadContext()
expect(f.owner.getLivePtyForHandle('absent', old)).toBeNull()
const handle = f.runtime.preAllocateHandleForPty('pty-owned')
expect(wake).toHaveBeenCalledTimes(1)
expect(f.owner.handleByPtyId.get('pty-owned')).toBe(handle)
expect(f.owner.leaves.size).toBe(0)
expect(f.owner.handleByPtyIncarnation.size).toBe(0)
f.runtime.onPtySpawned('pty-owned')
const pty = f.owner.recordPtyWorktree('pty-owned', TEST_WORKTREE_ID, { paneKey: PANE })
let finish: ((result: TerminalLeafMoveResult) => void) | undefined
const commit = vi.fn(
() =>
new Promise<TerminalLeafMoveResult>((resolve) => {
finish = resolve
})
)
const move = f.runtime.moveMobileSessionLeafToNewTab(
`id:${TEST_WORKTREE_ID}`,
{
sourceTabId: 'tab-original',
targetTabId: 'tab-moved',
leafId: LEAF
},
commit
)
expect(commit).toHaveBeenCalledOnce()
const before = wake.mock.calls.length
expect(pty.paneKey).toBe(PANE)
const warmed = f.runtime.createTerminalIdentityReadContext()
f.owner.getPtyRecordForPaneKey(`tab-moved:${LEAF}`, warmed)
if (!finish) {
throw new Error('Expected the production move to await commit')
}
finish({ status: 'moved', ptyId: 'pty-owned' })
await expect(move).resolves.toEqual({ status: 'moved', ptyId: 'pty-owned' })
expect(pty.paneKey).toBe(`tab-moved:${LEAF}`)
expect(wake.mock.calls.length).toBeGreaterThan(before)
expect(
f.owner.getPtyRecordForPaneKey(
`tab-moved:${LEAF}`,
f.runtime.createTerminalIdentityReadContext()
)
).toBe(f.owner.getPtyRecordForPaneKey(`tab-moved:${LEAF}`))
stop()
})
@@ -1,25 +1,8 @@
import { afterEach, describe, expect, it, vi } from 'vitest'
import type { PluginHookName } from '../../../../shared/plugins/contributions/plugin-hook-contribution'
import type { PluginHookChainOutcome } from '../../../../shared/plugins/workers/plugin-hook-chain'
import { setPluginHookRunner } from '../../../plugins/hooks/plugin-hook-dispatch'
import {
runTerminalSendPluginHook,
terminalHookOrderKind,
TerminalPluginHookOrder
} from './terminal-send-plugin-hook'
import { terminalHookOrderKind, TerminalPluginHookOrder } from './terminal-send-plugin-hook'
function installRunner(
decide: (payload: unknown) => PluginHookChainOutcome | Promise<PluginHookChainOutcome>
) {
const run = vi.fn(async (_event: PluginHookName, payload: unknown) => decide(payload))
setPluginHookRunner({ wants: (event) => event === 'terminal.send', run })
return run
}
const rewrite = (changes: Record<string, unknown>) => (payload: unknown) => ({
kind: 'continue' as const,
payload: { ...Object(payload), ...changes }
})
const allowed = vi.fn(async () => undefined)
afterEach(() => {
@@ -27,21 +10,6 @@ afterEach(() => {
allowed.mockClear()
})
describe('terminal.send hook site', () => {
it('refuses a rewrite the caller’s own checks would refuse, and skips them when unchanged', async () => {
installRunner(rewrite({ text: 'claude --resume owned-by-chat' }))
const refuse = vi.fn(async () => {
throw new Error('legacy writer refused')
})
await expect(runTerminalSendPluginHook('t', { text: 'ls' }, 'driving', refuse)).rejects.toThrow(
'legacy writer refused'
)
installRunner(rewrite({}))
await runTerminalSendPluginHook('t', { text: 'ls' }, 'driving', refuse)
expect(refuse).toHaveBeenCalledOnce()
})
})
describe('TerminalPluginHookOrder', () => {
const pass = async <A>(value: A) => value
@@ -1,86 +0,0 @@
import { describe, expect, it, vi } from 'vitest'
import { collectLeafIds } from '../../../../shared/workspace-layout/terminal-pane-layout-tree'
import { toRuntimeExecutionHostId } from '../../../../shared/execution-host'
import { setupLayoutOwnerRuntime, WORKTREE_ID } from '../../orca-runtime-layout-owner-start-fixture'
vi.mock('../../../../shared/layout-owner-switch', () => ({ ORCA_LAYOUT_OWNER_IS_MAIN: true }))
const TAB_ID = 'window-tab'
const LEAF_ID = '33333333-3333-4333-8333-333333333333'
async function windowTab(runtime: ReturnType<typeof setupLayoutOwnerRuntime>['runtime']) {
// The window drew the tab and recorded it in main under its own ids, as createTab does.
const reply = await runtime.applyLayoutCommand({
type: 'createTerminalTab',
workspace: WORKTREE_ID,
tabId: TAB_ID,
leafId: LEAF_ID
})
expect(reply.ok).toBe(true)
}
describe("the window's new tab, started by main in a layout-owner build", () => {
it('starts the first pane in the tab the window wrote, in its folder, with nothing revealed', async () => {
const { runtime, spawn, session } = setupLayoutOwnerRuntime()
await windowTab(runtime)
const statuses: unknown[] = []
spawn.mockImplementationOnce(async (args) => {
statuses.push(runtime.startStatusOf(`${args.tabId}:${args.leafId}`))
return { id: 'pty-window' }
})
const created = await runtime.startWindowTabTerminal(
WORKTREE_ID,
{ tabId: TAB_ID, leafId: LEAF_ID },
{ cwd: '/workspace/sub', command: 'make dev' }
)
expect(spawn).toHaveBeenCalledTimes(1)
expect(spawn.mock.calls[0]![0]).toMatchObject({
tabId: TAB_ID,
leafId: LEAF_ID,
cwd: '/workspace/sub',
resolvedWorkspace: expect.objectContaining({
id: WORKTREE_ID,
path: '/workspace',
connectionId: null
}),
command: 'make dev'
})
// Publish starting status so views cannot spawn duplicates; clear it after binding.
expect(statuses).toEqual([{ status: 'starting' }])
expect(runtime.startStatusOf(`${TAB_ID}:${LEAF_ID}`)).toBeNull()
expect(created).toMatchObject({ tabId: TAB_ID, ptyId: 'pty-window', surface: 'background' })
expect(session().tabsByWorktree[WORKTREE_ID]?.map((tab) => tab.id)).toEqual([TAB_ID])
expect(collectLeafIds(session().terminalLayoutsByTabId[TAB_ID]?.root)).toEqual([LEAF_ID])
})
it('keeps the pane showing why when its start fails, as a start that timed out does', async () => {
const { runtime, spawn, session } = setupLayoutOwnerRuntime()
await windowTab(runtime)
spawn.mockRejectedValueOnce(new Error('Request timed out: createOrAttach'))
await expect(
runtime.startWindowTabTerminal(WORKTREE_ID, { tabId: TAB_ID, leafId: LEAF_ID }, {})
).rejects.toThrow('Request timed out: createOrAttach')
expect(session().tabsByWorktree[WORKTREE_ID]?.map((tab) => tab.id)).toEqual([TAB_ID])
expect(runtime.startStatusOf(`${TAB_ID}:${LEAF_ID}`)).toEqual({
status: 'failed',
reason: 'Request timed out: createOrAttach'
})
})
it("refuses a paired server's tab, starting nothing, so the window starts it as today", async () => {
const { runtime, spawn } = setupLayoutOwnerRuntime()
await windowTab(runtime)
Object.assign(runtime, {
getWorkspaceSessionHostIdsForTab: () => [toRuntimeExecutionHostId('env-1')]
})
await expect(
runtime.startWindowTabTerminal(WORKTREE_ID, { tabId: TAB_ID, leafId: LEAF_ID }, {})
).rejects.toThrow('host_not_owned')
expect(spawn).not.toHaveBeenCalled()
})
})
@@ -1,5 +1,4 @@
import type * as WorktreeListingModule from '../git/worktrees/catalog/worktree-listing'
// Check scan-cache gating here; real-Git fingerprint behavior lives in repo-worktree-admin-fingerprint.test.ts. Windows CI also exercises repo-path handling.
import { beforeEach, describe, expect, it, vi } from 'vitest'
const electronMocks = vi.hoisted(() => {
@@ -1,35 +0,0 @@
import { describe, expect, it, vi } from 'vitest'
import {
WORKTREE_TERMINAL_SLEEP_TIMEOUT_ERROR,
WorktreeTerminalMutationLock
} from './worktree-terminal-mutation-lock'
const KEY = 'repo::/tmp/worktree'
describe('WorktreeTerminalMutationLock', () => {
it('hands the turn onward when a queued sleep abandons ahead of a spawn', async () => {
vi.useFakeTimers()
try {
const lock = new WorktreeTerminalMutationLock()
const releaseSpawn = await lock.acquire(KEY, 'shared')
const sleep = lock.acquire(KEY, 'exclusive', Date.now() + 1_000)
const rejection = expect(sleep).rejects.toThrow(WORKTREE_TERMINAL_SLEEP_TIMEOUT_ERROR)
let laterSpawnAcquired = false
const laterSpawn = lock.acquire(KEY, 'shared').then((release) => {
laterSpawnAcquired = true
return release
})
await vi.advanceTimersByTimeAsync(1_001)
await rejection
// The spawn was queued behind the sleep; its abandonment must release it.
const releaseLater = await laterSpawn
expect(laterSpawnAcquired).toBe(true)
releaseSpawn()
releaseLater()
expect(lock.trackedKeyCount).toBe(0)
} finally {
vi.useRealTimers()
}
})
})
@@ -1,78 +0,0 @@
import { beforeEach, expect, it, vi } from 'vitest'
const io = vi.hoisted(() => ({ run: vi.fn(), plugins: vi.fn(async () => []) }))
vi.mock('../wsl/execution/wsl-runner', () => ({ runWslProcess: io.run }))
vi.mock('./claude-plugin-skill-sources-wsl', () => ({
discoverClaudePluginSkillSourcesInWsl: io.plugins
}))
vi.mock('./discovery', () => ({ clearSkillRootScanCache: vi.fn(), discoverSkills: vi.fn() }))
import { clearSkillDiscoveryCaches, discoverSkillsOnTarget } from './skill-discovery-target'
const target = { kind: 'wsl' as const, distro: 'Ubuntu', homeDir: '/home/test', cwd: '/repo' }
const record = (...fields: string[]) => `${fields.join('\0')}\0`
const encoded = Buffer.from('---\nname: shared-frontmatter\ndescription: Fixture\n---\n').toString(
'base64'
)
const common = '/opt/physical/SKILL.md'
const rows = [
record('S', '0', '/home/test/.codex/skills/.system/bundle/SKILL.md', common, '1', encoded),
record('S', '0', '/home/test/.codex/skills/alias-a/SKILL.md', common, '1', encoded),
record('S', '1', '/home/test/.agents/skills/alias-b/SKILL.md', common, '1', encoded),
...Array.from({ length: 6 }, (_, i) =>
record(
'S',
'0',
`/home/test/.codex/skills/skill-${i}/SKILL.md`,
`/physical/skill-${i}/SKILL.md`,
'1',
encoded
)
)
]
const output = record('R', '0', '1') + record('R', '1', '1') + rows.join('')
beforeEach(() => {
clearSkillDiscoveryCaches()
io.run.mockReset()
io.plugins.mockClear()
io.run.mockResolvedValue({ code: 0, timedOut: false, stdout: output, stderr: '' })
})
it('keeps both home aliases when a bundled canonical duplicate appears first', async () => {
const [a, b, bundle] = await Promise.all([
discoverSkillsOnTarget({ ...target, names: ['alias-a'], sourceKinds: ['home'] }, []),
discoverSkillsOnTarget({ ...target, names: ['alias-b'], sourceKinds: ['home'] }, []),
discoverSkillsOnTarget({ ...target, names: ['bundle'], sourceKinds: ['bundled'] }, [])
])
expect(io.run).toHaveBeenCalledTimes(1)
expect(a.skills.map((s) => s.directoryPath)).toEqual(['/home/test/.codex/skills/alias-a'])
expect(b.skills.map((s) => s.directoryPath)).toEqual(['/home/test/.agents/skills/alias-b'])
expect(bundle.skills.map((s) => s.directoryPath)).toEqual([
'/home/test/.codex/skills/.system/bundle'
])
expect(a.skills[0].providers).toEqual(['codex'])
expect(b.skills[0].providers).toEqual(['agent-skills'])
expect(a.skills[0].id).toBe(b.skills[0].id)
expect(a.skills[0].sourceKind).toBe('home')
})
it('cache projections do not contaminate later aliases or source metadata', async () => {
const first = await discoverSkillsOnTarget(
{ ...target, names: ['alias-a'], sourceKinds: ['home'] },
[]
)
first.skills[0].providers.push('claude')
first.skills[0].rootPaths!.push('/poison')
first.sources[0].providers.push('claude')
const later = await discoverSkillsOnTarget(
{ ...target, names: ['alias-a'], sourceKinds: ['home'] },
[]
)
expect(later.skills[0].providers).toEqual(['codex'])
expect(later.skills[0].rootPaths).toEqual(['/home/test/.codex/skills'])
expect(later.sources[0].providers).not.toContain('claude')
expect(io.run).toHaveBeenCalledTimes(1)
})
it('does not cache failed scans as an empty successful observation', async () => {
io.run.mockResolvedValueOnce({ code: 1, timedOut: false, stdout: '', stderr: 'failure' })
const req = { ...target, names: ['alias-a'], sourceKinds: ['home' as const] }
await expect(discoverSkillsOnTarget(req, [])).rejects.toThrow('skill-discovery-wsl-scan-failed')
expect((await discoverSkillsOnTarget(req, [])).skills).toHaveLength(1)
expect(io.run).toHaveBeenCalledTimes(2)
})
@@ -1,98 +0,0 @@
import { describe, expect, it } from 'vitest'
import type { OrcadMigrationCatalogPayload } from '../../shared/orcad-migration/orcad-migration-manifest'
import { isRecord } from '../../shared/orcad-migration/orcad-migration-manifest-fields'
import { ownedEditorFileId } from '../../shared/workspace-session/workspace-session-pane-ownership'
import {
ORCAD_MIGRATION_PATH_FIELDS,
type OrcadMigrationPathFieldKind
} from './orcad-migration-path-fields'
import {
parseFieldLocation,
rewriteOrcadMigrationPayloadPaths,
type OrcadMigrationPathRewriter
} from './orcad-migration-path-rewrite'
const WT = 'repo-1::/src/wt'
const SAMPLE: Record<OrcadMigrationPathFieldKind, string> = {
absolute: '/src/a',
'maybe-absolute': '/src/b',
relative: 'rel/c',
url: 'file:///src/d',
'worktree-id': WT,
'owner-key': `worktree:${WT}`,
'pty-session-id': `${WT}@@pty-1`,
'editor-file-id': ownedEditorFileId('/src/wt/f.ts', WT, 'env-1')
}
/** Mark rewritten relative paths and map /src to /dst so omissions remain visible. */
function payloadWithEveryField(): OrcadMigrationCatalogPayload {
const root: Record<string, unknown> = {
repositories: [],
projectGroups: [],
folderWorkspaces: []
}
for (const field of ORCAD_MIGRATION_PATH_FIELDS) {
place(root, parseFieldLocation(field.at), SAMPLE[field.kind])
}
// Sparse fixtures suffice because traversal follows the field table, not a full payload schema.
return { repositories: [], projectGroups: [], folderWorkspaces: [], ...root }
}
function place(
node: Record<string, unknown>,
steps: ReturnType<typeof parseFieldLocation>,
sample: string
): void {
const [step, next, ...rest] = steps
if (step?.type !== 'prop') {
throw new Error('table locations start with a property')
}
if (!next) {
node[step.name] = sample
return
}
node[step.name] = placeInto(node[step.name], [next, ...rest], sample)
}
function placeInto(
existing: unknown,
steps: ReturnType<typeof parseFieldLocation>,
sample: string
): unknown {
const [step, ...rest] = steps
if (!step) {
return sample
}
if (step.type === 'items') {
const items = Array.isArray(existing) ? existing : []
items[0] = placeInto(items[0], rest, sample)
return items
}
const record = isRecord(existing) ? existing : {}
if (step.type === 'keys') {
record[sample] = true
} else if (step.type === 'values') {
record.entry = placeInto(record.entry, rest, sample)
} else {
record[step.name] = placeInto(record[step.name], rest, sample)
}
return record
}
describe('orcad migration path-rewrite stage', () => {
it('refuses a rewrite that would merge two records', () => {
const payload = payloadWithEveryField()
const session = payload.dormantState?.workspaceSession
expect(session).toBeDefined()
session!.tabsByWorktree = { 'repo-1::/src/a': [], 'repo-1::/src/b': [] }
const merging: OrcadMigrationPathRewriter = {
path: (value) => (value.startsWith('/src') ? '/dst' : value)
}
expect(() => rewriteOrcadMigrationPayloadPaths(payload, merging)).toThrow(
'orcad_migration_path_rewrite_key_collision'
)
})
})
@@ -19,11 +19,6 @@ const MIXED: LeaseRow[] = [
{ ptyId: 'exp', state: 'expired' },
{ ptyId: 'ter', state: 'terminated' }
]
const UNRESOLVED: LeaseRow[] = [
{ ptyId: 'det', state: 'detached' },
{ ptyId: 'exp', state: 'expired' },
{ ptyId: 'ter', state: 'terminated' }
]
describe('terminal gate characterization: census-only verdicts', () => {
it('a census that throws is bridged as unverifiable, never exited', async () => {
@@ -38,47 +33,6 @@ describe('terminal gate characterization: census-only verdicts', () => {
hostTerminals: 0
})
})
it.each([
[
'an attached lease',
MIXED,
{ verdict: 'live', ptyIds: ['att'], reason: 'terminals on this host are still running' }
],
[
'detached or expired leases',
UNRESOLVED,
{
verdict: 'unverifiable',
ptyIds: ['det', 'exp'],
reason: 'no SSH relay could confirm these terminals exited'
}
],
[
'only terminated leases',
[{ ptyId: 'ter', state: 'terminated' }],
{
verdict: 'unverifiable',
ptyIds: [],
reason: "no census of every relay on this host's account was taken"
}
],
[
'no leases',
[],
{
verdict: 'unverifiable',
ptyIds: [],
reason: "no census of every relay on this host's account was taken"
}
]
] as const)('without a census, %s blocks', async (_label, leases, expected) => {
for (const census of [undefined, null]) {
await expect(
assessOrcadMigrationTerminals(store([...leases]), 'ssh-1', census)
).resolves.toEqual(expected)
}
})
})
describe('acting on a terminal proof', () => {
@@ -1,84 +0,0 @@
import { describe, expect, it, vi } from 'vitest'
import { AgentHookServer } from '../agent-hooks/server'
import { makePaneKey } from '../../shared/workspace-layout/stable-pane-id'
import { AGENT_HOOK_INFER_INTERRUPT_METHOD } from '../../shared/agent-hook-listener/transport/agent-hook-interrupt-reconciliation'
import { bindRemoteClaudeInterruptReconciliation } from './ssh-agent-hook-interrupt-reconciliation'
vi.mock('../telemetry/client', () => ({ track: vi.fn() }))
vi.mock('../telemetry/cohort-classifier', () => ({ getCohortAtEmit: vi.fn(() => ({})) }))
const PANE = makePaneKey('tab-1', '11111111-1111-4111-8111-111111111111')
const REVISION = '11111111-1111-4111-8111-111111111112'
function host(revision: string | undefined) {
const server = new AgentHookServer()
server.ingestRemote(
{
paneKey: PANE,
tabId: 'tab-1',
source: 'claude',
launchToken: 'launch-a',
hostTurnRevision: revision,
providerSession: { key: 'session_id', id: 'session-a' },
hookEventName: 'UserPromptSubmit',
payload: {
state: 'working',
prompt: 'do work',
agentType: 'claude',
mainAgent: { state: 'working', stateStartedAt: Date.now() }
}
},
'ssh-owner'
)
const infer = () => {
const row = server.getStatusSnapshotForPane(PANE)[0]
if (!row) {
throw new Error('Missing row')
}
return server.inferInterrupt({
paneKey: PANE,
baselineUpdatedAt: row.receivedAt,
baselineStateStartedAt: row.stateStartedAt,
baselinePrompt: row.prompt,
baselineAgentType: row.agentType,
intent: 'ctrl-c'
})
}
return { server, infer }
}
describe('remote interrupt mux and version fences', () => {
it.each(['current', 'foreign', 'replaced', 'disposed', 'unsubscribed', 'legacy'])(
'%s mux dispatches only with a matching current host proof',
(scope) => {
const { server, infer } = host(scope === 'legacy' ? undefined : REVISION)
const request = vi.fn().mockResolvedValue({ applied: true })
const mux = { request, isDisposed: () => scope === 'disposed' }
const unsubscribe = bindRemoteClaudeInterruptReconciliation(
server,
mux,
scope === 'foreign' ? 'other-host' : 'ssh-owner',
() => scope !== 'replaced'
)
try {
if (scope === 'unsubscribed') {
unsubscribe()
}
expect(infer()).toBe(true)
if (scope === 'current') {
expect(request).toHaveBeenCalledExactlyOnceWith(AGENT_HOOK_INFER_INTERRUPT_METHOD, {
paneKey: PANE,
hostTurnRevision: REVISION,
launchToken: 'launch-a',
providerSession: { key: 'session_id', id: 'session-a' },
intent: 'ctrl-c'
})
} else {
expect(request).not.toHaveBeenCalled()
}
} finally {
unsubscribe()
server.stop()
}
}
)
})
@@ -6,9 +6,6 @@ vi.mock('electron', () => ({ app: { dock: { hide: vi.fn() }, setActivationPolicy
const {
applyBackgroundActivationPolicy,
applyServeDesktopWindowActivationPolicy,
isBackgroundLaunch,
isOrcadSidecarLaunch,
isWindowlessLaunch,
showWindowWithoutStealingFocus
} = await import('./foreground-activation-policy')
@@ -26,30 +23,6 @@ function makeWindow(destroyed = false): BrowserWindow & {
}
}
describe('isBackgroundLaunch', () => {
it('treats the orcad browser sidecar as an explicit background launch', () => {
expect(isOrcadSidecarLaunch({ ORCA_ORCAD_SIDECAR: '1' })).toBe(true)
expect(isOrcadSidecarLaunch({})).toBe(false)
expect(isBackgroundLaunch({ ORCA_ORCAD_SIDECAR: '1', ORCA_E2E_FOREGROUND: '1' })).toBe(true)
expect(isWindowlessLaunch({ ORCA_ORCAD_SIDECAR: '1', ORCA_E2E_HEADFUL: '1' })).toBe(true)
})
})
describe('isWindowlessLaunch', () => {
it('keeps explicit background launches hidden while headful E2E can paint', () => {
expect(isWindowlessLaunch({ ORCA_E2E_HEADLESS: '1' })).toBe(true)
expect(isWindowlessLaunch({ ORCA_E2E_HEADLESS: '1', ORCA_E2E_HEADFUL: '1' })).toBe(false)
expect(isWindowlessLaunch({ ORCA_BACKGROUND_LAUNCH: '1' })).toBe(true)
expect(
isWindowlessLaunch({
ORCA_BACKGROUND_LAUNCH: '1',
ORCA_E2E_HEADFUL: '1',
ORCA_E2E_FOREGROUND: '1'
})
).toBe(true)
})
})
describe('showWindowWithoutStealingFocus', () => {
it('never reveals an explicitly background window', () => {
const window = makeWindow()
@@ -1,125 +0,0 @@
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
const { statMock } = vi.hoisted(() => ({
statMock: vi.fn()
}))
vi.mock('node:fs/promises', () => ({ stat: statMock }))
import {
LOCAL_WORKTREE_PATH_PROBE_TIMEOUT_MS,
localWorktreePathsExistOrAreUnverifiable
} from './local-worktree-path-presence'
function fsError(code: string): NodeJS.ErrnoException {
return Object.assign(new Error(code), { code })
}
function installFakeAbortSignalTimeout(): AbortController[] {
const controllers: AbortController[] = []
vi.spyOn(AbortSignal, 'timeout').mockImplementation((timeoutMs) => {
const controller = new AbortController()
controllers.push(controller)
setTimeout(
() => controller.abort(new DOMException('The operation timed out.', 'TimeoutError')),
timeoutMs
)
return controller.signal
})
return controllers
}
describe('local worktree path presence', () => {
beforeEach(() => {
statMock.mockReset()
})
afterEach(() => {
vi.useRealTimers()
vi.restoreAllMocks()
})
it('returns one result per distinct path and only treats ENOENT/ENOTDIR as missing', async () => {
statMock.mockImplementation(async (pathValue: string) => {
if (pathValue === '/missing') {
throw fsError('ENOENT')
}
if (pathValue === '/not-a-directory') {
throw fsError('ENOTDIR')
}
if (pathValue === '/permission-denied') {
throw fsError('EACCES')
}
return { isDirectory: () => true }
})
const result = await localWorktreePathsExistOrAreUnverifiable([
'/present',
'/missing',
'/not-a-directory',
'/permission-denied',
'/present'
])
expect([...result.entries()]).toEqual([
['/present', true],
['/missing', false],
['/not-a-directory', false],
['/permission-denied', true]
])
expect(statMock).toHaveBeenCalledTimes(4)
})
it('settles timed-out batches without exceeding or leaking the global stat bound', async () => {
vi.useFakeTimers()
const timeoutControllers = installFakeAbortSignalTimeout()
const releaseStats: (() => void)[] = []
let returnMissing = false
statMock.mockImplementation(() =>
returnMissing
? Promise.reject(fsError('ENOENT'))
: new Promise((resolve) => releaseStats.push(() => resolve({ isDirectory: () => true })))
)
try {
const firstPaths = ['/first-1', '/first-2', '/first-3', '/first-4']
const first = localWorktreePathsExistOrAreUnverifiable(firstPaths)
let firstSettled = false
void first.then(() => {
firstSettled = true
})
await vi.advanceTimersByTimeAsync(0)
expect(statMock).toHaveBeenCalledTimes(2)
await vi.advanceTimersByTimeAsync(LOCAL_WORKTREE_PATH_PROBE_TIMEOUT_MS - 1)
expect(firstSettled).toBe(false)
await vi.advanceTimersByTimeAsync(1)
await expect(first).resolves.toEqual(
new Map(firstPaths.map((pathValue) => [pathValue, true]))
)
expect(statMock).toHaveBeenCalledTimes(2)
const secondPaths = ['/second-1', '/second-2', '/second-3']
const second = localWorktreePathsExistOrAreUnverifiable(secondPaths)
await vi.advanceTimersByTimeAsync(LOCAL_WORKTREE_PATH_PROBE_TIMEOUT_MS)
await expect(second).resolves.toEqual(
new Map(secondPaths.map((pathValue) => [pathValue, true]))
)
expect(statMock).toHaveBeenCalledTimes(2)
returnMissing = true
releaseStats.splice(0).forEach((release) => release())
await vi.advanceTimersByTimeAsync(0)
await expect(localWorktreePathsExistOrAreUnverifiable(['/after-release'])).resolves.toEqual(
new Map([['/after-release', false]])
)
expect(statMock).toHaveBeenCalledTimes(3)
} finally {
timeoutControllers.forEach((controller) => controller.abort())
releaseStats.splice(0).forEach((release) => release())
await Promise.resolve()
await Promise.resolve()
}
})
})
@@ -60,15 +60,6 @@ afterEach(() => {
})
describe('releasing orphaned WSL holders at launch', () => {
it('touches nothing for a user who never ran a WSL host', async () => {
const { factory } = fakeHolder()
await expect(
releaseUnwantedWslHolders({ userDataPath, isWanted: () => false, holder: factory })
).resolves.toEqual([])
// No holder means no process table read and no wsl.exe.
expect(factory).not.toHaveBeenCalled()
})
it('keeps a live host’s holder and releases only orphans and pending removals', async () => {
addHost('Ubuntu', 42000)
addHost('Debian', 42001)