Skip to content

fix(codex): guard the first read in the history injection preflight - #5007

Merged
lidge-jun merged 2 commits into
devfrom
codex/preflight-first-read-macos
Sep 18, 2026
Merged

lidge-jun merged 2 commits into
devfrom
codex/preflight-first-read-macos

Conversation

@lidge-jun

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

Copy link
Copy Markdown
Owner

Summary

  • The history injection preflight guarded the wrong operation, so the ocx sync refuses Codex injection with generic history_injection_preflight_unavailable when state_5.sqlite is a WAL store with no live writer (-shm absent) #4943 fix did not hold on macOS. sqlite3_open_v2 never reads page 1, so a store whose header says WAL is not inspected until the first prepare. On macOS that is where the absent -shm is raised — one frame above openCodexStateForPreflight, after the classification and the immutable fallback have already returned. The error reached the catch-all, so a user with a cleanly-closed WAL store still got history_injection_preflight_unavailable and ocx sync still had no way forward.
  • The failure is not a classification problem. macOS raises exactly the condition the narrowing was written for: SQLiteError, code: "SQLITE_CANTOPEN", errno: 14, unable to open database file, with neither sidecar on disk. isStateDbCantOpenError matches it. It simply never saw it, because the read happened in the caller.
  • The first read now happens inside the guarded attempt: openCodexStateForPreflight opens { readonly: true } and reads sqlite_master before returning the connection.
  • Every existing guarantee is intact. { readonly: true } stays the primary path, so a live writer's WAL is still joined and a thread another process just migrated to paginated history still refuses. The immutable fallback is still admitted only when neither -wal nor -shm is on disk. Any other failure still keeps the original error and the refusal that follows from it.
  • The test seam now names which step to fail ("open" or "first-read") instead of only the constructor; existing zero-argument hooks are unaffected.
  • Worth stating plainly: fix(codex): inspect a cleanly-closed WAL state store in the history preflight #4957 landed with a full green Linux suite and nine green Windows shards, and the gap was in its one case with no injected failure — the case that is deliberately platform-dependent. Bun bundles SQLite 3.53.2 on Linux and that build materializes both sidecars on the same first read, so the primary path just succeeds there. macOS uses the system libsqlite3 (3.51.0 on macos-26-arm64) and refuses. Coverage looked complete because the other five cases inject the failure and therefore assert the narrowing decision rather than the platform.

Closes #4943 on macOS; Linux and Windows behavior is unchanged.

Verification

  • Local verification was not run, because this lane forbids it. No local suite, focused test, typecheck, build, install, or ocx invocation was executed at any point. Everything below is hosted CI at head a052e02f797b4ee4038bde71557bc48a485a4b9a.
  • Cause, established on a hosted diagnostic branch (codex/diag-macos-wal-preflight, since deleted along with its temporary workflow and probe script; nothing from it is in this PR). Both runners built the fixture the failing test builds: journal_mode=wal in the header, no -wal and no -shm on disk.
    • macOS, job 105460260315, macos-26-arm64, Bun 1.4.0, SQLite 3.51.0: the read-only open succeeds; the first PRAGMA table_info then throws SQLiteError SQLITE_CANTOPEN / errno 14 / unable to open database file, with both sidecars still absent. Run through the policy exactly as fix(codex): inspect a cleanly-closed WAL state store in the history preflight #4957 shipped it, that error escapes to the caller; run through this PR's policy, the open, the pragma and a row read all succeed.
    • Linux, job 105451726898, Bun 1.4.0, bundled SQLite 3.53.2: the open succeeds and the first read succeeds, materializing both -wal and -shm. That is why the fallback is never exercised on Linux and why the defect is invisible there.
    • The immutable fallback itself is healthy on macOS: opening file:…?immutable=1 with SQLITE_OPEN_READONLY | SQLITE_OPEN_URI and reading through it succeeds, and leaves no sidecar.
  • Fix, proven on the platform that failed. macos 1/2, job 105469685293 is green, and all seven cases of the WAL describe block pass there, including reaches a verdict on a cleanly-closed WAL store rather than the catch-all refusal — the case that failed deterministically on dev (runs 35286915280 and 35291460951, an 8ms assertion, not a timeout). The [Bug]: spawned Bun child processes stop producing output and never exit, on both macOS and Windows CI legs #4956 combo-failover hook timeout did not recur on this leg.
  • Full PR run 35303058384: 25 checks green, covering both macOS shards, all four Linux shards, and the gates. Windows shards do not run on pull_request in this workflow, so — given that a platform gap is exactly what this PR is repairing — the full matrix was dispatched at the same head: run 35307801714, where all nine Windows shards, both macOS shards and all four Linux shards reported success. Read that run per job rather than by its aggregate: its trailing macos control lane (the dispatch-only whole-pool serial rerun of the same macOS tests) was cancelled afterwards, which marks the run cancelled even though every test leg had already passed.
  • Regression coverage: tests/codex-integration/codex-history-provider.test.ts gains a case that injects the failure at the first read and asserts both that the preflight still reaches a verdict and that the attempt actually offers that phase. On the pre-fix code that phase does not exist, so the case fails on every platform rather than only on macOS.

