Offline example. Supplier data, Primitive mail, and Millwork receipts are
simulated. The commands send no email and start no Millwork run or charge.
How an email handoff becomes verifiable
An agent can ask for missing details by email; once the work is ready, your output check decides whether it can move to the next agent. Your output check is a check applied to a run’s output; the API calls it a verifier. Millwork records its result. Goal: run a supplier-to-buyer quote handoff between email agents for an approved request for quotation (RFQ), check the quote before it moves on, and verify the record the receiving agent gets. You are done when:npm run cases exits 0 and prints a record verified
line, and changing one character in an earlier entry of a copy of the record
makes verification fail.
Run the offline cases
Download the checked example, run its cases with Node 22, and verify a record yourself.
Hand it to your coding agent
Give this page to your coding agent. Ask it to run the offline cases, verify that changing a copy of the record fails, and report the result.
Run the example
You need Node.js 22. The example installs its own pinned Millwork CLI withnpm ci; it needs no API key, no Primitive account and no Millwork
organization.
1
Download the checked example
Copy the downloader into
primitive-example.mjs, review it, then run it with
node primitive-example.mjs. It creates a new primitive-agent-email
directory, downloads only this docs site’s example file, and checks every file
against the digest it pins before writing anything.Copy the downloader
Copy the downloader
2
Run the cases
verify-record
command with those values filled in. The last step runs the published
millwork verifier test --local against the example’s check.3
Verify a record yourself
Run the Expected result: the same
verify-record command that npm run cases printed for
fixtures/rfq-042. The keys in it are labelled test-only keys from the example:record verified line. Now copy
record.jsonl, change one hex digit of the packet_sha256 value on the
transfer_prepared entry in the copy, and run the command against the copy: it
prints hash_mismatch and exits 1. Change the quote text in a copy of
witness.eml instead: it prints action_mismatch. Run npm run cases again;
the heads it prints do not change. The second transfer, fixtures/rfq-077, has
its own record, mail and command in the same output.Make this your case
Edit three files. Everything else is the record, theverify-record check and
the simulated services, which you keep.
After an edit, run
npm run cases. A rule change changes the recorded heads, so
the run reports which fixture no longer matches its committed record. Rewrite
one fixture’s record on purpose with
npm run cases -- --update-golden <fixture>; it prints the old and new heads
and leaves every other fixture’s record byte for byte unchanged. An ordinary
run never rewrites a record.
To serve the check from your own HTTPS endpoint later, start from Recipe D.
This page has no CLI recipe of its own, so there is no --recipe value for
it; use millwork verifier init quote-check --recipe d --json, then read
Verify agent or pipeline completion
for the evidence-read pattern this check follows.
When something fails
Each negative case innpm run cases prints a decision for the buyer agent or
a code from verify-record. A decision of reject stops that mail or handoff,
quarantine holds mail until a person releases it, and pause:<reason> stops
automation until a person reconciles it. The controller never starts a second
send or a new paid run to recover; it first looks up the earlier request by its
request key, and it stops when that lookup cannot settle what happened.
How the handoff works
The example uses these words for its parts:
For one handoff, the buyer agent freezes the transfer packet and records an
attempt ID. It sends Millwork a run request that carries only that ID. Millwork
calls your output check with
{candidate: {attempt_id}}, and the check reads
the frozen packet from your own store and returns its verdict. Millwork returns
a receipt with that verdict. The buyer agent then signs the record head and
sends the handoff mail carrying it, and the sourcing agent runs verify-record
against the mail it kept.
Routine mail, such as the clarification reply, has no Millwork run. Only the
consequential handoff is checked. Primitive moves the mail, you own the record,
the controller and the check, and Millwork supplies the check call and the
receipt. There is no central coordinator.
What the handoff mail carries
The handoff mail’s plain-text part ends with one fixed line,-- MILLWORK WITNESS v1 --, followed by the signed head, sequence number and
action. The controller refuses to send a packet whose quote, filename or other
part already contains that line. The receiving agent rebuilds the action from
its own mailbox address, the decoded subject, the checked text and the
attachment digests, and requires it to equal the signed action. Raw mail bytes
do not need to equal the submitted body.
The keeper serves its record entries separately. The receiver checks them
against chain and check public keys it pinned out of band and against the head
in the mail it kept. verify-record cannot tell you who sent a copied mail,
whether a later entry replaced the handoff, or whether the same head went out
in a second send. Decide separately whether to trust mail from the keeper’s
sender.
The run request
In a separately approved live run, the controller sends this request with a request key, so a retry does not start the work twice. The saved agent returns only the attempt ID, and the check reads the frozen packet from your own store. Thesandbox data class labels your own made-up test data; it is
not a protection and adds no isolation.
on_eval: [], a failed check records needs_review and starts no
fallback or repair. The controller accepts a receipt only when it is a
completed live run on the pinned saved agent, its final check is a pass from
your output check, and the check’s own signed observation agrees. A test run, a
basic output-presence check, or a missing receipt holds the handoff. The
example’s receipts are simulated; a real receipt match is a later step that you
approve separately.