Skip to content

fix(web-search): consolidate replay isolation and bounded bridge repair - #5562

Closed
luvs01 wants to merge 13 commits into
devfrom
stack/search-replay-boundaries
Closed

luvs01 wants to merge 13 commits into
devfrom
stack/search-replay-boundaries

Conversation

@luvs01

@luvs01 luvs01 commented Sep 22, 2026

Copy link
Copy Markdown
Collaborator

Summary

Combine #5480 and #5365 with the end-to-end bridge-leg regression and contract updates carried in 973a4ac70281add989e7b6643a08463f25baacce. Preserve all six source commits, their review follow-ups and original attribution.

  • Restore a retained search result only under the same principal, conversation, provider, destination, adapter, model and physical credential. Configured API-key principals remain distinct on loopback; keyless loopback keeps its existing shared local principal.
  • Rebind the initial request before rebuilding after credential reselection, while refusing continuation dispatch if the already-served binding changes.
  • Keep a sidecar's original 429 evidence when Retry-After or cleanup/backoff would outlive the existing deadline.
  • Preserve the shadow-helper marker through combo dispatch, using the same provider/model intersection rule at early and late interception.
  • Apply terminal repair to the raw first search leg and every successful continuation under the same scheduler, abort and budget contracts. A complete but still-open leg can therefore finish without silently losing the intercepted search lifecycle.

The source production repair already exists in the #5365 carries, so it is not applied twice. Its distinct outer handleResponses regression and both structure-document updates are retained. The additional combined assertion verifies that a result produced by repaired legs remains restricted to its caller/thread/credential scope.

Validation exposed two cleanup boundaries. The key-failover fixture now owns proxy listeners and cancellable requests, waits for the shared listener-stop operation, then drains existing native/config producers and ACL child reaping before removing its home. The initial shared sandbox cleanup carry 421ba780ae is also carried by #5549; this proposal further resolves cleanup dependencies during protected setup and drains native releases before config hardening. Home isolation, guard arming, run-lock admission and ownership-checked stale recovery are preserved. The HTTP fixture uses existing test-only icacls runners to isolate key/pacing behavior from OS permission setup; dedicated ACL tests and real SQLite lease checks remain intact. No production ACL rule, deletion strength, test timeout, skip or size cap is weakened.

Verification

  • Head 6ea3a95c212c94103177fd17ffa37b28f05ce843, based on dev a6b54297b03fc218105273887ce147c901848d89. The final follow-up changes only the shared cleanup documentation; all production and test content equals the 199-pass 6b122cd2 tree. Structure checks passed. A virtual merge with ci: consolidate review gates and test cleanup boundaries #5549 head 8dc4050fade83b8cfad76f3ab1ed7946f7dc9cf8 is conflict-free at tree 33cac6e8a3262ed5fe706110d5ce891334c4851e, with both sets of production behavior preserved; this is not a full-suite execution of the virtual union.
  • All six source commits match stable patch IDs with authors/dates preserved. The three unique integration-test/document paths match their source patch at carry 973a4ac70281add989e7b6643a08463f25baacce. The existing production-repair equivalence was reviewed separately; path/patch equality is not used as a substitute for that semantic comparison.
  • Initial eight-file run: 166 pass / 39 fail. The key-failover fixture showed explicit EBUSY during removal, followed by hook timeouts and spend-owner conflicts; another suite reported an unnamed cleanup hook timeout. These failures are retained in the record, not reclassified as a passing run.
  • After the fixture cleanup correction, the complete key-failover file passed 42/42, including the original two failing cases and dispatch-time search-cache rebinding. After the global sandbox correction, cleanup/inline-document/bridge suites passed 88/88. These are separate runs; overlapping earlier results are not summed into a fabricated final-suite total.
  • The combined repaired-search scope case passed with 15 assertions, including denial under another principal, thread or credential. Typecheck, structure and whitespace checks passed on the final combined tree; privacy and unchanged-cap ratchet checks passed during preparation. The common cleanup source separately passed its deterministic negative-control regression and guard coverage.
  • The required changed-import-graph invocation was attempted once on the preceding integration head 65c3477dd2806b931f43eac35c11cf7af925e763. Bun 1.4.2 selected zero tests despite 24 changed files, including changed test files; the repository guard correctly returned exit 1. The wrapper preserves the requested comparison ref, so ref omission is not supported as a cause. The internal candidate/dependency mapping failure remains unexplained; no timeout, guard or selector was bypassed. Explicit-file checks are separate scoped evidence, not a replacement claim of complete automatic impact coverage.
  • The first explicit eight-file fallback then failed 170 pass / 26 fail / one error: timeout left a proxy and request body alive beyond teardown, retaining the old home's spend lease. After fixture ownership/cancellation fixes and deterministic permission preparation, the original eight-file scope passed 199 / 199 tests, 976 assertions, 38.90 seconds at unchanged limits. Three added regressions cover delayed shared stop, ordinary assertion propagation, and actual proxy cancellation followed by acquiring a real SQLite lease for a new home. Final typecheck, structure, privacy and ratchet checks passed; the committed tree equals the tested tree.
  • Cross-platform CI run 35743465771 passed on the preceding 65c3477 head. It does not validate the new fixture commit; updated-head hosted checks remain pending.
  • Full-suite completion, exact-head cross-platform CI, live-provider acceptance and explicit security review remain outstanding. This is a draft integration proposal, not merge or release readiness.

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.

