Image Candidate Protocol v2

September 10, 2026 ยท View on GitHub

Status: implemented bounded candidate protocol; HOSTED GREEN under the v0.4.0 release baseline. Broader support and programme completion remain separate from this release evidence.

Audience: agent builders, compiler contributors, and reviewers.

The host explicitly starts semaprax serve-candidates <manifest> or opens an ImageSession with ImageHostCapability::CandidateOnly. This selects semaprax.image-agent-protocol.v2; the existing serve-image command and read-only v1 method/catalog/result bytes are unchanged. No request can switch profiles or acquire source-write, runtime test, process, or build authority.

Capabilities and lifecycle

V2 adds candidate_prepare to semantic_read. It inherits v1's host-bound manifest, strict duplicate-key codec, 65,536-byte request frames, 1,048,576-byte response frames, silent non-executing notifications, and held-source authentication. Every admitted operation authenticates the original Project before execution and after rendering its complete response. Any observed source drift permanently invalidates all candidate and draft access.

Candidates and drafts are immutable process-local objects. A successful operation returns a new digest handle and retains its predecessor unchanged. No human source file, Git state, lock, managed generation, or disk cache is written. EOF drops the registries after the final authentication check.

There are at most 16 candidate entries and 16 draft entries. A conservative 256 MiB serialized-report budget includes each candidate report and every draft's report plus its privately retained last-valid candidate report, even when the corresponding public candidate handle was discarded. Shared Arc objects can be counted more than once. This is not a total-memory bound for retained HIR. Exact duplicate handles reuse existing entries.

Registry mutation follows preparation, semantic validation, capacity admission, complete response bounding, and the final held-input recheck. Any failure leaves registry entries unchanged. An oversized v2 result returns -32001 without mutating the registry and permits a smaller follow-up query; oversized input still terminates. Discard removes only the named handle. Sibling candidates and drafts that retain their own private candidate remain usable.

Methods

All new methods require the exact startup image_revision. Candidate methods also require candidate_revision except initial open, recovery restore, and validation catalog. Draft methods require draft_revision except initial hole creation. These are content digests, not filesystem paths or authority credentials.

MethodAdditional parametersResult
candidate/opennoneCompact unchanged candidate handle
candidate/apply-intentcandidate_revision, structured intentNew independently source-validated candidate handle
candidate/querycandidate_revision; optional offset, chunk_bytesBounded UTF-8 chunk of exact canonical candidate report
candidate/recovery-exportcandidate_revision; optional offset, chunk_bytesExact canonical complete-candidate capsule UTF-8 chunks
candidate/recovery-restorestructured capsuleReplayed candidate handle from this session's admitted original base
candidate/validatecandidate_revisionIndependent replay of retained changes from base, tests not run
candidate/impactcandidate_revision, target; optional depth, max_bytes, max_nodesCandidate-bound Project semantic impact
candidate/comparecandidate_revision, other_candidate_revisionExisting descriptive same-base comparison
candidate/mergecandidate_revision, other_candidate_revisionReconciled candidate plus bounded report; shared original base retained
candidate/rebasecandidate_revision, new_base_candidate_revisionReplayed candidate on the selected retained candidate's admitted revision
candidate/discardcandidate_revisionConfirmation that only that handle was removed
change/catalogcandidate_revision, targetTarget-specific constructor discovery; arbitrary payload legality is not promised
expression/catalogcandidate_revision, targetRevision-bound expression identities, expected types, scope, and replacement eligibility
protocol/constructor-schemasnone beyond image_revisionSelf-contained closed typed-expression, intent, and change-envelope JSON Schemas
validation/catalognoneAvailable independent replay and external gates still required
hole/opencandidate_revision, target, hole_id; optional draft_revisionNew draft with a body hole, or new sibling with another hole
hole/querydraft_revision, hole_idBound typed context for one unresolved hole
hole/filldraft_revision, hole_id, structured expressionNew draft after full existing candidate admission
hole/completedraft_revisionCandidate handle only when zero unresolved holes remain
hole/discarddraft_revisionRemoves only the named draft

intent is passed through SemanticChange::new with the selected candidate's current Project revision and compiler-owned mandatory requirements. It is not a graph mutation, source string, or arbitrary patch. The existing semantic change implementation owns the closed nested constructor grammar.

