Skip to content

test(responses): pin the #4546 incident as one system, not five fixes (#4546) - #4772

Merged
lidge-jun merged 5 commits into
codex/bl5-recovery-limiter-dispatchfrom
codex/bl7-4546-integration-regression
Sep 16, 2026
Merged

lidge-jun merged 5 commits into
codex/bl5-recovery-limiter-dispatchfrom
codex/bl7-4546-integration-regression

Conversation

@lidge-jun

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

Copy link
Copy Markdown
Owner

Summary

Each layer of this lane closes one seam of the #4546 amplification: the credential hop charged twice, the spend ledger with no caller, the refusal reported as a provider fault, the usage attributed to the wrong key, the withheld recovery that said "retry now". What none of them checks is whether the seams agree with each other.

This composes the real primitives — the request execution budget, the durable spend ledger with its request-scoped caller, the pool recovery limiter — and asserts the numbers describe the same events: physical sends, budget consumption, ledger reservation and settlement, and the refusal the caller is given.

The scenarios are the incident's own:

  • a request whose every layer tries to recover still stops at the ceiling, and the account move is refused because the repair already took the single shared reserve;
  • concurrent requests bound to one held account produce exactly one probe and one withheld result, sharing a process-wide allowance rather than each holding a private one, and the refused caller is given a strictly future time;
  • a caller with a working detour keeps it instead of adding a second trial to an account already known to be failing;
  • a fan-out child spends the parent's allowance rather than a fresh one, and the second move is refused on the request's single target transition;
  • a restart neither resets a ceiling nor settles the same send twice, and a replayed send id cannot authorise another dispatch.

A fixture that only counted sends would have passed throughout the incident, which is why every case ties a send count to the spend the ledger recorded for it.

This is the lane tip: its CI run is the gate for every layer beneath it.

Verification

No local suite, focused test, typecheck, install, or build step was run. The repository owner prohibits local suite execution in this lane after a past local run deleted real user home data. Verification is static reading plus hosted CI.

  • Every assertion is derived from the production modules themselves rather than from a mock of them: the budget, the ledger, the request-scoped spend caller and the pool limiter are the real implementations, with an in-memory journal standing in for the on-disk one.
  • The restart case round-trips through that journal, so it exercises the same replay path a real process does.
  • Registered in both scripts/test-layout/layout.json and tests/fixtures/test-layout-expected.json.

Hosted CI runs on this branch and gates the stack. The run URL and conclusion are reported to the integrating maintainer.

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.

Ledger refusal scope, and why an observe-only ledger may not refuse

Two corrections landed on this branch after the first CI run, both on the bl2 contract. A reviewer will reasonably ask why a ledger that enforces nothing by default is allowed to refuse anything at all, so the answer belongs here.

It refuses only an operator's configured ceiling. reserve() can deny for four different reasons, and only one of them is a spend decision: spend-limit-exceeded. The others — tracking capacity, an undurable reservation, a journal replay could not prove complete — all mean the ledger cannot account for this send, which is not the same statement as this send must not happen. The first version refused on any denial, so an install that had never configured a ceiling could stop sending because a journal got corrupted or a capacity bound was reached. That turns a feature added for observation into an outage, and it is a worse regression than the one #4707 describes. The default policy sets no maxTokens on any scope, so an unconfigured install now keeps exactly the count caps it already had and is never newly refused.

The shared ledger is resolved on the first charge, not when the request is built. sharedSpendLedger() opens a journal under the OpenCodex home and caches it for the life of the process. Resolving it during request construction meant two things: a request that never dispatches — refused at admission, answered locally, cancelled before its first send — created a journal it had no business creating, and the home captured was whichever one happened to be current for the first request the process ever built, not the home in effect when a send actually goes out. Deferring to the first charge fixes both.

Assertions corrected

Three assertions in the new tests claimed states the code never reaches. tsconfig includes only src, so test code is never typechecked and a test that asserts the opposite of what it claims passes silently; these were found by reading.

  • The concurrent-probe case asserted a limiter refusal, but the second caller short-circuits on the lease before it reaches the limiter and costs no allowance — the assertion claimed a path the code never took. The shared bound is now proved by asking the limiter directly.
  • The exhausted-ceiling case asserted final-recovery-spent where the total ceiling refuses first. It asserts total-exhausted, and checks reserveSpent separately for the point it was actually making.
  • The unstructured-error control asserted an exact 502 where the property that matters is that the identity is gone; it now asserts that.

File-size ratchet

src/server/responses/core.ts reached 214 lines against a 210-line cap in tests/fixtures/file-size-baseline.json — the spend-observer wiring added four lines. The ratchet only ever lowers caps, so exceeding one is a hard failure. The comment and the expression are each one line again and the file is back at its cap.

Why one test caught a real defect and three others caught nothing

The replay reconciliation shipped in this lane with a bug: it treated a replayed undispatched reservation as abandoned and handed its tokens back. tests/lib/spend-reservation-ledger.test.ts caught it, because that test asserts an invariant — an exhausted scope is still exhausted after a restart — rather than the shape of the code that produces it. The reasoning behind the bug was wrong in a way no shape assertion would have noticed: open does not prove nothing was sent, because the torn-tail rule directly above says the journal may be missing its last record, so a send that dispatched and died before its dispatch record landed is exactly what survives replay as open. Returning those tokens resets a ceiling that had already fired, which defeats the reason the ledger is on disk at all. Both live states now resolve to unresolved spend.

Three assertions added by this lane did the opposite and had to be corrected. One claimed a limiter refusal on a path that short-circuits before reaching the limiter; one named a denial reason the total ceiling pre-empts; one pinned an exact status where the property that mattered was the loss of an error identity. All three passed while asserting something other than what they claimed to cover, and none would have been caught by typechecking — tsconfig includes only src, so test code is never typechecked.

The source oracle in tests/lib/transient-budget-scope-source.test.ts was changed in the same spirit: instead of pinning the literal text of the line that mints the send budget, it now asserts that the spend observer is installed at that same place, which is the property that matters — a combo child inherits the parent's holder and must not open a second set of ledger entries for the same physical sends.

…#4546)

Each layer of this lane closes one seam of the #4546 amplification: the
credential hop that was charged twice, the spend ledger with no caller, the
refusal reported as a provider fault, the usage attributed to the wrong key, the
withheld recovery that said "retry now". What none of them checks is whether the
seams agree with each other.

This composes the real primitives -- the request execution budget, the durable
spend ledger with its request-scoped caller, the pool recovery limiter -- and
asserts that the numbers describe the same events: physical sends, budget
consumption, ledger reservation and settlement, and the refusal the caller is
given.

The scenarios are the incident's own: a request whose every layer tries to
recover, concurrent requests contending for one process-wide recovery
allowance, a caller that keeps its detour instead of adding a second trial to a
failing account, a fan-out child spending the parent's allowance rather than a
fresh one, and a restart that must neither reset a ceiling nor settle the same
send twice.

A fixture that only counted sends would have passed throughout the incident,
which is why every case ties a send count to the spend the ledger recorded for
it.
@lidge-jun
lidge-jun requested a review from Ingwannu as a code owner September 16, 2026 02:32
@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: ecea7d33-0cae-40a1-8e48-e47a00b0e0a5

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.

@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:35:25.529599Z 6317c41 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.

The account-change scenario the incident needs, written against current
behaviour because the #4710 refusal is owned by another lane and is not in this
stack yet.

What it pins now: continuation state is dropped and the turn continues, an
uploaded file reference is classified non-portable and is NOT removed by the
scrub, and the carriers must be read directly because the portability verdict
reports only the first reason it finds -- a body carrying both a response id and
a file reports the response id.

What it documents: once the refusal lands, that body must be declined before
dispatch and the refusal must win over the response id. The two properties above
are what the change has to preserve, so they are asserted now.

Also pins the accounting invariant that refusal owes: a decision made before
dispatch spends no send and books no ledger entry. A refusal counted as a send
would appear as provider load that never happened and would push a healthy
account toward a cooldown.
@lidge-jun

