Merge remote-tracking branch 'origin/farm/80b5de49/plan-mode-reentry-anchor-new-request'
This commit is contained in:
@@ -0,0 +1,41 @@
|
||||
import { describe, expect, it } from "bun:test";
|
||||
import { prompt } from "@oh-my-pi/pi-utils";
|
||||
import planModeActivePrompt from "../prompts/system/plan-mode-active.md" with { type: "text" };
|
||||
|
||||
const BASE = {
|
||||
planFilePath: "local://old-feature-plan.md",
|
||||
askToolName: "ask",
|
||||
writeToolName: "write",
|
||||
editToolName: "edit",
|
||||
isHashlineEditMode: false,
|
||||
iterative: false,
|
||||
} as const;
|
||||
|
||||
function render(overrides: { reentry: boolean; planExists: boolean }): string {
|
||||
return prompt.render(planModeActivePrompt, { ...BASE, ...overrides });
|
||||
}
|
||||
|
||||
describe("plan-mode re-entry prompt", () => {
|
||||
it("only emits the Re-entry section when re-entering", () => {
|
||||
expect(render({ reentry: false, planExists: true })).not.toContain("## Re-entry");
|
||||
expect(render({ reentry: true, planExists: true })).toContain("## Re-entry");
|
||||
});
|
||||
|
||||
it("anchors the turn on the new request, not the old plan", () => {
|
||||
const rendered = render({ reentry: true, planExists: true });
|
||||
const reentry = rendered.slice(rendered.indexOf("## Re-entry"));
|
||||
// The new request is the primary input; the old plan is reference only.
|
||||
expect(reentry).toMatch(/NEW request[\s\S]*primary input/);
|
||||
// Corrections to an incomplete old plan must be folded into the new plan,
|
||||
// never substituted for it (the reported failure: dropping the new request).
|
||||
expect(reentry).toMatch(/combine, never substitute/);
|
||||
});
|
||||
|
||||
it("does not contradict the planExists guidance on a different task", () => {
|
||||
const rendered = render({ reentry: true, planExists: true });
|
||||
// planExists branch: different task -> leave old plan, write a fresh file.
|
||||
expect(rendered).toContain("leave that plan in place and start a fresh");
|
||||
// Re-entry must agree; the old "Different task -> overwrite it" directive is gone.
|
||||
expect(rendered).not.toMatch(/[Dd]ifferent task → overwrite/);
|
||||
});
|
||||
});
|
||||
@@ -48,11 +48,14 @@ Every question MUST change the plan or settle a load-bearing choice. Batch them.
|
||||
{{#if reentry}}
|
||||
## Re-entry
|
||||
|
||||
You are re-entering plan mode with a NEW request. That new request is the primary input and MUST be planned; the existing plan is only reference. You NEVER narrow the turn to reconciling the old plan and drop the new request.
|
||||
|
||||
<procedure>
|
||||
1. Read the existing plan.
|
||||
2. Compare the new request against it.
|
||||
3. Different task → overwrite it. Same task continuing → update it and delete outdated sections.
|
||||
4. Write your plan's `<slug>`/title as plain text to `xd://propose` when complete.
|
||||
1. Read the new request and make it the plan you build this turn.
|
||||
2. Read the existing plan as reference only.
|
||||
3. Same task continuing → update that plan with `{{editToolName}}` and delete outdated sections. Different task → leave that plan in place and write a fresh `local://<slug>-plan.md` for the new request.
|
||||
4. If the old plan has unfinished or broken work the new request depends on, fold those corrections INTO the new plan — combine, never substitute the old fix for the new request.
|
||||
5. Call `resolve` with `action: "apply"` and `extra: { title }` when the new request is decision-complete.
|
||||
</procedure>
|
||||
{{/if}}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user