diff --git a/apps/server/src/providers/codex-provider.ts b/apps/server/src/providers/codex-provider.ts index 3c8979f6..f4211b4f 100644 --- a/apps/server/src/providers/codex-provider.ts +++ b/apps/server/src/providers/codex-provider.ts @@ -845,11 +845,9 @@ export class CodexProvider extends BaseProvider { options.model, CODEX_JSON_FLAG, ...configOverrideArgs, + ...(schemaPath ? [CODEX_OUTPUT_SCHEMA_FLAG, schemaPath] : []), '-', // Read prompt from stdin to avoid shell escaping issues ]; - if (schemaPath) { - args.push(CODEX_OUTPUT_SCHEMA_FLAG, schemaPath); - } const envOverrides = buildEnv(); if (executionPlan.openAiApiKey && !envOverrides[OPENAI_API_KEY_ENV]) { diff --git a/apps/server/src/services/auto-mode/facade.ts b/apps/server/src/services/auto-mode/facade.ts index c63d9889..2d8e9c9e 100644 --- a/apps/server/src/services/auto-mode/facade.ts +++ b/apps/server/src/services/auto-mode/facade.ts @@ -519,15 +519,22 @@ export class AutoModeServiceFacade { useWorktrees = false, _calledInternally = false ): Promise { - // Do not call handleFacadeError here: ExecutionService.executeFeature already - // classifies and emits auto_mode_error, so calling handleFacadeError would - // produce duplicate error events. Simply let the error propagate to the caller. - return await this.recoveryService.resumeFeature( - this.projectPath, - featureId, - useWorktrees, - _calledInternally - ); + // Note: ExecutionService.executeFeature catches its own errors internally and + // does NOT re-throw them (it emits auto_mode_error and returns normally). + // Therefore, errors that reach this catch block are pre-execution failures + // (e.g., feature not found, context read error) that ExecutionService never + // handled — so calling handleFacadeError here does NOT produce duplicate events. + try { + return await this.recoveryService.resumeFeature( + this.projectPath, + featureId, + useWorktrees, + _calledInternally + ); + } catch (error) { + this.handleFacadeError(error, 'resumeFeature', featureId); + throw error; + } } /** diff --git a/apps/server/src/services/branch-utils.ts b/apps/server/src/services/branch-utils.ts index 3526c5ab..a2e1c3c0 100644 --- a/apps/server/src/services/branch-utils.ts +++ b/apps/server/src/services/branch-utils.ts @@ -92,20 +92,22 @@ export async function stashChanges( } args.push('-m', message); - await execGitCommandWithLockRetry(args, cwd); + const stdout = await execGitCommandWithLockRetry(args, cwd); + + // git exits 0 but prints a benign message when there is nothing to stash + const stdoutLower = stdout.toLowerCase(); + if ( + stdoutLower.includes('no local changes to save') || + stdoutLower.includes('nothing to stash') + ) { + logger.debug('stashChanges: nothing to stash', { cwd, message, stdout }); + return false; + } + return true; } catch (error) { const errorMsg = getErrorMessage(error); - // "Nothing to stash" is benign – no work was lost, just return false - if ( - errorMsg.toLowerCase().includes('no local changes to save') || - errorMsg.toLowerCase().includes('nothing to stash') - ) { - logger.debug('stashChanges: nothing to stash', { cwd, message, error: errorMsg }); - return false; - } - // Unexpected error – log full details and re-throw so the caller aborts // rather than proceeding with an un-stashed working tree logger.error('stashChanges: unexpected error during stash', { diff --git a/apps/ui/src/components/views/board-view/dialogs/create-branch-dialog.tsx b/apps/ui/src/components/views/board-view/dialogs/create-branch-dialog.tsx index ce5087c4..9c22e801 100644 --- a/apps/ui/src/components/views/board-view/dialogs/create-branch-dialog.tsx +++ b/apps/ui/src/components/views/board-view/dialogs/create-branch-dialog.tsx @@ -112,6 +112,9 @@ export function CreateBranchDialog({ if (open) { setBranchName(''); setBaseBranch(''); + // Update the ref synchronously so fetchBranches() sees the cleared value + // immediately, rather than the stale value from the previous open. + baseBranchRef.current = ''; setError(null); setBranches([]); setBaseBranchPopoverOpen(false);