Skip to content

fix(server): stop silently dropping OpenCode child requests - #16870

Open
Adamulek123 wants to merge 2 commits into
pingdotgg:mainfrom
Adamulek123:fix/v2-opencode-child-request-giveup
Open

Adamulek123 wants to merge 2 commits into
pingdotgg:mainfrom
Adamulek123:fix/v2-opencode-child-request-giveup

Conversation

@Adamulek123

@Adamulek123 Adamulek123 commented Oct 7, 2026 •

Copy link
Copy Markdown
Contributor

Problem

When OpenCode's child request cannot reach an active owning turn, the adapter retries five times and then silently drops its routing work. The native permission or question can remain blocked while the client shows no prompt. A maintainer confirmed this path in #16846.

Change

After the existing five-second backoff:

  • If the native session's parent chain reaches a registered thread here but no active owning turn, reject the request through the SDK with a ten-second deadline.
  • If any lookup reaches an unregistered root, skip rejection and session errors on exhaustion. Preserve that observation across later lookup failures. An unregistered root may belong to another runtime or may be local and not registered yet.
  • Otherwise, report a warning and provider-session error without native rejection.

Successful rejection settles the request. A native NotFoundError also settles it without marking the session errored, since another caller may already have answered it. Failed rejection leaves it unsettled so a later replay can route it.

OpenCode permission rejection cancels every pending permission in the asking native session. Its SDK has no single-request denial. Question rejection affects only the named question. This behavior is documented at the call site.

Only OpenCodeAdapterV2.ts and its tests change. The shared server path applies to web, desktop, and mobile clients; OpenCode2 is unchanged and covered by its adapter suite. No contract or configuration changes. #15778 addresses recovery and detach rather than this inline routing path.

Validation

  • Parent rerun of the OpenCode and OpenCode2 adapter suites: 147 tests passed.
  • With OpenCodeProvider.test.ts: 172 passed, one failed. The failure asserts a POSIX credential path against a Windows path in unchanged code; the implementation agent also reproduced it before this follow-up.
  • Fifteen new cases use TestClock to cover exhaustion, duplicate events, late ownership, settled requests, SDK failure and timeout, unknown-root lookup failures, and already-answered races. The implementation agent reports independent mutation checks for the rejection cases and all four follow-up cases.
  • Targeted lint passes with the existing unused layer warning. Whitespace checks pass.
  • The implementation agent's server typecheck reported 2,662 diagnostics, none in the two changed files. This is not a clean typecheck or a comparison against untouched main.

Limits

Ownership that remains unresolved can still leave a request waiting natively. A warning and session error expose lookup failures; an unregistered root is deliberately ignored to avoid interfering with another runtime. The fix does not add automatic replay when routing work ends.

Tests use mocked SDK clients. No live OpenCode process or client verification was run.

Implemented with gpt-6.1-sol through the Codex harness.

OpenCode child permissions and questions disappeared after five routing retries, leaving the native request waiting without a visible failure.

Keep the existing five-second backoff, then emit a warning and provider-session error. Reject permissions and questions only when the native parent chain proves this runtime owns them. Bound rejection delivery to ten seconds and keep failed rejections available for later routing. Ignore requests whose complete parent chain belongs to another runtime.

The SDK supports both rejection paths, and OpenCode waits on these requests without a request timeout. Longer or unbounded retries only postpone the failure. Blind rejection can cancel another session on an external server, so unresolved ownership emits a failure without guessing who owns the request.

Add deterministic clock and event tests for exhausted routing, failed and timed-out rejection, duplicate asks, late relations, externally settled requests, and unrelated sessions.

Model: gpt-6.1-sol (Codex)
@github-actions github-actions Bot added vouch:trusted PR author is trusted by repo permissions or the VOUCHED list. size:M 30-99 changed lines (additions + deletions). labels Oct 7, 2026
Routing retries overwrote an earlier unknown-root classification with transient lookup failures, allowing exhaustion to report a sibling request as a session error. Native rejection also treated an already-answered request as a delivery failure.

Keep the observed foreign classification sticky through exhaustion. A failed lookup provides no new ownership evidence and must not overturn the earlier classification. Treat native NotFoundError as settled, so a later route cannot resurrect the answered request.

Add TestClock regressions for permissions and questions, document the native permission rejection cascade, and clarify that an unknown root can also be an unregistered local root.

Model: gpt-6.1-sol (Codex)
@Adamulek123
Adamulek123 marked this pull request as ready for review October 7, 2026 21:01
@macroscopeapp

macroscopeapp Bot commented Oct 7, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Approved at 05c1e52

Macroscope's review found this PR approvable — This is a narrowly scoped OpenCode adapter bug fix that replaces silently abandoned child requests with ownership-aware error reporting and bounded native rejection. The production change is isolated and supported by comprehensive deterministic tests covering retries, late ownership, duplicate events, failed rejection, timeout, and already-settled requests.

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

@coderabbitai coderabbitai 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.

Pre-merge checks failed. Please resolve the failing checks before merging.

@coderabbitai

coderabbitai Bot commented Oct 7, 2026

Copy link
Copy Markdown

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: 9e3f56db-fe47-492e-882c-bc8f1464bd04
📥 Commits

Reviewing files that changed from the base of the PR and between 611132c and 05c1e52.

📒 Files selected for processing (2)
  • apps/server/src/orchestration-v2/Adapters/OpenCodeAdapterV2.test.ts
  • apps/server/src/orchestration-v2/Adapters/OpenCodeAdapterV2.ts

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


📝 Walkthrough

Walkthrough

OpenCode request routing now distinguishes proven-foreign sessions from unresolved owner lookups. After retries, it leaves proven-foreign requests alone and rejects eligible permission or question requests for registered local threads. Tests cover retry and rejection outcomes.

Changes

OpenCode request routing

Layer / File(s) Summary
Distinguish foreign and unresolved owners
apps/server/src/orchestration-v2/Adapters/OpenCodeAdapterV2.ts
resolveSessionOwner returns null when a complete parent chain ends at an unknown root. Routing tracks this result separately from unresolved lookups.
Handle requests after routing retries
apps/server/src/orchestration-v2/Adapters/OpenCodeAdapterV2.ts, apps/server/src/orchestration-v2/Adapters/OpenCodeAdapterV2.test.ts
After retries, routing leaves proven-foreign requests alone. For eligible requests, it uses permission.reply or question.reject; tests cover rejection outcomes, request visibility, and settled requests.

Priority: ➖ Normal

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

Change: Bug fix

Sequence Diagram(s)

sequenceDiagram
  participant Adapter as OpenCodeAdapterV2
  participant Native as OpenCode native API
  participant Session as Provider session
  Adapter->>Native: Reject permission or question request
  Native-->>Adapter: Rejection result
  Adapter->>Session: Record routing or rejection error
Loading

Suggested reviewers: juliusmarminge

Merge Risk: 🔵 Low · up to 05c1e

An unroutable permission can occasionally cancel another pending permission and fail its tool call. This is a bounded risk to accept or address when merging.

Security Architecture Review

Security architecture risk: 🔵 Low · up to 05c1e

Automatic rejection is guarded by locally recognized session ownership and does not demonstrate an authorization bypass. A remaining failure-containment risk is that background rejection can overlap a new turn and affect permissions or error status after that turn starts.

Retained concerns

  • Low · reliability · inferred: Background rejection is not fenced against new-turn admission. A new turn can start while the native rejection is pending; completion can then mark the shared provider session errored and, under the documented permission contract, cancel newer permissions in the same asking session. Per-request local settlement does not establish reconciliation for canceled siblings. Turn finalization clears existing local requests, but does not prevent this later overlap.
Security review details

Security Blast Radius

  • inferred — The documented direct cancellation scope is every pending permission within the asking native session, not merely the exhausted request. Failure reporting affects the enclosing provider session. Cross-tenant or cross-environment cancellation is not established by the inspected evidence.

Trust Boundaries and Controls

  • observed — Broadcast native requests do not alone authorize rejection. A registered ownership chain is required. Any observed complete chain ending at an unregistered root suppresses exhaustion rejection and session errors, even if subsequent lookups fail; unavailable ownership information alone causes reporting without native rejection.

Resilience and Maintainability Implications

  • inferred — Turn finalization and individual terminal-event handling provide local cleanup, but do not establish atomicity between background session-wide rejection and a newly admitted turn. Native sibling-event delivery remains a contract gap, rather than a verified authorization vulnerability.

Hardening Proposals

  • proposed — Coordinate session-wide rejection with new-turn admission and fence its final status update to the relevant lifecycle generation. Confirm the native sibling-cancellation event contract before choosing a reconciliation strategy for all affected local requests.

Caution

Pre-merge checks failed

Please resolve all errors before merging. Addressing warnings is optional.

  • Ignore (reviewers only)

❌ Failed checks (1 error)

Check name Status Explanation Resolution
Approvability ❌ Error The pull request changes an external side effect in apps/server/src/orchestration-v2/Adapters/OpenCodeAdapterV2.ts. After routing retries, it now calls the OpenCode SDK methods `client.permission.re… This pull request needs a maintainer's review before CodeRabbit approves it. Review the new OpenCode rejection side effects and their impact on native sessions.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly identifies the main fix: preventing OpenCode child requests from being silently dropped. It uses a concise conventional commit format.
Description check ✅ Passed The description covers the problem, implementation, scope, approval context, verification results, known limitations, test gaps, and agent attribution. It is detailed and aligned with the repository t…
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.
Full details: Approvability

Explanation

The pull request changes an external side effect in apps/server/src/orchestration-v2/Adapters/OpenCodeAdapterV2.ts. After routing retries, it now calls the OpenCode SDK methods client.permission.reply(..., reply: "reject") and client.question.reject(...), which send native rejection requests to the external OpenCode server. This behavior is new in the reviewed diff.

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

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

@Adamulek123

Copy link
Copy Markdown
Contributor Author

@coderabbitai which checks failed

@coderabbitai

coderabbitai Bot commented Oct 7, 2026

Copy link
Copy Markdown

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail
gh pr view 16870 --repo pingdotgg/t3code --json url,headRefOid,statusCheckRollup,body

Length of output: 13674


@Adamulek123 No GitHub checks currently show a failure on #16870. Lint, Typecheck, Build, and all test jobs passed.

  • Cancelled: one Native fingerprint diff run. A separate run passed.
  • Skipped: EAS Preview, Deploy web preview, Mobile Native Static Analysis, and PR size label-definition sync.

The PR description reports two local validation failures, separate from GitHub CI:

  • One test in OpenCodeProvider.test.ts failed on a POSIX-versus-Windows credential-path assertion in unchanged code.
  • The local server typecheck reported 2,662 diagnostics, none in the changed files. The GitHub Typecheck check passed.

View the GitHub CI run.

You are interacting with an AI system.

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:M 30-99 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.

1 participant