test: retire cases whose predicates never read the varied input (#23965)

* test: retire provider cross-products and mock-delegate tests in store, hooks and mobile transport

Semantic sweep of renderer store/hooks and mobile/src/transport. 8 cases and one
file removed across 8 files; no production file touched.

`full-creation-structured-launch.test.ts` is deleted whole. Its subject,
`beginFullCreationStructuredLaunch`, is a four-line forward to
`beginStructuredAgentSessionProvisionalLaunch` with a fixed argument shape, and
the test mocked exactly that inner call — so the asserted `['begin','reveal','open']`
array was pushed entirely by the mock's own `mockImplementation`, and the second
case returned the mock's `null`. The symbol itself stays; `full-creation-execution.ts:233`
still calls it. The ordering guard against real orchestration code lives in
`lib/worktree-creation-structured-session.test.ts`.

Provider cross-products over paths with no provider branch:
`issue-source-actions.ts:213-240` nulls every field unconditionally, its only
branches being `baseBranchNamesWorkspace`, `name === lastAutoNameRef` and
`noteRef === lastAutoNoteRef` — none provider-dependent. The github/gitlab rows and
the linear/jira block differed only in a selection label, which
`shared/new-workspace/workspace-source.test.ts:107` owns. The github-pr row stays:
it is the only one entering with a non-null `smartGitHubPrStartPointSelectionRef`,
which is the documented reason that reset exists.

Also removed: two discovery cases recombining a single key derivation
(`installed-agent-skill-discovery.ts:207`); a sustained-failure case where only one
write ever occurs, so `mockRejectedValue` and `mockRejectedValueOnce` reach
identical code; a `keeps plain labels when no endpoint is provided` replay of
`isTailscaleEndpoint(undefined)`, owned at `remote-runtime-tailscale-hint.test.ts:44`;
a 1000-host fanout case whose expectation is the output of the helper under test,
with no count-dependent branch.

Kept where the inputs differ even though the assertions repeat: the cellular
escalation trio drives three distinct rpc-client failure paths (connect timeout,
silence after upgrade, a `close()` that never fires `onclose`); the five
connection-log redaction cases map to five distinct regexes in a module with no
test of its own; the `it.each([false, true])` settlement table lands on opposite
sides of `while (dirtyHosts.delete(hostId))`; and the case deleting
`Array.prototype.toSorted` is an engine-compat ratchet, since Hermes lacks it.

* test: retire composer cases whose predicates never read the varied input

Completes the wave-11 sweep of renderer hooks. Four composer files trimmed; no
production file touched.

Each removed case varies something the production path never inspects:
- "treats a slash-containing local branch as reusable" — `resolveComposerBranchReuse`
  never looks at `/`;
- the empty-stack drop-owner case — `at(-1)` has no branch to take;
- "passes a free-form reason straight through" — `compactIpcErrorMessage` is an
  identity on that input, owned by `lib/ipc-error.test.ts`;
- "gives no reason at all when the batch failed for differing reasons" — passes no
  `commonFailure` at all, so its input shape is identical to the case above it;
- "keeps the current request pending until it settles" — asserts `await` semantics
  rather than anything production decides, so no regression can fail it.

Kept deliberately: "does give the shared reason when every path failed the same
way", because removing it would leave `attachment-drop-state.ts:188` — the upload
path's `commonFailure` wiring — with no check at all; the local-path assertion
only covers line 248.
This commit is contained in:
Neil
2026-09-29 18:41:55 -07:00
committed by GitHub
parent 26bb7c23f1
commit cd63c998ab
16 changed files with 0 additions and 645 deletions
@@ -644,40 +644,6 @@ describe('useAllHostClients', () => {
}
})
it('keeps startup connection fanout constant for a large saved-host list', async () => {
const hosts = Array.from({ length: 1_000 }, (_, index) => ({
...HOST,
id: `host-${index}`,
name: `Host ${index}`,
lastConnected: index
}))
const hostIds = hosts.map((host) => host.id)
const autoConnectHostIds = selectHomeAutoConnectHostIds(hosts)
connectMock.mockReturnValue(makeFakeClient('connected'))
loadHostsMock.mockResolvedValue(hosts)
let renderer: ReactTestRenderer | null = null
function Probe(): null {
useAllHostClients(hostIds, { autoConnectHostIds })
return null
}
try {
await act(async () => {
renderer = create(createElement(RpcClientProvider, null, createElement(Probe)))
await Promise.resolve()
})
expect(connectMock).toHaveBeenCalledTimes(3)
expect(connectMock.mock.calls.map(([host]) => host.id)).toEqual([
'host-999',
'host-998',
'host-997'
])
} finally {
act(() => renderer?.unmount())
}
})
it('closes a demoted Home client when the recent-host set rotates', async () => {
const hosts = [
{ ...HOST, id: 'host-a', lastConnected: 4 },
@@ -80,12 +80,6 @@ describe('classifyConnection Tailscale hint', () => {
expect('hint' in warning && warning.hint).toBeFalsy()
})
it('keeps plain labels when no endpoint is provided', () => {
const verdict = classifyConnection({ ...base, reconnectAttempts: 3 })
expect(verdict.kind).toBe('warning')
expect('hint' in verdict && verdict.hint).toBeFalsy()
})
it('never hints on healthy states', () => {
const verdict = classifyConnection({
state: 'connected',
@@ -100,22 +100,4 @@ describe('connection log persistence revisions', () => {
expect(save).toHaveBeenCalledTimes(2)
expect(save).toHaveBeenLastCalledWith('a', store.get('a'))
})
it('writes once per snapshot during a sustained failure', async () => {
const save = vi.fn<ConnectionLogPersistence['save']>(async () => {})
const store = createConnectionLogStore(200, { load: async () => [], save })
await store.hydrate('a')
await drain()
save.mockReset().mockRejectedValue(new Error('unavailable'))
for (let i = 0; i < 3; i++) {
store.append('a', entry(i))
}
await drain()
expect(save).toHaveBeenCalledTimes(1)
save.mockClear().mockResolvedValue(undefined)
store.append('a', entry(3))
await drain()
expect(save).toHaveBeenCalledTimes(1)
expect(save).toHaveBeenLastCalledWith('a', store.get('a'))
})
})
@@ -115,19 +115,6 @@ function suspend(backgroundMs: number): void {
vi.setSystemTime(Date.now() + backgroundMs)
}
// Steps the clock one second at a time and reports how long the user has to sit
// on "Connecting…" before the client opens its next socket.
async function millisecondsUntilNextDial(dialsBefore: number): Promise<number> {
const limitMs = 180_000
for (let elapsedMs = 0; elapsedMs <= limitMs; elapsedMs += 1_000) {
if (sockets.length > dialsBefore) {
return elapsedMs
}
await vi.advanceTimersByTimeAsync(1_000)
}
return limitMs
}
// Waits out the current backoff so the phone is suspended mid-dial — the state
// the reporter's phone resumed into — rather than between dials.
async function advanceUntilDialing(client: ReturnType<typeof connect>): Promise<void> {
@@ -181,36 +168,6 @@ describe('foregrounding a phone that was suspended mid-dial', () => {
expect(client.getState()).toBe('connected')
})
it('does not strand the user on "Connecting…" for a minute after returning', async () => {
const client = connect(TAILSCALE_ENDPOINT, 'token', 'server-key')
latest().authenticate()
latest().close()
// The desktop drops off the network while the user is still in the app, so
// the tiered backoff climbs into its slow tail (15s, 30s, 60s) before they
// ever leave. This is what makes the post-resume wait a full minute.
for (let dial = 0; dial < 6; dial++) {
await vi.advanceTimersByTimeAsync(12_000 + 60_000)
}
expect(client.getReconnectAttempt()).toBeGreaterThanOrEqual(6)
await advanceUntilDialing(client)
suspend(90_000)
const dialsBefore = sockets.length
client.notifyForeground()
// The desktop is reachable again and the user is looking at the screen, so
// the redial has to be in flight now — not after the abandoned socket's
// connect budget expires and another tail-length backoff is waited out.
expect(await millisecondsUntilNextDial(dialsBefore)).toBe(0)
latest().authenticate()
expect(client.getState()).toBe('connected')
expect(label(client, TAILSCALE_ENDPOINT)).toBe('Connected')
client.close()
})
it('clears the escalated label once the foreground redial lands', async () => {
const client = connect(TAILSCALE_ENDPOINT, 'token', 'server-key')
latest().authenticate()
@@ -111,21 +111,6 @@ describe('host credential cleanup', () => {
expect(deleteCredential).toHaveBeenCalledOnce()
})
it('returns after durable intent without waiting for the native delete', async () => {
vi.useFakeTimers()
const deleteCredential = vi.fn(() => new Promise<void>(() => undefined))
await expect(
scheduleHostCredentialCleanup('host-1', deleteCredential, 3_000)
).resolves.toBeUndefined()
await flushMicrotasks()
expect(storedPendingIds).toEqual(['host-1'])
expect(deleteCredential).toHaveBeenCalledOnce()
await vi.advanceTimersByTimeAsync(3_000)
})
it('clears a timed-out pending entry when the native delete later succeeds', async () => {
vi.useFakeTimers()
let resolveDelete: (() => void) | null = null
@@ -217,53 +217,4 @@ describe('Claude background-turn completion notifications', () => {
})
expect(banners[0]?.stateStartedAt).not.toBe(banners[1]?.stateStartedAt)
})
it('notifies every turn under a persistent session cron with distinct ids', async () => {
const cron = [{ id: 'cron-1' }]
const { banners } = await play([
{
at: 1_700_000_000_000,
payload: { hook_event_name: 'UserPromptSubmit', prompt: 'first' }
},
{
at: 1_700_000_001_000,
payload: {
hook_event_name: 'Stop',
last_assistant_message: 'First done.',
session_crons: cron
}
},
{
at: 1_700_000_002_000,
payload: { hook_event_name: 'UserPromptSubmit', prompt: 'second' }
},
{
at: 1_700_000_003_000,
payload: {
hook_event_name: 'Stop',
last_assistant_message: 'Second done.',
session_crons: cron
}
},
{
at: 1_700_000_004_000,
payload: { hook_event_name: 'UserPromptSubmit', prompt: 'third' }
},
{
at: 1_700_000_005_000,
payload: {
hook_event_name: 'Stop',
last_assistant_message: 'Third done.',
session_crons: cron
}
}
])
expect(banners.map((banner) => banner.body)).toEqual([
'First done.',
'Second done.',
'Third done.'
])
expect(new Set(banners.map((banner) => banner.stateStartedAt)).size).toBe(3)
})
})
@@ -150,14 +150,6 @@ describe('Grok hook completion notifications', () => {
})
})
it('keeps the captured SessionEnd and shutdown Stop tail silent', async () => {
const shutdownTail = capturedHooks.filter(
(hook) => hook.reason === 'shutdown' || hook.hookEventName === 'session_end'
)
expect(await play(shutdownTail)).toHaveLength(0)
})
it.each([
{
eventName: 'StopFailure',
@@ -205,43 +197,6 @@ describe('Grok hook completion notifications', () => {
})
})
// Pins grok-events.ts finite-task allowlist; broadening it to monitors or sessionCrons must redden.
it.each([
{
label: 'monitor',
backgroundTasks: [
{ id: 'monitor-1', type: 'monitor', status: 'running', description: 'watch the build' }
],
sessionCrons: []
},
{
label: 'cron',
backgroundTasks: [],
sessionCrons: [
{ id: 'cron-1', schedule: 'every minute', recurring: true, prompt: 'check the build' }
]
}
])('announces with only a running $label outstanding', async (scenario) => {
const notifications = await play([
{
hookEventName: 'UserPromptSubmit',
timestamp: '2026-09-12T03:01:00.000Z',
prompt: 'finish the request'
},
{
hookEventName: 'Stop',
timestamp: '2026-09-12T03:01:01.000Z',
reason: 'end_turn',
stopHookActive: false,
backgroundTasks: scenario.backgroundTasks,
sessionCrons: scenario.sessionCrons
}
])
vi.advanceTimersByTime(1_500)
expect(notifications).toHaveLength(1)
})
it('stays silent while a background subagent outlives the main agent, then announces once', async () => {
const turn = (timestamp: string, promptId: string, backgroundTasks: unknown[]) => [
{ hookEventName: 'UserPromptSubmit', timestamp, sessionId: 'session-1', promptId },
@@ -277,35 +232,6 @@ describe('Grok hook completion notifications', () => {
})
})
it('does not announce a delayed cancellation after the next prompt starts', async () => {
const notifications = await play([
{
hookEventName: 'UserPromptSubmit',
timestamp: '2026-09-12T03:02:00.000Z',
sessionId: 'session-1',
promptId: 'prompt-old',
prompt: 'old turn'
},
{
hookEventName: 'UserPromptSubmit',
timestamp: '2026-09-12T03:02:01.000Z',
sessionId: 'session-1',
promptId: 'prompt-new',
prompt: 'new turn'
},
{
hookEventName: 'StopCancelled',
timestamp: '2026-09-12T03:02:02.000Z',
sessionId: 'session-1',
promptId: 'prompt-old',
reason: 'user_interrupt'
}
])
vi.advanceTimersByTime(1_500)
expect(notifications).toHaveLength(0)
})
it('announces once from idle_prompt after repeated continuation Stops', async () => {
const notifications = await play([
{
@@ -230,28 +230,6 @@ describe('agent hook completion notifications', () => {
expect(dispatchTerminalNotification).toHaveBeenCalledTimes(1)
})
it('tracks hook completion for terminal attention when OS completion notifications are disabled', async () => {
mockStoreState.settings.experimentalTerminalAttention = true
mockStoreState.settings.notifications.agentTaskComplete = false
const { observeAgentHookCompletionForNotification } =
await import('./agent-hook-completion-notifications')
observeAgentHookCompletionForNotification({
paneKey,
worktreeId: 'wt-1',
payload: hookStatus('done')
})
vi.advanceTimersByTime(HOOK_DONE_QUIET_MS)
expect(dispatchTerminalNotification).toHaveBeenCalledWith(
'wt-1',
expect.objectContaining({
source: 'agent-task-complete',
paneKey
})
)
}, 15_000)
it('uses tab-level PTY liveness when an inactive pane leaf binding is temporarily missing', async () => {
mockStoreState.terminalLayoutsByTabId = {
'tab-1': {
@@ -372,35 +350,6 @@ describe('agent hook completion notifications', () => {
)
})
it('carries hook stateStartedAt into delayed completion notifications', async () => {
const { observeAgentHookCompletionForNotification } =
await import('./agent-hook-completion-notifications')
observeAgentHookCompletionForNotification({
paneKey,
worktreeId: 'wt-1',
payload: { ...hookStatus('working'), stateStartedAt: 1_700_000_000_000 }
})
observeAgentHookCompletionForNotification({
paneKey,
worktreeId: 'wt-1',
payload: { ...hookStatus('done'), stateStartedAt: 1_700_000_010_000 }
})
vi.advanceTimersByTime(HOOK_DONE_QUIET_MS)
expect(dispatchTerminalNotification).toHaveBeenCalledWith(
'wt-1',
expect.objectContaining({
source: 'agent-task-complete',
paneKey,
agentStatusSnapshot: expect.objectContaining({
state: 'done',
stateStartedAt: 1_700_000_010_000
})
})
)
})
it('does not fire a completion notification for a session-boundary done row', async () => {
const { observeAgentHookCompletionForNotification } =
await import('./agent-hook-completion-notifications')
@@ -438,34 +387,6 @@ describe('agent hook completion notifications', () => {
expect(dispatchTerminalNotification).toHaveBeenCalledTimes(1)
})
it('does not notify twice when the same done hook snapshot replays after activation', async () => {
const { observeAgentHookCompletionForNotification } =
await import('./agent-hook-completion-notifications')
observeAgentHookCompletionForNotification({
paneKey,
worktreeId: 'wt-1',
payload: { ...hookStatus('working'), stateStartedAt: 1_700_000_000_000 }
})
observeAgentHookCompletionForNotification({
paneKey,
worktreeId: 'wt-1',
payload: { ...hookStatus('done'), stateStartedAt: 1_700_000_010_000 }
})
vi.advanceTimersByTime(HOOK_DONE_QUIET_MS)
expect(dispatchTerminalNotification).toHaveBeenCalledTimes(1)
observeAgentHookCompletionForNotification({
paneKey,
worktreeId: 'wt-1',
payload: { ...hookStatus('done'), stateStartedAt: 1_700_000_010_000 }
})
vi.advanceTimersByTime(HOOK_DONE_QUIET_MS)
expect(dispatchTerminalNotification).toHaveBeenCalledTimes(1)
})
it('prunes retained coordinators when pane liveness is removed from the store', async () => {
const {
_getAgentHookCompletionNotificationCoordinatorCountForTest,
@@ -518,45 +439,6 @@ describe('agent hook completion notifications', () => {
expect(dispatchTerminalNotification).not.toHaveBeenCalled()
})
it('does not notify on each Cursor shell tool hook during a working turn', async () => {
const { observeAgentHookCompletionForNotification } =
await import('./agent-hook-completion-notifications')
observeAgentHookCompletionForNotification({
paneKey,
worktreeId: 'wt-1',
payload: {
state: 'working',
prompt: 'fix the bug',
agentType: 'cursor'
}
})
observeAgentHookCompletionForNotification({
paneKey,
worktreeId: 'wt-1',
payload: {
state: 'working',
prompt: 'fix the bug',
agentType: 'cursor',
toolName: 'Shell',
toolInput: 'pnpm test'
}
})
observeAgentHookCompletionForNotification({
paneKey,
worktreeId: 'wt-1',
payload: {
state: 'working',
prompt: 'fix the bug',
agentType: 'cursor',
toolName: 'Read',
toolInput: '/repo/src/app.ts'
}
})
expect(dispatchTerminalNotification).not.toHaveBeenCalled()
})
it('notifies when a Claude permission request needs input without completing the task', async () => {
const { observeAgentHookCompletionForNotification } =
await import('./agent-hook-completion-notifications')
@@ -617,13 +499,6 @@ describe('agent hook completion notifications', () => {
)
})
it('notifies for a blocked Codex permission request', async () => {
seedCodexPane(paneKey)
await observeCodexPermissionPause('blocked')
expect(dispatchTerminalNotification).toHaveBeenCalledTimes(1)
})
it('does not notify on Grok routine permission prompt notifications during tool use', async () => {
const { observeAgentHookCompletionForNotification } =
await import('./agent-hook-completion-notifications')
@@ -701,68 +576,6 @@ describe('agent hook completion notifications', () => {
)
})
it('suppresses an internal milestone completion when hook work resumes before quiet', async () => {
const { observeAgentHookCompletionForNotification } =
await import('./agent-hook-completion-notifications')
observeAgentHookCompletionForNotification({
paneKey,
worktreeId: 'wt-1',
payload: hookStatus('working')
})
observeAgentHookCompletionForNotification({
paneKey,
worktreeId: 'wt-1',
payload: hookStatus('done')
})
vi.advanceTimersByTime(HOOK_DONE_QUIET_MS - 1)
expect(dispatchTerminalNotification).not.toHaveBeenCalled()
expect(
dispatchAgentHookTerminalLifecycle.mock.calls.filter(
([, payload]) => payload.state === 'done'
)
).toHaveLength(0)
observeAgentHookCompletionForNotification({
paneKey,
worktreeId: 'wt-1',
payload: hookStatus('working')
})
vi.advanceTimersByTime(HOOK_DONE_QUIET_MS)
expect(dispatchTerminalNotification).not.toHaveBeenCalled()
expect(
dispatchAgentHookTerminalLifecycle.mock.calls.filter(
([, payload]) => payload.state === 'done'
)
).toHaveLength(0)
observeAgentHookCompletionForNotification({
paneKey,
worktreeId: 'wt-1',
payload: hookStatus('done')
})
vi.advanceTimersByTime(HOOK_DONE_QUIET_MS)
expect(dispatchTerminalNotification).toHaveBeenCalledTimes(1)
expect(dispatchAgentHookTerminalLifecycle).toHaveBeenCalledWith(
paneKey,
expect.objectContaining({ state: 'done', agentType: 'codex' })
)
expect(dispatchTerminalNotification).toHaveBeenCalledWith(
'wt-1',
expect.objectContaining({
source: 'agent-task-complete',
paneKey,
agentStatusSnapshot: expect.objectContaining({
state: 'done',
agentType: 'codex',
prompt: 'implement notifications',
lastAssistantMessage: 'Done.'
})
})
)
})
const MANY_PANES = [
{ tabId: 'tab-1', leafId: '11111111-1111-4111-8111-111111111111', ptyId: 'pty-1' },
{ tabId: 'tab-2', leafId: '22222222-2222-4222-8222-222222222222', ptyId: 'pty-2' },
@@ -174,23 +174,4 @@ describe('agent hook completion store sync', () => {
})
expect(shouldSyncAgentHookCompletionForStoreUpdate(reorderedTabs, twoTabPrevious)).toBe(true)
})
it('compares effective tracking state instead of unrelated settings identity', () => {
const previous = createState({
settings: {
experimentalTerminalAttention: true,
notifications: { enabled: false, agentTaskComplete: false }
}
})
const stillTrackedByNotifications = createState({
...previous,
settings: {
experimentalTerminalAttention: false,
notifications: { enabled: true, agentTaskComplete: true }
}
})
expect(shouldSyncAgentHookCompletionForStoreUpdate(stillTrackedByNotifications, previous)).toBe(
false
)
})
})
@@ -274,17 +274,6 @@ describe('resolveComposerBranchReuse', () => {
).toEqual({ reuseEligibleBranch: 'feature-x', defaultReuse: true })
})
it('treats a slash-containing local branch as reusable (ref equals local name)', () => {
expect(
resolveComposerBranchReuse({
refName: 'fix/bug-0',
localBranchName: 'fix/bug-0',
selectionProducedOverride: true,
branchCheckedOutElsewhere: false
})
).toEqual({ reuseEligibleBranch: 'fix/bug-0', defaultReuse: true })
})
it('does not offer reuse for a remote-only ref (ref carries an origin/ prefix)', () => {
expect(
resolveComposerBranchReuse({
@@ -48,15 +48,6 @@ describe('showComposerDropFailureToast', () => {
}
})
it('passes a free-form failure reason straight through', () => {
showComposerDropFailureToast({
failureCount: 2,
total: 4,
commonFailure: { status: 'failed', reason: 'EACCES: permission denied' }
})
expect(lastToast().description).toBe('EACCES: permission denied')
})
it('shows no description when nothing explained the failure', () => {
showComposerDropFailureToast({ failureCount: 1, total: 2 })
expect(lastToast().description).toBeUndefined()
@@ -82,10 +73,4 @@ describe('showComposerDropFailureToast', () => {
expect(first).toBeDefined()
expect(lastToast().id).toBe(first)
})
it('gives no reason at all when the batch failed for differing reasons', () => {
showComposerDropFailureToast({ failureCount: 3, total: 6 })
expect(lastToast().title).toBe('3 of 6 items could not be attached.')
expect(lastToast().description).toBeUndefined()
})
})
@@ -10,10 +10,4 @@ describe('composer drop owner', () => {
expect(isCurrentComposerDropOwner([page, modal], page)).toBe(false)
expect(isCurrentComposerDropOwner([page, modal], modal)).toBe(true)
})
it('rejects async drop completions after their owner unmounts', () => {
const owner = Symbol('owner')
expect(isCurrentComposerDropOwner([], owner)).toBe(false)
})
})
@@ -139,21 +139,6 @@ describe('local composer drop lifetime', () => {
expect(hook.attach).not.toHaveBeenCalled()
})
it('keeps the current request pending until it settles', async () => {
const gate = Promise.withResolvers<void>()
authorize.mockImplementationOnce(() => gate.promise)
const hook = renderDrop()
const settled = vi.fn()
const pending = hook.result.current.applyLocalComposerDrop(['/drop/one']).then(settled)
hook.unmount()
await Promise.resolve()
expect(settled).not.toHaveBeenCalled()
gate.resolve()
await pending
expect(settled).toHaveBeenCalledOnce()
expect(stat).not.toHaveBeenCalled()
})
it('preserves mixed results, order, duplicate filtering and one failure report', async () => {
const order: string[] = []
authorize.mockImplementation(async ({ targetPath }) => {
@@ -1,72 +0,0 @@
import { beforeEach, describe, expect, it, vi } from 'vitest'
type BeginArgs = {
plan: unknown
target: { worktreeId: string }
beforeOpen?: (sessionId: string) => boolean | void
}
const mocks = vi.hoisted(() => ({
beginStructuredAgentSessionProvisionalLaunch:
vi.fn<(args: BeginArgs) => { sessionId: string; tab: { id: string } } | null>()
}))
vi.mock('@/lib/structured-agent-session-provisional-tab', () => ({
beginStructuredAgentSessionProvisionalLaunch: mocks.beginStructuredAgentSessionProvisionalLaunch
}))
import { adoptAgentSessionLaunchVerdict } from '@/lib/agent-session-launch-plan'
import { beginFullCreationStructuredLaunch } from './full-creation-structured-launch'
const plan = adoptAgentSessionLaunchVerdict({
route: 'structured-native-chat',
agent: 'codex',
prompt: 'Fix the route',
promptDelivery: 'auto-submit'
})
describe('beginFullCreationStructuredLaunch', () => {
beforeEach(() => {
vi.clearAllMocks()
mocks.beginStructuredAgentSessionProvisionalLaunch.mockImplementation((args) => {
args.beforeOpen?.('session-1')
return { sessionId: 'session-1', tab: { id: 'agent-session:session-1' } }
})
})
it('allocates the final identity before revealing and opening the chat surface', () => {
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' } }
})
const launch = beginFullCreationStructuredLaunch({
plan,
worktreeId: 'worktree-1',
beforeOpen: (sessionId) => {
order.push(`reveal:${sessionId}`)
return true
}
})
expect(launch).toMatchObject({ sessionId: 'session-1', tab: { id: 'agent-session:session-1' } })
expect(order).toEqual(['begin', 'reveal:session-1', 'open'])
expect(mocks.beginStructuredAgentSessionProvisionalLaunch).toHaveBeenCalledWith({
plan,
hooks: {},
target: { worktreeId: 'worktree-1' },
beforeOpen: expect.any(Function)
})
})
it('returns no surface when reveal or ownership is refused', () => {
mocks.beginStructuredAgentSessionProvisionalLaunch.mockReturnValue(null)
expect(
beginFullCreationStructuredLaunch({ plan, worktreeId: 'worktree-1', beforeOpen: vi.fn() })
).toBeNull()
})
})
@@ -15,20 +15,6 @@ const sources: LinkedWorkItemSummary[] = [
number: 42,
title: 'Fix checkout',
url: 'https://github.com/acme/app/pull/42'
},
{
provider: 'github',
type: 'issue',
number: 43,
title: 'Fix checkout',
url: 'https://github.com/acme/app/issues/43'
},
{
provider: 'gitlab',
type: 'mr',
number: 44,
title: 'Fix checkout',
url: 'https://gitlab.com/acme/app/-/merge_requests/44'
}
]
@@ -135,36 +121,4 @@ describe('create more source reset', () => {
expect(result.current.branchAutoNameRef.current).toBe('')
}
)
it.each(['linear', 'jira'] as const)('clears a %s task on a folder target', (provider) => {
const item: LinkedWorkItemSummary = {
provider,
type: 'issue',
number: 0,
title: 'Fix checkout',
url:
provider === 'linear'
? 'https://linear.app/acme/issue/APP-45'
: 'https://acme.atlassian.net/browse/APP-45'
}
const { result } = renderHook(() => useSelectedSourceReset(item, true))
expect(result.current.selection?.kind).toBe(provider)
act(() => result.current.resetForNextCreate())
expect(result.current.selection).toBeNull()
expect(result.current.linkedWorkItem).toBeNull()
expect(result.current.name).toBe('')
expect(result.current.note).toBe('')
})
it('clears a plain branch selection before the next create', () => {
const { result } = renderHook(() => useSelectedSourceReset(null, false, 'feature/checkout'))
expect(result.current.selection).toEqual({ kind: 'branch', label: 'feature/checkout' })
act(() => result.current.resetForNextCreate())
expect(result.current.selection).toBeNull()
expect(result.current.baseBranch).toBeUndefined()
})
})
@@ -204,23 +204,6 @@ describe('installed agent skill discovery lifecycle', () => {
expect(discover).toHaveBeenLastCalledWith(LOCAL, { projectRuntime: repairProjectRuntime })
})
it('keys WSL targets by distro so two distros do not share one entry', async () => {
const discover = discoverSkillsForRuntimeTarget
discover.mockResolvedValueOnce(result(1))
discover.mockResolvedValueOnce(result(2))
await expect(
discoverInstalledAgentSkills(false, { runtime: 'wsl', wslDistro: 'Ubuntu' })
).resolves.toEqual(result(1))
await expect(
discoverInstalledAgentSkills(false, { runtime: 'wsl', wslDistro: 'Debian' })
).resolves.toEqual(result(2))
await expect(
discoverInstalledAgentSkills(false, { runtime: 'wsl', wslDistro: 'Ubuntu' })
).resolves.toEqual(result(1))
expect(discover).toHaveBeenCalledTimes(2)
})
it('keys targets by runtime and project identity', () => {
expect(getSkillDiscoveryTargetKey(undefined)).toBe('host')
expect(getSkillDiscoveryTargetKey({ runtime: 'wsl', wslDistro: 'Ubuntu' })).toBe('wsl:Ubuntu')
@@ -254,24 +237,6 @@ describe('installed agent skill discovery lifecycle', () => {
)
})
it('isolates project-runtime caches by target-contained filters', async () => {
discoverSkillsForRuntimeTarget.mockResolvedValueOnce(result(1)).mockResolvedValueOnce(result(2))
await expect(
discoverInstalledAgentSkills(false, {
projectRuntime: resolvedWslProjectRuntime,
names: ['orchestration']
})
).resolves.toEqual(result(1))
await expect(
discoverInstalledAgentSkills(false, {
projectRuntime: resolvedWslProjectRuntime,
names: ['computer-use']
})
).resolves.toEqual(result(2))
expect(discoverSkillsForRuntimeTarget).toHaveBeenCalledTimes(2)
})
it('keys the bounded cache by runtime scope, not by the client target', async () => {
// Why: #6887 scopes remote scans by environment. The cap rewrites this same
// module, so pin that getRuntimeScopedSkillDiscoveryKey stays the producer —