feat: ship Codex-Claude Bridge 1.2.0-rc.1#32
Conversation
Two independent gaps let broker processes (and their codex app-server children) survive indefinitely: - ensureBrokerSession()'s only real caller (CodexAppServerClient.connect via withAppServer) never passed killProcess, so when a stale session is detected the code deleted its pid/log bookkeeping without killing the still-running process, orphaning it permanently. Pass the terminateProcessTree helper already used elsewhere in app-server.mjs. - tests/runtime.test.mjs exercises the CLI's review/task commands against fresh temp-dir repos, which lazily spawns a brand-new detached broker per test via the default connect path. Only a handful of tests explicitly ran the SessionEnd hook to tear theirs down, so most brokers (plus their app-server children) leaked for the life of the machine. Track every dir made by makeTempDir() and reap any broker left in it via a single after() hook, using the same teardownBrokerSession + terminateProcessTree path the SessionEnd hook already uses in production. Verified with `ps -axo pid,ppid,command | grep -E 'app-server-broker|codex-plugin-test'` returning empty after a full tests/runtime.test.mjs run (57 tests, 54 pass / 3 pre-existing unrelated failures reproduced identically with these changes stashed out).
There was a problem hiding this comment.
Pull request overview
Prepares codex-plugin-cc@1.2.0-rc.1 as a public release candidate, introducing the “Codex-Claude Bridge” durable delegation/control-plane (tmux-owned Claude workers, recovery, messaging, delivery + acknowledgement, and independent Codex verification) while keeping the legacy workspace/review lanes and compatibility identifiers intact.
Changes:
- Adds the durable bridge runtime surface: request/result/receipt/event/message contracts, broker orchestration, delivery + inbox fallback, verification loop, and CLI/pluggable command routing.
- Updates safety + release hygiene: dist-tag selection (
nextfor prereleases), packaged-file closure checks, expanded docs/identity guidance, and tighter process cleanup/identity handling for brokers. - Expands tests to cover bridge contracts, broker behaviors, packaging closure, migration shim behavior, and teardown leak prevention.
Reviewed changes
Copilot reviewed 91 out of 94 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| tests/runtime.test.mjs | Adds suite-level broker leak cleanup + identity-mismatch preservation test. |
| tests/helpers.test.mjs | Tests broker cleanup helper behavior and wait semantics. |
| tests/helpers.mjs | Implements broker leak reaping + process-tree wait utilities for tests. |
| tests/broker-lifecycle.test.mjs | Adds broker identity/cleanup tests for persisted session handling. |
| test/release-docs.test.mjs | Extends workflow/docs assertions for bridge release + dist-tag behavior. |
| test/package-bridge.test.mjs | Ensures packed tarball contains required bridge/Codex adapter closure. |
| test/identity-migration.test.mjs | Validates identity/migration docs, shim behavior, and notices. |
| test/commands.test.mjs | Updates plugin manifest identity expectations (display name). |
| test/bridge-worker-protocol.test.mjs | Tests worker prompt + structured-result normalization contract. |
| test/bridge-request.test.mjs | Tests bridge request building and validation/error codes. |
| test/bridge-receipt.test.mjs | Tests receipt creation + immutable state transitions/redaction. |
| test/bridge-broker.test.mjs | Tests broker retry/heartbeat/repair wiring and bounds. |
| skills/claude-review/SKILL.md | Updates skill routing guidance to prefer durable bridge delegation. |
| SECURITY.md | Expands threat model to include durable bridge lane and its states. |
| scripts/validate-repo.mjs | Enforces required bridge docs/schemas/scripts and syntax checks. |
| scripts/lib/state.mjs | Exports primitives used by bridge (private dirs, atomic writes, queued locks). |
| scripts/lib/bridge-worker-protocol.mjs | Defines worker prompt + JSON result marker parsing/normalization. |
| scripts/lib/bridge-verification.mjs | Implements Codex-owned verification loop + bounded repair support. |
| scripts/lib/bridge-supervisor.mjs | Supervises durable jobs via leases, inspection, delivery gating, recovery. |
| scripts/lib/bridge-receipt.mjs | Implements immutable bridge receipt creation/update helpers. |
| scripts/lib/bridge-policy.mjs | Defines trust profiles + effective permission args and path/tool validation. |
| scripts/lib/bridge-input.mjs | Implements durable same-session messaging queue + ack artifacts. |
| scripts/lib/bridge-delivery.mjs | Implements delivery to Codex origin (or waiter) + durable inbox drain. |
| scripts/lib/bridge-contracts.mjs | Adds Ajv-based schema validators + receipt semantic chronology checks. |
| scripts/lib/bridge-broker.mjs | Implements broker loop, heartbeat writing, and repair lifecycle wiring. |
| scripts/claude-review-companion.mjs | Routes new bridge commands and extends CLI usage/help + error envelope. |
| scripts/bridge-broker.mjs | Adds broker executable entrypoint. |
| schemas/bridge-result.schema.json | Adds schema for normalized worker result payload. |
| schemas/bridge-receipt.schema.json | Adds schema for durable receipt + delivery/verification lifecycle. |
| schemas/bridge-message-operation.schema.json | Adds schema for durable messaging operations. |
| schemas/bridge-event.schema.json | Adds schema for ordered, deduplicated durable events. |
| schemas/bridge-delegation-request.schema.json | Adds schema for immutable delegation request envelope. |
| RELEASE_NOTES_v1.2.0-rc.1.md | Adds RC release notes documenting bridge scope and identity. |
| plugins/codex/scripts/session-lifecycle-hook.mjs | Avoids killing mismatched live PIDs by verifying broker identity first. |
| plugins/codex/scripts/lib/codex.mjs | Adds deadlines + broker-disable support for Codex app-server operations. |
| plugins/codex/scripts/lib/broker-lifecycle.mjs | Adds broker identity token, identity inspection, safer teardown behavior. |
| plugins/codex/scripts/lib/app-server.mjs | Ensures broker session spawn uses terminateProcessTree for cleanup. |
| plugins/codex/scripts/app-server-broker.mjs | Adds identity token flag support and usage update. |
| packages/codex-plugin-cc-shim/README.md | Documents the inert/private migration shim purpose. |
| packages/codex-plugin-cc-shim/package.json | Adds private shim package manifest and bin forwarding definitions. |
| packages/codex-plugin-cc-shim/NOTICE | Adds shim NOTICE with upstream attribution references. |
| packages/codex-plugin-cc-shim/LICENSE | Adds Apache-2.0 license text for shim package. |
| packages/codex-plugin-cc-shim/bin/codex-claude.mjs | Implements forwarding shim with recursion guard + signal propagation. |
| package.json | Bumps version to 1.2.0-rc.1, updates files list, adds Ajv deps, updates scripts. |
| package-lock.json | Locks Ajv/ajv-formats and bumps version metadata. |
| docs/plans/2026-07-11-codex-plugin-cc-x-launch-video-design.md | Adds launch video design plan doc (non-runtime). |
| docs/NPM_PUBLISH_CHECKLIST.md | Updates publish checklist for prerelease next tagging and smoke checks. |
| docs/bridge-runtime-compatibility.md | Documents compatibility gate and security model boundaries. |
| docs/bridge-migration.md | Records identity + naming/migration gates and approval boundaries. |
| docs/bridge-capabilities.md | Documents bridge contracts, profiles, recovery, and durable state semantics. |
| docs/architecture.md | Adds bridge architecture overview and links to bridge docs. |
| CONTRIBUTING.md | Updates contribution rules (runtime deps minimal/pinned; plugins/codex is prod adapter). |
| commands/workspace.md | Reframes workspace as legacy lane; points to delegate for durable jobs. |
| commands/wait.md | Adds bridge wait command guidance and exit-code contract. |
| commands/status.md | Updates status command to support bridge jobs and distinct lifecycle states. |
| commands/send.md | Adds durable message sending guidance and caveats. |
| commands/recover.md | Adds durable recovery guidance with fail-closed ownership rules. |
| commands/logs.md | Adds bridge logs command guidance. |
| commands/list.md | Adds bridge list command guidance. |
| commands/gc.md | Adds bounded GC command guidance (dry-run unless --apply). |
| commands/enable.md | Updates enable docs to include new bridge slash commands. |
| commands/delegate.md | Adds durable delegation command guidance and verification requirements. |
| commands/cancel.md | Updates cancel docs for bridge vs legacy routes and two-phase cancellation. |
| commands/bridge-doctor.md | Adds bridge-doctor command guidance and interpretation boundaries. |
| commands/attach.md | Adds tmux attach command guidance and safety notes. |
| CHANGELOG.md | Adds 1.2.0-rc.1 changelog entry and highlights. |
| .gitignore | Ignores additional production/worktree/acceptance artifacts. |
| .github/workflows/release.yml | Publishes prereleases to next and stable to latest via tag selection. |
| .github/workflows/pull-request-ci.yml | Adds macOS tmux integration job; retains pinned action SHAs. |
| .codex-plugin/plugin.json | Updates plugin version/identity/description/prompts for bridge. |
| const platform = options.platform ?? process.platform; | ||
| const listProcess = options.listProcess ?? spawnSync; | ||
| const result = platform === "win32" | ||
| ? listProcess("powershell.exe", [ | ||
| "-NoProfile", "-NonInteractive", "-Command", | ||
| `(Get-CimInstance Win32_Process -Filter 'ProcessId = ${session.pid}').CommandLine` | ||
| ], { encoding: "utf8", windowsHide: true }) | ||
| : listProcess("ps", ["-ww", "-p", String(session.pid), "-o", "command="], { | ||
| encoding: "utf8", windowsHide: true | ||
| }); |
| function parseReport(text) { | ||
| if (typeof text !== "string") throw new Error("Claude terminal result is not text"); | ||
| const markerIndex = text.lastIndexOf(BRIDGE_RESULT_MARKER); | ||
| if (markerIndex < 0) throw new Error(`Claude result is missing ${BRIDGE_RESULT_MARKER} marker`); | ||
| let encoded = text.slice(markerIndex + BRIDGE_RESULT_MARKER.length).trim(); | ||
| if (encoded.startsWith("```")) { |
| export function acquireQueuedLock(lockFile, options = {}) { | ||
| const timeoutMs = options.timeoutMs ?? 5_000; | ||
| const startedAt = Date.now(); | ||
| let delayMs = 5; |
| export async function ensureBrokerSession(cwd, options = {}) { | ||
| const existing = loadBrokerSession(cwd); | ||
| if (existing && (await isBrokerEndpointReady(existing.endpoint))) { | ||
| return existing; | ||
| } |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 8e11411155
ℹ️ About Codex in GitHub
Codex has been enabled to automatically 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 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| } | ||
| const suppliedContent = options.message ?? positionals.slice(1).join(" ").trim(); | ||
| const content = questionId ? `[Answer to Claude question ${questionId}]\n${suppliedContent}` : suppliedContent; | ||
| const message = enqueueBridgeInput(artifacts(jobId, bridgeOptions).jobDir, jobId, content); |
There was a problem hiding this comment.
Persist the ledger entry before queueing sends
When send supplies content that the ledger rejects after queueing—such as a message over the 64 KiB event quota, or --question-id with an empty answer—this writes the runtime input file first and then throws in appendBridgeCodexMessage. A live worker polls that queue directly, so the CLI can report an error while Claude still receives unrecorded input, leaving the authoritative event journal inconsistent with what steered the session; validate/append first or remove the queued file on failure.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 92 out of 95 changed files in this pull request and generated 3 comments.
Comments suppressed due to low confidence (1)
plugins/codex/scripts/lib/broker-lifecycle.mjs:181
- ensureBrokerSession() clears broker.json even when the broker PID is live but the process identity cannot be verified (alive=true, exact=false). In that case the code neither kills the process nor preserves durable identity for later cleanup, so it can leak a detached broker and then start a second one, creating inconsistent state. Consider failing closed (throw) when a live PID cannot be attributed, mirroring the test teardown behavior that refuses identity-mismatch cleanup.
| function writeJsonAtomic(file, payload) { | ||
| const temporary = `${file}.${process.pid}.tmp`; | ||
| fs.writeFileSync(temporary, `${JSON.stringify(payload, null, 2)}\n`, { mode: 0o600 }); | ||
| fs.renameSync(temporary, file); | ||
| } |
| const brokerIdentity = brokerSession | ||
| ? inspectBrokerProcessIdentity(brokerSession, cwd) | ||
| : { alive: false, exact: false }; | ||
|
|
||
| if (brokerEndpoint) { | ||
| await sendBrokerShutdown(brokerEndpoint); | ||
| } |
| } | ||
| } | ||
| }, 50).unref?.(); | ||
| }, 50); |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 044a0b14ce
ℹ️ About Codex in GitHub
Codex has been enabled to automatically 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 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| if (!MESSAGE_PATTERN.test(messageId)) throw new Error("invalid bridge continuation message id"); | ||
| const now = options.now ?? (() => new Date()); | ||
| const createdAt = now().toISOString(); | ||
| const sequence = String(Date.parse(createdAt)).padStart(16, "0"); |
There was a problem hiding this comment.
Preserve enqueue order for same-millisecond sends
When two send invocations enqueue messages within the same millisecond, they get the same timestamp prefix here and listPendingBridgeInput() then sorts by the random UUID suffix, so Claude can replay the later message before the earlier one. This breaks the documented ordered collaboration channel for rapid or concurrent sends; use a monotonic sequence under a lock (or another deterministic counter) instead of millisecond time alone.
Useful? React with 👍 / 👎.
| pluginDirs: options["plugin-dir"], | ||
| mcpConfigs: options["mcp-config"], | ||
| addDirs: options["add-dir"], |
There was a problem hiding this comment.
Resolve delegate path flags before building requests
These CLI path flags are passed through exactly as typed, but buildBridgeRequest() rejects non-absolute pluginDirs, mcpConfigs, and addDirs via canonicalPath(). In normal CLI use such as running from a repo with --add-dir ../shared or --plugin-dir ., delegation fails before launch even though the rest of the delegate paths are resolved relative to the caller; resolve these values against the invocation cwd before passing them into the request builder.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 224a2a3df4
ℹ️ About Codex in GitHub
Codex has been enabled to automatically 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 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| if (brokerEndpoint) { | ||
| await sendBrokerShutdown(brokerEndpoint); |
There was a problem hiding this comment.
Gate broker shutdown on the verified identity
When the persisted broker record points at a live process whose identity is not an exact match, this still sends broker/shutdown before the later ownership check. app-server-broker.mjs exits on that method, so a stale or corrupted broker record whose endpoint reaches another live broker can terminate a process this hook explicitly decides not to tear down; only send shutdown in the same !alive || exact branch used for teardown.
Useful? React with 👍 / 👎.
| const unknown = parsed.positionals.find((value) => value.startsWith("--")); | ||
| if (unknown) throw usageError(`Unknown bridge option ${unknown}`); |
There was a problem hiding this comment.
Preserve option-like task text after
--
For delegate and send, any task/message token after the option terminator that starts with -- is now rejected as an unknown bridge option. That breaks the documented delegate ... -- <task> path for normal requests involving CLI flags, such as -- fix --dry-run handling or a task beginning with --foo; restrict the unknown-option scan to arguments before the terminator instead of all positionals.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 45162a0fc7
ℹ️ About Codex in GitHub
Codex has been enabled to automatically 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 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| .split("\0").filter(Boolean).sort(); | ||
| if (files.length > 200_000) throw new Error("workspace snapshot exceeds 200000 files"); | ||
| const dirty = new Set(); | ||
| const porcelain = git(root, ["status", "--porcelain=v1", "-z"]).split("\0").filter(Boolean); |
There was a problem hiding this comment.
Sanitize git status during workspace capture
When verification runs in a workspace whose Git config sets core.fsmonitor, this git status --porcelain call executes that fsmonitor hook while inheriting the broker/Codex environment from run(); Git documents the fsmonitor hook as invoked when core.fsmonitor is configured (https://git-scm.com/docs/githooks). That makes the supposedly independent read-only verifier execute repository-configured code before and after the worker, with access to any unfiltered broker environment, so use the same hardened Git invocation/config disables used by the existing git-safe path.
Useful? React with 👍 / 👎.
| payload | ||
| }, options); | ||
| writeStateAtomic(paths.state, state); | ||
| if (TERMINAL_STATES.has(nextStatus)) releaseWorkspaceLease(state, options); |
There was a problem hiding this comment.
Hold the workspace lease until verification settles
For a completed worker, this releases the workspace lease before writeBridgeResult() and the subsequent Codex verification/possible repair loop finish, so a user can start another bridge job in the same workspace while the first job is still capturing its post-worker snapshot. In that overlap, the second worker's edits are attributed to the first job's integrity check, causing false verification failures or corrupting the first result; keep the lease until the receipt reaches a final verification/delivery state, or transfer it only to the bounded repair child.
Useful? React with 👍 / 👎.
Outcome
Ships the public release candidate for Codex-Claude Bridge by Kenmege: durable Codex-to-Claude delegation through tmux-owned Claude workers, collaborative messaging, recovery, and independent verification. CAMPUS remains a separate system.
The existing npm package remains the compatibility lane. This PR prepares
codex-plugin-cc@1.2.0-rc.1for the npmnextdist-tag; stablelatestremains1.1.1. The scoped-package and repository-name cutover remain blocked until npm scope ownership/trusted-publisher evidence is proven.Release safeguards
next, stable versions tolatestRELEASE_NOTES_v1.2.0-rc.1.mdVerification
node --test --test-reporter=tap: 616/616 passednpm run check: passednpm run pack:check: passed, 94-file tarballcodex-claudeandcodex-claude-reviewpresentcodex-claude --version:1.2.0-rc.1codex-claude bridge-doctor --json: runtime ready; Claude, Codex, tmux, and independent verifier availablePost-merge release sequence
v1.2.0-rc.1.latest=1.1.1,next=1.2.0-rc.1).