Copy link
Copy Markdown
Owner Author

리뷰 · 우선순위 77 / 80

설명

이 PR은 이슈 #4546에서 터진 비용 폭주를 다섯 개 레이어 각각의 버그로 보지 않고, 한 시스템이 같은 숫자를 말하는지를 한 파일로 고정합니다. 제목 그대로 pin the #4546 incident as one system, not five fixes입니다. 바꾼 파일은 세 개뿐입니다. 새 테스트 tests/responses/responses-4546-incident-regression.test.ts(+222줄)를 추가하고, scripts/test-layout/layout.jsontests/fixtures/test-layout-expected.jsonresponses 묶음으로 등록합니다. types.ts/config.ts 분리나 예전 core.ts/bridge 모놀리스를 건드리지 않으니 close-don't-rebase 대상이 아닙니다.

지금 CURRENT dev HEAD는 3070d64d8822c6d8c62989665f82ab665e4d164c(package 2.57.0, 마지막 머지 #4714)입니다. 이 PR의 base는 dev가 아니라 codex/bl5-recovery-limiter-dispatch(#4771)입니다. 스택은 #4745(bl1 hop permit) → #4756(bl2 durable ledger caller) → #4758(bl3 budget refusal) → #4760(bl4 usage attribution) → #4771(bl5 withheld retryAt) → 이 PR #4772(bl7 tip)입니다. remote에 codex/bl6-account-change-file-scope 브랜치는 보이지만 열린 PR은 없고, 본문·커밋이 말한 대로 account-change file 거절(#4710)은 다른 레인 소유라 이 스택 tip에서 의도적으로 비워 둔 상태입니다. CURRENT dev에는 아직 src/server/responses/request-spend.ts가 없고, 테스트가 부르는 createRequestSpendTracker·ledger 배선·recovery limiter 보강은 위 bl 스택이 가져온 것입니다. 그래서 이 PR만 단독으로 dev에 얹으면 컴파일이 안 됩니다. 부모들이 먼저 랜딩해야 합니다.

테스트가 묶는 생산 모듈은 가짜 mock이 아니라 실제 구현입니다. createRequestExecutionBudget/CODEX_TEXT_GUARDED_BUDGET_POLICY(maxTotalModelSends=4, base 3 + finalRecovery 1) from src/lib/request-execution-budget.ts, createSpendReservationLedger + in-memory SpendJournal from src/lib/spend-reservation-ledger.ts, createRequestSpendTracker from src/server/responses/request-spend.ts, createPoolBackpressureLimiter/resolveHeldAccountDispatch from src/routing/probe-lease.ts, account-change scrub helpers from src/server/responses/account-change-state.ts와 issuer map from src/codex/routing입니다. 시나리오는 일곱 개입니다. (1) 같은 계정 transient 3번 + repair 1번으로 천장에 닿은 뒤 account-failover는 final-recovery-spent로 거절되고 ledger reserved는 4×500(입력 100+출력 천장 400)과 일치. (2) 동시에 잡힌 held 계정은 probe 하나·withheld 하나, retryAt은 반드시 now보다 큼. (3) 이미 probe가 나간 뒤 detour가 있으면 두 번째 시도를 안 하고 detour를 유지. (4) combo fan-out child가 부모 allowance를 쓰며 두 번째 target 전환은 거절. (5) journal 재시작 후 settled/unresolved/reserved가 그대로이고 같은 sendId 재settle은 false. (6) account 변경 시 continuation은 떨어지고 file_id는 scrub되지 않으며 carriers를 직접 읽어야 함(#4710 PENDING 주석). (7) dispatch 전 거절은 send도 ledger도 0. 본문이 말한 대로 보내기만 센 픽스처는 사고 내내 통과했을 것이라, 각 케이스가 send 수와 ledger 숫자를 같이 묶는 설계가 이 PR의 핵심 가치입니다.

같은 #4546 에픽의 다른 열린 줄기와는 겹치지 않습니다. #4764/#4765는 codex/cx1→cx2 라우팅·문서 스택이고, 이 PR은 codex/bl* 비용·복구·ledger 스택 tip입니다. 에픽 #4546은 아직 OPEN입니다. mergeable은 MERGEABLE, mergeStateStatus는 UNSTABLE입니다. tip 커밋 94db101206f8에는 [skip ci]가 없습니다. 본문도 This is the lane tip: its CI run is the gate for every layer beneath it라고 적었고, Cross-platform CI가 pending으로 잡혀 있습니다(로컬 suite 금지 정책과 맞음). Draft 아니고 labels는 비어 있습니다(라벨은 건드리지 않음).

점수 77/80입니다. CURRENT dev 기준으로는 아직 랜딩 전 스택 tip이지만, #4546을 레이어 단위 회귀가 아니라 시스템 불변식으로 고정하는 테스트 PR이라 유지 가치가 큽니다. 감점 여지는 아래 잔여 관찰(자식 객체 별칭, 스택 의존, #4710 미완)뿐입니다.

라인 1 - src/routing/probe-lease import가 두 줄로 나뉘어 있음(기능 문제는 아니고 정리 여지).
경로 tests/.../responses-4546-incident-regression.test.ts fan-out 케이스 - const child = parent는 같은 객체를 별칭한 것. 주석은 combo child 상속을 말하지만, 실제 combo 배선이 holder를 넘기는 경로는 이 파일만으로는 증명하지 않음. 의도 문서화는 되고 불변식(공유 used/전환 한도)은 맞지만 상속 배선 자체의 회귀 핀은 아님.
경로 base=codex/bl5-recovery-limiter-dispatch - CURRENT dev 직머지 불가. #4745#4756#4758#4760→#4771이 먼저 랜딩해야 하며 tip CI가 그 게이트.
경로 account-change 케이스 PENDING (#4710) - file_id 비휴대 + scrub 미제거는 지금 동작으로 고정했고, dispatch 전 거절 단언은 의도적으로 비움. #4710은 아직 OPEN. bl6 브랜치는 있으나 이 PR에 묶이지 않음.
CI/merge - MERGEABLE UNSTABLE, tip 94db101에 Cross-platform CI pending( [skip ci] 없음). 로컬 suite/typecheck/build는 돌리지 말 것(본문·레인 정책과 동일).
스택 분리 - #4764/#4765(cx 문서·org quota)와 파일 겹침 없음. 같은 에픽 #4546의 다른 줄기. 중복 close 대상 아님.

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

너의 추천
유지. 부모 스택(#4745#4756#4758#4760#4771)이 랜딩하고 tip Cross-platform CI가 그린 다음에 머지. 머지 후 #4546은 아직 OPEN으로 두고, #4710 거절이 들어오면 이 테스트에 거절 단언만 추가. close-don't-rebase·중복 close 해당 없음. 라벨은 변경하지 않음.

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

@lidge-jun

Copy link
Copy Markdown
Owner Author

리뷰 · 우선순위 77 / 80

이 PR(#4772)은 #4546 증폭 사고를 “다섯 개 개별 수정”이 아니라 한 시스템으로 고정하는 tip 회귀 테스트다. 요청 실행 예산(createRequestExecutionBudget), 내구 소비 원장(createSpendReservationLedger)과 요청 스코프 호출자(createRequestSpendTracker), 풀 복구 리미터(createPoolBackpressureLimiter/resolveHeldAccountDispatch)를 실제 생산 모듈로 조립해서, 물리 전송 수·예산 소모·원장 예약/정산·클라이언트 거절이 같은 사건을 말하는지 본다. 보내기 횟수만 세는 픽스처는 사고 내내 통과했을 수 있다는 점이 핵심이다. 지금 dev HEAD(3070d64d8, package 2.57.0)에는 이 통합 파일이 아직 없다. 신규 tests/responses/responses-4546-incident-regression.test.ts(+222)와 layout 등록 두 줄만 건드린다.

시나리오는 사고 축을 그대로 옮겼다. (1) 같은 요청이 여러 층에서 복구를 시도해도 천장에서 멈추고, repair가 공유 reserve를 쓰면 account-failover는 final-recovery-spent로 거절된다. (2) 같은 held 계정에 동시 요청이면 probe 하나·withheld 하나이며, withheld의 retryAt은 미래다(이 어서트는 바로 아래 레이어 #4771의 nextRecoveryAt에 의존). (3) 이미 동작하는 detour가 있으면 실패 계정에 두 번째 probe를 안 쓴다. (4) fan-out 자식은 부모 예산을 쓰며 두 번째 타깃 전환이 거절된다. (5) 재시작 후 저널 재생이 천장/정산을 리셋·이중정산하지 않는다. (6) 계정 변경 scrub은 continuation은 버리고 file_id는 남긴다 — #4710 사전거절은 PENDING CONTRACT로 명시. (7) 디스패치 전 거절은 send/spend를 쓰지 않는다. layout.json과 test-layout-expected.json에 파일명을 등록했다.

스택 tip이다. base는 codex/bl5-recovery-limiter-dispatch(#4771)이고, 레인은 #4745#4756#4758#4760#4771→이 PR(bl7, 헤드 94db101206f8). MERGEABLE/UNSTABLE. 본문이 tip CI 게이트라고 명시하므로 DEV-STACK-08상 이 브랜치에서 전체 CI가 돌아야 한다. 프로덕션 런타임 코드는 안 바꾸고 테스트·레이아웃만이라 types/config 분할에 무효화되지 않는다. epic #4546은 여전히 OPEN인 채 “층이 서로 맞는지”만 잠근다.

파일 tests/responses/responses-4546-incident-regression.test.ts - 생산 프리미티브를 조립한 시스템 회귀. 사고 서사와 어서트가 잘 맞는다
케이스 ceiling/recovery intersection - repair 후 account-failover 거절 + ledger reserved=4*500이 예산 used와 같이 움직이는지 본다
케이스 concurrent held probe - #4771 없이는 withheld retryAt>now 어서트가 깨질 수 있다. tip이 bl5에 쌓인 이유가 코드에 드러난다
케이스 restart journal replay - settle 후 새 ledger 재생·동일 send id 재settle false. 이중정산 방지 계약이 분명하다
케이스 account-change file_id + PENDING #4710 - scrub이 파일을 지우지 않음을 지금 고정하고, 사전거절은 후속 이슈에 맡긴다. 주석이 정직하다
layout 등록 두 파일 - 새 테스트가 responses 버킷에 들어가도록 레이아웃 계약을 맞췄다. 누락이면 tip CI hygiene에서 걸릴 자리

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

너의 추천
KEEP as tip gate. #4546 레인을 한 시스템으로 고정하는 가치가 크고, 런타임 변경 없이 테스트만이라 리스크가 낮다. 부모 bl 스택(특히 #4771의 future retryAt)과 함께 tip CI 그린 확인 후 머지. #4546/#4701/#4710은 이 PR만으로 닫지 말 것. close-don't-rebase 해당 없음.

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

@github-actions github-actions Bot added the chore Maintenance, CI, tests, refactors, or build changes (not a user-facing bug or feature). label Sep 16, 2026
@github-actions

Copy link
Copy Markdown
Contributor

Deterministic PR hygiene checks passed.

…fusing sends (#4546)

Four fixes, batched into one push so the queue only pays once.

1. src/server/responses/core.ts was 214 lines against a 210-line cap in
   tests/fixtures/file-size-baseline.json. The spend-observer wiring added four
   lines of comment and continuation. The comment is now one line and the
   expression one line, and the file is back at its cap. The ratchet only ever
   lowers caps, so growing past one is a hard failure rather than a nudge.

2. The spend tracker refused a dispatch on ANY ledger denial. Only an operator's
   configured ceiling should: capacity, durability and a journal this process
   could not prove complete all mean the ledger cannot ACCOUNT for the send,
   which is not a reason to refuse one. An unconfigured install keeps the count
   caps it already had and is not newly refused, and a degraded ledger must not
   become an outage.

3. The shared ledger is now resolved on the first charge rather than when the
   request is built. It opens a journal under the OpenCodex home, and a request
   that never dispatches has no business creating one; this also means the home
   in effect at dispatch is the one written to, instead of whichever home was
   current when the first request of the process happened to be constructed.

4. Three assertions in the new tests claimed states the code never reaches.
   The concurrent-probe case asserted a limiter refusal, but the second caller
   short-circuits on the lease before it reaches the limiter and costs no
   allowance; the shared bound is now proved by asking the limiter directly.
   The exhausted-ceiling case asserted final-recovery-spent where the total
   ceiling refuses first, so it asserts total-exhausted and checks reserveSpent
   separately for the point it was making. The unstructured-error control
   asserted an exact 502 where the property that matters is that the identity is
   gone, so it asserts that instead.

Tests are not typechecked -- tsconfig includes only src -- so a test that
asserts the opposite of what it claims passes silently. These were found by
reading, not by running.
Two source-of-truth failures from the previous tip run, both mine.

tests/lib/transient-budget-scope-source.test.ts pinned the exact core.ts line
that mints the request's send budget, and bl2 changed it to install the spend
observer. The oracle now matches the new shape and additionally asserts the
observer is attached at the same place, which is the property that actually
matters: a combo child inherits the parent's holder and must not open a second
set of ledger entries for the same physical sends.

tests/lib/spend-reservation-ledger.test.ts caught a real defect in the replay
reconciliation, not a stale expectation. An exhausted scope must still be
exhausted after a restart -- that is the whole reason the ledger is on disk --
and abandoning a replayed undispatched reservation handed its tokens back and
reset the ceiling.

The distinction I drew was wrong. "Open" does not prove nothing was sent: the
torn-tail rule immediately above says the journal may be missing its last
record, so a send can dispatch and die before its dispatch record lands.
Both live states now resolve to unresolved spend, which is the conservative
answer and the one that preserves the ceiling.

The bl2 wiring test asserted the old split and is updated to the new figures,
along with the structure contract and the tracker's own comment.
@lidge-jun

Copy link
Copy Markdown
Owner Author

Landing the send-budget and durable-accounting lane, top layer first. This layer pins the #4546 incident scenarios as an integration regression.

Evidence at the verified tip d9e5b28 (tree b69974e3f2c5da4e1cda8e302943a5ea6b107474), from run 35055864527:

  • test 1/4, 2/4, 3/4 and 4/4 all completed with conclusion success, confirmed through the check-runs API rather than the check rollup, so the heavy jobs actually executed and were not path-filtered.
  • macos 1/2 and 2/2, gates and the aggregate ci check all completed with conclusion success.
  • The same commit also carries a ci failure and a gates cancellation from run 35055864360. Its annotation reads needed job(s) did not pass: changes=cancelled: that run was superseded by workflow concurrency when the six branches were pushed together. It is a cancellation, not a test failure.
  • The lane absorbed dev at 5e3029e from the bottom layer upward, so each pull request keeps its own layer diff (8 / 11 / 8 / 51 / 2 / 10 files) and the tip stays reviewable. Four merge conflicts were resolved by keeping both sides: the documentation sections that dev and this lane each added, and all four test imports. No test was deleted and no dev-only content was lost.
  • git merge-tree --write-tree origin/dev <tip> reports a clean merge.
  • Ancestry verified so each layer closes as MERGED: bl1, bl2, bl3, bl4 and bl5 are all 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: a maintainer with maintain or admin access may integrate into dev without a second maintainer approval, recording the decision and exact-head CI evidence.

@lidge-jun
lidge-jun merged commit ba16750 into codex/bl5-recovery-limiter-dispatch Sep 16, 2026
28 of 44 checks passed
@lidge-jun
lidge-jun deleted the codex/bl7-4546-integration-regression branch September 16, 2026 04:48
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

chore Maintenance, CI, tests, refactors, or build changes (not a user-facing bug or feature).

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant