From ce4f4da809d3a370d261f008e17c6b5ee5843b93 Mon Sep 17 00:00:00 2001 From: Brennan Benson <79079362+brennanb2025@users.noreply.github.com> Date: Wed, 16 Sep 2026 15:11:13 -0700 Subject: [PATCH] fix(orchestration): stop the sender-terminal refusal recommending another pane's handle MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The structured-session guard told callers to pass `--from `, but the explicit-flag branch returns before that guard runs — so following the advice succeeds, against a handle that necessarily belongs to a different pane, and the next `check` consumes that pane's unread mail. Both refusals now say what is actually true: no handle names a structured chat session, and a caller that does have one should pass its own. Also pins ORCA_STRUCTURED_SESSION in the gate CLI test, which until now decided which refusal it exercised from ambient environment. --- src/cli/handlers/orchestration-gate-cli.test.ts | 7 ++++++- src/cli/handlers/orchestration/terminal-identity.ts | 11 ++++++++--- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/src/cli/handlers/orchestration-gate-cli.test.ts b/src/cli/handlers/orchestration-gate-cli.test.ts index a2793ac9323..217e2f359f1 100644 --- a/src/cli/handlers/orchestration-gate-cli.test.ts +++ b/src/cli/handlers/orchestration-gate-cli.test.ts @@ -34,6 +34,9 @@ import { okFixture, queueFixtures } from '../test-fixtures' const originalTerminalHandle = process.env.ORCA_TERMINAL_HANDLE const originalPaneKey = process.env.ORCA_PANE_KEY +// Why: a structured-session marker inherited from the runner diverts these cases to the +// structured refusal, so which branch they exercise would depend on who ran them. +const originalStructuredSession = process.env.ORCA_STRUCTURED_SESSION const restoreEnv = (name: string, value: string | undefined): void => { if (value === undefined) { @@ -54,6 +57,7 @@ describe('orchestration gate commands carry caller identity', () => { errorSpy = vi.spyOn(console, 'error').mockImplementation(() => {}) delete process.env.ORCA_TERMINAL_HANDLE delete process.env.ORCA_PANE_KEY + delete process.env.ORCA_STRUCTURED_SESSION process.exitCode = 0 }) @@ -62,6 +66,7 @@ describe('orchestration gate commands carry caller identity', () => { errorSpy.mockRestore() restoreEnv('ORCA_TERMINAL_HANDLE', originalTerminalHandle) restoreEnv('ORCA_PANE_KEY', originalPaneKey) + restoreEnv('ORCA_STRUCTURED_SESSION', originalStructuredSession) process.exitCode = 0 }) @@ -192,7 +197,7 @@ describe('orchestration gate commands carry caller identity', () => { expect(process.exitCode).toBe(1) const stderr = errorSpy.mock.calls.map((call) => String(call[0])).join('\n') - expect(stderr).toContain('Pass --from ') + expect(stderr).toContain("Pass --from with your own terminal's handle") expect(callMock).not.toHaveBeenCalledWith('orchestration.gateCreate', expect.anything()) }) diff --git a/src/cli/handlers/orchestration/terminal-identity.ts b/src/cli/handlers/orchestration/terminal-identity.ts index 505bfaac262..83097e02126 100644 --- a/src/cli/handlers/orchestration/terminal-identity.ts +++ b/src/cli/handlers/orchestration/terminal-identity.ts @@ -36,10 +36,14 @@ export async function resolveOrchestrationTerminalHandle( // rightful worker never saw its mail. Refusing is the only honest answer: this child genuinely // cannot infer its own identity. if (isStructuredSessionWithoutIdentity()) { + // Why not suggest --${flagName}: the explicit-flag branch above returns before this guard, so + // that advice succeeds — against a handle that necessarily belongs to another pane. throw new RuntimeClientError( 'no_active_sender_terminal', - `This chat session has no orchestration identity of its own, so --${flagName} cannot be inferred. ` + - `Pass --${flagName} explicitly; guessing would act on another pane's mailbox.` + `This chat session has no orchestration identity of its own, so --${flagName} cannot be inferred, ` + + `and no terminal handle names it — every live handle belongs to a different pane, whose mailbox ` + + `passing it would consume. Drive a worker directly instead: create a worktree, create a terminal ` + + `in it with an agent, then use terminal send and terminal read.` ) } if (flagName === 'from') { @@ -192,6 +196,7 @@ export function throwNoActiveSenderTerminal(): never { throw new RuntimeClientError( 'no_active_sender_terminal', 'Could not determine the sender terminal for this orchestration command. ' + - 'Pass --from or run the command inside a live Orca terminal with ORCA_TERMINAL_HANDLE set.' + "Pass --from with your own terminal's handle — another pane's handle would act on its mailbox — " + + 'or run the command inside a live Orca terminal with ORCA_TERMINAL_HANDLE set.' ) }