Checklist

  • Scope stays focused and avoids unrelated cleanup.
  • Docs or release notes were updated when needed. (structure/codex-home.md owns this open policy and now records that the guard covers the first read, and why the Linux and Windows evidence could not see the gap.)
  • Security-sensitive changes were reviewed for secrets, auth, and unsafe defaults. (No auth, credential, workflow, or release surface is touched; the change is one read-only SQLite connection policy.)

#4957 narrowed the preflight so a cleanly-closed WAL store is inspected instead
of refused, and it guarded the wrong operation. sqlite3_open_v2 never reads page
1, so a store whose header says WAL is not inspected until the first prepare. On
macOS that is where the absent -shm is raised, one frame above the guarded
attempt: the classification and the fallback had already returned, the error
reached the catch-all, and a user with a cleanly-closed WAL store still got
history_injection_preflight_unavailable with no way forward (#4943).

The first read now happens inside the attempt, so the failure is classified where
the policy lives. Every existing guarantee is unchanged: { readonly: true } stays
the primary path and still joins a live writer's WAL, the immutable fallback is
still admitted only when neither -wal nor -shm is on disk, and any other failure
still keeps the original error and the refusal that follows from it.

Linux cannot show the defect. Bun bundles its own SQLite there and that build
materializes both sidecars on the same first read, so the primary path simply
succeeds; macOS uses the system libsqlite3, which refuses. That is why #4957
passed the full Linux suite and nine Windows shards while its one platform-
dependent case failed on macOS.
@lidge-jun
lidge-jun requested a review from Ingwannu as a code owner September 18, 2026 02:48
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 18, 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-18T02:52:25.334679Z 923c156 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

Copy link
Copy Markdown
Contributor

Deterministic PR hygiene checks passed.

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

coderabbitai Bot commented Sep 18, 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: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Advanced

Run ID: 31b7a447-4f55-4c86-b42d-bb0fcee985fd

📥 Commits

Reviewing files that changed from the base of the PR and between 923c156 and a052e02.

📒 Files selected for processing (1)
  • structure/codex-home.md

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


📝 Walkthrough

Walkthrough

The preflight now performs an initial sqlite_master read inside the primary read-only attempt. It classifies failures from both opening and first read, closes partial database handles, and adds phase-specific integration coverage and documentation.

Changes

WAL preflight handling

Layer / File(s) Summary
Failure phase contract
src/codex/history-state-open.ts
Adds the exported StateDbPreflightOpenPhase type and updates the test failure hook to accept "open" or "first-read".
Primary read attempt and validation
src/codex/history-state-open.ts, tests/codex-integration/codex-history-provider.test.ts, structure/codex-home.md
The primary read-only attempt reads sqlite_master, closes partially opened databases after errors, and allows SQLITE_CANTOPEN from the first read to use the existing fallback classification. The integration test covers both phases. The documentation describes platform-specific WAL sidecar behavior.

Priority: ➖ Normal

Estimated code review effort: 2 (Simple) | ~15 minutes

Change: Bug fix · Severity of issue fixed: Medium

Sequence Diagram(s)

sequenceDiagram
  participant Preflight
  participant SQLite
  participant sqlite_master
  Preflight->>SQLite: Open state database read-only
  Preflight->>SQLite: Read sqlite_master
  SQLite->>sqlite_master: Prepare first read
  sqlite_master-->>Preflight: Success or SQLITE_CANTOPEN
  Preflight->>Preflight: Classify the failure phase
Loading

Merge Risk: ⚪ Minimal · up to a052e

No actionable merge-blocking risk remains identified for this change.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 66.67% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 3 functions across 2 files. (1 skipped: 1… Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Issue #4943 requires preflight to inspect a healthy cleanly closed WAL store through an immutable read path, or to return a distinct failure. src/codex/history-state-open.ts now performs the initial…
Out of Scope Changes check ✅ Passed The changes remain within issue #4943. Production changes are limited to Codex state-store preflight opening and failure classification in src/codex/history-state-open.ts. Regression coverage in `te…
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the primary change: guarding the first read during the history injection preflight to fix the macOS WAL failure path.
Full details: Docstring Coverage

Explanation

Docstring coverage is 66.67% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 3 functions across 2 files. (1 skipped: 1 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.

@lidge-jun

Copy link
Copy Markdown
Owner Author

리뷰 · 우선순위 74 / 80

이 PR은 #4943 을 macOS에서 다시 고칩니다. 지금 dev#4957 이 만든 src/codex/history-state-open.tsopenCodexStateForPreflight 는 읽기 전용으로 열고, 그게 SQLITE_CANTOPEN 이고 디스크에 -wal/-shm 이 없으면 immutable=1 로 다시 엽니다. 그 설계 자체는 맞습니다. 다만 macOS 시스템 libsqlite3 는 sqlite3_open_v2 단계에서는 실패하지 않고, 첫 prepare(페이지 1을 읽는 순간)에서야 없는 -shm 을 발견합니다. 그래서 분류와 불변 폴백이 끝난 뒤, 호출자(history-provider 의 프리플라이트) 쪽에서 에러가 터지고, catch-all 이 다시 history_injection_preflight_unavailable 로 접어 버립니다. Linux/Windows 는 Bun 번들 SQLite 가 그 첫 읽기에서 사이드카를 만들어 주어서 같은 케이스가 안 보입니다. 그래서 #4957 은 리눅스·윈도우 초록인데도 원 이슈 플랫폼(macOS Codex App + 깨끗이 닫힌 WAL state_5.sqlite)에서는 ocx sync 가 여전히 막힙니다.

이번 변경은 그 첫 읽기를 openCodexStateForPreflight 안으로 넣습니다. { readonly: true } 로 연 직후 sqlite_master 를 한 번 읽고, 그 실패도 같은 try/catch 에서 분류합니다. 실패 시 db?.close() 한 뒤, 예전과 같이 CANTOPEN + 사이드카 없음이면 immutable 폴백, 사이드카가 있거나 다른 이유면 원 에러를 그대로 올립니다. 테스트 훅은 phase open | first-read 를 받게 바뀌었고, 인자 없는 기존 훅은 런타임에서 그대로 동작합니다. structure/codex-home.md 도 가드가 생성자만이 아니라 첫 읽기까지라는 점을 적어 두었습니다. Closes #4943. 현재 dev HEAD 4c0124acb (#4996 tip) 기준이고, types/config 분할과 겹치지 않는 좁은 버그픽스입니다.

지금 dev 에서 이 모듈은 열기만 하고 바로 반환합니다. PR은 열기 직후 SELECT count(*) AS tables FROM sqlite_master 를 넣어, macOS가 실패하는 지점을 분류 범위 안으로 옮깁니다. 기존 보장(읽기 전용 우선, 사이드카 있으면 거절, 없으면 immutable)은 그대로입니다. 회귀 테스트는 first-read 단계에서 실패를 주입해 모든 플랫폼에서 프리픽스 코드가 깨지도록 잡았고, 진짜 증거는 강제 실패 없는 기존 cleanly-closed WAL 케이스의 macOS 초록입니다.

라인 96 - history-state-open.ts 성공 경로마다 sqlite_master SELECT가 한 번 더 돕니다. 의도는 맞지만 프리플라이트가 자주 도는 길이라, 비용과 리눅스에서 사이드카가 생기는 타이밍은 메인이 알고 있어야 합니다.

라인 1723 - codex-history-provider.test.ts first-read 주입 케이스는 훅 회귀로는 좋습니다. 다만 #4943 을 닫는 진짜 증거는 강제 실패 없는 기존 WAL 케이스의 macOS 샤드 초록입니다. 그 전에 머지하면 #4957 때와 같은 구멍이 남습니다.

setStateDbPreflightOpenFailureForTests - 타입이 (path, phase) 로 넓어졌습니다. 기존 0~1인자 훅은 JS에서 문제 없고 테스트도 그 방식입니다. 다른 브랜치가 엄격한 1인자 타입만 넘기면 타입 깨질 수 있으니 합류 시 한 번만 보면 됩니다.

CI / #4956 - 지금 macOS·테스트 샤드가 pending 입니다. 콤보 failover 훅 타임아웃(#4956)은 30초짜리라, 8ms assertion 실패와는 구분해 보면 됩니다.

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

너의 추천
macOS CI(특히 강제 실패 없는 cleanly-closed WAL 케이스)가 초록이면 머지하고 #4943 을 닫으세요. Linux/Windows만 초록인 채로는 #4957 때와 같은 구멍입니다. types/config 분할과 무관하니 rebase 강요할 이유 없고, 합류 전 라벨·리뷰어는 그대로 두면 됩니다.

이 댓글은 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: 923c156975

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

const forcedRead = openFailureForTests?.(resolvedPath, "first-read");
if (forcedRead) throw forcedRead;
// Page 1, read while the failure is still this function's to classify.
db.query<{ tables: number }, []>("SELECT count(*) AS tables FROM sqlite_master").get();

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 structure document mapped to src/codex

This changes behavior in src/codex/, but the commit updates only structure/codex-home.md; structure/INDEX.md also maps this area to runtime.md, config.md, catalog.md, subagents.md, providers/openai-tiers.md, gui-and-management-api.md, and ops/docs-and-release.md. Review and update each mapped document in this change so their duplicated history-writer contract summaries remain synchronized, as required for changes to an owned source area.

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

Useful? React with 👍 / 👎.

@lidge-jun
lidge-jun merged commit 384253a into dev Sep 18, 2026
64 of 65 checks passed
@lidge-jun
lidge-jun deleted the codex/preflight-first-read-macos branch September 18, 2026 04:52
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