Co-authored-by: Epinephrine luvs01@hanmail.net
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>

luvs01 and others added 11 commits September 22, 2026 22:48
…ota evidence

Bind replay-cache restoration to request and serving identity, and preserve the original quota response when Retry-After would outlive the sidecar deadline. Keep the current developer-role folding contract in the destination fixture.

(cherry picked from commit 498498d)
…rage

Apply the existing source-target non-intersection rule before early combo interception, and add production-path behavioral coverage for terminal-less continuation repair.

(cherry picked from commit 4bfcc0a)
Carry the end-to-end handleResponses regression and transport contracts for repairing both the first and continuation search legs. The corresponding production changes are already preserved by the earlier terminal-repair carries; keep this broader integration coverage without applying that implementation twice.

Source commit: b1044e7
Co-authored-by: Epinephrine <luvs01@hanmail.net>
Keep test-home isolation, guard arming, and lock admission ahead of cleanup dependencies. Await config/native producers and the exact sandbox reap barrier in afterAll; the synchronous exit fallback defers undrained roots to ownership-checked recovery. Add a deterministic caller-belt/reap ordering regression without changing production ACL behavior or test timeouts.

(cherry picked from commit fa96c7bca6780bb4d871ff5b5b0bb1187f3b9d16)
@coderabbitai

coderabbitai Bot commented Sep 22, 2026

Copy link
Copy Markdown
Contributor

Important

Draft PR not reviewed

Draft PRs are not automatically reviewed by default.

  • Trigger a manual review

To automatically review draft PRs, update your CodeRabbit configuration:

reviews:
  auto_review:
    drafts: true

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 Sep 22, 2026
@lidge-jun

Copy link
Copy Markdown
Owner

리뷰 · 우선순위 69 / 80

