diff --git a/autoplan/bin/guard-reasons.ts b/autoplan/bin/guard-reasons.ts index d0b8135bd..10d937e10 100644 --- a/autoplan/bin/guard-reasons.ts +++ b/autoplan/bin/guard-reasons.ts @@ -19,7 +19,7 @@ interface Reason { disposition: Disposition; text: (d: Detail) => string } export const FALLBACK = 'Fallback: run /plan-ceo-review, then /plan-devex-review, then /plan-eng-review by hand; ' + 'or run /context-save, start a new Claude Code session (not --resume), run /context-restore, then /autoplan .'; const PUBLISH_SEPARATELY = (d: Detail) => `Publish the filled Phase ${d.phase} report as your own parent assistant text in a message ` + - `whose only tool call is the Bash no-op \`: autoplan-published \`, then make this phase-entry call in a later message.`; + `whose only tool call is the Bash no-op \`true autoplan-published \`, then make this phase-entry call in a later message.`; export const REASONS = { // Journal ownership (lib/claude-public-transcript.ts OwnedTranscriptReason). diff --git a/autoplan/bin/phase-publication-hook.ts b/autoplan/bin/phase-publication-hook.ts index 73afd1ded..f6de0de14 100644 --- a/autoplan/bin/phase-publication-hook.ts +++ b/autoplan/bin/phase-publication-hook.ts @@ -651,7 +651,7 @@ function unverifiedOutput(code: string, detail: Detail, version: string | undefi const notice = reasonText(code, detail, version); return { systemMessage: `[autoplan] ${notice}`, hookSpecificOutput: { hookEventName: 'PreToolUse', additionalContext: `[autoplan] ${notice} This call proceeds unverified. Publish each completed phase report as your own parent ` + - 'assistant text, in a message whose only tool call is the Bash no-op `: autoplan-published `, before entering the next phase.' } }; + 'assistant text, in a message whose only tool call is the Bash no-op `true autoplan-published `, before entering the next phase.' } }; } function decided(decision: Decision, via: EvaluationPath, version?: string): object { diff --git a/autoplan/sections/phase-close.md b/autoplan/sections/phase-close.md index fb9624a88..fffa9fd8e 100644 --- a/autoplan/sections/phase-close.md +++ b/autoplan/sections/phase-close.md @@ -42,7 +42,7 @@ preparation does not perform them. 6. **Publish the parent report.** After successful verification, SEND the filled report below now as visible parent assistant text, using actual findings and voice statuses, in its own message whose only tool call is the Bash no-op - `: autoplan-published ` (no output). The guard counts a report only when a + `true autoplan-published ` (no output). The guard counts a report only when a later record follows it, so this message is the next operation before any next-phase tool call. Use the packet's `report` fields for this phase, the actual host's reviewer names, and N/A when either review voice is missing; confirmed counts require both voices. diff --git a/autoplan/sections/phase-close.md.tmpl b/autoplan/sections/phase-close.md.tmpl index 68d405306..e11f976f6 100644 --- a/autoplan/sections/phase-close.md.tmpl +++ b/autoplan/sections/phase-close.md.tmpl @@ -40,7 +40,7 @@ preparation does not perform them. 6. **Publish the parent report.** After successful verification, SEND the filled report below now as visible parent assistant text, using actual findings and voice statuses, in its own message whose only tool call is the Bash no-op - `: autoplan-published ` (no output). The guard counts a report only when a + `true autoplan-published ` (no output). The guard counts a report only when a later record follows it, so this message is the next operation before any next-phase tool call. Use the packet's `report` fields for this phase, the actual host's reviewer names, and N/A when either review voice is missing; confirmed counts require both voices. diff --git a/docs/autoplan-guard-troubleshooting.md b/docs/autoplan-guard-troubleshooting.md index 8ea3c409c..4c1c8f447 100644 --- a/docs/autoplan-guard-troubleshooting.md +++ b/docs/autoplan-guard-troubleshooting.md @@ -38,8 +38,9 @@ to the hook. So the guard counts a phase report only when a later journaled record follows it: a tool result, or a record from another assistant message. The phase-close procedure therefore publishes each report in a message whose -only tool call is the Bash no-op `: autoplan-published ` (it prints -nothing and needs no permission), and makes the next phase's `Read` or `Agent` +only tool call is the Bash no-op `true autoplan-published ` (it prints +nothing and needs no permission; Claude Code 2.1.292 asks for approval of a bare +`:` command, so the no-op is `true`), and makes the next phase's `Read` or `Agent` call in a later message. Sessions that put the report and the next phase's call in one message get one [`publication_unflushed`](#publication-unflushed) denial naming that fix. @@ -191,7 +192,7 @@ be verified, so Claude Code is not writing it to the journal in time. ### `publication_missing` No filled report for the previous phase appears after its close packet Read. Publish it as your own assistant text in a message whose only tool call is -`: autoplan-published `, then enter the next phase in a later message. +`true autoplan-published `, then enter the next phase in a later message. ### `publication_unflushed` diff --git a/test/autoplan-publication-guard.test.ts b/test/autoplan-publication-guard.test.ts index 20eed46da..2ec8d242f 100644 --- a/test/autoplan-publication-guard.test.ts +++ b/test/autoplan-publication-guard.test.ts @@ -356,7 +356,7 @@ describe('Autoplan parent publication guard', () => { expect(warning).toContain('code too_large'); expect(warning).toContain('docs/autoplan-guard-troubleshooting.md#too-large'); expect(warning).toContain('Phase-report enforcement was skipped'); - expect(output.hookSpecificOutput.additionalContext).toContain(': autoplan-published '); + expect(output.hookSpecificOutput.additionalContext).toContain('true autoplan-published '); }); test('#3050: the batch read and the narrowed parent loop report too_large, not a plain error', async () => { diff --git a/test/autoplan-publication-payload.test.ts b/test/autoplan-publication-payload.test.ts index b03426d1e..f29993480 100644 --- a/test/autoplan-publication-payload.test.ts +++ b/test/autoplan-publication-payload.test.ts @@ -110,7 +110,7 @@ describe('Cause B: the current call is not journaled at hook time (payload path) expect(codeOf(await f.hook(f.input('b', 'Agent', dispatch(f, { model: 'haiku' }))))).toBe('agent_key'); const next = await f.hook(f.input('c', 'Read', { file_path: section('design-phase.md') })); expect(codeOf(next)).toBe('publication_missing'); - expect(denial(next)).toContain(': autoplan-published '); + expect(denial(next)).toContain('true autoplan-published '); }); }); @@ -121,7 +121,7 @@ describe('Publication flush: a report counts only once a later journaled record const f = make(); f.report(f.nextMessage()); f.journal(); const output = await f.hook(next(f)); expect(codeOf(output)).toBe('publication_unflushed'); - expect(denial(output)).toContain('`: autoplan-published `'); + expect(denial(output)).toContain('`true autoplan-published `'); expect(denial(output)).not.toMatch(/\bretry\b/i); }); diff --git a/test/helpers/autoplan-guard-fixture.ts b/test/helpers/autoplan-guard-fixture.ts index 70d903788..b646b898d 100644 --- a/test/helpers/autoplan-guard-fixture.ts +++ b/test/helpers/autoplan-guard-fixture.ts @@ -59,7 +59,7 @@ export function guardFixture(phase: Phase = 'ceo', opts: { methodologyLines?: nu /** Phase-close step 6: the report and the no-op in their own message, then the no-op's result. */ const publish = () => { const messageId = nextMessage(); - report(messageId); use(`noop-${messageId}`, 'Bash', { command: `: autoplan-published ${phase}` }, messageId); + report(messageId); use(`noop-${messageId}`, 'Bash', { command: `true autoplan-published ${phase}` }, messageId); result(`noop-${messageId}`, { content: '' }); }; diff --git a/test/helpers/carve-guards.ts b/test/helpers/carve-guards.ts index 373d763f9..80e83edde 100644 --- a/test/helpers/carve-guards.ts +++ b/test/helpers/carve-guards.ts @@ -575,7 +575,7 @@ do not launch the downstream skill or open a browser.`, maxSkeletonBytes: 70_000, // Phase-specific outside coverage, native fallback, and harness guard. minUnionBytes: 85_000, // measured union 86,926 mustContain: ['6 Decision Principles', 'TASTE DECISION', 'USER CHALLENGE', 'consensus', 'Restore Point'], - maxSizeRatio: 1.1215, // Four validated outside invocations replace raw CLI calls; phases keep independent coverage. + Oct 7 wave guard (CEO-20): conditional `run_in_background` request plus the background-run recovery at each foreground dispatch site, and phase-close publishing each report with the `: autoplan-published ` no-op before the next phase entry in a later message (CEO-1); measured 114,320 / 101,979 = 1.1210 (2026-10-07). + maxSizeRatio: 1.1215, // Four validated outside invocations replace raw CLI calls; phases keep independent coverage. + Oct 7 wave guard (CEO-20): conditional `run_in_background` request plus the background-run recovery at each foreground dispatch site, and phase-close publishing each report with the `true autoplan-published ` no-op before the next phase entry in a later message (CEO-1); measured 114,320 / 101,979 = 1.1210 (2026-10-07). }, spec: { skill: 'spec',