Merge requires the library's shared original-base and conflict checks; its resulting source diff remains anchored to that original base. Rebase selects only an existing candidate registry entry's admitted Project revision. Neither accepts source bytes, a path, a caller-created HIR, or an unregistered revision. Both routes replay through the existing candidate source and target checks. Their response combines a compact candidate handle and the exact reconciliation report, capped at 65,536 bytes before wrapping. Report overflow, conflict, registry capacity, or authentication failure leaves every registry entry unchanged. The complete resulting candidate report remains available through the existing chunk query. These are conservative bounded operations, not a general source merge or behavioral-equivalence proof.

candidate/query starts at byte offset zero. chunk_bytes is 1024โ€“65,536 (default 16,384). Offsets must be UTF-8 character boundaries within the canonical report; the response supplies next_offset until complete. Concatenating chunks reproduces the exact candidate report including its terminal LF. The digest binds every chunk selection; clients cannot select an arbitrary file. Impact uses the existing context byte/node/depth bounds and adds the selected candidate handle without changing the nested Project impact schema.

Additive v5 candidate/impact-summary and candidate/impact-page methods provide compact, candidate/query-bound navigation over the exact final candidate impact artifact. Project Candidate Impact Navigation v1 owns their bounds, opaque references and strict nonclaims. They do not change the v2 candidate/impact request or payload bytes.

hole/open without a draft begins from the specified candidate; with a draft, that draft must belong to the same original candidate. Hole IDs are bounded to 128 ASCII identifier bytes; nested hole context and constructor limits belong to Project Candidate Holes v1. A draft never exposes materializable source or a candidate revision for its incomplete state. Its last-valid candidate can escape only through successful completion with no remaining holes. Failed fills preserve the original draft and siblings.

Discovery and nonclaims

The merged v2 method catalog drives dispatch, parameter schemas, query discovery, version-matched instructions, and generated TypeScript/Python/Rust source helpers. V2 results use semaprax.image-agent-result.v2; discovery payloads use their additive .v2 schemas. Existing semantic payload schemas stay unchanged. Closed RPC envelopes retain their existing constructor URN references. protocol/constructor-schemas supplies the matching self-contained closed documents described in Candidate Constructor Schemas v1. These describe structural grammar; the compiler still owns lexical, scope, type, effect, and ownership admission. Complete nested semantic response/HIR schemas and a runtime JSON Schema validator are not bundled. Generated helpers perform no transport or filesystem I/O themselves.

Candidate validation reparses canonical source and independently replays existing compiler-owned target projections. It does not execute generated code, run Project tests, establish behavioral equivalence, approve changes, or commit source. Required external gates remain reported as unrun for that preview. This report field does not classify the compiler's released regression corpus. Comparison remains descriptive; callers must explicitly select the separate merge/rebase methods for their bounded reconciliation behavior.

tests/image_protocol/candidate_transport_v2.rs covers profile separation, catalog/client consistency, immutable siblings, canonical report chunks, replay validation, invalid-intent atomicity, stale handles, hole completion boundaries, candidate/draft capacity, retained draft lifetime after candidate discard, absorbing Unix source drift, retained-base merge/rebase, closed constructor documents, and expression discovery. These implemented regressions have hosted-green v0.4.0 release evidence.

Caller-managed recovery

Complete candidate recovery capsules are content-addressed replay inputs. candidate/recovery-export derives the whole bounded capsule before slicing; chunk size is an output bound, not a work bound. candidate/recovery-restore canonicalizes its structured object and independently replays it against this session's startup Project revision. The existing strict 64 KiB request limit applies, including the JSON-RPC envelope; use the CLI for larger capsules. Restore does not accept another retained candidate as a base. A capsule originally based on a derived/rebased revision needs that exact source revision independently admitted by a new host session.

Restore prepares a complete typed candidate and bounded response before registry mutation and final held-source authentication. Malformed/stale/full-registry failures leave existing handles untouched. A source-drift failure permanently invalidates the session as before. No method in this v2 profile persists or restores drafts, unresolved holes, approvals, capabilities, session state, or warm HIR. The implemented v5 draft-recovery and archive routes retain their separate startup, replay and completion boundaries.