Skip to content

M79: plans as files — save the approved plan, implement it in a fresh conversation - #53

Open
RandyNorthrup wants to merge 11 commits into
mainfrom
feature/m79-plans-as-files
Open

RandyNorthrup wants to merge 11 commits into
mainfrom
feature/m79-plans-as-files

Conversation

@RandyNorthrup

Copy link
Copy Markdown
Owner

What

M79 (PLAN D49): a plan the user approved survives and can drive a clean run, on both backends.

  • A Plan-mode reply gets Save plan and Implement in a fresh conversation; pressing either is the approval. A reply counts as a plan only if its turn was sent in Plan mode, not steered, and finished without leaving Plan mode.
  • Plans are saved as Markdown in .agents/plans/YYYY-MM-DD-<slug>.md, Muse Code's own convention (its bundled plan skill in 1.4.0), recorded in D13. .agents is a protected path, so saving asks first. Saving never replaces a file (hidden stage + hard link), and the folder is re-checked against link/junction swaps.
  • Implement starts a fresh conversation from the plan alone, through a reusable startFromBrief path that M74's /handoff will reuse. On the Model API the plan's steps seed the todo list; on Muse Code the brief asks the agent to list them itself.
  • Plans… in the palette lists saved plans to open or implement.

Trust

  • A plan file picked from Plans… is untrusted content (it may come from a clone or another model): the conversation starts in Manual (or Plan), whatever the initial mode says, and the model is told nobody confirmed who wrote it. Only a reply saved from a Plan-mode turn in this conversation gets the "approved" wording. Never Bypass in a remote window.
  • Hidden HTML in a plan is detected: Save warns, Implement saves but does not start.
  • Restricted Mode refuses Save and Implement; Plans… still lists and opens files.

Evidence

  • Muse Code's plan marker comes from a live capture (1.4.0, contributor model, empty workspace, 19 model attempts); the fixture matches it byte for byte.
  • Live Model API run through the real controller: 10 requests (~$0.0008), Plan → Save → Implement → todo list completed → file written.
  • Reviewed in three parallel classes; every finding fixed in 39bb79e (findings table in docs/certification/m79.md). 75 red drills.
  • npm run quality exited 0 (run alone). Muse Code Implement was not run live, as the cert says.

🤖 Generated with Claude Code

RandyNorthrup and others added 4 commits September 28, 2026 01:17
In Plan mode the latest reply gets Save plan and Implement in a fresh
conversation; pressing either is the approval. Neither backend marks a
plan on the wire: a live Muse Code 1.4.0 Plan-mode capture (19 attempts)
showed the plan as an ordinary agentMessage wrapped in its bundled plan
skill's handoff, and MSP has no todo-set command.

- Save writes the plan byte for byte to .agents/plans/YYYY-MM-DD-<slug>.md,
  the location Muse Code's own plan skill names (D13), with a numeric
  suffix on a taken name. It is published by a hard link from a hidden
  stage (createFileExclusively, now shared with memory), confined to the
  workspace's own .agents/plans, asks first (.agents is protected, D24),
  and is refused in Restricted Mode. A Muse Code plan reply's two handoff
  lines are left out.
