Skip to content

refactor(oauth): move in-flight login state into its own module - #5220

Merged
lidge-jun merged 3 commits into
devfrom
carry/oauth-index-split
Sep 19, 2026
Merged

lidge-jun merged 3 commits into
devfrom
carry/oauth-index-split

Conversation

@lidge-jun

@lidge-jun lidge-jun commented Sep 19, 2026 •

Copy link
Copy Markdown
Owner

Summary

src/oauth/index.ts reached 2009 lines after #5090 and is not in the file-size baseline, so it tripped NEW_OVERSIZED at the 2000-line threshold. That fails the gate for every branch whose merge ref includes current dev, not just for OAuth work — it was reported by the contract-campaign task hitting it on its own merge ref.

Raising the cap is not available. updateBaseline only lowers a cap and tests/ci-workflows/file-size-ratchet.test.ts asserts that. Trimming the comments #5090 added — the ones explaining why a raw paste carrying an explicit code#state suffix is state-bearing and must be validated rather than exempt — would have fit the line budget and is the wrong trade.

src/oauth/login-flow-state.ts takes the bookkeeping for a login that has started and not yet settled: the per-provider state, abort and manual-paste maps, the slot helpers, reconcileOAuthFlowState, and submitManualLoginCode. The cut follows a seam rather than a line count — nothing moved reads or writes a stored credential, and nothing left behind touches an in-flight slot. index.ts re-exports reconcileOAuthFlowState and submitManualLoginCode, so the six management route modules and the Codex login flow that import them are unchanged. Three imports that the move left unused were dropped.

The raw code#state validation from #5090 moves with submitManualLoginCode unchanged, comments included. index.ts ends at 1894 lines.

Verification

  • Local checks: NOT RUN. This lane is prohibited from running local suites, focused tests, typecheck, builds, installs or any live ocx command, so no local result is claimed. Exact-head hosted CI is the executable evidence.
  • Static review instead: confirmed the moved group's only external dependencies are parseCallbackInput, retainedUtf8Bytes and the GenerationContext type, so the new module imports them directly and the dependency direction stays one-way with no cycle; confirmed loginManual has no remaining reference in index.ts; confirmed every external importer of submitManualLoginCode resolves through the re-export; confirmed the three pruned imports had no other use in the file.

Checklist

  • Scope stays focused and avoids unrelated cleanup.
  • Docs or release notes were updated when needed. Not needed: this moves code between modules with no user-facing surface change.
  • Security-sensitive changes were reviewed for secrets, auth, and unsafe defaults. This touches OAuth login-flow state, so it was reviewed specifically for that: no credential is read or written by the moved code, the state validation added in fix(oauth): validate raw paste code#state suffix against expected state #5090 is carried byte-for-byte, and the module boundary does not widen who can reach submitManualLoginCode.

Summary by CodeRabbit

  • Bug Fixes
    • Improved manual login code handling during OAuth sign-in.
    • Invalid, empty, oversized, or incomplete authorization responses are now rejected with clearer validation.
    • Login responses are checked against the expected sign-in state to prevent mismatched submissions.
    • Pending login submissions are handled more reliably when sign-in flows complete or are canceled.
  • Refactor
    • Reorganized OAuth sign-in flow handling while preserving the existing sign-in experience.

src/oauth/index.ts reached 2009 lines after #5090 and is not in the
file-size baseline, so it tripped NEW_OVERSIZED at the 2000-line
threshold and failed the gate for every branch whose merge ref includes
current dev.

Raising the cap is not available: updateBaseline only lowers, and
tests/ci-workflows/file-size-ratchet.test.ts asserts it. Trimming the
comments that #5090 added to explain why a raw paste carrying an
explicit state suffix is state-bearing would have fit the budget and is
the wrong trade.

src/oauth/login-flow-state.ts takes the bookkeeping for a login that has
started and not yet settled: the per-provider state, abort and manual
paste maps, the slot helpers, the flow-state reconciliation, and
submitManualLoginCode. The cut follows a seam rather than a line count.
Nothing moved reads or writes a stored credential, and nothing left
behind touches an in-flight slot. index.ts re-exports
reconcileOAuthFlowState and submitManualLoginCode, so the management
routes and the Codex login flow that import them are unchanged.

