diff --git a/config/scripts/ci-shard-timings.json b/config/scripts/ci-shard-timings.json index d8ab7dad402..8b77f6ed245 100644 --- a/config/scripts/ci-shard-timings.json +++ b/config/scripts/ci-shard-timings.json @@ -10467,7 +10467,6 @@ "src/renderer/src/lib/ai-vault-resume-shell.test.ts": 487, "src/renderer/src/lib/ai-vault-resume-target.test.ts": 199, "src/renderer/src/lib/ai-vault-session-drag.test.ts": 45, - "src/renderer/src/lib/ai-vault-session-fork-startup.test.ts": 315, "src/renderer/src/lib/ai-vault-session-resume-preparation.test.ts": 135, "src/renderer/src/lib/ai-vault-tab-title-sync-inputs.test.ts": 192, "src/renderer/src/lib/ai-vault-tab-title-sync.test.ts": 803, @@ -11039,7 +11038,6 @@ "src/renderer/src/lib/worktree-agent-live-surface-adoption-layout-owner.test.ts": 1430, "src/renderer/src/lib/worktree-agent-structured-inventory.test.ts": 155, "src/renderer/src/lib/worktree-attachment-label.test.ts": 34, - "src/renderer/src/lib/worktree-creation-agent-seeding.test.ts": 846, "src/renderer/src/lib/worktree-creation-agent-seeds.test.ts": 1098, "src/renderer/src/lib/worktree-creation-backend-startup-focus.test.ts": 1775, "src/renderer/src/lib/worktree-creation-background-mount.test.ts": 2109, @@ -11056,7 +11054,6 @@ "src/renderer/src/lib/worktree-creation-structured-session-host-decline.test.ts": 2373, "src/renderer/src/lib/worktree-creation-structured-session.test.ts": 837, "src/renderer/src/lib/worktree-default-display-name.test.ts": 48, - "src/renderer/src/lib/worktree-default-terminal-tabs-layout-owner.test.ts": 1147, "src/renderer/src/lib/worktree-display-name-order.test.ts": 57, "src/renderer/src/lib/worktree-host-connection-phase.test.ts": 1364, "src/renderer/src/lib/worktree-initial-terminal-close-records.test.ts": 2576, diff --git a/src/renderer/src/lib/ai-vault-session-fork-startup.test.ts b/src/renderer/src/lib/ai-vault-session-fork-startup.test.ts deleted file mode 100644 index e288e4fbd60..00000000000 --- a/src/renderer/src/lib/ai-vault-session-fork-startup.test.ts +++ /dev/null @@ -1,101 +0,0 @@ -import { getDefaultSettings } from '../../../shared/constants' -import { TEST_REPO, makeWorktree } from '../store/slices/worktrees-slice-test-fixtures' -import { describe, expect, it, vi } from 'vitest' -import type { AppState } from '@/store/types' -import { buildAiVaultForkStartupForWorktree } from './ai-vault-session-fork-startup' - -vi.mock('@/lib/new-workspace', () => ({ - CLIENT_PLATFORM: 'darwin' -})) - -type ForkState = Pick< - AppState, - | 'activeRepoId' - | 'activeWorktreeId' - | 'folderWorkspaces' - | 'projectGroups' - | 'endpointProjects' - | 'endpointProjectHostSetups' - | 'repos' - | 'settings' - | 'worktreesByRepo' -> - -function makeState(): ForkState { - return { - activeRepoId: 'repo-1', - activeWorktreeId: 'repo-1::worktree-1', - folderWorkspaces: [], - projectGroups: [], - repos: [{ ...TEST_REPO, id: 'repo-1', path: '/Users/ada/repo' }], - endpointProjects: [ - { - id: 'repo-1', - displayName: 'repo', - badgeColor: '', - sourceRepoIds: ['repo-1'], - createdAt: 0, - updatedAt: 0 - } - ], - endpointProjectHostSetups: [], - settings: { - ...getDefaultSettings('/tmp'), - agentPermissionDefault: 'own', - agentDefaultArgs: { claude: '', codex: '' }, - agentDefaultEnv: { claude: {}, codex: {} } - }, - worktreesByRepo: { - 'repo-1': [ - makeWorktree({ id: 'repo-1::worktree-1', repoId: 'repo-1', path: '/Users/ada/repo' }) - ] - } - } -} - -const SESSION_ID = '019fd532-7c11-7a90-b6de-4e1a2c3d5f60' - -describe('AI Vault fork into the CLI', () => { - it('forks Claude in the recorded folder without claiming the chat-owned session', () => { - const startup = buildAiVaultForkStartupForWorktree({ - state: makeState(), - worktreeId: 'repo-1::worktree-1', - session: { agent: 'claude', sessionId: SESSION_ID, cwd: '/Users/ada/repo', codexHome: null } - }) - - expect(startup).toMatchObject({ - command: `claude '--resume' '${SESSION_ID}' '--fork-session'`, - cwd: '/Users/ada/repo' - }) - // A cold restore of the tab must never re-enter the conversation the chat still owns. - expect(startup).not.toHaveProperty('providerSession') - }) - - it('forks Codex under the home the transcript was found in', () => { - const startup = buildAiVaultForkStartupForWorktree({ - state: makeState(), - worktreeId: 'repo-1::worktree-1', - session: { - agent: 'codex', - sessionId: SESSION_ID, - cwd: '/Users/ada/repo', - codexHome: '/Users/ada/.orca/codex-accounts/a1/home' - } - }) - - expect(startup?.command).toBe( - `CODEX_HOME='/Users/ada/.orca/codex-accounts/a1/home' codex '-c' 'tui.resume_cwd=current' 'fork' '${SESSION_ID}'` - ) - expect(startup).not.toHaveProperty('providerSession') - }) - - it('returns null for an agent that cannot fork instead of a plain resume', () => { - expect( - buildAiVaultForkStartupForWorktree({ - state: makeState(), - worktreeId: 'repo-1::worktree-1', - session: { agent: 'gemini', sessionId: SESSION_ID, cwd: '/Users/ada/repo', codexHome: null } - }) - ).toBeNull() - }) -}) diff --git a/src/renderer/src/lib/github-pr-start-point.test.ts b/src/renderer/src/lib/github-pr-start-point.test.ts index 28acc7f63bb..2f7adceb5d1 100644 --- a/src/renderer/src/lib/github-pr-start-point.test.ts +++ b/src/renderer/src/lib/github-pr-start-point.test.ts @@ -23,75 +23,6 @@ describe('resolveGitHubPrStartPointForRepo', () => { }) }) - it('resolves local PR heads through worktree IPC with PR branch hints', async () => { - vi.mocked(window.api.worktrees.resolvePrBase).mockResolvedValue({ - baseBranch: 'abc123', - compareBaseRef: 'refs/remotes/origin/main', - branchNameOverride: 'feature/fix', - pushTarget: { remoteName: 'origin', branchName: 'feature/fix' } - }) - - await expect( - resolveGitHubPrStartPointForRepo({ - repoId: 'repo-1', - prNumber: 42, - target: { kind: 'local' as const }, - headRefName: 'feature/fix', - baseRefName: 'main', - isCrossRepository: false - }) - ).resolves.toMatchObject({ - baseBranch: 'abc123', - branchNameOverride: 'feature/fix' - }) - - expect(window.api.worktrees.resolvePrBase).toHaveBeenCalledWith({ - repoId: 'repo-1', - prNumber: 42, - headRefName: 'feature/fix', - baseRefName: 'main', - isCrossRepository: false - }) - expect(callRuntimeRpc).not.toHaveBeenCalled() - }) - - it('routes runtime-owned PR head resolution through runtime RPC', async () => { - vi.mocked(callRuntimeRpc).mockResolvedValue({ - baseBranch: 'def456', - compareBaseRef: 'refs/remotes/origin/develop', - branchNameOverride: 'feature/runtime', - pushTarget: { remoteName: 'fork', branchName: 'feature/runtime' } - }) - - await expect( - resolveGitHubPrStartPointForRepo({ - repoId: 'repo-runtime', - prNumber: 7, - target: { kind: 'environment' as const, environmentId: 'env-1' }, - headRefName: 'feature/runtime', - baseRefName: 'develop', - isCrossRepository: true - }) - ).resolves.toMatchObject({ - baseBranch: 'def456', - branchNameOverride: 'feature/runtime' - }) - - expect(callRuntimeRpc).toHaveBeenCalledWith( - { kind: 'environment', environmentId: 'env-1' }, - 'worktree.resolvePrBase', - { - repo: 'repo-runtime', - prNumber: 7, - headRefName: 'feature/runtime', - baseRefName: 'develop', - isCrossRepository: true - }, - { timeoutMs: 30_000 } - ) - expect(window.api.worktrees.resolvePrBase).not.toHaveBeenCalled() - }) - it('throws resolver errors so submit can abort before creating from the wrong base', async () => { vi.mocked(window.api.worktrees.resolvePrBase).mockResolvedValue({ error: 'Could not resolve PR head.' diff --git a/src/renderer/src/lib/launch-work-item-direct-agent-routing.test.ts b/src/renderer/src/lib/launch-work-item-direct-agent-routing.test.ts index e5dd68c00de..37cd0cd050b 100644 --- a/src/renderer/src/lib/launch-work-item-direct-agent-routing.test.ts +++ b/src/renderer/src/lib/launch-work-item-direct-agent-routing.test.ts @@ -41,28 +41,6 @@ describe('beginDirectWorkItemStructuredLaunch', () => { }) }) - it('opens the provisional chat synchronously and preserves the requested tab id', () => { - const order: string[] = [] - mocks.beginStructuredAgentSessionProvisionalLaunch.mockImplementation((args) => { - order.push('begin') - args.beforeOpen('session-1') - order.push('open') - return { sessionId: 'session-1', tab: { id: 'agent-session:session-1' } } - }) - - expect( - beginDirectWorkItemStructuredLaunch({ - plan: structuredPlan, - primaryTabId: null, - beforeOpen: (sessionId) => { - order.push(`reveal:${sessionId}`) - return true - } - }) - ).toEqual({ completed: true, structuredLaunch: true, primaryTabId: 'agent-session:session-1' }) - expect(order).toEqual(['begin', 'reveal:session-1', 'open']) - }) - it('does not claim completion when the provisional opener is refused', () => { mocks.beginStructuredAgentSessionProvisionalLaunch.mockReturnValue(null) @@ -74,20 +52,6 @@ describe('beginDirectWorkItemStructuredLaunch', () => { }) ).toEqual({ completed: false, structuredLaunch: true, primaryTabId: 'setup-shell-tab' }) }) - - it('skips structured opening for non-structured routes', () => { - expect( - beginDirectWorkItemStructuredLaunch({ - plan: adoptAgentSessionLaunchVerdict({ - ...structuredPlan, - route: 'terminal-tui' - }), - primaryTabId: null, - beforeOpen: vi.fn() - }) - ).toEqual({ completed: false, structuredLaunch: false, primaryTabId: null }) - expect(mocks.beginStructuredAgentSessionProvisionalLaunch).not.toHaveBeenCalled() - }) }) // Why (#29599): a paired server's new workspace picks from that workspace's agents on the server. diff --git a/src/renderer/src/lib/legacy-runtime-owner-answers.test-fixture.ts b/src/renderer/src/lib/legacy-runtime-owner-answers.test-fixture.ts deleted file mode 100644 index ae9b11b9592..00000000000 --- a/src/renderer/src/lib/legacy-runtime-owner-answers.test-fixture.ts +++ /dev/null @@ -1,85 +0,0 @@ -/** - * What the deleted `resolveRuntimeOwnerForWorktree` answered over the owner matrix (main @ - * 37c314bcc960), so its replacements can list every deliberate difference. - */ -export const LEGACY_RUNTIME_OWNER_GOLDEN: Readonly> = { - 'focused | null': null, - 'focused | global-floating-terminal': null, - 'focused | local-repo::wt': null, - 'focused | legacy-repo::wt': null, - 'focused | runtime-repo::wt': 'owner-env', - 'focused | ssh-repo::wt': null, - 'focused | hub-repo::wt': 'hub-env', - 'focused | twin-repo::wt': 'ambiguous', - 'focused | repo-twin-only::wt': 'ambiguous', - 'focused | stale-hub-repo::wt': null, - 'focused | rival-repo::wt': 'ambiguous', - 'focused | conflict-repo::wt': 'ambiguous', - 'focused | projected-twin::wt': 'ambiguous', - 'focused | detected-repo::wt': 'owner-env', - 'focused | unknown-repo::wt': null, - 'focused | folder:local-folder': null, - 'focused | folder:runtime-folder': 'owner-env', - 'focused | folder:ssh-folder': null, - 'focused | folder:twin-folder': null, - 'focused | folder:group-twin-folder': null, - 'focused | folder:unstamped-folder': null, - 'focused | folder:restored-folder': 'owner-env', - 'focused | folder:missing-folder': null, - 'no focus | null': null, - 'no focus | global-floating-terminal': null, - 'no focus | local-repo::wt': null, - 'no focus | legacy-repo::wt': null, - 'no focus | runtime-repo::wt': 'owner-env', - 'no focus | ssh-repo::wt': null, - 'no focus | hub-repo::wt': 'hub-env', - 'no focus | twin-repo::wt': 'ambiguous', - 'no focus | repo-twin-only::wt': 'ambiguous', - 'no focus | stale-hub-repo::wt': null, - 'no focus | rival-repo::wt': 'ambiguous', - 'no focus | conflict-repo::wt': 'ambiguous', - 'no focus | projected-twin::wt': 'ambiguous', - 'no focus | detected-repo::wt': 'owner-env', - 'no focus | unknown-repo::wt': null, - 'no focus | folder:local-folder': null, - 'no focus | folder:runtime-folder': 'owner-env', - 'no focus | folder:ssh-folder': null, - 'no focus | folder:twin-folder': null, - 'no focus | folder:group-twin-folder': null, - 'no focus | folder:unstamped-folder': null, - 'no focus | folder:restored-folder': 'owner-env', - 'no focus | folder:missing-folder': null, - 'single focus | null': null, - 'single focus | global-floating-terminal': null, - 'single focus | local-repo::wt': null, - 'single focus | legacy-repo::wt': 'focused-env', - 'single focus | runtime-repo::wt': 'owner-env', - 'single focus | ssh-repo::wt': null, - 'single focus | hub-repo::wt': 'hub-env', - 'single focus | twin-repo::wt': 'ambiguous', - 'single focus | repo-twin-only::wt': 'ambiguous', - 'single focus | stale-hub-repo::wt': null, - 'single focus | rival-repo::wt': 'ambiguous', - 'single focus | conflict-repo::wt': 'ambiguous', - 'single focus | projected-twin::wt': 'ambiguous', - 'single focus | detected-repo::wt': 'owner-env', - 'single focus | unknown-repo::wt': null, - 'single focus | folder:local-folder': null, - 'single focus | folder:runtime-folder': 'owner-env', - 'single focus | folder:ssh-folder': null, - 'single focus | folder:twin-folder': 'focused-env', - 'single focus | folder:group-twin-folder': 'focused-env', - 'single focus | folder:unstamped-folder': 'focused-env', - 'single focus | folder:restored-folder': 'owner-env', - 'single focus | folder:missing-folder': 'focused-env', - 'active local | twin-repo::wt': null, - 'active ssh:box | twin-repo::wt': null, - 'active local | rival-repo::wt': null, - 'active ssh:box | rival-repo::wt': null, - 'active local | conflict-repo::wt': null, - 'active ssh:box | conflict-repo::wt': 'hub-a', - 'active local | folder:twin-folder': null, - 'active ssh:box | folder:twin-folder': null, - 'active local | folder:group-twin-folder': null, - 'active ssh:box | folder:group-twin-folder': null -} diff --git a/src/renderer/src/lib/pane-manager/terminal-link-provider-guard.test.ts b/src/renderer/src/lib/pane-manager/terminal-link-provider-guard.test.ts index 9d5d19c9e47..8ba3bb46386 100644 --- a/src/renderer/src/lib/pane-manager/terminal-link-provider-guard.test.ts +++ b/src/renderer/src/lib/pane-manager/terminal-link-provider-guard.test.ts @@ -1,9 +1,6 @@ -import type { ILink, ILinkProvider, Terminal } from '@xterm/xterm' +import type { ILink, ILinkProvider } from '@xterm/xterm' import { beforeEach, describe, expect, it, vi } from 'vitest' -import { - guardLinkProvider, - installGuardedLinkProviderRegistration -} from './terminal-link-provider-guard' +import { guardLinkProvider } from './terminal-link-provider-guard' const mocks = vi.hoisted(() => ({ recordRendererCrashBreadcrumb: vi.fn() @@ -51,17 +48,6 @@ describe('guardLinkProvider', () => { ) }) - it('passes provided links through unchanged when the provider succeeds', () => { - const links = [{ text: 'term_abc' }] as unknown as ILink[] - const provider: ILinkProvider = { - provideLinks: (_lineNumber, callback) => callback(links) - } - const guarded = guardLinkProvider(provider, 'orca-handle') - - expect(collectLinks(guarded)).toBe(links) - expect(mocks.recordRendererCrashBreadcrumb).not.toHaveBeenCalled() - }) - it('does not double-invoke the callback when the provider throws after resolving', () => { const links = [{ text: 'file.ts' }] as unknown as ILink[] const provider: ILinkProvider = { @@ -79,31 +65,3 @@ describe('guardLinkProvider', () => { expect(mocks.recordRendererCrashBreadcrumb).toHaveBeenCalledOnce() }) }) - -describe('installGuardedLinkProviderRegistration', () => { - it('guards every provider registered after install (addon-internal included)', () => { - const registered: ILinkProvider[] = [] - const terminal = { - registerLinkProvider: (provider: ILinkProvider) => { - registered.push(provider) - return { dispose: vi.fn() } - } - } as unknown as Terminal - - installGuardedLinkProviderRegistration(terminal) - - terminal.registerLinkProvider({ - provideLinks: () => { - throw new RangeError('Invalid array length') - } - }) - - expect(registered).toHaveLength(1) - expect(() => collectLinks(registered[0])).not.toThrow() - expect(collectLinks(registered[0])).toBeUndefined() - expect(mocks.recordRendererCrashBreadcrumb).toHaveBeenCalledWith( - 'terminal_link_provider_error', - expect.objectContaining({ provider: 'provider-1', errorName: 'RangeError' }) - ) - }) -}) diff --git a/src/renderer/src/lib/resolve-owner-acceptance.test-fixture.ts b/src/renderer/src/lib/resolve-owner-acceptance.test-fixture.ts deleted file mode 100644 index 92779b3574b..00000000000 --- a/src/renderer/src/lib/resolve-owner-acceptance.test-fixture.ts +++ /dev/null @@ -1,79 +0,0 @@ -/** Hand-reviewed owner per matrix case for `resolveOwnerOrLocal` (round-3 design §5). */ -export const OWNER_OR_LOCAL_ACCEPTANCE: Record = { - 'focused | global-floating-terminal': 'self@local', - 'focused | local-repo::wt': 'self@local', - 'focused | legacy-repo::wt': 'self@local', - 'focused | runtime-repo::wt': 'env:owner-env@local', - 'focused | ssh-repo::wt': 'self@ssh:box', - 'focused | hub-repo::wt': 'env:hub-env@ssh:box', - 'focused | twin-repo::wt': 'ambiguous', - 'focused | repo-twin-only::wt': 'self@local', - 'focused | stale-hub-repo::wt': 'env:hub-env@ssh:target', - 'focused | rival-repo::wt': 'ambiguous', - 'focused | conflict-repo::wt': 'ambiguous', - 'focused | projected-twin::wt': 'ambiguous', - 'focused | detected-repo::wt': 'env:owner-env@local', - 'focused | unknown-repo::wt': 'self@local', - 'focused | folder:local-folder': 'self@local', - 'focused | folder:runtime-folder': 'env:owner-env@ssh:inner-ssh', - 'focused | folder:ssh-folder': 'self@ssh:box', - 'focused | folder:twin-folder': 'ambiguous', - 'focused | folder:group-twin-folder': 'ambiguous', - 'focused | folder:unstamped-folder': 'self@local', - 'focused | folder:restored-folder': 'env:owner-env@local', - 'focused | folder:missing-folder': 'self@local', - 'no focus | global-floating-terminal': 'self@local', - 'no focus | local-repo::wt': 'self@local', - 'no focus | legacy-repo::wt': 'self@local', - 'no focus | runtime-repo::wt': 'env:owner-env@local', - 'no focus | ssh-repo::wt': 'self@ssh:box', - 'no focus | hub-repo::wt': 'env:hub-env@ssh:box', - 'no focus | twin-repo::wt': 'ambiguous', - 'no focus | repo-twin-only::wt': 'self@local', - 'no focus | stale-hub-repo::wt': 'env:hub-env@ssh:target', - 'no focus | rival-repo::wt': 'ambiguous', - 'no focus | conflict-repo::wt': 'ambiguous', - 'no focus | projected-twin::wt': 'ambiguous', - 'no focus | detected-repo::wt': 'env:owner-env@local', - 'no focus | unknown-repo::wt': 'self@local', - 'no focus | folder:local-folder': 'self@local', - 'no focus | folder:runtime-folder': 'env:owner-env@ssh:inner-ssh', - 'no focus | folder:ssh-folder': 'self@ssh:box', - 'no focus | folder:twin-folder': 'ambiguous', - 'no focus | folder:group-twin-folder': 'ambiguous', - 'no focus | folder:unstamped-folder': 'self@local', - 'no focus | folder:restored-folder': 'env:owner-env@local', - 'no focus | folder:missing-folder': 'self@local', - 'single focus | global-floating-terminal': 'self@local', - 'single focus | local-repo::wt': 'self@local', - 'single focus | legacy-repo::wt': 'self@local', - 'single focus | runtime-repo::wt': 'env:owner-env@local', - 'single focus | ssh-repo::wt': 'self@ssh:box', - 'single focus | hub-repo::wt': 'env:hub-env@ssh:box', - 'single focus | twin-repo::wt': 'ambiguous', - 'single focus | repo-twin-only::wt': 'self@local', - 'single focus | stale-hub-repo::wt': 'env:hub-env@ssh:target', - 'single focus | rival-repo::wt': 'ambiguous', - 'single focus | conflict-repo::wt': 'ambiguous', - 'single focus | projected-twin::wt': 'ambiguous', - 'single focus | detected-repo::wt': 'env:owner-env@local', - 'single focus | unknown-repo::wt': 'self@local', - 'single focus | folder:local-folder': 'self@local', - 'single focus | folder:runtime-folder': 'env:owner-env@ssh:inner-ssh', - 'single focus | folder:ssh-folder': 'self@ssh:box', - 'single focus | folder:twin-folder': 'ambiguous', - 'single focus | folder:group-twin-folder': 'ambiguous', - 'single focus | folder:unstamped-folder': 'self@local', - 'single focus | folder:restored-folder': 'env:owner-env@local', - 'single focus | folder:missing-folder': 'self@local', - 'active local | twin-repo::wt': 'self@local', - 'active ssh:box | twin-repo::wt': 'self@ssh:box', - 'active local | rival-repo::wt': 'self@local', - 'active ssh:box | rival-repo::wt': 'ambiguous', - 'active local | conflict-repo::wt': 'self@local', - 'active ssh:box | conflict-repo::wt': 'ambiguous', - 'active local | folder:twin-folder': 'self@local', - 'active ssh:box | folder:twin-folder': 'self@ssh:box', - 'active local | folder:group-twin-folder': 'self@local', - 'active ssh:box | folder:group-twin-folder': 'self@ssh:box' -} diff --git a/src/renderer/src/lib/resolve-owner-acceptance.test.ts b/src/renderer/src/lib/resolve-owner-acceptance.test.ts index 4a5fd92a990..a0f38eda582 100644 --- a/src/renderer/src/lib/resolve-owner-acceptance.test.ts +++ b/src/renderer/src/lib/resolve-owner-acceptance.test.ts @@ -3,8 +3,7 @@ import type { ExecutionHostOwnerMatch } from '../../../shared/worktree-execution import type { HostAuthority } from '../../../shared/host-authority' import { resolveOwnerOrLocal } from './worktree-runtime-owner' import { OWNER_RESOLUTION_CASES } from './runtime-owner-alias-matrix.test-fixture' -import { OWNER_OR_LOCAL_ACCEPTANCE } from './resolve-owner-acceptance.test-fixture' -import { LEGACY_RUNTIME_OWNER_GOLDEN } from './legacy-runtime-owner-answers.test-fixture' + import type { WorktreeRuntimeOwnerState } from './worktree-runtime-owner-state' function describeMatch(match: ExecutionHostOwnerMatch): string { @@ -15,55 +14,6 @@ function describeMatch(match: ExecutionHostOwnerMatch): string { return `${endpoint.kind === 'self' ? 'self' : `env:${endpoint.environmentId}`}@${at}` } -const casesWithIds = OWNER_RESOLUTION_CASES.flatMap((c) => - c.worktreeId === null ? [] : [{ ...c, worktreeId: c.worktreeId }] -) - -// Each deliberate change from the deprecated resolver, with the design's reason (§5). -const DELIBERATE_CHANGES: Record = { - 'twin-repo::wt': 'D77: a twin is never picked by default', - 'projected-twin::wt': 'D77', - 'folder:twin-folder': 'D77', - 'folder:group-twin-folder': 'D77', - 'rival-repo::wt': 'B: servers contest the SSH host', - 'conflict-repo::wt': 'B', - 'stale-hub-repo::wt': 'C: reached through the server whose repo row names the target', - 'legacy-repo::wt': 'an unstamped row is local; the Active Server focus never claims it', - 'folder:unstamped-folder': 'same', - 'folder:missing-folder': 'same' -} - -describe('resolveOwnerOrLocal acceptance (round-3 design §5)', () => { - it('pins the owner of every matrix case', () => { - const actual = Object.fromEntries( - casesWithIds.map((c) => [ - c.name, - describeMatch(resolveOwnerOrLocal(c.state, { workspaceId: c.worktreeId })) - ]) - ) - expect(actual).toEqual(OWNER_OR_LOCAL_ACCEPTANCE) - }) - - it('differs from the deprecated resolver only in the deliberate cases', () => { - const changed = casesWithIds.filter((c) => { - const owner = resolveOwnerOrLocal(c.state, { workspaceId: c.worktreeId }) - const environmentId = - owner.kind === 'resolved' && owner.owner.endpoint.kind === 'environment' - ? owner.owner.endpoint.environmentId - : null - const legacy = LEGACY_RUNTIME_OWNER_GOLDEN[c.name] - // Why: the deprecated environment-id reader collapsed `ambiguous` to `null`. - return owner.kind !== 'resolved' || environmentId !== (legacy === 'ambiguous' ? null : legacy) - }) - for (const c of changed) { - expect(DELIBERATE_CHANGES[c.worktreeId], c.name).toBeDefined() - } - expect(new Set(changed.map((c) => c.worktreeId))).toEqual( - new Set(Object.keys(DELIBERATE_CHANGES)) - ) - }) -}) - describe('resolveOwnerOrLocal probes', () => { const base = OWNER_RESOLUTION_CASES.find((c) => c.name === 'focused | stale-hub-repo::wt')!.state const single = OWNER_RESOLUTION_CASES.find( diff --git a/src/renderer/src/lib/terminal-pty-owner-acceptance.test.ts b/src/renderer/src/lib/terminal-pty-owner-acceptance.test.ts index f19f94c67f9..ed63eeb60c2 100644 --- a/src/renderer/src/lib/terminal-pty-owner-acceptance.test.ts +++ b/src/renderer/src/lib/terminal-pty-owner-acceptance.test.ts @@ -2,48 +2,10 @@ import { describe, expect, it } from 'vitest' import { resolveTerminalHostOwnership, resolveTerminalPtyOwner } from './terminal-worktree-route' import { resolveOwner } from './resolve-owner' import { OWNER_RESOLUTION_CASES } from './runtime-owner-alias-matrix.test-fixture' -import { LEGACY_RUNTIME_OWNER_GOLDEN } from './legacy-runtime-owner-answers.test-fixture' + import type { WorktreeRuntimeOwnerState } from './worktree-runtime-owner-state' -const describeOwner = (owner: ReturnType) => - owner.kind === 'runtime' ? owner.environmentId : owner.kind === 'ambiguous' ? 'ambiguous' : null - -// Each deliberate change from the deleted resolver's teardown answer (round-3 design §3, §4, §7). -const DELIBERATE_CHANGES: Record = { - // C: teardown follows spawn, which already starts these PTYs on the server. - 'focused | stale-hub-repo::wt': 'hub-env', - 'no focus | stale-hub-repo::wt': 'hub-env', - 'single focus | stale-hub-repo::wt': 'hub-env', - // D77: a folder twin names no host; only each PTY's own `remote:` id may. - 'focused | folder:twin-folder': 'ambiguous', - 'no focus | folder:twin-folder': 'ambiguous', - 'single focus | folder:twin-folder': 'ambiguous', - 'focused | folder:group-twin-folder': 'ambiguous', - 'no focus | folder:group-twin-folder': 'ambiguous', - 'single focus | folder:group-twin-folder': 'ambiguous', - // B: two servers contest the selected SSH host, so spawn refuses and teardown must too. - 'active ssh:box | rival-repo::wt': 'ambiguous', - 'active ssh:box | conflict-repo::wt': 'ambiguous', - // An unstamped row is this machine's; the Active Server focus never claims its PTYs. - 'single focus | legacy-repo::wt': null, - 'single focus | folder:unstamped-folder': null, - 'single focus | folder:missing-folder': null -} - describe('resolveTerminalPtyOwner acceptance', () => { - it('answers as the deleted resolver did, except the deliberate cases', () => { - const actual = Object.fromEntries( - OWNER_RESOLUTION_CASES.map((c) => [ - c.name, - describeOwner(resolveTerminalPtyOwner(c.state, c.worktreeId)) - ]) - ) - expect(actual).toEqual({ ...LEGACY_RUNTIME_OWNER_GOLDEN, ...DELIBERATE_CHANGES }) - for (const name of Object.keys(DELIBERATE_CHANGES)) { - expect(LEGACY_RUNTIME_OWNER_GOLDEN[name], name).not.toBe(DELIBERATE_CHANGES[name]) - } - }) - it('keeps a setup terminal and a row-less worktree local under single focus', () => { const single = OWNER_RESOLUTION_CASES.find((c) => c.name === 'single focus | legacy-repo::wt')! expect(resolveTerminalPtyOwner(single.state, 'ephemeral-setup-terminal:skill-panel')).toEqual({ diff --git a/src/renderer/src/lib/worktree-creation-agent-seeding.test.ts b/src/renderer/src/lib/worktree-creation-agent-seeding.test.ts deleted file mode 100644 index 58961258f26..00000000000 --- a/src/renderer/src/lib/worktree-creation-agent-seeding.test.ts +++ /dev/null @@ -1,99 +0,0 @@ -import { beforeEach, describe, expect, it, vi } from 'vitest' -import type { WorktreeCreationRequest } from './pending-worktree-creation' - -const mocks = vi.hoisted(() => ({ - activateAndRevealWorktree: vi.fn(), - completeWorktreeCreation: vi.fn(), - ensureWorktreeHasInitialTerminal: vi.fn(), - ensureWebRuntimeWorktreeTerminalAfterWake: vi.fn() -})) - -const store = { - activePendingCreationId: null as string | null, - activeView: 'tasks' as 'tasks' | 'terminal', - createWorktree: vi.fn(), - pendingWorktreeCreations: {} as Record, - repos: [] -} - -vi.mock('@/store', () => ({ useAppStore: { getState: () => store } })) -vi.mock('@/lib/worktree-activation', () => ({ - activateAndRevealWorktree: mocks.activateAndRevealWorktree -})) -vi.mock('@/lib/worktree-initial-terminal-seeding', () => ({ - ensureWorktreeHasInitialTerminal: mocks.ensureWorktreeHasInitialTerminal -})) -vi.mock('@/lib/worktree-creation-completion', () => ({ - completeWorktreeCreation: mocks.completeWorktreeCreation -})) -vi.mock('@/lib/web-runtime-worktree-terminal-after-wake', () => ({ - ensureWebRuntimeWorktreeTerminalAfterWake: mocks.ensureWebRuntimeWorktreeTerminalAfterWake -})) - -import { executeWorktreeCreation } from './worktree-creation-flow-execute' - -const request: WorktreeCreationRequest = { - repoId: 'repo-1', - name: 'feature', - setupDecision: 'inherit', - agent: 'codex', - agentLaunchRoute: 'terminal-tui', - pendingFirstAgentMessageRename: false, - note: '', - startupPlan: null, - quickPrompt: '', - quickTelemetry: null -} - -describe('executeWorktreeCreation agent seeding', () => { - beforeEach(() => { - vi.clearAllMocks() - store.activePendingCreationId = null - store.activeView = 'tasks' - store.pendingWorktreeCreations = { 'creation-1': { creationId: 'creation-1' } } - store.createWorktree.mockResolvedValue({ - worktree: { id: 'worktree-1', repoId: request.repoId } - }) - }) - - it('treats a background blank workspace as empty instead of waiting for the host', async () => { - await executeWorktreeCreation('creation-1', { ...request, agent: null }) - - expect(mocks.ensureWebRuntimeWorktreeTerminalAfterWake).toHaveBeenCalledWith('worktree-1', { - startup: undefined, - agent: null, - activate: false, - unknownHostCountIsEmpty: true - }) - }) - - it('routes a background agent selection through host-aware surface creation', async () => { - await executeWorktreeCreation('creation-1', request) - - expect(mocks.activateAndRevealWorktree).not.toHaveBeenCalled() - expect(mocks.ensureWorktreeHasInitialTerminal).not.toHaveBeenCalled() - expect(mocks.ensureWebRuntimeWorktreeTerminalAfterWake).toHaveBeenCalledOnce() - expect(mocks.ensureWebRuntimeWorktreeTerminalAfterWake).toHaveBeenCalledWith('worktree-1', { - startup: undefined, - agent: 'codex', - activate: false, - unknownHostCountIsEmpty: true - }) - expect(mocks.completeWorktreeCreation).toHaveBeenCalledWith( - expect.objectContaining({ primaryTabId: null }) - ) - }) - - it('passes the agent selection through an active reveal', async () => { - store.activePendingCreationId = 'creation-1' - store.activeView = 'terminal' - mocks.activateAndRevealWorktree.mockReturnValue({ primaryTabId: null }) - - await executeWorktreeCreation('creation-1', request) - - expect(mocks.activateAndRevealWorktree).toHaveBeenCalledWith( - 'worktree-1', - expect.objectContaining({ agent: 'codex' }) - ) - }) -}) diff --git a/src/renderer/src/lib/worktree-default-terminal-tabs-layout-owner.test.ts b/src/renderer/src/lib/worktree-default-terminal-tabs-layout-owner.test.ts deleted file mode 100644 index c3fe9df5c06..00000000000 --- a/src/renderer/src/lib/worktree-default-terminal-tabs-layout-owner.test.ts +++ /dev/null @@ -1,116 +0,0 @@ -import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' -import { useAppStore, type AppState } from '@/store' -import { makeWorktree, seedStore } from '@/store/slices/store-test-helpers' -import { createStoreCascadesMockApi } from '@/store/slices/store-cascades-test-harness' -import { LayoutApplyParams } from '../../../shared/rpc-contract/workspace-layout-params' -import { applyDefaultTerminalTabs } from './worktree-default-terminal-tabs' - -const mocks = vi.hoisted(() => ({ - layoutOwnerIsMain: true, - callRuntimeRpc: vi.fn(async (_target: unknown, _method: string, _params: unknown) => ({ - ok: true, - result: {} - })) -})) - -vi.mock('../../../shared/layout-owner-switch', () => ({ - get ORCA_LAYOUT_OWNER_IS_MAIN() { - return mocks.layoutOwnerIsMain - } -})) -vi.mock('@/runtime/runtime-rpc-client', () => ({ callRuntimeRpc: mocks.callRuntimeRpc })) -vi.mock('sonner', () => ({ - toast: { info: vi.fn(), success: vi.fn(), error: vi.fn(), warning: vi.fn() } -})) - -createStoreCascadesMockApi() - -const WT = 'repo1::/path/wt1' -const NO_TEMPLATES = { runCommands: false, tabs: [] } - -const store = useAppStore -let initialState: AppState - -beforeEach(() => { - mocks.layoutOwnerIsMain = true - mocks.callRuntimeRpc.mockClear() - initialState = store.getState() - seedStore(store, { - worktreesByRepo: { repo1: [makeWorktree({ id: WT, repoId: 'repo1', path: '/path/wt1' })] }, - activeWorktreeId: WT - }) -}) - -afterEach(() => { - store.setState(initialState, true) -}) - -function applyNoTemplates(): string | null { - return applyDefaultTerminalTabs( - store.getState(), - WT, - undefined, - undefined, - undefined, - NO_TEMPLATES, - undefined, - undefined - ) -} - -function sentCommands() { - return mocks.callRuntimeRpc.mock.calls.map(([, method, params]) => { - expect(method).toBe('layout.apply') - return LayoutApplyParams.parse(params).command - }) -} - -describe('the default-tabs mark in a layout-owner build', () => { - it('records the mark in main when a workspace has no default tab templates', () => { - expect(applyNoTemplates()).toBeNull() - - expect(store.getState().tabsByWorktree[WT]).toBeUndefined() - expect(sentCommands()).toEqual([{ type: 'setDefaultTabsApplied', workspace: WT }]) - }) - - it('records the mark after the default tabs, so main never holds it without their rows', () => { - const tabId = applyDefaultTerminalTabs( - store.getState(), - WT, - undefined, - undefined, - undefined, - { runCommands: false, tabs: [{ title: 'one' }, { title: 'two' }] }, - undefined, - undefined - ) - - expect(tabId).not.toBeNull() - expect(sentCommands().map((command) => command.type)).toEqual([ - 'createTerminalTab', - 'renameTab', - 'createTerminalTab', - 'renameTab', - 'setDefaultTabsApplied' - ]) - }) - - it('records it once: a workspace already marked sends nothing', () => { - applyNoTemplates() - mocks.callRuntimeRpc.mockClear() - - store.getState().markDefaultTerminalTabsApplied(WT) - applyNoTemplates() - - expect(mocks.callRuntimeRpc).not.toHaveBeenCalled() - }) - - it('sends nothing with the switch off', () => { - mocks.layoutOwnerIsMain = false - - applyNoTemplates() - - expect(store.getState().defaultTerminalTabsAppliedByWorktreeId[WT]).toBe(true) - expect(mocks.callRuntimeRpc).not.toHaveBeenCalled() - }) -})