Skip to content

fix(codex): revalidate hard-lock fallback targets before request rewriting - #7340

Closed
luvs01 wants to merge 1 commit into
lidge-jun:devfrom
luvs01:fix/hard-lock-target-resolution-20261019
Closed

luvs01 wants to merge 1 commit into
lidge-jun:devfrom
luvs01:fix/hard-lock-target-resolution-20261019

Conversation

@luvs01

@luvs01 luvs01 commented Oct 19, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

  • When the main-account hard-lock blocks a request, the configured fallback override rewrites the request model to an operator-chosen target. Before this change, a fallback target that no longer resolves - a provider qualifier whose provider was removed, disabled, or renamed - was still written into the body, where it could only route through the terminal default-provider fallback of the router. The request then went to a destination the operator did not select.
  • The override now resolves the chosen target through the shared resolveChosenTarget check (already used by the shadow-call intercept and memory-model routing) before touching the body. Targets that cannot resolve keep the refusal of the source lock, and the request body is left untouched.
  • Configured combo selectors, routing-policy profiles and account namespaces keep their own admission, which is where their declared failover and evidence already run. Bare model ids and provider-declared slash model ids keep their ordinary routing semantics.

Verification

Run on the staged diff over current dev (8640c2f). Tooling: Bun 1.4.2; TypeScript 7.0.2 via node_modules/typescript/bin/tsc. In this worktree bun run <script> fails with a node_modules bin-remap error, so the underlying scripts were invoked directly (equivalent commands; nothing patched):

  • bun test tests/codex-integration/main-account-hard-lock-fallback-routing.test.ts - 18 pass / 0 fail / 99 expect(). New coverage: stale qualified targets keep the blocked source model and reasoning; admission is rechecked across provider removal, re-addition and alias rename; bare and declared slash ids stay valid; combo, policy and account-namespace selectors keep their admission; and an end-to-end regression asserting a stale qualified fallback returns a refusal with zero upstream sends.
  • bun test tests/lab/core-lab-boundary.test.ts - 25 pass / 0 fail.
  • bun scripts/structure-ssot.ts (structure:check) - passed.
  • bun scripts/privacy-scan.ts (privacy:scan) - passed.
  • bun node_modules/typescript/bin/tsc --noEmit (typecheck) - clean.
  • File-size ratchet - NOT verified locally: bun scripts/file-size-ratchet.ts exits with ENOENT on this sparse checkout because tracked .github entries are skip-worktree and absent, so the scan never reached evaluation. A manual look at the three changed files (118, 101, 388 lines, none listed in tests/fixtures/file-size-baseline.json, threshold 2000) is partial evidence only, not a gate pass; the gate runs on CI.
  • Not run locally: the full bun run test suite (concurrent worktree contention). The commands above are the actual executed evidence; everything else is left to CI.
  • Hosted CI on the PR head 9e24bdd: Cross-platform CI run 39180940291 completed SUCCESS (all four Ubuntu test shards, gates and the ci aggregate job): https://github.com/lidge-jun/opencodex/actions/runs/39180940291. Conditional Windows/macOS shards and other skipped jobs were not executed; skipped is not executed. The initial Enforce PR target branch run 39180940155 was cancelled before it evaluated this PR and is not a pass.
  • Review note: adjacent to fix(codex): limit Direct hard-lock fallback to the locked account #7328, which edits the same three files. The test-file import change is identical; the routing-file guard block and the test describe blocks will need a rebase for whichever lands second that preserves both behavior changes: fix(codex): limit Direct hard-lock fallback to the locked account #7328 scopes which caller identity a direct-mode lock may substitute, and this change validates that the fallback target itself still resolves.

Checklist

  • Scope stays focused and avoids unrelated cleanup.
  • Docs or release notes were updated when needed.
  • Security-sensitive changes were reviewed for secrets, auth, and unsafe defaults. (pending maintainer review)
  • Local scoped validation ran: focused regression files, typecheck, structure gate and privacy scan on the staged diff over current dev.
  • Prepared on current dev (8640c2f).
  • Bot findings addressed (CodeRabbit reviewed this head and generated no actionable comments; review comment 6257088935).
  • Ready for review.

