STATE≡LAYER
sophia. A RECORD FOR THINKING THINGS

System reference06

Evidence envelopes and the action gate

An envelope binds a claim to the actor, the authority it held at admission, the evidence it relied on, and the context it was actually given, so a consequential action is admitted or refused on the record rather than on an agent's say-so.

Every claim on this page checked against the product source at a pinned revision: 2026-08-19 @ 0c2a08a7.

What this is

An evidence envelope is an immutable record that binds five things together before a consequential action is allowed to happen: the claim being made, the actor making it, the authority that actor held at the moment of admission, the evidence the claim relies on, and the context the agent was actually given to work from. The action gate evaluates that envelope and produces exactly one of two durable outcomes: admitted, in which case the action dispatches through an atomic outbox and its result is verified independently of the agent’s own report, or refused, in which case a structured receipt records why and what a safe next step would be. Later contradiction or supersession updates the record; it never erases it.

One thing to know before anything else: this mechanism is source complete, not yet live. The full contract, gate, outbox, and verification pipeline exist on the main branch of the codebase (proxy/src/evidenceEnvelope/) and pass their tests, including a hostile fixture, but the mechanism has not yet been through live production acceptance in the running product. The public system map carries the same label. This page describes what the source guarantees, and says so plainly where the guarantee is a tested design rather than accumulated production hours.

Why it exists

Retrieval hands an agent text. That is what most memory systems are: the agent asks, gets passages back, and then acts on whatever it concluded, with nothing binding the conclusion to what it actually read, who it actually was, or what it was actually allowed to do. The gap shows up exactly when it is most expensive: at the moment an agent does something consequential and reports “done.”

Three failure modes drive the design, and all three are pinned as named cases in the hostile fixture (proxy/src/evidenceEnvelope/__fixtures__/evidence-envelope-v1-hostile.json), each with its exact expected refusal code and envelope digest. A handoff pins one revision but the checkout has since moved: source.handoff_stale. A lease scoped to repository A requests a write against repository B: authority.action_out_of_scope. A work receipt proves a tool returned, but nothing independently verifies the completion the agent is claiming: evidence.completion_unsupported. In each case the interesting property is that the refusal is computable from the envelope itself, because the envelope carried the pinned revision, the assignment scope, and the evidence references as first-class fields instead of leaving them implicit in a transcript.

How it works

Every fact in the envelope is either known with provenance or explicitly not. The payload schema (proxy/src/evidenceEnvelope/validation.ts) forces each binding, actor identity, lease, assignment, expected and observed source revision, delivered context receipt, into an explicit-value shape: known with an observed_at timestamp and a source_ref naming where it was read, or unknown / not_applicable with a reason code. Nothing is ever rounded up from missing to assumed. The whole payload is canonicalized under RFC 8785 with a domain separator and hashed, and the digest names the envelope forever; a corrected envelope is a new version that must carry its predecessor’s digest (proxy/src/evidenceEnvelope/contract.ts).

Preparation is server-attested; the agent supplies intent, never authority. In the current Phase 1 flow, sophia.prepare_evidence_envelope_set_activity accepts only a work ID, the desired activity, an optional reason, and an idempotency key (proxy/src/evidenceEnvelope/preparationService.ts). Sophia derives the actor’s identity, lease, entity scope, the current coordination head, and the codebase snapshot from the authenticated connection, appends an immutable context-delivery receipt to the coordination ledger, and returns a strict draft. At submission, the draft must resolve back to that ledger receipt: the receipt’s digest must match the delivered context recorded in the draft, and the requested action must be bound through one canonical specification item (proxy/src/evidenceEnvelope/contextDeliveryReceipt.ts). A draft that claims context it was never delivered does not evaluate; it fails the binding check before anything persists. The submission tool’s own schema states the rule: “Runtime authority is never accepted here” (proxy/src/mcp/tools/evidenceEnvelopeTools.ts).

Admission is nine named checks, and refusal is enumerated. The gate evaluates approval, authority, context, evidence, identity, policy, result, snapshot, and source, in that fixed order, against a coherent snapshot of current state, not against what the envelope asserts about itself. Any non-passing check refuses the envelope. Every refusal is one of 38 enumerated codes ranked in the contract, and a refusal produces a durable receipt carrying the primary code, all codes, the candidate digest, a safe_next_action sentence, and an effects block that states outright: admitted_envelope_created: false, requested_action_committed: false (proxy/src/evidenceEnvelope/contract.ts). A refused envelope leaves no half-committed action to clean up.

Approval, when required, binds the exact action and only releases once. An approval is bound to the precise action digest, parameter hash, connection, lease, and principal; parameters that drift after approval refuse as approval.params_drifted. An admitted action that still needs approval sits in the outbox with its authority-ready marker null, and a one-way handoff releases it only after the user’s approval is consumed, single-use, with crash retries recognized as idempotent replays rather than second consumptions (proxy/src/evidenceEnvelope/approvalHandoff.ts).

