{
  "spec": "heron-ingest-conformance",
  "version": 1,
  "note": "Known-answer vectors for the computations an integrator must get byte-exact. Generated from Heron's production code by `npm run spec`; see spec/README.md.",
  "canonicalization": [
    {
      "name": "keys_sorted",
      "note": "Keys are sorted, not emitted in insertion order. The single most common divergence: a client that hashes its own serialization gets a different hash for the same arguments.",
      "input": {
        "tool": "gmail.send",
        "seq": 4,
        "id": "act_1"
      },
      "canonical": "{\"id\":\"act_1\",\"seq\":4,\"tool\":\"gmail.send\"}"
    },
    {
      "name": "keys_sorted_nested",
      "note": "Sorting is recursive. A client that sorts only the top level passes the case above and fails on any real argument object.",
      "input": {
        "z": {
          "y": 1,
          "x": 2
        },
        "a": {
          "c": 3,
          "b": 4
        }
      },
      "canonical": "{\"a\":{\"b\":4,\"c\":3},\"z\":{\"x\":2,\"y\":1}}"
    },
    {
      "name": "array_order_preserved",
      "note": "Arrays are sequences, not sets: their order is data and is never sorted.",
      "input": {
        "recipients": [
          "c",
          "a",
          "b"
        ]
      },
      "canonical": "{\"recipients\":[\"c\",\"a\",\"b\"]}"
    },
    {
      "name": "keys_sorted_by_utf16_code_unit",
      "note": "RFC 8785 §3.2.3 sorts by UTF-16 code unit, not by locale or byte. A locale-aware sort (or Python's default, which is code point) reorders non-ASCII keys and changes the bytes.",
      "input": {
        "1": "One",
        "€": "Euro Sign",
        "\r": "Carriage Return",
        "": "Control",
        "ö": "Latin Small Letter O With Diaeresis"
      },
      "canonical": "{\"\":\"Control\",\"\\r\":\"Carriage Return\",\"1\":\"One\",\"ö\":\"Latin Small Letter O With Diaeresis\",\"€\":\"Euro Sign\"}"
    },
    {
      "name": "numbers_as_ecmascript",
      "note": "RFC 8785 §3.2.2 adopts ECMAScript number serialization. 1e21 renders in exponential form, small magnitudes do not, and a trailing .0 is never emitted — a language whose float formatter differs (Python repr, Go %v) must special-case this.",
      "input": {
        "big": 1e+21,
        "small": 1e-7,
        "half": 1.5,
        "zero": 0,
        "whole": 100
      },
      "canonical": "{\"big\":1e+21,\"half\":1.5,\"small\":1e-7,\"whole\":100,\"zero\":0}"
    },
    {
      "name": "negative_zero_is_zero",
      "note": "-0 serializes as 0. A client that preserves the sign produces different bytes for an argument no one would think to test.",
      "input": {
        "n": 0
      },
      "canonical": "{\"n\":0}"
    },
    {
      "name": "string_escapes",
      "note": "Only the escapes RFC 8785 §3.2.2.2 requires: quote, backslash, and the C0 controls. Everything else — including non-ASCII — travels literally in UTF-8, so a client that escapes to \\uXXXX for safety diverges.",
      "input": {
        "s": "a\"b\\c\nd\te\u0001f"
      },
      "canonical": "{\"s\":\"a\\\"b\\\\c\\nd\\te\\u0001f\"}"
    },
    {
      "name": "non_ascii_is_literal",
      "note": "Non-ASCII stays as UTF-8, never \\u-escaped. Surrogate pairs (an emoji) are one code point in the output.",
      "input": {
        "s": "naïve café 🕊 москва"
      },
      "canonical": "{\"s\":\"naïve café 🕊 москва\"}"
    },
    {
      "name": "literals_and_empties",
      "note": "null, booleans, and empty containers. An implementation that drops null-valued keys silently changes the hash of every action whose optional field is absent.",
      "input": {
        "a": null,
        "b": true,
        "c": false,
        "d": {},
        "e": []
      },
      "canonical": "{\"a\":null,\"b\":true,\"c\":false,\"d\":{},\"e\":[]}"
    }
  ],
  "args_hash": [
    {
      "name": "send_with_anchor",
      "note": "A realistic `args_hash`: the hash is over the arguments the tool actually receives, including the anchor token where a recipient was tokenised on the edge.",
      "args": {
        "to": "<email:600a8b954d966aaf>",
        "subject": "Subscription updated",
        "body": "Your plan renews on the 1st."
      },
      "canonical": "{\"body\":\"Your plan renews on the 1st.\",\"subject\":\"Subscription updated\",\"to\":\"<email:600a8b954d966aaf>\"}",
      "args_hash": "sha256:17066b93e17871e3da709ebb76beaad795b59147ec9e6c0b36b36d0b25b5f49e"
    },
    {
      "name": "nested_and_mixed",
      "note": "Nested objects and arrays, mixed types — the shape a real tool call has.",
      "args": {
        "rows": [
          {
            "id": 2,
            "amount": 4900
          },
          {
            "id": 1,
            "amount": 150
          }
        ],
        "dry_run": false,
        "note": null
      },
      "canonical": "{\"dry_run\":false,\"note\":null,\"rows\":[{\"amount\":4900,\"id\":2},{\"amount\":150,\"id\":1}]}",
      "args_hash": "sha256:ce144c1a6c7bf2e07cfad2edb8de6fcd9654240314c4e8f2114fff1da7d9062e"
    },
    {
      "name": "empty_args",
      "note": "A tool called with no arguments still carries a hash; it is the hash of `{}`, not an empty string.",
      "args": {},
      "canonical": "{}",
      "args_hash": "sha256:44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a"
    }
  ],
  "chain": [
    {
      "name": "session_genesis",
      "note": "The chain head at session open is the plain canonical hash of the genesis record — no predecessor to fold in.",
      "record": {
        "kind": "session",
        "org": "org_conformance",
        "session": "ses_conformance",
        "agent": "agent_billing_v2",
        "principal": {
          "type": "human",
          "ref": "user_881"
        },
        "original_request_hash": "sha256:0b37bb8ed166a6987041232758b5e84bcf2fa2ec0baefc13e15261b1873116ff",
        "started_at": "2026-07-13T12:35:52.000Z",
        "v": 2,
        "task_ref_hash": null,
        "parent_session_ref_hash": null,
        "grant_hash": null,
        "grant_scope_hash": null
      },
      "prev_hash": null,
      "record_hash": "sha256:84c0ce3c697b3585d11022c55ae8642c4d98f68e44363f8e20a75ca3b297c54e"
    },
    {
      "name": "session_genesis_v2_delegated",
      "note": "The same session having named the job it belongs to, the run that spawned it and the authority it was given. All four are digests — the references themselves never travel, so this pre-image stays publishable — and `grant_scope_hash` is over the named set as it is published, which is what lets a reviewer recompute it rather than take it. Nothing here reaches a verdict.",
      "record": {
        "kind": "session",
        "org": "org_conformance",
        "session": "ses_conformance",
        "agent": "agent_billing_v2",
        "principal": {
          "type": "human",
          "ref": "user_881"
        },
        "original_request_hash": "sha256:0b37bb8ed166a6987041232758b5e84bcf2fa2ec0baefc13e15261b1873116ff",
        "started_at": "2026-07-13T12:35:52.000Z",
        "v": 2,
        "task_ref_hash": "sha256:df13eb76abb8a1fa0e7fb8352c119836e1d2059411712581e0da5973ebaca689",
        "parent_session_ref_hash": "sha256:2c2fd6d3f925f361ea1a2811af0f860e3fa304049fb12370815bc578ee2cade4",
        "grant_hash": "sha256:3878aecd8ce47eded7c29769be67f77af241b7248e79fd8af5dbfc9673d8e668",
        "grant_scope_hash": "sha256:c6da2d36371ecd7bc0067ca57a8905833d4b2e282b4016625898678fbf9e28b3"
      },
      "prev_hash": null,
      "record_hash": "sha256:fd4054ac96ddd0137a5a0d8a57be7d17d07d0123518b4f876413612ac8c58623"
    },
    {
      "name": "session_genesis_v1_legacy",
      "note": "Format 1, kept because invariant #2 requires every existing chain to keep verifying: the genesis record was the one signed structure with no version, so v1 carries no `v` key at all and its bytes are exactly what they were. Recomputed under the version stored on the session row, never under the current one.",
      "record": {
        "kind": "session",
        "org": "org_conformance",
        "session": "ses_conformance",
        "agent": "agent_billing_v2",
        "principal": {
          "type": "human",
          "ref": "user_881"
        },
        "original_request_hash": "sha256:0b37bb8ed166a6987041232758b5e84bcf2fa2ec0baefc13e15261b1873116ff",
        "started_at": "2026-07-13T12:35:52.000Z"
      },
      "prev_hash": null,
      "record_hash": "sha256:5bd63e9f52ba6c8b87fa8c3a7efea615c04ab98e60d7c901f2bc5606810fe17b"
    },
    {
      "name": "action_v3_with_signals",
      "note": "record_hash = SHA256( JCS(record) || prev_hash_bytes ), where prev_hash is folded in as the raw 32 bytes the hex decodes to — not as its `sha256:…` text. Concatenating the string instead is the mistake this catches. v3 also folds the resource reference in as `resource_ref_hash`, never verbatim, so the record pre-image can be published for the reviewer to recompute.",
      "record": {
        "kind": "action",
        "org": "org_conformance",
        "session": "ses_conformance",
        "action": "act_conformance_1",
        "seq": 0,
        "tool": "gmail.send",
        "args_hash": "sha256:a5027f82ac0fa84f912d52284c53dd2cac390cf3049e69e88380871d5d3d801a",
        "requested_at": "2026-07-13T12:35:53.000Z",
        "v": 3,
        "resource_ref_hash": "sha256:ca06c026337f080862accb6b88c7515760d99f555744e99ba66459e2eaaf3a49",
        "signals_hash": "sha256:de457d946a5b8787928da989fe07710d669b0bb0a0bf575a46591cfec1284e6a"
      },
      "prev_hash": "sha256:84c0ce3c697b3585d11022c55ae8642c4d98f68e44363f8e20a75ca3b297c54e",
      "record_hash": "sha256:c878657b0ad109505c6c8b0cbbb6a15ddd4ed9cf765c785f7af4093fe87975af"
    },
    {
      "name": "action_v3_without_signals",
      "note": "No signals means `signals_hash: null`, not an absent key and not the hash of `{}`. Likewise `resource_ref_hash: null` when there is no reference.",
      "record": {
        "kind": "action",
        "org": "org_conformance",
        "session": "ses_conformance",
        "action": "act_conformance_1",
        "seq": 0,
        "tool": "gmail.send",
        "args_hash": "sha256:a5027f82ac0fa84f912d52284c53dd2cac390cf3049e69e88380871d5d3d801a",
        "requested_at": "2026-07-13T12:35:53.000Z",
        "v": 3,
        "resource_ref_hash": "sha256:ca06c026337f080862accb6b88c7515760d99f555744e99ba66459e2eaaf3a49",
        "signals_hash": null
      },
      "prev_hash": "sha256:84c0ce3c697b3585d11022c55ae8642c4d98f68e44363f8e20a75ca3b297c54e",
      "record_hash": "sha256:443c243e5368bc1158b56e577e34a819c569af7455c0360b2500a866503f239c"
    },
    {
      "name": "action_v2_legacy",
      "note": "Format 2, kept because invariant #2 requires every existing chain to keep verifying: a v2 action embeds `resource_ref` verbatim (no `resource_ref_hash`) and is recomputed under the version stored on the row.",
      "record": {
        "kind": "action",
        "org": "org_conformance",
        "session": "ses_conformance",
        "action": "act_conformance_1",
        "seq": 0,
        "tool": "gmail.send",
        "args_hash": "sha256:a5027f82ac0fa84f912d52284c53dd2cac390cf3049e69e88380871d5d3d801a",
        "requested_at": "2026-07-13T12:35:53.000Z",
        "resource_ref": "gmail:account_12",
        "v": 2,
        "signals_hash": "sha256:de457d946a5b8787928da989fe07710d669b0bb0a0bf575a46591cfec1284e6a"
      },
      "prev_hash": "sha256:84c0ce3c697b3585d11022c55ae8642c4d98f68e44363f8e20a75ca3b297c54e",
      "record_hash": "sha256:31b12f55371c2020f03ed93dac3f3349e78eb1226e090fe2ebfef94fe674d71a"
    },
    {
      "name": "action_v1_legacy",
      "note": "Format 1, kept for the same reason: a v1 action carries neither `v` nor `signals_hash` nor `resource_ref_hash`, embeds `resource_ref` verbatim, and is recomputed under the version stored on the row.",
      "record": {
        "kind": "action",
        "org": "org_conformance",
        "session": "ses_conformance",
        "action": "act_conformance_1",
        "seq": 0,
        "tool": "gmail.send",
        "args_hash": "sha256:a5027f82ac0fa84f912d52284c53dd2cac390cf3049e69e88380871d5d3d801a",
        "requested_at": "2026-07-13T12:35:53.000Z",
        "resource_ref": "gmail:account_12"
      },
      "prev_hash": "sha256:84c0ce3c697b3585d11022c55ae8642c4d98f68e44363f8e20a75ca3b297c54e",
      "record_hash": "sha256:6879f4c548522279aefa979a7101d41441114d5636b5894333e34ba35f5b1bd0"
    },
    {
      "name": "action_rehashed_under_wrong_predecessor",
      "note": "The same record under a different prev_hash yields a different record_hash — that is what stops a chain being re-anchored by replaying a record elsewhere.",
      "record": {
        "kind": "action",
        "org": "org_conformance",
        "session": "ses_conformance",
        "action": "act_conformance_1",
        "seq": 0,
        "tool": "gmail.send",
        "args_hash": "sha256:a5027f82ac0fa84f912d52284c53dd2cac390cf3049e69e88380871d5d3d801a",
        "requested_at": "2026-07-13T12:35:53.000Z",
        "v": 3,
        "resource_ref_hash": "sha256:ca06c026337f080862accb6b88c7515760d99f555744e99ba66459e2eaaf3a49",
        "signals_hash": "sha256:de457d946a5b8787928da989fe07710d669b0bb0a0bf575a46591cfec1284e6a"
      },
      "prev_hash": "sha256:159461a57ce09b0eae455ed087df71b8e1d20fb3a73fcf65f17450d73893c0fe",
      "record_hash": "sha256:0c99ea632cf87ee42832f4f5031fa3f3fde257b6e9d59935766b2d844fae7cf9"
    }
  ],
  "execution_evidence": [
    {
      "name": "executed",
      "note": "The signed bytes are JCS over exactly these six fields — the request body minus `signature`. Signing the whole body, or the body with `signature` present, produces a valid-looking signature we record as INVALID_VENDOR_SIGNATURE.",
      "payload": {
        "action_id": "act_conformance_1",
        "decision_id": "dec_conformance_1",
        "outcome": "EXECUTED",
        "result_hash": "sha256:f5482a39fbe0a49ff7962f56208024be9258aad19612e2742edc29c801aa9728",
        "error_code": null,
        "executed_at": "2026-07-13T12:35:54.000Z"
      },
      "canonical": "{\"action_id\":\"act_conformance_1\",\"decision_id\":\"dec_conformance_1\",\"error_code\":null,\"executed_at\":\"2026-07-13T12:35:54.000Z\",\"outcome\":\"EXECUTED\",\"result_hash\":\"sha256:f5482a39fbe0a49ff7962f56208024be9258aad19612e2742edc29c801aa9728\"}",
      "seed_base64": "AQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQE=",
      "public_key_base64url": "iojj3XQJ8ZX9UtstPLpdcspnCb8dlBIb83SIAbQPb1w",
      "signature": "gRn8weW8sdqgJwov82cEAECuREfwxOEAj2YtBNCgza2q_oJ6u4c6vFAL-X5tAX7HhKxPSrGjIGToewVXvy1JBg"
    },
    {
      "name": "blocked_with_nulls",
      "note": "Honouring a DENY is still a signed statement. `result_hash` and `error_code` are present and null — omitting a null key changes the canonical bytes and invalidates the signature.",
      "payload": {
        "action_id": "act_conformance_2",
        "decision_id": "dec_conformance_2",
        "outcome": "BLOCKED",
        "result_hash": null,
        "error_code": "heron_denied",
        "executed_at": "2026-07-13T12:35:55.000Z"
      },
      "canonical": "{\"action_id\":\"act_conformance_2\",\"decision_id\":\"dec_conformance_2\",\"error_code\":\"heron_denied\",\"executed_at\":\"2026-07-13T12:35:55.000Z\",\"outcome\":\"BLOCKED\",\"result_hash\":null}",
      "seed_base64": "AQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQE=",
      "public_key_base64url": "iojj3XQJ8ZX9UtstPLpdcspnCb8dlBIb83SIAbQPb1w",
      "signature": "rEW1UZ_d9gPo6UJ6mWpeV028b0W4KcjmbTTMYgslQ2o8lI2R6ivV67Ny3hf3oC0EdiapTzleF5aCO34MIkfpAQ"
    }
  ],
  "anchors": [
    {
      "name": "email",
      "note": "HMAC-SHA256 over `scope|type|value`, hex, first 16 chars, wrapped in `<type:…>`. A different truncation or a raw digest is a token that matches nothing.",
      "type": "email",
      "value": "maria@acme.com",
      "secret": "heron-conformance-vectors-secret",
      "scope": "vendor",
      "token": "<email:0f367fdcc159df07>"
    },
    {
      "name": "email_normalized",
      "note": "The value is trimmed and lowercased first, so the same address written differently in the request and in the arguments still compares equal — which is the entire reason the token exists.",
      "type": "email",
      "value": "  Maria@ACME.com  ",
      "secret": "heron-conformance-vectors-secret",
      "scope": "vendor",
      "token": "<email:0f367fdcc159df07>"
    },
    {
      "name": "phone",
      "note": "Phones are anchors too, and are not normalized beyond trim/lowercase — the digits as written are what is hashed.",
      "type": "phone",
      "value": "+1 (415) 555-0142",
      "secret": "heron-conformance-vectors-secret",
      "scope": "vendor",
      "token": "<phone:1d2686f311927b84>"
    }
  ],
  "anchor_text": [
    {
      "name": "request_anchors",
      "note": "`original_request.anchors` is the set of anchor tokens in the request — the request itself never travels. Two dialects of 'what is an email' produce two different tokens for one address and the comparison against the arguments silently stops working, so the patterns are part of the contract, not an implementation detail. Note the repeated address collapses to one token.",
      "secret": "heron-conformance-vectors-secret",
      "scope": "vendor",
      "text": "Email maria@acme.com and CC Maria@acme.com, then text +1 (415) 555-0142 about invoice 4900.",
      "anchors": [
        "<email:0f367fdcc159df07>",
        "<phone:1d2686f311927b84>"
      ]
    }
  ]
}