- Implement starts a new conversation from a brief (ConversationBrief,
  startFromBrief, for M74's /handoff): the plan file as named text, a
  MODEL_TEXT note, nothing else of the old conversation, the starting
  permission mode. On the Model API the plan's steps become the todo list
  first (AgentSession.setTodos); on Muse Code the note asks the model to
  take them as its list.
- Plans... in the palette lists the saved plans to open or implement.
- 25 strings in fifteen tables, harness scenarios plan, plan-brief and
  plan-narrow, 17 red drills, a live Model API case (7 requests);
  npm run quality exits 0.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…iles

Brings in D49's settled rules (2407109), among them "Untrusted content":
a conversation built on untrusted content starts in a mode that asks,
whatever initialPermissionMode says. The M79 review fixes build on it.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Merges origin/plan/d49-competitive-program (2407109) for D49's settled
"Untrusted content" rule, then fixes every finding of the three class
reviews of 059ea2a in one pass.

- A plan picked from Plans... is untrusted content: it starts in Manual
  (Plan when that is the starting mode), whatever initialPermissionMode
  says, and its note tells the model nobody confirmed who wrote it. Only
  a reply saved from a Plan-mode turn here is "approved"; even that never
  starts in Bypass in a remote window.
- Save and Implement read the reply back on every press, resume the
  conversation after a restart (resumeTarget), and say why when they do
  nothing (another conversation, history not served, too large, a busy
  action, saved but not started). A reply counts only when its turn was
  sent in Plan mode, not steered, and finished with Plan never left.
- The same plan saved already is found by its bytes, not written twice.
  A plan with HTML the panel hides is saved with a warning, not started.
- createFileExclusively checks the folder again after mkdir and before
  the link, for plans and memory notes (a junction swap after the check
  is refused); a file where the folder should be says so; a held stage is
  removed again and stale stages swept. Plan files keep the plan limit
  even when they start like a PDF; names refuse control and format
  characters; cuts keep whole characters.
- The model reads English MODEL_TEXT and, on the Model API, the steps its
  todo list was set to; a failed brief takes its todo list and chip back.
  The log names a plan by its date and a hash.
- 9 strings in fifteen tables, the plan-brief harness as the Model API,
  75 red drills (SHA-256 restored), live case19 through the panel's
  controller (10 requests, about $0.0008); npm run quality exits 0.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@RandyNorthrup

Copy link
Copy Markdown
Owner Author

@codex review

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 28, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-09-28T20:19:26.484663Z 01f7f76 Manual request
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 335a533fff

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/core/plans/planDocument.ts Outdated
Comment thread src/host/conversation/conversationController.ts Outdated
RandyNorthrup and others added 2 commits September 28, 2026 08:46
… a refused mode change

Codex on PR #53:
- P1: hasHiddenMarkup found fences by a line prefix, so a backtick fence
  whose info string holds a backtick hid the lines after it while the
  panel rendered them, an HTML comment included. The plan is now parsed
  with the panel's own grammar (mdast-util-from-markdown +
  micromark-extension-gfm + mdast-util-gfm, the versions react-markdown
  and remark-gfm resolve to) and every html node is flagged. Swept: the
  title and the steps come from the same tree; link and picture titles
  and definitions nothing refers to, which the panel never shows, are
  flagged too, and the notices say so in all 15 tables. A jsdom test
  checks the flags against MarkdownView's own render.
- P2: pending plan turns were cleared before setApprovalMode succeeded;
  a refusal now puts them back (a turn that finished meanwhile becomes a
  plan turn). Swept: updateEffort no longer shows an effort the session
  refused, unless the new model no longer serves the old tier.

Pinned in PLAN.md D3; +114.5 KiB host bundle (563.2 of 600 KiB);
character-entities added to the notices. 19 red drills, SHA-256
restored.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The panel's Markdown parser (114.5 KiB) no longer rides in the activation
bundle, following M57's pattern: src/core/plans/planMarkdown.ts is built
from src/host/planMarkdownEntry.ts into dist/planMarkdown.js (114.7 KiB,
budget 150 KiB) and required by planMarkdownLoader on the first Save
plan, Implement or Plans...; dist/extension.js is 449.7 KiB again.

- No fallback: a reader that cannot load refuses the plan action with
  planMarkdownUnavailable (15 tables) and is tried again next time; the
  hidden-text check is never skipped.
- The reader imports no value of shared/constants (it would bring the
  English table): it lists items, planDocument.ts cuts and caps them.
- check-bundle-split fails when dist/extension.js carries the reader, its
  entry or any file of the parser's packages; budget, host-globals check,
  .vscodeignore, notices, knip, dpdm and an integration test cover the
  new bundle. requireFile/forgetFile moved to host/lazyBundle.ts.
- Tests: the real built bundle, a missing and a wrong one, and the
  controller refusing Save, Implement and Plans...; the jsdom parity test
  stays. 26 red drills, SHA-256 restored (one the split gate itself).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@RandyNorthrup

