Merge PR #4166: fix(agent): preserve approved plan path (@roboomp)

This commit is contained in:
can1357
2026-07-01 21:53:19 +02:00
6 changed files with 50 additions and 18 deletions
+3
View File
@@ -50,6 +50,9 @@
### Fixed
- Fixed the multi-select (checkbox) `ask` picker showing no visible cursor on themes where `accent` fg is close to the terminal foreground; the focused option now renders as a full-width `selectedBg` highlight band spanning label and wrapped description rows, matching the Ctrl+R history overlay ([#4157](https://github.com/can1357/oh-my-pi/issues/4157)).
### Fixed
- Fixed approved plan execution prompts embedding the full plan instead of requiring the executor to read the durable `local://<slug>-plan.md` file. ([#4164](https://github.com/can1357/oh-my-pi/issues/4164))
## [16.2.12] - 2026-07-01
@@ -0,0 +1,34 @@
import { describe, expect, it } from "bun:test";
import { prompt } from "@oh-my-pi/pi-utils";
import planModeApprovedPrompt from "../prompts/system/plan-mode-approved.md" with { type: "text" };
import planModeCompactInstructionsPrompt from "../prompts/system/plan-mode-compact-instructions.md" with {
type: "text",
};
import planModeReferencePrompt from "../prompts/system/plan-mode-reference.md" with { type: "text" };
const PLAN_FILE_PATH = "local://durable-plan.md";
const PLAN_SENTINEL = "SENTINEL_HEADROOM_COMPRESSED_PLAN_CONTENT";
describe("approved plan execution prompts", () => {
it("loads the approved plan from the durable local file instead of embedding content", () => {
const approved = prompt.render(planModeApprovedPrompt, {
planContent: PLAN_SENTINEL,
planFilePath: PLAN_FILE_PATH,
contextPreserved: false,
});
const reference = prompt.render(planModeReferencePrompt, {
planContent: PLAN_SENTINEL,
planFilePath: PLAN_FILE_PATH,
});
const compact = prompt.render(planModeCompactInstructionsPrompt, {
planFilePath: PLAN_FILE_PATH,
});
for (const rendered of [approved, reference, compact]) {
expect(rendered).toContain(PLAN_FILE_PATH);
expect(rendered).not.toContain(PLAN_SENTINEL);
}
expect(approved).toContain("MUST read `local://durable-plan.md`");
expect(reference).toContain("MUST read `local://durable-plan.md`");
});
});
@@ -1,25 +1,22 @@
Plan approved.
{{#if contextPreserved}}
- Context preserved. Use conversation history when useful; this plan is the source of truth if it conflicts with earlier exploration.
- Context preserved. Use conversation history when useful; the plan file is the source of truth if it conflicts with earlier exploration.
{{/if}}
<instruction>
You MUST execute this plan step by step. You have full tool access.
You MUST read `{{planFilePath}}` before executing.
The file content is the authoritative plan; visible/compressed context is secondary.
Read failure? Report the exact path and error instead of guessing.
After reading, you MUST execute the plan step by step with full tool access.
You MUST verify each step before proceeding to the next.
{{#has tools "todo"}}
Before execution, initialize todo tracking with `todo`.
After reading the plan, initialize todo tracking with `todo`.
After each completed step, immediately update `todo`.
If `todo` fails, fix the payload and retry before continuing.
{{/has}}
The plan path is for subagent handoff only. You already have the plan; NEVER read it.
</instruction>
The full plan is injected below. You MUST execute it now:
<plan path="{{planFilePath}}">
{{planContent}}
</plan>
<critical>
NEVER stop because inline plan content is compressed, expired, or unrecoverable. Read `{{planFilePath}}`.
You MUST keep going until complete. This matters.
</critical>
@@ -12,5 +12,6 @@ You MUST drop:
- Restated context already present in the plan file.
{{#if planFilePath}}
The approved plan file is at `{{planFilePath}}`; it is the authoritative source of truth and need not be re-summarized in detail.
The approved plan file is at `{{planFilePath}}`; it is the authoritative source of truth.
You MUST preserve this durable path and the fact that the executor must read it directly after compaction.
{{/if}}
@@ -1,11 +1,10 @@
## Existing Plan
<plan path="{{planFilePath}}">
{{planContent}}
</plan>
The approved plan file is at `{{planFilePath}}`.
<instruction>
If this plan is relevant to current work and not complete, you MUST continue executing it.
If you do not have the current plan content in visible context, you MUST read `{{planFilePath}}`.
If the plan is stale or unrelated, you MUST ignore it.
The plan path is for subagent handoff only. You already have the plan; NEVER read it.
NEVER stop because inline plan content is compressed, expired, or unrecoverable. Read the file.
</instruction>
@@ -6666,9 +6666,8 @@ export class AgentSession {
const planFilePath = this.#planReferencePath;
const resolvedPlanPath = resolveLocalUrlToPath(planFilePath, this.#localProtocolOptions());
let planContent: string;
try {
planContent = await Bun.file(resolvedPlanPath).text();
await fs.promises.access(resolvedPlanPath, fs.constants.R_OK);
} catch (error) {
if (isEnoent(error)) {
return null;
@@ -6678,7 +6677,6 @@ export class AgentSession {
const content = prompt.render(planModeReferencePrompt, {
planFilePath,
planContent,
});
this.#planReferenceSent = true;