VAEP Specification v1.0.0-rc3 — Consolidated Redline, revision 12 (freeze candidate)
Status: Freeze candidate. The normative text is complete. Freeze still requires the artifact items of the §13 checklist (OPEN-1, OPEN-2, OPEN-3 and the regenerated protocol/*.json files), which are not text.
Revision history: Revisions 2–11 each answered the freeze review of the revision before. Revision 12 answers the freeze review of revision 11 and the amendment set proposed against it (§15).
Base: VAEP v1.0.0-rc2 (Oct 10, 2026). Supersedes: the earlier rc3 draft "Protocol, Authorization, Lifecycle and Evidence-Binding Amendment", in full. Date: 2026-10-11.
rc3 makes VAEP's execution model safe for money and other irreversible effects, and makes the agent interface machine-checkable. It does six things:
- It corrects two unsafe rc2 rules immediately (§1).
- It fixes how a business operation is identified, so two agents cannot execute the same refund twice.
- It counts every unresolved obligation against monetary bounds.
- It admits each external effect for transmission at most once.
- It prevents an entity from claiming an external effect completed before the provider confirms it.
- It defines every lifecycle, effect state and error code as data, not prose.
#0. How to read this document
0.1 Authority. rc3 = rc2 + this redline. Every rc2 requirement not listed in the §2 supersession index stays normative and unchanged. This document governs only the IDs listed there. Any other conflict with rc2 is an erratum and blocks freeze.
AMD-01 P Precedence (amends §0). The order of authority is:
- JSON Schemas and protocol state-machine files (Appendix A);
- VPL grammar and semantics;
- normative prose;
- informative material, including examples and the Agent Guide.
A contradiction among items 1–3 blocks release.
AMD-02 P Single source. Four tables in this document are generated from the same source as four protocol files:
| Table | File |
|---|---|
| CR lifecycle (§6) | protocol/cr-lifecycle.json |
| Effect lifecycle (§5.4), including every expanded LR pair (EFS-07) | protocol/effect-lifecycle.json |
| Error registry (§8) | protocol/errors.json |
| Risk-floor registry (§9) | protocol/risk-floors.json |
A table here that differs from its file is a defect.
AMD-03 P Fail closed. Sometimes VAEP cannot establish an authorization decision, an execution outcome, an artifact identity or a lifecycle transition. When that happens, it refuses the unsafe operation, records why, and reports the uncertainty through the protocol. An unestablished outcome is never treated as "did not execute."
VRF-01 P Verification method (amends CNF-01). Every normative requirement declares exactly one verification method:
- suite S — an executable conformance test;
- deployment D — evidence from the installation;
- process P — an audit record.
Every suite requirement maps to at least one executable test. Every security-critical suite requirement also has a negative control (CNF-08). A requirement without a declared method is a defect in this specification.
#1. Immediate errata to rc2
These two corrections are issued now as rc2 errata under GOV-04, because rc2 as published is unsafe in both places.
- Each takes effect on publication.
- Implementations have 30 days to apply them, but the rc2 conformance claim is withheld from publication until an installation has applied both.
- Until then, an installation MUST disable autonomous execution of actions with money effects, or document an equivalent compensating control in its conformance report.
- Both errata are carried into rc3 unchanged.
#ERRATUM-1 — Access-token jti versus DPoP proof jti
Original (AUTH-02): "…every jti and DPoP proof jti MUST be rejected on reuse within the token lifetime."
Defect: Rejecting reuse of the access token's jti makes every grant single-use. That contradicts the 15-minute grant lifetime and RFC 9449, under which one DPoP-bound access token is presented on many requests, each with a fresh proof.
Replacement (AUTH-02, corrected): Grants MUST be sender-constrained with DPoP (RFC 9449) using server-issued nonces. The access token's jti identifies the token for revocation and audit. The gateway MUST NOT reject a request solely because the token's jti was seen before. Each DPoP proof's jti MUST be rejected on reuse within the replay-retention window (AUTH-16). The remaining AUTH-02 text is unchanged.
Fixtures:
- add test
access_token_multi_request_accepted; - add test
dpop_proof_replay_rejected; - add a negative control: with the proof replay cache disabled, the replay test fails;
- retire any rc2 fixture asserting rejection of access-token
jtireuse.
Migration: Drop access-token jti entries from the replay cache and keep proof jti entries. No data migration.
#ERRATUM-2 — Budget reservation released on timeout
Original (AUTH-11): "…committed on success, and released on failure or after 10 minutes."
Defect: A reservation can be released while its effect has been dispatched or may have completed, for example with a slow provider or a crashed worker. The released amount can then be spent again, so cumulative caps such as total refunds can be exceeded.
Replacement (AUTH-11, last sentence), self-contained for rc2: A reservation is never released by elapsed time. It is:
- committed on recorded success of its action;
- released on a recorded rollback, or on a recorded provider response documented as non-executing;
- otherwise held for human reconciliation.
In rc3, EXE-07 supersedes this sentence with the full reservation state machine.
Fixtures:
- add test
reservation_not_released_after_commit_timeout; - add fault-injection boundaries 5, 6 and 9 (CNF-09);
- add a negative control: restoring the 10-minute release makes the cap-overspend test fail.
Migration: Before the erratum counts as applied, audit every reservation that was released by timeout while an outbox row or provider request existed. Re-reserve or reconcile each one.
#2. Supersession index
| rc2 item | Disposition | By |
|---|---|---|
| AUTH-02 | Corrected | ERRATUM-1, AUTH-15, AUTH-16, AUTH-17 |
| AUTH-11 (last sentence) | Corrected | ERRATUM-2, EXE-07 |
| AUTH-03 | Retained; delegation clarified | AUTH-20 |
| §7.2 approval column | Amended | AUTH-20, AUTH-21 |
| CR-07 | Retained; computation defined | RSK-01 – RSK-05 |
| §8 lead paragraph ("any failed gate sends it back to Draft") | Replaced | LC-10 |
| LC-01 | Amended: transitions are those in protocol/cr-lifecycle.json |
LC-08 |
| LC-03 | Replaced | LC-13 |
| LC-04 | Amended | LC-14 |
| REV-03 | Retained; "Held" now names only the reconciliation state | LC-12 |
| EFF-04 | Retained; Halt also moves the CR to Suspended | LC-12 |
| CON-09 | Amended | EXE-02 |
| EFF-01 | Amended | EXE-03, EXE-04 |
| §5.3 aggregate example and Appendix A built-ins | Amended (the example used a filter the grammar cannot express) | CON-23 |
| §5 business-request entities | Amended: platform-managed quarantined state |
CON-24 |
| IDN-02 kind set | Amended | IDN-10 |
| MCP-01 | Amended: cr.withdraw permitted |
MCP-14 |
| MCP-02 | Amended | MCP-06, MCP-07, ERR-01 – ERR-05 |
| §17 tool table | Replaced | §7 |
| SEC-03 | Amended | ART-06 |
| CNF-01 | Amended | VRF-01 |
| §20 | Amended | GUA-02 – GUA-06 |
| RP-4 and EFF-08 (restore) | Amended: requests, effects and reservations are reconciled before dispatch resumes | EXE-11, EXE-19 |
| IDN-09 ("CR returns to Validated") | Amended: transitions T8, T9, T12, T26 | LC-08 |
| AUTH-12 ("halts in-flight CRs at their next gate") | Amended: revocation acts immediately | LC-15, AUTH-22 |
| LC-05 | Amended: serialization also covers Suspended CRs | T10 guard |
§10.2 res claim |
Amended: res also names environments |
AUTH-18 |
| §5.2 ("each transition names the Action that triggers it") | Amended: platform-event transitions permitted | CON-19, CON-20, CON-24 |
| §8 (gate failure returns the CR to Draft; CR content editable) | Replaced: CRs are immutable and failures are terminal. This is a substantive change to audit and recovery behavior, not a clarification | LC-10, §13 step 5 |
| LC-06 (approved advances at Live) | Retained; LC-16 confirms a revert never moves it | LC-16 |
| AUD-02 | Amended: rejection records carry a reason class (invalid_content or integrity_incident) |
ART-03 |
| IDN-09 (Live CR whose evidence key is revoked) | Amended: stays Live; incident and drift | LC-14 |
| §16.1 role table | Amended (see the role changes below this table) | EXE-01, EXE-13, EXE-17, CON-19, CON-24, EXE-11 |
| EFF-04 ("pause outbox dispatch") | Retained; implemented as deferral, never blocked |
EFS-06 |
§16.1 vaep_app grants |
Amended: no DELETE on vaep_obligation, vaep_invocation or effect rows |
CON-21 |
| §11.2 adapter proxy | Amended: the proxy admits a call only by a single-use transmission append against a ledger sent head, and never retries a forwarded call |
EXE-18 |
| Agent Guide (rc2) | Superseded; must be regenerated from rc3 schemas | §13 |
§16.1 role changes.
vaep_ctl(new role). It may updatevaep_invocation.stateandfencing_token. It may write every effect-row state. It may write the obligation statesconfirmedandreleasedand the obligation'squarantinedflag. It may updatemanaged_by: state_machinefields only forconfirms_effect,on_effect_*and quarantine (CON-24) transitions. It is never present in app, worker or agent runtimes.- Restore job. It runs as
vaep_owner. - Proxy ledger credential (new). It may append
transmissionrecords only (EXE-17).
rc3 adds no CR operation types. GOV-03 requires new ops to ship first as extensions in at least two installations (OPEN-4).
#3. Authorization (amends §10)
#3.1 Scopes and authority separation
AUTH-18 S Scope names. The scope names are exactly the rc2 names: read:contract, propose, read:cr, act:<action>, read:runtime. rc3 adds none.
proposenever authorizesaction.invoke.- An
act:<action>grant'sresclaim MUST name the permitted action IDs, environments and resource scope. - Its
objclaim binds the grant to one objective. - The execution budget stays in the server-side ledger and is never carried in the token (§10.2 unchanged).
- Holding several scopes does not merge decisions. Each request is authorized only against the scope its tool requires (§7.2).
AUTH-19 S No production effect from authoring. No cr.* tool causes a production business action. Verification runs only in environments whose identifiers differ from production, against sandboxes or recorded fakes (EFF-06). A production invocation requires an act:<action> grant whose res names the production environment.
#3.2 Tokens and DPoP
AUTH-15 S Token reuse. An unexpired, unrevoked access token MAY authorize any number of requests within its scope, audience and objective. Token revocation and proof replay detection are separate mechanisms.
AUTH-16 S Proof uniqueness. Every DPoP proof carries a unique jti. The gateway validates:
htmandhtu;ath(the access-token hash);- the current server nonce;
iatwithin a declared clock skew of at most 60 s.
An accepted proof jti MUST be rejected for the replay-retention window, which is at least the token lifetime plus the skew.
Replay detection is atomic across all gateway replicas in one acceptance domain, meaning every replica that accepts tokens for the same aud value.
A new proof never creates a new business operation: business identity is fixed by EXE-02.
AUTH-17 S Replay-cache failure. If the gateway cannot determine whether a proof was already accepted, it rejects the request with E_INTERNAL_UNAVAILABLE. A cache outage never disables replay protection.
#3.3 Approval
AUTH-20 S Delegated R2 approval. Delegated auto-approval applies to R2 only.
- Who. A named service principal (the delegate), declared in the human-signed objective together with its risk ceiling (R2) and the maximum number of CRs it may approve.
- Who it cannot be. The delegate MUST NOT be:
- the authoring agent;
- any agent in the author's delegation chain;
- any principal whose code, policy or configuration lies within the objective's declared entities, actions or policies.
- Its policy. Changes to the delegate's policy are R4 under AUTH-09. The delegate's decision is a deterministic policy over the evidence bundle. It approves only when every check passed, computed risk is exactly R2, and there is no T or A element in the CR's assurance graph. The policy version is recorded in the approval record (ART-02).
- Assurance graph. It is the set of elements introduced by the CR, plus, for every action in
affected:- that action's declared effects;
- the policy rules that permit it;
- the invariants whose predicates read a field it writes;
- the integration adapters its effects use.
- Fallback. Without a valid delegation, R2 needs one human approval.
- Never for R3/R4. Delegation never satisfies R3 or R4.
AUTH-21 S Effective approval. The effective approval requirement is the strictest of:
- the computed risk class minimum (§7.2);
- the objective's restrictions;
- the conformance level's autonomy limit (§18);
- privileged-change rules (AUTH-09, CON-08, DJ-05);
- any incident or recovery rule.
A conformance level may permit an approval mechanism but never waives the §7.2 minimum.
#3.4 Revocation
AUTH-22 S Effect of revocation. Revocation takes effect within at most 5 s (AUTH-12). From then on, it blocks new admissions, lifecycle advancement and effect dispatch.
- Recheck. Every dispatch rechecks authorization immediately before authorizing the send (F1b).
- Refusing unsent effects. When revocation takes effect, the control plane appends
refusedfor every effect under the revoked objective whose effective head (EXE-12) is any of: none,continued,send_revoked,pre_sendorsent(F2, F2b, F2e). Arefusedappended against asenthead makes the proxy'stransmissionappend fail (EXE-18). So revocation that lands before transmission admission always prevents the send. - Already transmitted. Revocation does not undo an effect whose head is
transmissionor later. That effect continues through the §5.4 states and is reported truthfully. - Committed but unsent. Revocation after the business transaction commits but before transmission leaves the effect
blocked. The business entity stays in its pending state (CON-19), so it never claims an execution that did not happen.
AUTH-23 S No resurrected authority. Workers act under the invocation's objective. Once that objective is revoked, no worker may dispatch, retry, compensate or cancel under it.
The recovery authority.
- It is the objective's human owner, or a named human principal designated by a separately signed incident objective. It is always a human principal.
- An agent working under an incident objective may propose recovery steps but can never act as recovery authority.
- It is the only principal that can decide to continue, cancel or reconcile blocked, suspended or quarantined work, whether or not the objective is revoked.
- Attesting a
reconciliation_requiredoutcome also requires the R3 approver role (F15, F16). - Its decisions are executed by the control plane (EXE-17).
Continuing a blocked effect (F17). When the recovery authority continues a blocked effect, the effect is re-bound to that authority. Its next F1b recheck then tests all of the following:
- the designating incident objective (or the owner's objective) is active and unrevoked;
- the recovery authority principal is still named in it;
- the
continuedrecord is the predecessor of the dispatcher'spre_send.
No act: grant is involved: the human never invokes the action, and the platform dispatches a committed effect. Each such step's audit record names the deciding principal.
#4. Business requests and money (amends §5)
CON-17 S Business-request entity. Every action that declares an E2 or E3 effect MUST take as input the ID of a business-request entity declared in the contract (for example refund_request).
- Separate creation. The request entity is created by a different action from the effecting action, and each requires its own
act:scope. Whether one principal may hold both scopes is the objective's decision. An objective that grants both to one principal says so in its signed text (AUD-02 records it). - Own state machine. The request entity has its own state machine.
- Executable state. The effecting action's precondition requires the request to be in an executable state.
- Executed at most once. The effecting action's transition moves the request out of that state. Because the request state is
managed_by: state_machine(CON-05), one request is executed at most once.
CON-18 S Uniqueness is declared, never inferred. Every business-request entity MUST declare uniqueness as one of:
- A list of fields. The platform compiles it as a partial UNIQUE constraint (class M) over requests not in a
failed,cancelledor declared withdrawn state. That scoping is the normative form, so a new request after a definitive failure is not a duplicate. A request in the platformquarantinedstate (CON-24) is inside the constraint. - The literal
"none", with a one-line rationale.
Rules:
- Omitting the declaration is a validation error at
/entities/<entity>/uniqueness. - The contract summary lists every
"none"declaration. - A declared uniqueness key is compiled as a block invariant with a stable ID.
- Any change to the key other than removing fields from it is a weakening under CON-08 and is R4 (§9). That includes adding a field, adding any condition beyond the normative scoping above, and switching to
"none". - A second legitimate partial refund on the same invoice is a distinct request with a distinct ID. It is bounded by CON-21, not by uniqueness.
CON-19 S Completion is confirmed, not assumed.
Confirmation states. A state may be marked confirms_effect: <effect_id>. A transition into such a state happens only through the platform's confirmation event when that effect reaches succeeded. Three changes are one database transaction (EFS-04):
- the effect's move to
succeeded; - the obligation record's move to
confirmed(EXE-13); - the entity's transition.
No action, escape hatch, schedule or migration may write a confirmation state.
Pending states. An E2/E3 action moves its entity to a pending state that is not so marked.
- The pending state's only outgoing transitions are the platform's confirmation event and the
on_effect_failedandon_effect_cancelledevents. - No action, schedule, escape hatch or migration may move an entity out of it.
Validator. It rejects:
- an E2/E3 action whose own transition targets a
confirms_effectstate; - an E2/E3 action whose entity has no
confirms_effectstate for that effect; - any other transition out of a pending state.
CON-20 S Failure paths are declared. Every E2/E3 action MUST declare on_effect_failed and on_effect_cancelled transitions from its pending state.
- Target. Each target MUST be a state the effecting action's precondition does not accept. A failed request is never re-executed; a retry is a new request (
E_REQUEST_RETRY_NOT_PERMITTED). - Trigger. Only platform events trigger these transitions, under the platform service principal, and they are audited.
- Validator. It rejects an action that omits either transition or whose target is executable.
CON-21 S Pessimistic accounting.
Declaring the bound. Every business-request entity whose effecting action has a money(currency) effect MUST declare bounded_by as one of:
{ relation: <relation to the bound owner>, bound: <field on the bound owner> };- the literal
"none", with a rationale. The contract summary lists it.
A request carries exactly one money effect (EXE-13).
The generated invariant. When a bound is declared, the platform generates a block invariant with a stable ID over the monetary obligation records that reference the bound owner:
sum_where(owner.obligations, o, o.state != "released", o.amount) <= owner.<bound>
What the bound means. The bound field is the lifetime total the owner allows across all its requests. An example is invoice.refundable_amount, the amount originally refundable on the invoice.
- It is not a running balance and is never reduced to reflect confirmed obligations. Confirmed obligations stay in the sum, so nothing is counted twice and nothing is omitted.
- The validator rejects a contract in which any of these writes the bound field: a
confirms_effecttransition, anon_effect_*transition, the creating action, or the effecting action. - Any other write to the bound field is an ordinary contract-declared write, serialized by the lock protocol below. A decrease that would place the sum above the new bound is rejected by the invariant.
What counts. Every obligation not in released counts. That includes:
requested,reservedandconfirmedobligations;- obligations whose reservation is held (EXE-07);
- quarantined obligations (CON-24).
An obligation is released only by one of:
- evidence of non-execution (EFS-02);
- declared withdrawal before commit (EXE-13);
- a recovery-authority attestation of non-creation (EXE-19).
A reversal of a confirmed obligation (for example a chargeback) is a separate request, with its own obligation against its own bound. The original stays confirmed.
The obligation record. It is a platform table in the application database (vaep_obligation), owned by vaep_owner.
| Column | Notes |
|---|---|
tenant_id |
|
bound_owner |
typed reference |
| request reference | |
amount |
|
currency |
|
state |
one of requested, reserved, confirmed, released |
quarantined |
boolean |
- It is created in the same transaction as the business request, in state
requested. - A request has exactly one obligation, and the obligation is in exactly one state, so it is counted at most once.
amount,currency,bound_owner,tenant_idand the request reference are immutable after creation, enforced by avaep_ownertrigger.vaep_appholds no DELETE onvaep_obligation,vaep_invocationor effect rows (§16.1 amended). Deletion of these rows is impossible from any runtime role.- The invariant compiles only if the bound's currency matches; mixing currencies is a compile error (CON-03).
- If
sum_where's predicate yields null or the error value for a row, the row is counted (CON-23).
Tenancy. In a multi-tenant application, the aggregate is per tenant_id, and the bound owner is always within that tenant. An application that declares a cross-tenant bound is outside the rc3 conformance claim (OPEN-5).
Fail-closed blocking. This can block a legitimate refund while another refund is unresolved. That is the intended fail-closed behavior.
Coverage. The invariant compiles under §5.3.1, with trigger coverage on the request table, the obligation table and every table that writes the bound field (CON-14).
Lock protocol (normative for class M).
- Lock row. Each bound owner has one row in
vaep_agg_lock, keyed by (tenant_id, bound owner). It is created in the transaction that creates the bound owner, so acquisition is always an UPDATE, never an insert race. - Which transactions lock. Every transaction that inserts a counted obligation, or writes the bound field, executes
UPDATE vaep_agg_lock … SET version = version + 1for that bound. It then evaluates the invariant by a statement that starts after that UPDATE returns. Transactions that only confirm or release an obligation never increase the sum, and need not lock. - Under READ COMMITTED. The aggregate read is a new statement with a new snapshot, so it observes every transaction that committed before the lock was granted.
- Under REPEATABLE READ and SERIALIZABLE. A concurrent transaction may have updated and committed the lock row after this transaction's snapshot was taken. In that case the UPDATE fails with a serialization failure, so a stale snapshot can never pass the check.
- Retries. Serialization failures, deadlocks and lock timeouts abort the business transaction. The Executor retries it under the same invocation attempt and fencing token, up to a declared limit. When the limit is exhausted, the attempt ends
failed_without_effectwithE_INTERNAL_UNAVAILABLE(retry classlater). An aborted attempt is never treated as a commit. - Commit. A commit succeeds only when the aggregate observed after acquiring the lock, including the new obligation, does not exceed the bound.
CNF-13 requires scripted interleavings that demonstrate this at all three isolation levels. Acquiring the lock is not sufficient evidence on its own: each schedule must show that the aggregate observed by every successful commit includes every concurrent committed write.
CON-22 S Two counters, both must pass. The objective ledger (AUTH-06, AUTH-11) and the contract invariants (CON-21) are independent.
- An invocation proceeds only if both admit it.
- A refusal returns the refusing counter's code:
E_BUDGET_EXHAUSTEDorE_INVARIANT_VIOLATION. - Nightly reconciliation compares the ledger's committed and held amounts with the effects they reference. Any mismatch is reported as drift (RUN-03) through
incident.get.
CON-23 S VPL filtered aggregates. rc2's §5.3 example (sum(payments.amount where invoice=X)) cannot be expressed in the Appendix A grammar. rc3 adds two built-ins:
sum_where(v.rel, x, pred, x.field)count_where(v.rel, x, pred)
They are ordinary call productions, so the grammar is unchanged. For sum_where, a row whose predicate is null or the error value is included (fail closed). The §5.3 example is rewritten with sum_where.
CON-24 S Platform quarantine state. The validator adds a platform-managed state quarantined to every business-request entity. A contract may not declare transitions into or out of it.
- Not executable. A quarantined request is never executable. Invoking its effecting action returns
E_BUSINESS_REQUEST_STATEwith retry classafter_reconciliation. - Still counted. It counts against uniqueness (CON-18), and its obligation (with
quarantined = true) counts against its bound (CON-21). - Two exits. Its only exits are platform events executed by the control plane on a recovery-authority decision (EXE-19):
request_reconstructedmoves it to the entity's declared created state. Its obligation staysrequested, withquarantined = false.request_voidedmoves it to the entity's declared withdrawn state, and its obligation isreleased.
Unmediated channels. A refund issued directly in the provider's own dashboard never passes through VAEP and is invisible to CON-21. That channel is unmediated (§11.2). The contract summary lists it under EV-03, and non-guarantee 4 of §20 applies.
#5. Execution model (new §11A; amends CON-09, EFF-01, AUTH-11)
#5.1 Invocation record
EXE-01 S Record before execution. The invocation record is a row in the application database (vaep_invocation, owned by vaep_owner).
What vaep_app may do. Column privileges and a constraint trigger, compiled under §5.3.1 and owned by vaep_owner, limit vaep_app to:
- inserting a row in
admitted; - moving
admitted→committed, oradmitted→failed_without_effect, by a CAS under the row's currentfencing_token.
Setting state := abandoned, and any change to fencing_token, are permitted only to vaep_ctl. A vaep_app write that violates this fails at the database (CNF-08 negative control).
One live attempt per identity. A partial UNIQUE constraint on (application, environment, action, key) applies over rows not in abandoned or failed_without_effect. For E0/E1 actions, key is (objective, client key). This makes two live attempts under one identity impossible: the second insert fails, and the caller receives E_INVOCATION_IN_PROGRESS or the durable outcome.
Admission order.
- The gateway reserves budget (AUTH-11).
- The gateway appends an
admittedledger record. It carries the invocation ID, identity key, canonical input hash, action, environment, objective and reservation ID. - The row is inserted, carrying the reservation ID.
The gateway's ledger credential is one vaep_app never holds. A row with no unvoided admitted ledger record is a forgery: it is reported as drift (RUN-03f) and never dispatches (F1). In particular, after the control plane voids an admitted record (admission-lease expiry, EXE-07), any later row carrying that reservation ID is a forgery.
Row contents. Before business execution begins, the row is recorded in its own transaction, in state admitted, with:
invocation_id, principal, objective, action and environment;- the business-request ID (E2/E3) or idempotency key (other actions);
- the canonical input hash (JCS + SHA-256, §6);
- the authorized resource scope, the expected contract revision and the execution status.
The reservation is keyed by invocation_id and carried in the inserted row. It is never written to the row afterwards.
#5.2 Identity and atomicity
EXE-02 S Idempotency identity. The identity is (application, environment, action, key).
- For E2/E3 actions,
keyis the business-request ID (CON-17). It is never a value minted by the agent. - For E0/E1 actions,
keyis a client-supplied idempotency key, additionally scoped by objective. - The principal is never part of the identity, so any authorized agent retrying the same request resolves to the same invocation.
- Same identity with the same input hash returns the durable prior outcome or the current status.
- Same identity with a different input hash returns
E_IDEMPOTENCY_CONFLICT.
Permanent input binding. The ledger keeps one identity chain per identity.
- Every
admittedappend is a conditional append on that chain. - The first
admittedrecord binds the identity to its canonical input hash, permanently. - The ledger rejects any later
admittedrecord whose hash differs (E_IDEMPOTENCY_CONFLICT). This holds even after every prior attempt becameabandonedorfailed_without_effect. - Because the ledger is outside restore scope (EXE-14), the binding survives every restore.
The proof boundary. The identity names one logical operation. Every attempt under it is recorded permanently, including failed ones. A new attempt under the same identity is admitted only when every prior attempt is proven non-executed. That means:
- no invocation row for the identity shows a commit;
- the ledger holds no
invocation_committedrecord for the identity; - the ledger holds no effect record beyond
pre_sendorvoidfor the identity.
The ledger checks are keyed by the identity's key: the business_request_id for E2/E3 actions, and the idempotency key otherwise. They are never keyed by invocation_id.
Rollback alone is sufficient only because EXE-03 makes dispatch impossible before commit. Any attempt not meeting the proof is treated as in progress, and the caller receives E_INVOCATION_IN_PROGRESS or the durable outcome.
No second effect identity. effect_id is derived solely from (invocation_id, declared effect name), per EXE-04. A new invocation is admitted only on the proof above. So no permitted retry can mint a second effect_id for a logical operation whose first attempt may have executed.
EXE-03 S One transaction. For a given invocation, these happen in one database transaction:
- the business mutation;
- a compare-and-swap of the invocation row from
admittedtocommittedunder the attempt's fencing token (EXE-09). A row alreadyabandoned, or carrying a newer token, aborts the transaction; - for an action with a money effect: a CAS of the request's obligation from
requestedtoreserved(withquarantined = false), as an UPDATE that must affect exactly one row, or the transaction aborts. The effecting action's compiled precondition also requires such an obligation. A money request with no obligation row is therefore unexecutable. Non-money E2/E3 actions have no obligation and no monetary reservation (EXE-07); - the CON-21 lock protocol, when a money obligation is reserved;
- creation of every effect record the action declares, and only those, in
pending_dispatch(EXE-15); - creation of every outbox row.
A committed mutation without these records is impossible by construction.
After commit. The Executor reports the commit, and the control plane appends an invocation_committed ledger record carrying the commit watermark (EXE-12). Until that record exists, the effects stay pending_dispatch and no F1 may run.
Repair scan. On every control-plane start, and at least every minute, the repair scan does the following:
- For each committed invocation row lacking
invocation_committed: if the row has an unvoidedadmittedrecord, it appendsinvocation_committed. Otherwise it raises drift and quarantines the row (CNF-09 boundary 5). - It voids orphaned
pre_sendrecords (F1c, F1d). - It appends
send_revokedforsentheads whose lease has expired with notransmission(F39; boundary 12).
Restore job. The EXE-11 restore job runs as vaep_owner inside the Executor (§16.1). It is the only role that may insert invocation, effect, obligation or request rows outside the action decorator. Fixtures assert this under crash injection.
EXE-04 S Effect identity and adapter declarations.
Effect identity. Every external effect has effect_id = the IDN-02 identifier of kind eff over (invocation_id, declared effect name), with no nonce. The same logical effect therefore has the same ID in the application database, the ledger and any restore. When the provider supports idempotency keys, the adapter sends a provider key derived deterministically from effect_id.
Adapter declarations. Every adapter declares the following. All of it is part of the manifest digest (ART-01).
provider_idempotent: true | false— whether the provider deduplicates requests by the derived key. rc3 never re-dispatches an effect after transmission (EXE-05). This declaration therefore affects only reconciliation and the GUA-03 statement of provider-side execution count.non_executing_responses— a closed set of response classes (status codes, error codes) the provider documents as meaning the operation did not execute.lookup_finality: true | false— whether the provider contract documents that a lookup by the derived key returns a final answer. "Final" includes any request the provider accepted before the lookup, whether or not it had finished processing.- the deadlines in EFS-05.
A response outside non_executing_responses is never failed. It leads to outcome_unknown (F7) or provider_pending (F6).
EXE-05 S Ambiguous outcomes. Any transmitted effect whose outcome is not definitively established enters outcome_unknown (F7, F10).
- No re-dispatch. VAEP never issues a new
effect_idfor the same logical effect. In rc3 it never re-dispatches an effect whose ledger chain contains atransmissionrecord: no transition leads fromtransmissionback topre_send(EXE-12). - How it resolves. Only by provider evidence (F8, F9), by reconciliation lookup (F11, F12), or by human attestation (F15, F16).
Opening a lookup. Before any lookup, the reconciler appends an open_lookup record naming the effective head, which must be transmission or a prior open_lookup.
- The ledger accepts that append only if the fencing token on the effect's
sentrecord has no live lease. Either the lease expired, or the sender recorded its outcome. - A live lease proves only that no other worker may start a send under that token. It never proves that an already-transmitted request has finished.
What a lookup can prove.
-
Execution (F11). A provider confirmation by the derived key, or by the stored provider reference, establishes execution.
-
Non-execution (F12). This requires both:
- the response class is in
non_executing_responses; and - the adapter declares
lookup_finality: true.
A "not found", "not yet processed" or equivalent response is otherwise inconclusive.
- the response class is in
Recording the verdict. The verdict is a terminal record naming the open_lookup head. A repeated lookup appends a new open_lookup naming the previous one.
Closing reconciliation. Inconclusive lookups may repeat until reconciliation_deadline. The control plane then appends lookup_closed, and the effect moves to reconciliation_required (F14). If the provider offers no lookup by key and no provider reference exists, lookup_closed is appended at once: the effect is never re-dispatched to find out.
EXE-06 P No exactly-once claim. VAEP claims exactly-once execution at the provider only where the provider's contract documents it. The default guarantee is GUA-03.
EXE-07 S Reservation states. Reservations move through reserved, committed, released and held. Reservation transitions are recorded in the ledger by the control plane, referencing the record that justifies them.
| From | To | When |
|---|---|---|
| reserved | committed | The invocation's money effect reaches succeeded. An invocation with no money effect reserves only a count, which commits when its business transaction commits. |
| reserved | released | The business transaction rolls back; or the invocation row is abandoned (idempotent repair); or the money effect reaches failed or cancelled while the reservation is still reserved. |
| reserved | released | No invocation row references the reservation when the admission lease (60 s) expires. The control plane first appends a void naming the admitted record, so a late row insert is a forgery under EXE-01 and never dispatches. It then releases. |
| reserved | held | The money effect enters outcome_unknown, reconciliation_required or blocked. Non-money effects never move a reservation to held. |
| held | committed | The money effect reaches succeeded, by any evidence-backed path. |
| held | released | The money effect reaches failed or cancelled, by any evidence-backed path. |
Rules:
- A
heldreservation staysheldthrough F17 (blocked→pending_dispatch) until its money effect terminates. - F39 (
send_revoked) never moves a reservation: the effect was never transmitted. - Cumulative caps count
reserved,heldandcommitted. - A reservation's monetary component belongs to exactly one money effect (EXE-13), so settlement is never partial.
- Every
heldreservation has an exit: its money effect terminates, or the invocation is proven non-executed.
EXE-08 S Retries are re-authorized. Every retry re-runs authentication, authorization, revocation and policy checks. Idempotency never bypasses authorization. A caller no longer authorized to see the result receives E_SCOPE_DENIED with execution_status: not_started for its own request, and nothing about the prior invocation (ERR-05).
EXE-09 S Fencing.
Tokens. Dispatchers and executors hold leases carrying a monotonically increasing fencing token. The control plane issues it, and it is stored on the invocation row (EXE-01).
- Every durable write to the invocation row, every effect-state transition in the application database, and the EXE-03 commit itself are compare-and-swaps against that row's token. The database rejects a write whose token is older than the row's.
- The control plane updates the row's token (and may mark it
abandoned) only by a CAS of its own. - The fencing-token counter is outside application restore scope (EXE-14), so a post-restore lease never reissues a token.
What prevents a stale send. The decisive condition is at the ledger, not the application-database row. The dispatch-intent ledger is append-only and head-linked (EXE-12):
- The
pre_send→sentappend names the dispatcher's ownpre_send. - The proxy's
transmissionappend names thatsentrecord (EXE-18). - Any of
void,send_revoked,refusedorcancelledappended in between makes the later append fail, and the actor must not proceed.
Lease expiry alone is never sufficient.
#5.3 Invocation status
EXE-10 S Derived from effects. Invocation state is derived from the business transaction and its effects. The order of precedence is: unknown, then partial, then terminal.
| Invocation state | Condition | execution_status |
|---|---|---|
| admitted | Recorded; business transaction not started | not_started |
| abandoned | Lease expired while admitted; fenced and terminal (ERRATUM-2) |
failed_without_effect |
| executing | Business transaction running; or committed with any effect not yet terminal, including blocked |
in_progress |
| completed | Committed; no effects, or every effect succeeded |
succeeded |
| failed_without_effect | Business transaction rolled back; no local state changed and nothing was dispatched | failed_without_effect |
| completed_external_failed | Committed (local state and E0/E1 effects applied); every external effect failed or cancelled; declared failure transitions applied |
completed_external_failed |
| partially_effected | Every effect terminal; some succeeded, others failed or cancelled |
partially_effected |
| outcome_unknown | Any effect outcome_unknown |
unknown |
| reconciliation_required | Any effect reconciliation_required |
unknown |
EXE-12 S Dispatch-intent ledger.
What it is. The control plane keeps an append-only dispatch-intent ledger. It is restore-independent (EXE-14) and distinct from the audit log (§15), which it feeds.
Record kinds.
| Group | Kinds |
|---|---|
| Request | request_created, request_committed, request_aborted, request_quarantined, request_reconstructed, request_voided, request_withdrawn |
| Invocation | admitted, invocation_committed |
| Per-effect | pre_send, sent, send_revoked, transmission, open_lookup, lookup_closed, refused, continued, void, and the terminals succeeded, failed, cancelled |
| Control | dispatch_frozen, dispatch_unfrozen (per application, EXE-11) |
Chains and the head CAS. Per-effect records form a chain per effect_id, and identity records form a chain per identity (EXE-02).
- Each record names its predecessor record ID.
- The ledger accepts an append only if the named record is still the chain's effective head. This is a true compare-and-swap on the head. State and token are carried in the record but are not the condition.
Effective head. The effective head is the latest record that is not void and has not been voided. A void record may follow only a pre_send. It voids that pre_send, so the effective head reverts to the pre_send's predecessor (or to none).
Permitted per-effect sequences (normative).
| Effective head | May be followed by |
|---|---|
| none | pre_send, refused, cancelled |
pre_send |
sent, void, refused, cancelled |
sent |
transmission, send_revoked, refused, cancelled |
send_revoked |
pre_send, refused, cancelled |
continued |
pre_send, refused, cancelled |
refused |
continued, cancelled |
transmission |
succeeded, failed, open_lookup, lookup_closed |
open_lookup |
succeeded, failed, open_lookup, lookup_closed |
lookup_closed |
succeeded, failed |
succeeded, failed, cancelled |
nothing (terminal) |
Build check. The build check on protocol/effect-lifecycle.json rejects any path that leads from a transmission record to pre_send, sent, send_revoked, refused or cancelled. Once a transmission has been admitted, the effect resolves only by evidence. A later human attestation of definitive non-execution produces failed, never cancelled.
Ledger-first rule. Every effect-row transition except F6, F7 and F10 is first a conditional append to the ledger, and only then a row write that records the appended record's ID. F6, F7 and F10 are row-level refinements within a transmission head. Because each append is conditional on the effective head, two actors racing for one effect cannot both succeed: the second append fails, and its actor stops. Examples of such races:
- a dispatcher sending while a recovery authority cancels;
- the proxy admitting while the control plane revokes.
The row in the application database mirrors the ledger and is never the deciding record.
Record contents. Every record carries:
- the idempotency identity key (
business_request_idor idempotency key) andtenant_id; - where applicable:
effect_id,invocation_id, the obligation (amount,currency,bound_owner,state), the request's uniqueness fields and declared pending state, and the provider idempotency key.
The ledger is authoritative for send state and for every decision that releases or confirms money.
Watermark. Every ledger record carries the application database's (timeline ID, commit LSN) at the time it is appended. A restore to point P on timeline history H defines a record as "after the restore point" if its position is later than P in H. This distinguishes records from an abandoned timeline after a second restore.
Request creation and commit. These are governed by EXE-19.
EXE-13 S One money effect. An action declares at most one effect whose amount is money(currency). Its reservation and obligation (CON-21) belong to that effect alone. Non-money effects never hold a reservation. A contract violating this is rejected with E_MULTIPLE_MONEY_EFFECTS.
Obligation lifecycle. The obligation record moves forward only:
| State | Reached |
|---|---|
requested |
at request creation, or when a quarantined request is created (CON-24) |
reserved |
in the EXE-03 transaction |
confirmed |
in the EFS-04 transaction on succeeded |
released |
by any of the paths below |
An obligation is released when:
- its effect reaches
failedorcancelled(EFS-04); - its request leaves its executable state for a declared non-executable terminal state, with no
invocation_committedfor the request. The action commits the entity transition; the control plane then appendsrequest_withdrawnand writesreleasedasvaep_ctl, audited; - its quarantined request is voided (
request_voided, EXE-19).
It never moves backwards: a forward-only constraint trigger owned by vaep_owner rejects any other change.
Who may write what.
confirmed,released, thequarantinedflag, and every effect state outside thevaep_appset are written only byvaep_ctl. Each such write records on the row the ID of the ledger record that justifies it.vaep_appcan write obligations only torequested(at creation) andreserved(in EXE-03).vaep_appcan write effect rows only topending_dispatch,pre_send,dispatchingandprovider_pending.- Adapters run as
vaep_appand report outcomes to the control plane, which performs every other write asvaep_ctl.
Both triggers are CNF-08 negative controls. The validator requires every business-request entity to declare at least one withdrawal transition.
EXE-14 D Restore-independent control state. The following are stored outside the application database and outside the scope of any application restore:
- the dispatch-intent ledger, including the identity chains and
transmissionrecords; - the fencing-token counter;
- the budget ledger.
An installation's conformance evidence names each store and shows that an application restore cannot roll it back.
EXE-15 S Only declared effects dispatch. An effect row is dispatchable only if (committed action, effect name) is in the contract revision the invocation row names. An undeclared effect row is drift (RUN-03a) and never reaches pre_send. The EXE-03 constraint trigger rejects inserting an effect row whose name the action does not declare.
EXE-16 S Amount binding. For the money effect, a constraint trigger owned by vaep_owner enforces effect.amount = obligation.amount and effect.currency = obligation.currency, at insert and on every update.
- The
pre_sendandsentrecords carry the amount. - F1b refuses to authorize a send if the amount differs from the obligation's.
- The proxy refuses to admit a call whose amount differs (EXE-18).
CNF-08 negative control: a vaep_app write of a different amount must fail.
EXE-17 S Append authority and role separation.
Who may append each record kind.
| Record kinds | Appended only by |
|---|---|
admitted |
gateway (gateway credential) |
pre_send, sent |
dispatcher (worker credential), each naming its own fencing token |
open_lookup |
reconciler (worker credential) |
transmission |
adapter proxy (proxy credential) |
invocation_committed, void, send_revoked, refused, continued, cancelled, lookup_closed, succeeded, failed, every request_* record, dispatch_frozen, dispatch_unfrozen |
control plane |
Any other appender is refused (CNF-08 negative control). A worker therefore cannot release capacity, revoke a send or record an outcome: it can only move an effect toward sent, or open a lookup.
Three distinct roles for every terminal transition, and for every move of an obligation to confirmed or released:
- Evidence producer. The adapter (provider responses) or the recovery authority (human attestation). It may only submit evidence or a decision request.
- Decision-maker. The platform policy, or the recovery authority where §5.4 requires one (F3, F3b, F3c, F15, F16, F17, F18).
- Durable transition executor. The control plane only. It verifies the evidence, conditionally appends the record naming that evidence, and only then applies the database state change (EFS-04).
No adapter, worker or recovery-authority credential may append a terminal record, write a terminal effect state, or write a confirmed or released obligation. A terminal record always names the evidence record it rests on: a provider response hash, a lookup response hash, or a human attestation.
EXE-18 S The proxy admits each effect for transmission at most once.
Admission conditions. The adapter proxy (§11.2) admits an outbound provider call only when all of the following hold:
- the call carries a provider idempotency key derived from an
effect_idwhose effective head issent; - that
sentrecord's fencing token has a live lease; - the call's amount and currency equal the
sentrecord's; - dispatch is not frozen for the application (EXE-11).
How admission works. Admission is the proxy's conditional append of a transmission record naming that sent record. The append succeeds only if sent is still the effective head.
-
On success, the proxy forwards the call exactly once. It never retries a forwarded call: transport-level retries in the proxy and its HTTP client are disabled, and a retry would be a second transmission.
-
On failure, it refuses and does not forward.
-
If it cannot determine whether its append succeeded (ledger unavailable, timeout, ambiguous response), it does not forward. Either outcome is then safe:
- if the append failed, the head is still
sent, and F39 later revokes it; - if the append succeeded, the head is
transmission, and the effect is treated as possibly sent (F7).
The proxy never forwards, and never admits again, on the basis of uncertainty.
- if the append failed, the head is still
Rules.
- The
transmissionrecord is the only way into atransmissionhead. - From
transmission, only the control plane (terminals,lookup_closed) or the reconciler (open_lookup) may append. - Cancellation and refusal are not permitted from
transmission. - Because EXE-12 permits no path from
transmissionback topre_send, eacheffect_idhas at most onetransmissionrecord, ever.
Limit of the guarantee. This guarantees at most one admitted transmission per effect. It does not guarantee that the provider executed the effect at most once (GUA-03).
CNF-08 negative controls:
- with the conditional append removed, two concurrent calls with identical arguments must be shown to produce two outbound transmissions;
- with forwarding-on-uncertainty enabled, a fault-injected append timeout must be shown to produce an unrecorded transmission.
EXE-19 S Request creation, commit repair and quarantine.
1. Before commit. The transaction that creates a business request (CON-17) obtains its durable transaction identity. This is an identifier whose commit status the database can later report; the installation's [D] evidence names the mechanism (in PostgreSQL, for example, pg_current_xact_id() with pg_xact_status()).
The control plane then appends request_created, carrying:
- the request ID and its contract revision;
- a creation sequence number, monotonic and assigned by the control plane;
- the transaction identity;
- the canonical reconstruction payload: a JCS-canonical, SHA-256 content-addressed record of every immutable normative field of the request entity, its declared created state and uniqueness fields, and for a money request the obligation fields (
amount,currency,bound_owner,tenant_id).
The transaction commits only after request_created is durable. If the append fails, the transaction aborts.
2. After commit. The control plane appends request_committed, naming the same transaction identity and carrying the commit watermark.
3. Repair scan. On every control-plane start, and at least every minute, the scan examines every request_created that has none of the resolution records request_committed, request_aborted, request_reconstructed or request_voided. It applies the first rule that matches:
| # | Condition | Action |
|---|---|---|
| a | The database contains the request row, with a matching payload hash | Append request_committed. |
| b | An invocation_committed record names the request |
Append request_committed, citing that record. The row is re-created per EXE-11 step 2 if it is missing. |
| c | The database reports the transaction identity as aborted (not used after a restore for transactions after the restore point) | Append request_aborted. |
| d | The database reports the transaction as in progress | Wait. |
| e | Otherwise: status unavailable, or the transaction lies after the restore point of a restore | Append request_quarantined. The control plane (or, during restore, the restore job) creates the request row in quarantined (CON-24) from the payload, with its obligation requested and quarantined = true. |
4. Resolution. A quarantined request is resolved only by a recovery-authority decision, citing evidence, which the control plane executes:
request_reconstructed— evidence that the request was genuinely created. The row moves to its declared created state, and the obligation staysrequestedwithquarantined = false.request_voided— an attestation of non-creation. The row moves to its declared withdrawn state, and the obligation isreleased.
Database transaction status read from an abandoned timeline is never evidence.
5. Never frees capacity. A quarantined request counts against uniqueness and against its bound at full amount until it is resolved. It is never silently discarded, and never reconstructed from partial data. Unresolved quarantine is reported as drift (RUN-03).
6. Completeness frontier. After a restore, dispatch does not resume (EXE-11 step 7) until every request_created record that meets either condition below has a resolution record or is represented in the database as quarantined:
- it lies after the restore point;
- it was outstanding at the restore point.
EXE-11 S Restores never re-send effects
After RP-4, or any restore or replication path that applies data with triggers disabled, the steps below run in order.
Step 1 — Freeze. The control plane appends dispatch_frozen for the application.
action.invokereturnsE_RESTORE_RECONCILING.- No F1 may run.
- The proxy refuses every admission for the application (EXE-18), including under leases issued before the restore.
Step 2 — Requests.
- For every
request_committedwhose request row is missing, the row is re-created from its canonical reconstruction payload in its declared created state, with its obligationrequested. - For every
request_withdrawn, the entity is moved to its withdrawn state (actorplatform, eventrestore), and the obligation isreleased. request_reconstructedandrequest_voidedrecords are replayed as in EXE-19 step 4.- Every unresolved
request_createdis classified by the EXE-19 repair scan.
Step 3 — Invocations. For every invocation_committed after the restore point:
- The invocation row is re-created as
committed, or anadmittedrow is CASed tocommitted, under a fresh fencing token. - Its business request is moved to the pending state the committed action declared (actor
platform, eventrestore), so no agent can re-execute it. - Its obligation is moved
requested→reserved. This is the only obligation write the restore job makes, and it is forward in the obligation's order. - Every effect the committed action declared is re-created in
pending_dispatch, under its deterministiceffect_id(EXE-04), with its outbox row.
A boundary-10 fixture asserts that the re-created ID equals the ledger's.
Step 4 — Normalize effect heads. For every effect_id with any ledger record, the control plane acts under a fresh fencing token:
- it appends
voidfor every effective headpre_send; - it appends
send_revokedfor every effective headsent.
No such send can still be admitted: the proxy is frozen, and its append against those heads now fails.
Step 5 — Replay effects. Every effect row is moved to the state its effective head maps to, by the ledger-replay transition (EFS-07). Obligations and reservations follow EFS-04 and EXE-07. A terminal row that disagrees with a terminal head is drift and keeps dispatch frozen.
Step 6 — Validate. Before dispatch is unfrozen, the restore job checks that:
- every obligation's
amount,currency,bound_owner,tenant_idand state match the ledger records for its request; - every CON-21 aggregate, including quarantined obligations, is within its bound;
- every uniqueness constraint holds.
The EFF-08 invariant sweep runs. Any mismatch is drift and keeps dispatch frozen.
Step 7 — Resume. The control plane appends dispatch_unfrozen only when both hold:
- steps 2–6 are complete, including the EXE-19 completeness frontier;
- every effect that step 5 placed in
outcome_unknownis terminal orreconciliation_required.
Reconciliation of a transmission record uses the provider idempotency key the ledger holds. Where the provider offers no lookup by that key and no provider reference exists, the effect goes directly to reconciliation_required (F14) for human resolution. It is never re-dispatched to find out.
#5.4 Effect lifecycle
EFS-01 S Exhaustive. The effect lifecycle below, mirrored in protocol/effect-lifecycle.json, is exhaustive. That includes every LR pair generated by EFS-07. Any other transition is rejected with E_LIFECYCLE_CONFLICT and logged as a security event.
Retired transition IDs are never reused: F1e, F2c, F2d, F13 and F19–F38. F19–F38 are replaced by the LR family.
States.
| State | Kind | Effective head(s) | Meaning | Counts against caps |
|---|---|---|---|---|
| pending_dispatch | initial | none, continued, send_revoked |
Created in the business transaction with its outbox row (EXE-03); not authorized for sending. | yes |
| pre_send | active | pre_send |
A dispatcher holds a fenced lease; the send is not yet authorized. | yes |
| dispatching | active | sent or transmission |
Head sent: send authorized, not admitted by the proxy, provably not transmitted. Head transmission: admitted; the provider call may be in flight. |
yes |
| provider_pending | active | transmission |
The provider accepted the request; the final result is not yet known. | yes |
| outcome_unknown | active | transmission or open_lookup |
The request may have executed; automated reconciliation is running. | yes |
| reconciliation_required | active | lookup_closed |
Automated reconciliation is exhausted; a human decision is required. | yes |
| blocked | active | refused |
Provably never transmitted; dispatch refused by revocation or policy denial (safety holds defer instead, EFS-06). | yes |
| succeeded | terminal | succeeded |
The provider confirmed execution. | yes |
| failed | terminal | failed |
The provider confirmed non-execution, or a human attested it. | no |
| cancelled | terminal | cancelled |
Never transmitted; cancelled by the recovery authority. | no |
Transitions.
| ID | From | To | Event | Decided by / executed by | Guard and ledger record |
|---|---|---|---|---|---|
| F1 | pending_dispatch | pre_send | lease acquired | dispatcher | Fencing token valid (EXE-09); dispatch not frozen; no deferral (EFS-06); unvoided admitted and invocation_committed records exist for this invocation; (invocation, effect name) declared by the committed action (EXE-15); effective head is none, continued or send_revoked; pre_send appended naming it, carrying the token and amount. |
| F1b | pre_send | dispatching | send authorized | dispatcher | Effective head is this dispatcher's own pre_send; amount equals the obligation's (EXE-16); authorization recheck passes (AUTH-22); sent appended naming the pre_send. An append failure means the head moved, and the dispatcher stops. sent authorizes; only the proxy's transmission admits (EXE-18). |
| F1c | pre_send | pending_dispatch | lease lost before send | control plane | Effective head is a pre_send whose token is not the row's current token, or whose lease expired; void appended naming it. Obligation and reservation unchanged (CNF-09 boundary 12). |
| F1d | pending_dispatch | pending_dispatch | orphan intent voided | control plane | A pre_send head exists under a token that is not the row's current one (crash between ledger append and row CAS); void appended naming it; audited self-transition. |
| F2 | pending_dispatch | blocked | dispatch refused | platform policy / control plane | Revocation or policy denial only; refused appended naming the effective head (none, continued or send_revoked) before the row write. |
| F2b | pre_send | blocked | dispatch refused | platform policy / control plane | As F2, naming the pre_send head. |
| F2e | dispatching | blocked | dispatch refused before admission | platform policy / control plane | As F2, naming a sent head; succeeds only if the proxy has not appended transmission. |
| F3 | pending_dispatch | cancelled | cancellation decided | recovery_authority / control plane | cancelled appended naming the effective head (none, continued or send_revoked) before the row write; the row records that record ID (EXE-13). |
| F3b | pre_send | cancelled | cancellation decided | recovery_authority / control plane | As F3, naming the pre_send head; any dispatcher append naming that pre_send then fails. |
| F3c | dispatching | cancelled | cancellation decided before admission | recovery_authority / control plane | As F3, naming a sent head; succeeds only if the proxy has not appended transmission, whose append then fails. |
| F39 | dispatching | pending_dispatch | unused send authorization revoked | control plane | Effective head is sent, and its token's lease has expired or is not the row's current token; send_revoked appended naming it; the proxy can no longer admit under it. Obligation and reservation unchanged. |
| F4 | dispatching | succeeded | provider definitive success | adapter evidence / control plane | Effective head transmission; provider response stored by hash; control plane verifies it and appends succeeded naming it (EXE-17). |
| F5 | dispatching | failed | provider definitive rejection | adapter evidence / control plane | Effective head transmission; response class is in non_executing_responses (EXE-04); control plane verifies it and appends failed. Any other response takes F6 or F7. |
| F6 | dispatching | provider_pending | provider accepted (async) | adapter | Effective head transmission; provider reference stored. Row-only refinement. |
| F7 | dispatching | outcome_unknown | timeout, transport error, crash or lease loss | adapter report / control plane | Effective head transmission: any case where the request may have been sent. Row-only refinement. |
| F8 | provider_pending | succeeded | provider final success | adapter evidence / control plane | Authenticated callback or lookup; terminal appended naming the effective head (EXE-17). |
| F9 | provider_pending | failed | provider final failure | adapter evidence / control plane | Authenticated callback or lookup whose class is in non_executing_responses; terminal appended naming the effective head. A weaker failure signal leaves the effect provider_pending until F10. |
| F10 | provider_pending | outcome_unknown | provider_pending_deadline exceeded |
control plane | Row-only refinement. |
| F11 | outcome_unknown | succeeded | reconciliation confirms execution | reconciler evidence / control plane | open_lookup head (EXE-05); lookup evidence stored (EFS-02); succeeded appended naming it. A provider callback may also satisfy this, naming the current effective head. |
| F12 | outcome_unknown | failed | reconciliation confirms non-execution | reconciler evidence / control plane | open_lookup head, accepted only if the sent token has no live lease (EXE-05); response class is in non_executing_responses and the adapter declares lookup_finality: true; failed appended naming the open_lookup. Otherwise the effect stays outcome_unknown until F14. |
| F14 | outcome_unknown | reconciliation_required | reconciliation exhausted | control plane | reconciliation_deadline exceeded, lookup inconclusive, or no lookup possible; lookup_closed appended naming the effective head. |
| F15 | reconciliation_required | succeeded | human attestation | recovery_authority (R3 approver role) / control plane | Evidence of execution recorded; succeeded appended naming lookup_closed and the attestation. |
| F16 | reconciliation_required | failed | human attestation | recovery_authority (R3 approver role) / control plane | Evidence of non-execution recorded; failed appended naming lookup_closed and the attestation. |
| F17 | blocked | pending_dispatch | continuation decided | recovery_authority / control plane | continued appended naming the refused head; effect re-bound to the deciding principal's authority; F1b then rechecks against that authority (AUTH-23). |
| F18 | blocked | cancelled | cancellation decided | recovery_authority / control plane | cancelled appended naming the refused head, before the row write. |
| LR | any non-terminal | mapped state | ledger replay | control plane | EFS-07. |
EFS-02 S Evidence required.
- Evidence for verdicts.
outcome_unknownandreconciliation_requiredmove tosucceededorfailedonly with a stored evidence record: a provider or lookup response (by hash), or a human attestation with evidence. A timeout, a missing callback or the passage of time is never evidence. - Proof of non-transmission. The only proof that an effect was never transmitted is an effect chain containing no
transmissionrecord.blockedandcancelledare reachable only from states whose effective head precedestransmission. The build check onprotocol/effect-lifecycle.jsonrejects any other source state. - Justification. Each such transition is justified by a
refusedorcancelledledger record, never by a row-level decision.
EFS-03 S What counts against caps. Every state except failed and cancelled counts against CON-21 invariants and ledger caps.
EFS-04 S Outcome transactions. In each case, the ledger's terminal record is appended first. Then, in one database transaction, the platform applies:
- On
succeeded: the effect'ssucceededstate, the obligation'sconfirmedstate, the entity'sconfirms_effecttransition (CON-19), and the reservation's move tocommitted. - On
failed: the effect'sfailedstate, the obligation'sreleasedstate,on_effect_failed(CON-20), and the reservation's release. - On
cancelled: the effect'scancelledstate, the obligation'sreleasedstate,on_effect_cancelled(CON-20), and the reservation's release.
A crash between the ledger append and the transaction is repaired on recovery by the LR transition (EFS-07; CNF-09 boundary 11).
EFS-05 S Deadlines. Each adapter declares:
provider_pending_deadline(default 24 h);reconciliation_deadline(default 4 h after enteringoutcome_unknown).
Fixtures drive both with injected time.
EFS-06 S Deferral is not blocking. A Held cohort (REV-03) or the EFF-02 canary rule withholds dispatch leases. The effect stays pending_dispatch with a recorded deferred_reason, its reservation stays reserved, and the platform clears the deferral when the condition lifts.
blocked is reserved for revocation and policy denial, which need a recovery-authority decision. This preserves rc2 EFF-04's "pause outbox dispatch" without creating per-effect human work.
EFS-07 S Ledger replay (LR). The control plane moves an effect row whose state is inconsistent with its effective head to the state the head maps to. Obligations and reservations follow EFS-04 and EXE-07.
Mapping from effective head to row state.
| Effective head | Consistent row states | Mapped state |
|---|---|---|
none, continued, send_revoked |
pending_dispatch | pending_dispatch |
refused |
blocked | blocked |
transmission |
dispatching, provider_pending, outcome_unknown | outcome_unknown |
open_lookup |
outcome_unknown | outcome_unknown |
lookup_closed |
reconciliation_required | reconciliation_required |
succeeded / failed / cancelled |
the same terminal | the same terminal |
pre_send and sent heads are never replayed. They are first voided or revoked: by EXE-11 step 4, or by F1c and F39.
When LR runs. Only in these cases:
- during EXE-11, with dispatch frozen;
- in the crash-repair scan, for a row lagging a head appended by the ledger-first rule (for example CNF-09 boundary 11).
Guards. An LR transition never moves a terminal row; a terminal row that disagrees with the head is drift. Its evidence is the head record itself.
Generated pairs. protocol/effect-lifecycle.json expands LR into one entry per (from, to) pair that the table makes reachable, with ID LR:<from>:<to>.
#6. Change Request lifecycle (replaces §8 lead paragraph; amends LC-01, LC-03, LC-04)
LC-08 S The state machine is normative. The lifecycle in protocol/cr-lifecycle.json is normative. Each transition names its event, actor class, guard and audit record. Any transition not listed is rejected with E_LIFECYCLE_CONFLICT and logged as a security event (LC-01).
The successful path has seven states: Draft, Validated, Verifying, Verified, Approved, RollingOut, Live. Suspended interrupts a rollout. Rejected, Withdrawn, Superseded and Reverted are terminal for that CR identity.
States.
| State | Kind | Meaning |
|---|---|---|
| Draft | transient | Recorded by cr.draft; validation pending. |
| Validated | path | Contract-valid against the current approved base. |
| Verifying | path | Submitted; V1–V10 running in the Verifier's trust domain. |
| Verified | path | All checks passed; signed evidence bundle exists; 72 h expiry running. |
| Approved | path | Approvals required by AUTH-21 recorded against this bundle and manifest. |
| RollingOut | path | The Executor is driving cohorts to the target revision. |
| Live | terminal-success | LC-06 convergence established in every environment and cohort. |
| Suspended | interruption | Rollout stopped for safety; awaits a human recovery or continuation decision. |
| ReVerifying | interruption | Re-verification of a Suspended CR against its unchanged manifest; its only exits return to Suspended, or lead to Superseded. |
| Rejected | terminal | Failed validation, verification, approval or artifact check. Successor CR required. |
| Withdrawn | terminal | Withdrawn by author or owner, or by objective revocation, before rollout. |
| Superseded | terminal | Approved base moved before rollout, or reconciled by a successor; evidence void. Successor CR required. |
| Reverted | terminal | Recovery returned every cohort to the base revision. |
Transitions.
| ID | From | To | Event | Actor | Guard |
|---|---|---|---|---|---|
| T1 | Draft | Validated | validation passed | platform | Contract schema valid; CON-01..13 and CR-01..08 pass (the V1 check set, run by the Change service, not the Verifier); base == approved, or base == the target of a Suspended CR that this CR declares reconciles: (LC-16) |
| T2 | Draft | Rejected | validation failed | platform | Any blocking finding, including E_STALE_BASE |
| T3 | Validated | Verifying | cr.submit |
author | submission_key unused or same draft; objective active with CR budget (AUTH-11); no escalation pending (LC-07); for R2+: no open drift, mutation block or quarantined test on affected entities (RUN-04, VER-04, VER-05) |
| T4 | Verifying | Verified | verification passed | verifier | V1–V10 pass; bundle signed and bound to manifest (ART-02) |
| T5 | Verifying | Rejected | verification failed | verifier | Any required check failed |
| T6 | Verified | Approved | approvals complete | approver | AUTH-20/21 satisfied; evidence unexpired; approvals bind bundle + manifest |
| T7 | Verified | Rejected | approval denied | approver | A required approver denies |
| T8 | Verified | Validated | evidence expired or signature invalidated | platform | 72 h after Verified (LC-14), or signing key revoked (IDN-09) |
| T9 | Approved | Validated | evidence expired or signature invalidated | platform | As T8; recorded approvals void |
| T10 | Approved | RollingOut | deployment started | executor | recovery_authority | ART-03 checks pass; no overlapping CR in RollingOut, Suspended or ReVerifying (LC-05). The one exception is the Suspended CR this CR declares reconciles:: then the recovery authority is the actor, and every cohort of the reconciled CR must have observed == its target (REV-02: at most base and target may run); otherwise the reconciling CR waits at Approved. No Held cohort and no open drift on affected entities; objective active |
| T11 | RollingOut | Live | convergence established | platform | LC-06 in every environment and cohort |
| T12 | RollingOut | Suspended | safety hold | executor | monitor | Any cohort Held (REV-03); EFF-04 Halt; objective revoked and the CR not re-bound by T33; or a signing key of its evidence or approval revoked (IDN-09) |
| T13 | RollingOut | Reverted | certified recovery complete | executor | Certified matrix row (EFF-04); observed == base in all cohorts |
| T14 | Suspended | RollingOut | continuation decided | recovery_authority | All cohorts Converged, each Held cohort repaired per LC-19 with its completion evidence recorded. ART-03 checks 2–7 pass against the current bundle for this manifest; a fresh bundle from T27 is acceptable; a check 5–7 mismatch is a security event forcing T15 or T27. The objective is active, or the CR has been re-bound to the recovery authority (LC-20). Authority valid (AUTH-23). If any check fails, the exits are T15 or T27 |
| T15 | Suspended | Reverted | recovery complete | recovery_authority | Observed == base in all cohorts (LC-16; REV-02 permits no other revision) |
| T27 | Suspended | ReVerifying | re-verification ordered | recovery_authority | Same immutable CR and manifest; evidence expired or invalidated; deployed and observed identities preserved |
| T28 | ReVerifying | Suspended | re-verification finished | verifier | Either a new bundle bound to the same manifest is recorded, or failure with reasons is recorded. Approvals against the new bundle are recorded while Suspended (LC-20) and gate T14, not T28 |
| T29 | ReVerifying | Suspended | approval denied or objective revoked | platform | A required approver denies, or the objective is revoked and the CR not re-bound by T33, during re-verification; verification job cancelled |
| T30 | Suspended | Superseded | reconciled by successor | platform | A CR declaring reconciles: this CR reached Live (LC-16); its cohorts are now on the successor target |
| T31 | ReVerifying | Superseded | reconciled by successor | platform | As T30; evaluated whenever the CR is Suspended or ReVerifying |
| T32 | Suspended | Suspended | approval recorded | approver | An AUTH-21 approval against the bundle produced in ReVerifying is recorded (LC-20); audited self-transition |
| T33 | Suspended | Suspended | re-bound to recovery authority | recovery_authority | The objective was revoked; the CR is re-bound to the deciding principal's authority (LC-20); audited self-transition |
| T26 | Verifying | Validated | signature invalidated | platform | A signing key used in this verification revoked (IDN-09); verification restarts on resubmission |
| T16 | Draft | Withdrawn | withdraw or objective revoked | author | owner | platform | – |
| T17 | Validated | Withdrawn | withdraw or objective revoked | author | owner | platform | – |
| T18 | Verifying | Withdrawn | withdraw or objective revoked | author | owner | platform | Verification job cancelled. A CR that has been RollingOut can never be in Verifying; it re-verifies in ReVerifying |
| T19 | Verified | Withdrawn | withdraw or objective revoked | author | owner | platform | – |
| T20 | Approved | Withdrawn | withdraw or objective revoked | author | owner | platform | Recorded approvals void |
| T21 | Validated | Superseded | base moved | platform | Approved revision != CR base (LC-13), unless the CR declares reconciles: a Suspended CR whose target is its base |
| T22 | Verifying | Superseded | base moved | platform | As T21 |
| T23 | Verified | Superseded | base moved | platform | As T21 |
| T24 | Approved | Superseded | base moved | platform | As T21 |
| T25 | Approved | Rejected | artifact mismatch | executor | ART-03 digest or manifest mismatch; rejection reason = integrity_incident (not invalid_content); logged as security event |
A transition attempt whose guard fails is rejected with the code that guard names, and the CR stays where it was. For example, cr.submit during open drift returns E_BLOCKED_DRIFT and the CR stays Validated; a later resubmission can succeed.
Actor classes.
platform— the Change service;author— the CR's authoring principal;owner— the objective's human owner;verifier;approver— a human, or the AUTH-20 delegate for R2;executor;monitor— the runtime monitor;recovery_authority— per AUTH-23.
LC-09 P rc2 mapping. rc2's lifecycle diagram (p. 15) is captioned "7 states, 2 failure exits," and its prose names only six states. rc3's table above is authoritative for rc3. OPEN-1 records confirmation of the diagram. If the diagram differs, §13 gains an explicit rc2 → rc3 state mapping, and the rc3 table does not change.
LC-10 S Immutability. A CR's normative content is fixed at cr.draft, and its ID is the content hash (IDN-02).
- Failed validation, verification, approval or artifact checks move it to Rejected with recorded reasons. It is never edited back into Draft.
- The author may create a successor citing
supersedes: <cr_id>. The successor is validated and verified independently.
LC-11 S Withdrawal. The author or objective owner may withdraw a CR with cr.withdraw from Draft, Validated, Verifying, Verified or Approved (T16–T20).
- Withdrawal voids recorded approvals and cancels any running verification.
- Once RollingOut begins, withdrawal is rejected. The only paths out are Live or recovery (T12–T15).
LC-12 S Suspended versus Held. At CR level, interruption is called Suspended. Held refers only to the per-environment/cohort reconciliation state (REV-03).
- Entry. A CR enters Suspended (T12) when any cohort is Held, on an EFF-04 Halt, or when its objective is revoked during rollout.
- What is preserved. Suspended preserves the deployed and observed revision identities.
- Exit. Suspended exits only by:
- a recovery-authority decision: T14 to continue, T15 to revert to base, or T27 to re-verify; or
- a reconciling successor reaching Live (T30).
- ReVerifying. Re-verification happens in ReVerifying. Its only exits are back to Suspended (T28 on completion, T29 on denial or revocation) or to Superseded (T31). So a CR that has been RollingOut can never reach a pre-rollout exit such as Withdrawn, Rejected or Validated, and expired or invalidated evidence never traps a Suspended CR.
- Serialization. A Suspended CR blocks overlapping CRs at T10, as RollingOut does (LC-05).
LC-13 S Rebase (replaces LC-03). When the approved revision moves past a CR's base before rollout, the CR becomes Superseded (T21–T24) and its evidence is void. A successor is drafted against the new base. A CR's base never changes in place.
LC-14 S Evidence expiry (amends LC-04). Evidence expires 72 hours after Verified, and the CR returns to Validated (T8, T9).
- Approvals recorded against the expired bundle are void.
- Re-verification produces a new bundle, and approval must be given again against it (ART-02).
- Revocation of a key that signed a Live CR's evidence or approval leaves the CR Live (terminal), but raises an incident and is reported as drift (RUN-03), so that a reconciling CR can re-establish evidence.
LC-15 S Objective revocation.
- Before rollout: the CR is Withdrawn (T16–T20, actor
platform). - During rollout: the CR is Suspended (T12), and the recovery-decision process of EFF-04 applies.
Revocation never itself authorizes a rollback.
LC-16 S Rollout failure.
- A certified automatic recovery that converges every cohort to base gives Reverted (T13).
- Otherwise the CR is Suspended (T12).
The recovery revision is always the CR's base. REV-02 allows at most the base and target app revisions to run at once. So while a CR is Suspended with cohorts on both, no other CR can deploy and the approved revision cannot move.
Recovering to a different revision. A recovery that needs a different revision is not a revert. Instead:
- A reconciling CR declaring
reconciles: <this CR>is drafted with base = this CR's target. - For such a CR, T1 and T21 accept that base in place of approved.
- T10 admits it under recovery authority while this CR stays Suspended. This is the one exception to LC-05.
- When the reconciling CR reaches Live, approved advances to its target, and this CR becomes Superseded (T30).
A Reverted CR never advances the approved revision. Live is reached only through LC-06.
LC-17 S Concurrency. Every transition is a compare-and-swap on lifecycle_version. The losing actor receives E_LIFECYCLE_CONFLICT with after_reread.
LC-18 S Agent visibility. cr.status returns the state, lifecycle_version and permitted_next_actions for the calling principal.
permitted_next_actionsis exactly the set of transitions inprotocol/cr-lifecycle.jsonthat meet all three conditions:- the principal holds the transition's actor class;
- its guard currently holds;
- the principal may invoke it through an MCP tool.
- Transitions whose actor is
recovery_authority, a non-delegatedapprover, orplatform, and that have no corresponding tool (MCP-14), are omitted. They are reported separately asexternal_decisions_requiredwhen relevant.
Agents act only on permitted_next_actions.
LC-19 S Repairing a Held cohort. A cohort leaves Held only by one of:
- (a) a certified automatic recovery step completing with observed == base for that cohort;
- (b) a recovery-authority continuation step completing with observed == target;
- (c) a recovery-authority decision to revert the whole CR (T15).
Each step writes a completion record naming the cohort, the before and after revisions, and the fingerprint evidence (REV-01). T14 requires such a record for every cohort that was Held.
LC-20 S Approvals and re-binding while Suspended.
- Approvals required by AUTH-21 against a bundle produced in ReVerifying are recorded while the CR is Suspended, by the approver actor, as the audited self-transition T32. MCP-14 is unchanged: no tool records them.
- The recovery authority may re-bind a Suspended CR whose objective was revoked to its own authority (T33), as F17 does for effects. T14 then proceeds without T12 re-firing on revocation.
#7. MCP interface (amends §17)
#7.1 Envelope and errors
MCP-05 S Schemas. Every tool has normative input and output JSON Schemas at mcp/<tool>.input.schema.json and mcp/<tool>.output.schema.json. Objects are closed (additionalProperties: false). Unknown fields are rejected with E_VALIDATION_FAILED.
Extension keys are the one exception. An extension key is any key beginning with x- (GOV-02). It is:
- accepted, but never validated;
- never stored in a normative artifact;
- never read by an authorization or execution decision.
A minor protocol version may add optional fields only. A client that does not know a field receives it under its own name and may ignore it. These three rules are the single definition that MCP-12 and GOV-02 refer to.
MCP-06 S Response envelope. Every response has this shape:
{ "protocol_version": "1.0.0-rc3",
"request_id": "<uuid>",
"status": "success | accepted | rejected | failed | indeterminate",
"result": {},
"error": null }
| status | Meaning | execution_status |
|---|---|---|
| success | Completed synchronously; result present | succeeded, or n/a for reads |
| accepted | Durable request admitted; outcome via cr.status or invocation.status |
not_started or in_progress |
| rejected | Refused before any business execution | not_started only |
| failed | Admitted, then ended with every effect terminal | failed_without_effect, completed_external_failed or partially_effected |
| indeterminate | Final outcome not established | in_progress or unknown |
Rules:
request_idis for correlation only and is never an idempotency key.indeterminateMAY carryerrorwithexecution_statusin {in_progress,unknown};resultthen carriesinvocation_id. No other status carries an error with those execution statuses.- On
rejectedorfailed,resultMAY carry the IDs of durable records created (for example thedraft_idof a Rejected draft). Otherwise it is null.
MCP-07 S Error object. The error object contains:
code,requirement_id,message;path(JSON Pointer);retry_class,execution_status;findings[], where each finding hascode,requirement_id,path,message, andseverityoferrororwarning;details.
cr.draft returns every validation finding in one response. The top-level code is E_VALIDATION_FAILED whenever any finding has severity error.
MCP-08 S Pinned pagination.
contract.getresolves exactly one immutable revision. The literalapprovedresolves on the first page.- Every page returns
revision,items,next_cursorandcomplete. - Cursors are bound to the revision, slice and authorization scope, and never advance to a newer revision. An invalid cursor returns
E_CURSOR_INVALID.
MCP-09 S Content-addressed drafts. cr.draft returns a draft ID equal to the CR's content hash, so identical drafts get identical IDs. It also returns:
- the computed affected set;
- the computed risk and the risk-policy version;
- the findings;
- the base revision;
- the required approval class.
Lifecycle, approvals, evidence and execution events are separate records that reference the immutable CR.
MCP-10 S Invocation input. action.invoke requires:
- action, environment, resource scope and typed inputs;
business_request_id(E2/E3) oridempotency_key(others);expected_revision.
The gateway rejects an invocation outside the grant's environment or resource scope.
MCP-11 S Durable status. Invocation status is retrievable independently of the original request, by invocation_id or by (action, key). It returns the §5.3 invocation state, execution_status, and per-effect states, with high-PII fields redacted.
MCP-12 S Versioning.
- Every request and response carries
protocol_version. - A minor version never changes the meaning of an existing field.
- A server rejects an unsupported major version (
E_PROTOCOL_VERSION). - Clients discover features through MCP-13, never by parsing version strings.
MCP-13 S Discovery. Tools and their input schemas are exposed through standard MCP tool discovery. A capability descriptor (protocol/capabilities.schema.json) names the supported schema versions, the error-registry version, the CR-lifecycle version and the effect-lifecycle version.
MCP-14 S What no tool does (amends MCP-01). No tool does any of the following:
- approve, deploy, roll back, or continue or revert a Suspended CR;
- attest an effect outcome;
- cancel or continue a blocked effect;
- resolve a quarantined request.
Those are gate, human or recovery-authority decisions. cr.withdraw is permitted.
#7.2 Tools (ten; replaces the §17 table)
| Tool | Scope | Required input | Returns | Possible status |
|---|---|---|---|---|
contract.get |
read:contract |
revision (ID or approved), selector, page_size, cursor |
revision, items, next_cursor, complete |
success, rejected |
contract.explain |
read:contract |
revision, element_id |
Ordered CR provenance chain with evidence references (AUD-05) | success, rejected |
cr.draft |
propose |
objective, base, intent, ops, expected_behavior, migration, recovery; optional affected and risk |
Draft ID, computed affected set, risk, risk-policy version, findings, required approval class, lifecycle state | success, rejected |
cr.plan |
propose |
draft_id |
Implementation plan and migration plan with artifact digests, compatibility analysis, effect classification, recovery matrix row | success, rejected |
cr.submit |
propose |
draft_id, submission_key |
Lifecycle state (Verifying) and lifecycle_version |
accepted, rejected |
cr.withdraw |
propose |
cr_id, lifecycle_version, reason |
Lifecycle state (Withdrawn) | success, rejected |
cr.status |
read:cr |
cr_id |
State, lifecycle_version, base and target, gate results, evidence and manifest references, approval requirements and status, reconciliation status, blocking reasons, permitted_next_actions, external_decisions_required |
success, rejected |
action.invoke |
act:<action> |
Per MCP-10 | invocation_id, invocation state, execution_status |
success, accepted, rejected, failed, indeterminate |
invocation.status |
act:<action> of the original invocation, or read:runtime |
invocation_id, or action + key |
Per MCP-11 | success, rejected |
incident.get |
read:runtime |
incident_id or authorized filters |
Incident state, affected revisions and resources, drift findings (including quarantined requests), remediation references | success, rejected |
Further rules:
- Repeating
cr.submitwith the samesubmission_keyand draft returns the same result. Reusing the key with a different draft is rejected (E_IDEMPOTENCY_CONFLICT). cr.withdrawis a compare-and-swap onlifecycle_version.- No tool returns hidden-oracle inputs or expected outputs (VER-06).
- Creating a business request is an ordinary
action.invokeof the declared creating action (CON-17). It needs no extra tool.
#8. Error registry
ERR-01 S Coverage. Every rejectable normative requirement maps to a code below, or to E_VALIDATION_FAILED with a finding that names the requirement. The registry is protocol/errors.json.
ERR-02 P Stable meaning. A code's meaning never changes within a major protocol version.
ERR-03 S Retry class from durable state. The registry gives each code a default retry_class, but the server derives the returned value from durable state. For example:
E_INVARIANT_VIOLATIONreturnsafter_reconciliationwhen unresolved or quarantined obligations hold the amount.E_BUDGET_EXHAUSTEDreturnsafter_authorizationwithhuman_requiredonly when committed plus held reservations alone exceed the cap.E_BUSINESS_REQUEST_STATEreturnsafter_reconciliationfor a quarantined request.
A network timeout is never reported as failed_without_effect.
ERR-04 S Approval errors. An error that requires human approval names the approval class. It never names approvers or private approval channels.
ERR-05 S No leaks. Error messages and details never contain secrets, unredacted pii: high values, hidden-oracle content, or the existence of resources the caller cannot see.
Retry classes.
retry_class |
The agent must |
|---|---|
| never | Never resend: the identical request can never succeed. |
| after_revision | Change the request content (new draft, corrected input), then send. |
| after_reread | Re-fetch current state (cr.status, contract.get, invocation.status), then decide. |
| new_proof | Resend with a fresh DPoP proof, using any nonce the error returned. |
| after_authorization | Obtain a new or wider grant, or a human decision. Never retry under the same authority. |
| after_reverification | Wait for the CR to pass verification again; this cannot succeed before then. |
| same_idempotency_key | Query status, or resend with the SAME key. Never mint a new key. |
| after_reconciliation | Wait until reconciliation resolves the named effect, request or cohort. |
| later | Retry with backoff, keeping the same key: this is a transient platform condition. |
Codes.
| Code | Req. | Default retry | execution_status |
Human | Meaning |
|---|---|---|---|---|---|
| E_GRANT_MISSING | AUTH-01 | after_authorization | not_started | – | No grant presented. |
| E_GRANT_EXPIRED | AUTH-15 | after_authorization | not_started | – | Grant expired. |
| E_REVOKED | AUTH-22 | after_authorization | not_started, in_progress, failed_without_effect | yes | Grant, objective, principal or key revoked. A revoked objective needs a new human-signed objective, not a new grant. |
| E_DPOP_INVALID | AUTH-16 | new_proof | not_started | – | Proof does not match method, URI, token hash or clock window. |
| E_DPOP_NONCE | AUTH-16 | new_proof | not_started | – | Server nonce required or stale; the response carries a new nonce. |
| E_DPOP_REPLAY | AUTH-16 | new_proof | not_started | – | Proof jti already accepted. |
| E_AUDIENCE | AUTH-14 | never | not_started | – | Token presented to a service other than its aud. |
| E_SCOPE_DENIED | AUTH-18 | after_authorization | not_started | – | Grant lacks the scope this tool requires. |
| E_OUT_OF_OBJECTIVE | AUTH-07 | after_revision | not_started | – | CR touches entities outside the objective. |
| E_DELEGATION_WIDENS | AUTH-04 | after_authorization | not_started | – | Delegation hop widens scope or exceeds depth 2. |
| E_FORBIDDEN_COMPOSITION | AUTH-08 | never | not_started | yes | Sequence forbidden within this objective. |
| E_PRIVILEGED_CHANGE | AUTH-09 | after_authorization | not_started | yes | Out-of-band human approval required. |
| E_APPROVAL_REQUIRED | AUTH-21 | after_authorization | not_started | yes | Required approval missing; details name the approval class only (ERR-04). |
| E_POLICY_DENIED | AUTH-13 | after_reread | not_started, in_progress, failed_without_effect | – | Policy denies at admission or at the execution-time recheck. |
| E_BUDGET_EXHAUSTED | AUTH-11 | later | not_started | – | Objective ledger cannot reserve the cost now. The server returns after_authorization with human_required when committed plus held alone exceed the cap (ERR-03). |
| E_VALIDATION_FAILED | CR-01 | after_revision | not_started | – | Contract or CR validation failed; every finding is listed in error.findings. |
| E_OP_UNKNOWN | CR-08 | after_revision | not_started | – | Op not in the closed vocabulary. |
| E_CR_TOO_LARGE | CR-06 | after_revision | not_started | – | More than 25 ops or 5 entities. |
| E_MULTI_OBJECTIVE | CR-05 | after_revision | not_started | – | CR spans more than one objective. |
| E_RISK_UNDERSTATED | CR-07 | after_revision | not_started | – | Declared risk below computed risk. |
| E_RISK_EXCEEDS_GRANT | RSK-03 | after_revision | not_started | – | Computed risk above grant risk_max. Split the CR; widening the objective is a human decision. |
| E_AFFECTED_MISMATCH | CR-02 | after_revision | not_started | – | Declared affected set differs from computed. |
| E_STALE_BASE | LC-13 | after_revision | not_started | – | CR base is no longer the approved revision. |
| E_INVARIANT_NOT_COMPILABLE | CON-06 | after_revision | not_started | – | Block invariant cannot compile to class M. |
| E_IDEMPOTENCY_CONFLICT | EXE-02 | never | not_started | – | Same idempotency identity, different canonical inputs. |
| E_INVOCATION_IN_PROGRESS | EXE-02 | same_idempotency_key | not_started, in_progress | – | Original invocation still executing or not proven non-executed. |
| E_BUSINESS_REQUEST_DUPLICATE | CON-18 | never | not_started | – | Business request violates its declared uniqueness key. |
| E_BUSINESS_REQUEST_STATE | CON-17 | after_reread | not_started, failed_without_effect | – | Business request is not in an executable state. Returns after_reconciliation if quarantined (ERR-03). |
| E_INVARIANT_VIOLATION | CON-06 | never | not_started, failed_without_effect | – | Commit would violate a block invariant. Returns after_reconciliation when unresolved or quarantined obligations hold the amount (ERR-03). |
| E_MULTIPLE_MONEY_EFFECTS | EXE-13 | after_revision | not_started | – | An action declares more than one money effect, or a non-money effect that holds a reservation. |
| E_INTEGRITY_INCIDENT | ART-03 | never | not_started | yes | Artifact or manifest mismatch; CR Rejected with reason integrity_incident; a successor may reuse the plan. |
| E_REQUEST_RETRY_NOT_PERMITTED | CON-20 | after_revision | not_started | – | A failed or cancelled business request cannot be re-executed; create a new request. |
| E_RESTORE_RECONCILING | EXE-11 | after_reconciliation | not_started | – | Dispatch frozen after a restore until requests and effects are reconciled. |
| E_EFFECT_OUTCOME_UNKNOWN | EXE-05 | after_reconciliation | unknown | – | External effect outcome not established. |
| E_REVISION_MISMATCH | MCP-10 | after_reread | not_started | – | expected_revision incompatible with the running revision. |
| E_CURSOR_INVALID | MCP-08 | after_reread | not_started | – | Cursor invalid, expired or bound to another scope; restart pagination. |
| E_LIFECYCLE_CONFLICT | LC-17 | after_reread | not_started | – | Transition not permitted from the current state or version. |
| E_CR_TERMINAL | LC-10 | never | not_started | – | CR is in a terminal state; create a successor. |
| E_EVIDENCE_EXPIRED | LC-14 | after_reverification | not_started | – | Evidence older than 72 h. |
| E_ARTIFACT_MISMATCH | ART-03 | never | not_started | yes | Artifact or manifest differs from the approved, evidence-bound manifest. Security event. |
| E_RECONCILIATION_HELD | REV-03 | after_reconciliation | not_started | – | Affected cohort is Held. |
| E_BLOCKED_DRIFT | RUN-04 | after_reconciliation | not_started | – | Open drift blocks R2+ CRs on these entities. |
| E_BLOCKED_MUTATION | VER-04 | later | not_started | – | Mutation kill rate below 70% blocks R2+ CRs. |
| E_BLOCKED_FLAKY | VER-05 | later | not_started | – | Quarantined test blocks R2+ CRs on its entities. |
| E_ESCALATION_REQUIRED | LC-07 | after_authorization | not_started | yes | Third rejection in this objective; the owner must clear the next submission. |
| E_PROTOCOL_VERSION | MCP-12 | never | not_started | – | Unsupported major protocol version. |
| E_INTERNAL_UNAVAILABLE | AMD-03 | later | not_started, failed_without_effect, unknown | – | Required platform dependency unavailable, or serialization retries exhausted (CON-21); failed closed. |
Codes marked Human cannot be resolved by the agent. The agent loop ends there.
#9. Risk classification (amends §7.2)
RSK-01 S Deterministic. Risk is a deterministic function of (CR content, base revision, risk-policy version). The risk-policy version is recorded in the plan, the evidence bundle and the approval record.
RSK-02 S Platform computes. The computed class is authoritative. The author may raise it but never lower it (CR-07).
RSK-03 S Formula. Computed risk = the maximum of the floors of every op in the CR and of every applicable modifier. The registry is protocol/risk-floors.json.
Op floors.
| Op | Floor | Note |
|---|---|---|
| add_entity | R0 | New table, no behavior |
| add_field | R0 | Optional field; a required field without a default adds the migrate modifier |
| alter_field | R2 | Changes existing data shape; narrowing adds the contract modifier |
| deprecate_field | R1 | – |
| remove_field | R3 | Always a contract migration (CR-04) |
| add_relation | R1 | – |
| add_state | R1 | – |
| add_transition | R2 | Changes reachable behavior of an existing machine |
| alter_guard | R2 | – |
| add_action | R1 | Modifiers raise it for E3, money or high PII |
| alter_action | R2 | – |
| deprecate_action | R2 | Removes availability of existing behavior |
| add_invariant | R0 alert / R2 block | A new block invariant can reject existing production writes |
| strengthen_invariant | R2 | – |
| weaken_invariant | R2 alert / R4 block | CON-08 |
| add_policy | R2 | R4 if widening (RSK-05) |
| alter_policy | R2 | R4 if widening (RSK-05) |
| add_schedule | R2 | Introduces autonomous recurring execution |
| add_integration | R3 | New external surface and dependency |
| register_escape_hatch | R3 | R4 with E3 effects or pii: high access (ESC-04) |
Modifier floors.
| Modifier | Floor |
|---|---|
| E3 (irreversible) effect declared or touched | R3 |
Money movement (any money(currency) effect) |
R3 |
Reads or writes a pii: high field |
R3 |
Migration class migrate |
R2 |
Migration class contract |
R3 |
| New dependency (lockfile change) | R3 |
Changes an adapter's provider_idempotent, non_executing_responses or lookup_finality declaration |
R3 |
| Policy widening (RSK-05) | R4 |
| Removes, weakens or downgrades a block invariant (CON-08) | R4 |
Changes a CON-21 bounded_by declaration, other than to a stricter bound |
R4 |
| Disables a trigger or drops a block constraint (DJ-05) | R4 |
| Removes a business-request uniqueness declaration, adds a field to its key, or makes its constraint partial beyond the CON-18 normative scoping | R4 |
Escape hatch with E3 effects or pii: high access |
R4 |
RSK-04 S Conformance doesn't lower risk. A conformance level never lowers the computed class. Levels govern autonomy, not hazard.
RSK-05 S Widening, defined. A policy change widens if, after the change, any (principal, action, resource, environment) tuple is permitted that was not permitted before.
- Scope of the test. It is evaluated over the full before/after authorization relation, including principals, resource classes and actions introduced by the CR.
- New principals and resource classes. A new principal or resource class is widening. Merely copying an existing principal's permission set to a new principal creates newly permitted tuples, and is therefore widening.
- The delegation exception. It is the only exception, and requires all of:
- an explicit, already-authorized delegation relationship, in which the new principal is a named delegate of an existing principal;
- that existing principal's permitted set is a superset of the new principal's;
- the delegation is itself recorded in the base revision, or in the same CR under AUTH-20 rules.
- Actions introduced in the same CR. A grant of access to such an action is not widening only if every grantee could already perform the same reads and writes, on the same entities and fields, through actions in the base revision. Otherwise it is widening.
#10. Evidence-to-artifact binding (amends §6, §9, SEC-03)
The safety contract is normative now. Continuous runtime attestation is phased (ART-05).
IDN-10 S New kinds. The IDN-02 kind set adds:
mf(deployment manifest);apr(approval record);inv(invocation);eff(effect).
Domain separation applies unchanged.
ART-01 S Deployment manifest. Every production deployment references an immutable manifest (schemas/deployment-manifest.schema.json) containing:
- the CR ID, and the base and target contract IDs;
- digests of every executable artifact, the lockfile, the SBOM, the generated migrations, the compiled constraints and triggers, and the policy module;
- adapter identities and versions, with each adapter's EXE-04 declarations;
- the target environment and compatibility constraints;
- the
config_envelope(ART-04).
Secrets appear only as versioned references, never as values.
ART-02 S Binding. The evidence bundle names the manifest ID it verified. An approval record (apr, schemas/approval.schema.json) binds and signs, per IDN-05:
- the CR ID, evidence bundle ID and manifest ID;
- the approver, approval-policy version and risk-policy version;
- the timestamp.
ART-03 S Executor checks before deploy. The Executor verifies all of the following:
- the CR is Approved;
- the required approvals are valid;
- the evidence is unexpired and not invalidated;
- signatures and key windows are valid (IDN-07);
- the manifest equals the approved manifest;
- every artifact matches its digest;
- the target environment satisfies the compatibility constraints and the grant.
Any failure blocks deployment. A digest or manifest mismatch also:
- moves the CR to Rejected (T25), with rejection reason
integrity_incident; - returns
E_INTEGRITY_INCIDENT; - is logged as a security event.
The CR is Rejected rather than held, because its evidence no longer describes any deployable artifact set. The reason class lets a successor CR reuse the content and the plan without pretending the incident did not happen.
ART-04 S Configuration envelope. Every configuration key the application reads is declared in the manifest as exactly one of:
-
pinned — its value is committed by hash;
-
ranged — it has a declared range or enumeration, and the evidence bundle records which values V3–V10 ran against. A ranged value is non-material only if either:
- (a) the Verifier has established and recorded that the relevant behavioral properties are monotonic across the declared range, and both endpoints were verified; or
- (b) every value that will be used in production is explicitly enumerated and verified.
Otherwise any change between unverified values is material (ART-07);
-
non-behavioral — it is on an allowlist, for example replica count or autoscaling bounds.
Generated code reads configuration only through a typed accessor generated from the envelope. The accessor has no method for an undeclared key, so such code fails V2. A runtime read outside the accessor is reported as drift (RUN-03).
ART-07 S Material change, testably defined. A change is material if and only if it changes the manifest ID. That includes:
- any digest;
- any pinned value;
- any range;
- any value outside its declared range;
- any key moved onto or off the allowlist;
- any adapter declaration (EXE-04).
A material change requires new verification and approval.
- Changing an allowlisted key is non-material.
- Changing a ranged value within its range is non-material only under the conditions of ART-04.
- A rebuilt artifact whose digest differs is material, even when built from the same source revision.
ART-05 D Runtime attestation (phased).
- All levels: the REV-01 code-revision attribute MUST equal the manifest's artifact digest. A mismatch is drift (RUN-03).
- L3: continuous measurement of executables and configuration against the manifest is required.
- L2: reports MUST label deployments "bound at deploy; not attested at runtime" (CNF-12).
ART-06 D Offline verification (amends SEC-03). A third party with the audit log and public keys can verify the chain: objective → CR → evidence bundle → approval record → manifest → deployed artifact digests → audit record. The check must detect a substituted executable, migration or pinned configuration.
#11. Conformance (amends §18)
CNF-07 S Protocol suite. An installation passes the rc3 protocol suite before claiming rc3 compatibility. The suite covers at least:
- Tokens and proofs:
- multi-request token reuse;
- proof replay, including across replicas, and replay-cache outage;
- authoring/action scope separation.
- Identity:
- concurrent identical invocations;
- cross-agent retry of one business request;
- business-request uniqueness;
- permanent idempotency input binding across abandoned attempts and restore (EXE-02);
- a second attempt under the same business request from a different agent, at every CNF-09 boundary (at most one logical operation executes).
- Money and completion:
confirms_effectenforcement;- pessimistic accounting under 40 concurrent requests;
- a monetary invariant with confirmed obligations in the sum, with no double count (CON-21);
- reservation reconciliation;
- a request withdrawn before invocation (the obligation must release).
- Dispatch and outcomes:
- revocation after commit (the
blockedstate), and revocation racing the proxy admission (F2e); - fencing-token rejection;
- ambiguous provider outcomes;
- provider finality after lease expiry, with
lookup_finalitytrue and false (EXE-05, F12); - single-use proxy transmission, and no forwarding on append uncertainty (EXE-18);
- unused send authorization revoked and re-dispatched exactly once (F39);
- grammar: no path from
transmissiontopre_send,refusedorcancelled(EXE-12); - control-plane-only terminal transitions (EXE-17);
- revocation discovered in
pre_sendwith the ledger stillpre_send(must reachblocked).
- revocation after commit (the
- Lifecycle:
- pagination under concurrent revision change;
- submission idempotency;
- concurrent lifecycle transitions;
- withdrawal;
- revocation during rollout;
- evidence expiry;
- lease expiry before commit (the
abandonedstate).
- Artifacts:
- artifact substitution;
- non-monotonic range verification (ART-04);
- offline chain verification.
- Restore and repair:
CNF-08 S Negative controls. Every security-critical mechanism has a negative control showing that its test fails when the mechanism is removed. At minimum:
- Authorization: the proof replay cache.
- Money:
- Identity and completion:
- Database privileges:
- the
vaep_invocationprivilege trigger: avaep_appwrite ofabandonedor offencing_tokenmust fail; - the obligation and effect forward-only triggers: a
vaep_appwrite ofreleased,confirmedor a terminal effect state must fail.
- the
- Ledger:
- fencing tokens, and the fenced ledger append: a stale dispatcher's
pre_send→sentmust fail; - head-linked append: an append naming a stale head must fail;
- append authority: a worker or adapter credential appending
failed, or a worker appendingtransmission, must be refused.
- fencing tokens, and the fenced ledger append: a stale dispatcher's
- Outcomes:
- the
non_executing_responsesset: an undeclared response class must not reachfailed; - the provider-finality requirement: with
lookup_finality: false, a not-found lookup must not reachfailed.
- the
- Proxy:
- Grammar: inserting an edge from
transmission,open_lookuporlookup_closedtopre_sendmust fail the build check. - Artifacts: the Executor digest check.
CNF-09 S Fault injection. The suite injects a crash at each of these durable boundaries:
- before the invocation record;
- after the invocation record;
- after budget reservation;
- after the business mutation, before commit;
- after commit, before
invocation_committed; - after provider success, before acknowledgement;
- during reconciliation;
- during lifecycle advancement or deployment;
- after transmission, before any provider response;
- a point-in-time restore taken before dispatch, applied after dispatch (EXE-11);
- between the ledger's terminal record and the EFS-04 transaction;
- between the ledger
pre_sendappend and the row CAS, and between the row CAS and thesentappend (dispatcher crash inpre_send). A second dispatcher takes over, and the stale one is woken afterwards and attempts to send. This is repeated after a restore, so that the stale token value could recur; - between the
request_createdappend and the request's database commit, and between commit andrequest_committed. This is repeated across a restore; - between
sentand the proxy'stransmissionappend, racing F39, F2e and F3c; - between the proxy's
transmissionappend and forwarding; during forwarding; and with the append's acknowledgement lost; - lease expiry while a provider request is in flight, with a lookup returning not-found before the request lands.
After each crash, the suite asserts the EFS, EXE and CON-21 invariants.
CNF-13 S Deterministic concurrency schedules. For the CON-21 invariant and lock protocol, the suite runs scripted interleavings, not only load. Each runs under READ COMMITTED, REPEATABLE READ and SERIALIZABLE:
- two requests whose sum exceeds the bound, interleaved at every pair of statement boundaries;
- a request interleaved with a decrease of the bound;
- a request interleaved with a release;
- a request interleaved with a confirmation.
Each schedule asserts two things: at most the bound is held by non-released obligations, and every successful commit observed every concurrent committed write. Serialization failures must be shown to retry as new transactions. The negative control removes the vaep_agg_lock UPDATE and shows the schedule overspends.
CNF-10 S Interoperability. Two independently implemented clients pass the protocol interoperability fixtures.
CNF-11 D Critical paths. L2 and L3 reports list security-critical, monetary and irreversible-effect paths separately from aggregate coverage.
- A critical action never qualifies for autonomous execution because overall coverage exceeds 90%.
- Each critical path itself needs class M mediation and authorization.
CNF-12 P Claim labels. Conformance reports label each of the following separately:
- protocol conformance;
- contract conformance;
- deployment conformance (deploy-bound or runtime-attested);
- runtime assurance;
- business-intent attestation.
None is presented as another.
#12. Guarantees (amends §20)
The rc2 guarantees stand. The following clarifications are added.
GUA-02 P Authorization. "Mechanically enforced authorization" means that a request or effect crossing the declared authorization boundary cannot execute without valid authority. It says nothing about whether the objective was wise.
GUA-03 S External effects (M on mediated channels, C1).
- Admission. Each
effect_idis admitted for transmission by the proxy at most once, ever (EXE-18). After atransmissionrecord exists, VAEP never issues another transmission for that effect. - Fail closed. The admission limit fails closed on uncertainty.
- Provider-side count. Provider-side execution count is not guaranteed. Provider-side deduplication is exactly-once only where the adapter's provider contract documents idempotency by the derived key (EXE-06).
- Admission is not execution. Admission is never equated with provider execution, and outcomes are resolved only by evidence (EFS-02).
- Restores. A database restore never causes a re-send (EXE-11).
GUA-04 S Confirmed completion (M, C1). No entity is in a confirms_effect state unless that effect is succeeded.
GUA-05 S Bounded money (M, C1, within the stated boundary). Declared monetary bounds (CON-21) count every non-released obligation, including confirmed, held and quarantined ones. Through VAEP-mediated paths, a bound is never exceeded.
The enforcement boundary is:
- writes through
vaep_appor narrower (§16.1); - channels under complete mediation (§11.2);
- restores handled by EXE-11 and EXE-19.
Worker compromise is inside the boundary, for three reasons:
- EXE-17 limits a worker's appends;
- EXE-16 binds amounts;
- EXE-18 makes the proxy refuse any call the ledger has not authorized, and any second transmission.
The claim is class M only for an installation whose conformance evidence includes all of:
- the deterministic schedules of CNF-13 at all three isolation levels;
- the §11.2 negative probes;
- fault-injection boundaries 1–16;
- for every adapter used by a money effect, its EXE-04 declarations in the manifest digest.
Without that evidence, the claim is class T.
An adapter declaring lookup_finality: false never permits automated release of an obligation from a lookup. Such effects reach reconciliation_required.
GUA-06 P Truthful status. VAEP reports uncertain or partial outcomes as such. It never reports them as success, as failure without effect, or as a completed rollback without evidence.
New non-guarantees:
- actions taken at a provider outside VAEP;
- exactly-once execution at the provider, unless documented (EXE-06);
- runtime attestation below L3 (ART-05);
- the correctness of a
uniqueness: "none"declaration; - the correctness of an adapter's EXE-04 declarations against the provider's actual behavior.
GUA-01 applies to every item above.
#13. Release criteria and migration
Freeze checklist. rc3 is frozen only when every item holds:
- ERRATUM-1 and ERRATUM-2 are published against rc2, with fixtures.
- All schemas in Appendix A are published and pass a mutual-consistency check.
protocol/effect-lifecycle.jsonis regenerated for revision 12. That means:protocol/risk-floors.jsongains the revision 12 modifiers.protocol/errors.jsonreflects the revision 12 retry derivations.requirements-rc3.tsvlists CON-24, EXE-14, EXE-17, EXE-18, EXE-19 and EFS-07, each with its verification method.- Every table in this document matches its
protocol/*.jsonfile (AMD-02), and the freeze reviewer has examined the files, not only the tables. - OPEN-1, OPEN-2 and OPEN-3 are closed.
- Every rc3 suite requirement has an executable test, and every security-critical one has a negative control.
- The protocol suite and fault-injection suite pass on a real PostgreSQL installation.
- Two independent MCP clients pass the interoperability fixtures.
- The Agent Guide is regenerated from the rc3 schemas and checked against them.
- The release notes distinguish specification completion from demonstrated implementation conformance.
Migration from rc2. Implementations migrating from rc2:
- Apply ERRATUM-1 and ERRATUM-2.
- Add to every contract with E2/E3 actions: business-request entities,
confirms_effectstates, failure transitions,bounded_bydeclarations and CON-21 invariants. The validator will reject contracts without them. - Add the invocation, effect, obligation and reservation records, fencing, the restore-independent ledger (EXE-14) and the proxy transmission append.
- Add the EXE-04 adapter declarations to every adapter.
- Implement the ten-tool interface and the envelope.
- Map existing CRs to rc3 states. Any CR that an rc2 installation had returned to Draft after a gate failure becomes Rejected, with a successor if needed.
- Bind evidence and approvals to manifests.
- Pass the rc3 suite.
rc2 applications do not inherit rc3 conformance.
#14. Open items
| ID | Blocks freeze | Item |
|---|---|---|
| OPEN-1 | Yes | Confirm the rc2 page-15 lifecycle diagram against LC-08. In particular, check what its "2 failure exits" were. |
| OPEN-2 | Yes | Author the JSON Schemas for: the ten tools; the envelope; the manifest; approval, invocation, effect and obligation records; and every dispatch-intent ledger record kind of EXE-12. |
| OPEN-3 | Yes | Write the executable fixtures and negative controls for every [S] requirement (see requirements-rc3.tsv), including CNF-09 boundaries 13–16. |
| OPEN-4 | No | Removal ops and escape-hatch modification ship first as extensions under GOV-03. Meanwhile, re-registering an escape hatch under its existing ID is a modification, priced by ESC-04. |
| OPEN-5 | No (claim excluded) | CON-21 is per tenant in rc3. Applications needing a cross-tenant monetary bound are outside the rc3 conformance claim until a later revision defines it. Other rc2 §21.2 questions remain open. |
| OPEN-6 | No | Idempotent automated re-dispatch after transmission (the former F13), for adapters declaring both provider_idempotent: true and lookup_finality: true. Under GOV-03 it may return only as an extension shipped in two installations, with its own fault-injection boundaries. |
#15. Revision history
#Revision 2 changes
Answers to the freeze review of revision 1. Items marked P0 were release blockers.
| Review item | Disposition | Where |
|---|---|---|
| P0-1 duplicate execution after rollback | Proof boundary stated; identity never released | EXE-02, EXE-12 |
| P0-2 multi-effect settlement | One money effect per action | EXE-13, EXE-07 |
| P0-3 tombstones undefined | Monetary obligation record replaces tombstones and is what CON-21 sums | CON-21, EXE-13, EXE-11 |
| P0-4 no dispatching → blocked | New pre_send state; blocked reachable only from provable non-send |
§5.4, EFS-02, checker rule |
| P0-5 restore deadlock / atomicity | Dispatch-intent ledger with watermark; request-creation boundary; no re-dispatch to discover outcome | EXE-12, EXE-11 |
| CON-19/EFS-04 atomicity | One transaction; ledger-first ordering with boundary 11 | CON-19, EFS-04 |
| T1 validation boundary | V1 check set run by the Change service at T1; Verifier runs V1–V10 | T1 guard |
| Held repair before T14 | LC-19 | LC-19, T14 |
| T25 Rejected vs hold | Kept Rejected; reason class integrity_incident |
ART-03, AUD-02 row |
| Ranged config | Verified values recorded; else material | ART-04 |
| AUTH-20 modified T/A | Assurance graph | AUTH-20 |
| RSK-05 new principals | Full before/after relation | RSK-05 |
| Extension fields | Single definition | MCP-05 |
| EXE-10 misleading status | completed_external_failed added |
EXE-10, MCP-06 |
| GUA-05 stronger than evidence | Boundary and required evidence stated; class T without it | GUA-05, CNF-13 |
| 30-day errata window | Claim withheld; monetary autonomy disabled until applied | §1 |
| "Delivered" artifacts not examined | They are in the package; the freeze checklist now requires reviewing the files | §13 |
| OPEN-5 multi-tenant | Per-tenant bound normative; cross-tenant excluded from claim | CON-21, OPEN-5 |
| Immutable CRs are substantive | Indexed as a replacement of §8 behavior | §2 |
#Revision 3 changes
| Review item | Disposition | Where |
|---|---|---|
| Blocker 1: effect row and ledger disagree; cancel after send | Ledger authoritative for send state; F1/F2/F3 guards read it; F2c/F2d to outcome_unknown |
EXE-12, §5.4 |
| Blocker 2: restore re-executes a request | invocation_committed ledger record; invocation row and pending state replayed; EXE-02 proof keyed by request |
EXE-11, EXE-12, EXE-02 |
| Blocker 3: lease-expiry release races commit | Invocation row in app DB; EXE-03 commit is a CAS on its token; release only after abandoned CAS |
EXE-01, EXE-03, EXE-09, ERRATUM-2 |
| Held reservations stuck | held → released on proven non-execution; non-money effects never hold | EXE-07 |
| No retry after definitive failure | Failure targets non-executable; retry is a new request; partial uniqueness is normative | CON-20, CON-18 |
| Ledger LSN before commit; phantom obligations | request_committed record; request_created alone ignored |
EXE-12, EXE-11 |
| Requested obligation never released | Released on declared withdrawal with no commit | EXE-13 |
| Suspended CR with dead evidence | T27/T28 re-verification; reconciling CR may pass T10 | §6, LC-12, LC-16 |
| Risk modifier contradicts CON-18 | Modifier reworded | risk-floors.json |
| Restore needs transitions the machine lacks | F19, F20, F21, F2c | effect-lifecycle.json |
| No envelope status for in-progress errors | indeterminate may carry error |
MCP-06 |
| Untestable terms | Standing and assurance graph defined; AMD-03 → [P] | RSK-05, AUTH-20, AMD-03 |
#Revision 4 changes
| Review item | Disposition | Where |
|---|---|---|
| Blocker 1: restore misses effects dispatched after P under an earlier commit | Watermark on every record; step 4 iterates by effect; F1/F1b require no prior ledger record | EXE-12, EXE-11, §5.4 |
| Blocker 2: restored request becomes a bare obligation | Request row re-created from ledger before effects; uniqueness fields carried in ledger | EXE-11 step 2, EXE-12 |
| Blocker 3: re-verification exposed pre-rollout exits | ReVerifying state; only exit is back to Suspended | §6, LC-12 |
| Blocker 4: forward fix unreachable; no exit after successor Live | reconciles: declaration admitted at T1/T21/T10; T30 Suspended → Superseded |
§6, LC-16 |
| Boundary 5 and 11 repair paths | Scan-and-append for invocation_committed; F22–F25 ledger replay |
EXE-03, EFS-04 |
| Concurrent same-key E0/E1 | Partial UNIQUE on live invocation rows | EXE-01 |
| Reservation leak after abandon CAS | Idempotent release on abandoned |
ERRATUM-2, EXE-07 |
| Reversal release undefined | Reversal is a separate obligation | CON-21 |
| Untestable terms | Acceptance domain defined; ledger placement is EXE-14 [D]; CON-17 listing moved to objective | AUTH-16, EXE-12, CON-17 |
E_BUDGET_EXHAUSTED ends the loop spuriously |
Default later; after_authorization only when committed + held exceed cap |
ERR-03, errors.json |
#Revision 5 changes
| Review item | Disposition | Where |
|---|---|---|
Blocker 1: dispatcher crash in pre_send strands the effect |
F1c/F1d recovery; void ledger records; pre_send → sent is a fenced CAS |
§5.4, EXE-09, EXE-12, CNF-09 boundary 12 |
| Blocker 2: restore after commit, before dispatch, leaves no effect rows | Step 3 re-creates declared effects in pending_dispatch |
EXE-11 |
| T14 unreachable after re-verification | T28 records the new bundle's approvals before returning | §6 |
| Reconciling CR violates REV-02 | T10 requires every cohort on the reconciled CR's target | §6, LC-16 |
vaep_invocation not DB-enforced |
vaep_ctl role; privilege trigger; negative control |
EXE-01, CNF-08, §2 |
| ERRATUM-2 vs EXE-07 | Erratum defers to EXE-07 | §1 |
| F1 vs F1b citations | Corrected | AUTH-22, AUTH-23 |
| Step numbering in guards | Corrected to step 4 | effect-lifecycle.json |
| Objective missing from E0/E1 key | Key is (objective, client key) | EXE-01 |
Boundary 11 from outcome_unknown / reconciliation_required |
F28–F31 ledger replay | §5.4, EFS-04 |
#Revision 6 changes
| Review item | Disposition | Where |
|---|---|---|
Blocker 1: effect_id undefined; restore mints a second ID |
Deterministic eff ID over (invocation, effect name) |
EXE-04, EXE-11 |
| Blocker 2: ReVerifying has no exit on denial or revocation | T28 on completion only; T29 denial/revocation; T31 superseded; approvals recorded while Suspended | §6, LC-20 |
| Money writes not DB-enforced | vaep_ctl terminal writes; forward-only triggers; restore as vaep_owner |
EXE-13, EXE-11, CNF-08 |
| "Documented as non-executing" untestable | non_executing_responses declared, in manifest digest |
EXE-04, F5/F9/F12 |
| Held/canary create per-effect human work | Deferral, not blocked |
EFS-06, F2/F2b |
| ERRATUM-2 not implementable on rc2 | Self-contained wording; EXE-07 supersedes in rc3 | §1 |
| EXE-07 contradictions | Unreachable row removed; exits corrected | EXE-07 |
| T14 ping-pong on revoked objective; T30 missed in ReVerifying | Re-binding (LC-20); T31 | §6 |
| Ledger "append-only" vs CAS; token reissue after restore | Conditional append defined; counter outside restore scope; equal-token case | EXE-09, F1c |
| T10 actor; EFS-02 checker claim; step-4 citations; LC-18 | Corrected | §6, EFS-02, EXE-11, LC-18 |
#Revision 7 changes
| Review item | Disposition | Where |
|---|---|---|
| Blocker 1: cancel/refuse not fenced; send after cancel | Ledger-first rule: blocked, cancelled, sent and terminals are conditional appends before any row write |
EXE-12, F2/F2b/F3/F18 |
| Blocker 2: forged invocation laundered by repair scan; request without obligation | Gateway admitted ledger record required by F1 and the scan; obligation CAS must affect one row; precondition requires obligation; only declared effects (EXE-15) |
EXE-01, EXE-03, EXE-15 |
| No role for mandatory writes | vaep_ctl grants completed; adapters report, control plane writes; withdrawal references request_withdrawn |
EXE-13, §2 |
| Reservation stuck after F13 | Held exits include post-re-dispatch terminals | EXE-07 |
| Error statuses for in-transaction failures | failed_without_effect added; not_started on in-progress |
errors.json |
| LC-20 events absent from file | T32, T33 self-transitions | §6 |
| EFS-02 check contradicts F18 | cancelled sources include blocked; checker updated |
EFS-02 |
| Unreachable held → released row and fixture | Removed | EXE-07, CNF-07 |
| LSN ambiguity after second PITR | (timeline, LSN) watermark | EXE-12 |
blocked meaning; T10 wording |
Corrected | effect-lifecycle.json, T10 |
#Revision 8 changes
| Review item | Disposition | Where |
|---|---|---|
| Blocker 1: continued effect stuck behind refused head | continued record; F1/F2/F3 accept absent or continued head |
EXE-12, §5.4 |
| Blocker 2: re-dispatch races reconciliation | Head-linked appends (true CAS on predecessor ID); open_lookup head; F13 refused while a lookup is open |
EXE-12, EXE-05, F11–F13 |
| T12 vs T33 ping-pong | T12 excludes re-bound CRs | §6 |
| T14 skips artifact checks | ART-03 checks 2–7 | §6 |
| Non-money E2/E3 unexecutable | Obligation CAS only for money actions | EXE-03 |
| Withdrawn request executable after restore; obligation moved backwards | request_withdrawn replayed; obligation forward-only in restore |
EXE-11 |
| Worker trust boundary | Per-kind append authority (EXE-17); amount binding (EXE-16) | EXE-17, EXE-16, GUA-05 |
| Actor/role mismatches; refused kind; F13 sequence | Actors |platform; record grammar completed |
§5.4, EXE-12 |
| Admission ordering | Reserve → admitted → row |
EXE-01 |
Restore of dispatching / provider_pending; F1c vs F21; EXE-09 placement; boundary 12 |
F32/F33; F21 removed; EXE-14; GUA-05 boundaries 1–12 | §5.4, EXE-11, EXE-09, GUA-05 |
#Revision 9 changes
| Review item | Disposition | Where |
|---|---|---|
Blocker 1: vaep_app can delete or re-point an obligation |
No DELETE; bound_owner, tenant_id, request reference immutable |
CON-21, §2 |
| Blocker 2: lookup opened during an in-flight send | open_lookup accepted only when the sent token has no live lease |
EXE-05, F11/F12 |
Blocker 3: restore leaves refused / continued / cancelled heads unmatched |
F34–F38 replay transitions; step 4 bullets | §5.4, EXE-11 |
| Blocker 4: reservation without an invocation row | Release after admission lease expiry, with the admitted record voided first |
EXE-07 |
| Crash between F13's two appends | F1e orphan void | §5.4 |
| Held exits enumerated incompletely | Any terminal of the money effect | EXE-07 |
| T29 ping-pong on re-bound CR | T33 exclusion | §6 |
| Worker sends unbound amounts via proxy | EXE-18: proxy enforces the ledger | EXE-18, GUA-05, §2 |
| Actor mismatches; stale F21; undefined re-bound recheck | Corrected; recheck defined | §5.4, EFS-02, AUTH-23 |
| Lookup grammar | open_lookup → open_lookup; lookup_closed record |
EXE-12, EXE-05 |
#Revision 10 changes
| Review item | Disposition | Where |
|---|---|---|
| P0-1: lease expiry ≠ provider finality | Distinguish lease (no new send) from provider finality guarantee; F12 requires explicit provider contract property | EXE-05, F12, GUA-05 |
| P0-2: request commit-to-ledger crash window | Request-commit repair scan (EXE-19); quarantine instead of silent discard | EXE-19, EXE-12, EXE-11 |
| P0-3: incomplete request reconstruction | Canonical reconstruction payload required; recreate only from complete payload | EXE-19, EXE-11 |
| P1-4: RSK-05 new-principal exception | Exception narrowed to explicit already-authorized delegation; copying permissions is widening | RSK-05 |
| P1-5: permanent idempotency input binding | Immutable identity record with atomic check on every admission | EXE-02 |
| P1-6: endpoint-only range verification | Require monotonicity proof or explicit enumeration; otherwise material | ART-04, ART-07 |
#Revision 11 changes
| Review item | Disposition | Where |
|---|---|---|
| P0-1: proxy does not enforce one outbound send | Single-use transmission record at proxy; atomic append before forward; fail closed on uncertainty | EXE-18, GUA-03, GUA-05 |
| P0-2: reconstruction payload after commit | Canonical payload in request_created before DB commit; unrecoverable classification when history lost |
EXE-19, EXE-12, EXE-11 |
| P0-3: terminal transitions bypass ledger authority | Explicit three-role separation; only the control plane appends terminals and writes terminal states | EXE-17, F4/F5/F8/F9/F15/F16 |
| P0-4: monetary invariant not proven across isolation levels | Explicit transaction protocol (lock, snapshot, retry); CNF-13 must show observed aggregate | CON-21, CNF-13 |
| P1: late row after voided admission | Explicit forgery rule | EXE-01, EXE-07 |
| P1: effect identity on retry | Proof that permitted retries cannot mint a second effect_id |
EXE-02, EXE-04 |
| P1: restore validation before unfreeze | Obligation and aggregate validation required before dispatch resumes | EXE-11 |
P1: refundable_balance double-count |
Definition excluding confirmed from sum | CON-21 |
P1: permitted_next_actions vs MCP-14 |
Distinguish agent-invocable from external decisions | LC-18 |
#Revision 12 changes
Answers to the freeze review of revision 11 and of the amendment set proposed against it.
| Review item | Disposition | Where |
|---|---|---|
P0: revision 11 refundable_balance still double-counts (the formula summed confirmed obligations against a balance that already subtracted them); generated columns cannot read other rows |
Bound is a lifetime field on the bound owner; confirmed obligations stay in the sum; no running balance; validator forbids effect-driven writes to the bound; proposed stored-column alternative rejected | CON-21, EFS-04 |
P0: proposed grammar kept lookup_closed → pre_send, re-opening a second transmission after F13's removal |
lookup_closed → terminals only; build check forbids any path from transmission back to sending |
EXE-12, EFS-01 |
| P0: CON-21 lock protocol relied on a snapshot guarantee REPEATABLE READ cannot give | Mechanism-specific protocol: lock-row UPDATE, then a fresh-statement read; serialization failure on stale snapshot; bounded retries | CON-21, CNF-13 |
P0: sent without transmission had no recovery transition (EFS-01 exhaustiveness) |
send_revoked record; F39; F2e/F3c refuse or cancel before admission |
EXE-12, §5.4, AUTH-22 |
| Proposed amendment: transmission head, F13 removal (Option A), GUA-03 and EXE-18 honesty | Accepted. F13, F1e, F2c, F2d retired; idempotent re-dispatch moved to OPEN-6; GUA-03 strengthened to one admission per effect_id, ever |
EXE-05, EXE-18, GUA-03, OPEN-6 |
| Proposed amendment: completeness frontier and bound freeze refusing all admissions | Accepted in substance, revised: quarantined requests become non-executable rows (CON-24) whose obligations count at full amount, so capacity is never freed, without refusing unrelated admissions; frontier defined on request_created records |
EXE-19, CON-24, EXE-11 |
| "Durable transaction identity" and "authoritative evidence" undefined | Transaction-status classification table; abandoned-timeline status is never evidence | EXE-19 |
| Identity record location unspecified (a database record would not survive restore) | Identity chain in the ledger | EXE-02, EXE-14 |
| Finality guarantee implicit | lookup_finality declared per adapter, in manifest; changing it is R3 |
EXE-04, ART-01, RSK-03 |
Append authority conflicts (invocation_committed by dispatcher vs control plane; transmission by control plane vs proxy; F7 written by vaep_app) |
Single authority table; F7/F10 executed by control plane | EXE-17, §5.4 |
| Proxy transport retries could duplicate a forwarded call | Proxy never retries; no forward on append uncertainty | EXE-18 |
continued successors inconsistent with F2/F3 guards |
Grammar completed | EXE-12 |
| Restore could be raced by a pre-restore lease | dispatch_frozen honored by the proxy; heads normalized before replay |
EXE-11 |
| F19–F38 per-pair enumeration fragile | LR rule with head→state mapping, expanded in the JSON | EFS-07 |
| Failure-path atomicity unstated | failed / cancelled outcome transactions are atomic |
EFS-04 |
| EXE-14 referenced but not a requirement | Promoted to [D] requirement | EXE-14 |
| Appendix A duplicate row; stale "Delivered" status | Removed; statuses corrected | Appendix A |
#Appendix A additions
| Artifact | Path | Status in this redline |
|---|---|---|
| CR lifecycle | protocol/cr-lifecycle.json |
Delivered |
| Effect lifecycle | protocol/effect-lifecycle.json |
Regenerate for revision 12 (blocks freeze) |
| Error registry and retry classes | protocol/errors.json |
Update for revision 12 derivations (blocks freeze) |
| Risk-floor registry | protocol/risk-floors.json |
Update for revision 12 modifiers (blocks freeze) |
| Requirement index (rc3 additions) | requirements-rc3.tsv |
Update for revision 12 IDs (blocks freeze) |
| Dispatch-intent ledger records | schemas/dispatch-intent.schema.json |
OPEN-2 |
| Obligation record | schemas/obligation.schema.json |
OPEN-2 |
| Capability descriptor | protocol/capabilities.schema.json |
OPEN-2 |
| Envelope and error schemas | protocol/response.schema.json, protocol/error.schema.json |
OPEN-2 |
| Tool schemas | mcp/<tool>.{input,output}.schema.json (×10) |
OPEN-2 |
| Record schemas | schemas/{deployment-manifest,approval,invocation,effect-execution}.schema.json |
OPEN-2 |
| Fixtures | tests/protocol/, tests/fault_injection/ |
OPEN-3 |
(End of revision 12 consolidated redline.)
Requirement index
100 requirements defined in this redline, by verification method (VRF-01): S suite (86) · D deployment (4) · P process (10). Requirements carried over from rc2 unchanged are not listed here.
| ID | Method | Requirement | Section |
|---|