Skip to content

fix(cross-model): reach every peer through one pinned acpx transport - #1843

Merged
tmchow merged 16 commits into
mainfrom
feat/acpx-cross-model-transport
Oct 7, 2026
Merged

tmchow merged 16 commits into
mainfrom
feat/acpx-cross-model-transport

Conversation

@tmchow

@tmchow tmchow commented Oct 7, 2026

Copy link
Copy Markdown
Collaborator

Summary

Cross-model peers in ce-pov, ce-doc-review, ce-code-review, and ce-work now reach every model through one pinned acpx transport instead of a hand-built adapter per CLI. A CLI that changes its flags or output format no longer breaks peers until someone patches four scripts. Peers that used to fail now run, and each route's real trust boundary is stated from live probes.

This lands the whole plan (docs/plans/2026-09-28-1150-refactor-acpx-cross-model-transport-plan.md) in one PR. The plan called for one PR per worker; every worker carries the same parity-tested transport block, and a stack would have left the tree half-migrated between merges.

What changes for users

  • New prerequisite: Node 22.13+ with npx. Each worker checks it before contacting any provider. A missing prerequisite reports transport unavailable (pre-egress, shared|route): <reason>, nothing is sent, and the skill reports that peer as not run. ce-setup gains a node health row and offers, with approval, to warm the npm cache so the first peer run doesn't wait on a download.
  • Large diffs reach every route in full. ce-code-review used to send grok-cli and OpenCode a truncated diff; every route now reads the full staged diff from private temp.
  • Served-model receipts for codex and native Grok. Before, only Claude reported which model actually answered.
  • The trust boundary is stated per route, not assumed.
Route Read-only in review skills Notes
claude Writes denied Starts in --safe-mode, so the reviewed repo's settings and hooks don't apply
grok-cli Writes denied Except on large code-review diffs, where it reads with its own tools, which can write
grok-cursor, cursor, composer Writes refused Can read anywhere
opencode Writes denied Plan mode
codex Can write codex-acp ignores its read-only mode

Design decisions

  • acpx is required; native adapters are deleted. No fallback path. One parser reads every route's ACP stream, and success is the worker's own session/prompt result (end_turn), not the exit code.
  • Claude peers set an explicit permission mode. Live probes showed Claude's ACP adapter inherits the user's default mode. On a machine with bypassPermissions that default, a "read-only" peer could write; every Claude route now sets mode=default (review) or a deliberate mode (ce-work).
  • npx starts from private scratch, never the reviewed repo. npx resolves a package from its working directory's node_modules and .npmrc first. The agent still gets the repo through --cwd.
  • Exact pin plus a weekly canary. acpx is pre-1.0 and has shipped breaking changes in patch releases. The canary runs the newest acpx against the contract suite in a job with no write token, and only then opens a pin-bump PR from a separate job that never runs acpx.
  • OpenCode runs in plan mode. Denying bash or read in OpenCode's config makes its free tier reject the session over ACP.
  • Cursor routes request ACP preset ids (grok-cursor at high/fast, composer at fast), since Cursor's ACP server rejects effort variants.

Session-settled decisions carried from planning: acpx required with native adapters deleted (user-directed, over a ce-pov-only trial); native Windows tested, not special-cased (user-directed); read-only posture best effort, not a gate (user-directed); hostile checkout out of scope (user-directed, over npm-config guards); grok-cursor at Cursor's high/fast preset (user-directed); exact pin with a weekly canary (user-directed, over floating latest).

Validation

  • bun run test: 4493 pass, 0 fail, plus typecheck, release:validate, and skill guards.
  • Contract suite (bun run test:acpx-contract, new CI job): runs the real acpx 0.19.4 against a stub agent and pins the exit codes, prompt-id matching, cancellation, model rejection, and config-override behavior the parser depends on.
  • Each new test fails on its regressing variant. Every new scenario was checked once with its key assertion inverted or its fix reverted.
  • Live runs on the real CLIs: every route of every worker published a result. ce-code-review large diffs (316 KB) were read in full on grok-cli and OpenCode. A Claude peer in a repo with project hooks did not fire them. Real ce-work units on codex and grok-cursor completed and were committed by the host.
  • Live A/B (main vs this branch) on Claude and Codex hosts for ce-pov, ce-doc-review, and ce-code-review: no regressions. One run per cell.
  • Not verified: native Windows. The new Git Bash smoke step in windows-native CI runs for the first time on this PR.

