From 2bf43b7140ee2a1e4dc8696263a22b98ede5ae8b Mon Sep 17 00:00:00 2001 From: Neil <4138956+nwparker@users.noreply.github.com> Date: Tue, 27 Oct 2026 13:58:49 -0700 Subject: [PATCH] fix(automations): keep a desktop automation's dispatch token off a runtime host's create (#34525) A desktop-scheduled automation whose project lives on a managed Orca server sent its dispatch token to the server, which never minted it and rejected every run with "Invalid automation provenance request". The token now goes only to this desktop's own create. Fixes #34433 --- .../worktrees-create-captured-host.test.ts | 36 +++++++++++++++++++ .../create/worktree-create-payload.ts | 9 ++--- 2 files changed, 41 insertions(+), 4 deletions(-) diff --git a/src/renderer/src/store/slices/worktrees-create-captured-host.test.ts b/src/renderer/src/store/slices/worktrees-create-captured-host.test.ts index c87f6cb2e26..ec50f54780b 100644 --- a/src/renderer/src/store/slices/worktrees-create-captured-host.test.ts +++ b/src/renderer/src/store/slices/worktrees-create-captured-host.test.ts @@ -96,6 +96,42 @@ describe('background creation host capture', () => { }) }) + // #34433: the dispatch token lives in this desktop's main process, so a runtime host can only reject it. + it.each(['local', 'runtime:env-1'])( + 'sends the automation provenance request only to this desktop for %s', + async (executionHostId) => { + const store = createTestStore() + const worktree = makeWorktree({ id: 'repo1::/path/feature', repoId: 'repo1' }) + mockApi.worktrees.create.mockResolvedValue({ worktree }) + runtimeEnvironmentCall.mockResolvedValue({ + id: 'create', + ok: true, + result: { worktree }, + _meta: { runtimeId: 'runtime-remote' } + }) + store.setState({ repos: [], worktreesByRepo: { repo1: [] } }) + const automationProvenanceRequest = { + automationId: 'automation-1', + automationRunId: 'run-1', + dispatchToken: 'token-1', + createRequestId: 'create-1' + } + const createWorktree = store.getState().createWorktree + const args: Parameters = ['repo1', 'feature'] + args[25] = { executionHostId, automationProvenanceRequest } + await createWorktree(...args) + if (executionHostId === 'local') { + expect(mockApi.worktrees.create).toHaveBeenCalledWith( + expect.objectContaining({ automationProvenanceRequest }) + ) + } else { + const hostParams = runtimeEnvironmentCall.mock.calls[0]?.[0]?.params + expect(hostParams).toMatchObject({ repo: 'repo1' }) + expect(hostParams).not.toHaveProperty('automationProvenanceRequest') + } + } + ) + // #18097: the unresolved-owner sentinel must not ride the direct-SSH fallback onto this machine. it('refuses a create whose captured owner is the unresolved-owner sentinel', async () => { const store = createTestStore() diff --git a/src/renderer/src/store/slices/worktrees/create/worktree-create-payload.ts b/src/renderer/src/store/slices/worktrees/create/worktree-create-payload.ts index 48192acbba9..bdcc6a8fa22 100644 --- a/src/renderer/src/store/slices/worktrees/create/worktree-create-payload.ts +++ b/src/renderer/src/store/slices/worktrees/create/worktree-create-payload.ts @@ -113,9 +113,6 @@ function sharedCreateFields( ...(options?.linkedWorkItem !== undefined ? { linkedWorkItem: options.linkedWorkItem } : {}), ...(options?.linkedTaskSourceContext !== undefined ? { linkedTaskSourceContext: options.linkedTaskSourceContext } - : {}), - ...(options?.automationProvenanceRequest - ? { automationProvenanceRequest: options.automationProvenanceRequest } : {}) } } @@ -128,7 +125,11 @@ export function buildLocalWorktreeCreateArgs( repoId: request.repoId, ...sharedCreateFields(request, attempt), ...(request.startup ? { startup: request.startup } : {}), - ...(request.creationId ? { creationId: request.creationId } : {}) + ...(request.creationId ? { creationId: request.creationId } : {}), + // Why local only: this desktop's main minted the dispatch token, so a runtime host must reject it. + ...(request.options?.automationProvenanceRequest + ? { automationProvenanceRequest: request.options.automationProvenanceRequest } + : {}) } }