The outbox is atomic with the decision, and dispatch is fenced. When an envelope with a requested action is admitted, the envelope version, its evaluation event, the outbox row, and the decision event commit in one database transaction (proxy/src/evidenceEnvelope/service.ts); there is no moment where an action is admitted but unrecorded, or recorded but unadmitted. Dispatch then claims the row under a fenced lease (a fresh token plus a row version and attempt count), allows at most three attempts with bounded backoff, and terminalizes a third expired lease as expired rather than retrying forever. Every attempt at the same action carries the same idempotency key, derived from the action’s digest, so an executor that crashed mid-flight sees a recognizable replay instead of a new command. Terminal settlement is exactly-once: finalizing the outbox row and appending the terminal lifecycle event commit as one transaction, and only the current lease holder’s fence is accepted (proxy/src/evidenceEnvelope/actionOutboxDispatcher.ts, proxy/src/evidenceEnvelope/lifecycleService.ts).

A result is verified against independent artifacts, never taken from the actor’s report. A terminal event counts as verified only when two things cross-check (proxy/src/evidenceEnvelope/terminalVerification.ts): a server-minted work receipt for the action result whose actor identity matches the envelope’s actor and whose disposition is complete, and a separately persisted verified evidence row (a test assertion or runtime observation, in verified state, at corroborated or structural trust tier, with zero hard failures and no open material contradiction) whose receipt URI and hash point at that exact work receipt. The schema deliberately allows a reference kind of agent_assertion so an agent’s own report can be recorded, and deliberately never counts it toward verification. Saying “done” is admissible testimony; it is not proof.

The record survives being wrong. Lifecycle events are append-only and hash-chained. A decided envelope can later be marked corrected or superseded by a successor, and those currency events attach only to envelopes that actually reached a decision (proxy/src/evidenceEnvelope/contract.ts). Reading an envelope back through sophia.read_evidence_envelope always returns the original admission or refusal, and reports currency as unknown unless a bounded contradiction snapshot is actually available, so an unverified empty contradiction list is never presented as “still current.” This is the same posture Truth and provenance takes with mined claims: the honest answer when nothing has been checked is unknown, not clean.

What your agent does with it

// Phase 1 shapes, drawn from the source contract and its integration
// tests rather than a live capture: the mechanism is source complete
// and awaiting live acceptance.

const prepared = await sophia.prepare_evidence_envelope_set_activity({
work_id: 'work:4f2a91c0',
activity: 'ready',
reason: 'Implementation and tests complete on the pinned revision.',
idempotency_key: 'prep-refactor-auth-01',
});
// → { draft, draft_digest, receipt_ref, head_event_hash, ... }
// Sophia derived your identity, lease, entity scope, coordination head,
// and codebase snapshot itself, and receipted the delivered context in
// the immutable coordination ledger. You cannot supply any of that.

const result = await sophia.submit_evidence_envelope({ draft: prepared.draft });
// Admitted → the action row committed with the decision, then dispatched:
// { status: 'decided', decision: 'admitted', outbox_id: 'outbox:...',
//   dispatch: { state: 'succeeded', attempts: 1, ... } }

// Refused → a durable structured decision, not an exception:
// { status: 'decided', decision: 'refused', outbox_id: null,
//   primary_code: 'source.handoff_stale',
//   refusal_codes: ['source.handoff_stale'],
//   side_effects_permitted: false,
//   refusal_receipt: {
//     safe_next_action: 'Refresh and re-attest the handoff against the
//       current exact source revision.',
//     effects: { admitted_envelope_created: false,
//                requested_action_committed: false } } }

await sophia.read_evidence_envelope({
envelope_id: prepared.draft.payload.envelope_id,
envelope_version: 1,
});
// → the immutable payload plus its verified, append-only lifecycle.

The refusal is the point. It names exactly which of the nine checks failed, it certifies that nothing was created or committed, and it tells the agent the safe way forward (here: re-observe the source and re-attest, rather than retrying the same stale claim harder). A weak agent with this receipt in hand knows more than a strong agent with a stack trace.

Boundaries

The gate’s action scope today is deliberately narrow. The action contract admits any canonical sophia.* tool name in principle (proxy/src/evidenceEnvelope/actionContract.ts), but submission accepts exactly one reviewed executor in Phase 1, the coordination set_activity action, and refuses anything else as unsupported. Wider action serving is the roadmap, not the present tense. Registration is also runtime-gated: the prepare and submit tools can be withheld from every connection by a single environment switch (proxy/src/mcp/runtimeToolAvailability.ts).

“Independently verified” means the record cross-checks two artifacts that the agent could not mint alone; it does not mean the gate re-runs your tests at settlement time. The verification reads persisted verified evidence rows and server-minted work receipts and proves they agree with each other and with the envelope. If the underlying evidence row is wrong, the gate faithfully binds a wrong row. Digests bind content everywhere in this pipeline, but nothing is cryptographically signed by a per-machine key; that boundary is the same one the skills pipeline documents.

This also inherits The Security Model’s local-trust boundary. Whoever controls your OS user controls the daemon, its database, and therefore the envelopes themselves. The gate defends the integrity of the record against confused, stale, or overreaching agents; it does not defend against a person who already has your account.