Known residual risks:

  • Idle timeouts were tuned on the native CLIs and have not been re-measured through acpx.
  • The canary tests acpx against a stub agent, so a release that breaks a built-in adapter can still pass it; the bump PR asks for a live check of changed adapters.
  • The Claude --safe-mode wrapper probably can't launch on native Windows; that route then reports itself unavailable.

Related: #1797
Related: #1776

New concepts

Agent Client Protocol (ACP) through acpx. ACP is a JSON-RPC protocol between a client and a coding agent: the client opens a session, sends a prompt, and receives streamed session/update messages, permission requests, and a final stopReason. acpx is a headless ACP client with built-in launchers for Codex, Claude, Grok, Cursor, OpenCode, and others. It prints the raw stream with --format json.

Before this PR, each CLI had its own argv builder and output parser, and each broke on that CLI's releases. With ACP, the worker reads one message shape for every route. It finds the result whose id matches its own session/prompt request and reads the reply from agent_message_chunk updates.

In this PR, acpx_outcome treats exit 5 after end_turn as success (a permission was denied but the turn finished) and exit 0 with cancelled as failure.

ACP is the wrong tool when you need a CLI-only feature its adapter doesn't expose. Codex's -s read-only sandbox is one such feature, and this PR gives it up.

Security Disclosure

  • Shell/exec in skills changed. All four workers now run npx -y acpx@0.19.4. That command fetches acpx, which launches the codex and claude adapters through npx within acpx's own version ranges. The acpx pin is exact; the adapters float within those ranges (accepted in planning).
  • Peer write posture changed. The codex adapter does not enforce read-only, so codex review peers can now write in the reviewed tree; before, codex exec -s read-only enforced it. grok-cli on large ce-code-review diffs can also write. No ce-work route confines its worker to the workspace, so every route records cooperative posture. All of this is documented in each skill's trust-boundary section; read-only was settled as best effort.
  • Fixed during the migration: Claude peers no longer inherit bypassPermissions from user settings.
  • ce-work environment. The env -i allowlist gains npm's cache, registry, and user-config locations, never token values. Redaction now covers JSON-escaped forms of each secret and runs on the ACP stream, which echoes the prompt and file contents, before any log or failure evidence is written.
  • CI. The new canary workflow splits permissions: the job that runs untrusted acpx@latest has read-only contents and no persisted token; PR, issue, and dispatch writes happen in jobs that never execute acpx.
  • Residual: a hostile checkout is out of scope. npx no longer resolves acpx from the reviewed repo, but peers still run in the user's own trusted checkout.

Agent Disclosure

  • Model: Claude Code · claude-opus-5-5

tmchow added 10 commits October 7, 2026 14:19
Adds a scripted ACP stub agent and an opt-in contract suite
(`bun run test:acpx-contract`, CI job on ubuntu) that runs the real
pinned acpx and pins what the cross-model workers will depend on:
exit codes, the prompt result found by request id, permission denial
after end_turn (exit 5), SIGTERM -> cancelled, pre-prompt model
rejection, and .acpxrc.json agent overrides. Canned real acpx streams
and a PATH-shadowing npx stub serve later worker tests, which never
run real npx.

Includes the acpx transport plan this work executes.
Every ce-pov peer route now reaches its agent through one pinned acpx
(0.19.4) invocation and one ACP stream parser, replacing the per-CLI argv,
envelope parsers, and the Claude-only receipt.

- Success is the prompt's own end_turn result (exit 5 after a denied
  permission still publishes; a cancelled turn does not).
- Failures before the prompt is sent report
  "transport unavailable (pre-egress, shared|route)": Node older than
  22.13, npx or npm fetch failure, a missing agent CLI, an acpx config
  file that replaces the route's agent launch, or a rejected model.
