Skip to content

[WRONG BRANCH] fix(responses): bound combo recall model retention - #4755

Merged
lidge-jun merged 1 commit into
codex/cf2-output-headroomfrom
codex/cf3-combo-recall-retention
Sep 16, 2026
Merged

lidge-jun merged 1 commit into
codex/cf2-output-headroomfrom
codex/cf3-combo-recall-retention

Conversation

@lidge-jun

@lidge-jun lidge-jun commented Sep 16, 2026

Copy link
Copy Markdown
Owner

Summary

Carries #4525 by @luvs01 onto current dev, stacked on #4750. Base is codex/cf2-output-headroom; retarget to dev once the parents land.

The remembered model id is provider-reported and arrives on the response, so nothing upstream of the recall store bounds its length. Lane keys are already SHA-256 digests, which means the existing 256-lane cap bounded the number of entries but not the bytes those entries held, and a long-running process could accumulate arbitrarily large remembered strings.

src/server/responses/combo-session-recall.ts now bounds retention on two further axes: 1 KiB per remembered model id and 64 KiB in aggregate. The size test runs on code units before encoding, because a UTF-8 encoding is never smaller than its code-unit count, so the bound never pays the allocation it exists to prevent. Aggregate eviction drops the least recently written lane — the front of the map, because every write re-inserts its own lane at the back — and a single entry is capped far below the aggregate budget, so a write can never evict itself. Every removal path now goes through one helper that releases the entry's bytes, so the counter cannot drift from the map through read-time invalidation, reconciliation, or a lane rewrite.

The store is also registered for periodic expiry. The 30-minute TTL was previously evaluated only on read or on a generation change, so a lane that is never read again held its entry until the process exited.

The ordering change, and why it went the other way

The original patch cleared the lane when a model id was too large to retain. That is unsafe here. The publication callback carries a config generation, not a request order, so two accepted completions on the same lane under the same generation can arrive out of order — and a clearing branch lets the older one erase the newer selection. There is no monotonic request token in this path to compare against, and introducing one would mean threading ordering state through core-combo.ts before async dispatch, which is well outside a retention-bounding change.

So an unretainable model id declines the write instead. That matches how every other rejection in rememberComboForLane already returns (stale generation, unknown owner, blank model), and it preserves the established contract exactly: an older response never overwrites or clears a newer one. The docs were written to that behavior rather than to the stronger claim.

The 16 mechanical cross-link sentences the original patch appended to unrelated structure/ documents are dropped. structure:check does not read the git diff and cannot require them; only structure/transports/responses.md, which owns this contract, is updated.

Verification

No local suite, no focused test file, no typecheck, no build and no dependency install was run — this lane is under an explicit owner instruction forbidding local execution. Evidence is static source reading plus hosted CI.

Static checks performed:

  • Confirmed lane keys are bounded: sessionLaneIdFromRequest composes normalizeLogConversationId values, which are SHA-256 digests truncated to LOG_CONVERSATION_ID_LEN. The model id really is the only field of unbounded length, so byte accounting is scoped to it.
  • Traced every existing removal site and routed each through the new helper: read-time invalidation, the reconciliation loop, and the lane rewrite inside rememberComboForLane.
  • Checked the two sweeper tests that iterate the production registration table. global fake-clock sweep invokes every production clock registration derives its expectation from STATE_STORE_REGISTRATIONS itself, so adding sweepExpired is self-adjusting; a sweeper tick expires continuation and Antigravity rows registers only those two stores, so its rowsRemoved: 2 is unaffected.
  • Re-read the recall assertions in tests/responses/responses-compaction-routing.test.ts (TTL boundary at 30 minutes, 257-write lane eviction keeping lanes 1 and 256, selector precedence, invalidation on delete/rename/disable). All use short model ids well inside the 1 KiB bound and lane counts inside the 64 KiB budget, so none changes.
  • File-size ratchet: combo-session-recall.ts 89 → 149 lines, no baseline entry, global threshold 2,000.
  • structure/transports/responses.md is already in grace.oversizeDocs, which is a boolean allowlist with no recorded size, so the addition does not trip the budget check.