Summary by CodeRabbit

  • Bug Fixes
    • Hard-lock fallback routing now checks that concrete targets are available before rewriting a blocked request. Unavailable or invalid targets preserve the original request and refusal.
    • Qualified fallback targets no longer route to the default provider when their provider or alias is unavailable.
  • Documentation
    • Clarified which fallback targets are validated and how unavailable targets are handled.

@coderabbitai

coderabbitai Bot commented Oct 19, 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: Repository: lidge-jun/opencodex/.coderabbit.yaml
  • Review profile: ASSERTIVE
  • Plan: Advanced
  • Run ID: 89cc16be-b09e-4e52-a61e-fe3bffe217ae
📥 Commits

Reviewing files that changed from the base of the PR and between 8640c2f and 9e24bdd.

📒 Files selected for processing: 3
  • src/server/responses/main-hard-lock-routing.ts
  • structure/codex-account-controls.md
  • tests/codex-integration/main-account-hard-lock-fallback-routing.test.ts

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

📝 Walkthrough

Fallback routing now validates concrete targets before rewriting a hard-locked request. Unavailable targets leave the request unchanged. Tests cover target admission and stale-target handling, and the documentation describes the routing rules.

Changes: Hard-lock fallback target admission

Layer / File(s) Summary
Validate fallback targets
src/server/responses/main-hard-lock-routing.ts, tests/codex-integration/main-account-hard-lock-fallback-routing.test.ts, structure/codex-account-controls.md
The router checks concrete fallback targets with previewRouteModel before rewriting the request. Tests cover unavailable targets, configuration changes, supported target forms, and stale qualified targets. The documentation describes the admission rules.

Priority: ➖ Normal

Estimated code review effort: 2 (Simple) | ~12 minutes

Change: Bug fix

Suggested reviewers: lidge-jun

Merge Risk: ⚪ Minimal · up to 9e24b

The change makes hard-locked requests refuse stale or unavailable fallback targets instead of rewriting them. No concrete merge-blocking risk was identified in the supplied context.

Pre-merge checks: Passed 4 · Failed 1

Failed Failed checks: 1 warning

