Merge remote-tracking branch 'origin/farm/80b5de49/plan-mode-reentry-anchor-new-request'

This commit is contained in:
can1357
2026-07-15 15:34:06 +02:00
3 changed files with 49 additions and 4 deletions
+1
View File
@@ -41,6 +41,7 @@
- Fixed prewalk repeatedly continuing after a bash-only task such as `commit` had already completed ([#5551](https://github.com/can1357/oh-my-pi/issues/5551)).
- Fixed the Bash tool hanging when in-process commands read process substitution operands such as `<(cmd)` ([#5557](https://github.com/can1357/oh-my-pi/issues/5557)).
- Fixed `/share` and `/export` web views rendering inline Markdown inside list items as literal text ([#5567](https://github.com/can1357/oh-my-pi/issues/5567)).
- Fixed plan-mode re-entry dropping a new plan request when a prior plan artifact existed: the re-entry prompt led with the old plan and contradicted the plan-file guidance, so weak models only reconciled the incomplete previous plan. Re-entry now anchors on the new request and folds any old-plan corrections into it ([#5576](https://github.com/can1357/oh-my-pi/issues/5576)).
## [16.5.2] - 2026-07-14
@@ -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}}