Guarded Automation: Agents That Run Operations Module 1 · One Action, One Moment

The Action Receipt

Last reviewed · content updated

Intermediate

What you'll learn

~15 min
  • Name every field a full action receipt carries and what each one proves
  • Read a denied and a halted receipt row and explain what each still records
  • State which controls an action receipt supports evidence for, and which lesson owns the chain itself

Before the detail — Decision: write one row for every attempted action, not only the ones that succeeded, in a store the agent cannot reach to edit. Outcome: a record that answers what an agent tried, what it was allowed to do, and what actually happened, from a place nobody has to trust the agent’s own account of. Artifact: the receipt chain, accepted when every attempted action has one primary row and every compensating action has its own linked row. Status of what follows: reusable guidance, built on a committed NON-PRODUCTION chain.

Prompt first: what is your action log actually missing

Here is one line from our system's action log:
2026-09-14 19:41 agent=mu-ai-011 action=draft_work_order result=success
List every fact this line is missing that an incident
investigation or an audit would later need: what the world looked
like immediately before the action, what it looked like immediately
after, who or what approved it, what class of action it was, and how
I would prove this exact line has not been edited since it was
written.

The prompt starts from a line most systems already log, rather than a wish list, so the gaps it surfaces are exactly the fields this lesson adds — not a rebuild of logging from scratch. Most of those gaps close with fields already sitting in a database row somewhere; the rest close only once the store itself is somewhere the agent cannot reach.

One row, in full

Call 1 of MU-AI-011’s storm-night run is quoted verbatim below from receipts/mu-ai-011.receipts.jsonl; this is the complete committed row:

{"action":"read_ranking","action_receipt_id":"AR-CB66A55CC326","actor_kind":"agent","agent":"mu-ai-011","agent_id":"mu-ai-011","approval":{"approvers":[],"consumed_request_id":null,"keys":[],"ref":null},"autonomy_level":"L3-autonomous","class":"read_only","decision":"allow","idempotent":true,"outcome":"verified","params":{},"plan_hash":"f3b94b6fd28be6aee2e6096f316b06026624389d7321ae793ec8c703e4acf429","policy_id":"envelope/mu-ai-011","policy_version":"2026-08-29.2","post_state_hash":"f3b94b6fd28be6aee2e6096f316b06026624389d7321ae793ec8c703e4acf429","pre_state_hash":"f3b94b6fd28be6aee2e6096f316b06026624389d7321ae793ec8c703e4acf429","prerequisite":{"addendum_rows":8,"addendum_unfilled":8,"handoff":"policy/assurance-handoff.json","owner":"Distribution Operations (Mgr. L. Tran)","release_record_id":null,"target_environment":"non-production-lab"},"prev_hash":"GENESIS","request_hash":"22edac80339a2c1536c89b2fcc9ed54aea6bbae7559e9df91991f4cc03cff4cb","request_id":"REQ-732F13C2E2","row_hash":"7c82f3455e6afc061ca0c2fdadf59c92df1bd22f8334e8132cc9078ed17d9034","run_id":"RUN-STORM-2026-0914","seq":1,"target":"meridian-work-management","ts":"2026-09-14T19:40:00Z","verified":true,"verify_note":"state hash unchanged","zone":"it-data"}

The full row carries fields beyond the ones the prompt above named. plan_hash, policy_id, and policy_version identify exactly which computed plan and which version of the envelope the attempt was checked against; request_hash and request_id tie the row to one specific request, never a class of requests; run_id and seq place the row inside one run, in order. The prerequisite object is its own small receipt inside the receipt: owner, release_record_id, target_environment, and how many of the eight addendum rows remain unfilled — the same facts Lesson 1.1’s ownerless refusal and Lesson 1.3’s register both depend on, captured fresh on every single attempt rather than checked once at the start of the run.

action_receipt_id is AR- followed by twelve hexadecimal characters. prev_hash identifies the preceding row; Lesson 5.1 owns insertion, deletion, reordering, recomputation, and anchor verification. Identical hashes show that the captured target state was unchanged; different hashes show only that the captured target state changed. Confirming that it changed as planned requires the retained plan, captured states, and independent verification result.

Each attempted action gets a primary receipt row; if a compensator runs, it gets a second row linked by compensates_receipt_id. A complete row lets the reviewer check the attempt without asking the agent again.

A denial keeps its class

Not every row records success, and a denied action is not a blank line. Call 3 asked to pre-position fourteen crews against an envelope maximum of three, and the row it produced still carries the facts that mattered before the denial fired:

