Repository navigation
fix(router): preserve policy authorization across fallback redirects (carry #6380) - #6396
Conversation
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
… traces truncate An eligibility probe that throws (e.g. a blocked-model redirect cycle) now skips that fallback candidate instead of selecting it and dead-ending the retry. Redirect membership uses the full policy evaluation result carried on the request context, so an eligible destination truncated out of the bounded trace can still receive a hop. Eligibility probes run through previewRouteModel so they never write combo selection state.
…d scope The dev merge made policy fallback authorization request-owned: the retry's resolved route must sit inside the eligibility captured from the real first route, and the candidate probe resolves each trace row concretely. Synthetic traces can no longer authorize candidates the real evaluation never admitted, so the fixtures now declare them for real: the second candidate joins the native-hop profile, and opencode-go-2 joins the provider map. Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
…ied fallback logging
The shadow-intercept probe resolved its target through the request's policy capture, so a declined policy-profile target left its eligibility and policy route kind on the request's own route. Probe without the capture and capture only when interception is accepted.
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
|
Navigate logical layers of code changes, visualize relationships, and explore their blast radius. 🧰 Additional context used📚 Code guidelines (2)No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Repository: lidge-jun/opencodex/.coderabbit.yaml Review profile: ASSERTIVE Plan: Advanced Run ID: 📒 Files selected for processing (6)
Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 7 remain after this review. 📝 WalkthroughWalkthroughPolicy routes retain their original eligible provider/model set through request preparation and fallback. Retries resolve concrete candidates, reject out-of-scope redirects, and skip previously attempted physical destinations. Tests and documentation cover subagent recovery, virtual-model mapping, and attempt-state preservation. ChangesPolicy-Scoped Fallback Dispatch
Priority: ➖ Normal Estimated code review effort: 4 (Complex) | ~40 minutes Change: Bug fix Sequence Diagram(s)sequenceDiagram
participant handleResponsesWithPolicyFallback
participant prepareResponsesRequest
participant routeConcreteModel
participant PolicyRequestScope
participant Upstream
handleResponsesWithPolicyFallback->>prepareResponsesRequest: Concrete fallback candidate and request scope
prepareResponsesRequest->>routeConcreteModel: Resolve candidate route
routeConcreteModel-->>prepareResponsesRequest: Provider and model
prepareResponsesRequest->>PolicyRequestScope: Validate route and record prepared destination
prepareResponsesRequest->>Upstream: Dispatch prepared request
Merge Risk: ⚪ Minimal · up to The change retains original policy authorization across routing and retries. No actionable merge-blocking issue is established; merge after normal exact-head checks pass. Security Architecture ReviewSecurity architecture risk: 🔵 Low · up to The change strengthens authorization during redirects and retries. No new authorization bypass was established in the reviewed paths, but incomplete recovery and concurrency coverage leaves some residual uncertainty. Retained concerns Security review detailsSecurity Blast Radius
Trust Boundaries and Controls
Resilience and Maintainability Implications
Hardening Proposals
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
Full details: Docstring CoverageExplanation Docstring coverage is 33.33% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 21 functions across 15 files. (3 skipped: 3 unsupported.)
✨ Finishing Touches 💡 1📝 Generate docstrings 💡
🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 5cb070123c
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| } | ||
| } | ||
|
|
||
| const localRefusals = new WeakSet<Response>(); |
There was a problem hiding this comment.
Preserve the refusal marker across response wrapping
On the normal /v1/responses path, handleResponsesCore owns a translator budget, and finalizeOwnedTranslatorBudget replaces every body-bearing response with a new Response. Because this marker is keyed only by the original object, isPolicyCandidateRefusal returns false after the core call. If policy scope locally rejects a subagent/recovery destination or a duplicate candidate after an upstream failure, fallback therefore returns the synthetic 409 or overwrites the last real failure instead of skipping to another eligible candidate. Propagate the marker through response wrappers or use internal metadata that survives wrapping, and cover the default runCore path rather than only the test stub.
Useful? React with 👍 / 👎.
|
✅ Deterministic PR hygiene checks passed. |
…cy-scope # Conflicts: # src/server/responses/request-prepare.ts # structure/transports/responses.md
|
Maintainer integration record (MAINTAINERS.md, dev-only) — DRAFT from lane L3
|
Summary
Carries #6380 by @luvs01 onto a maintainer branch and adds one maintainer fix found in its security review. The original commits are kept as they are, with their authors.
What #6380 fixes. Policy fallback could authorize a redirect from the bounded diagnostic trace or reselect a public alias, so a retry could leave the provider/model set the original policy evaluation allowed. The fix keeps the first evaluation's full membership as request-owned state (
src/server/responses/policy-request-scope.ts), runs every retry as a concrete candidate, and checks the final destination before dispatch. Subagent selection, encrypted-task recovery, alias collisions, and virtual wire models stay inside that membership. A candidate outside it is refused locally with409 policy_candidate_unavailable.Maintainer fix (behavior change, not a mechanical carry):
5cb070123c"fix(responses): capture policy scope only for an accepted shadow target". The shadow-call intercept resolved its target through the same capture that fixes the request's policy scope, before deciding whether to intercept. When the target was a policy profile and interception was declined (source and target intersect), the request kept its own model but carried the profile's eligibility. A model outside the profile was refused with 409, and a model inside it was relabeled as a policy route, which made it eligible for policy fallback to providers it never selected. The shadow probe now skips the capture, and the accepted target is captured where it becomes the request's route. A regression case intests/routing/routing-policy-dispatch-scope.test.tscovers both selectors, andstructure/transports/policy-fallback.mdrecords the rule.Carried from #6380. Original author: @luvs01.
Co-authored-by: luvs01 27862058+luvs01@users.noreply.github.com
Verification
05bc47d728passed full hosted Cross-platform CI before the carry. This branch is that head plus5cb070123c.git merge-tree --write-tree origin/dev HEADis clean.git diff --check,bun run scripts/privacy-scan.ts,bun run structure:checkpass. The touched files are not intests/fixtures/file-size-baseline.json. The PR adds nosrc/labimport to the core request path (src/router.ts,src/server/lifecycle.ts,src/server/responses/core.ts).dev, the tree has no conflicts, andstructure:checkand the privacy scan pass.Checklist
Summary by CodeRabbit
Bug Fixes
Documentation