- Served models come from the adapter's _meta report, so codex and the
  native grok route now record one too.
- Each route runs in its read-only mode where the adapter has one; Claude
  launches through a --safe-mode wrapper so the reviewed repository's
  project settings and hooks do not apply. The codex adapter does not
  enforce read-only, which the reference and guide now state.
- OpenCode runs in plan mode: denying bash outright makes its free tier
  reject the session over ACP.

The acpx transport block is parity-tested for every migrated worker and
pinned to the version the contract suite tests.
Cross-model peers now run through acpx, which needs Node 22.13 or newer
and npx. The health report gains a `node` row for that capability, and
ce-setup offers, with approval, to warm the npm cache for the pinned
acpx and the codex and claude adapters it launches (`check-health
--warm-acpx`), so the first peer run does not wait on a download.

Warming installs each package into npx's cache with
`npx --package=<spec> -- node --version`; `npm cache add` only caches
the top-level tarball, not the install npx reuses. The pin lines are
parity-tested against the workers' pin.
The doc-review worker now reaches every route through the pinned acpx
with --deny-all from an empty per-peer workspace, sharing ce-pov's acpx
transport block (parity-tested) and receipt logic. Native per-CLI argv,
envelope parsers, and the hard-only grok path are removed.

- The trust boundary is restated from live probes: claude, grok-cli, and
  opencode refuse reads outside the empty workspace; codex and the
  Cursor routes can still read anywhere, and codex can write because
  its adapter does not enforce read-only mode.
