Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/reliable-plan-artifact.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@planningo/duul": patch
---

Improve plan-file workflow guidance so callers replace and verify each plan revision as one snapshot instead of retrying stale text edits.
10 changes: 6 additions & 4 deletions .claude/agents/duul-planner.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ You will receive:
- Dependencies and imports needed

**Write the plan in compressed "caveman" style to save tokens:** drop articles (a/an/the), filler (just/really/basically), and pleasantries; prefer fragments over full sentences; use short synonyms. Keep EXACT: file paths, identifiers, function/type names, code, and the verbatim quote of the user's request. Brevity must never drop a required section or change technical meaning.
3. **Submit the plan via file (preferred for reliability).** Write the full plan markdown to `.duul/plan.md` under the workspace root using the Write tool, THEN call `request_plan_review` with `plan_file: ".duul/plan.md"` (relative path). This avoids the large-argument tool-call failure where a big inline `plan` string collapses to an empty `{}`. For a short plan you may inline `plan` instead. ALWAYS include:
3. **Submit the plan via file (preferred for reliability).** Write the full plan markdown to `.duul/plan.md` under the workspace root using the Write tool, THEN read it back and call `request_plan_review` with `plan_file: ".duul/plan.md"` (relative path). This avoids the large-argument tool-call failure where a big inline `plan` string collapses to an empty `{}`. For a short plan you may inline `plan` instead. ALWAYS include:
- `workspace_root`: the workspace root path (required when using `plan_file`)
- `plan_file`: `".duul/plan.md"` (relative path) — OR `plan` with the full text inline for short plans
- `user_original_request`: the user's verbatim message
Expand Down Expand Up @@ -68,8 +68,10 @@ A large inline `plan` string is the #1 cause of failed DUUL calls: the model tri

**The fix: route the large plan through a file.**

1. Write the full plan markdown to `.duul/plan.md` under the workspace root with the **Write** tool (Write has a tiny, reliable schema — big content goes through fine here).
2. Call `request_plan_review` with a *small* argument object that points at the file:
1. Compose the complete next revision before editing. Use the **Write** tool to replace all of `.duul/plan.md` in one operation; do not build a plan with a chain of exact-text `Edit` calls. Exact-text replacements become stale after the first edit and produce `String to replace not found` errors.
2. Read `.duul/plan.md` back after writing. Only submit the file after confirming it contains the complete intended revision.
3. If Write is unavailable, read the file immediately before one Edit, make one whole-document replacement, and read it back. If that Edit reports a mismatch, stop the edit sequence, re-read the file, and regenerate the full snapshot — never retry the stale replacement.
4. Call `request_plan_review` with a *small* argument object that points at the file:

```json
{
Expand All @@ -80,7 +82,7 @@ A large inline `plan` string is the #1 cause of failed DUUL calls: the model tri
}
```

The server reads `.duul/plan.md` and uses its contents as the plan. `plan_file` must be a **relative** path inside `workspace_root`.
The server reads `.duul/plan.md` and uses its contents as the plan. `plan_file` must be a **relative** path inside `workspace_root`. Apply the same snapshot protocol on every REVISE round; do not edit the plan concurrently with another agent.

**Short plans only:** you may instead inline `plan` directly. Exactly one of `plan` or `plan_file` is required.

Expand Down
12 changes: 11 additions & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,17 @@ Only activate when the user mentions **"DUUL"** (or **"두울"**) in their reque
3. If the subagent reports `requires_human_review === true`: pause and ask the user.
4. Extract `approved_plan`, `review_id`, and `git_head_sha` from the subagent's response.

**Fallback:** If the subagent fails or the MCP tool is not accessible from the subagent, fall back to running Phase 1 directly (same as before but in the main agent).
**Fallback:** If the subagent fails or the MCP tool is not accessible from the subagent, fall back to running Phase 1 directly (same as before but in the main agent). Use the plan-artifact protocol below in either path.

### Plan-artifact protocol (required in Phase 1)

The plan-file escape hatch only helps if its contents are written reliably. Treat `.duul/plan.md` as an immutable snapshot for each review round:

1. Compose the complete revision before using a file-edit tool.
2. Use `Write` to replace the entire file once, then read it back and verify the intended plan is present.
3. Do not assemble a revision with several exact-text `Edit` operations. Their search strings are stale as soon as an earlier edit succeeds, causing `String to replace not found` errors.
4. If `Write` is unavailable, read immediately before a single whole-file `Edit`; after any mismatch error, stop, re-read, and write a fresh full snapshot. Never retry the failed replacement text.
5. Only then call `request_plan_review` with `plan_file: ".duul/plan.md"`. One agent owns this artifact at a time.

### Phase 2: Unit-verify Ping-Pong (Opus, start IMMEDIATELY after Phase 1 approval — do NOT ask for confirmation)

Expand Down
3 changes: 3 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ const SERVER_INSTRUCTIONS = `
DUUL — Dual-phase Upfront-plan & Unit-verify Loop.
Activate ONLY when user says "DUUL" or "두울". See project CLAUDE.md for full protocol.
Key rules: pass workspace_root, pass previous_review_id on each round, never stop between phases.
Plan-file reliability: compose a complete revision before changing .duul/plan.md; write the whole file once,
read it back, then submit plan_file. Do not chain exact-text edits. On an edit mismatch, re-read and write a
fresh snapshot; only one agent may modify the plan artifact at a time.
`.trim();

const server = new McpServer(
Expand Down
Loading