{"protocol":"mcp","version":"2025-06-18","name":"CodeRifts API Governance","description":"Signed, offline-verifiable decisions on API contract changes for AI agents.","tools":[{"name":"preflight_change_set","description":"Use this when: a change set of contract artifacts modifies OpenAPI, GraphQL, protobuf, AsyncAPI, MCP manifests, or agent tool schemas before merge, deploy, publish, or tool registration. Do not call for documentation-only changes, static readiness scoring, or receipt verification. Use analyze for risk only; authorize requires context.operation for permission. For receipt verification use coderifts.verify_receipt instead; for details of a past decision use coderifts.get_decision_details instead. Inputs: preflight_mode is required: \"analyze\" (risk only; no receipt, no execution_action) or \"authorize\" (may mint a receipt; requires context.operation — merge is not deploy is not publish). Supply exactly one artifact source: artifacts[] (1–20 items, each {id, type, before, after} as the FULL spec/schema text, not a path or URL; type is openapi|graphql|grpc|asyncapi|mcp_manifest|agent_tools) XOR derivation=\"server\" (server reads GitHub Compare; needs context.repository + context.base + context.head; sending artifacts[] together is 400). Grant fields (include_execution_grant, grant_version, tenant_id, executor_id, adapter_id, target_uri, expected_state_token, state_nonce, policy_hash) apply only when preflight_mode=\"authorize\" AND include_execution_grant=true; analyze ignores them. previous_receipt is a chain token base64url(body).base64url(signature) to LINK a prior decision — it does not re-verify; call verify_receipt for that. idempotency_key replays authorize only (24h), never analyze.","inputSchema":{"type":"object","properties":{"artifacts":{"type":"array","description":"1–20 contract documents analyzed together. Each item is {id, type, before, after} where before/after are the FULL document strings (YAML/JSON/proto text), not URLs or file paths. Omit this array entirely when derivation=\"server\".","minItems":1,"maxItems":20,"items":{"type":"object","properties":{"id":{"type":"string","description":"Caller-chosen id, unique within the bundle"},"type":{"type":"string","enum":["openapi","graphql","grpc","asyncapi","mcp_manifest","agent_tools"],"description":"Artifact kind. One of openapi, graphql, grpc, asyncapi, mcp_manifest, agent_tools. Determines how before/after text is parsed. Not inferred from the filename."},"before":{"type":"string","description":"The baseline document as raw text (the spec/schema/manifest body). Empty string means \"no prior version\" (create), not \"load from disk\"."},"after":{"type":"string","description":"The proposed document as raw text, same kind as before. Must be the bytes you intend to merge/deploy/publish, not a diff."}},"required":["id","type","before","after"]}},"context":{"type":"object","description":"Optional apply-site context folded into the bundle fingerprint. operation distinguishes merge vs deploy vs publish (and other labels); the server accepts any non-empty string; conventional values: merge, deploy, tool_call, publish. The receipt/gate must match this label.","properties":{"operation":{"type":"string","description":"Application operation for this change set (fingerprint + envelope). Server accepts any non-empty string; conventional values: merge, deploy, tool_call, publish. Merge is not deploy is not publish — the receipt/gate must match this label."},"environment":{"type":"string","description":"Target environment (e.g. production, staging, npm) — optional; folded into fingerprint when set."},"repository":{"type":"string","description":"Repository identity (optional; fingerprint context)"},"branch":{"type":"string","description":"Branch name (optional; fingerprint context)"},"pull_request":{"description":"Pull request id when applicable (optional; fingerprint context)","oneOf":[{"type":"string"},{"type":"number"}]},"policy_profile":{"type":"string","description":"Policy profile name (optional; fingerprint context)"},"base":{"type":"string","description":"Base commit/ref SHA the change set was computed against (optional; PR/commit identity)"},"head":{"type":"string","description":"Head commit/ref SHA of the proposed change (optional; PR/commit identity)"},"target_id":{"type":"string","description":"Optional apply-site target (IntentContext parity; not folded into the bundle fingerprint)"},"target_uri":{"type":"string","description":"Optional apply-site URI the cr.exec.v2 grant binds (handler fallback: input.target_uri, then context.target_uri, then repository/head-derived). Distinct from target_id."},"fingerprint":{"type":"string","description":"Optional change fingerprint (IntentContext parity; not folded into the bundle fingerprint)"},"audience":{"type":"string","description":"Optional audience (IntentContext parity; REST/MCP accept, server-derived audience still wins on the envelope)"}}},"previous_receipt":{"type":"string","description":"Optional prior chain token to LINK this call into a receipt chain: base64url(body).base64url(signature). Linking is not verification — a linked token is not re-checked here; use verify_receipt."},"include_execution_grant":{"type":"boolean","description":"Authorize only. When true on an allow-class authorize, the response includes a signed execution_grant, or HTTP 503 SIGNER_UNAVAILABLE — never an unsigned grant. Default false. Analyze ignores this flag. Ignored unless preflight_mode=\"authorize\"."},"state_nonce":{"type":"string","description":"Authorize+grant only. Opaque nonce copied into the signed grant as its own field (not folded into scope_hash). Absent → BEARER grant. Ignored unless include_execution_grant is true."},"grant_version":{"type":"string","enum":["v1","v2"],"x-coderifts-effective-default":"v1","x-coderifts-default-changes-at":"2026-09-18","description":"Grant envelope to mint when include_execution_grant is true. Omitting this yields cr.exec.v1 until 2026-09-18 and cr.exec.v2 on and after it (see x-coderifts-effective-default / x-coderifts-default-changes-at). The response meta.grant_version is the version actually issued. An explicit value always wins — pin \"v1\" to keep current behaviour with no code change on the date."},"tenant_id":{"type":"string","description":"Authorize+grant v2 only. Tenant the grant is issued under. ASCII slug. When omitted the server uses \"default\" — pin it if you are not on the default tenant."},"executor_id":{"type":"string","description":"Authorize+grant v2 only. Executor identity the grant is bound to (example: agent:ci-bot, host:github-actions). Empty/absent is not \"any executor\"."},"adapter_id":{"type":"string","description":"Authorize+grant v2 only. Adapter that will apply the change. Conventional values: fs, postgres, git. Must match the adapter the executor actually uses; a git grant does not authorize an fs write."},"target_uri":{"type":"string","description":"Authorize+grant v2 only. URI the grant binds (example: git://owner/repo.git/refs/heads/main). Fallback if omitted: context.target_uri, then repository/head-derived. Distinct from context.target_id."},"expected_state_token":{"type":"string","description":"Authorize+grant v2 only. Compare-and-swap token the executor must observe at apply time (the \"before\" state). Signed as its own field. Omit only if the adapter has no prior state; do not send a placeholder."},"audience":{"type":"string","description":"Requester identity for the decision envelope. Accepted here; the server-derived audience wins when both are present."},"policy_hash":{"type":"string","description":"Authorize+grant v2 only. Policy identity bound into the grant, sha256: + 64 hex. When supplied, apply must use that same policy; a different policy is a different grant."},"idempotency_key":{"type":"string","description":"Optional client key; in authorize mode, a repeat with the same key + body replays the original decision (24h). Analyze responses are not replayed."},"preflight_mode":{"type":"string","enum":["analyze","authorize"],"description":"REQUIRED. \"analyze\" = informational risk only (no decision/execution_action/safe_for_agent; analysis_outcome + may_execute:false). \"authorize\" = operation-bound path; may mint a receipt (requires context.operation). Decision Spec 2.0: omission is an error unless decision_spec_version is '1.0' (30-day legacy pin with soft-default analyze)."},"decision_spec_version":{"type":"string","enum":["1.0","2.0"],"description":"Optional pin. '1.0' = legacy contract (soft-default mode + analyze still carries decision/execution_action) until the sunset date. Omit or '2.0' = current contract."},"derivation":{"type":"string","enum":["server"],"description":"\"server\" = the server derives artifacts[] from GitHub Compare via the App installation. Allowed only when context.repository, context.base and context.head are all present and the tenant has a proven binding for that repository. Do not send artifacts[] in the same call (400 — one source of truth). Omit this field for the caller-supplied artifacts[] path."}},"required":["preflight_mode"],"if":{"properties":{"preflight_mode":{"const":"authorize"}},"required":["preflight_mode"]},"then":{"required":["context"],"properties":{"context":{"type":"object","required":["operation"],"properties":{"operation":{"type":"string","minLength":1}}}}}},"annotations":{"readOnlyHint":false,"openWorldHint":false},"outputSchema":{"type":"object","oneOf":[{"type":"object","description":"ANALYZE mode (Decision Spec 2.0): informational only. MUST NOT contain decision, execution_action, safe_for_agent, chain_receipt, execution_grant, decision_result, control_envelope, or required_action_core. Branch on analysis_outcome; never treat as permission. Field set GENERATED from preflight-response.v2.producer.json (agent-facing allowlist).","properties":{"preflight_mode":{"const":"analyze"},"analysis_outcome":{"type":"string","enum":["NO_BREAK_DETECTED","BREAKS_DETECTED","ANALYSIS_FAILED"],"description":"Closed analysis outcome set derived from engine-visible state only."},"authorization_effect":{"const":"NONE","description":"Analyze never authorizes; always NONE."},"may_execute":{"const":false,"description":"Analyze never grants execute permission."},"receipt_kind":{"const":"NONE","description":"Analyze never mints a receipt."},"analysis_control":{"type":"object"},"risk_score":{"type":"integer","minimum":0,"maximum":100},"breaking_changes":{"type":"integer","minimum":0},"requires_migration":{"type":"boolean"},"evidence_quality":{"type":"string"},"patterns":{"type":"array","items":{"type":"string"}},"pattern_sources":{"type":"array"},"detected_patterns":{"type":"array","description":"GOVERNANCE detector detail rows, emitted by the pattern detectors and validated against the decision-spec field contract before they leave the server. Row shape measured live: name, severity, description, consequence, affected_path, affected_field; optional side (request|response) on ENUM_NARROWING. Names ⊆ patterns (not equality). Agent-detector names may appear only in patterns. Free-text fields are untrusted.","items":{"type":"object","additionalProperties":false,"required":["name","severity","description","consequence","affected_path","affected_field"],"properties":{"name":{"type":"string","description":"Governance pattern name; appears in patterns when both are carried."},"severity":{"type":"string","description":"Row severity from the pattern catalog (observed set: CRITICAL, HIGH, MEDIUM). NOT a closed control enum — branch on execution_action, never on this."},"description":{"type":"string","description":"What the detector matched. Untrusted free text."},"consequence":{"type":"string","description":"What breaks for a consumer if this ships. Untrusted free text."},"affected_path":{"type":"string","description":"Contract path this row is about. Empty string when the detector had none — the key is always emitted."},"affected_field":{"type":"string","description":"Field within affected_path. Empty string when the detector had none — the key is always emitted."},"side":{"type":"string","enum":["request","response"],"description":"Optional; currently set on ENUM_NARROWING only. Request-side narrowing is agent-breaking (threaded so safe_for_agent can distinguish it). Absent when the detector did not set it."}}}},"breaking_changes_details":{"type":"array","description":"Per-change IR/detail rows, mapped from the engine's change IR. Measured row keys: type, path, method, field, severity, description. Distinct from breaking_changes (integer count).","items":{"type":"object","additionalProperties":true,"properties":{"type":{"type":"string","description":"Change kind / IR type code (e.g. response.body.property.remove)."},"path":{"type":"string"},"method":{"type":"string"},"field":{"type":"string"},"severity":{"type":"string"},"description":{"type":"string"}}}},"severity_summary":{"type":"object","description":"Bundle severity axes, computed once per change set. Distinct axes, not contradictory. Measured keys: diff_severity, governance_severity, policy_effect, note.","additionalProperties":false,"properties":{"diff_severity":{"type":"string","description":"Structural size of the schema change."},"governance_severity":{"type":"string","description":"How the rule engine rates the change."},"policy_effect":{"type":"string","description":"Resulting decision effect label."},"note":{"type":"string"}}},"decision_basis":{},"analysis":{"type":"object","description":"Tier-2 analysis mirror, assembled by the response builder alongside the control surface. Dual-write of the flat analysis fields present on the verdict, plus remediations[]. PROPERTIES ARE GENERATED — do not hand-edit them. OPEN BY DESIGN: additionalProperties stays TRUE and this is not an oversight. The fields above are copied conditionally, so which of them appear depends on the input — a verdict with no PII findings simply omits pii_findings. Closing this object would turn every future analysis field into a breaking change that fails inside the consumer, and would reject exactly the verdict paths that no one sampled when the union was built. Declared, not closed: you can now see what you may get, and you must still tolerate more.","properties":{"breaking_changes":{"type":"integer"},"breaking_changes_details":{"type":"array"},"change_ir":{"type":"array"},"changelog":{"type":"array"},"compatibility_suggestions":{"type":"array"},"coverage_gap":{"type":"object","additionalProperties":true},"coverage_gap_reason":{"type":"string"},"decision_basis":{"type":"object","additionalProperties":true},"degraded":{"type":"boolean"},"detected_patterns":{"type":"array"},"evidence_quality":{"type":"string"},"fallback_reason":{"type":"string"},"non_breaking_changes":{"type":"array"},"pattern_sources":{"type":"array"},"patterns":{"type":"array"},"pii_findings":{"type":"array"},"policy_violations":{"type":"array"},"remediations":{"type":"array","items":{"type":"object","description":"One remediation for one detected breaking change. `instruction` is imperative prose that a model may read and act on; it is generated per input and is NOT a fixed string, so the schema declares that the field exists and does not pin its text.","properties":{"change_type":{"type":"string"},"precise_label":{"type":"string"},"target":{"type":"string"},"target_ref":{"type":"object","additionalProperties":true},"recommended_transform":{"type":"string"},"effort":{"type":"string"},"instruction":{"type":"string","description":"Imperative remediation text. Model-visible. Generated per input; not a closed vocabulary."},"evidence":{"type":"object","additionalProperties":true}},"additionalProperties":true}},"requires_migration":{"type":"boolean"},"risk_dimensions":{"type":"object","additionalProperties":true},"risk_level":{"type":"string"},"risk_score":{"type":"integer"},"security_findings":{"type":"array"},"semver_suggestion":{"type":"string"},"should_block":{"type":"boolean"},"stats":{"type":"object","additionalProperties":true},"token_cost_impact":{"type":"object","additionalProperties":true}},"additionalProperties":true},"human_report":{"type":"object","description":"Human-readable report tier, assembled by the response builder; analyze returns a reduced form. Measured keys: summary, breaking_highlights, suggestions, next_steps_prose.","additionalProperties":false,"properties":{"summary":{"type":"string"},"breaking_highlights":{"type":"array"},"suggestions":{"type":"array"},"next_steps_prose":{"type":"string"}}},"verdict_fingerprint":{"type":"string"},"bundle_fingerprint":{"type":"string"},"decision_spec_version":{"type":"string","description":"Decision Spec major for this response (typically '2.0')."},"artifacts":{"type":"array"},"evidence":{"type":"array"},"operation":{},"timestamp":{"type":"string"},"scorer_version":{"type":["string","null"],"description":"Fingerprint-bound scorerVersion() (observation; not permission)."},"calibration_version":{"type":["string","null"]},"policy_pin_status":{"type":["object","null"],"additionalProperties":true},"blast_radius":{"type":"object","additionalProperties":false,"description":"Additive COUNTS (not a score). Pure function of the change-set + request graphs. Not in the verdict_fingerprint preimage.","required":["endpoints","fields","params","consumers_declared","consumers_observed","graph_source"],"properties":{"endpoints":{"type":"integer","minimum":0},"fields":{"type":"integer","minimum":0},"params":{"type":"integer","minimum":0},"consumers_declared":{"type":"integer","minimum":0},"consumers_observed":{"type":"integer","minimum":0},"graph_source":{"type":"string","enum":["none","declared","observed","declared+observed"]}}}},"required":["preflight_mode","analysis_outcome","authorization_effect","may_execute","receipt_kind","decision_spec_version"],"not":{"anyOf":[{"required":["decision"]},{"required":["execution_action"]},{"required":["safe_for_agent"]},{"required":["chain_receipt"]},{"required":["execution_grant"]},{"required":["decision_result"]},{"required":["control_envelope"]},{"required":["required_action_core"]}]},"additionalProperties":true},{"type":"object","description":"AUTHORIZE mode: operation-bound decision. Branch on execution_action (not decision, not safe_for_agent). Field set GENERATED from preflight-response.v2.producer.json (agent-facing allowlist). Compatibility: https://coderifts.com/schemas/decision-result.v1.consumer.json","properties":{"preflight_mode":{"const":"authorize"},"receipt_kind":{"type":"string","enum":["operation_authorization","NONE"],"description":"operation_authorization when a chain receipt was issued; NONE if signer unconfigured."},"decision":{"type":"string","enum":["ALLOW","WARN","REQUIRE_APPROVAL","BLOCK"],"description":"Compatibility mirror of control_envelope.decision (same value). Prefer control_envelope for branching; use decision as explanation only."},"execution_action":{"type":"string","enum":["CONTINUE","CONTINUE_WITH_MONITORING","REQUEST_APPROVAL","STOP"],"description":"Compatibility mirror of control_envelope.execution_action (same value). Canonical branch key; unrecognised values are not permission (fail closed)."},"safe_for_agent":{"type":"boolean","description":"Compatibility mirror of control_envelope.safe_for_agent (same value). Not a branch key — do not branch on safe_for_agent (use execution_action)."},"control_envelope":{"type":"object","description":"Branch source (control/1.0). Machine-control surface from attachControlSurface / buildControlEnvelope. Agents and @coderifts/agent-guard branch on control_envelope.execution_action. Top-level decision/safe_for_agent/execution_action mirror these values for compatibility. Includes next_agent_step (structured remediation SUGGESTION derived from execution_action + required_action; null on CONTINUE*; not permission — still branch on execution_action)."},"risk_score":{"type":"integer","minimum":0,"maximum":100},"breaking_changes":{"type":"integer","minimum":0},"requires_migration":{"type":"boolean"},"evidence_quality":{"type":"string"},"patterns":{"type":"array","items":{"type":"string"}},"pattern_sources":{"type":"array"},"detected_patterns":{"type":"array","description":"GOVERNANCE detector detail rows, emitted by the pattern detectors and validated against the decision-spec field contract before they leave the server. Row shape measured live: name, severity, description, consequence, affected_path, affected_field; optional side (request|response) on ENUM_NARROWING. Names ⊆ patterns (not equality). Agent-detector names may appear only in patterns. Free-text fields are untrusted.","items":{"type":"object","additionalProperties":false,"required":["name","severity","description","consequence","affected_path","affected_field"],"properties":{"name":{"type":"string","description":"Governance pattern name; appears in patterns when both are carried."},"severity":{"type":"string","description":"Row severity from the pattern catalog (observed set: CRITICAL, HIGH, MEDIUM). NOT a closed control enum — branch on execution_action, never on this."},"description":{"type":"string","description":"What the detector matched. Untrusted free text."},"consequence":{"type":"string","description":"What breaks for a consumer if this ships. Untrusted free text."},"affected_path":{"type":"string","description":"Contract path this row is about. Empty string when the detector had none — the key is always emitted."},"affected_field":{"type":"string","description":"Field within affected_path. Empty string when the detector had none — the key is always emitted."},"side":{"type":"string","enum":["request","response"],"description":"Optional; currently set on ENUM_NARROWING only. Request-side narrowing is agent-breaking (threaded so safe_for_agent can distinguish it). Absent when the detector did not set it."}}}},"breaking_changes_details":{"type":"array","description":"Per-change IR/detail rows, mapped from the engine's change IR. Measured row keys: type, path, method, field, severity, description. Distinct from breaking_changes (integer count).","items":{"type":"object","additionalProperties":true,"properties":{"type":{"type":"string","description":"Change kind / IR type code (e.g. response.body.property.remove)."},"path":{"type":"string"},"method":{"type":"string"},"field":{"type":"string"},"severity":{"type":"string"},"description":{"type":"string"}}}},"severity_summary":{"type":"object","description":"Bundle severity axes, computed once per change set. Distinct axes, not contradictory. Measured keys: diff_severity, governance_severity, policy_effect, note.","additionalProperties":false,"properties":{"diff_severity":{"type":"string","description":"Structural size of the schema change."},"governance_severity":{"type":"string","description":"How the rule engine rates the change."},"policy_effect":{"type":"string","description":"Resulting decision effect label."},"note":{"type":"string"}}},"decision_basis":{},"analysis":{"type":"object","description":"Tier-2 analysis mirror, assembled by the response builder alongside the control surface. Dual-write of the flat analysis fields present on the verdict, plus remediations[]. PROPERTIES ARE GENERATED — do not hand-edit them. OPEN BY DESIGN: additionalProperties stays TRUE and this is not an oversight. The fields above are copied conditionally, so which of them appear depends on the input — a verdict with no PII findings simply omits pii_findings. Closing this object would turn every future analysis field into a breaking change that fails inside the consumer, and would reject exactly the verdict paths that no one sampled when the union was built. Declared, not closed: you can now see what you may get, and you must still tolerate more.","properties":{"breaking_changes":{"type":"integer"},"breaking_changes_details":{"type":"array"},"change_ir":{"type":"array"},"changelog":{"type":"array"},"compatibility_suggestions":{"type":"array"},"coverage_gap":{"type":"object","additionalProperties":true},"coverage_gap_reason":{"type":"string"},"decision_basis":{"type":"object","additionalProperties":true},"degraded":{"type":"boolean"},"detected_patterns":{"type":"array"},"evidence_quality":{"type":"string"},"fallback_reason":{"type":"string"},"non_breaking_changes":{"type":"array"},"pattern_sources":{"type":"array"},"patterns":{"type":"array"},"pii_findings":{"type":"array"},"policy_violations":{"type":"array"},"remediations":{"type":"array","items":{"type":"object","description":"One remediation for one detected breaking change. `instruction` is imperative prose that a model may read and act on; it is generated per input and is NOT a fixed string, so the schema declares that the field exists and does not pin its text.","properties":{"change_type":{"type":"string"},"precise_label":{"type":"string"},"target":{"type":"string"},"target_ref":{"type":"object","additionalProperties":true},"recommended_transform":{"type":"string"},"effort":{"type":"string"},"instruction":{"type":"string","description":"Imperative remediation text. Model-visible. Generated per input; not a closed vocabulary."},"evidence":{"type":"object","additionalProperties":true}},"additionalProperties":true}},"requires_migration":{"type":"boolean"},"risk_dimensions":{"type":"object","additionalProperties":true},"risk_level":{"type":"string"},"risk_score":{"type":"integer"},"security_findings":{"type":"array"},"semver_suggestion":{"type":"string"},"should_block":{"type":"boolean"},"stats":{"type":"object","additionalProperties":true},"token_cost_impact":{"type":"object","additionalProperties":true}},"additionalProperties":true},"human_report":{"type":"object","description":"Human-readable report tier, assembled by the response builder; analyze returns a reduced form. Measured keys: summary, breaking_highlights, suggestions, next_steps_prose.","additionalProperties":false,"properties":{"summary":{"type":"string"},"breaking_highlights":{"type":"array"},"suggestions":{"type":"array"},"next_steps_prose":{"type":"string"}}},"chain_receipt":{"type":"string"},"execution_grant":{"type":"string","description":"Opt-in cr.exec.v1 execution grant (PHASE-0). Issued only when include_execution_grant is true on authorize. Short-lived mutation-bound sibling of chain_receipt; never unsigned. Optional inner state_nonce (ATOMIC profile) is additive and is NOT in scope_hash."},"chain_status":{"type":"string"},"decision_result":{"type":"object","description":"decision-result.v1 envelope (control enums closed). Additive fields may appear and are not permission. Full schema: https://coderifts.com/schemas/decision-result.v1.consumer.json (producer: https://coderifts.com/schemas/decision-result.v1.producer.json).","properties":{"spec_version":{"type":"string","pattern":"^decision-result\\.v1(\\.[0-9]+)?$"},"decision":{"type":"string","enum":["ALLOW","WARN","REQUIRE_APPROVAL","BLOCK"]},"execution_action":{"type":"string","enum":["CONTINUE","CONTINUE_WITH_MONITORING","REQUEST_APPROVAL","STOP"]},"safe_for_agent":{"type":"boolean"},"decision_id":{"type":"string"},"fingerprint":{"type":"string"},"input_fingerprint":{"type":"string"},"decision_body_hash":{"type":["string","null"]},"operation":{"type":["string","null"]},"environment":{"type":["string","null"]},"repository":{"type":["string","null"]},"base":{"type":["string","null"]},"head":{"type":["string","null"]},"audience":{"type":["string","null"]},"authority":{"type":["object","null"],"description":"Additive. { audience, tenant_scope: bound|unbound, binding_proven_at? }. Informational — not permission, not a verify-receipt gate, not an ACL."},"derivation":{"type":["object","null"],"description":"Additive. Present only when derivation:\"server\" produced this envelope. { source, platform?, base_sha, head_sha }. Covered by body_hash; not fingerprint."},"receipt":{"type":"object"},"expires_at":{"type":"string"},"blast_radius":{"type":"object","description":"Additive COUNTS (not a score). Not permission.","properties":{"endpoints":{"type":"integer","minimum":0},"fields":{"type":"integer","minimum":0},"params":{"type":"integer","minimum":0},"consumers_declared":{"type":"integer","minimum":0},"consumers_observed":{"type":"integer","minimum":0},"graph_source":{"type":"string"}}}},"additionalProperties":true},"verdict_fingerprint":{"type":"string"},"bundle_fingerprint":{"type":"string"},"decision_spec_version":{"type":"string","description":"Decision Spec major for this response (typically '2.0')."},"coderifts_version":{"type":"string"},"artifacts":{"type":"array"},"evidence":{"type":"array"},"operation":{},"timestamp":{"type":"string"},"scorer_version":{"type":["string","null"],"description":"Fingerprint-bound scorerVersion() (same as decision_result.scorer_version / FP preimage)."},"calibration_version":{"type":["string","null"],"description":"Calibration model version when set; null until a calibrated model ships."},"policy_pin_status":{"type":["object","null"],"description":"policy_pin observation (898). match null=no pin; false=drift warning (non-blocking).","additionalProperties":true},"blast_radius":{"type":"object","additionalProperties":false,"description":"Additive COUNTS (not a score). Pure function of the change-set + request graphs. Not in the verdict_fingerprint preimage.","required":["endpoints","fields","params","consumers_declared","consumers_observed","graph_source"],"properties":{"endpoints":{"type":"integer","minimum":0},"fields":{"type":"integer","minimum":0},"params":{"type":"integer","minimum":0},"consumers_declared":{"type":"integer","minimum":0},"consumers_observed":{"type":"integer","minimum":0},"graph_source":{"type":"string","enum":["none","declared","observed","declared+observed"]}}}},"required":["preflight_mode","decision","execution_action","safe_for_agent","receipt_kind","decision_spec_version"],"additionalProperties":true,"allOf":[{"if":{"properties":{"receipt_kind":{"const":"operation_authorization"}},"required":["receipt_kind"]},"then":{"required":["chain_receipt"]}},{"if":{"properties":{"execution_action":{"enum":["CONTINUE","CONTINUE_WITH_MONITORING"]}},"required":["execution_action"]},"then":{"properties":{"receipt_kind":{"const":"operation_authorization"}},"required":["receipt_kind","chain_receipt","decision_result"]}}]}]}},{"name":"verify_receipt","description":"Verify a CodeRifts signed chain-receipt you ALREADY HOLD: cryptographic\nauthenticity (signature + key id), body binding, and — when lifecycle indices\nare available — whether it is currently valid authorization (not expired,\nsuperseded, or revoked) for a stated operation/target.\n\nUse this when:\n- You already obtained a chain_receipt / receipt token from a prior preflight\n  (or CI artifact) and are about to act (merge/deploy) under that receipt.\n- A contract-gate or policy requires offline/online proof that the receipt is\n  authentic for this change before proceeding.\n- You must distinguish \"signature ok\" from \"currently authorized\" (stale or\n  superseded receipts must not be treated as live approval).\n\nDo not use when:\n- You do not have a receipt yet — call coderifts.preflight_change_set first.\n- You need a NEW decision for a changed base→head set — preflight again;\n  verify_receipt does not re-diff specs.\n- The receipt you hold binds a different operation or target than the one you\n  are about to perform — call coderifts.preflight_change_set with\n  context.operation set to that operation (a merge receipt does not authorize\n  a deploy); verify_receipt cannot re-scope or re-issue a decision.\n- You only need human-readable history of an old decision_id without a receipt\n  token — use coderifts.get_decision_details.\n- The change set itself is unknown or incomplete — fix the change set and\n  preflight; do not \"verify\" a placeholder.\n\nInputs: receipt token (required); optional intended context (operation, environment,\nfingerprint, target_id, audience, repository/branch/pull_request, base/head) and the\nbody_hash-bound decision_result envelope. 30s clock-skew leeway on expiry. A 0s\ngrace for declared destructive production operations is defined in the policy but\nis unreachable today: the intended-context schema has no destructive field, so\nnothing can declare one and the 30s leeway always applies.\nReturns { valid, status, currently_authorized (bool|null), reason, payload, authz_* }.\nBranch on currently_authorized; null = not evaluated.\n\nWhen a decision envelope is also in hand (e.g. from a prior preflight), its\ncontrol_envelope.next_agent_step (if present) is structured remediation guidance\nthe agent MAY follow after a non-CONTINUE decision — still branch on\nexecution_action; next_agent_step is suggestion, not permission.","inputSchema":{"type":"object","description":"Two evaluation modes (schema-documented; no mode discriminator field). SIGNATURE: supply token only — signature + expiry; currently_authorized is null. AUTHORIZATION: also supply intended context (operation, environment, fingerprint, target_id, audience, repository/branch/pull_request, and/or base/head) AND the body_hash-bound decision_result envelope so currently_authorized / authz_status / authz_reason can be evaluated. Token alone is always accepted; omitting the envelope when context fields are present yields a signature verdict plus fail-closed authorization (currently_authorized false), not a schema reject.","properties":{"token":{"type":"string","description":"The chain receipt token (base64url(body).base64url(signature))"},"operation":{"type":"string","description":"Intended operation the receipt must authorize (conventional: merge|deploy|tool_call|publish). Triggers authorization evaluation when non-empty; supply decision_result for full scope binding."},"target_id":{"type":"string","description":"Intended apply-site target the receipt must bind"},"environment":{"type":"string","description":"Intended environment (e.g. production) — must match the receipt"},"fingerprint":{"type":"string","description":"Intended change fingerprint — must equal the receipt fp"},"audience":{"type":"string","description":"Intended audience — must match the receipt"},"repository":{"type":"string","description":"Intended repository the receipt must bind (place binding; optional)"},"branch":{"type":"string","description":"Intended branch the receipt must bind (place binding; optional)"},"pull_request":{"description":"Intended pull-request id the receipt must bind (place binding; optional)","oneOf":[{"type":"string"},{"type":"number"}]},"base":{"type":"string","description":"Intended base commit/ref SHA the receipt must match (signed-wins vs envelope.base)"},"head":{"type":"string","description":"Intended head commit/ref SHA the receipt must match (signed-wins vs envelope.head)"},"decision_result":{"type":"object","description":"The body_hash-bound decision envelope (carries operation/target/decision). Required for a meaningful AUTHORIZATION evaluation of scope; without it, intended context alone fails closed on authorization (currently_authorized false) while signature status remains independent. Full schema: https://coderifts.com/schemas/decision-result.v1.consumer.json.","properties":{"spec_version":{"type":"string","pattern":"^decision-result\\.v1(\\.[0-9]+)?$"},"decision":{"type":"string","enum":["ALLOW","WARN","REQUIRE_APPROVAL","BLOCK"]},"execution_action":{"type":"string","enum":["CONTINUE","CONTINUE_WITH_MONITORING","REQUEST_APPROVAL","STOP"]},"safe_for_agent":{"type":"boolean"},"decision_id":{"type":"string"},"fingerprint":{"type":"string"},"input_fingerprint":{"type":"string"},"decision_body_hash":{"type":["string","null"]},"operation":{"type":["string","null"]},"environment":{"type":["string","null"]},"repository":{"type":["string","null"]},"base":{"type":["string","null"]},"head":{"type":["string","null"]},"audience":{"type":["string","null"]},"authority":{"type":["object","null"],"description":"Additive. { audience, tenant_scope: bound|unbound, binding_proven_at? }. Informational — not permission, not a verify-receipt gate, not an ACL."},"derivation":{"type":["object","null"],"description":"Additive. Present only when derivation:\"server\" produced this envelope. { source, platform?, base_sha, head_sha }. Covered by body_hash; not fingerprint."},"receipt":{"type":"object"},"expires_at":{"type":"string"},"blast_radius":{"type":"object","description":"Additive COUNTS (not a score). Not permission.","properties":{"endpoints":{"type":"integer","minimum":0},"fields":{"type":"integer","minimum":0},"params":{"type":"integer","minimum":0},"consumers_declared":{"type":"integer","minimum":0},"consumers_observed":{"type":"integer","minimum":0},"graph_source":{"type":"string"}}}},"additionalProperties":true}},"required":["token"]},"annotations":{"readOnlyHint":true,"openWorldHint":false},"outputSchema":{"type":"object","description":"Receipt signature/status plus optional authorization layer. additionalProperties true: additive fields may appear and are not permission. Envelope compatibility when a decision_result is supplied: https://coderifts.com/schemas/decision-result.v1.consumer.json schema description (single source).","properties":{"valid":{"type":"boolean","description":"true iff status is VERIFIED_CURRENT or RETIRED_KEY_VALID_AT_ISSUE"},"status":{"type":"string","enum":["VERIFIED_CURRENT","VERIFIED_EXPIRED","VERIFIED_WRONG_AUDIENCE","VERIFIED_WRONG_ENVIRONMENT","VERIFIED_SUPERSEDED","VERIFIED_SCOPE_MISMATCH","VERIFIED_UNBOUND_OPERATION","VERIFIED_UNBOUND_TARGET","VERIFIED_UNBOUND_REPOSITORY","VERIFIED_UNBOUND_BRANCH","VERIFIED_UNBOUND_PULL_REQUEST","UNKNOWN_KEY","UNKNOWN_KEY_STATUS","RETIRED_KEY_VALID_AT_ISSUE","KEY_RETIRED_AFTER_SIGNING","REVOKED_KEY","REVOKED_KEY_UNDECIDABLE","KEY_REVOKED","REVOCATION_UNDECIDABLE","AUTHORIZATION_UNDECIDABLE","INVALID_SIGNATURE","MALFORMED","UNSUPPORTED_VERSION","REGISTRY_UNREACHABLE"]},"reason":{"type":["string","null"]},"correlation_id":{"type":"string","description":"Route-owned trace id, set by the route itself rather than by correlation middleware; always a non-empty string on 200"},"payload":{"type":"object"},"currently_authorized":{"type":["boolean","null"],"description":"Whether the receipt currently authorizes the intended operation/target/fp (§106). null means authorization could not be evaluated (e.g. no intended context) — not unauthorized and not authorized."},"authz_reason":{"type":"string","description":"When currently_authorized=false: the deny reason (operation_mismatch, decision_not_allow, superseded, expired, target_mismatch, environment_mismatch, …)"},"authz_status":{"type":"string","description":"Authorization-level status (VERIFIED_WRONG_ENVIRONMENT / VERIFIED_SUPERSEDED / VERIFIED_SCOPE_MISMATCH / …)"},"authz_state":{"type":"string","description":"Lifecycle state from isCurrentlyAuthorized when authorization is evaluated (optional; omitted when currently_authorized is null)"},"authz_note":{"type":"string","description":"Present when no intended context supplied: status reflects signature+expiry only"},"binding_level":{"type":"string","description":"place_and_content | content_only — forensic, not a second verdict"},"signed_value":{"type":["string","null"],"description":"Signed envelope slot when signed-wins fail-closed (source_binding_mismatch)"},"caller_value":{"type":["string","null"],"description":"Caller claim that differed from the signed envelope"}},"required":["valid","status","currently_authorized"],"additionalProperties":true}},{"name":"get_decision_details","description":"Retrieve a PAST CodeRifts decision by decision_id (or fingerprint): full\nreport payload, breaking changes list, scores, and linked receipt metadata\nif stored.\n\nUse this when:\n- You have a decision_id (or fingerprint) from a previous preflight, PR\n  comment, or CI log and need to inspect or explain that past decision.\n- You are auditing why a prior ALLOW/WARN/BLOCK was issued.\n- You are NOT requesting a new analysis of current before/after specs.\n\nDo not use when:\n- You need a decision for the CURRENT uncommitted or PR head change set —\n  call coderifts.preflight_change_set with the current artifacts.\n- You hold a receipt token and only need cryptographic/lifecycle verification —\n  use coderifts.verify_receipt.\n- You have no decision_id/fingerprint — run preflight first to create one.\n\nInputs: at least one of decision_id (preferred) or fingerprint is required\n(empty {} is rejected by the server as INVALID_INPUT). Returns the stored\ndecision document or not_found.\n\nScoping — fingerprint lookup returns only YOUR OWN decisions. A fingerprint is\nderived from content, not from an account, so two callers who preflight\nbyte-identical specs derive the same one; the lookup is therefore constrained\nto the decisions your credential can prove it owns.\n\nA decision that exists but is not yours returns the SAME not_found as one that\nwas never issued. This is deliberate: a distinguishable \"exists but forbidden\"\nwould confirm to any caller that a given content hash had been decided on by\nsomeone, which is the fact the scoping exists to withhold. Do not read\nnot_found as proof that no such decision exists anywhere.\n\nDecisions persisted without context.repository cannot currently be attributed\nto an account, and are not retrievable by fingerprint at all — not by their\nowner either. Retrieve those by decision_id, which is unchanged and unscoped.\nThis is a limitation of what older stored rows carry, not a property of the\nlookup: rows written from now on record the account directly, so the gap\nnarrows as older rows age out. If a fingerprint you expect returns not_found,\nuse the decision_id before concluding the decision is missing.\n\nWhen the stored envelope carries control fields, control_envelope.next_agent_step\nis structured remediation guidance the agent MAY follow for non-CONTINUE\nexecution_action values (null on CONTINUE*). Still branch on execution_action;\nnext_agent_step is a suggestion, not permission.","inputSchema":{"type":"object","properties":{"decision_id":{"type":"string","minLength":1,"description":"The decision_id from a prior decision_result envelope. Provide this and/or fingerprint (at least one non-empty string required)."},"fingerprint":{"type":"string","minLength":1,"description":"A verdict fingerprint (sha256:...); returns the latest matching decision. Provide this and/or decision_id (at least one non-empty string required)."}},"minProperties":1,"additionalProperties":false},"annotations":{"readOnlyHint":true,"openWorldHint":false},"outputSchema":{"type":"object","description":"A stored CodeRifts decision: the original decision_result.v1 envelope + lookup meta. Retrieval-path control fields (safe_for_agent/execution_action/verdict_fingerprint/control_envelope) mirror the fresh preflight response and are present only when the stored envelope carries their source field. additionalProperties true: additive fields may appear and are not permission. Compatibility rule (single source): https://coderifts.com/schemas/decision-result.v1.consumer.json schema description.","properties":{"decision":{"type":"string","enum":["ALLOW","WARN","REQUIRE_APPROVAL","BLOCK"]},"preflight_mode":{"type":"string","enum":["analyze","authorize"]},"safe_for_agent":{"type":"boolean"},"execution_action":{"type":"string","enum":["CONTINUE","CONTINUE_WITH_MONITORING","REQUEST_APPROVAL","STOP"]},"verdict_fingerprint":{"type":"string"},"control_envelope":{"type":"object","description":"Control envelope (control/1.0) derived from the stored decision_result. Includes next_agent_step (structured remediation SUGGESTION for non-CONTINUE execution_action; null on CONTINUE*; not permission — still branch on execution_action)."},"required_action_core":{"type":"object","description":"Branchable required-action core { type, reason_code, recheck_required } when present on the envelope."},"risk_score":{"type":"integer","minimum":0,"maximum":100},"breaking_changes":{"type":"integer","minimum":0},"patterns":{"type":"array","items":{"type":"string"}},"pattern_sources":{"type":"array"},"coderifts_version":{"type":"string"},"decision_spec_version":{"type":"string"},"timestamp":{"type":"string"},"evidence_quality":{"type":"string"},"requires_migration":{"type":"boolean"},"chain_receipt":{"type":"string"},"receipt_kind":{"type":"string","enum":["NONE","operation_authorization"]},"operation":{"type":"string"},"evidence":{"type":["object","array","null"]},"decision_semantic_hash":{"type":"string"},"decision_result":{"type":"object","description":"decision-result.v1 envelope (control enums closed). Additive fields may appear and are not permission. Full schema: https://coderifts.com/schemas/decision-result.v1.consumer.json (producer: https://coderifts.com/schemas/decision-result.v1.producer.json).","properties":{"spec_version":{"type":"string","pattern":"^decision-result\\.v1(\\.[0-9]+)?$"},"decision":{"type":"string","enum":["ALLOW","WARN","REQUIRE_APPROVAL","BLOCK"]},"execution_action":{"type":"string","enum":["CONTINUE","CONTINUE_WITH_MONITORING","REQUEST_APPROVAL","STOP"]},"safe_for_agent":{"type":"boolean"},"decision_id":{"type":"string"},"fingerprint":{"type":"string"},"input_fingerprint":{"type":"string"},"decision_body_hash":{"type":["string","null"]},"operation":{"type":["string","null"]},"environment":{"type":["string","null"]},"repository":{"type":["string","null"]},"base":{"type":["string","null"]},"head":{"type":["string","null"]},"audience":{"type":["string","null"]},"authority":{"type":["object","null"],"description":"Additive. { audience, tenant_scope: bound|unbound, binding_proven_at? }. Informational — not permission, not a verify-receipt gate, not an ACL."},"derivation":{"type":["object","null"],"description":"Additive. Present only when derivation:\"server\" produced this envelope. { source, platform?, base_sha, head_sha }. Covered by body_hash; not fingerprint."},"receipt":{"type":"object"},"expires_at":{"type":"string"},"blast_radius":{"type":"object","description":"Additive COUNTS (not a score). Not permission.","properties":{"endpoints":{"type":"integer","minimum":0},"fields":{"type":"integer","minimum":0},"params":{"type":"integer","minimum":0},"consumers_declared":{"type":"integer","minimum":0},"consumers_observed":{"type":"integer","minimum":0},"graph_source":{"type":"string"}}}},"additionalProperties":true},"meta":{"type":"object","properties":{"decision_id":{"type":"string"},"source":{"type":"string"},"created_at":{"type":"string"},"retrieval_mode":{"type":"string","enum":["stored"]},"omitted_sections":{"type":"array"}},"additionalProperties":true}},"required":["decision_result","meta"],"additionalProperties":true}}],"correlation_id":"4013dc7c-42a1-476d-85fa-21dd81be1716"}