Skip to content

fix(server): keep a user's approval answer when Pi, OpenCode or Muse confirm it - #16064

Open
saphid wants to merge 111 commits into
pingdotgg:mainfrom
saphid:stack/21-plugin-approvals
Open

saphid wants to merge 111 commits into
pingdotgg:mainfrom
saphid:stack/21-plugin-approvals

Conversation

@saphid

@saphid saphid commented Oct 5, 2026 •

Copy link
Copy Markdown
Contributor

Pi, OpenCode and Muse can replace a user's recorded approval decision or question answer with an older cached request when the provider acknowledges it or the turn ends. Muse also marks declined approvals completed, and its intermediate approval updates can reopen an answered request.

Keep the orchestrator's persisted response authoritative. Pi suppresses its cached replacement after successful delivery. OpenCode retains the submitted response before delivery and uses native settlement to clean up tracking. Muse distinguishes T3 answers from automatic or native decisions: acknowledgements, same-requirement updates and cleanup preserve T3's answer and question card; a genuinely new approval requirement gets its own request identity after the decision acknowledgement. Terminal acknowledgements suppress trailing updates for stages that Muse already closed. Unanswered requests and automatic child approvals retain their event producers. Declines keep cancelled cards and nodes.

Rebuilt from @saphid's work in #16064, preserving the contributor's commits and merging main. Removed the OpenCode answering flag and separate cleanup helper. Consolidated five new replay cases into three integrated cases, strengthened three existing fixtures, and updated adapter tests to await the next native milestone rather than a redundant replacement event.

Verification at c3df197c75:

  • Main fails six focused assertions; Codex and Grok controls pass. Review regressions also fail immediately before their fixes: Muse cleanup loses answers, and a recorded-protocol stageResolved update reopens an answered request.
  • All 325 tests pass across the Pi, OpenCode and Muse adapters and orchestration replays (324 originally, 321 after consolidation, plus one combined Muse regression and two terminal/continuing-stage checks and one automatic-child error regression). The stage-update regression extends that case without increasing the count.
  • Scoped Pi/OpenCode/Muse/server typechecks, targeted lint and formatting pass. Two existing unused-layer warnings remain.
  • Independent Claude Opus 5.5 and Codex GPT-6 Sol reviews: round 1 found Muse cleanup/card replacement; round 2 found intermediate approval updates erasing ownership. Round 3 found a duplicate card after terminal multi-stage decisions. Round 4 found a stale fallback card after an automatic child decision error. All confirmed findings are fixed; both round-5 full-diff reviews returned no findings.

Before/after replay output and current-head checks.

Verification is headless: native-protocol replay through real orchestration, persisted projections and the effect worker. No real Pi/OpenCode/Muse account, browser, Electron, macOS, Windows, iOS or Android verification was run. Multi-stage checks cover updates received while the decision is in flight. Updates after acknowledgement, already-terminal updates and late acknowledgements after delivery errors are source-reviewed. No live client or remote/tunnel claim is made.

Human follow-up: accept and decline Pi/OpenCode approvals, answer questions, and decline a Muse approval; check that the answer and card status survive provider acknowledgement and reconnect. For Muse, also accept for the session and check that an intermediate stage update preserves that choice while a genuinely new requirement prompts separately. Pi's pre-existing expiry race between response commit and adapter delivery remains outside this fix. Retaining a user's answer after delivery errors records their intent; it does not prove provider receipt.

CI handoff: execution checks and correctness review pass. Macroscope Approvability is neutral, explicitly requesting human approval for concurrent provider lifecycle changes. This remains an unmerged human-review gate.

Tests: GPT-6 Astra; implementation: GPT-6.1 Sol; independent reviews: Claude Opus 5.5 and GPT-6 Sol. Codex and Claude harnesses in T3 Code.

@github-actions github-actions Bot added the vouch:trusted PR author is trusted by repo permissions or the VOUCHED list. label Oct 5, 2026
@macroscopeapp

macroscopeapp Bot commented Oct 5, 2026 •

Copy link
Copy Markdown
Contributor

Macroscope has since reviewed this pull request. An earlier review was skipped by a cost limit; a review has now completed, so that notice no longer applies.

@github-actions github-actions Bot added the size:XXL 1,000+ changed lines (additions + deletions). label Oct 5, 2026
@macroscopeapp

macroscopeapp Bot commented Oct 5, 2026 •

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Needs Human Approval

Macroscope's review found this PR requires human approval — This PR changes production approval and question lifecycle handling across the Pi, OpenCode, and Muse integrations, including response persistence, native acknowledgement cleanup, session status, and multi-stage approval state. The changes are substantial and concurrency-sensitive despite the accompanying test coverage.

You can add or adjust custom eligibility rules. Learn more.

@saphid
saphid force-pushed the stack/21-plugin-approvals branch 10 times, most recently from f6ea0da to cc46a47 Compare October 6, 2026 16:03
@saphid

saphid commented Oct 6, 2026