Hosted CI: this is the tip of the cf1 → cf2 → cf3 stack, so its run gates all three layers. Run URL and conclusion are recorded in a comment on this PR.

Hosted CI evidence

Head 246d703aec275b3c7267ebde9a004334614b905e. Run: https://github.com/lidge-jun/opencodex/actions/runs/35049570323

The four test shards are reported explicitly rather than relying on the rollup, because a stacked tip can have its heavy jobs filtered out by the changes path filter and still aggregate green, and because the rollup does not reflect shard failures while the run is in progress.

test 1/4  completed  success
test 2/4  completed  success
test 3/4  completed  success
test 4/4  completed  success

Also success on this head: changes, gates, storage policy, api usage, hygiene, macos 1/2, macos 2/2, keyring ubuntu|windows|macos, docker smoke, npm-global ubuntu|windows|macos, react-doctor, label, resolve-pr, select windows runner. The Windows shard matrix and the macOS control job are skipped by the workflow's own selection, not failed.

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.

@lidge-jun
lidge-jun requested a review from Ingwannu as a code owner September 16, 2026 02:18
@github-actions

Copy link
Copy Markdown
Contributor

Deterministic PR hygiene checks passed.

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 16, 2026

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review Completed 2026-09-16T02:21:25.670433Z 9a50b85 PR opened
ℹ️ 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" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@github-actions github-actions Bot added the bug Something isn't working label Sep 16, 2026
@coderabbitai

coderabbitai Bot commented Sep 16, 2026

Copy link
Copy Markdown
Contributor

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

🗂️ Base branches to auto review (2)
  • ^dev$
  • ^preview$

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Advanced

Run ID: c3a5b558-aa46-4fd4-80f4-ea16f63d9eb7

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

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.

@lidge-jun

Copy link
Copy Markdown
Owner Author

리뷰 · 우선순위 76 / 80

이 PR은 이슈 #4525를 현재 dev 위로 다시 실어 온 유지보수 패치입니다. 콤보 세션 리콜 저장소는 대화 레인마다 마지막으로 성공한 콤보의 responseModel을 기억해 압축 요청이 같은 콤보를 다시 고르게 돕습니다. 현재 dev HEAD 3070d64d8822c6d8c62989665f82ab665e4d164c (패키지 2.57.0)의 src/server/responses/combo-session-recall.ts는 레인 수 256개와 30분 TTL만 있습니다. 레인 키는 이미 SHA-256 요약이라 길이가 묶여 있지만, 기억되는 모델 id는 공급자 응답에서 오므로 위쪽에서 길이를 막지 않습니다. 그래서 오래 돌아가는 프로세스에서는 레인 개수 한도만으로는 바이트가 계속 커질 수 있습니다.

고치는 축은 세 가지입니다. (1) 모델 id 하나당 1 KiB, 저장소 전체 64 KiB. 크기 검사는 UTF-8로 인코딩하기 전에 코드 유닛으로 먼저 보고, UTF-8이 코드 유닛보다 작아질 수 없으니 한도를 지키려고 할당을 먼저 쓰지 않습니다. (2) 집계가 넘치면 Map 맨 앞(가장 오래 안 쓰인 쓰기)부터 지웁니다. 쓰기마다 같은 레인을 뒤로 다시 넣기 때문에 앞쪽이 곧 LRU이고, 한 항목이 집계보다 훨씬 작아서 자기 자신을 쫓아내지는 않습니다. (3) 삭제는 모두 deleteEntry 한길로 가서 recallBytes가 맵과 어긋나지 않게 합니다. 읽기 무효화, 세대 재조정, 레인 덮어쓰기 모두 여기로 모입니다.

