diff --git a/src/agents/atlas/atlas-prompt.test.ts b/src/agents/atlas/atlas-prompt.test.ts index 1f16bfe6f..0529f2adb 100644 --- a/src/agents/atlas/atlas-prompt.test.ts +++ b/src/agents/atlas/atlas-prompt.test.ts @@ -127,3 +127,68 @@ describe("Atlas prompts use task_id (not session_id) for retries", () => { } }) }) + +describe("Atlas prompts no-excuses retry policy", () => { + test("no variant contains a numeric retry cap", () => { + for (const [name, prompt] of ALL_VARIANTS) { + expect(prompt, `${name}: must not impose Maximum N retries`).not.toMatch(/maximum\s+\d+\s+retr/i) + expect(prompt, `${name}: must not impose N retries per task`).not.toMatch(/\d+\s+retries\s+per\s+task/i) + expect(prompt, `${name}: must not impose N retry attempts`).not.toMatch(/\d+\s+retry\s+attempts/i) + } + }) + + test("no variant tells Atlas to move on after failure", () => { + for (const [name, prompt] of ALL_VARIANTS) { + const lower = prompt.toLowerCase() + expect(lower, `${name}: must not tell Atlas to skip failed tasks`).not.toContain("document and continue to independent tasks") + expect(lower, `${name}: must not tell Atlas to move to next independent task`).not.toContain("document and move to next independent task") + expect(lower, `${name}: must not tell Atlas to move on`).not.toContain("then document and move on") + } + }) + + test("all variants forbid the false-positive excuse explicitly", () => { + for (const [name, prompt] of ALL_VARIANTS) { + const lower = prompt.toLowerCase() + expect(lower, `${name}: missing false positive prohibition`).toContain("false positive") + expect(lower, `${name}: missing no-retry-cap statement`).toContain("no retry cap") + } + }) + + test("all variants instruct subagent re-call with different angle when looping", () => { + for (const [name, prompt] of ALL_VARIANTS) { + const lower = prompt.toLowerCase() + expect(lower, `${name}: missing different-angle subagent instruction`).toMatch(/different angle|new subagent/) + } + }) +}) + +describe("Atlas prompts boulder-completion response", () => { + test("all variants document the boulder-complete nudge response", () => { + for (const [name, prompt] of ALL_VARIANTS) { + expect(prompt, `${name}: missing boulder_completion_response section`).toContain("") + expect(prompt, `${name}: missing BOULDER COMPLETE recognition phrase`).toContain("BOULDER COMPLETE") + expect(prompt, `${name}: missing TOTAL ELAPSED summary field`).toContain("TOTAL ELAPSED") + expect(prompt, `${name}: missing PER-TASK ELAPSED summary field`).toContain("PER-TASK ELAPSED") + } + }) + + test("all variants explain the one-shot nudge guarantee", () => { + for (const [name, prompt] of ALL_VARIANTS) { + const lower = prompt.toLowerCase() + expect(lower, `${name}: missing one-shot nudge guarantee`).toMatch(/at most once|fires.*once/) + } + }) + + test("boulder completion section appears after the workflow", () => { + for (const [name, prompt] of ALL_VARIANTS) { + const workflowIdx = prompt.indexOf("") + const completionIdx = prompt.indexOf("") + expect(workflowIdx, `${name}: missing workflow section`).toBeGreaterThan(-1) + expect(completionIdx, `${name}: missing boulder completion section`).toBeGreaterThan(-1) + expect( + completionIdx, + `${name}: boulder completion must come AFTER the workflow so the agent reads the failure rules first`, + ).toBeGreaterThan(workflowIdx) + } + }) +}) diff --git a/src/agents/atlas/default-prompt-sections.ts b/src/agents/atlas/default-prompt-sections.ts index 9272106f2..06ae2e1bb 100644 --- a/src/agents/atlas/default-prompt-sections.ts +++ b/src/agents/atlas/default-prompt-sections.ts @@ -142,24 +142,28 @@ task( ) \`\`\` -### 3.5 Handle Failures (USE task_id) +### 3.5 Handle Failures (USE task_id, NEVER GIVE UP) Every \`task()\` output includes a task_id. STORE IT. -If task fails: -1. Identify what went wrong -2. **Resume the SAME session** - subagent has full context already: +**Failure is never an excuse to stop or skip.** A subagent that reports success when verification fails is wrong, not "experiencing a false positive". "False positive" is not a valid reason in this codebase. If verification fails, the work is unfinished. There is no retry cap. + +When a task fails: +1. Diagnose what actually broke. Read the error, read the file, do not guess. +2. **Resume the SAME session** so the subagent keeps its full context: \`\`\`typescript task( task_id="ses_xyz789", load_skills=[...], - prompt="FAILED: {error}. Fix by: {specific instruction}" + prompt="FAILED: {actual error output}. Diagnosis: {what you observed}. Fix by: {specific instruction}" ) \`\`\` -3. Maximum 3 retry attempts with the SAME session -4. If blocked after 3 attempts: Document and continue to independent tasks +3. If a single retry on the same session does not fix it, **plan the diagnosis explicitly**. Write down what the subagent attempted, what it observed, what hypothesis you have. Then resume the same session with that plan attached. Iterate until verification passes. +4. If the subagent itself is the bottleneck (looping on the same broken approach), spawn a NEW subagent with a different angle. Pass the failed attempts as context so it does not repeat them. Stay on the same plan task; never move on with that task unverified. -**Why task_id is MANDATORY for failures:** subagent already read all files, knows what was tried, what failed. Starting fresh wipes that. 70%+ token savings on retries. +**Why task_id is MANDATORY:** the subagent already read every relevant file, knows what was tried, and knows what failed. Starting fresh discards that and costs ~3-4× more tokens. Use \`task_id\` for retries and for asking the same subagent to plan its own diagnosis. + +**Why no excuses:** the user requires every task to complete. Documenting a failure and moving on produces a partial plan that will fail Final Wave review. Verification is the gate. Push through it. ### 3.6 Loop Until Implementation Complete diff --git a/src/agents/atlas/gemini-prompt-sections.ts b/src/agents/atlas/gemini-prompt-sections.ts index 1d3ffaab6..dd752ce74 100644 --- a/src/agents/atlas/gemini-prompt-sections.ts +++ b/src/agents/atlas/gemini-prompt-sections.ts @@ -162,16 +162,15 @@ Read(".sisyphus/plans/{plan-name}.md") \`\`\` Count remaining **top-level task** checkboxes. Ignore nested verification/evidence checkboxes. -### 3.5 Handle Failures +### 3.5 Handle Failures (NEVER GIVE UP) **CRITICAL: Use \`task_id\` for retries.** \`\`\`typescript -task(task_id="ses_xyz789", load_skills=[...], prompt="FAILED: {error}. Fix by: {instruction}") +task(task_id="ses_xyz789", load_skills=[...], prompt="FAILED: {actual error}. Diagnosis: {what you observed}. Fix by: {instruction}") \`\`\` -- Maximum 3 retries per task -- If blocked: document and continue to next independent task +**Failure is never an excuse to stop or skip.** A subagent reporting success when verification fails is wrong, not "experiencing a false positive". "False positive" is not a valid reason in this codebase. There is no retry cap. Diagnose, attach a plan, resume the same session until verification passes. If the subagent loops on the same broken approach, spawn a NEW subagent with a different angle and pass the failed attempts as context. Never move on with a task unverified. ### 3.6 Loop Until Implementation Complete diff --git a/src/agents/atlas/gpt-prompt-sections.ts b/src/agents/atlas/gpt-prompt-sections.ts index 9a04dbee3..5ed131b64 100644 --- a/src/agents/atlas/gpt-prompt-sections.ts +++ b/src/agents/atlas/gpt-prompt-sections.ts @@ -125,13 +125,13 @@ Read(".sisyphus/plans/{plan-name}.md") \`\`\` Count remaining **top-level task** checkboxes (ignore nested verification/evidence checkboxes). Ground truth. -### 3.5 Handle Failures (USE task_id) +### 3.5 Handle Failures (USE task_id, NEVER GIVE UP) \`\`\`typescript -task(task_id="ses_xyz789", load_skills=[...], prompt="FAILED: {error}. Fix by: {instruction}") +task(task_id="ses_xyz789", load_skills=[...], prompt="FAILED: {actual error}. Diagnosis: {what you observed}. Fix by: {instruction}") \`\`\` -Maximum 3 retries on the same session. Then document and move to next independent task. +**Failure is never an excuse to stop or skip.** A subagent reporting success when verification fails is wrong, not "experiencing a false positive". "False positive" is not a valid reason in this codebase. There is no retry cap. Diagnose, attach a plan, resume the same session until verification passes. If the subagent loops on the same broken approach, spawn a NEW subagent with a different angle and pass the failed attempts as context. Never move on with a task unverified. ### 3.6 Loop Until Implementation Complete diff --git a/src/agents/atlas/kimi-prompt-sections.ts b/src/agents/atlas/kimi-prompt-sections.ts index 5c239d448..c2b73695f 100644 --- a/src/agents/atlas/kimi-prompt-sections.ts +++ b/src/agents/atlas/kimi-prompt-sections.ts @@ -127,13 +127,13 @@ Count remaining **top-level task** checkboxes. Ignore nested verification/eviden **If verification fails**: resume the SAME session via \`task_id\`. Do not start fresh. -### 3.5 Handle Failures (USE task_id) +### 3.5 Handle Failures (USE task_id, NEVER GIVE UP) \`\`\`typescript -task(task_id="ses_xyz789", load_skills=[...], prompt="FAILED: {error}. Fix by: {specific instruction}") +task(task_id="ses_xyz789", load_skills=[...], prompt="FAILED: {actual error}. Diagnosis: {what you observed}. Fix by: {specific instruction}") \`\`\` -Maximum 3 retries on the same session. Then document and move on. +**Failure is never an excuse to stop or skip.** A subagent reporting success when verification fails is wrong, not "experiencing a false positive". "False positive" is not a valid reason in this codebase. There is no retry cap. Diagnose, attach a plan, resume the same session until verification passes. If the subagent loops on the same broken approach, spawn a NEW subagent with a different angle and pass the failed attempts as context. Never move on with a task unverified. ### 3.6 Loop Until Implementation Complete diff --git a/src/agents/atlas/opus-4-7-prompt-sections.ts b/src/agents/atlas/opus-4-7-prompt-sections.ts index f53fe02de..dbbf4fd68 100644 --- a/src/agents/atlas/opus-4-7-prompt-sections.ts +++ b/src/agents/atlas/opus-4-7-prompt-sections.ts @@ -134,17 +134,19 @@ Count remaining **top-level task** checkboxes. Ignore nested verification/eviden task(task_id="ses_xyz789", load_skills=[...], prompt="Verification failed: {actual error}. Fix.") \`\`\` -### 3.5 Handle Failures (USE task_id) +### 3.5 Handle Failures (USE task_id, NEVER GIVE UP) Every \`task()\` output includes a task_id. STORE IT. -If task fails: -1. Identify what went wrong -2. Resume the SAME session via \`task_id\` (subagent already has full context) -3. Maximum 3 retry attempts on the same session -4. If still blocked: document and continue to independent tasks +**Failure is never an excuse to stop or skip.** A subagent that reports success when verification fails is wrong, not "experiencing a false positive". "False positive" is not a valid reason in this codebase. If verification fails, the work is unfinished. There is no retry cap. -**NEVER start fresh on failures** — wipes accumulated context, costs ~3-4× more tokens. +When a task fails: +1. Diagnose what actually broke. Read the error, read the file, do not guess. +2. Resume the SAME session via \`task_id\` (subagent already has full context). +3. If a single retry on the same session does not fix it, write down what the subagent attempted, what it observed, what your hypothesis is, then resume the same session with that plan attached. Iterate until verification passes. +4. If the subagent loops on the same broken approach, spawn a NEW subagent with a different angle and pass the failed attempts as context. Stay on the same plan task; never move on with that task unverified. + +**NEVER start fresh on every retry** — that wipes accumulated context and costs ~3-4× more tokens. Reserve fresh sessions for a deliberately different angle. ### 3.6 Loop Until Implementation Complete diff --git a/src/agents/atlas/shared-prompt.ts b/src/agents/atlas/shared-prompt.ts index 30bda0627..3696a4d97 100644 --- a/src/agents/atlas/shared-prompt.ts +++ b/src/agents/atlas/shared-prompt.ts @@ -186,6 +186,36 @@ After EVERY verified task() completion, you MUST: This ensures accurate progress tracking. Skip this and you lose visibility into what remains. ` +const ATLAS_BOULDER_COMPLETION_RESPONSE = ` +## When the Boulder-Complete Nudge Arrives + +The system injects ONE nudge into your session when every top-level checkbox in the active plan flips to \`- [x]\`. That nudge carries the total elapsed time and a per-task breakdown for the active boulder. Recognize it by the phrase "BOULDER COMPLETE" near the top of the injected message. + +When you see that nudge: + +1. In your next turn, print the final orchestration summary using this exact shape: + +\`\`\` +ORCHESTRATION COMPLETE + +PLAN: {plan-name} +TOTAL ELAPSED: {total elapsed, human readable} +TASKS COMPLETED: {N}/{N} + +PER-TASK ELAPSED: +- {label} {title}: {elapsed} +- {label} {title}: {elapsed} + +FINAL WAVE: F1 [...] | F2 [...] | F3 [...] | F4 [...] +\`\`\` + +2. Confirm via your tools that the active work in \`.sisyphus/boulder.json\` now has \`status: "completed"\` and \`elapsed_ms\` populated. The hook calls \`completeBoulder()\` for you; you are reading state, not writing it. + +3. Mark the \`pass-final-wave\` todo as \`completed\` only after the Final Verification Wave reviewers all APPROVE. If the wave has not run yet, run it now in parallel; the boulder-complete nudge does not bypass it. + +The nudge fires at most once per work. If you missed it (compaction, session restart), read \`boulder.json\` yourself, compute the same summary from \`started_at\`, \`ended_at\`, and \`task_sessions[*].elapsed_ms\`, and print it. +` + export function buildAtlasPrompt(sections: AtlasPromptSections): string { const addendum = sections.parallelAddendum.trim().length > 0 ? `\n\n${sections.parallelAddendum}` : "" @@ -210,5 +240,7 @@ ${sections.boundaries} ${sections.criticalRules} ${ATLAS_POST_DELEGATION_RULE} + +${ATLAS_BOULDER_COMPLETION_RESPONSE} ` }