action_receipt_id AR-EF7DF8B481D7
action pre_position_crew
class compensable
autonomy_level L1-approval-required
decision deny
deny_reason 'crews'=14 exceeds the envelope maximum 3
outcome denied
pre_state_hash null (refused before the world was captured)

The row keeps class and autonomy_level even though the action never ran: those two facts describe what was asked for, not what happened, and an assessor reading this row during a later review can tell at a glance that a compensable action was attempted and refused, without opening the envelope file to find out what kind of action it was. pre_state_hash and request_hash go null here too, for the same reason the halted row’s do: nothing was captured or planned before the envelope refused the request.

A halt marks what was not evaluated

A halted row retains request and chain context but leaves envelope-derived fields unevaluated. When the kill switch is present, the guard halts the attempt before the prerequisite and envelope checks, and the row this call produces is quoted verbatim from the same committed chain:

{"action":"draft_work_order","action_receipt_id":"AR-2C664999FF26","actor_kind":"agent","agent":"mu-ai-011","agent_id":"mu-ai-011","approval":{"approvers":[],"consumed_request_id":null,"keys":[],"ref":null},"autonomy_level":"NOT-EVALUATED-halted-before-envelope","class":null,"decision":"halted","halted_before":"the prerequisite check","idempotent":true,"outcome":"KILLED","params":{"crews":2,"feeder_id":"F-3140"},"plan_hash":null,"policy_id":"envelope/mu-ai-011","policy_version":"2026-08-29.2","post_state_hash":null,"pre_state_hash":null,"prerequisite":null,"prev_hash":"7f1587f05d7e5fc2b6d8cbbe832b4402610e1c5c881637eec2fcfeaa168d9b82","request_hash":null,"request_id":"REQ-455648C395","row_hash":"e1495d41ea403277cafc308bb10a0a3c37c6b0b883fda0cc0c18dbeecb2060c0","run_id":"RUN-STORM-2026-0914","seq":10,"target":"meridian-work-management","ts":"2026-09-14T19:48:00Z","verified":false,"zone":null}

Request and chain fields survive intact — request_id, run_id, seq, agent_id, target, params, ts, and prev_hash all still read exactly as they would on any other row. What goes to null is everything the envelope or the prerequisite check would have produced: class, zone, plan_hash, pre_state_hash, post_state_hash, request_hash, and prerequisite itself, because none of those checks ever ran. autonomy_level reads NOT-EVALUATED-halted-before-envelope rather than one of the four pinned rungs, and halted_before names exactly which check the switch pre-empted. A halted row and a denied row look different for a reason — one records a decision the envelope made, the other records that no decision was reached at all.

What the receipt is not

An action receipt is not an incident record. Operating in Production 1.4 (a separate training in this series) has no dedicated field for this id, and none is needed: its Timeline and Decisions rows are free text, and this chain’s own query tool writes action_receipt_id=AR-… straight into that cell — a human incident record cites the id in prose; it does not reach into this chain to answer questions this training already answered. “Stores ids, no join” stays the Operating in Production pattern established for trace_id and release_record_id; Zero Trust 5.1 owns transport and retention for this chain. This lab proves only that guarded actions cannot target receipts/; a production no-agent-write property additionally needs separate credentials and an append-only store.

If these controls are in Meridian’s contract or adopted control baseline, a verified receipt supports evidence for AU-10 — non-repudiation evidence for a person or acting process; AC-6(9) — privileged-function execution logging; and CM-3 — change-approval decision records. It never satisfies any of them by itself. A gapless chain strengthens the evidence package, but reviewers still require the other evidence each control specifies.

Stop and escalate when a receipt row is missing a field this lesson lists, or when class is populated on a row whose decision is halted — either one means the chain is not being written the way this module specified, and the fix belongs to whoever owns the guard, not to a note explaining the gap away.

KNOWLEDGE CHECK

An auditor asks why a halted receipt row has class=null instead of the class the action would have had if it had run. What is the correct answer?

Key takeaway

Every attempt gets an honest primary row, and every compensator gets its own linked row. The next lesson, Where the Policy Is Enforced, shows where Module 2’s envelope makes the decision recorded in each primary receipt.

LEADERSHIP DECISION every attempted action gets exactly one
receipt row, including denials and halts, in
a store the agent cannot write
PRACTITIONER ACTION check that class and autonomy_level are
recorded correctly on denied rows and left
null only on halted ones before treating a
receipt chain as audit-ready
SUCCESS MEASURE every action-taking agent's receipts map
cleanly to AU-10, AC-6(9), and CM-3 evidence
requests within the next assessment cycle -
assessor-hours saved
Search lessons