The raw code#state validation from #5090 moves with submitManualLoginCode
unchanged, comments included.

index.ts ends at 1894 lines.

No local suite, focused test, typecheck or build was run. Exact-head
hosted CI is the execution evidence.
@lidge-jun
lidge-jun requested a review from Ingwannu as a code owner September 19, 2026 21:16
@github-actions github-actions Bot added the intake: hygiene-blocked Deterministic PR hygiene checks failed label Sep 19, 2026
@github-actions

Copy link
Copy Markdown
Contributor

⚠️ Deterministic hygiene checks failed.

  • missing_regression_test — Behavior changed under src/ or gui/src/ without a test change. Add focused coverage or obtain test-exception-approved.

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 19, 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-19T21:19:55.689409Z 30859d7 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 chore Maintenance, CI, tests, refactors, or build changes (not a user-facing bug or feature). label Sep 19, 2026
@coderabbitai

coderabbitai Bot commented Sep 19, 2026 •

Copy link
Copy Markdown
Contributor

Review Change StackReview 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: 6b45105f-4bbd-4aae-8ac8-3556788a2cb8

📥 Commits

Reviewing files that changed from the base of the PR and between 7b953e9 and bbc8252.

📒 Files selected for processing (2)
  • scripts/test-layout/layout.json
  • tests/fixtures/test-layout-expected.json

Included review availability: Your plan provides up to 10 included reviews per hour; 4 remain after this review.


📝 Walkthrough

Walkthrough

The OAuth login flow state machinery moved to src/oauth/login-flow-state.ts. src/oauth/index.ts now imports and re-exports the required functions. OpenAI image formatting and test-layout mappings also changed.

Changes

OAuth login flow state

Layer / File(s) Summary
Login flow state module
src/oauth/login-flow-state.ts
Defines OAuth state maps, manual code slots, generation reconciliation, code waiting, slot management, and validated code submission.
OAuth index integration
src/oauth/index.ts
Uses the extracted helpers, re-exports reconcileOAuthFlowState and submitManualLoginCode, and removes the former local machinery and unused imports.

OpenAI image options

Layer / File(s) Summary
Image options formatting
src/adapters/openai-chat.ts
Uses a local imageOptions object for image normalization. Existing request options and handlers remain unchanged.

Test layout mapping

Layer / File(s) Summary
Client runtime test mapping
scripts/test-layout/layout.json, tests/fixtures/test-layout-expected.json
Adds the client-runtime.test.ts to clients mapping in the configuration and expected fixture.

Priority: ⬇️ Low

Estimated code review effort: 3 (Moderate) | ~20 minutes

