Skip to content

fix(planreview): stop comments dirtying the plan, and gate decisions on saving - #115

Merged
tusharbhardwaj-bk merged 1 commit into
expbkmainfrom
t3code/brest-followup
Aug 18, 2026
Merged

tusharbhardwaj-bk merged 1 commit into
expbkmainfrom
t3code/brest-followup

Conversation

@tusharbhardwaj-bk

@tusharbhardwaj-bk tusharbhardwaj-bk commented Aug 18, 2026 •

Copy link
Copy Markdown
Collaborator

Three follow-ups from the first release on expbkt3.

1. Commenting re-sent the whole plan as a reviewer edit

Reported: sending annotations attached a <plan_edit mode="full"> block containing the entire document, even though nothing had been edited. The attached copy was Plate-normalized (***, \ hard breaks, * bullets), which was the clue.

Root cause, reproduced before changing anything: Plate serializes a commented leaf as an MDX JSX element, and mdast-util-to-markdown throws on it inside a table cell —

Error: Cannot handle unknown node `mdxJsxTextElement`
  ❯ handleTableCell mdast-util-gfm-table/lib/index.js:216

handleChange treats a failed serialize as a reviewer edit ("Treat an editor tree that cannot be serialized as changed"), so isDirty flipped, the client sent editedMarkdown, the server cut a version, and the diff against the agent baseline was non-empty — producing the full-plan resend. Any plan containing a table hit this the moment you commented on it.

The earlier trap test missed it because its fixture had no table.

Fix: stripPlanReviewCommentMarks removes comment properties and re-merges the leaves split: true fragmented; getMarkdown() and the change comparison both serialize through it. Comments are review state and can no longer influence what the agent is told changed. A second test asserts the raw serializer still throws, so the strip cannot be quietly deleted later on the assumption Plate handles it now.

2. Unsaved hand edits did not gate the decision

Deciding on a plan whose edits are not yet a version leaves it ambiguous which text was approved. Unsaved edits now replace the decision row with a single Save the plan, with a one-line explanation beneath.

3. Approving with open comments contradicted itself

The prompt said "implement the plan you proposed above, exactly as written" and then attached a list of requested changes. It now says to start implementing and treat the comments as amendments to apply along the way, explicitly not to return to planning. The button reads Approve with comments in that state.

Verification

151 tests passed across 10 scoped files. tsgo --noEmit clean for apps/web, packages/shared and apps/server; lint and check-fork-markers.ts pass.

One existing server test asserted the old approval wording for exactly this case and was updated to assert the new intent, including not.toContain("exactly as written").

Reviewer check on expbkt3: comment on a plan containing a table and send — the prompt should carry only the <review_comment> blocks and no <plan_edit>.

🤖 Generated with Claude Code


View with [code]smith Autofix with [code]smith
Need help on this PR? Tag @codesmith-bot with what you need. Autofix is disabled.

…on saving

Three follow-ups from reviewing the first release on expbkt3.

Commenting re-sent the entire plan as a reviewer edit. Plate serializes a
commented leaf as an MDX JSX element, and `mdast-util-to-markdown` throws on it
inside a table cell — so on any plan containing a table, serialization failed,
the editor's change handler read "cannot serialize" as a reviewer edit, and the
panel shipped the whole Plate-normalized document under `<plan_edit mode="full">`.
Nobody had edited anything. `stripPlanReviewCommentMarks` now removes comment
properties and re-merges the leaves `split: true` fragmented, so the plan is
always serialized as if it carried no comments. A test pins the underlying
serializer failure too, so the strip cannot be quietly dropped later.

Deciding on a plan with unsaved hand edits was ambiguous — neither side could say
afterwards which text was approved. Unsaved edits now replace the decision row
with a single **Save the plan**.

Approving with open comments told the agent to implement "exactly as written" and
then handed it a list of changes. It now says to start implementing and apply the
comments as amendments, without returning to planning.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@github-actions github-actions Bot added vouch:trusted PR author is trusted by repo permissions or the VOUCHED list. size:L labels Aug 18, 2026
@github-actions

Copy link
Copy Markdown

Thread transfer impact

✅ Thread transfer remains within every enforced ceiling.

ℹ️ No successful main baseline artifact is available yet. This run establishes the initial measurement.

Provider Metric Main baseline This PR Impact PR ceiling
Codex Total thread wire — 11.4 KiB — 15.1 KiB ✅
Codex Thread snapshot wire — 5.7 KiB — 7.3 KiB ✅
Codex Live turn WebSocket wire — 5.7 KiB — 7.8 KiB ✅
Codex Live turn WebSocket decoded — 50.4 KiB — 66.4 KiB ✅
Codex Live turn messages — 11 — 21 ✅
Claude Total thread wire — 11.5 KiB — 15.1 KiB ✅
Claude Thread snapshot wire — 5.7 KiB — 7.3 KiB ✅
Claude Live turn WebSocket wire — 5.7 KiB — 7.8 KiB ✅
Claude Live turn WebSocket decoded — 51.2 KiB — 66.4 KiB ✅
Claude Live turn messages — 11 — 21 ✅

Baseline: unavailable · PR result: 89c9f14 · Source CI: success

Scenario and decoded snapshot size

10 historical turns, 5 command tools per turn, 878.9 KiB retained MCP result per historical turn, and a 1.05 MiB retained result in the measured turn.

  • Codex decoded thread snapshot: 95.6 KiB
  • Claude decoded thread snapshot: 96.3 KiB

Updated in place by a trusted workflow. PR artifacts are strictly validated and never executed.

@tusharbhardwaj-bk
tusharbhardwaj-bk merged commit 217d391 into expbkmain Aug 18, 2026
14 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:L vouch:trusted PR author is trusted by repo permissions or the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant