diff --git a/src/local/auto-fix-loop.test.ts b/src/local/auto-fix-loop.test.ts index 50b73747..74cca802 100644 --- a/src/local/auto-fix-loop.test.ts +++ b/src/local/auto-fix-loop.test.ts @@ -609,6 +609,34 @@ describe('runWithAutoFix', () => { expect(repair?.content).toContain('MISSING_ENV_VAR:'); }); + // Regression: assertRickyWorkflowEnv used to throw at module-load time + // unconditionally, before the SDK had a chance to honour --start-from. + // That made resuming with --start-from impossible if the resumed step + // didn't actually need the missing env var (the upstream-only step did, + // but it was being skipped). The injected helper now warns-and-continues + // when process.env.START_FROM is set so resumed steps can run and any + // step that genuinely needs a missing value still fails with its own + // signal at the point of use. + it('injects an env-assert helper that honors START_FROM for --start-from resumes', () => { + const repair = repairWorkflowDeterministically({ + artifactPath: 'workflows/generated/foo.ts', + artifactContent: workflowContent(), + evidence: missingEnvEvidence(), + response: blockerResponse('MISSING_ENV_VAR', 'run-1', 'runtime-launch'), + }); + + expect(repair?.applied).toBe(true); + // Resume signal acknowledged. + expect(repair?.content).toContain('process.env.START_FROM'); + // Warn-and-continue path uses console.warn rather than throwing so the + // SDK can proceed to the resumed step. + expect(repair?.content).toMatch(/console\.warn\([^)]*Skipping env-var assertion/); + expect(repair?.content).toMatch(/--start-from active/); + // The non-resume path still throws fast — preserves the original + // contract for first-run invocations. + expect(repair?.content).toContain('throw new Error(`MISSING_ENV_VAR:'); + }); + // Regression: when a master-rendered workflow embeds a `node --input-type=module` // HEREDOC inside a .step({ command: ... }) string, the embedded shell text // contains the literal substring `from 'node:fs'`. The previous import-detection diff --git a/src/local/auto-fix-loop.ts b/src/local/auto-fix-loop.ts index c0cf6118..fc00d8b3 100644 --- a/src/local/auto-fix-loop.ts +++ b/src/local/auto-fix-loop.ts @@ -822,9 +822,19 @@ function loadRickyWorkflowEnv(cwd = process.cwd()) { function assertRickyWorkflowEnv(names: string[]): void { const missing = names.filter((name) => !process.env[name]); - if (missing.length > 0) { - throw new Error(\`MISSING_ENV_VAR: \${missing.join(', ')}. Add missing values to .env.local or export them before rerunning.\`); + if (missing.length === 0) return; + // When the workflow is being resumed via --start-from, the SDK sets + // process.env.START_FROM. Skipped upstream steps may have been the + // ones that needed these env vars; the resumed steps may not. Warn + // instead of failing fast so the resume can proceed and any step that + // actually needs a missing value will fail naturally with its own + // signal. Without this, --start-from on a workflow whose missing env + // var isn't in the resumed step's path is unrecoverable from the CLI. + if (process.env.START_FROM) { + console.warn(\`[ricky] Skipping env-var assertion (--start-from active): missing \${missing.join(', ')}. Resumed steps that actually need these will fail with their own error.\`); + return; } + throw new Error(\`MISSING_ENV_VAR: \${missing.join(', ')}. Add missing values to .env.local or export them before rerunning.\`); } function unquoteRickyWorkflowEnvValue(value: string): string { @@ -839,9 +849,12 @@ function unquoteRickyWorkflowEnvValue(value: string): string { function rickyWorkflowEnvAssertSource(): string { return `function assertRickyWorkflowEnv(names: string[]): void { const missing = names.filter((name) => !process.env[name]); - if (missing.length > 0) { - throw new Error(\`MISSING_ENV_VAR: \${missing.join(', ')}. Add missing values to .env.local or export them before rerunning.\`); + if (missing.length === 0) return; + if (process.env.START_FROM) { + console.warn(\`[ricky] Skipping env-var assertion (--start-from active): missing \${missing.join(', ')}. Resumed steps that actually need these will fail with their own error.\`); + return; } + throw new Error(\`MISSING_ENV_VAR: \${missing.join(', ')}. Add missing values to .env.local or export them before rerunning.\`); }`; }