Change: Refactor

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 33.33% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 6 functions across 3 files. (2 skipped: 2… Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately and concisely describes the primary change: moving OAuth in-flight login state from src/oauth/index.ts into the new src/oauth/login-flow-state.ts module.
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.
Full details: Docstring Coverage

Explanation

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

  • Fix all pre-merge checks with AI
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Commit to this branch
  • Create a new PR

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

github-actions Bot commented Sep 19, 2026 •

Copy link
Copy Markdown
Contributor

✅ READY

  • all PR quality gates passed.

Hygiene

✅ Deterministic PR hygiene checks passed.

@github-actions
github-actions Bot marked this pull request as draft September 19, 2026 21:17

@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: 30859d7d30

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

Comment on lines +8 to +10
* Split out of `index.ts` when that file crossed the repository file-size ratchet at 2009
* lines. The cut follows a seam rather than a line count: everything here is bookkeeping for
* a login that has started and not yet settled, and none of it reads or writes a stored

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 the owned OAuth architecture documents

This creates a new owner for in-flight OAuth login state, but none of the four architecture documents mapped to src/oauth/ (structure/runtime.md, structure/transports/inventory.md, structure/providers-and-adapters.md, and structure/providers/xai-grok.md) is updated. Add the new module boundary to the relevant sections of every mapped document so the maintained architecture reference continues to identify where this security-sensitive state lives.

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

Useful? React with 👍 / 👎.

@lidge-jun

Copy link
Copy Markdown
Owner Author

리뷰 · 우선순위 62 / 80

로그인을 시작해서 아직 끝나기 전에 들고 있는 상태를 src/oauth/index.ts에서 빼, src/oauth/login-flow-state.ts로 옮긴 PR입니다.

index.ts는 #5090 뒤에 2009줄이 됐습니다. 이 파일은 줄 수 기준표에 없습니다. 기준표에 없는 파일이 2000줄 이상이면 NEW_OVERSIZED로 게이트가 실패합니다. 그 실패는 OAuth를 고치는 브랜치만이 아니라, 지금 dev를 머지 기준으로 삼는 브랜치마다 붙습니다. updateBaseline은 Math.min으로 상한을 내리기만 합니다. 올리기는 없습니다. #5090이 붙인 설명, 붙여넣기에 코드#표시가 있으면 그 표시를 검사해야 한다는 문장을 지워서 줄 수를 맞추는 쪽은 하지 않았습니다.

옮긴 것은 공급자별 로그인 상태, 중단용 AbortController, Kiro가 마무리 중인지 표시하는 집합, 손으로 붙인 코드 칸, 칸을 비우고 만드는 함수, 끝난 로그인을 치우는 reconcileOAuthFlowState, 붙여넣기를 받는 submitManualLoginCode입니다. 저장된 자격 증명을 읽거나 쓰는 코드는 안 움직였습니다. index.ts는 바깥에서 쓰던 두 이름만 다시 내보냅니다. 그래서 src/oauth에서 submitManualLoginCode를 가져오던 관리 경로와 Codex 로그인은 고칠 곳이 없습니다. 헤드의 index.ts는 1894줄입니다. 새 파일은 127줄입니다. 베이스는 dev입니다. 같은 분리를 하는 다른 열린 PR은 없습니다. types.ts와 config.ts는 안 바꿉니다.

함수 본문은 잘라 붙인 것과 같습니다. 새 파일은 parseCallbackInput, retainedUtf8Bytes, GenerationContext 타입만 가져옵니다. callback-server.ts는 index.ts를 가져오지 않아서 두 파일이 서로를 부르지 않습니다. 기존 테스트 tests/oauth/oauth-manual-code.test.ts는 src/oauth 배럴에서 submitManualLoginCode를 가져옵니다. 다시 내보내기만 있으면 그 테스트는 같은 함수를 봅니다. 이 리뷰는 테스트를 다시 실행하지 않았습니다.

src/oauth/index.ts 54줄 - ensureManualCodeSlot을 가져오지만 이 파일에서는 한 번도 안 씁니다. 그 함수는 새 파일 안에서만 씁니다. 밖으로 내보낼 이유도 없습니다. tsconfig.json에는 noUnusedLocals가 없어서, 타입 검사가 이 import를 실패로 보지 않습니다.

src/oauth/login-flow-state.ts submitManualLoginCode - 함수 위 설명은 잘못된 붙여넣기를 여기서 받아 두고, 콜백 루프가 다시 묻는다고 적혀 있습니다. 코드는 코드가 없거나 표시가 다르면 바로 { ok: false }를 돌려줍니다. 이 문장은 옮기기 전부터 코드와 달랐고, 이번 PR이 동작을 바꾼 것은 아닙니다.

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

위생 검사가 missing_regression_test로 이 PR을 드래프트로 붙잡아 두었습니다. src/가 바뀌었는데 테스트 diff가 없기 때문입니다. 동작은 안 바뀌었습니다. 같은 함수를 보는 테스트는 이미 있고, import는 배럴을 탑니다. 새 테스트를 지어 넣을지, test-exception-approved로 이 관문을 넘길지는 메인테이너 몫입니다.

예전에는 index.ts 안에만 있던 loginState, loginAbort, kiroLoginSettling이 새 파일에서 export 됩니다. src/oauth 배럴은 이 지도를 다시 내보내지 않습니다. src/oauth만 가져오는 코드는 예전처럼 지도를 못 만집니다. 새 파일을 직접 가져오면 진행 중인 로그인 지도를 바꿀 수 있습니다. 그 파일을 내부 모듈로 둘지는 메인테이너 몫입니다.

너의 추천

안 쓰는 ensureManualCodeSlot import를 지우세요. 그 함수와 ManualCodeSlot은 새 파일 밖으로 내보내지 마세요. 설명 한 줄은 잘못된 붙여넣기를 바로 거절한다고 고치면 코드와 맞습니다. 없어도 머지를 막을 이유는 아닙니다.

테스트는 새로 만들지 마세요. 동작이 같아서입니다. test-exception-approved로 드래프트를 푸는 쪽이 맞습니다. 상한을 올리지 마세요. #5090 설명을 줄 수 맞추려고 지우지 마세요. types.ts/config.ts 분할과 무관하니 이 PR을 닫지 마세요. 겹쳐서 닫을 PR도 없습니다. 이 분리가 들어가면 2000줄 게이트가 다른 브랜치를 막지 않습니다.

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

src/adapters/openai-chat.ts reached 823 lines against a committed cap of
822 after #5208, so the ratchet reports GREW and the gate fails for every
branch whose merge ref includes current dev.

The overage is one line out of a six-line addition, which does not justify
extracting a module. Naming the options object instead of spreading it
across three lines expresses the same call with the same arguments and
leaves the file at 821, one line under the cap. No comment was removed and
no behaviour changed: normalizeOpenAIChatImages receives the same
tierBias and abortSignal, and the rejection handler still rethrows on a
real abort and otherwise finishes.

No local suite, focused test, typecheck or build was run. Exact-head
hosted CI is the execution evidence.
@lidge-jun lidge-jun added the test-exception-approved Maintainer approved a non-automated regression-test exception label Sep 19, 2026
@github-actions github-actions Bot removed the test-exception-approved Maintainer approved a non-automated regression-test exception label Sep 19, 2026
@lidge-jun

Copy link
Copy Markdown
Owner Author

Two gate results on the first push needed answers rather than edits, so recording both here.

hygiene and enforce-target reported missing_regression_test: behaviour changed under src/ without a test change. That rule is right in general and does not fit this change. Every line moved between modules is byte-identical, index.ts re-exports both public names so no caller resolves differently, and the code#state validation added in #5090 travels with submitManualLoginCode unchanged including its comments. There is no behaviour here to write a regression against, and adding a test that asserts an import still resolves would be the dummy the gate exists to prevent. The repository's own escape for exactly this case is test-exception-approved, which I have applied as maintainer, with this comment as the record of why.

The second push answers a real finding. test 4/4 surfaced a second ratchet offender that was already on dev: src/adapters/openai-chat.ts at 823 lines against a cap of 822, from #5208. One line over, out of a six-line addition — not enough to justify extracting a module. Naming the options object instead of spreading it across three lines expresses the same call with the same arguments and leaves the file at 821. normalizeOpenAIChatImages still receives the same tierBias and abortSignal, and the rejection handler still rethrows on a real abort and otherwise finishes. No comment was removed.

Reported by the contract-campaign task, which hit both on its own merge ref. Neither was visible on this PR's first head because the OAuth offender was masking the shard.

@lidge-jun lidge-jun added the test-exception-approved Maintainer approved a non-automated regression-test exception label Sep 19, 2026
@github-actions github-actions Bot removed the intake: hygiene-blocked Deterministic PR hygiene checks failed label Sep 19, 2026
@github-actions
github-actions Bot marked this pull request as ready for review September 19, 2026 21:26
tests/clients/client-runtime.test.ts arrived with #5144 and was recorded
in neither scripts/test-layout/layout.json explicit nor
tests/fixtures/test-layout-expected.json, so the layout guards fail for
every branch whose merge ref includes current dev.

The file already sits in the domain directory its name implies, so the
registration is the whole fix: clients in both inventories, matching the
other tests/clients entries. No guard was relaxed and no seed regex was
widened to make the file resolve implicitly.

Reported by the contract-campaign task, which verified the omission by
static blob lookup on dev before raising it.

No local suite, focused test, typecheck or build was run. Exact-head
hosted CI is the execution evidence.
@github-actions github-actions Bot removed the test-exception-approved Maintainer approved a non-automated regression-test exception label Sep 19, 2026
@lidge-jun
lidge-jun merged commit 936e352 into dev Sep 19, 2026
33 checks passed
@lidge-jun
lidge-jun deleted the carry/oauth-index-split branch September 19, 2026 21:47
@lidge-jun

Copy link
Copy Markdown
Owner Author

추가 리뷰 · 우선순위 38 / 80

첫 리뷰 다음에 커밋이 두 개 더 왔습니다. 로그인 상태를 옮긴 부분은 그대로입니다. 새로 온 것은 줄 수 상한 하나, 테스트 목록 하나입니다. 둘 다 함수가 하는 일을 바꾸지 않습니다. 이 PR은 이미 dev에 머지됐습니다.

7b953e9는 src/adapters/openai-chat.ts입니다. #5208 뒤에 이 파일이 823줄이 됐습니다. 기준표 상한은 822줄입니다. 한 줄이라도 넘으면 GREW가 뜨고, 지금 dev를 머지 기준으로 쓰는 브랜치마다 게이트가 실패합니다. 파일을 새로 나누지는 않았습니다. 이미지를 줄일 때 넘기던 값을 세 줄로 적던 것을, imageOptions라는 변수 한 줄로 바꿨습니다. 지금 파일은 821줄입니다. 상한보다 한 줄 적습니다. normalizeOpenAIChatImages에 들어가는 tierBias와 abortSignal은 이전과 같습니다. 줄이기가 실패하면, 중단 신호가 있을 때만 그 에러를 다시 던지고, 아니면 요청을 그대로 끝냅니다. 그 줄도 안 바뀌었습니다.

bbc8252는 테스트가 어느 묶음에 속하는지 적는 목록입니다. tests/clients/client-runtime.test.ts는 #5144에서 들어왔습니다. scripts/test-layout/layout.json과 tests/fixtures/test-layout-expected.json에는 그 이름이 없었습니다. 그래서 목록 검사가 실패합니다. clients 묶음이 이름만 보고 집어넣는 규칙은 aside-profile, cline-client, desktop- 같은 앞부분만 봅니다. client-runtime.test.ts는 그 앞에 안 맞습니다. 그래서 목록에 직접 "clients"로 적는 것이 맞습니다. 두 파일 모두 그렇게 적혔습니다. 규칙을 넓히지는 않았습니다.

src/oauth/index.ts 54줄 - ensureManualCodeSlot은 아직 가져오기만 하고 이 파일에서는 안 씁니다. 첫 리뷰에서 말한 그대로입니다. 이번 두 커밋은 이 줄을 고치지 않았습니다.

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

새로 고를 것은 없습니다. missing_regression_test는 메인테이너가 test-exception-approved로 이미 넘겼습니다. 이미지 호출과 테스트 목록은 검사만 맞추는 수정입니다. 안 쓰는 import를 지금 지울지는 급하지 않습니다. tsconfig.json의 noUnusedLocals가 꺼져 있어서 타입 검사가 실패로 보지 않습니다.

너의 추천

이 헤드를 다시 열 필요는 없습니다. imageOptions와 client-runtime.test.ts 등록은 맞습니다. ensureManualCodeSlot import는 다음에 src/oauth/index.ts를 건드릴 때 같이 빼도 됩니다.

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

lidge-jun added a commit that referenced this pull request Sep 23, 2026
)

* fix(service): combine startup ownership, token binding, and slot retention

Carries #5512 by @luvs01 (head a12b2ad), which
consolidates #5477, #5306 and #5357:

- bind the service API token to its owning state, canonicalize qualified-localhost
  binds, and carry WSL ownership state honestly (#5477);
- take a fresh task listing for the second startup ownership decision (#5306);
- retain workflow slots for streaming turns (#5357);
- own server-auth fixture lifetime and project a current-schema config for it.

Squashed from the PR's own diff (origin/dev...a12b2ad) onto current dev.

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

* fix(server): self-heal a replaced package tree via drain-and-restart

Carries #5513 by @luvs01 (head 4d168f1), which
consolidates #5393 and its scheduler follow-up: detect a replaced installed package
tree, degrade health honestly, and drive a timer-driven, retryable drain-and-restart
whose verify step is deferred past scheduler re-entry. The guard factory lives in
src/server/index/package-tree-guard.ts.

Squashed from the PR's own diff (a12b2ad...4d168f1) onto the #5512 carry.

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

* fix(security): combine install discovery, credential, and transport hardening

Carries #5515 by @luvs01 (head 843f299), which
consolidates #5359, #5285 and #5322:

- keep selected Codex installation discovery off network filesystems, probe
  oversized wrappers through a held-handle prefix read, and stop a PATH scan at a
  refused probe (#5359);
- exclude npm candidates inside the launch directory subtree (#5285);
- refuse plaintext remote hub origins, fail closed on POSIX chmod for credential
  files, and skip the frame-log write when descriptor hardening fails (#5322).

Squashed from the PR's own diff (origin/dev...843f299) onto the chain carry.
Integration: structure/runtime.md wording reflowed by two lines so the combined
service and security stacks stay within the 600-line structure budget.

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

* fix(security): combine management-auth and boundary hardening

Carries #5516 by @luvs01 (head 245d542), which
consolidates #5326, #5312, #5363 and #5317:

- harden pairing redemption, agent roster intake, and SOCKS5 decoding (#5326);
- guard gh resolution, anchor the grok managed-region fences to whole lines, and
  bound provider-controlled text (#5312);
- harden management-auth admission and provenance (#5363);
- bound the /healthz version before it reaches diagnostics (#5317).

Squashed from the PR's own diff (843f299...245d542) onto the #5515 carry.
Integration: both stacks rewrote the shared server-auth test fixtures. The carry
keeps the #5512 current-schema fixture projection and config helper (including
its 4 KiB boundary case) and adds this PR's Aside sync capability assertions.

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

* fix(security): combine adapter argv and upstream-body hardening

Carries #5517 by @luvs01 (head 260a87b), which
consolidates #5315 and #5336:

- stage Qoder and CodeBuddy system prompts in private files instead of
  child-process argv, with exclusive creation and owned cleanup (#5315);
- bound upstream error bodies and resolve account-scoped transports (Copilot,
  Devin) from the same OAuth snapshot as the bearer (#5336).

Squashed from the PR's own diff (245d542...260a87b) onto the #5516 carry.

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

* feat(codebuddy): integrate capture-only tools with private prompt staging

Carries #5582 by @luvs01 (head 3061ef9), which
integrates the capture-only CodeBuddy tool bridge from #5148 by @mdwsk88 with the
private prompt staging from #5517. Requests with a tool catalog advertise only the
allowed tools through an isolated MCP server that captures calls without executing
them; the client keeps approval, sandboxing and execution. Pre-init, undeclared,
excessive or incomplete calls are rejected, streamed malformed tool arguments are
suppressed, bridge staging failures return a fixed message, and an opt-in live
acceptance harness is included. Design context: #5146.

Squashed from the PR's own diff (260a87b...3061ef9) onto the #5517 carry.

Co-authored-by: mdwsk88 <924038395@qq.com>

* fix(client): bound total hub catalog response lifetime

Carries #5252 by @luvs01 (head 779ef91): give the
hub catalog body read an overall deadline (24x the inactivity window, capped at
120 s) on top of the inactivity window, and release refused, HTTP-error and 304
bodies without awaiting their cancellation.

Squashed from the PR's own diff (origin/dev...779ef91).

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

* fix(grok): reserve model aliases only when the written config stays valid

Reimplements #5281 by @luvs01. A user sub-table such as [model.ocx-mine.extra]
only creates an implicit parent, so it no longer forces the generated table to a
suffixed alias. The alias choice is now checked against the bytes actually
written: the unsuffixed alias is used only when the final config (after
model-reference rewriting) parses; otherwise the conservative choice that also
reserves deeper headers is used, and a valid user file for which neither choice
parses is refused without writing. Malformed user TOML keeps the previous
conservative reservation.

The original change reserved only exact two-segment headers, which could emit a
duplicate [model.x] table when the user defines model.x through dotted keys.

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

* fix(codex-auth): scope Codex OAuth cancellation to the originating flow

Reimplements #4923 by @luvs01 on the current login-state layout (in-flight
controllers moved to src/oauth/login-flow-state.ts in #5220). Cancelling a Codex
login was keyed only by provider, so a stale modal posting an old flowId could
abort a newer attempt, and a cancel without a flowId expired every pending flow.

- Each in-flight controller records the flowId that started it; a cancel whose
  flowId does not match the active attempt is refused before anything aborts.
- POST /api/codex-auth/login/cancel requires a non-empty flowId, rejects unknown
  or non-pending flows with 400 without touching any row, and expires only that
  flow. Provider-wide cancellation through /api/oauth/login/cancel is unchanged.
- ocx account cancel requires --flow for Codex providers and sends no request
  without it.

The dashboard's 409 recovery keeps its code; its ownerless cancel is now refused,
so it ends in the existing "already in progress" message instead of superseding a
flow it does not own.

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

* fix(socks5): bound compressed event streams by expansion, not total size

Review follow-up to the #5516 carry. The 32 MiB decoded-body cap applied to every
gzip/deflate response, so a long, normally compressed SSE stream through the
SOCKS5 tunnel was cut once its cumulative output crossed the cap. Buffered
responses keep the absolute cap; event streams may continue while decoded bytes
stay within the greater of 32 MiB or 128x the coded bytes consumed, which still
stops high-ratio bombs.

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

* fix(codex): keep scanning PATH past a missing Windows candidate

Review follow-up to the #5515 carry. The held-handle reader reported a missing
file or directory as open-refused, so the default existence probe stopped the
PATH scan at the first absent PATHEXT candidate (for example codex.com) before it
reached an installed codex.cmd. NtCreateFile's object-name-not-found and
object-path-not-found statuses now map to a distinct not-found result that lets
the scan continue; every other failure still refuses.

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

* fix(server): require Windows ACL hardening before a frame-log append

Review follow-up to the #5515 carry. On Windows the frame log ignored a failed
permission change and appended anyway. Each append now hardens the target with
the required Windows ACL helper and checks that the path still names the opened
file before writing; any failure writes nothing.

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

* fix(devin): bind catalog authority to the tenant destination

Review follow-up to the #5517 carry.

- The observe-only OAuth snapshot applied the Copilot-validated apiBaseUrl to
  every provider, so a crafted Devin credential could carry a Copilot host that
  the snapshot claimed as its own. The overlay now applies only to github-copilot.
- Devin's live roster, stale fallback and cooldown were keyed by the token alone
  while discovery also depends on the validated tenant URL. The catalog authority
  and the matching routing-cache resolver now fingerprint the token together with
  the validated destination URL.

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

* fix(codebuddy): fail closed on unverified bridge turns and staging collisions

Review follow-up to the #5582 carry.

- With the capture-only tool bridge armed, a successful terminal event is no
  longer accepted unless the CLI's system/init frame confirmed the bridge server;
  a turn that ends without it fails with tool_bridge_init_missing.
- A tool_use block that arrives only in the complete assistant message, without
  the partial tool events the bridge captures, now fails the turn instead of
  being dropped silently; partial captures are deduplicated by id.
- The catalog and MCP config staging files are created exclusively (wx, 0600),
  like the prompt file, so a pre-existing file fails before spawn.
- The history-argument repair for a missing JSON object prefix is documented and
  tested as a provider-agnostic contract; other malformed strings keep {}.

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

* fix(service): keep service-command ownership bound to the recorded home

Review follow-up to the #5512 carry. On WSL with CODEX_HOME unset, the carried
allowance treated a legacy Linux ~/.codex install record as owned when discovery
now selects the Windows profile, so service stop could stop the Linux-home
service and then restore native Codex in the Windows home, and repair could
rewrite the recorded home. Service commands again require the exact recorded
home and name it in the refusal; the unattended startup inspector reaches the
same foreign verdict.

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

* fix(server): veto a package-tree restart when its server stops or loses ownership

Review follow-up to the #5513 carry.

- A package-tree restart accepted by the guard stayed scheduled after an explicit
  server.stop(), so the drain-and-respawn could reopen a server the caller had
  stopped. The caller that accepted a pending restart now receives a veto, and
  the guard uses it on dispose.
- When running as a supervised service child, the automatic path checks service
  home ownership when accepting and again before the handoff; a mismatch keeps
  the 503 fence and skips the restart.

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

* fix(security): resolve gh from fixed paths and look up pairing grants by digest

Review follow-ups to the #5516 carry.

- On Windows the automatically polled star-status route derived gh.exe roots from
  ProgramFiles and LOCALAPPDATA, so a process environment could select any
  absolute directory. Windows candidates are now the fixed system install paths,
  and the child PATH is only the resolved executable's directory. Other installs
  report gh as unavailable, which only hides the sidebar star state.
- Pairing redemption looked each guess up by scanning every live grant; the map
  is keyed by the grant digest, so the lookup is now a direct get. A valid grant
  still redeems behind a throttled source.

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

* test(server): cover the one-shot Aside sync capability end to end

Review follow-up to the #5516 carry, which added a one-shot, HMAC-bound
capability for the default ocx sync path without exercising it. A real listener
now proves single use, refusal on replay, wrong path, query, method, pid or port,
expiry and a bad MAC, and that the CLI default path performs the attestation and
a bodyless POST (through a narrow transport seam).

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

* test: register the review follow-up test files in the layout maps

Adds the three new test files from the L4 review follow-ups to both
scripts/test-layout/layout.json and tests/fixtures/test-layout-expected.json.

* test(grok): pin re-injection and strip for a nested user model table

Review follow-up to the #5281 reimplementation: two injections are byte
identical, every intermediate file parses, and strip restores the exact user
content.

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

* fix(server): harden a Windows frame log once per file identity

Re-review follow-up: requiring Windows ACL hardening on every append spawned
icacls for every relayed frame and could stall the realtime relay. The hardened
file identity (device and inode) is now remembered for the log path; an
unchanged file skips the respawn, and a replaced file at the same path is
hardened again before any write.

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

* docs(structure): describe the package-tree restart veto and ownership recheck

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

* fix(server): stop an automatic restart from handing off after an explicit shutdown

Security review follow-up to the #5513 carry. Once an automatic package-tree
restart entered its drain, an operator shutdown (signal or management stop)
could still be followed by the restart handoff, because the drain cannot tell
its own listener stop from an independent one. Explicit shutdown paths now mark
the process, and an admission-bound restart checks that mark before every
handoff step. Manually requested restarts keep their behavior.

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

* fix(server): mark a management stop before its asynchronous teardown

Security re-review follow-up: the management stop route marked the explicit
shutdown only after awaiting the shared teardown, so an automatic restart
draining concurrently could reach its handoff in that window. The mark now
precedes the first await after the stop is accepted.

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

* test(server): allow post-lookup pruning in the pairing digest regression

The digest-lookup regression trapped every iteration of the grant map, so a
valid redemption failed once session minting pruned expired grants after the
lookup (hosted CI test 4/4). The trap now fails only on a scan that precedes the
digest lookup, which is the regression it guards.

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

* fix: repair standalone bridge and restart ownership

Use the compiled CLI as the capture-only MCP entrypoint, release automatic restart fences on veto, align Devin discovery, and tighten Windows and local transport handling. Apply the documented Qoder prompt environment for both regions and update focused regressions and operator docs.

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

Co-authored-by: mdwsk88 <924038395@qq.com>

---------

Co-authored-by: luvs01 <27862058+luvs01@users.noreply.github.com>
Co-authored-by: mdwsk88 <924038395@qq.com>
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