{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://agenticrail.nz/spec/receipt-schema-v3.json",
  "title": "AgenticRail Pre-Execution Enforcement Receipt — v3",
  "description": "Schema for the cryptographically signed receipt issued by the AgenticRail gate for every pre-execution enforcement decision. slp8_receipt_v3 extends slp8_receipt_v2 with four new enforcement surfaces: HUMAN_OVERRIDE (override decision audit trail), TOOL_DENY (tool provenance gate), AGENT_HANDSHAKE (inter-agent receipt handshake), and SYSTEM_HALT (multi-agent compositionality enforcement). All v2 receipts remain valid — the extension is additive and backwards compatible. Published 2026-05-29.",
  "version": "slp8_receipt_v3",
  "datePublished": "2026-05-29",
  "dateModified": "2026-07-08",
  "changeNote": "2026-07-08: added prev_receipt_hash (nullable, required) — sha256 of the immediately preceding receipt's full canonical JSON, a genuine hash chain that catches an in-place edit to any earlier receipt's content, complementing prev_receipt_id's identifier-only linkage. Same field added to slp8_receipt_v2 at receipt-schema.json; this v3 extension mirrors it. Additive — schema version unchanged (slp8_receipt_v3). The fingerprint field below is unrelated to this change (it references the /spec/surfaces/ overview) and is left as-is. 2026-07-08 (later same day): removed a reference to the Hokianga Spine from the reasons field description — AgenticRail is a sequence-enforcement and accountability layer only, not a language or data sovereignty product; the Hokianga Spine was never wired into the live enforcement engine and is withdrawn. Non-normative errata — no schema structure change.",
  "extends": "slp8_receipt_v2",
  "author": "Kade Cowper — TUARA KURI LIMITED",
  "contact": "hello@agenticrail.nz",
  "specUrl": "https://agenticrail.nz/spec/surfaces/",
  "copyright": "© 2026 TUARA KURI LIMITED. This document's text is protected by copyright. The schema it describes — and any enforcement mechanism implementing it — may be freely and independently implemented by others; publishing this schema openly is a deliberate choice to support standards engagement and independent verifiability.",
  "fingerprint": "f78767c88d227f5885a123bc35296fc970be02f1096b15bebd899c9403f60671",

  "type": "object",

  "required": [
    "ts_ms",
    "pack_id",
    "version",
    "decision",
    "reasons",
    "executed",
    "sealed",
    "meta",
    "prev_receipt_id",
    "prev_receipt_hash",
    "key_id",
    "signature_alg",
    "signature"
  ],

  "properties": {

    "ts_ms": {
      "type": "integer",
      "description": "Unix timestamp in milliseconds at which the enforcement decision was made. Used for freshness validation: |ts_ms - now| > 300,000 ms triggers STALE_TIMESTAMP denial.",
      "examples": [1747821600000]
    },

    "pack_id": {
      "type": "string",
      "pattern": "^[0-9a-f]{64}$",
      "description": "SHA-256 hex digest of the canonical pack object (alphabetically sorted keys, no extra whitespace). Serves as the receipt's unique identifier and integrity anchor.",
      "examples": ["e58338a76d00405d2f3a1b9c4e7f8a2d1c6b3e9f0a4d7c2b5e8f1a3d6c9b2e5"]
    },

    "version": {
      "type": "string",
      "enum": ["slp8_receipt_v2", "slp8_receipt_v3"],
      "description": "Receipt format version. v3 receipts may carry any of the new decision types and new denial codes. v2 receipts remain fully valid — the verification endpoint accepts both."
    },

    "decision": {
      "type": "string",
      "enum": [
        "ALLOW",
        "DENY",
        "HUMAN_OVERRIDE",
        "TOOL_DENY",
        "AGENT_HANDSHAKE",
        "SYSTEM_HALT"
      ],
      "description": "Enforcement decision. v2 values (ALLOW, DENY) retain their original semantics. HALT is NOT a decision and never appears in this field: it is a rejection returned at the boundary before enforcement runs, so no receipt is written at all. Do not confuse it with SYSTEM_HALT, which is a v3 receipt type and IS receipted. v3 adds four new decision types — one per enforcement surface introduced in slp8_receipt_v3. HUMAN_OVERRIDE: a human reviewer approved or rejected an AI decision; receipt records the override event. TOOL_DENY: a tool load was refused by the provenance gate. AGENT_HANDSHAKE: Agent B's gate validated Agent A's sealed chain and authorised the handshake. SYSTEM_HALT: supervisor gate detected compositional failure across multiple agents in a shared namespace."
    },

    "reasons": {
      "type": "array",
      "items": {
        "type": "string",
        "enum": [
          "SEQUENCE_VIOLATION",
          "REPLAY_NONCE",
          "SEALED_SEQUENCE",
          "UNKNOWN_STEP",
          "FUNCTION_STEP_MISMATCH",
          "ACTION_NOT_ALLOWED",
          "STALE_TIMESTAMP",
          "ARTIFACT_UNBOUND",
          "TOOL_NOT_REGISTERED",
          "TOOL_SIGNATURE_INVALID",
          "AGENT_HANDSHAKE_FAIL",
          "CHAIN_BREAK",
          "CIRCULAR_AUTH",
          "SPLIT_AUTHORITY"
        ]
      },
      "description": "Denial, halt, or override reason codes. v2 codes carry their original meanings. UNKNOWN_STEP (v2, core; corrected 2026-07-05, supersedes the previously-published but unreachable NO_POLICY_MATCH): step/function value not present in this sequence's own declared step_order — an unrecognized name alone falls through to a permissive generic policy so custom step_order sequences work; only a name absent from the declared step_order is rejected. ARTIFACT_UNBOUND (v2, core): a RECORD_RESULT at boundary did not supply attestation.witnessed_pack_id matching the actual immediately-preceding receipt — the witness must attest to the real prior artifact, not an unverified claim. v3 adds: TOOL_NOT_REGISTERED (tool not present in signed registry), TOOL_SIGNATURE_INVALID (tool manifest does not match registered signature), AGENT_HANDSHAKE_FAIL (Agent A's chain is broken, unsealed, or unverifiable), CHAIN_BREAK / CIRCULAR_AUTH / SPLIT_AUTHORITY (system-level failure types carried on SYSTEM_HALT receipts).",
      "examples": [[], ["ARTIFACT_UNBOUND"], ["TOOL_NOT_REGISTERED"], ["AGENT_HANDSHAKE_FAIL"], ["CIRCULAR_AUTH"]]
    },

    "executed": {
      "type": "boolean",
      "description": "Whether the proposed transition was PERMITTED (the gate's verdict). True for ALLOW, AGENT_HANDSHAKE, and HUMAN_OVERRIDE with outcome=APPROVE. False for DENY, HALT, TOOL_DENY, SYSTEM_HALT, and HUMAN_OVERRIDE with outcome=REJECT. This attests the decision only — it does NOT assert the downstream action was carried out or succeeded; the executor's outcome is not part of the signed receipt (the public API response reports it separately under `execution_submitted` / `result`)."
    },

    "sealed": {
      "type": "boolean",
      "description": "Whether this receipt completes and permanently seals the sequence. v3: SYSTEM_HALT receipts always seal — once a system halts, the namespace is retired."
    },

    "meta": {
      "type": "object",
      "description": "Enforcement context captured at decision time. No field is modified after the receipt is written. v3 receipts add optional surface-specific blocks: override, tool, handshake, system.",
      "required": [
        "model_id",
        "sequence_id",
        "step",
        "function",
        "action_type",
        "policy_map_ids"
      ],
      "properties": {
        "model_id": {
          "type": ["string", "null"],
          "examples": ["MSMD"]
        },
        "sequence_id": {
          "type": ["string", "null"],
          "examples": ["whiro-write-f2646de0f129"]
        },
        "step": {
          "type": ["string", "null"],
          "examples": ["intake", "execution", "settle"]
        },
        "function": {
          "type": ["string", "null"],
          "examples": ["intake"]
        },
        "action_type": {
          "type": ["string", "null"],
          "examples": ["CHECK_STATE", "RECORD_RESULT", "TOOL_LOAD", "OVERRIDE_DECISION", "HANDSHAKE", "SYSTEM_CHECK"]
        },
        "policy_map_ids": {
          "type": "array",
          "items": { "type": "string" }
        },
        "override": {
          "type": ["object", "null"],
          "description": "Present when decision is HUMAN_OVERRIDE. Records the override event independently of the system being overridden.",
          "required": ["reviewer_id", "rationale_hash", "review_duration_ms", "overridden_step", "outcome"],
          "properties": {
            "reviewer_id": {
              "type": "string",
              "description": "Identifier of the human reviewer. Should resolve to a verifiable identity in the operator's directory."
            },
            "rationale_hash": {
              "type": "string",
              "pattern": "^[0-9a-f]{64}$",
              "description": "SHA-256 of the reviewer's documented rationale. Rationale text stored separately; hash bound into the receipt."
            },
            "review_duration_ms": {
              "type": "integer",
              "minimum": 0,
              "description": "Elapsed time between the override request and the override decision. Systematic rubber-stamping (near-zero duration) becomes detectable across the receipt chain."
            },
            "overridden_step": {
              "type": "string",
              "description": "The step name (or pack_id reference) of the AI decision being overridden."
            },
            "outcome": {
              "type": "string",
              "enum": ["APPROVE", "REJECT"],
              "description": "The human's decision. APPROVE: AI step permitted to proceed. REJECT: AI step refused; sequence continues to next step or halts per policy."
            }
          },
          "additionalProperties": false
        },
        "tool": {
          "type": ["object", "null"],
          "description": "Present when decision is TOOL_DENY (or, optionally, on tool load ALLOW receipts). Records the tool provenance check.",
          "required": ["tool_id", "tool_hash", "registry_checked"],
          "properties": {
            "tool_id": {
              "type": "string",
              "description": "Identifier of the tool the agent attempted to load. Includes source server for MCP tools."
            },
            "tool_hash": {
              "type": "string",
              "pattern": "^[0-9a-f]{64}$",
              "description": "SHA-256 of the tool manifest as presented to the gate at load time."
            },
            "registry_checked": {
              "type": "boolean",
              "description": "Whether the gate consulted the signed tool registry. False indicates a registry unavailability — should not occur in production."
            },
            "registry_signature_id": {
              "type": ["string", "null"],
              "description": "Identifier of the signature found in the registry for this tool_id. Null when tool_id is not present in the registry."
            }
          },
          "additionalProperties": false
        },
        "handshake": {
          "type": ["object", "null"],
          "description": "Present when decision is AGENT_HANDSHAKE. Records the cross-agent chain verification.",
          "required": [
            "source_agent_id",
            "source_sequence_id",
            "source_final_receipt_id",
            "source_chain_verified",
            "receiving_agent_id"
          ],
          "properties": {
            "source_agent_id": {
              "type": "string",
              "description": "Identifier of Agent A (the source agent passing instructions)."
            },
            "source_sequence_id": {
              "type": "string",
              "description": "Sequence ID of Agent A's completed chain."
            },
            "source_final_receipt_id": {
              "type": "string",
              "pattern": "^[0-9a-f]{64}$",
              "description": "pack_id of Agent A's sealed terminal receipt. Anchors the handshake to a specific cryptographic point in Agent A's chain."
            },
            "source_chain_verified": {
              "type": "boolean",
              "description": "True if Agent B's gate independently verified Agent A's complete chain from intake to settle, including all signatures and chain links. False triggers AGENT_HANDSHAKE_FAIL."
            },
            "source_chain_break_at": {
              "type": ["string", "null"],
              "pattern": "^[0-9a-f]{64}$|^$",
              "description": "If source_chain_verified is false, the pack_id of the receipt at which chain verification failed. Null on successful handshake."
            },
            "receiving_agent_id": {
              "type": "string",
              "description": "Identifier of Agent B (the receiving agent whose gate issued this receipt)."
            }
          },
          "additionalProperties": false
        },
        "system": {
          "type": ["object", "null"],
          "description": "Present when decision is SYSTEM_HALT. Records the system-level compositional failure detected by the supervisor gate.",
          "required": [
            "system_id",
            "failing_agent_id",
            "failing_sequence_id",
            "failure_type",
            "agents_in_namespace"
          ],
          "properties": {
            "system_id": {
              "type": "string",
              "description": "Shared namespace identifier for the multi-agent system."
            },
            "failing_agent_id": {
              "type": "string",
              "description": "Identifier of the agent whose state triggered the halt."
            },
            "failing_sequence_id": {
              "type": "string",
              "description": "Sequence ID at which the failure was detected."
            },
            "failure_type": {
              "type": "string",
              "enum": ["CHAIN_BREAK", "CIRCULAR_AUTH", "SPLIT_AUTHORITY", "HANDSHAKE_FAIL"],
              "description": "Compositional failure taxonomy. CHAIN_BREAK: an agent's chain failed integrity verification. CIRCULAR_AUTH: agents granted each other authority no single agent held. SPLIT_AUTHORITY: agents received conflicting instructions purporting to come from the same source. HANDSHAKE_FAIL: an unauthorised inter-agent handshake was attempted."
            },
            "chain_break_at_receipt_id": {
              "type": ["string", "null"],
              "pattern": "^[0-9a-f]{64}$|^$",
              "description": "If failure_type is CHAIN_BREAK, the pack_id of the broken link. Null otherwise."
            },
            "agents_in_namespace": {
              "type": "integer",
              "minimum": 1,
              "description": "Count of agents participating in the shared namespace at the time of the halt."
            }
          },
          "additionalProperties": false
        }
      },
      "additionalProperties": false
    },

    "attestation": {
      "type": ["object", "null"],
      "description": "Optional signed evidence attached to this step. Unchanged from v2.",
      "examples": [null, { "chip_id": "WHIRO-2026-05-29-001" }]
    },

    "payload_hash": {
      "type": ["string", "null"],
      "pattern": "^[0-9a-f]{64}$|^$"
    },

    "prev_receipt_id": {
      "type": ["string", "null"],
      "pattern": "^[0-9a-f]{64}$|^null$",
      "description": "pack_id of the immediately preceding receipt in this sequence — an identifier reference, not a content hash (see prev_receipt_hash for that guarantee). v3: in AGENT_HANDSHAKE receipts, prev_receipt_id is the receiving agent's last receipt — the source agent's chain is referenced via meta.handshake.source_final_receipt_id, not via prev_receipt_id."
    },

    "prev_receipt_hash": {
      "type": ["string", "null"],
      "pattern": "^[0-9a-f]{64}$|^null$",
      "description": "SHA-256 hex digest of the immediately preceding receipt's full canonical JSON (signature included) — not merely its pack_id. Added 2026-07-08. An in-place edit to any earlier receipt changes its hash, breaking every subsequent prev_receipt_hash link even if pack_id references still match. Null for the first step, and null when the resolved anchor predates this field — treated as 'not verifiable', not 'broken'."
    },

    "key_id": {
      "type": "string",
      "examples": ["k1_2026-02-22_01"]
    },

    "signature_alg": {
      "type": ["string", "null"],
      "enum": ["hmac-sha256", "Ed25519", null]
    },

    "signature": {
      "type": ["string", "null"]
    }

  },

  "additionalProperties": false,

  "$defs": {

    "pack": {
      "title": "AgenticRail Enforcement Pack — v3",
      "description": "The enforcement decision object whose SHA-256 canonical hash becomes the pack_id. v3 packs may carry the new decision values and surface-specific meta blocks. pack_id = SHA-256(canonicalJson(pack)).",
      "type": "object",
      "required": ["pack_version", "decision", "reasons", "executed", "meta"],
      "properties": {
        "pack_version": {
          "type": "string",
          "enum": ["slp8_pack_1.0", "slp8_pack_1.1"],
          "description": "slp8_pack_1.0 used by v2 receipts. slp8_pack_1.1 used by v3 receipts carrying surface-specific meta blocks."
        },
        "decision": {
          "type": "string",
          "enum": [
            "ALLOW",
            "DENY",
            "HUMAN_OVERRIDE",
            "TOOL_DENY",
            "AGENT_HANDSHAKE",
            "SYSTEM_HALT"
          ]
        },
        "reasons": {
          "type": "array",
          "items": { "type": "string" }
        },
        "executed": {
          "type": "boolean"
        },
        "meta": {
          "type": "object"
        }
      },
      "additionalProperties": false
    }

  }
}