Copy link
Copy Markdown
Contributor Author

Review requested

Date (UTC) Reviewer Where
2026-10-06 Julius Discord DM

Logged so this PR shows when a maintainer was asked to review it.

@saphid
saphid force-pushed the stack/21-plugin-approvals branch 14 times, most recently from 24284ff to 3884505 Compare October 10, 2026 11:10
github-actions Bot and others added 3 commits October 12, 2026 11:27
…ions

PluginNotifications reaches the plugin supervisor through its module
namespace and builds each PluginHostCallError where the call fails, without
a forwarding helper or a shared stopped error.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A plugin with the `transforms` capability and a declared `transforms.enrich`
can now answer `t3.transform.enrich` with short titled context. Before the
provider session opens, the turn-start effect records one `plugin_context`
item per plugin (at most 4, plus one record counting any plugins not
called), calls the plugins outside the thread lock, saves their answers or
why none was added, and sends the provider the kept context ahead of the
user's text. Only turns the history pager counts are enriched; steering,
`/` commands and wakes are not. A retried start reuses the saved records and
never calls a plugin twice; Stop closes a running record. Failures fail open.

The records are existing `dynamic_tool` items, so every client decodes them.
The web/desktop inspector and the mobile expanded detail show "From <plugin>"
with the context, or "Not added" with the reason, instead of the input. The
capability's consent line now says the plugin reads your messages.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
A plugin that declares the approvals capability is offered the command,
file and permission requests agents send, once the request's approval card
is shown, and can approve or decline that one request through the
orchestrator's ordinary respond path; the first recorded answer wins and
anything else leaves the request with the user. Approval rows answered by
a plugin are titled with the plugin, and expanding them shows its reason
on web, desktop and mobile. Pi and OpenCode v1 no longer overwrite T3's
recorded answer when they acknowledge it.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@saphid
saphid force-pushed the stack/21-plugin-approvals branch from 400b930 to 0442612 Compare October 12, 2026 00:28
github-actions Bot and others added 2 commits October 14, 2026 14:25
This PR now carries only the provider acknowledgement fix. The plugin
system it was stacked on (pingdotgg#16047-pingdotgg#16063) and the plugin approval feature
wait on Ideas discussion #6837; the fix applies to ordinary approval
answers on main and does not need them.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…confirm it

When a user answers an approval request, the orchestrator records the
request as resolved with the decision, and marks the request's card and
node cancelled for a decline. Only then does it hand the answer to the
provider. Pi and OpenCode v1 then re-emitted the request record they had
cached when it was asked, with no decision and the card completed, and
Muse marked the card completed even for a decline. Provider events
replace the projected records, so the thread lost the decision and
showed a declined approval as completed.

Pi no longer reports a request it just answered. OpenCode v1 remembers
the requests T3 answered and, when OpenCode confirms one or its turn
ends, only stops tracking it instead of reporting it again. Muse settles
a declined or cancelled request's card and node as cancelled.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@saphid saphid changed the title feat(server,web,mobile): let plugins answer approval requests fix(server): keep a user's approval answer when Pi, OpenCode or Muse confirm it Oct 14, 2026
@github-actions github-actions Bot added size:M 30-99 changed lines (additions + deletions). and removed size:XXL 1,000+ changed lines (additions + deletions). labels Oct 14, 2026
macroscopeapp[bot]
macroscopeapp Bot previously approved these changes Oct 14, 2026
@coderabbitai

coderabbitai Bot commented Oct 14, 2026 •

Copy link
Copy Markdown
Contributor

Review in Change Stack →

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration
  • Configuration used: Path: .coderabbit.config.ts
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 6584de5f-5185-4ca6-8ac2-26ed5a0923e1
📥 Commits

Reviewing files that changed from the base of the PR and between 85272ee and 449ad5c.

📒 Files selected for processing: 5
  • apps/server/src/orchestration-v2/testkit/OrchestratorReplayFixtures.integration.test.ts
  • packages/provider-muse/src/server/adapter.ts
  • packages/provider-opencode/src/server/adapter.ts
  • packages/provider-pi/src/server/adapter.test.ts
  • packages/provider-pi/src/server/adapter.ts

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

📝 Walkthrough

OpenCode and Pi adapters no longer report a second resolution after an answer is recorded. Muse maps declined or cancelled decisions to cancelled node and turn-item statuses. Replay tests cover accept and decline outcomes for all three providers.

Changes: Approval resolution handling

Layer / File(s) Summary
Handle answers and provider acknowledgements
packages/provider-opencode/src/server/adapter.ts, packages/provider-pi/src/server/adapter.ts, packages/provider-pi/src/server/adapter.test.ts
OpenCode tracks answers submitted by T3 and suppresses duplicate resolution events when the provider acknowledges them. Pi no longer marks the runtime request, node, or turn item resolved after sending the response.
Map Muse decisions to node and turn-item status
packages/provider-muse/src/server/adapter.ts
Muse marks the node and turn item cancelled for resolved decline or cancel decisions. Other resolved decisions remain completed; the runtime request status is unchanged.
Verify approval outcomes in replay tests
apps/server/src/orchestration-v2/testkit/OrchestratorReplayFixtures.integration.test.ts
Replay tests check OpenCode and Pi accept and decline decisions, and Muse accept and decline outcomes. Transcript helpers add a Pi confirmation exchange or change OpenCode replies for declines.

Priority: ⬇️ Low

Estimated code review effort: 3 (Moderate) | ~25 minutes

Change: Feature

Suggested reviewers: juliusmarminge

Merge Risk: ⚪ Minimal · up to 449ad

No actionable approval-resolution issue remains from the reviewed changes. The change is mergeable after normal checks.

Security architecture risk: 🔵 Low · up to 449ad

The changes preserve recorded approval decisions rather than letting later acknowledgements overwrite them. No new approval bypass was established. Risk remains low, rather than minimal, because interrupted delivery and recovery behavior were not fully verified.

Retained concerns
No architecture-level concerns identified.

Security review details

Security Blast Radius

  • observed — The changed OpenCode tracking is per adapter session and includes requests originating from child sessions and descendants. Its existing capabilities prohibit cross-thread session pooling and declare that pending requests do not survive restart. This is a session/request-tree scope, not evidence of tenant-wide exposure.

Trust Boundaries and Controls

  • observed — Response delivery remains bound to the stored thread/request identity and live provider session. Adapter response methods additionally require a matching pending request and the appropriate decision or answer payload. These controls precede the changed acknowledgement handling; upstream plugin installation and capability authorization were not independently verified.

Resilience and Maintainability Implications

  • observed — Pi serializes response handling, deletes pending tracking only after sending succeeds, and identity-checks expiry events. A failed send therefore retains its existing cancellation/retry path. OpenCode retains failed sends in its pending maps; matching native settlement or turn finalization removes them, and unexpected event-stream termination fails active turns.
  • observed — OpenCode keeps answering set after send failure and treats later cancellation of that request as a resolved local snapshot without emitting another resolution. This preserves answer ownership, but does not separately establish successful delivery. Send errors still propagate; the ten-second timeout and lack of pending-request survival across restart predate this PR. Recovery after retry exhaustion or a missing acknowledgement while the turn remains active remains unverified.
Pre-merge checks: Passed 3 · Failed 1

Failed Failed checks: 1 warning

Check name Status Explanation Resolution
Description check Warning The description provides substantial problem, change, and verification details, but it does not follow the required section structure. It also does not provide the required triaged issue or discussion… Add the required ## Problem, ## Change, ## Scope and approval, and ## Verification headings. Under Scope and approval, link the triaged issue or approval discussion and include the explicit maintainer approval comment, or explain why this f…
Passed Passed checks: 3 passed
Check name Status Explanation
Linked Issues check Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check Passed Check skipped because no linked issues were found for this pull request.
Title check Passed The title clearly describes the primary fix: preserving user approval answers when Pi, OpenCode, or Muse confirms them.
Full details: Description check

Explanation

The description provides substantial problem, change, and verification details, but it does not follow the required section structure. It also does not provide the required triaged issue or discussion with explicit maintainer approval of direction and scope; the objectives state that approval has not been obtained.

Resolution

Add the required ## Problem, ## Change, ## Scope and approval, and ## Verification headings. Under Scope and approval, link the triaged issue or approval discussion and include the explicit maintainer approval comment, or explain why this focused fix qualifies for an exemption. Include a conventional commit title if it is not provided elsewhere in the pull request metadata. Preserve the existing verification details under ## Verification, including limitations and human follow-up items.

  • Fix all pre-merge checks with AI
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create a new PR
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

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

…l answer when Pi, OpenCode or Muse confirm it

Preserve the scoped contribution against upstream 70d60e0.
Focused checks and read-only integration review recorded in the PR.

GPT-6.1 Sol (high), Codex harness through T3 Code.
@macroscopeapp
macroscopeapp Bot dismissed their stale review October 17, 2026 03:00

Dismissing prior approval to re-evaluate aef0c25

macroscopeapp[bot]
macroscopeapp Bot previously approved these changes Oct 17, 2026
@juliusmarminge juliusmarminge self-assigned this Oct 19, 2026
@juliusmarminge

Copy link
Copy Markdown
Member

Taking over this one. I’ll rebuild the approval acknowledgement fix on current main, keep the replay coverage focused, and verify that Pi, OpenCode and Muse preserve the recorded answer and decline status before pushing to this branch.

@macroscopeapp
macroscopeapp Bot dismissed their stale review October 19, 2026 08:47

Dismissing prior approval to re-evaluate c3df197

@github-actions github-actions Bot added size:L 100-499 changed lines (additions + deletions). and removed size:M 30-99 changed lines (additions + deletions). labels Oct 19, 2026

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

size:L 100-499 changed lines (additions + deletions). 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.

2 participants