이 PR은 웹 검색·Responses 쪽에서 따로 열려 있던 고침 두 줄기(#5480 재생 칸 격리·429 데드라인, #5365 그림자 콤보 격리·터미널 없는 검색 다리 수리)를 dev 위에 한 장으로 모은 초안 통합본입니다. 브리지가 대신 돌려 준 검색 결과는 이제 누가·어느 대화·어느 공급자·어댑터·모델·목적지·키인지가 모두 맞을 때만 다시 꺼냅니다. 키를 고른 뒤에는 새 신원으로 먼저 다시 묶고 본문을 다시 만듭니다. 사이드카가 429를 받았을 때 남은 시간보다 긴 대기는 하지 않고, 그 429를 타임아웃으로 바꾸지 않습니다. 그림자 가로채기는 이른 자리와 늦은 자리에서 같은 교차 규칙을 쓰고, 콤보 자식까지 격리 표시를 넘깁니다. 패스스루 브리지는 첫 다리와 이어가기 다리 모두에 터미널 수리를 같은 계약으로 감쌉니다. 테스트 홈을 지울 때는 ACL 자식이 끝날 때까지 기다린 뒤 지우는 정리를 #5549와 같은 순서로 가져왔습니다. base는 dev이고, 작성자도 머지·릴리스 준비 완료라고 쓰지 않았습니다. types.ts·config.ts 분할이나 미리보기 배포 이야기는 없습니다.

라인 - 상태 · #5480 / #5365 — 원본 PR 두 개가 아직 OPEN입니다. 이 브랜치가 그 커밋을 그대로 실은 통합 차량이면, 머지 전에 둘을 무효·중복으로 닫아 이중 머지·이중 리뷰를 막아야 합니다.

라인 - tests/preload.ts / tests/helpers/test-sandbox-cleanup.ts / #5549 — 샌드박스 정리 순서 수정이 #5549와 같은 파일을 건드립니다. 둘 다 dev에 넣으면 충돌하거나 한쪽 커밋이 비게 됩니다. 어느 쪽이 정리 조각을 가져갈지 먼저 정해야 합니다.

라인 - 검증 · changed-import-graph — 작성자가 이 head에서 Bun이 바뀐 파일 24개인데도 테스트를 0개 고르고 guard가 exit 1을 냈다고 적었습니다. 원인 설명이 없고, 파일별 수동 통과로 대신했다고 스스로도 쓰지 않았습니다. 자동 영향 범위 증거가 비어 있습니다.

라인 - 체크리스트 · Security-sensitive — 재생 칸이 주체·대화·자격증명 경계를 다루는데 보안 칸이 비어 있습니다. 루프백은 API 키를 내면 키별 주체, 키 없으면 공유 loopback으로 두는 규칙은 본문·코드가 맞지만, 메인테이너가 한 번 더 보면 좋습니다.

메인테이너의 판단이 필요한 지점

이 PR을 #5480+#5365의 유일한 머지 차량으로 둘지, 아니면 원본을 각각 머지할지. 샌드박스 정리는 #5549에 두고 여기서 빼을지, 여기 carry를 살리고 #5549에서 겹치는 조각을 뺄지. draft를 ready로 올리기 전에 changed-import-graph 실패 원인을 고칠지, 합의한 대체 영향 범위 증거로 넘길지.

너의 추천

방향(재생 칸을 대화·서빙 신원으로 좁히기, 키 교체 후 재바인딩, 데드라인 밖 429 유지, shadow 교차 규칙 통일, 첫·이어가기 다리 터미널 수리, ACL reap 후 정리)은 맞고, #5480·#5365에서 지적했던 구멍도 이 트리에서 메운 상태로 보입니다. draft는 유지한 채 (1) #5480·#5365를 이 PR에 흡수되면 닫고, (2) #5549와 정리 파일 소유권을 한 줄로 정하고, (3) changed-import-graph 또는 합의된 대체 증거를 채운 뒤 Ready로 올리세요. 호스티드 linux 테스트·hygiene는 초록으로 보이니, 그 위에 위 세 가지만 고정하면 됩니다.

이 댓글은 grok-bot이 작성했습니다

A timed-out pacing test left its proxy and request in a local finally while
afterEach stopped the mock upstream and removed the state home. Own that work
in the fixture, share one listener-stop promise, and settle cancellation before
releasing the homes. Preserve ordinary assertion errors while consuming the
expected late teardown abort.

Use the existing test-only icacls runners for the synthetic HTTP fixture so
cold Windows permission setup does not consume its unchanged five-second
behavior budget. Preserve real listener and SQLite ownership checks, including
reacquisition from a different home after cancellation. Resolve cleanup owners
during protected preload setup, and drain native releases before config work.

Validation: the original explicit eight-file wrapper passes 199 tests with
976 assertions; typecheck, structure, privacy and the file-size ratchet pass.
This does not claim real OS ACL coverage or repair the separate changed-mode
empty-selection result.
…ures

Keep key-failover HTTP fixture execution evidence in the PR and validation manifest. This source document describes common cancellation, listener, producer and reap ownership without importing CI-only workflow contracts.
@lidge-jun

Copy link
Copy Markdown
Owner

Partial landing note: the replay-isolation commits carried from #5480 landed on dev in #5575 (b7351ddef3), rewritten. A caller with no principal now fails closed instead of using a shared loopback principal, and the sidecar keeps one three-send budget across reset and 429 recovery.

Still only in this PR: Cursor combo shadow-call isolation; terminal-less and continuation bridged-leg repair in passthrough-delivery.ts; the test sandbox cleanup and ACL-worker reaping; the key-failover fixture shutdown; and their docs. Rebasing onto dev will conflict in request-prepare.ts, executor.ts, bridge-search-replay-cache.ts, server-key-failover-e2e.test.ts and the replay tests. Take dev's version of the replay parts.

lidge-jun added a commit that referenced this pull request Sep 22, 2026
After close() began, createTestCaseLifecycle().run() swallowed any error
named AbortError, so an unrelated cancellation inside a case could read as
success. Absorb only abort.signal.reason, which signal listeners and an
aborted fetch reject with, and pin that a foreign AbortError still rejects.

This diverges from the helper copy in #5562 by this one condition; that
lane should take this version when it rebases.
@lidge-jun

Copy link
Copy Markdown
Owner

Carried in #5608: seven commits are cherry-picked with authorship kept, plus follow-ups that keep combo shadow interception on the dispatch pick and bind the repaired-leg replay test to a keyed caller. The changes from 76aa665 and 7e826dc are already on dev through b7351dd (#5575). The shared sandbox cleanup from 421ba78 and the helper portion of 6b122cd are carried by #5597, which replaced #5549. The key-failover fixture lifecycle from 6b122cd and the documentation in 6ea3a95 build on that helper and are not carried; they can be re-offered once #5597 lands. Closing as superseded. Thank you @luvs01.

@lidge-jun lidge-jun closed this Sep 22, 2026
lidge-jun added a commit that referenced this pull request Sep 23, 2026
…down (#5483, #5549) (#5597)

* test(ci): move the PR trigger contract into a review-lanes test file

Move "PR checks reach every branch the target gate accepts" byte for byte
out of ci-workflows.test.ts, which sits near its file-size cap, into a
registered sibling. The following review-gate commits extend that contract
there instead of growing the capped file.

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

* fix(ci): treat private and generator members as code; match stacked parents by head repo

Comment-only detection treated any changed line starting with "#" or "*"
as a comment, so a src/ change adding a #private member or a *generator
method skipped the regression-test requirement. Track block-comment state
per diff line instead; anything ambiguous still counts as code.

The wrong-base gate exempted a PR as stacked when any open PR's head ref
matched its base ref, comparing that PR's BASE repository, which is always
this one. A fork PR from its own "main" therefore exempted every PR aimed
at main. Match the open PR's head repository against the base repository
and fail closed when it is unavailable.

Carries #5311 (via #5549).

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

* ci: run the expensive suite for any repository workflow or script change

The push trigger and the pull-request scope filter listed four workflow
files by name, so a change to any other workflow or to the gate scripts
under .github/scripts skipped the suite that exercises them. Replace the
four entries with .github/workflows/** and .github/scripts/** on both
paths, and pin the new list in the review-lanes contract.

Carries #5297 (via #5549).

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

* ci: add a manual release-gates lane that leaves diagnostic suites unrequested

A manual dispatch always requested the unsharded macOS control, and the
default lane also requested nine Windows suite shards, so collecting
ordinary gate evidence by hand also paid for both diagnostics. Add
lane=release-gates: the ordinary jobs keep their usual selection, while
macos-control now runs only for an explicit allowlist (empty, all,
macos-control) so an unknown future lane skips it. The aggregate mirrors
that allowlist for its dispatch expectation.

The review-lanes contract evaluates every job condition across events and
lanes, and executes the checked-in aggregate step to prove release-gates
accepts the diagnostic skips but still rejects a failed, cancelled or
missing requested producer. A manual lane does not change release
eligibility, which still requires push-event CI on the exact SHA.

Carries #4597 and its aggregate follow-up (via #5549).

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

* test: drain sandbox producers and ACL reaps before removing the preload root

The preload removed its isolated root in afterAll and again from a
synchronous exit hook, while native-main startup releases, config
hardening and Windows ACL child reaps could still be running against it.
Resolve those owners during protected setup, then have teardown drain the
producers and wait for the registered reaps before removing the root. The
exit fallback only retries an already drained root and otherwise leaves it
for ownership-checked stale recovery. A delayed-reap regression pins the
order, and a case lifecycle helper gives fixtures one shared stop/close.

The three files and the structure paragraph are byte-identical to the
copies carried by #5562, so either landing order merges cleanly.

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

* test: absorb only the case lifecycle's own abort reason during teardown

After close() began, createTestCaseLifecycle().run() swallowed any error
named AbortError, so an unrelated cancellation inside a case could read as
success. Absorb only abort.signal.reason, which signal listeners and an
aborted fetch reject with, and pin that a foreign AbortError still rejects.

This diverges from the helper copy in #5562 by this one condition; that
lane should take this version when it rebases.

---------

Co-authored-by: luvs01 <27862058+luvs01@users.noreply.github.com>
lidge-jun added a commit that referenced this pull request Sep 23, 2026
…y work (#5608)

* docs(devlog): plan the L5 luvs01 Responses and usage bundle

Record the disposition, provenance and ordered carry recipe for #5474, #5305, #5434, #5560, #5542, #5553, #5562 and #5556.

* fix(usage): verify OAuth rotation before withheld attribution

(cherry picked from commit 1ac1ba0)

* test(oauth): cover the eligible-target positive case and gate assertion

(cherry picked from commit f6778bf)

* fix(oauth): require a live two-account roster before withheld attribution

The eligibility probe added for #5434 could report an alternate when the failed account had been removed and one stored account remained, because it skipped the roster-size guard that rotation applies. Read the roster once, apply the same guard, and describe the probe as applying no cooldown rather than as non-mutating.

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

* fix(adapters): close Grok continuation and normalized tool catalog gaps

Keep the existing request shape, scope external Cursor continuations to the
current user request, repair ctc_ ids for xAI, and rebuild a full replay when
a checkpoint no longer fits the envelope. Invalid or unreadable inputs fail
closed to the previous safe default with debug diagnostics.

This is the net change of two source commits. Their empty-catalog selector
edit to src/adapters/xai-web-search.ts is omitted because dev already carries
that rule (b20acc7, #5376), and the temporary test additions to capped files
are replaced by the final extracted test files.

(cherry picked from commit 71a9fe5)
(cherry picked from commit ffd50f4)
Refs #5350, #5560

* fix(cursor): preserve continuation scope and avoid false repetition recovery

(cherry picked from commit 5a99d4d)
(cherry picked from commit 31f21f0)

* fix(responses): lower undeclared historical custom tools when the destination denies them

Routed lowering collected only current custom declarations, so a compacted or replayed custom_tool_call leaked to xAI-like gateways as the native item type and came back as a misleading 422 missing id. Convert protocol-history items from the top-level input without expanding the live catalog, request full replay for orphan results, and fail closed before serializing leftovers.

Co-authored-by: Cursor <cursoragent@cursor.com>
(cherry picked from commit 5da2883)
(cherry picked from commit a326b67)

* test(responses): split historical custom-tool replay coverage off the passthrough ratchet cap

openai-responses-passthrough.test.ts is already at its 4809-line ceiling. Keep the new wire fixtures in a responses-prefixed file so the layout seed resolves it without raising a cap.

Co-authored-by: Cursor <cursoragent@cursor.com>
(cherry picked from commit 6f43794)
(cherry picked from commit 7e8fb09)

* fix(responses): reject malformed historical custom calls

(cherry picked from commit 52f7448)
(cherry picked from commit 57407be)

* fix(responses): bind historical outputs to custom calls

(cherry picked from commit 5654b41)
(cherry picked from commit c7781bf)

* fix(responses): reject duplicate historical call ids

(cherry picked from commit dc948dc)
(cherry picked from commit fbecefa)

* fix(xai): preserve stateful tool output continuations

(cherry picked from commit 4edc411)
(cherry picked from commit 82a5f6d)

* fix(xai): keep replay-miss reasoning cleanup independent of output repair

Co-Authored-By: Epinephrine <luvs01@hanmail.net>
(cherry picked from commit 67ccd8d)
(cherry picked from commit aac783f)

* test(responses): verify combined continuation boundaries

Exercise stateful output-only deltas, independent replay-miss reasoning cleanup,
capability-driven historical lowering, placeholder ordering, native item-ID
repair and preservation of the existing empty-catalog denial. Record the
combined history contract and register the carried and new regression files.

The layout-marker cleanup from e8e179f was
completed while resolving its preceding source commit onto the current map.
The existing dev selector normalization and role-fixture corrections remain
authoritative and are not replaced by weaker or duplicate source changes.

Co-authored-by: Yeonwoo Choi <32544727+twoimo@users.noreply.github.com>
Co-authored-by: maosisheng <maosisheng@gmail.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Epinephrine <luvs01@hanmail.net>
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
(cherry picked from commit 2ec0cd1)

* fix(responses): normalize wrapped MCP tool names

(cherry picked from commit b4c839b)
(cherry picked from commit f68b32646069d3e385748fc6c36848765c68e02e)
(cherry picked from commit e555e73)

* fix(responses): unwrap default apply patch aliases

(cherry picked from commit 453df76)
(cherry picked from commit d63ff542415895fa25f5599aad9dc632c80de92d)
(cherry picked from commit 7cbbf44)

* docs(structure): record default.-prefixed alias recovery in the freeform contract

The responses owners still claimed only bare exec/apply_patch calls accept alternate-field or outer-fence recovery; compileCodeModeHelperInput now also unwraps provider-invented default. aliases. Update runtime.md, transports/responses.md, providers/{chat-compat,kiro,xai-grok}.md.

Co-Authored-By: Epinephrine <luvs01@hanmail.net>
(cherry picked from commit 165ddd0)
(cherry picked from commit d0776e703681729e2ea447775e77397faab16f29)
(cherry picked from commit 19a2005)

* fix(responses): compile structured shell payloads sent to code-mode exec

(cherry picked from commit 9430bbd)
(cherry picked from commit 59bc70bc30f954eea5a63f8a6fcf4de4dcbfd865)
(cherry picked from commit 9662528)

* test(responses): verify combined tool normalization boundaries

Verify that default.apply_patch names and patch/content wrappers emit the same executable input through JSON and fragmented SSE. Move the carried decision record to unused ADR-0099 because current dev already owns ADR-0097; preserve both the structured code-mode shell and existing schema-bound flat-shell contracts.

Co-authored-by: kosta <kosta963@gmail.com>
Co-authored-by: Flowershangfromthebranches <152056395+Flowershangfromthebranches@users.noreply.github.com>
(cherry picked from commit 3da6366a60ac5964e8a8de43486125d72729f8c8)
(cherry picked from commit b57d7c5)

* fix(responses): refuse ambiguous OpenCode Go resets

(cherry picked from commit 62ac159)
(cherry picked from commit b8f9a45)

* test(responses): prove OpenCode Go pre-answer reset refusal by execution

Per review on #5446: replace the replaySafe source-string count with an execution test that drops the connection before the answer on an opencode.ai/zen/go destination and asserts the 429 upstream_reset_replay_refused with exactly one send.

(cherry picked from commit 92b74ec)
(cherry picked from commit 808dd85)

* fix(routing): isolate policy retry body snapshot

(cherry picked from commit 8e2a0fe)
(cherry picked from commit db854bf)

* test(routing): pin the retry snapshot against nested input mutation

(cherry picked from commit a240fc9)
(cherry picked from commit b037810)

* fix(responses): isolate policy compaction state

(cherry picked from commit 714119e)
(cherry picked from commit e6f9339)

* fix(tests): bound the cold-spawn warm-up child on a live event loop

The warm-up child was waited on through Bun.spawnSync, which made the spawn's
own timeout the only bound it could honour — and no bound at all when the
child or the primitive wedged: while a synchronous spawn blocks, the event
loop is dead, so the hook budget and the per-test timeout freeze inside the
same wait and nothing reports anything. Run 35511743422's macos 2/2 leg held
that shape for eighteen silent minutes inside client-connect.test.ts before
the job ceiling cut it and reported cancelled, which the ci gate reads as
failure.

The bound now lives on the parent's live loop: an asynchronous spawn, SIGKILL
at the existing derived deadline, a short reap grace, and the call settles
with or without the child's exit or EOF — so a descendant holding the pipes
or a child that outlives its kill cannot turn a warm-up into an unbounded
wait. A timed-out child now fails the warm-up by name instead of hanging the
job.

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
(cherry picked from commit aa9b889)
(cherry picked from commit f86a534)

* fix(responses): fail closed on synthetic or stale compaction source selectors

compactionRoutingKeepsProviderIdentity evaluated override.sourceModel as the
raw client selector, so a synthetic --fast/--effort form of a policy or combo
selector (ocx/primary--fast) missed resolvePolicyProfileId/resolveComboId and
fell through routeConcreteModel to the default provider. The same fallthrough
swallowed policy aliases renamed or deleted mid-conversation, since
config.routingProfiles is mutated in place. Both cases could wrongly report
identity match and let provider-private compaction state or caller credentials
cross a backend boundary.

Strip synthetic-row suffixes via parseSyntheticRowId before the identity
checks, and treat a source that only routes through the default provider as
unproven: it can never match a concrete identity.

Co-Authored-By: Epinephrine <luvs01@hanmail.net>
(cherry picked from commit 7e59315)
(cherry picked from commit 76b40f9)

* fix(codex): bind scoped quota suppression to alternate

(cherry picked from commit 23a3694)
(cherry picked from commit 385f338)

* fix(ci): restore core.ts to file-size ratchet cap

The scoped-quota re-export grew src/server/responses/core.ts past its committed 210-line cap (213). Collapse the two-name re-export back to one line; the file's export list already carries longer single-line statements.

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
(cherry picked from commit 559db16)
(cherry picked from commit feb0c16)

* fix(codex): record wrapped quota on suppressed moves and bind caller main

- record the normalized 429/402 outcome before returning no-alternate on a
  suppressed same-workspace move, so a 5xx-wrapped quota refusal still cools
  the refused account instead of reading as transient
- bind a request-owned `main` alternate by the caller credential's own
  workspace id (chatgpt-account-id header, else the bearer token's account
  claim) via callerCodexWorkspaceAccountId
- apply the same scoped-quota workspace gate to the single bounded alternate
  send in the native /responses/compact path
- cover all three in tests and update the transport doc

Co-Authored-By: Epinephrine <luvs01@hanmail.net>
(cherry picked from commit 5267593)
(cherry picked from commit 466c75c)

* test(server): move scoped-quota auth cases into a sibling file under the size cap

server-auth.test.ts grew to 4684 against a 4589 baseline cap, so the
file-size ratchet failed shard 3/4. The three scoped-quota suppression
cases move byte-for-byte into server-auth-scoped-quota.test.ts, and the
pool-retry harness they share is extracted to
tests/helpers/pool-retry-harness.ts (per-run OPENCODEX_HOME dir, so each
importing file keeps its own module state under bun test --isolate).

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
(cherry picked from commit b391c99)
(cherry picked from commit 9050722)

* fix(codex): re-check abort after the scoped-quota body read

The workspace classification in shouldRetryCodexScopedQuotaOnAlternate
reads the first response body asynchronously, so a client disconnect can
land after the earlier abort check but before the branch records the
first account, cancels its body, and sends the alternate. Re-check the
abort signal immediately after the await in both paths: compact returns
the 499 client_cancelled response after releasing the alternate lease,
and the regular path releases its permit and lease and returns
no-alternate while still recording the first account's real outcome.

Co-Authored-By: Epinephrine <luvs01@hanmail.net>
(cherry picked from commit c52b64b)
(cherry picked from commit b2eda92)

* fix(codex): release the discarded compact rejection body on abort

The 499 exits around the scoped-quota classification return a fresh
response while the first rejection's body is still open; cancel it so
the abandoned upstream connection and tee resources are released.

Co-Authored-By: Epinephrine <luvs01@hanmail.net>
(cherry picked from commit 813efcc)
(cherry picked from commit 1069b54)

* fix(codex): keep compact abort cleanup off the return path

upstream.body.cancel() can wait on a custom or stalled source; awaiting
it at the abort checkpoints would park the 499 reply on cleanup. Fire
it with the request's abort reason and swallow rejection, the same
best-effort shape bufferCompactResponse already uses.

Co-Authored-By: Epinephrine <luvs01@hanmail.net>
(cherry picked from commit 2d1ee69)
(cherry picked from commit 37a006e)

* test(responses): preserve terminal refusal across recovery boundaries

Exercise both operator-granted and default-denied reset paths through policy fallback and the alternate-account eligibility gate. Clarify that retries serialize the original body snapshot while identity metadata is established per attempt, and document synthetic compaction identity and transient replacement refusal.

(cherry picked from commit be1fee9)

* test(cursor): pin exact host-wrapper classification in continuation scope

The wire carries no provenance for compaction summaries or ambient browser state, and the Codex client detects stored summaries by the same exact prefix. Pin that a user message that is itself an exact wrapper stays in history while the preceding real request remains the labeled one, and document the rule next to the continuation contract.

Co-authored-by: Yeonwoo Choi <32544727+twoimo@users.noreply.github.com>

* test(server): prove a suppressed same-workspace alternate is never sent

Both credentials in the 5xx-wrapped scoped-quota case carry the same workspace header, so a negative check on the other account id could not fail. Assert the exact one-send sequence, and narrow the transport contract: suppression covers the in-request move, while later requests still select by per-account health.

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

* docs(devlog): record the L5 responses sequence outcome

* docs(devlog): amend the L5 search and usage plan after review

* fix(responses): isolate Cursor combo shadow calls

(cherry picked from commit 3e5a4dd)
(cherry picked from commit 7f45883)

* fix(responses): repair terminal-less bridged search legs

(cherry picked from commit 1fb6005)
(cherry picked from commit c4fa8c8)

* fix(responses): repair continuation legs and prove behavior in tests

(cherry picked from commit 3948970)
(cherry picked from commit 8d46989)

* fix(responses): close shadow combo intersection and continuation coverage

Apply the existing source-target non-intersection rule before early combo interception, and add production-path behavioral coverage for terminal-less continuation repair.

(cherry picked from commit 4bfcc0a)
(cherry picked from commit 3f3fdf1)

* test(web-search): preserve complete but open bridge-leg coverage

Carry the end-to-end handleResponses regression and transport contracts for repairing both the first and continuation search legs. The corresponding production changes are already preserved by the earlier terminal-repair carries; keep this broader integration coverage without applying that implementation twice.

Source commit: b1044e7
Co-authored-by: Epinephrine <luvs01@hanmail.net>

(cherry picked from commit 973a4ac)

* test(web-search): keep repaired replay within caller and serving scope

(cherry picked from commit bb49c9f)

* test(server): reap fixture ACL workers before removing failover homes

(cherry picked from commit ae52669)

* fix(responses): keep combo shadow interception on the dispatch pick

The carried early intersection check sampled a combo target with routeModel before dispatch. The combo loop then picked again, so the interception decision could follow a different target than the one sent, and a random or round-robin combo advanced its selection state for a probe. Restore the existing combo rule (a shadow call rewritten to a combo enters the combo) and its regression test, and keep the combo-child isolation marker.

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

* test(web-search): bind repaired-leg replay to a keyed caller principal

The repaired-leg replay assertion expected a shared keyless loopback principal, which the replay cache refuses by design: keyless loopback callers get no retained replay. Configure an inbound API key, derive the expected principal with the production resolver, pass the full loopback admission, and assert that a keyless caller cannot form a scope.

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

* fix(usage): restrict legacy -main provider collapse to Codex labels

A -main suffix was stripped from ANY provider name, so a configured provider like openrouter-main silently merged into openrouter in usage summaries and pricing fallback. Restrict the compatibility mapping to the known Codex provider labels (openai/chatgpt/openai-multi-main), matching codexAccountProviders.

(cherry picked from commit 89a9272)
(cherry picked from commit 0f0ef96)

* fix(cli): mark access-key usage unavailable when attribution is absent

Without attributionSince the server is reporting an empty or unreadable usage log, but the table still rendered 0 totals and never-used markers that are indistinguishable from real data. Show an unavailable marker spanning the usage columns instead, matching the ambiguous-union precedent.

(cherry picked from commit 3c3f14b)
(cherry picked from commit 83514c3)

* fix(cli): treat a malformed attributionSince as unavailable

typeof === 'string' accepted any value, so a malformed attributionSince made
usageAvailable true and printed usage cells plus an 'attribution since'
footer. Parse once, derive availability from the validated result, and reuse
it for the footer. Covers the malformed-string regression.

(cherry picked from commit e7584c3)
(cherry picked from commit 1380693)

* fix(gui): keep the app-server status read alive across Models tab switches

(cherry picked from commit 9e6870d)
(cherry picked from commit 5563577)

* fix(usage): bound persisted requestedModel selectors

Selectors are not length-bound at admission, so persist over-long values as a bounded prefix + sha256 digest of the full selector and encode the request-history requested_model filter input identically. Two selectors sharing the bounded prefix stay distinguishable instead of colliding on a truncated prefix.

(cherry picked from commit e3d0f9b)
(cherry picked from commit c8a9d1a)

* fix(spend): enforce ceilings on native chat sends

(cherry picked from commit 347204f)
(cherry picked from commit 823a7d2)

* fix(usage): preserve observation and native reservation boundaries

(cherry picked from commit 22ee516)

* test(usage): isolate native spend regressions within file caps

(cherry picked from commit 1a8d5f7)

* test(usage): drain fixture owners before removing Windows homes

(cherry picked from commit 2241d03)

* test(usage): batch canonical pagination fixture writes

(cherry picked from commit ddfef13)

* fix(cli): accept only an ISO-8601 UTC attributionSince

Date.parse accepts strings such as "0" and rolls impossible dates over, so a malformed attributionSince could still switch the key table to numeric usage and never-used cells. Require the ISO-8601 UTC shape the server emits and an instant that round-trips to the same second.

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

* docs(usage): state the aliasing limit of the idempotent selector encoding

Encoded requestedModel values must map to themselves because rows are normalized again on read, so a literal selector equal to another selector's persisted form shares its identity. Say so where the encoding is defined, and pin the behavior in the request-history test so the limit is deliberate rather than silent.

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

* docs(devlog): record the L5 search and usage outcome

* docs(devlog): record the L5 pull request

* docs(structure): keep runtime.md within its 600-line budget

The default.-prefixed freeform wording wrapped one extra line and pushed structure/runtime.md over the structure SSOT budget. Reflow the paragraph; the wording is unchanged.

* refactor(cursor): move current-request selection out of protobuf-request

The carried continuation-scope helpers brought src/adapters/cursor/protobuf-request.ts to 2,000 lines, the file-size threshold for files without a recorded cap. Move the current-request selection and its host-wrapper classification to a sibling module unchanged; the caller passes its text extractor.

Co-authored-by: Yeonwoo Choi <32544727+twoimo@users.noreply.github.com>

* docs(devlog): record the #5553 re-pin in the L5 plan

* docs(devlog): record the L5 original closures

* fix(usage): keep the native Chat spend estimate out of recorded usage

The native Chat spend reservation reused usageLogInputTokens for its input estimate. That field also feeds attempt usage, so native Chat attempts started recording an estimate-inflated input instead of the provider-reported counts (hosted CI: server-xai-oauth-401-replay and server-key-failover-e2e). Reserve the estimate through a spend-only spendInputEstimateTokens field; the reservation still charges it before each physical send.

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

* test(server): identify the suppressed alternate by its credential

Hosted CI showed the send layer retrying the refused account's transient 502 on the same credential, so an exact one-dispatch count was the wrong oracle. Both accounts share a workspace header; record the credential each physical send presents and assert the suppressed alternate's credential never appears.

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

* fix: close L5 retry, continuation, and history review gaps

Classify spend refusals on transient retry legs, keep Cursor retry provenance in checkpoint actions, and rebuild legacy history selectors into the bounded projection. Scope policy snapshots and correct warm-up deadline handling, with focused regressions and documentation.

* fix: retain policy fallback for decorated selectors

Normalize supported Fast and effort policy selectors before snapshot gating, restore a real policy-hop fixture, and align Cursor wire assertions with retry provenance guidance.

---------

Co-authored-by: luvs01 <27862058+luvs01@users.noreply.github.com>
Co-authored-by: Yeonwoo Choi <32544727+twoimo@users.noreply.github.com>
Co-authored-by: twoimo <twoimo@twoimoui-MacBookPro.local>
Co-authored-by: maosisheng <maosisheng@gmail.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Epinephrine <luvs01@hanmail.net>
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: kosta <kosta963@gmail.com>
Co-authored-by: Flowershangfromthebranches <152056395+Flowershangfromthebranches@users.noreply.github.com>
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.

2 participants