fix(FN-8600): make the duplicate-report instruction writable, not self-contradictory
The planning prompt said "do not write PROMPT.md" and, in the same breath,
"write DUPLICATE: {id} to the output file" — where the output file IS
PROMPT.md. A planner that took the first clause literally wrote no file and
reported the duplicate in prose.
The engine only ever reads the verdict from PROMPT.md's contents, so that
duplicate was invisible: the task failed deterministic validation as
"PROMPT.md file not found or empty", retried, terminalized to failed, emitted
a task-wedge mail, was recovered to todo by self-healing, and re-planned —
three full Opus planning cycles on FN-8600 before it was caught, with no
operator decision ever surfaced because sourceMetadata.nearDuplicateOf is only
set on the branch that parses the file.
Both prompt sites now say to write PROMPT.md with the marker as its entire
contents, and say why prose alone is not recorded.
Note the engine ordering is already correct — tryFinalizeExplicitDuplicateMarker
runs before validateGeneratedPrompt, and a worktree-local spec is recovered
first. Nothing to reorder; the file simply never existed.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
7
.changeset/duplicate-marker-must-write-prompt.md
Normal file
7
.changeset/duplicate-marker-must-write-prompt.md
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
---
|
||||||
|
"@runfusion/fusion": patch
|
||||||
|
---
|
||||||
|
|
||||||
|
summary: Stop duplicate tasks re-planning in a loop instead of asking you to keep or delete them.
|
||||||
|
category: fix
|
||||||
|
dev: The planning prompt told the planner "do not write PROMPT.md" and "write DUPLICATE: <id> to the output file" — the output file being PROMPT.md. Planners resolved the contradiction by writing no file and reporting the duplicate in prose, which the engine cannot see (`parseExplicitDuplicateMarker` reads PROMPT.md's contents). The task then failed deterministic validation as "PROMPT.md not found or empty", retried, terminalized, sent a task-wedge mail, self-healed back to todo, and re-planned indefinitely — never setting `sourceMetadata.nearDuplicateOf`, which is what renders the operator's keep/delete decision. Both prompt sites now state that the file must be written with the marker as its entire contents.
|
||||||
@@ -273,7 +273,7 @@ Write a lean, executable PROMPT.md quickly. Preserve safety gates, but skip heav
|
|||||||
- Preserve required safety sections for bugs, workflow routing, forensic tasks, and decision-only work.
|
- Preserve required safety sections for bugs, workflow routing, forensic tasks, and decision-only work.
|
||||||
|
|
||||||
## Duplicate check
|
## Duplicate check
|
||||||
Before writing a spec, call \`fn_task_list\` for active work, then call \`fn_task_search\` with 2-4 targeted keyword phrases from the title/description, such as file paths, symptoms, and symbols. For any likely match in \`done\` or \`archived\`, call \`fn_task_show\` and inspect it before deciding. If an existing task covers the same work, do not write PROMPT.md; write exactly \`DUPLICATE: {existing-task-id}\`.
|
Before writing a spec, call \`fn_task_list\` for active work, then call \`fn_task_search\` with 2-4 targeted keyword phrases from the title/description, such as file paths, symptoms, and symbols. For any likely match in \`done\` or \`archived\`, call \`fn_task_show\` and inspect it before deciding. If an existing task covers the same work, do not write a spec — but still write PROMPT.md, with its entire contents being the single line \`DUPLICATE: {existing-task-id}\` and nothing else. That file is how the duplicate is recorded; announcing it only in your reply leaves no plan behind and re-plans the task in a loop.
|
||||||
|
|
||||||
## Required PROMPT.md shape
|
## Required PROMPT.md shape
|
||||||
Write PROMPT.md with Original Description, Before → After Transformation, Mission, Dependencies, Context to Read First, File Scope, Steps, Documentation Requirements, Completion Criteria, Git Commit Convention, and Do NOT. Put \`## Original Description\` immediately after the title/\`Created\`/\`Size\` metadata with the operator's original task description copied **verbatim** (do not paraphrase). Put \`## Before → After Transformation\` next, before \`## Mission\`, with concise Before/After bullets: current state, target state, why it satisfies the user's request at a glance. In \`## Steps\`, every executable heading MUST use \`### Step N: <name>\` (e.g. \`### Step 1: Preflight\`). Do not write bare \`### Preflight\` / \`### Implementation\` headings, and do not add review-level, triage subtask, or proactive subtask headings.
|
Write PROMPT.md with Original Description, Before → After Transformation, Mission, Dependencies, Context to Read First, File Scope, Steps, Documentation Requirements, Completion Criteria, Git Commit Convention, and Do NOT. Put \`## Original Description\` immediately after the title/\`Created\`/\`Size\` metadata with the operator's original task description copied **verbatim** (do not paraphrase). Put \`## Before → After Transformation\` next, before \`## Mission\`, with concise Before/After bullets: current state, target state, why it satisfies the user's request at a glance. In \`## Steps\`, every executable heading MUST use \`### Step N: <name>\` (e.g. \`### Step 1: Preflight\`). Do not write bare \`### Preflight\` / \`### Implementation\` headings, and do not add review-level, triage subtask, or proactive subtask headings.
|
||||||
@@ -552,9 +552,13 @@ Verified facts about this codebase's storage — cite these correctly so Plan Re
|
|||||||
## Duplicate check
|
## Duplicate check
|
||||||
Before writing a spec, first call \`fn_task_list\` to see active tasks, then call \`fn_task_search\` with 2-4 distinct keyword phrases from the task title and description (for example file paths, error symptoms, and symbol names).
|
Before writing a spec, first call \`fn_task_list\` to see active tasks, then call \`fn_task_search\` with 2-4 distinct keyword phrases from the task title and description (for example file paths, error symptoms, and symbol names).
|
||||||
For any likely match in \`done\` or \`archived\`, call \`fn_task_show\` to inspect details before deciding.
|
For any likely match in \`done\` or \`archived\`, call \`fn_task_show\` to inspect details before deciding.
|
||||||
If a task already covers the same work (even if worded differently), do NOT
|
If a task already covers the same work (even if worded differently), do not write a spec.
|
||||||
write a PROMPT.md. Instead, write a single line to the output file:
|
Instead you MUST still write PROMPT.md, with its ENTIRE contents being this one line and nothing else:
|
||||||
\`DUPLICATE: {existing-task-id}\`
|
\`DUPLICATE: {existing-task-id}\`
|
||||||
|
Writing that file IS how you report the duplicate. Reporting it only in your reply is not recorded:
|
||||||
|
the engine reads the verdict from PROMPT.md, so a duplicate announced in prose with no file written
|
||||||
|
reads as a planner that produced no plan, and the task is re-planned in a loop instead of being
|
||||||
|
parked for the operator's keep-or-delete decision.
|
||||||
|
|
||||||
## Dependency awareness
|
## Dependency awareness
|
||||||
When you plan to list a task in the \`## Dependencies\` section, first call \`fn_task_show\` on that task ID to read its PROMPT.md.
|
When you plan to list a task in the \`## Dependencies\` section, first call \`fn_task_show\` on that task ID to read its PROMPT.md.
|
||||||
|
|||||||
Reference in New Issue
Block a user