Check name Status Explanation Resolution
Docstring Coverage Warning Docstring coverage is 25.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 4 functions across 2 files. (1 skipped: 1… Write docstrings for the functions missing them to satisfy the coverage threshold.
Passed Passed checks: 4 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.
Description Check Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check Passed The title clearly and concisely describes the main change: revalidating hard-lock fallback targets before rewriting requests.
Full details: Docstring Coverage

Explanation

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

Resolution

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

  • 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

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.

@github-actions

Copy link
Copy Markdown
Contributor

✅ Deterministic PR hygiene checks passed.

@github-actions github-actions Bot added the bug Something isn't working label Oct 19, 2026
@luvs01
luvs01 marked this pull request as ready for review October 19, 2026 00:43
@Ingwannu

Copy link
Copy Markdown
Owner

Intake/scoped review at 9e24bdd19a6bb45e16055d4ba8e51f298f2bf909.

The chosen-target check is before raw-body rewriting and leaves configured Combo/policy selectors to their existing dispatch admission. Preserve the negative control where a removed qualifier would otherwise degrade into the terminal default provider, and the check that a refusal leaves model/effort untouched. Current functional CI is successful; the hard-lock credential/routing boundary review remains required.

Existing current-head CI was inspected; its success is limited to the jobs actually selected, with skipped platforms retaining no runtime certification. No local suite was run for this branch in this intake. No workflow approval, new scan, live configuration change or merge was performed. Required review/readiness gates remain in force.

lidge-jun added a commit that referenced this pull request Oct 19, 2026
…locks, and scope Direct fallback to the locked main (#7393)

* fix(codex): renew opted-in pool credit evidence on the existing sweep

* test(codex): assert renewal performs no native-main access

* fix(codex): limit Direct hard-lock fallback to the locked account

Use the original route's credential ownership and observed-main caller check before applying Direct hard-lock fallback. Preserve binding, missing-bearer and disabled-route behavior; document and cover the boundary.

* fix(responses): break hard-lock caller-check import cycle

Move the caller-check factory into core-auth, which owns the credential-domain helpers, and update its request-preparation import. Keep the function body and routing/authentication policy unchanged.

* fix(codex): revalidate hard-lock fallback targets before request rewriting

* fix(codex): clear failure windows when account identities are removed

Forget account-scoped failure samples and pinned degradation warnings during deletion and physical main-account replacement. Preserve sibling account windows and cover both lifecycle paths.

* fix(codex): fence late failure-window samples by dispatch identity

Carry the main dispatch proof and stored Pool credential generation into terminal outcomes. Reject stale success and transient samples after identity or credential replacement, while preserving existing health and probe handling. Cover main identity ABA, same-ID Pool recreation, context reuse, and valid-current controls.

* fix(codex): revalidate tagged failure samples after credential replacement

Retain each stored Pool sample's dispatch credential generation and filter it against one current store snapshot per insertion or lookup. Reset retired-generation degradation, recovery timers and pin warnings without clearing a replacement on stale arrival. Cover both unlocked-read boundaries, derived state and the 512-sample snapshot bound.

* fix(codex): preserve failure windows when credential snapshots fail

Distinguish failed reads or parsing from valid absent/empty credential stores. Preserve retained samples and derived failure-window state while evidence is unknown, keeping existing boolean liveness callers compatible. Cover fixture read/parse failures, recovery and empty-store retirement without adding snapshot reuse optimization.

* fix(codex): reject non-object credential snapshot shapes

Require a non-null, non-array object before normalizing store rows. Preserve failure-window evidence for invalid top-level arrays or scalars while keeping missing files and empty objects valid. Cover six top-level shapes without changing row normalization.

* refactor(responses): keep passthrough-dispatch within its line budget

* fix(codex): keep a paused main caller out of Pool fallback

Carries the paused-main regression from #7290 and the production guard it proves is needed, plus a per-generation credit-renewal pacing test requested at intake.

Co-authored-by: Hylouis233 <88263959+Hylouis233@users.noreply.github.com>

* fix(codex): recheck a predicted-reset main hard lock with metadata (#7312)

A predicted reset now arms the capped WHAM recovery schedule instead of suppressing it, so an early reset redeemed outside opencodex is learned from verified metadata. The schedule never runs past the predicted reset, skips paused and needs-reauth mains, and a predicted reset still never lifts the lock by itself.

* test(codex): await the async hard-lock fallback override in #7340 cases

#7328 made applyMainHardLockFallbackOverride async for the Direct caller check; the #7340 revalidation cases were written against the synchronous form and asserted on a Promise once both landed.

Co-authored-by: luvs01 <27862058+luvs01@users.noreply.github.com>

* fix(codex): recheck Direct hard-lock ownership synchronously before rewriting

The async caller check now resolves to a synchronous ownership proof that is evaluated after the last await, so a main identity or bearer change during the policy-binding wait cannot reroute the original caller onto the fallback.

Co-authored-by: luvs01 <27862058+luvs01@users.noreply.github.com>

* fix(codex): keep predicted-reset hold rechecks paced across transitions (#7312)

Track the first hold per identity/credential separately from the backoff reservation so transient 401/403 retries are not re-armed, arm a hold that appears during token preparation instead of reading immediately, pull the due time in when the predicted reset moves earlier or passes, and keep hold reads at least one base interval apart.

* fix(codex): honor a revised reset that elapses between hold sweeps (#7312)

Recovery scheduling now reads the latest blocking reset from current evidence even after it has passed, so a reset revised earlier and expired before the next sweep releases the old reservation. It remains scheduling evidence only; admission still requires verified metadata.

---------

Co-authored-by: Ingwannu <ingwannu@users.noreply.github.com>
Co-authored-by: Epinephrine <luvs01@hanmail.net>
Co-authored-by: Hylouis233 <88263959+Hylouis233@users.noreply.github.com>
Co-authored-by: luvs01 <27862058+luvs01@users.noreply.github.com>
@lidge-jun

Copy link
Copy Markdown
Owner

Carried into #7393 (squash d3dafd3) on dev with a Co-authored-by trailer, as part of the B1 codex pool / hard-lock bundle. It ships in the next release (2.90.0). Thanks!

@lidge-jun lidge-jun closed this Oct 19, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants