Skip to content

fix(oauth): rebase startup reconciliation on the persisted config (reimplementation of #3524) - #3564

Merged
lidge-jun merged 2 commits into
devfrom
codex/260905-startup-reconcile-persistence
Sep 5, 2026
Merged

fix(oauth): rebase startup reconciliation on the persisted config (reimplementation of #3524)#3564
lidge-jun merged 2 commits into
devfrom
codex/260905-startup-reconcile-persistence

Conversation

@lidge-jun

Copy link
Copy Markdown
Owner

Summary

Startup OAuth provider reconciliation and the model-rename startup migration both wrote their in-memory projection over the on-disk config, silently discarding a concurrent operator edit (src/oauth/index.ts:1284 on dev). Both now commit through mutatePersistedConfig and adopt the persisted result key by key (recursively, so sibling provider rows keep identity). #3524's version added an unguarded throw when persistence was unavailable, which turned a survivable state into a boot failure; this reimplementation warns and adopts instead, and a startup resilience test proves startServer survives a config removed between load and reconcile.

Carries #3524 (author @yansigit) onto current dev; reimplemented per the campaign plan. Supersedes #3524.

Security boundary: touches src/oauth/index.ts (restricted surface) — persistence ordering only, no credential or flow change.

Stack (merge bottom-up; each layer targets the branch below):

# Layer Branch Base
1 B1 codex/260905-oauth-failover-policy-boundaries dev
2 B2 codex/260905-kiro-continuation-auth-context B1
3 B3 codex/260905-claude-native-fallback B2
4 B4 codex/260905-startup-reconcile-persistence B3
5 B6 codex/260905-combo-failure-classification B4

Depends on the layers below it. Review this PR's diff only. Unit: devlog/_plan/260905_open_work_closeout/ (020, 021, 022).

Verification

  • bun run typecheck — exit 0 on this layer and on the stack top (d0f80e85f).
  • RED on dev with the source stashed: tests/oauth/oauth-provider-reconcile.test.ts 11 pass / 4 fail and tests/providers/model-rename-migration.test.ts 13 pass / 3 fail (concurrent-edit rebase — the RED-on-dev defect proof). tests/server/server-startup-reconcile-resilience.test.ts is RED against fix(oauth): persist startup reconciliation before adoption #3524's head only (dev never throws) and skips locally when Bun.serve cannot bind (hosted CI runs it). GREEN 52 pass / 0 fail unsandboxed; bun run test:changed 10747 pass / 0 fail unsandboxed. Layout guard 17/0.
  • Stack top: 214 pass / 0 fail across all layers' focused files + layout guard + tests/lab/core-lab-boundary.test.ts.
  • Exact-head hosted CI is the merge gate (no repository-wide local suite by maintainer instruction).

Checklist

  • Targets codex/260905-claude-native-fallback (stack layer)
  • Focused regression test RED before / GREEN after
  • Original author credited via Co-authored-by trailer in the branch commit

Co-authored-by: yansigit 44089734+yansigit@users.noreply.github.com

@lidge-jun lidge-jun added the bug Something isn't working label Sep 5, 2026
@lidge-jun
lidge-jun requested a review from Ingwannu as a code owner September 5, 2026 00:50
@lidge-jun lidge-jun added the bug Something isn't working label Sep 5, 2026
@coderabbitai

coderabbitai Bot commented Sep 5, 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: Team

Run ID: 4d1c4e93-f081-496a-ba41-0acf34ababc2

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.

@github-actions

github-actions Bot commented Sep 5, 2026

Copy link
Copy Markdown
Contributor

Deterministic PR hygiene checks passed.

@lidge-jun

Copy link
Copy Markdown
Owner Author

리뷰 · 우선순위 68 / 80

이 PR은 부팅 경로에서 OAuth 프로바이더 재조정·모델 rename 마이그레이션이 메모리 투영을 디스크에 덮어써서 동시에 고친 운영자 설정을 지우던 문제를 고칩니다. 지금 devsrc/oauth/index.ts reconcileOAuthProviders는 끝나면 saveConfig(config)로 통째 저장합니다(대략 :1284). 이 PR은 projectOAuthProviderReconciliation(순수 투영) → mutatePersistedConfig로 디스크 스냅샷에 다시 투영 → adoptOAuthReconciliation건드린 provider 키만 라이브 객체에 입히는 순서로 바꿉니다. persistence가 없으면 #3524처럼 throw 하지 않고 warn + in-memory adopt로 부팅을 살립니다. src/providers/model-rename-startup.ts도 같은 패턴이고, adopt는 재귀 key-by-key라 sibling provider 참조 identity가 끊기지 않게 합니다. 테스트는 concurrent-edit rebase RED-on-dev 증명과 server-startup-reconcile-resilience를 추가했습니다. 스택 B4: base=codex/260905-claude-native-fallback(#3563). 아래층 없이 단독 dev 머지 금지. #3524는 이 재구현이 랜딩하면 supersede로 닫습니다.

src/oauth/index.ts adoptOAuthReconciliation - touchedProviders만 clone/delete하는 범위는 “라이브 참조 보존” 목표와 맞다. 다만 투영이 provider를 이름 변경/합치기 수준까지 가면 touched 집합 계산이 빠질 수 있으니, 지금 규칙(필드 복구·defaultModel heal·antigravity catalog) 안에서만 안전하다고 보면 된다.

src/oauth/index.ts withOAuthReconciliationTouchedKeys - 디스크 rebase가 “이미 맞다”고 본 키를 라이브 투영 touched와 합치는 이유는 주석대로다. 이 합집합이 빠지면 부팅 직후 라이브만 stale이 된다.

src/oauth/index.ts outcome.status === "unavailable" - throw 제거는 캠페인 요구와 맞고, 다른 mutatePersistedConfig 소비자와 톤이 같다. warn 문구에 reason만 넣고 시크릿/경로 내용을 안 넣는지도 유지하자.

src/providers/model-rename-startup.ts adoptRecord - JSON.stringify로 leaf 동등 비교는 테스트 가능한 단순함이다. 키 순서/undefined 차이로 불필요한 재할당이 날 수는 있으나 rename 규모에서는 수용 가능해 보인다.

tests/server/server-startup-reconcile-resilience.test.ts - Bun.serve bind 실패 시 skip은 로컬 샌드박스용. hosted CI에서 실제로 도는지 머지 전 확인.

#3524 - OPEN leftover. 랜딩 후 Landed via + landed-via-maintainer.

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

  • B1…B4를 순서대로 올릴지 스택 랜딩으로 한 번에 갈지
  • oauth 표면이라 보안 리뷰 범위를 “persistence ordering only”로 한정해도 되는지(본문 주장)
  • resilience 테스트 skip 조건이 CI에서 항상 실행되는지

너의 추천
#3563까지 dev에 들어간 뒤 이 층을 올린다. 단독 머지 금지. 랜딩 후 #3524 닫기. types/config 스플릿 무효화 대상 아님.

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

t and others added 2 commits September 5, 2026 10:24
`ocx claude` hard-errored and returned 1 whenever Claude routing was disabled
(`src/cli/claude.ts:420` on dev), so the command was unusable with the Claude
toggle off even though a native `claude` binary was available. Launch natively
in that case instead.

Only an explicit `false` triggers the fallback — from config, or reported live
by `GET /api/claude-code` — so a proxy predating the `enabled` field stays
routed, and an absent proxy still starts one rather than falling back.

The native session must not inherit proxy state, so it removes only values it
can prove OpenCodex owns: `ANTHROPIC_BASE_URL` when it targets this proxy's own
loopback host and configured port with a proxy-issued admission token, the
`CLAUDE_CODE_*` discovery and auto-context levers, and model slots that resolve
only through the proxy. An unrelated `http://localhost:8080` gateway and a user
`sk-ant-` credential are preserved. Client-ownership preflight runs before any
fallback, so an invalid or mismatched connected client still fails closed.

Three fixes on top of the contributor's head:

- Sync all eight `docs-site` `guides/claude-code.md` pages, which still
  described `ocx claude` as proxy-only.
- Distinguish an absent `settings.json` from a corrupt one in
  `readPickerDefaultModel`. Swallowing both alike dropped the "saved model
  requires the proxy" warning exactly when the file was broken; a corrupt file
  now warns and names the path without echoing contents.
- Restore the `#764 / SERVICE_STOP_LIVENESS` rationale comment above
  `ensureProxyForClaude`, which the diff deleted while keeping the behavior.

Carry of #3519.

Co-authored-by: everton-dgn <58889432+everton-dgn@users.noreply.github.com>
reconcileOAuthProviders mutated the in-memory config and called
saveConfig(config), so a startup snapshot overwrote any operator edit made
after loadConfig() returned. runModelRenameStartupMigration had the same
shape. Both now project onto a clone and commit through
mutatePersistedConfig, which rebases the write on the newest on-disk
snapshot, so a concurrent edit survives.

Persistence failure degrades rather than throws. Both functions run inside
startServer (src/server/index.ts:651 and :663), which is synchronous by
design and wraps neither call in try/catch, so a throw there takes the whole
proxy down over a config file the operator can still repair. A missing,
malformed or contended config now warns once and adopts the projection in
memory, matching every other mutatePersistedConfig consumer
(src/storage/policy.ts, src/codex/plan-from-token.ts,
src/server/management/agent-settings-routes.ts).

Adoption is key by key over the touched keys only. A clear-and-reassign
preserves the top-level object identity while silently detaching every
nested sub-object a caller still holds a reference to.

Tests: the concurrent-edit cases are the RED-on-dev proof of the defect
(they fail against unmodified dev, which clobbers). The degrade-not-throw
assertions are RED against #3524's head, which threw. The new
tests/server/server-startup-reconcile-resilience.test.ts covers the boot
path; its /healthz case binds a listener and is skipped where Bun.serve
cannot bind, so it is a hosted-CI-only assertion.

Co-authored-by: yansigit <44089734+yansigit@users.noreply.github.com>
@lidge-jun
lidge-jun force-pushed the codex/260905-claude-native-fallback branch from e9e9ebd to dc07467 Compare September 5, 2026 01:24
@lidge-jun
lidge-jun force-pushed the codex/260905-startup-reconcile-persistence branch from 589347f to 29182de Compare September 5, 2026 01:24
Base automatically changed from codex/260905-claude-native-fallback to dev September 5, 2026 01:58
@lidge-jun

Copy link
Copy Markdown
Owner Author

Maintainer admin merge (ruleset bypass recorded per MAINTAINERS.md): reimplementation of #3524, exact-head CI green on 29182de (24 pass / 2 skipped / 0 fail), stack layer above the merged #3563 (adcf8a7). Security-surface note: src/oauth/index.ts persistence ordering only — no credential, token, or flow change; reviewer 023 read mutatePersistedConfig in full and confirmed no partial write is possible. wp2 Stack B of the 260905 open-work closeout.

@lidge-jun
lidge-jun merged commit 526d4bf into dev Sep 5, 2026
30 checks passed
@lidge-jun
lidge-jun deleted the codex/260905-startup-reconcile-persistence branch September 5, 2026 01:59
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