Zed ACP integration
Decision
Zuno targets the latest reviewed stable ACP V1 schema for its Zed integration. The V2 schema remains an explicitly labeled preview input until it is stable and Zed negotiates it in production. Protocol implementation work must start from the audited files under docs/upstream/acp, not from a floating branch or an unversioned web page.
The 2026-08-26 baseline is:
| Input | Pin | Commit | Role |
|---|---|---|---|
| ACP stable schema | schema-v1.21.0 | 272bf799f35a258c6a4107a0410ed361e83683d3 | Production wire contract |
| ACP Rust schema crate | v1.7.0 | 272bf799f35a258c6a4107a0410ed361e83683d3 | Typed Rust schema baseline |
| ACP V2 preview | schema-v2.0.0-alpha.3 | 272bf799f35a258c6a4107a0410ed361e83683d3 | Design tracking only |
| Zed | ac099b4a809a564f06907125e7a536c33cb60084 | same | Client behavior observation |
The exact annotated tag objects, release asset IDs, URLs, sizes, hashes, fetch time, and upstream main commits observed during capture are recorded in docs/upstream/acp/manifest.json.
2026-09-02 current Zed contract spot-check
The stable protocol baseline above is unchanged. For this implementation, current Zed main was additionally inspected at 97b1e64a177a2fe3c2803e323087b5c2fa6fff1e. Its workspace pins agent-client-protocol = 2.0.0 with unstable features while the client-consumed stable schema crate remains agent-client-protocol-schema = 1.5.0. Zuno keeps its production wire contract at stable ACP V1.21 and adds a typed decode contract test against that exact client schema:
CurrentModeUpdatemutates the session mode andConfigOptionUpdatereplaces the option list and wakes its watcher;- standard
SessionUpdate::Planis passed to the ACP thread model; - the thread replaces, extends, or truncates its current Plan entries from each complete update.
The exact schema dependency is enabled only by the zed-schema-contract feature and exercised through make test-zed-acp-schema. Its upstream crate enables serde_json/preserve_order; isolating the contract build prevents that test-only feature from changing production type layouts or unrelated workspace Clippy results.
Zuno's stdio black-box tests therefore assert standard mode, config, Plan, and live command updates. This spot-check does not advance the vendored ACP schema or copy Zed implementation code.
Official adapter behavior references
Zuno also reviews the official ACP adapters as executable design evidence. They are not wire-contract authorities and Zuno does not copy their implementation:
| Adapter | Reviewed commit | What Zuno reviews |
|---|---|---|
agentclientprotocol/codex-acp | 2b48e9822330fc09f3a94a81563e5c4bb779601a | Capability negotiation, thought_level configuration, command publication, rich prompt conversion, cancellation, tool and subagent projection, file changes, usage, and durable thread loading |
agentclientprotocol/claude-agent-acp | 8710ce1cbccf562cb04b4bcc30e053e960aee05f | Permission and elicitation lifecycle, edit review, TODO projection, client MCP, terminal ownership, and cancellation races while user input is pending |
For every ACP change, review the stable schema first, then compare the same behavior in these pinned adapters and Zed. Classify the result as adopt, adapt, or reject for Zuno's durable runtime. Adapter-specific extensions, authentication identities, hidden prompts, and runtime assumptions are not inherited merely because an adapter implements them.
The adapter commits are intentionally recorded separately from the vendored schema assets. A future review must choose new exact 40-character commits and update this table; it must not replace the pins with floating main links.
Upstream authorities
- Repository: https://github.com/agentclientprotocol/agent-client-protocol
- Stable protocol documentation: https://agentclientprotocol.com/protocol/v1/overview
- Zed external agents documentation: https://zed.dev/docs/ai/external-agents
- Zed evidence at the pinned commit: https://github.com/zed-industries/zed/blob/ac099b4a809a564f06907125e7a536c33cb60084/crates/agent_servers/src/acp.rs#L993
At the pinned Zed commit, the production connection path constructs InitializeRequest::new(ProtocolVersion::V1). Zed also declares V1 as its minimum supported version in the same file. This is why Zuno's production target is stable V1 even while V2 exists as an alpha schema.
Zed's repository is GPL-3.0-or-later. Zuno may use public behavior and protocol observations as design evidence, but this snapshot does not copy Zed source. The ACP repository and copied schema assets are distributed under Apache-2.0; the exact upstream license is stored at docs/upstream/acp/LICENSE.
Integration boundary
ACP is a client projection over Zuno's durable runtime, not a second agent loop. Zed requests must enter the same durable inbox and session lifecycle as the TUI, CLI, server, and future clients. ACP notifications must be projections of durable events, and disconnecting Zed must not silently discard durable work.
The stable schema snapshot is the acceptance source for these surfaces:
- initialization, version negotiation, implementation metadata, and honest client/agent capabilities;
- authentication methods only when a real handler exists;
- session creation, loading, listing, resuming, deletion, closing, mode/config changes, prompting, and cancellation where present in the stable schema;
- text, image, embedded-resource, and resource-link content only when the negotiated capabilities and runtime implementation both support them;
- assistant and reasoning chunks committed from one successful provider attempt;
- tool-call start/update/completion, raw input/output, locations, terminal content, and file diff content;
- human-in-the-loop permission requests with typed outcomes and cancellation;
- client file reads/writes and terminal operations only through negotiated handlers, with errors preserved rather than replaced by successful stubs;
- elicitation, plan, usage, configuration, mode, model, and session information updates where defined by the pinned schema;
- durable replay on load, live event ordering, backpressure, process cancellation, and exact session teardown.
Capabilities are promises. Zuno must not advertise an ACP method, content type, MCP transport, filesystem operation, terminal operation, renderer, or authentication flow until its provider and runtime consumer are wired and tested.
Implemented production surface
zuno acp is the production stdio entry point. It uses the same TurnHost, durable SQLite sessions, provider configuration, tools, permission policy, MCP runtime, work state, and lifecycle as the CLI and TUI; it is not a second agent loop.
| Area | Production behavior |
|---|---|
| Initialization | Negotiates stable protocol V1 and reports schema 1.21.0. Authentication methods are empty because Zuno uses its own configured provider credentials and has no ACP login handler. |
| Session lifecycle | Implements session/new, load, resume, list, delete, and close. session/new reserves an in-process id and publishes configuration, commands, Skills, and required MCP without creating a durable Session; pre-prompt Agent/model/Mode/reasoning changes remain process-local. The first accepted user prompt or durable native command atomically creates the Session with its first input, while closing an unused panel leaves no history row. Load and resume are cold: they restore the Agent, model, and thought level persisted on its row plus durable client projections without starting TurnHost, MCP, plugins, or watchers. A saved model absent from the current catalog falls back to configuration without failing the load. Concurrent requests for one session id share a stable registry entry and one activation gate. The first prompt, Start Work, or active Goal recovery activates it. Loading replays durable history, while resume treats the client transcript as already owned. |
| Session configuration | Implements an independent durable build/plan mode plus Agent, model, and reasoning_effort selection. Mode owns the read-only boundary; the Agent selector contains implementation Agents and, while planning, updates the saved future Work Agent without leaving Plan or waking a cold runtime. Every accepted change publishes current_mode_update and config_option_update. /start-work and session/set_mode(build) authorize the exact handoff-ready Plan revision, restore the saved identity, and either start immediately or queue at a safe point. Active replacement prepares and atomically publishes a candidate before retiring the old resources; unchanged optional MCP connections may transfer only under complete identity equality. |
| Commands and Skills | Publishes native /compact, /goal, /plan, /start-plan, and /start-work controls with real handlers, executable Catalog commands, and unambiguous slash-invokable Skills. Native commands resolve first and suppress same-named Catalog or Skill entries. Compact and Goal invoke shared durable TurnHost handlers. Plan entry is idempotent; Start Work requires an exact handoff-ready Plan revision, records one durable UserControl cycle, and queues safely when the session is busy. Native command text never enters the model. |
| Prompt execution | The durable inbox commits before execution. A concurrent ordinary prompt is steered or queued and waits for its associated InputAdmissionReceipt terminal outcome; acceptance is not a busy error. _meta.zuno.messageId is an optional session-scoped idempotency key, not a text deduplication rule. Session-owned execution survives a dropped RPC observer. The authoritative Plan projector deduplicates (plan_id, revision), flushes the final revision, and maps superseded to ACP completed with its true outcome in metadata. Load, resume, detached continuation and remount share that projector. A real command or Skill still requires idle execution; a mere leading / is not command proof. Unsupported foreign input shapes remain pending for their owning surface. |
| Prompt content | Advertises and accepts text, inline image, native resource_link, embedded text resource, and embedded image resource content. Audio remains false. Resource links stay typed; images use typed durable file parts; embedded text keeps URI, MIME, and body in one stable persisted envelope. Selection, diagnostics, fetched context, and branch diff use the generic embedded-resource path rather than Zed-specific prompt branches. |
| Assistant and tool projection | ACP text, reasoning, and pending tool updates are provisional until the assistant checkpoint because ACP has no rollback operation. A provider RetryRollback discards the failed attempt before any of those updates reach Zed; the successful replacement is then committed in order. An accepted question result is already durable, so Zuno immediately reasserts that tool call as in_progress with _meta.zuno.question.continuationPending: true while the continuation request is silent. The real completed update is sent before the continuation's checkpointed output, or during terminal cleanup if the turn fails or is interrupted. This keeps Zed visibly active without inventing assistant text or exposing provisional output. Tool dispatch start/update/completion, raw input/output, JSON/text content, written-file locations, and usage otherwise keep their normal order. A generated session title is a typed session_info_update; operational status and provider error text never masquerade as agent_thought_chunk, with one tagged exception: Zuno-originated notices (instruction.*, budget.*) are agent_thought_chunk updates carrying _meta.zuno.notice so a client can key on them rather than read them as model thought. |
| Delegation and child sessions | Every task call has a stable human-readable tool card plus raw details. If the client explicitly advertises the draft clientCapabilities.subagents object, Zuno advertises the matching session capability and additionally routes foreground child replay and live updates on the durable child session id. Spawn and terminal state stay on the direct parent route; child transcript, tools, reasoning, plan, and usage stay on the child route. Background children remain on the durable task/job lifecycle and are never represented as foreground native subagents. |
| File edits and diffs | edit, write, and apply_patch expose filenames through standard title (for example Editing main.rs) and absolute locations. Complete typed arguments use the native patch grammar for intended targets; partial input retains Editing files, and relative targets do not borrow the process cwd. Titles show at most three filenames and 160 Unicode characters plus an included (+N more) suffix. Completion uses observed mutation paths ahead of requested aliases. A successful native mutation publishes only stable ACP diff content with exact oldText/newText; original success text remains in rawOutput. Pre-write failures retain actionable text without fabricated diffs; uncertain mutations remain failed with observed paths/diffs and _meta.zuno.outcome: "uncertain". Permission requests, live delivery and replay share the filename policy; replay still filters unsafe locations. |
| Human input | Ordinary tool permission uses session/request_permission; questions use the shared durable QuestionPort. Root Plan/Work/Goal sessions can publish optional question_async; required Work/Goal input records an exact human wait. Forms use native single/multiple controls and custom input. Drafts persist separately from answers; empty acceptance and native cancellation defer without consent. questions/list and questions/respond support later responses with stable IDs and revisions. Plan authorization remains explicit and handoff-gated. Delegated children report blockers to the parent. Permission grants remain separate, session-owned and cleared on close; question replies never create permission grants. |
| Cancellation | Explicit $/cancel_request withdraws only the request's own pending contribution or atomically targets its selected native input; duplicate observers have no withdrawal authority. Reused wire IDs receive distinct internal request identities. session/cancel accepts optional _meta.zuno.expectedTurnId and validates the target at the signal boundary. Legacy session-only cancellation captures the live target at dispatch, never arms a future turn, and cannot infer network-stale intent. EOF/write failure uses a separate disconnect callback: it drops transport observation, not durable input. Subsequent native process shutdown settles its actual interrupted outcome separately. No disconnected observer fabricates completion or replays consumed input. session/close joins root-owned work before disposing host/MCP/client resources. |
| Durable load replay | Reconstructs a bounded retained suffix of user/assistant content, reasoning, tools, raw input/output, safe typed diffs and locations, resource links and image output, followed by the current plan and latest-context usage. Question and delegation tools replay as static cards while retaining raw details. When native subagents are negotiated, the durable child tree is restored in parent-before-child order and historical terminal state is conservatively disconnected. Omitted history is reported explicitly. |
| ACP-provided MCP | Advertises stdio and Streamable HTTP; SSE remains false. New/load/resume validate the complete client list. Load/resume freeze it without starting transports; activation mounts an isolated bundle, publishes only after every required ACP server connects and discovers successfully, and rolls partial startup back in reverse order. Host-configured optional MCP may use an identity-bound cached tool directory and singleflight first-call connection; no stdio process is pooled across sessions. |
| Client filesystem RPC | Not advertised. Agent file reads and writes use Zuno tools, sandbox/permission policy, and durable events; they do not masquerade as ACP client filesystem handlers. |
| Terminal RPC | Not advertised. Zuno will not emit terminal references until create/output/wait/kill/release ownership and cancellation are implemented as one lifecycle. |
Zuno steering extension
Plan snapshots retain _meta.zuno.{planId,revision,title,stackDepth} and optional goalId / parentPlanId. Entries retain _meta.zuno.stepId; removing a Plan sends empty entries with _meta.zuno.cleared: true. These projection contracts do not depend on a particular tool call or on the RPC that admitted an input.
ACP V1's PromptResponse reports processing completion, not admission. Concurrent session/prompt therefore waits for the native receipt's actual terminal outcome instead of returning busy or inventing a stop reason. Zuno additionally advertises _meta.zuno.steering and accepts session/steer with a mandatory expectedTurnId; turn-scoped updates expose that id in _meta.zuno.turnId. The method returns immediately after durable admission and exact-turn soft-interrupt routing, or fails with -32002 and a typed reason. Steering does not send session/cancel, add a cancellation grace period, or create a second turn lifecycle.
The reference is Codex eaa8b6d91701d6cabe464141facc677e5915fbfc: protocol/src/turn_input.rs::TurnInputSubmission, app-server/src/request_processors/turn_processor.rs::turn_start_inner and turn_steer_inner, and core/src/session/mod.rs::SessionIo::submit_turn_input (all under codex-rs). They separate accepted/steered input, exact-turn preconditions, and a receiver's lifetime from actual execution. Codex turn/start returns inProgress; Zuno adapts that separation to ACP's terminal prompt response and adds its own durable per-input ledger. This is not wire compatibility or a claim that Codex implements Zuno's database idempotency.
This extension does not change current Zed behavior by itself. A Zed client must detect the capability and use session/steer for follow-up input instead of sending session/cancel; the Stop action should continue to cancel.
Only user-audience notices are projected as tagged thought chunks. Diagnostic notices, including historical tool-declaration repair, are de-duplicated and logged without entering the ACP conversation.
Native cancellation boundary
SessionControl::abort_turn checks the expected turn under the registry lock that fires its signal. Input cancellation uses the native driver's selected input binding; the ACP driver installs that binding before durable promotion. Cancelling a pending contribution and then checking its bound input covers the promotion race without retargeting another input.
Local cancellation snapshots identify the registry, session, lease and turn/input identity generation. TUI captures a snapshot before dispatching a component event, then applies only the Stop requests emitted by that event. It neither queues a session-only abort for an asynchronous worker nor reuses it on the next event. Lifecycle teardown interrupts active work without arming a future turn. The ACP extension and legacy limits are documented in the exact cancellation contract.
Draft native-subagent extension
Native subagent projection is an adapter extension reviewed against the pinned codex-acp implementation; it is not part of Zuno's stable ACP V1.21 contract. Zuno therefore enables it only after direct two-sided negotiation:
{
"clientCapabilities": {
"subagents": {}
}
}2
3
4
5
The initialize response then includes:
{
"agentCapabilities": {
"sessionCapabilities": {
"subagents": {}
}
}
}2
3
4
5
6
7
Zuno does not recognize product-private _meta aliases as capability negotiation. Without this direct object, clients receive the stable task tool-call card only. With it, a foreground child adds:
subagent_spawnedon the direct parent session;- ordinary user, assistant, thought, tool, plan, and usage updates on the durable child session;
- one
subagent_state_updateon the parent after all queued child output has drained.
The parent session/prompt response is held behind that drain barrier, so the client cannot observe a completed parent while child output is still queued. Live terminal states are completed, failed, or cancelled; reloaded historical children use disconnected, because process liveness cannot be reconstructed from SQLite alone. Zuno advertises child cancel and close as false until those methods have real durable handlers. Nested foreground children follow the same direct-parent routing. Background work remains a durable job and never enters this foreground stream.
resource_link remains typed through ingress, SQLite, compaction, goal continuation, steering, and load replay. Text-only provider protocols lower it only at the provider boundary. This prevents Zed from reopening a thread with lossy prose in place of the original URI metadata. Replay applies a separate safety filter: remote links remain typed, while a local file link remains actionable only when it still resolves to a regular file inside the active worktree. Stale, external, and symlink-escaped local links become explanatory text.
Embedded text is bounded to 50 KiB and 2,000 lines per resource. Inline or embedded images must be valid base64 PNG, JPEG, GIF, or WebP payloads no larger than 5 MiB; the ACP transport also enforces its 8 MiB frame ceiling. Binary embedded resources other than images are rejected instead of injecting opaque base64 into model context.
Session-provided MCP
The production initialize response advertises stdio: true, http: true, and sse: false. The client supplies the complete standard mcpServers array on every new, load, or resume request. A load or resume therefore rebuilds only the resources declared by that request and never revives a prior process.
Names use [A-Za-z0-9_-]{1,32}. Other non-empty names are converted to a stable slug with an eight-character digest suffix; collisions after normalization are rejected. A stdio command must be an absolute path and runs with the session directory as cwd. HTTP accepts only absolute HTTP(S) URLs. Environment entries, header names and values, and case-insensitive duplicate header names are validated before the session becomes visible.
Client MCP servers are required profile effects. The session connects and discovers all of them before atomically publishing their tools. Any startup failure disposes already-started servers in reverse order. Close, activation failure, process EOF, and profile replacement use the same exact shutdown path. Commands, environment values, and headers are held only in process memory and have redacted Debug; they are not stored in SQLite or logs. Discovered tool schemas and later attempts still use Zuno's ordinary durable authority, permission, and result records.
Restore and teardown safety
Zed may retain an external-Agent process and its last selected thread after the Agent panel is hidden. Hiding the panel is therefore not treated as session/close. Zuno contains that client behavior at the ACP boundary:
session/loadandsession/resumevalidate their complete client MCP list, resolve session configuration, and transactionally rebuild theTurnHosttogether with the Zuno-configured and client-provided MCP set before publication. An active root Goal is then scheduled through the detached continuation observer without requiring a prompt.- A later load or resume for the same durable session replaces the prior process-local resources with the newly supplied complete list. Replay remains bounded and is not sent by resume.
- One stdio connection retains at most 32 open ACP sessions. A request that would exceed the bound fails explicitly instead of growing an unbounded host and MCP registry.
- Transcript hydration starts after the durable compaction boundary and keeps at most the newest 512 retained messages. Stored part hydration and total replay projection are limited to 16 MiB, with an 8 MiB per-update frame ceiling. An explicit omission message reports how many durable messages were left out. SQLite sizes stored part blobs before Rust JSON hydration, so an oversized message is omitted without first allocating its tool output or attachment payload.
- Replayed diff paths, written-file locations, and local file links are canonicalized. Only existing regular files inside the active worktree remain actionable. Missing files, external paths, and symlink escapes are filtered; omitted local resource links become non-actionable text.
session/closefirst makes the in-process session terminal, stops any active or just-admitted prompt, serializes against replay and activation, then shuts down the host and MCP runtime. A teardown-only pending interrupt is cleared so reopening the same durable session is not immediately cancelled.- Clean stdin EOF gives accepted requests whose responses are already ready a bounded 25 ms drain. Requests still blocked on work or human input are cancelled, including their request-scoped agent-to-client children.
These controls prevent a restored thread from eagerly recreating every runtime or replaying an unbounded set of stale actionable paths. They do not mutate Zed's workspace database, clear Zed's last_active_thread, or remove watchers that Zed created independently. An activated idle session remains mounted until Zed sends session/close or the stdio process ends; there is no timer-based idle demotion.
Zed setup and verification
The canonical operator guide is Use Zuno in Zed through ACP. It contains the current Zed agent_servers entry, Linux/macOS/Windows executable discovery, Zuno configuration overlays, deep and other Agent selection, permission ownership, logging, troubleshooting, and a copyable acceptance sequence.
This design document remains the protocol and architecture authority. Provider keys, OAuth state, model routing, Agents, Skills, extensions, permissions, and MCP configuration remain Zuno-owned rather than Zed-owned.
Run this acceptance sequence from a Zed External Agent thread:
- Start a Zuno thread and confirm the mode, Agent, model, and reasoning selectors are populated from Zuno, including
planin the Agent selector. - Open
/completion and verify native/compact,/goal,/plan,/start-plan, and/start-work, configured commands, and unambiguous Skills appear exactly once. - Execute
/goal verify ACP shorthand,/goal edit verify ACP actions, and/goal show; verify both direct-objective and action forms produce an Agent message and the slash text does not enter model input. Submit/goal createwithout an objective and verify ACP returns invalid params rather than an internal session error. - Select the Plan Agent and verify Zed receives Plan mode plus synchronized config updates. Select
deepand verify Build mode. Then execute/start-plan, create a durable Plan, and verify the bottom Plan panel appears before the prompt completes. Patch multiple revisions and verify the panel updates in place without reverting to an older revision, then execute/start-work. - After enough conversation history exists, execute
/compact; verify it ends normally, persists a summary, and does not send/compactas model input. - Add an image, selection, and branch diff; verify the selected model receives the supported content without a protocol error.
- Ask for a read-only inspection and verify reasoning and tool details stream without corrupting stdout JSON-RPC.
- Delegate one foreground child. If Zed negotiated native subagents, verify spawn appears on the parent, transcript/tools appear on the child route, and terminal state arrives before the parent prompt completes. Otherwise verify the stable task card remains complete and usable.
- Delegate one background child, close the root session, and verify the job is cancelled and joined without a native foreground-child stream.
- Request a file creation under strict permission policy, answer the Zed permission card, and verify the card names the target as
Editing <filename>in both execution and completion updates, has standardlocations, shows only the nativeA/M/Ddiff, and has no duplicate success sentence. Trigger one pre-write conflict and one uncertain mutation fixture; verify the former shows actionable recovery without a fabricated diff and the latter shows failed status, observed paths, and the uncertain outcome. - In Plan, ask a structured question with at least two options and verify Zed renders clickable choices plus an
Otherinput. Submit an answer, confirm the question remains visiblyin_progresswhile the model continues, and confirm it becomescompletedbefore the next committed assistant output. - Cancel a running prompt and verify the thread returns to an idle state.
- Install or rename one project Skill while the ACP session remains open and verify
/completion changes without restarting Zuno. - Close and reopen or import the thread, then verify content, question/task cards, child history, diff, plan, resource link, and usage are replayed once.
- Load the same open session again and verify the transcript is not duplicated.
Use Zed's dev: open acp logs command when diagnosing framing, ordering, or capability problems. ACP stdout is protocol-only; diagnostics belong on stderr or in Zuno's configured logs.
If the captured JSON contains a valid sessionUpdate: "plan" but the Agent panel does not render it, preserve the raw frame and treat that as a client compatibility defect. Zuno must not fabricate an Agent chat message to imitate the missing Plan UI.
The repository covers the same path without a GUI through production stdio tests:
cargo test -p zuno-acp
cargo test -p zuno --test acp_stdio2
Those tests drive initialization, lifecycle, dynamic reasoning configuration, command publication, mode/model replacement, rich prompt parsing, prompt streaming, strict permission, form elicitation, typed creation diff, resource-link roundtrip, request-to-session cancellation, durable load replay, plan, and usage. A release is not allowed to claim Zed UI acceptance until the manual sequence above has also been run against the intended Zed build and platform.
Audited snapshot
The snapshot layout is:
docs/upstream/acp/
├── LICENSE
├── README.md
├── SHA256SUMS
├── manifest.json
└── assets/
├── stable/
│ ├── meta.json
│ ├── meta.unstable.json
│ ├── schema.json
│ └── schema.unstable.json
└── v2-preview/
├── meta.json
├── meta.unstable.json
├── schema.json
└── schema.unstable.json2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
meta.json and schema.json are the stable release outputs. meta.unstable.json and schema.unstable.json are retained because they make preview/stable movement auditable; retaining them does not enable unstable features in Zuno. The V2 directory is similarly reference-only.
The manifest separates:
- immutable release identity: tag, annotated tag object, and peeled commit;
- release artifacts: asset ID, exact URL, byte size, and SHA-256;
- observations: fetch timestamp and upstream main commits at that time;
- Zed evidence: exact commit, source path, line, and requested protocol version;
- licensing: SPDX identifier, source URL, snapshot path, and checksum.
Refresh contract
The two update entry points implement the same contract:
./scripts/update-acp-spec.sh --verify
./scripts/update-acp-spec.sh --check-upstream
./scripts/update-acp-spec.sh --refresh2
3
pwsh -File scripts/update-acp-spec.ps1 -Mode Verify
pwsh -File scripts/update-acp-spec.ps1 -Mode CheckUpstream
pwsh -File scripts/update-acp-spec.ps1 -Mode Refresh2
3
They fail closed when:
- a pin is malformed;
- a tag is absent, is not annotated, or peels to an invalid commit;
- a GitHub release is missing or names a different tag;
- any required asset is missing or duplicated;
- an asset URL leaves the expected official repository;
- GitHub omits a SHA-256 release digest;
- downloaded size or SHA-256 differs from release metadata;
- the pinned commit does not contain the Apache-2.0 license;
- the pinned Zed commit cannot be read or no longer shows the V1 minimum and initialization request;
- the checked-in file set, checksum list, manifest, or upstream bytes differ.
Refresh stages every input under a temporary directory and copies files into the repository only after all remote checks pass. It does not resolve latest. By default it reproduces the pins already stored in the manifest. A version review supplies explicit ACP_STABLE_TAG, ACP_CRATE_TAG, ACP_PREVIEW_TAG, and ZED_COMMIT values, runs Refresh, reviews the manifest and schema diff, and then runs CheckUpstream.
The POSIX script supports Linux and macOS and requires curl, jq, and standard POSIX utilities. The PowerShell script supports Windows with PowerShell 7 or later. GITHUB_TOKEN is optional and is used only as an authorization header for GitHub rate limits.
Review checklist for a future pin change
- Read the ACP release notes and protocol documentation for every stable addition, removal, or stabilization.
- Review the schema diff between the old and candidate stable assets.
- Inspect the candidate Zed commit and confirm which protocol version its production connection requests.
- Review exact candidate commits for
codex-acpandclaude-agent-acp, then update the behavior-reference table without using floating branches. - Run
Refreshwith all reviewed schema and Zed pins explicitly provided. - Inspect
manifest.json,SHA256SUMS, and all schema diffs. - Classify each protocol change as implemented, intentionally unsupported, or preview-only in the ACP implementation plan.
- Run both platform scripts in offline verification mode; run at least one online
CheckUpstreamon each release platform before publishing.