Skip to content

fix(omp): OMP's plan review shows as a plan card you can answer from desktop and mobile chat - #31635

Merged
AmoabaKelvin merged 1 commit into
mainfrom
omp-plan-review
Oct 22, 2026
Merged

AmoabaKelvin merged 1 commit into
mainfrom
omp-plan-review

Conversation

@AmoabaKelvin

@AmoabaKelvin AmoabaKelvin commented Oct 21, 2026 •

Copy link
Copy Markdown
Collaborator
Files Added Deleted Net
Test 3 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​235 0 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​235
Prod 10 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​260 $\color{#cf222e}{\Huge{\mathbf{−}}}$​18 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​242

Fixes #24415

What users saw

When terminal OMP opened its plan review, the chat on the phone and desktop showed no plan and no buttons. The row read "done" because OMP aborts the turn to open the overlay. The only way out was the raw terminal and arrow keys.

Now

The row goes to Blocked and the chat shows the plan in the existing plan approval card (the one Claude's ExitPlanMode uses), with OMP's own choices: Approve and execute, Approve and compact context, Approve and keep context, Refine plan, Save and quit. Disabled choices are left out.

  • Approve and Save are answered through OMP's ctx.resolvePlanReview.
  • Refine closes the review (OMP's refine needs feedback text the card has no field for). OMP stays in plan mode, and the user's next message is the refinement. The card says so.

Root cause and fix

OMP 18.12 fires plan_review_requested / plan_review_resolved and offers ctx.resolvePlanReview. Orca's OMP status extension never listened for them.

  • The extension (src/main/pi/omp-plan-review-source.ts) posts the open review, and the hook listener makes it a blocked row with a { planReview } prompt envelope. The plan is clipped to the field's 16k cap so the JSON stays valid.
  • Answering: while the overlay has the keyboard, typing a command into the PTY doesn't reach OMP. Each card choice instead writes a private OSC naming the review (ESC ] 7503 ; orca-plan-review ; <reviewId> ; <choice> BEL) through the existing raw approval send path, the same one approvals already use locally, over SSH, on orcad and from the phone's terminal.send. The extension reads it with ctx.ui.onTerminalInput before the overlay does and calls resolvePlanReview. A card for a replaced review is consumed and ignored.
  • The wait ends on the review's own plan_review_resolved: done if OMP is idle, working if it started executing. An agent_end from an earlier turn's async child does not clear the card.
  • Mobile reuses the shared parser. The plan envelope wins over mobile's text heuristic, which could otherwise send y into the overlay.

Compatibility:

  • Older OMP never fires these events, so it behaves as before.
  • Old desktop or phone clients ignore the new planReview key. They see the Blocked row but no card.
  • The ACP chat route is unchanged: Orca never enters OMP plan mode there.

Verification

  • Focused tests: extension forwarding and answer routing, listener state and envelope, mobile card precedence. They fail without the fix.
  • Real OMP 18.12.2 in a hidden dev build of this branch, plan review opened with /plan-review:
    • The card showed.
    • Approve and execute started execution in a new session.
    • Refine closed the overlay and left OMP in plan mode.
    • Save and quit wrote the plan file.
    • The same flow on origin/main left the row at Done with no card.
  • Mobile: the real envelope captured from that run, rendered by the mobile MobileNativeChatPermission through a web-bundle harness. Tapping Approve sends the exact OSC. No paired device run.
  • Not checked live: Windows (ConPTY) and SSH hosts. Both carry the bytes through the same raw write path approvals already use.

Visual Proof

Before (origin/main): OMP's plan review is open in the terminal; chat shows nothing and the row says Done.

before.mp4

After: the plan card shows; Approve and execute starts the plan in OMP.

after.mp4

Mobile card (same envelope, mobile component):

mobile plan card

@coderabbitai

coderabbitai Bot commented Oct 21, 2026 •

Copy link
Copy Markdown
Contributor

Review in Change Stack →

📝 Walkthrough

The change adds OMP plan-review prompt serialization, validation, and answer mapping. Agent status handling now tracks plan-review requests and resolutions, while the PI handler forwards enabled choices and handles terminal input. Desktop and mobile chat parse review prompts into cards that show the proposed plan and available options.

Priority: ⚪ Pending latest changes

Merge Risk: 🔵 Low · up to 6bf41

OMP plan reviews are broadly supported, but malformed or oversized reviews and stale resolutions can suppress or clear the review card in bounded cases.

Pre-merge checks: Passed 4 · Failed 1

Failed Failed checks: 1 warning

Check name Status Explanation Resolution
Docstring Coverage Warning Docstring coverage is 52.38% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 21 functions across 12 files. (1 skipped:… Write docstrings for the functions missing them to satisfy the coverage threshold.
Passed Passed checks: 4 passed
Check name Status Explanation
Title check Passed The title clearly identifies the main change: OMP plan reviews are presented as answerable plan cards in desktop and mobile chat.
Description check Passed The description explains the user impact, implementation, rationale, linked issue, visual proof, testing, compatibility, and known verification gaps. It omits several template checklist sections, but …
Linked Issues check Passed Issue #24415 requires mobile interaction with OMP plan reviews instead of a stalled conversation. The PR adds a mobile planReview card, gives it precedence over approval text, exposes enabled review…
Out of Scope Changes check Passed The shared prompt envelope, desktop and mobile cards, OMP event listener, blocked-row state, OSC routing, localization, and tests directly support issue #24415. The PR preserves the ACP route and comp…
Full details: Description check

Explanation

The description explains the user impact, implementation, rationale, linked issue, visual proof, testing, compatibility, and known verification gaps. It omits several template checklist sections, but the required change details are substantially complete.

Full details: Linked Issues check

Explanation

Issue #24415 requires mobile interaction with OMP plan reviews instead of a stalled conversation. The PR adds a mobile planReview card, gives it precedence over approval text, exposes enabled review choices, and routes answers through review-specific OSC input. The listener publishes the blocked review and handles resolution. Focused tests cover mobile precedence, forwarding, answer routing, and review state.

Full details: Out of Scope Changes check

Explanation

The shared prompt envelope, desktop and mobile cards, OMP event listener, blocked-row state, OSC routing, localization, and tests directly support issue #24415. The PR preserves the ACP route and compatibility behavior. No unrelated change is demonstrated by the reviewed summary.

Full details: Docstring Coverage

Explanation

Docstring coverage is 52.38% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 21 functions across 12 files. (1 skipped: 1 unsupported.)

Resolution

Write docstrings for the functions missing them to satisfy the coverage threshold.

✨ Finishing Touches: 💡 1
📝 Generate docstrings: 💡
  • Commit to this branch
  • Create a new PR
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1


ℹ️ Review info
⚙️ Run configuration
  • Configuration used: Repository UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: fd0f6c64-b545-4251-a8dd-a7300a70894b
📥 Commits

Reviewing files that changed from the base of the PR and between 7a06b65 and 4ce13b899cce2762c315fca9b3702589080e6f28.

📒 Files selected for processing: 13
  • mobile/src/session/mobile-native-chat-permission.ts
  • mobile/src/session/use-mobile-native-chat-prompts.test.ts
  • mobile/src/session/use-mobile-native-chat-prompts.ts
  • src/main/pi/agent-status-extension-test-harness.ts
  • src/main/pi/agent-status-handler-source.ts
  • src/main/pi/agent-status-omp-plan-review.test.ts
  • src/main/pi/omp-plan-review-source.ts
  • src/renderer/src/components/native-chat/native-chat-interactive-prompt.ts
  • src/renderer/src/i18n/locales/en.json
  • src/shared/agent-hook-listener-omp-plan-review.test.ts
  • src/shared/agent-hook-listener/providers/pi-family-events.ts
  • src/shared/agent-hook-listener/providers/pi-family-tool-fields.ts
  • src/shared/omp-plan-review-prompt.ts

Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 7 remain after this review.

" if (typeof data !== 'string' || !data.startsWith(OMP_PLAN_REVIEW_ANSWER_PREFIX)) return undefined",
" const [answerId, choice] = data.slice(OMP_PLAN_REVIEW_ANSWER_PREFIX.length).replace(/\\x07$/, '').split(';')",
// Why: a card drawn for a replaced review must not answer or dismiss this one.
' if (answerId !== reviewId) return { consume: true }',

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

Validate the answer choice and the event.options shape in the generated handler.

The handler trusts two values without a runtime check:

  • Line 22 parses the answer with split(';'). If the data is …;<reviewId> with no ;choice, choice is undefined. The handler then calls ctx.resolvePlanReview(reviewId, undefined) and consumes the input.
  • Line 34 calls .filter on event.options ?? [] and reads option.disabled. If options is not an array, or an entry is null, the handler throws. This happens after ompPlanReview and the input listener are already registered. The review then never posts as blocked, and the listener stays active.

OMP 18.12.2 sends well-formed data, so this only matters for other OMP builds or malformed input. The fix is cheap.

🛡️ Proposed fix
-    '      if (answerId !== reviewId) return { consume: true }',
+    '      if (answerId !== reviewId || typeof choice !== \'string\' || choice.length === 0) return { consume: true }',
-    '      options: (event.options ?? []).filter((option) => !option.disabled).map((option) => ({ id: option.id, label: option.label })),',
+    '      options: (Array.isArray(event.options) ? event.options : []).filter((option) => option && !option.disabled).map((option) => ({ id: option.id, label: option.label })),',

Based on learnings: "check whether boundary data is used with shape or type assumptions not guaranteed by the boundary API or earlier runtime validation."

Also applies to: 34-34

Source: Learnings

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1


ℹ️ Review info
⚙️ Run configuration
  • Configuration used: Repository UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: d33c5bce-ed1c-4957-9dc6-9075c78fbd17
📥 Commits

Reviewing files that changed from the base of the PR and between 4ce13b899cce2762c315fca9b3702589080e6f28 and c5f7194.

📒 Files selected for processing: 1
  • src/shared/omp-plan-review-prompt.ts

Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 6 remain after this review.

Comment on lines +49 to +54
while (json.length > AGENT_STATUS_INTERACTIVE_PROMPT_MAX_LENGTH && text.length > 0) {
const excess = json.length - AGENT_STATUS_INTERACTIVE_PROMPT_MAX_LENGTH
text = text.slice(0, Math.max(0, text.length - excess - TRUNCATION_NOTE.length))
json = serialize(`${text}${TRUNCATION_NOTE}`)
}
return json

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win

Enforce the prompt cap when non-plan fields are large.

If the serialized options or reviewId exceed the cap, the loop stops when text is empty. It then returns an oversized prompt. Bound those fields or reject an envelope that still exceeds AGENT_STATUS_INTERACTIVE_PROMPT_MAX_LENGTH after clipping the plan. This keeps a large option label from breaking the status field’s length contract.

OMP 18.12 reports its plan review overlay to extensions and lets them answer it
with ctx.resolvePlanReview. Orca's status extension now posts the open review as a
blocked row with a plan envelope; desktop and mobile draw it with the existing plan
approval card. Each choice writes a private OSC naming the review, which the
extension reads through ctx.ui.onTerminalInput before the overlay sees it: approvals
and save go to ctx.resolvePlanReview, refine closes the overlay so the user can send
the change, and a card for a replaced review is ignored. The review stays blocked
until it resolves, even if an earlier turn's async child finishes under it.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Caution

Some comments are outside the diff and can’t be posted inline due to GitHub limitations.

⚠️ Outside diff range comments (1)

🟡 Minor · Preserve the OMP review ID through resolution handling. · pi-family-events.ts:71-79

src/shared/agent-hook-listener/providers/pi-family-events.ts:71-79
🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win

Preserve the OMP review ID through resolution handling.

/hook/omp accepts plan_review_resolved directly, outside the generated handler that rejects stale IDs. The listener clears interactivePrompt for every such event. Because the forwarded event omits review_id, an older resolution can clear the replacement review and remove its card.

Suggested fix
--- a/src/main/pi/omp-plan-review-source.ts
+++ b/src/main/pi/omp-plan-review-source.ts
@@
-    post('plan_review_resolved', { is_idle: isIdle })
+    post('plan_review_resolved', { review_id: event.reviewId, is_idle: isIdle })
--- a/src/shared/agent-hook-listener/providers/pi-family-events.ts
+++ b/src/shared/agent-hook-listener/providers/pi-family-events.ts
@@
   if (
     !stateName ||
     (stateName === 'working' && holdsOmpApproval(state, agentType, eventName, paneKey)) ||
     (eventName === 'agent_end' && holdsOmpPlanReview(state, agentType, hookPayload, paneKey))
   ) {
     return null
   }
+  if (isOmpPlanReviewEnd) {
+    const activeReviewId = parseOmpPlanReviewPrompt(
+      state.lastToolByPaneKey.get(paneKey)?.interactivePrompt
+    )?.reviewId
+    if (readString(hookPayload, 'review_id') !== activeReviewId) return null
+  }

   const snapshot = resolveToolState(

ℹ️ Review info
⚙️ Run configuration
  • Configuration used: Repository UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: fe257841-712a-43f1-b1af-41bfb956623f
📥 Commits

Reviewing files that changed from the base of the PR and between c5f7194 and 6bf419ea1f81f5cfa2ccd628d5e90a4f6519e209.

📒 Files selected for processing: 3
  • mobile/src/session/mobile-native-chat-permission.ts
  • src/renderer/src/components/native-chat/native-chat-interactive-prompt.ts
  • src/renderer/src/i18n/locales/en.json
🚧 Files skipped from review as they are similar to previous changes: 1
  • src/renderer/src/i18n/locales/en.json

Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 4 remain after this review.

@AmoabaKelvin
AmoabaKelvin merged commit b0fcdb4 into main Oct 22, 2026
43 of 44 checks passed
@AmoabaKelvin
AmoabaKelvin deleted the omp-plan-review branch October 22, 2026 00:10
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.

[Bug]: Mobile App hangs when the agent presents a plan for review

1 participant