정렬(ordering) 선택이 중요합니다. 원본 #4525는 너무 긴 모델 id면 레인을 clear하려 했는데, 이 콜백은 요청 순서가 아니라 설정 세대만 들고 옵니다. 같은 세대에서 늦게 도착한 큰 모델 id가 먼저 도착한 정상 선택을 지울 수 있습니다. 그래서 이 PR은 보관할 수 없으면 write를 거절하고 기존 항목을 남깁니다. rememberComboForLane의 다른 거절(세대 낡음, 소유자 없음, 빈 모델)과 같은 모양입니다. 또 TTL이 읽기·세대 바뀔 때만 검사되던 구멍을 막기 위해 sweepExpiredComboRecallsrc/lib/state-store-registrations.tscombo-session-recall 등록에 붙였습니다. 안 읽는 레인도 배경에서 만료됩니다.

파일은 combo-session-recall.ts, 등록표, tests/oauth/state-store-sweeper.test.ts의 바이트·거절·집계·스윕 회귀, structure/transports/responses.md와 combos 가이드(영/한)입니다. 원본이 넣었던 관계없는 structure/ 교차 링크 문장들은 빼 두었고, 계약 문서만 고쳤습니다. types.ts/config.ts 분할 캠페인이나 pre-split monolith 재편집과는 무관합니다. 베이스는 dev가 아니라 codex/cf2-output-headroom(#4750)이고, 그 아래는 #4744(cf1)입니다. 부모의 combo-session-recall.ts는 지금 dev와 같아서 이 파일 차이는 현재 tip 기준과 같습니다. 다만 머지 순서는 스택을 따릅니다. 원본 #4525는 아직 dev 대상 OPEN이고 CONFLICTING입니다. 이 PR이 착지하면 landed-via-maintainer로 닫는 게 맞습니다. Closes #4525.

경로 base codex/cf2-output-headroom - dev 직접 머지 대상이 아닙니다. 부모 #4744 → #4750이 먼저 안착해야 리타겟/순서 머지가 열립니다.
경로 #4525 - 원본 PR은 dev에서 CONFLICTING입니다. 중복으로 두지 말고 이 캐리 PR 머지 후 닫으세요.
경로 docs-site/.../guides/combos.md - 영문 가이드가 256 conversations라고 쓰고, 구조 문서·코드 주석은 lanes입니다. 동작 설명은 맞지만 용어만 살짝 갈라집니다.
경로 hosted CI (head 9a50b858e319) - hygiene / enforce-target / docker smoke / keyring 일부는 통과했지만 test 1–4, gates, macos, npm-global windows 등이 아직 pending이라 mergeStateStatus: UNSTABLE입니다. 로컬 스위트는 이 레인 금지라 호스티드 그린이 증거입니다.
경로 rememberComboForLane decline 분기 - 긴 모델 id가 기존 선택을 지우지 않는 회귀는 테스트로 잠겼습니다. 집계 64개×1KiB와 재쓰기 바이트 회수, 스윕 후 예산 복구도 같이 있습니다.

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

너의 추천
열어 두세요. 호스티드 CI(특히 test/gates) 그린을 확인하고 부모 #4744·#4750 착지 뒤 dev로 리타겟(또는 스택 순서 머지)하세요. 긴 모델 id에서 레인을 비우는 쪽으로 되돌리지 마세요. 머지 후 #4525는 Landed via #4755 + landed-via-maintainer로 닫으세요. Preview deploy는 계획에 없습니다.

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

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 9a50b858e3

ℹ️ 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".

{ name: "combo-session-recall", reconcileGeneration: reconcileComboRecall },
{
name: "combo-session-recall",
sweepExpired: sweepExpiredComboRecall,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Update every mapped structure document

The source-to-doc map in structure/INDEX.md shows that both changed areas, src/lib/ and src/server/, are described by multiple structure documents, but this commit updates only structure/transports/responses.md. Add the bounded-retention and periodic-sweep contract to every document mapped to those areas, or correct overly broad manifest ownership, so the repository’s architecture references do not remain incomplete.

AGENTS.md reference: src/AGENTS.md:L11-L11

Useful? React with 👍 / 👎.

@lidge-jun
lidge-jun force-pushed the codex/cf3-combo-recall-retention branch from 9a50b85 to ce4ad8d Compare September 16, 2026 02:29
The remembered model id is provider-reported and arrives on the response, so
nothing upstream of the recall store bounds its length. Lane keys are already
SHA-256 digests, which means the 256-lane cap bounded the number of entries but
not the bytes those entries held. A long-running process could accumulate
arbitrarily large remembered strings.

Bound retention on two more axes: 1 KiB per remembered model id and 64 KiB in
aggregate. The size test runs on code units before encoding, because a UTF-8
encoding is never smaller than its code-unit count, so the bound never pays the
allocation it exists to prevent. Aggregate eviction drops the least recently
written lane, which is the front of the map because every write re-inserts its
own lane at the back. A single entry is capped far below the aggregate budget, so
a write can never evict itself.

Every removal now goes through one helper that releases the entry's bytes, so the
counter cannot drift from the map through the read-time invalidation path, the
reconciliation path, or a lane rewrite.

An unretainable model id DECLINES the write rather than clearing the lane. That
is the ordering-sensitive part. This callback carries a config generation, not a
request order, so two accepted completions on one lane under the same generation
can arrive out of order; a clearing branch would let the older one erase the
newer selection. Declining matches how every other rejection in
rememberComboForLane already returns, and leaves the established contract intact:
an older response never overwrites or clears a newer one.

Register the store for periodic expiry as well. The TTL was previously evaluated
only on read or on a generation change, so a lane that is never read again held
its entry until the process exited.

Closes #4525

Co-authored-by: luvs01 <27862058+luvs01@users.noreply.github.com>
@lidge-jun
lidge-jun force-pushed the codex/cf3-combo-recall-retention branch from ce4ad8d to 246d703 Compare September 16, 2026 02:51
@lidge-jun

Copy link
Copy Markdown
Owner Author

Landing the combo fallback lane, top layer first.

Evidence at the exact head 246d703 (tree 4610771b34d2db6bc9d077257d51c145e3dc702e):

  • Heavy jobs actually executed rather than being path-filtered, read through the check-runs API rather than the check rollup: test 1-4/4 all completed with conclusion success, macos 1-2/2 succeeded, and the aggregate ci check completed with conclusion success.
  • gates, changes, storage policy, api usage, hygiene, docker smoke, keyring on three platforms, npm-global on three platforms and react-doctor all succeeded.
  • The windows shard matrix and macos control are workflow_dispatch-only and always skipped on pull_request. This lane carries no Windows-specific change, so that skip withholds no relevant evidence.
  • enforce-target is cancelled by workflow concurrency on the pr-gate-comment group, with a rerun queued behind the runner backlog. The conditions that check validates were confirmed directly: the base is the layer below, all three template sections are present, 6 changed files with none under gui/ and no truncated file list.
  • Ancestry verified so each layer closes as MERGED: codex/cf1-definite-context-overflow and codex/cf2-output-headroom are both ancestors of this tip.

Chained-child stacks merge top-down, so this lands in the parent branch and cascades to dev. CI evidence transfers by tree identity at each step.

Maintainer integration decision under MAINTAINERS.md / AGENTS.md, recorded with the exact-head evidence above.

@lidge-jun
lidge-jun merged commit e3b9913 into codex/cf2-output-headroom Sep 16, 2026
29 of 30 checks passed
@lidge-jun
lidge-jun deleted the codex/cf3-combo-recall-retention branch September 16, 2026 03:49
@github-actions github-actions Bot changed the title fix(responses): bound combo recall model retention [WRONG BRANCH] fix(responses): bound combo recall model retention Sep 16, 2026
@github-actions

github-actions Bot commented Sep 16, 2026

Copy link
Copy Markdown
Contributor

⏳ DRAFT

  • wrong target branch (codex/cf2-output-headroom); retarget to dev.

What to do

  • Retarget this PR to dev — all contributions go to dev.

Its title has been prefixed with [WRONG BRANCH].
Automatic draft conversion failed (token cannot change draft status). Please convert this pull request to a draft manually. The required enforce-target check will keep failing until every issue above is resolved.

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.

1 participant