Skip to content

fix(codex): adopt pre-substrate homes into the write coordinator (#1049) - #2612

Merged
lidge-jun merged 1 commit into
devfrom
codex/1049-pre-substrate-adoption
Aug 25, 2026
Merged

lidge-jun merged 1 commit into
devfrom
codex/1049-pre-substrate-adoption

Conversation

@lidge-jun

@lidge-jun lidge-jun commented Aug 25, 2026 •

Copy link
Copy Markdown
Owner

Summary

Closes #1049. inject-coordination.ts classified routed and indeterminate pre-substrate homes as legacy-uncoordinated, and inject.ts skipped transition publication on that path, so a home that predates the write substrate never entered the coordinator. git grep adoption-pending returned nothing — the planned adoption state was designed and never built.

A routed pre-substrate home now publishes a complete adoption-pending coordinator before any native write, and both apply and restore adopt through the write lock. Indeterminate homes keep the legacy-operable path: they are the case where we genuinely cannot tell what we are looking at, and guessing there is what would strand a home.

Crash-safety is the primary property here, not a footnote. Publication is atomic by construction: the coordinator is built in a temp file, committed, fsynced, and then linkSynced into place. Every crash window leaves exactly one of two states — no authoritative database at all, or a complete resumable one. There is no window that leaves a half-built coordinator, because a partially written temp file is never the published name.

Verification

bun x tsc --noEmit                                     exit 0
bun test <inject/transition/doctor>                     17 pass / 0 fail
bun test <the above + codex-composed-acceptance>        25 pass / 0 fail

The crash tests are real: a child process is killed at each of the three checkpoints (temp-created, temp-committed, published) and the parent then asserts the home is still adoptable, resuming a full transition through it.

Falsified independently on the merge with dev, not only on the branch: replacing linkSync with in-place creation — the obvious "simpler" implementation — turns all three crash cases red with CodexCoordinatorLegacyAmbiguousError: An existing unversioned coordinator database cannot be adopted automatically. That is exactly the stranding this issue is about, and it is what the atomic publication buys. Restored, all three pass.

The subagent's own run additionally showed 99 pass across 8 files and 56 pass across 5 files, with five new regressions driven red by reverting their production hunks.

Checklist

  • Targets dev
  • Design note recorded at devlog/_plan/260826_pre_substrate_adoption/010_design.md
  • Crash windows tested with real killed child processes, not simulated
  • Composed acceptance suite green
  • No credential, auth, workflow or release-automation surface touched

Summary by CodeRabbit

  • New Features

    • Added crash-safe adoption for existing routed homes with recoverable coordinator state.
    • Added explicit adoption handling for apply and restore operations.
    • Added detection for homes eligible for coordinator adoption.
  • Bug Fixes

    • Improved resilience when adoption is interrupted, allowing operations to resume safely without overwriting existing data.
  • Tests

    • Added coverage for adoption eligibility, coordinator creation, state transitions, and recovery across crash points.

@lidge-jun
lidge-jun requested a review from Ingwannu as a code owner August 25, 2026 19:57
@lidge-jun
lidge-jun merged commit 82dbb1a into dev Aug 25, 2026
6 checks passed
@lidge-jun
lidge-jun deleted the codex/1049-pre-substrate-adoption branch August 25, 2026 19:57
@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 Aug 25, 2026
@coderabbitai

coderabbitai Bot commented Aug 25, 2026 •

Copy link
Copy Markdown
Contributor

Review Change Stack

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: cb45b487-4c63-486e-a8bf-48a7c3ba4724

📥 Commits

Reviewing files that changed from the base of the PR and between 0740130 and 4e1de09.

📒 Files selected for processing (10)
  • devlog/_plan/260826_pre_substrate_adoption/010_design.md
  • src/codex/codex-write-lock.ts
  • src/codex/convergence-types.ts
  • src/codex/inject-coordination.ts
  • src/codex/inject.ts
  • src/codex/transition-state.ts
  • tests/codex-coordinator-doctor.test.ts
  • tests/codex-inject-write-lock.test.ts
  • tests/codex-transition-state-adoption.test.ts
  • tests/helpers/codex-adoption-crash-child.ts

📝 Walkthrough

Walkthrough

Changes

Pre-substrate Codex adoption

Layer / File(s) Summary
Adoption state and eligibility
devlog/_plan/.../010_design.md, src/codex/convergence-types.ts, src/codex/inject-coordination.ts, src/codex/transition-state.ts
Adds the adoption-pending generation-zero state and the adopt eligibility outcome. Validates adoption metadata and exposes its history schedule.
Atomic coordinator publication
devlog/_plan/.../010_design.md, src/codex/transition-state.ts, src/codex/codex-write-lock.ts
Creates a complete temporary SQLite database, commits and fsyncs it, publishes it without replacement, and opens the coordinator transaction with adoption direction metadata.
Apply and restore routing
src/codex/inject.ts, tests/codex-coordinator-doctor.test.ts, tests/codex-inject-write-lock.test.ts
Routes eligible residue through coordinated locking. Injection uses apply; native restore uses remove. Tests verify eligibility and the ready generation-one transition.
Crash recovery validation
devlog/_plan/.../010_design.md, tests/codex-transition-state-adoption.test.ts, tests/helpers/codex-adoption-crash-child.ts
Tests recovery after crashes at temp-created, temp-committed, and published. Resumed transactions complete the transition and cleanup.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant CodexInjection
  participant CodexWriteLock
  participant openCodexCoordinatorTransaction
  participant CoordinatorDatabase
  CodexInjection->>CodexWriteLock: request adoption with apply direction
  CodexWriteLock->>openCodexCoordinatorTransaction: open adoption transaction
  openCodexCoordinatorTransaction->>CoordinatorDatabase: publish adoption-pending database
  CoordinatorDatabase-->>openCodexCoordinatorTransaction: return published database
  openCodexCoordinatorTransaction-->>CodexWriteLock: provide coordinator transaction
  CodexWriteLock-->>CodexInjection: apply coordinated transition
Loading

Suggested reviewers: ingwannu

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch codex/1049-pre-substrate-adoption

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 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: 4e1de09d21

ℹ️ 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 thread src/codex/inject.ts
let transitionReceipt: { nativeGeneration: number; currentTxId: string } | undefined;

if (eligibility.kind === "coordinated") {
if (eligibility.kind === "coordinated" || eligibility.kind === "adopt") {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Coordinate the synchronous shutdown restore

When a foreground ocx start adopts a pre-substrate home and then exits, syncCleanup in src/cli/index.ts:305-329 calls the synchronous restoreNativeCodex() at line 321 rather than this newly coordinated async path. That function still mutates config, profile, catalog, and history without the write lock or a remove transition, so it can overwrite another process's coordinated apply and, even without concurrency, leaves the database claiming the last operation was an apply after the native files were restored. Route synchronous shutdown through the coordinator or defer cleanup to this async path, with a focused shutdown regression.

AGENTS.md reference: src/AGENTS.md:L22-L25

Useful? React with 👍 / 👎.


linkSync(tempPath, finalDatabasePath);
options.onCheckpoint?.("published");
if (process.platform !== "win32") fsyncPath(parent);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Tolerate unsupported directory fsync

On POSIX runtime namespaces backed by a filesystem that rejects directory fsync, such as a virtual/shared mount returning EINVAL or ENOTSUP, this throws after linkSync has already published the complete coordinator. The opener then reports a non-retryable lock_unavailable, causing the first injection or restore to fail even though a retry would open the newly created authority; src/lab/subject/installation-salt.ts:55-68 already handles this platform case by ignoring only known unsupported-directory-fsync codes. Apply the same handling here while retaining genuine I/O failures.

Useful? React with 👍 / 👎.

tarunravi pushed a commit to tarunravi/opencodex that referenced this pull request Sep 14, 2026
agentHits pushed a commit to agentHits/opencodex that referenced this pull request Sep 17, 2026
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