Copy link
Copy Markdown
Owner Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 6729c3f07a

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/core/plans/planMarkdown.ts Outdated
Comment thread src/core/plans/planDocument.ts Outdated
RandyNorthrup and others added 4 commits September 28, 2026 10:25
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Codex's third round on PR #53: a link's destination was sent in the
approved brief but not shown (P1), and the log took the first ten
characters of any plan name (P2). Per the owner's rule, a redesign:

- One rewritten tree for both sides. showPlanParts (shared/planView.ts)
  turns every part of a parsed plan into rendered text: a link's
  destination after its text as <https://...>, a picture's alt text and
  source, titles, definitions and footnotes as paragraphs, a code fence's
  whole info string as its label. The panel renders the reply the plan
  actions sit under through it (MarkdownView isPlan, a remark plugin);
  the brief is the same tree written back as Markdown (briefText,
  mdast-util-to-markdown 2.1.2), for a reply and for a file.
- The hidden-markup predicate is gone; only raw HTML, which the panel
  never renders, is found (hasRawHtml) and refused as before. The notices
  say HTML again in all 15 tables.
- A jsdom test renders 13 tricky plans in MarkdownView and checks that
  every text, code and code-info leaf of the brief appears in order in the
  DOM text, and that the brief holds no link, picture, definition or
  footnote a view could show only part of.
- planLogName: YYYY-MM-DD-#hash.md only for a verified real day, else
  #hash.md; the memory note's stage warning no longer names the note.
- A plan file must be UTF-8 text without control characters.
- 17 red drills, SHA-256 restored. dist/planMarkdown.js 139.0 KiB (budget
  150), dist/extension.js 464.8 KiB after merging main.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Grok Build on 6fe3e07:
- P1 fence info string: rejected on inspection. hast-util-to-jsx-runtime
  2.3.6 passes a data-* property under its attribute name (lib/index.js:617),
  so the panel reads data-info and labels the block with the whole info
  string (rendered in jsdom). The review's case joins the parity corpus and
  a test asserts the label; reading dataInfo turns both red.
- P1 control and format characters: a direction override or zero-width
  character is painted otherwise than the model reads it. hasUnshownCharacters
  (Cc but tab/LF/CR, DEL and C1 included; Cf) refuses Save and Implement of a
  reply and Implement of a plan file, with planUnshownCharacters (15 tables).
- P2 PLAN.md D3: the measured sizes (planMarkdown.js 139.0 KiB,
  extension.js 464.8 KiB).

6 red drills, SHA-256 restored.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…uct call for the owner

PR #53's two open Codex threads (on 6729c3f) were fixed by 6fe3e07: link
destinations are shown in the plan view and briefed from the same tree,
and planLogName keeps a prefix only for a verified day. This pins the
thread's exact name (customer-secret.md) in the log test and states in
the cert and README that plans with any control or format character
(emoji joiners, LRM/RLM included) are refused, a product call to make.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@RandyNorthrup

Copy link
Copy Markdown
Owner Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 01f7f7639b

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/webview/state/uiState.ts
Comment thread src/host/conversation/conversationController.ts Outdated
…gs no path

Codex on 01f7f76:
- P2 a view/gap reload rebuilt user cards without isPlanTurn, so Save plan
  and Implement vanished while the host still held the plan turn.
  historyLoaded now carries planTurnIds (finished or still running Plan-mode
  turns of those items) and the panel's replay marks their cards. isPlanTurn
  is the only per-card flag M79 added.
- P2 Plans... Open called openFile outside the plan failure path, so a plan
  deleted after the pick reached the general handler, which logs the error
  detail (its path). It now fails through planFailed: planOpenFailed (15
  tables) and the reason in the panel, only the error kind in the log.
  Every plan operation's failure path now does the same.

6 red drills, SHA-256 restored.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@RandyNorthrup

Copy link
Copy Markdown
Owner Author

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, add credits to your account and enable them for code reviews in your settings.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant