Commit c7f7323
authored
🤖 fix: preserve plan file after propose_plan Start Here (#1290)
Fixes a case where clicking **Start Here** on a proposed plan deletes
the plan
markdown file, preventing post-compaction `plan_file_reference`
attachments.
- Stop requesting `deletePlanFile` when starting from a proposed plan
- Include the plan path in the Start Here message (when known)
- Add a unit test covering the Start Here hook inputs
Validation: `make static-check`.
---
<details>
<summary>📋 Implementation Plan</summary>
# Preserve plan file across compactions (post-compaction attachments)
## What’s happening (root cause)
Compaction itself **does not delete the plan file**.
- **Compaction path**: `CompactionHandler.performCompaction()` clears
only `chat.jsonl` via `HistoryService.clearHistory()` and then appends a
summary message. It never calls the workspace-level history helpers that
clean up plan files.
- `src/node/services/compactionHandler.ts` →
`historyService.clearHistory()`
- `src/node/services/historyService.ts` → `truncateHistory(..., 1.0)`
(deletes `chat.jsonl` only)
The plan markdown file is deleted by *other* “reset-like” flows:
1) **“Start Here” on a proposed plan** (most likely):
- UI: `ProposePlanToolCall.tsx` calls `useStartHere(..., {
deletePlanFile: true })`.
- ORPC: `api.workspace.replaceChatHistory({ deletePlanFile: true })`.
- Backend: `WorkspaceService.replaceHistory(..., { deletePlanFile: true
})` → `deletePlanFilesForWorkspace()`.
2) **Full history clear**:
`WorkspaceService.truncateHistory(workspaceId, 1.0)` also deletes the
plan file.
Because the proposed-plan “Start Here” flow deletes the plan file, later
compactions can’t generate the `plan_file_reference` post-compaction
attachment (AttachmentService reads the plan file from disk).
## Repro (current behavior)
1. Enter Plan Mode and write a plan (plan file exists under
`~/.mux/plans/<project>/<workspace>.md`).
2. Call `propose_plan`.
3. Click **Start Here** in the plan tool UI.
4. Observe the plan file is removed from `~/.mux/plans/...`.
5. Later, when compaction happens,
`AttachmentService.generatePlanFileReference()` returns `null` (file
missing), so the post-compaction attachment does not include the plan.
## Fix options
### Option A (recommended): Don’t delete the plan file on propose_plan
“Start Here”
**Net LoC (product code): ~+0 to +10**
1. **Frontend:** keep the plan file on disk when the user clicks **Start
Here**:
- `src/browser/components/tools/ProposePlanToolCall.tsx`
- Change `useStartHere(..., { deletePlanFile: true })` → omit the option
(or pass `{ deletePlanFile: false }`).
2. **Frontend UX (recommended):** add a small note into the Start Here
content that the plan is still stored at the plan file path.
- Keep it short and conditional on `planPath` being known (so it doesn’t
break legacy/edge cases).
- Example copy: `*Plan file preserved at: <path>*`.
3. **Backend comment cleanup:** update/remove the comment in
`WorkspaceService.replaceHistory()` that claims propose_plan Start Here
deletes the plan file.
4. (Optional) Keep the backend `deletePlanFile` plumbing intact (it may
still be useful for a future explicit “delete plan file” action).
Why this works: post-compaction attachments read the plan file from disk
(`AttachmentService.generatePlanFileReference`), and the attachment
system is explicitly “mode-agnostic” (valuable in exec mode too).
### Option B: Keep deleting the plan file, but persist a “plan snapshot”
for compaction
**Net LoC (product code): ~+120–200**
Store an immutable snapshot at Start Here time (e.g.,
`~/.mux/sessions/<ws>/plan-snapshot.md`), and have `AttachmentService`
fall back to this snapshot when the plan file is missing.
This keeps the “no lingering plan files” behavior but still preserves
plan context for post-compaction attachments.
<details>
<summary>Details (Option B)</summary>
- On Start Here (propose plan), write snapshot before deleting plan
file.
- Update `AttachmentService.generatePlanFileReference()` to:
1) read `getPlanFilePath()`
2) read legacy path
3) read snapshot path
- Decide cleanup rules (delete snapshot on full clear, workspace delete,
etc.).
</details>
### Option C (defensive hardening): If plan file is missing, don’t
filter plan diffs out
**Net LoC (product code): ~+10–25**
In `AttachmentService.generatePostCompactionAttachments()`, only filter
out plan paths from `edited_files_reference` **if** a
`plan_file_reference` attachment was successfully created.
This doesn’t fix the Start Here case (history was replaced, so there are
no plan diffs to extract), but it prevents *other* “plan file missing”
scenarios from silently dropping plan context.
## Tests / validation
1. Manual:
- Run through the repro steps and confirm the plan file still exists
after Start Here.
- Confirm the Start Here message includes the “plan file preserved at …”
note (with the correct path).
- Trigger compaction and verify a `plan_file_reference` post-compaction
attachment is injected.
2. Automated (pick one):
- Browser unit test: mount `ProposePlanToolCall` and assert
`replaceChatHistory` is called with `deletePlanFile: undefined/false`,
and that the `summaryMessage` content includes the plan path note.
- Node unit test: verify `WorkspaceService.replaceHistory(..., {
deletePlanFile: true })` deletes plan file, and `deletePlanFile: false`
does not (guard against regressions).
</details>
---
_Generated with [`mux`](https://github.com/coder/mux) • Model:
openai:gpt-5.2 • Thinking: xhigh_1 parent 07a55f2 commit c7f7323
File tree
3 files changed
+103
-5
lines changed- src
- browser/components/tools
- node/services
3 files changed
+103
-5
lines changedLines changed: 95 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
223 | 223 | | |
224 | 224 | | |
225 | 225 | | |
226 | | - | |
227 | | - | |
| 226 | + | |
| 227 | + | |
| 228 | + | |
| 229 | + | |
| 230 | + | |
228 | 231 | | |
229 | 232 | | |
230 | 233 | | |
| |||
233 | 236 | | |
234 | 237 | | |
235 | 238 | | |
236 | | - | |
237 | | - | |
| 239 | + | |
238 | 240 | | |
239 | 241 | | |
240 | 242 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1921 | 1921 | | |
1922 | 1922 | | |
1923 | 1923 | | |
1924 | | - | |
| 1924 | + | |
| 1925 | + | |
1925 | 1926 | | |
1926 | 1927 | | |
1927 | 1928 | | |
| |||
0 commit comments