{
  "$comment": "Derivation contract, built by site/build.js buildIcpDocument() from the SAME frozen AGENT_CLASSES array (site/src/agent-classes.js; canon §4.0) any runtime selector must read. One enumeration. This file DEFINES NO CLASS — it selects ids from /agent-classes.json.",
  "$comment_domain": "Live on this origin, no key: POST /translate, /validate, /hash — the stateless engine doors. The spine's stateful doors (capture, query, MCP) open with a capture key; the dated launch ledger at /what-ships-today/ is authoritative for their status.",
  "property": "epcis.dev",
  "posture": "open source; capture is $0/event — stated as intent, not as terms (see no_ask_zone.capture_price_status); allowance is abuse governance, never a bill",
  "conforms_to": {
    "epcis": "2.0.1 (pinned)",
    "cbv_event_hash": "CBV 2.0 §8.9, GS1 Digital Link normalisation",
    "schema_provenance": "validators precompiled over pinned official GS1 schemas; sha256 PINS.json",
    "gated_against": "OpenEPCIS reference vectors",
    "statement": "GS1 and EPCIS are the standards this project conforms to."
  },
  "projection_law": "The spine speaks What/Who/When/Where/Why/How natively; EPCIS 2.0 is its lossy-down projection. project(event) always validates against the official EPCIS 2.0 JSON schema.",
  "ledger": {
    "live": [
      "POST /translate",
      "POST /validate",
      "POST /hash"
    ],
    "open": "see /what-ships-today/ — the dated launch ledger",
    "never_claimed": "no conformance attestation has ever been issued"
  },
  "classification": {
    "order": [
      "self_declared_hint",
      "user_agent_face",
      "declared_fallback"
    ],
    "hint": {
      "header": "x-epcis-agent-class",
      "query": "agent_class"
    },
    "face_default_map": {
      "mcp": "verifier",
      "cli": "coding",
      "sdk": "integration",
      "unknown": "integration"
    },
    "declared_fallback": "integration"
  },
  "authority": {
    "values": [
      "deputized",
      "self-principal"
    ],
    "deputized": "the ask goes to the deputizing human; the agent carries realised value as proof",
    "self-principal": "may transact against its own Mandate ceiling"
  },
  "presence": {
    "values": [
      "local",
      "relay",
      "absent"
    ],
    "gate_clearing": {
      "local": "the CLI opens the default browser",
      "relay": "hand a URL upstream",
      "absent": "charge against the ceiling, else fail closed"
    },
    "fail_closed_rule": "Absent a pre-registered notification channel, every human-gated scope fail-closes (never a silent grant)."
  },
  "$comment_persona_axes": "authority and presence are PERSONA axes — derived per arrival, never stored (R-ah). They are published here as a contract for the runtime selector; they are NOT part of any ICP conjunction.",
  "no_ask_zone": {
    "free_because": "compute / derived / bounded",
    "verbs": [
      "translate",
      "validate",
      "hash",
      "capture"
    ],
    "capture_price": "$0/event",
    "capture_price_status": "INTENT, NOT TERMS (canon §0.13). Our intent is that recording an event never costs money. Until published terms bind it, that is a commitment we make, not a contract you hold. When prices are published this document will carry them in terms — or it will say we changed our mind."
  },
  "gates": {
    "placed_on": [
      "durable statefulness (retention, traces, custody evidence)",
      "authority (grants, seats)",
      "licensed data"
    ],
    "never_placed_on": [
      "compute"
    ]
  },
  "entry_verbs": [
    {
      "verb": "npx epcis.dev",
      "audience": "agent",
      "autonomy": "full",
      "note": "bare invocation; never @latest."
    },
    {
      "verb": "translate",
      "readOnlyHint": true
    },
    {
      "verb": "query",
      "readOnlyHint": true
    },
    {
      "verb": "get_event",
      "readOnlyHint": true
    },
    {
      "verb": "trace_epc",
      "readOnlyHint": true
    },
    {
      "verb": "capture",
      "readOnlyHint": false,
      "price": "$0/event",
      "price_status": "intent, not terms — see no_ask_zone.capture_price_status"
    }
  ],
  "$comment_door": "The MCP door re-dispatches through the same worker fetch with the same key — a door, not a second path. npx epcis.dev mcp runs the same server locally.",
  "$comment_classes": "SUBSET BY ID. This array holds ids and nothing else. Every field of a class is resolved from the ONE enumeration (canon §4.0, served at /agent-classes.json). Re-stating any of them here is the duplication that already produced four live disagreements.",
  "agent_classes_ref": "/agent-classes.json",
  "agent_classes": [
    "coding",
    "integration",
    "verifier",
    "procurement"
  ],
  "$comment_subset": "Per canon §4.0 rule 3: this property publishes every class with an ED-4 SELF-PRINCIPAL arrival, plus the Developer-principal classes. verifier and procurement are here because they arrive HERE self-principal (route B2A); they are ALSO published by visibility.cloud, where they arrive deputized by a Business (B2A2B). Same enumeration, two views. assistant is NOT here: it has no self-principal arrival.",
  "route_derivation": {
    "rule": "B2[arrives]2[terminal principal-kind]. Human in the arrives position is the unmarked default and is ELIDED — except where the terminal principal-kind is Agent, because B2A already denotes an agent arriving self-principal and the H is what disambiguates.",
    "derives_exhaustively": {
      "human→Business": "B2B",
      "human→Developer": "B2D",
      "human→Agent": "B2H2A",
      "agent→Business": "B2A2B",
      "agent→Developer": "B2A2D",
      "agent→Agent": "B2A"
    },
    "null_case": "An arrival with no ICP has no terminal principal-kind, so route is null — absent, not a value (canon §0.5b), exactly as authority is null for an all-human chain.",
    "invariant": "a route's terminal-principal letter must equal the containing ICP's principal-kind, and its arrives-letter must equal the row's workerType",
    "this_property_serves": [
      "B2D",
      "B2A2D",
      "B2A"
    ]
  }
}
