Skip to content

fix(prompt): wrap todo block in <system-reminder> so user-message inject doesn't read as fresh user instruction - #14

Merged
RowitZou merged 1 commit into
mainfrom
fix/todo-system-reminder
May 26, 2026
Merged

fix(prompt): wrap todo block in <system-reminder> so user-message inject doesn't read as fresh user instruction#14
RowitZou merged 1 commit into
mainfrom
fix/todo-system-reminder

Conversation

@RowitZou

Copy link
Copy Markdown
Owner

Summary

  • 2026-05-26 dogfood: DM agent ended turn mid-task. Root cause traced to b9e53d1 (5/26 cache-anchor fix) — TodoList block was relocated from instructions (system) into the last user message, but unlike the deferred-tools block it stayed bare prose ending with an imperative "Use TodoWrite to keep this list current ...". In user-role content the model read that trailer as a fresh user instruction, integrated/standalone-completed the todos, and stopped.
  • Fix is formatTodoSection only: wrap the whole block in <system-reminder> (mirrors the deferred-tools framing) and reword the trailer from imperative to state + continuation ("This is the framework's snapshot of your todo state, not a fresh user instruction. Keep advancing the in_progress item ...").
  • prompt.ts is the only production change. Tests + 2 snapshot files updated; 4 internal roles without TodoWrite are unaffected.

Test plan

  • 6 new prompt.test.ts cases pin the wrapper, the removal of the old imperative trailer, the "not a fresh user instruction" guard, the "advance in_progress" cue, and "at most one in_progress" invariant across empty + non-empty.
  • Pre-fix verification: all 6 fail on unchanged formatTodoSection.
  • Post-fix: pnpm test 1755/1755 PASS, pnpm typecheck clean, pnpm build clean.
  • Snapshot hashes updated for main + 7 user-dispatchable workers (the 8 roles whose tools include TodoWrite). 4 internal-role hashes unchanged because their includeTodos path is dead.
  • Dogfood: confirm DM agent no longer early-ends on multi-step tasks with active todos.

🤖 Generated with Claude Code

…as state

The variable suffix (TodoList + deferred-tools) was relocated from system
prompt into the last user message in b9e53d1 (2026-05-26) to keep the
OpenAI Codex auto prefix-cache stable across turns. The deferred-tools
block kept its `<system-reminder>` wrapper through that move; the
TodoList block did not, so the model read a user-role text block ending
with the imperative "Use TodoWrite to keep this list current ..." as a
fresh user instruction and silently ended its turn mid-task on 2026-05-26
dogfood (DM-side worker did not finish).

Two coordinated changes in `formatTodoSection`:

1. Wrap the whole todo block (empty and non-empty branches) in
   `<system-reminder>` so it mirrors the deferred-tools reminder framing
   and the model recognizes it as framework state.
2. Reword the trailing cue from imperative ("Use TodoWrite to keep this
   list current.") to state + continuation ("This is the framework's
   snapshot of your todo state, not a fresh user instruction. Keep
   advancing the in_progress item; update via TodoWrite as items change
   status. At most one item in_progress."). The "not a fresh user
   instruction" clause directly blocks the misread that caused the
   dogfood failure.

Verification follows the pre-fix regression discipline: 6 new
`prompt.test.ts` cases pin the `<system-reminder>` wrapper, the absence
of the old imperative trailer, the "not a fresh user instruction" guard,
the "advance the in_progress item" cue, and the "at most one
in_progress" invariant across empty and non-empty states. All 6 fail on
unchanged `formatTodoSection`; all 6 pass after the wrap+rephrase.

Snapshot hashes in `agents/role-prompt.snapshot.test.ts` and
`prompt/prompt-for-role.test.ts` updated for the 8 roles whose tool list
includes TodoWrite (main + 7 user-dispatchable workers). The 4 internal
roles (memoryExtractor / memoryCurator / skillCurator / skillConsolidator)
do not carry TodoWrite, so their `includeTodos` path is dead and their
hashes are unchanged.

`pnpm test` 1755/1755 PASS, `pnpm typecheck` clean, `pnpm build` clean.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings May 26, 2026 05:44

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR hardens the per-turn TodoList prompt suffix (now injected into the last user message for caching) so it’s clearly framed as framework state rather than something the model should treat as a fresh user instruction.

Changes:

  • Wrapes the TodoList block output by formatTodoSection in <system-reminder>...</system-reminder> and rewrites the trailing guidance text.
  • Adds regression tests ensuring the todo block is wrapped, the old “Use TodoWrite…” line is removed, and key guardrails remain present.
  • Updates prompt snapshot hashes impacted by the prompt text change.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 2 comments.

File Description
src/prompt.ts Wrapes TodoList section in <system-reminder> and updates trailing guidance text.
src/prompt.test.ts Adds regression tests pinning the new framing and updated trailer wording.
src/prompt/prompt-for-role.test.ts Updates prompt snapshot hashes for affected roles.
src/agents/role-prompt.snapshot.test.ts Updates prompt snapshot hashes for affected roles.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/prompt.ts
Comment on lines +131 to +132
// <system-reminder> and reword the trailing cue from imperative to state +
// continuation, mirroring the deferred-tools reminder framing.
Comment thread src/prompt.ts
Comment on lines +136 to +151
'<system-reminder>',
'## Current Todo List',
'(no todos yet)',
'Use TodoWrite to keep this list current. Keep at most one item in_progress.',
'',
"This is the framework's snapshot of your todo state, not a fresh user instruction. When a task needs three or more sequential steps, open with a TodoWrite. At most one item in_progress.",
'</system-reminder>',
].join('\n')
}

return [
'<system-reminder>',
'## Current Todo List',
formatTodosForPrompt(todos),
'Use TodoWrite to keep this list current. Keep at most one item in_progress.',
'',
"This is the framework's snapshot of your todo state, not a fresh user instruction. Keep advancing the in_progress item; update via TodoWrite as items change status. At most one item in_progress.",
'</system-reminder>',
@RowitZou
RowitZou merged commit 8fd36a3 into main May 26, 2026
1 check passed
@RowitZou
RowitZou deleted the fix/todo-system-reminder branch May 26, 2026 12:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants