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)
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)
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)
npx tsx proof-bound-approval.ts; it calls no model and
writes to a temporary directory it removes:
Output
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)
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
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:postthrowsApprovalSubjectErrorfor a permission outside the stored plan;submitreturnsinvalidandresolveApprovalreturnswaitin 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
reviewerIdentityandactorobjects are kept beside their fingerprints, unauthenticated. Supply only non-secret identifiers; the store can’t catch a credential that isn’t on itsknownSecretslist. - The proof cache trusts its adapters. Source declarations and revision
behaviour are the host’s trusted code. A node’s
retrySafegoverns crash recovery; it doesn’t make a job read-only.
Full file: proof-bound-approval.ts
Full file: proof-bound-approval.ts
examples/proof-bound-approval.ts
Full file: proof-cache.ts
Full file: proof-cache.ts
examples/proof-cache.ts
Next steps
- A file change approved byte for byte: an approval bound to exact output bytes, with a backup and a readback.
- Callback gates: the stored client and the request identity an approval rides on.
- Workspace: capturing and verifying the anchor a result is bound to.