- The one-time overload retry now fires only on the ACP form a provider
  overload takes (claude-agent-acp's errorKind "overloaded"); other
  adapters retry overloads themselves.
- Pre-egress transport failures report the lens as not run, with no
  replacement route when the failure is shared by every route.
- A workspace that cannot be created now skips the peer instead of
  falling back to the run dir.

ce-pov's adapter comment is corrected: Claude's --allowed-tools is an
auto-approve list, not a tool restriction.
The adversarial-review worker now reaches every route through the pinned
acpx from the repository root, sharing the acpx transport block, receipt
kernel, override validation, and run loop with ce-doc-review and ce-pov
(all parity-tested). Native per-CLI argv, envelope parsing, and the
codex `git diff` prompt variant are removed.

- Large diffs: every route gets the private path of the full staged diff
  and reads it selectively. OpenCode's config allows external-directory
  reads for that, and grok-cli runs with --no-fs on large diffs, because
  acpx's file access refuses paths outside --cwd. Grok's own file tools
  also write without asking, so it does so only when the diff must be
  read from disk.
- --max-turns reaches only Claude over ACP, so only the claude route
  carries a turn limit.
- adversarial-codex-usage.json is now built from codex's ACP usage
  report; the unread raw event log, which would hold the outbound
  prompt, is no longer kept.
- The recovery reference treats a pre-egress transport failure as "not
  run": a shared failure goes straight to the in-process reviewer, a
  route failure still allows the one replacement.
- The trust boundary is restated per route from live probes, including
  that the codex adapter can write.
…acpx

ce-work's write-capable worker now reaches every route through the
pinned acpx with --approve-all in the prepared workspace, carrying the
same parity-tested transport block as the review workers. Native argv,
output parsers, the codex -o result file, and the Cursor --list-models
probe are removed.

- Cursor routes request ACP preset ids (grok-cursor at Cursor's
  high/fast preset, composer at fast); the controller accepts the
  bracketed form and compares served models on the id before "[".
- A served model matching the requested family is recorded "asserted"
  (the adapter's own report), never "verified"; a mismatch still blocks.
- Live probes show no route confines the worker to its workspace over
  ACP, so every route records restriction_posture "cooperative" and a
  run requiring enforced confinement finds every route unavailable. The
  reference records what still limits each route.
- The env -i allowlist adds npm's cache, registry, and user-config
  locations, never token values.
- Redaction covers JSON-escaped forms of each value and runs on the ACP
  stream (which echoes the prompt and file contents) before any log,
  reply text, or failure reason is taken from it.
- Success is the prompt's own end_turn; a prompt never sent reports
  "transport unavailable (pre-egress, shared|route)".
- OpenCode takes no effort setting over ACP.
acpx is pre-1.0 and has broken compatibility in patch releases, so the
pin is exact. A weekly (and manually dispatchable) workflow resolves the
newest acpx, runs the contract suite against it in a job with read-only
permissions and no persisted token, and only then, in jobs that never
execute acpx:

- on green, rewrites every pin copy and ce-setup's adapter specs with
  scripts/bump-acpx-pin.ts (specs read as text from the packed acpx
  agent registry), opens or updates one bump PR, and dispatches ci.yml
  on that branch, since a PR opened with GITHUB_TOKEN triggers no
  workflow;
- on red, opens or updates one tracking issue with the failing
  scenarios.

The bump script discovers pins by pattern, refuses copies that disagree
or are malformed, and is a no-op on a second run.
Git Bash's ps has no -o, so peer_alive reported a live peer as dead at
once: the idle and hard-cap guards never fired and the worker blocked
until the peer exited on its own. It now falls back to `ps -p` when
`ps -o` is unsupported.

OpenCode launches through acpx's raw --agent command, which acpx
rejects on native Windows, so the shared transport block reports that
route as a route-scoped pre-egress failure there instead of failing
after launch.

The windows-native CI job gains a Git Bash smoke that runs the ce-pov
worker through peer-job-runner.py with a stub npx: a published result
with its receipt, an idle reap that leaves no stub process, and
opencode reported unavailable.
The guides now state that OpenCode peers are unavailable on native
Windows, and the cross-model eval packs describe the stub npx harness
that replays canned acpx streams instead of fake per-CLI binaries.
…iew nits

npx resolves a package from its working directory's node_modules and
.npmrc before the cache or registry, so a repository with its own acpx
or npm config could decide which acpx ran. Every worker now starts npx
from its private scratch directory and passes the repository to the
agent only through --cwd; ce-setup's cache warm runs from an empty
temporary directory too, so it fills the same cache entries the
workers read.

Also from the branch review:
- auto-model routes (Cursor default, OpenCode) record an adapter-reported
  model without logging a false "model mismatch";
- the config template and its example copy list OpenCode among the
  routes that take no effort setting;
- ce-doc-review drops the out_missing_or_invalid helper whose only
  caller the migration removed;
- the pin-bump script's error paths (disagreeing copies, duplicate or
  missing pins, an unreadable agent registry) are now tested.
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 7, 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-10-07T22:33:06.213540Z 8e40df0 New commits
ℹ️ 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.

@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: 66ccbb885e

ℹ️ 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 tests/skills/ce-work-cross-model-routes.test.ts Outdated
Comment thread .github/workflows/acpx-canary.yml

@cursor cursor 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.

Security review of the acpx transport migration found one high-severity issue: adapter specs extracted from the npm tarball are written into a bash assignment that is evaluated when check-health runs.

Open in Web View Automation 

Sent by Cursor Security Agent: Security Reviewer

Comment thread scripts/bump-acpx-pin.ts Outdated
tmchow added 3 commits October 7, 2026 14:33
npm launches acpx through `sh -c`. bash execs the command, but Ubuntu's
dash does not, so a TERM sent to the npx leader stopped at the shell:
on Linux, ce-work's timeout and raw-output-cap stops left acpx and the
write-capable agent running. The route now runs in its own process
group and every stop signals the group, as the review workers already
do.

The acpx contract suite hit the same shell boundary (the SIGTERM case
hung on ubuntu CI) because it signalled npx. It now signals the acpx
process itself, which is the contract the workers depend on.
- bump-acpx-pin accepts only plain `[@scope/]name@range` adapter specs.
  The specs come from the untrusted acpx package and are written into a
  bash assignment check-health expands, so a spec carrying `$(...)`,
  backticks, quotes, a backslash, or a glob is now refused.
- The canary's bump-PR body carries the required Security and Agent
  Disclosure sections, which --body-file would otherwise skip.
- ce-work's route argv and effort-override tests run one emit per case
  instead of looping many subprocesses inside one test.
…talled

--emit-adapter printed `--agent  acp` when opencode was not on PATH,
which failed the route tests on CI runners. The launch falls back to the
bare `opencode` name; a live run still refuses the route in preflight
when the CLI is missing.

@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: c6f60da4ca

ℹ️ 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 skills/ce-setup/scripts/check-health
Comment thread skills/ce-work/scripts/cross-model-work.sh Outdated
Comment thread skills/ce-setup/references/acpx-cache-warm.md

@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: 5cb0ea199f

ℹ️ 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 tests/skills/ce-code-review-cross-model-routes.test.ts
Comment thread docs/guides/ce-code-review.md Outdated
- ce-work parses the ACP stream from the private raw copy. A redaction
  value that also appears in protocol text (such as `end_turn`) used to
  corrupt the outcome; logs, failure evidence, and the published receipt
  are still redacted.
- ce-setup's cache-warm offer says npm runs any install scripts the
  packages declare, as a first peer run would, instead of claiming
  nothing from the packages starts.
- ce-setup offers the warm only when the bundled health script reported
  the node row, since the warm runs that script; the inline fallback
  route no longer offers an action it cannot perform.

@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: 5c5290c628

ℹ️ 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 skills/ce-code-review/scripts/cross-model-adversarial-review.sh Outdated
Comment thread .github/workflows/ci.yml
tmchow added 2 commits October 7, 2026 15:05
- The worker route tests stub node at a fixed compliant version instead
  of using the machine's. The workers refuse Node older than 22.13, so
  on an older Node every expected-success route test failed before the
  stub npx ran.
- The ce-pov, ce-doc-review, and ce-code-review guides list jq among the
  peer prerequisites, as the ce-work guide already did.
- The three review workers accept cursor-grok-* model overrides on the
  grok-cursor route again, as main and ce-work do. Widening grok-4.7-* to
  grok-* had dropped that spelling, so a configured override skipped a
  valid Grok peer.
- The acpx-contract CI job runs npm-hosted acpx with read-only contents
  and no persisted checkout credential, matching the canary's contract job.
@tmchow
tmchow merged commit 67035e9 into main Oct 7, 2026
9 checks passed
@github-actions github-actions Bot mentioned this pull request Oct 7, 2026
michael-wojcik added a commit to michael-wojcik/compound-engineering-dsh that referenced this pull request Oct 9, 2026
… opt-in gate

Upstream moved one commit, EveryInc#1843: a large cross-model transport rework that
replaces per-provider peer invocation with one pinned acpx, and touches four
files that carry this port's bindings. The rebase was clean - 31 commits
replayed with no conflicts - because the bindings sit at the head of those files
and upstream edited elsewhere. Mirror unchanged at +140/-0, zero upstream lines
modified or removed repo-wide.

The binding claims were checked against the rework rather than assumed: every
token they rest on still exists in the tree (peer.outcome in 4 files, job id in
4, reap in 8, detached in 11), so the transport swap changed the route beneath
the contract without changing the contract the binding names.

The candidate list moved 23 to 24, and check 7 caught it before the doc did
again. The new file is ce-work/references/cross-model-execution.md, whose added
sentence about how every route *reaches its agent* matches the launcher pattern.
It governs external CLI workers and never instructs a subagent dispatch, so it
is a candidate rather than a binding site.

The opt-in transport gate now has a result: ACPX_CONTRACT=1 passes 9/9 and
acpx-transport-parity 2/2. On a cold npx cache the first test fails at its
60-second bound while the other eight pass, which reads as a broken transport
and is only a cold fetch.

Then the full suite was re-run the way the captured learning prescribes:
4511 pass, 10 skip, 8 fail, and zero cross-model transaction failures. The
family the gitignore diagnosis named is gone, and the total falls from 20 to 8.
Each residual family was checked for fork responsibility and none has any.
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.

1 participant