Skip to content

fix(server): handle proposed_plan fallback after mode switch#2455

Open
pedrokpp wants to merge 4 commits intopingdotgg:mainfrom
pedrokpp:fix/proposed-plan-build-mode-fallback
Open

fix(server): handle proposed_plan fallback after mode switch#2455
pedrokpp wants to merge 4 commits intopingdotgg:mainfrom
pedrokpp:fix/proposed-plan-build-mode-fallback

Conversation

@pedrokpp
Copy link
Copy Markdown
Contributor

@pedrokpp pedrokpp commented May 2, 2026

What Changed

This fixes proposed-plan rendering when a user starts in Plan mode, switches the same thread back to Build mode, and then asks for another plan.

On the server side, proposed-plan fallback extraction now runs when an assistant message completes with <proposed_plan>...</proposed_plan> in its final text, even if the provider did not emit a canonical turn.proposed.completed event.

When that happens, T3 Code now:

  • extracts the markdown inside <proposed_plan>
  • upserts a real proposed plan for the thread
  • strips the <proposed_plan> block out of the visible assistant chat message so the raw tagged text is not rendered alongside the plan card
  • still records the assistant completion state needed for the normal plan follow-up flow

As a result, the Build-mode fallback now behaves like a normal proposed plan:

  • the plan card appears correctly
  • the raw tagged assistant text does not remain visible
  • if the user later switches back to Plan mode, they can implement or refine that plan as normal

Regression coverage was added around assistant-message completion so the plan is projected correctly and the assistant message is suppressed visually while preserving the turn bookkeeping required by the follow-up UI.

Why

There was a mode-transition bug in the Codex flow:

  1. Start in Plan mode
  2. Generate a plan
  3. Switch the thread to Build mode
  4. Ask the assistant to propose another plan

In that path, the provider could return a <proposed_plan> block as ordinary assistant text instead of emitting the canonical proposed-plan event.

Before this fix, the UI could render the raw tagged markdown as a normal assistant message, or render both the raw tagged message and the proposed plan card at the same time. A follow-up regression also showed that suppressing the assistant text too aggressively could break the normal Implement flow when switching back to Plan mode.

That behavior was incorrect. Proposed-plan blocks should project as first-class proposed plans, the raw tagged payload should not remain in the chat transcript when it only exists to back the plan card, and the resulting plan should still behave like a normal actionable plan afterward.

This server-side fallback is the smallest fix because it reuses the existing proposed-plan projection pipeline instead of changing web rendering rules or broader mode behavior.

UI Changes

Before
2026-05-02_01-25

After
2026-05-02_01-26
2026-05-02_01-24

Repro shown in before/after:

  1. Start in Plan mode
  2. Switch to Build mode
  3. Ask for another plan
  4. Observe raw <proposed_plan> text in chat before this fix
  5. Observe only the proposed plan card after this fix
  6. Switch back to Plan mode
  7. Confirm the plan can still be implemented normally

Checklist

  • This PR is small and focused
  • I explained what changed and why
  • I included before/after screenshots for any UI changes
  • I included a video for animation/interaction changes

Note

Medium Risk
Touches ProviderRuntimeIngestion message finalization/proposed-plan projection, which can affect chat transcript rendering and plan upsert behavior across turn/approval boundaries, but is scoped and covered by new tests.

Overview
Ensures proposed plans still project when providers return a <proposed_plan>...</proposed_plan> block as plain assistant text (e.g., after mode switches), by extracting the block on assistant completion and upserting it into thread.proposedPlans.

finalizeAssistantMessage now strips <proposed_plan> blocks from emitted assistant deltas, still completes the assistant message even if the visible text becomes empty, and returns the raw text so callers can derive fallback plan markdown; this fallback projection is triggered on item.completed, turn.completed, and approval-boundary finalization.

Adds regression tests covering plan extraction from tagged assistant text on message completion, turn completion, and buffered text flushed at request.opened.

Reviewed by Cursor Bugbot for commit bbc954c. Bugbot is set up for automated code reviews on this repo. Configure here.

Note

Fix proposed_plan block handling in assistant messages after mode switch

  • Adds detection and extraction of <proposed_plan> blocks embedded in assistant message text using two new regex constants and a helper in ProviderRuntimeIngestion.ts.
  • Strips <proposed_plan> content from assistant message deltas so plan markup is never rendered as chat text; instead, a proposedPlan entity is projected for the active turn.
  • Handles three completion paths: assistant.message.completed, turn.completed, and request.opened (pause boundary) — each now extracts and finalizes any buffered proposed plan.
  • Buffered assistant text containing a proposed plan is held back from flushing as a delta until a completion event arrives.
  • Behavioral Change: assistant.complete is now emitted even when visible text is empty, as long as raw content (e.g. a plan block) was present.

Macroscope summarized bbc954c.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 2, 2026

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 943f81b5-ad73-40ff-b30c-0e10a084aac4

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions github-actions Bot added vouch:unvouched PR author is not yet trusted in the VOUCHED list. size:S 10-29 changed lines (additions + deletions). labels May 2, 2026
@macroscopeapp
Copy link
Copy Markdown
Contributor

macroscopeapp Bot commented May 2, 2026

Approvability

Verdict: Needs human review

This PR introduces new runtime behavior for extracting proposed plans from assistant message text via XML tag parsing. The changes affect multiple message finalization paths in core orchestration logic, warranting human review.

You can customize Macroscope's approvability policy. Learn more.

@github-actions github-actions Bot added size:M 30-99 changed lines (additions + deletions). and removed size:S 10-29 changed lines (additions + deletions). labels May 2, 2026
Copy link
Copy Markdown
Contributor

@cursor cursor Bot left a comment

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 29b428d. Configure here.

Comment thread apps/server/src/orchestration/Layers/ProviderRuntimeIngestion.ts
Comment thread apps/server/src/orchestration/Layers/ProviderRuntimeIngestion.ts
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:M 30-99 changed lines (additions + deletions). vouch:unvouched PR author is not yet trusted in the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant