Skip to content

feat: ship Codex-Claude Bridge 1.2.0-rc.1#32

Open
Kenmege wants to merge 16 commits into
mainfrom
codex/claude-agent-bridge
Open

feat: ship Codex-Claude Bridge 1.2.0-rc.1#32
Kenmege wants to merge 16 commits into
mainfrom
codex/claude-agent-bridge

Conversation

@Kenmege

@Kenmege Kenmege commented Jul 19, 2026

Copy link
Copy Markdown
Owner

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.1 for the npm next dist-tag; stable latest remains 1.1.1. The scoped-package and repository-name cutover remain blocked until npm scope ownership/trusted-publisher evidence is proven.

Release safeguards

  • prereleases publish to next, stable versions to latest
  • npm publish switch is disarmed while this PR is reviewed
  • Apache-2.0 licenses/notices retained and modified inherited files carry explicit change notices
  • no nested Codex runtime; the worker lane is Claude through tmux
  • exact release notes: RELEASE_NOTES_v1.2.0-rc.1.md

Verification

  • node --test --test-reporter=tap: 616/616 passed
  • npm run check: passed
  • npm run pack:check: passed, 94-file tarball
  • clean tarball install: both codex-claude and codex-claude-review present
  • installed codex-claude --version: 1.2.0-rc.1
  • installed codex-claude bridge-doctor --json: runtime ready; Claude, Codex, tmux, and independent verifier available
  • post-run broker/companion/tmux leak check: empty

Post-merge release sequence

  1. Re-verify the merged commit and required checks.
  2. Arm npm publishing only for the exact reviewed tag.
  3. Push v1.2.0-rc.1.
  4. Verify provenance and registry dist-tags (latest=1.1.1, next=1.2.0-rc.1).
  5. Disarm npm publishing again.

Kenmege added 12 commits July 11, 2026 19:37
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).
Copilot AI review requested due to automatic review settings July 19, 2026 06:41
Comment thread scripts/lib/bridge-cli.mjs Fixed
Comment thread scripts/lib/bridge-cli.mjs Fixed
Comment thread scripts/lib/bridge-input.mjs Fixed
Comment thread scripts/lib/bridge-production-verifier.mjs Fixed
Comment thread scripts/lib/bridge-runtime.mjs Fixed
Comment thread scripts/lib/claude-runner.mjs Fixed
Comment thread scripts/lib/claude-runner.mjs Fixed
Comment thread scripts/lib/tmux-executor.mjs Fixed
Comment thread test/bridge-messaging.test.mjs Fixed
Comment thread test/tmux-executor.test.mjs Fixed

Copilot AI 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.

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 (next for 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.

Comment on lines +98 to +107
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
});
Comment on lines +76 to +81
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("```")) {
Comment thread scripts/lib/state.mjs
Comment on lines +295 to 298
export function acquireQueuedLock(lockFile, options = {}) {
const timeoutMs = options.timeoutMs ?? 5_000;
const startedAt = Date.now();
let delayMs = 5;
Comment on lines 171 to 175
export async function ensureBrokerSession(cwd, options = {}) {
const existing = loadBrokerSession(cwd);
if (existing && (await isBrokerEndpointReady(existing.endpoint))) {
return existing;
}

@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: 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".

Comment thread scripts/lib/bridge-cli.mjs Outdated
}
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);

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 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 👍 / 👎.

Copilot AI review requested due to automatic review settings July 19, 2026 07:00

Copilot AI 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.

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.

Comment on lines +21 to +25
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);
}
Comment on lines +99 to 105
const brokerIdentity = brokerSession
? inspectBrokerProcessIdentity(brokerSession, cwd)
: { alive: false, exact: false };

if (brokerEndpoint) {
await sendBrokerShutdown(brokerEndpoint);
}
}
}
}, 50).unref?.();
}, 50);

@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: 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".

Comment thread scripts/lib/bridge-input.mjs Outdated
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");

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 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 👍 / 👎.

Comment on lines +399 to +401
pluginDirs: options["plugin-dir"],
mcpConfigs: options["mcp-config"],
addDirs: options["add-dir"],

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 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 👍 / 👎.

Copilot AI review requested due to automatic review settings July 19, 2026 12:49
Comment thread test/claude-runner.test.mjs Fixed

Copilot AI 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.

Copilot wasn't able to review this pull request because it exceeds the maximum number of lines (20,000). Try reducing the number of changed lines and requesting a review from Copilot again.

Copilot AI review requested due to automatic review settings July 19, 2026 12:53

Copilot AI 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.

Copilot wasn't able to review this pull request because it exceeds the maximum number of lines (20,000). Try reducing the number of changed lines and requesting a review from Copilot again.

@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: 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".

Comment on lines 103 to 104
if (brokerEndpoint) {
await sendBrokerShutdown(brokerEndpoint);

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 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 👍 / 👎.

Comment on lines +51 to +52
const unknown = parsed.positionals.find((value) => value.startsWith("--"));
if (unknown) throw usageError(`Unknown bridge option ${unknown}`);

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 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 👍 / 👎.

Copilot AI review requested due to automatic review settings July 19, 2026 13:27

Copilot AI 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.

Copilot wasn't able to review this pull request because it exceeds the maximum number of lines (20,000). Try reducing the number of changed lines and requesting a review from Copilot again.

@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: 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);

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 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);

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 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 👍 / 👎.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants