Skip to main content
Bind a review’s verdict or a person’s approval to the exact bytes it judged, and get it back only while those bytes still match. Use it when a result or a decision will be acted on later, or by another process, and a changed input must void it. For the question itself, read Callback gates; this page is what makes an answer stick to its subject. A proof artifact has one content digest. An accepted-result record binds a review outcome to that proof and the bytes it covered. An approval record binds a stored action decision to the same kind of exact subject. While every covered byte still matches, you can reuse the result or act on the approval. Change one of those bytes and you get wait instead.

Accept a result

writeProofArtifact stores the stable JSON bytes of one proof packet, and createAcceptedResultRecord binds a review outcome to it:
examples/proof-bound-approval.ts (excerpt)
The proof reference carries the content digest, byte length, media type and proof-packet purpose. Reordered object fields produce the same bytes and digest; changed proof data produces another. You choose the packet fields and the proof scope, and writeProofArtifact doesn’t check how the proof was produced. An accepted result is stored only when the graph matches the stored run plan and the run holds exactly one dispatch for that position, followed by a completed event with the same node and result. Missing, failed or conflicting completions are refused with INVALID_STORED_VALUE. The record binds the result to input hashes, proof scope and artifact, graph definition and type version, workspace anchor, and the reviewer identity you supply; the reviewer fingerprint is the digest of that identity. Writing the same complete record again at the same position does nothing. A different binding for the same completed result there is refused with REVISION_CONFLICT. Accepted results need a WorkspaceAnchor: workspace root, repository identity, HEAD revision, content fingerprint, capture scope and file states, with schemaVersion: 1. The validators bind its bytes into the record digest but don’t validate its fields, compare it with the run’s workspace binding or verify the checkout. For a real workspace, use an anchor your workspace provider captured, and verify it before you reuse a result. The example’s /workspace anchor is made-up data to show digest matching.

Bind an approval

createApprovalCallbackGate puts the subject digest into the callback request before its identity is created, so the question is about these exact bytes:
examples/proof-bound-approval.ts (excerpt)
The subject covers input artifact hashes, proof scope and artifact, the proposed output bytes, the effective permissions, and a workspace anchor or null. post stores that exact subject with the request, and submit stores the answer and the approval record in one event batch. The record also binds the stored run plan, graph, the actor you supply, the router path and the action decision. Each effective permission must match one permission admitted by the stored run plan, name and JSON scope exactly, key order aside; the subject may use a subset.

Resolve before you act

resolveAcceptedResult and resolveApproval return the stored value only while the current subject matches the record:
examples/proof-bound-approval.ts (excerpt)
Run the file with npx tsx proof-bound-approval.ts; it calls no model and writes to a temporary directory it removes:
Output
Two accepted-result records cite one proof digest. The callback answer survived reopening the storage. The unchanged result resolves to accepted and the unchanged approval to allow; a changed workspace anchor and a changed proposed output each return wait. A wait needs fresh proof or a decision by the host, and a changed binding needs a newly completed proof position; it can’t replace the acceptance stored for an earlier completion. The host owns every effect after approval: these APIs don’t make a backup, apply output, read the effect back, reconcile an uncertain effect or merge files.

Share evidence

createProofCache serves bounded proof packets for one host process and one stored run, so fresh workers get the same evidence without reading unchanged sources again:
examples/proof-cache.ts (excerpt)
Declare each source by id with a revision() that returns an authoritative content revision and a read(expectedRevision, maxBytes) that returns complete JSON or text for that revision. The cache checks revisions before and after capture and hashes the content; it refuses an oversized packet or a capture that races a source change, and never truncates. Declare each proof job’s sources, scope and mode: only read-only jobs can request packets or stored results, and the cache executes no job. Concurrent requests share source reads, a changed source invalidates only the packets that depend on it, and a host restart starts a cold cache while stored artifacts and records stay in run storage. resolveAccepted(jobId, position, current) derives the hashes, scope and artifact from the current packet and checks the stored acceptance again with the graph, verified anchor and reviewer identity you pass. Reviewers share evidence and keep separate answers: a different reviewer identity doesn’t inherit another’s result. Run npx tsx proof-cache.ts:
Output
Two packet requests shared one artifact. The stored review was reused, refused for a different reviewer, and refused again after the configuration changed, while the independent policy packet was kept. The effectful job was refused a packet.

Failure

  • INVALID_STORED_VALUE: the run has no single completed dispatch for the position an accepted result names.
  • REVISION_CONFLICT: a different binding for a result already accepted at that position.
  • SUBJECT_MISMATCH: post throws ApprovalSubjectError for a permission outside the stored plan; submit returns invalid and resolveApproval returns wait in the same case.
  • KNOWN_SECRET: the local event store rejects a write whose reviewer identity or actor contains a configured secret, nested values and keys included. A rejected approval write stores neither the answer nor the record.

Limits

  • Identities are stored whole. The supplied reviewerIdentity and actor objects are kept beside their fingerprints, unauthenticated. Supply only non-secret identifiers; the store can’t catch a credential that isn’t on its knownSecrets list.
  • The proof cache trusts its adapters. Source declarations and revision behaviour are the host’s trusted code. A node’s retrySafe governs crash recovery; it doesn’t make a job read-only.
examples/proof-bound-approval.ts
examples/proof-cache.ts

Next steps