diff --git a/.claude-plugin/marketplace.json b/.claude-plugin/marketplace.json index 56ec927b..2327d81b 100644 --- a/.claude-plugin/marketplace.json +++ b/.claude-plugin/marketplace.json @@ -10,7 +10,7 @@ "name": "agents-shipgate", "source": "./plugins/claude-code", "description": "Run Agents Shipgate Tool-Use Readiness workflows from Claude Code: detect whether Shipgate applies, bootstrap the advisory gate, verify agent-capability PRs, and triage findings without fabricating human approval. Requires the agents-shipgate CLI (pipx install agents-shipgate, runtime contract 15).", - "version": "0.16.0b4", + "version": "0.16.0b5", "author": { "name": "Three Moons Lab", "url": "https://threemoonslab.com/" diff --git a/.well-known/agents-shipgate.json b/.well-known/agents-shipgate.json index ea6d4202..51832292 100644 --- a/.well-known/agents-shipgate.json +++ b/.well-known/agents-shipgate.json @@ -3,7 +3,7 @@ "name": "agents-shipgate", "display_name": "Agents Shipgate", "tagline": "The deterministic merge gate for AI-generated agent capability changes", - "version": "0.16.0b4", + "version": "0.16.0b5", "license": "Apache-2.0", "publisher": { "name": "Three Moons Lab", @@ -172,16 +172,16 @@ "codex_boundary_result_schema_path": "docs/codex-boundary-result-schema.v2.json", "agent_boundary_result_schema_version": "shipgate.agent_boundary_result/v1", "agent_boundary_result_schema_path": "docs/agent-boundary-result-schema.v1.json", - "report_schema_version": "0.32", - "packet_schema_version": "0.10", - "verifier_schema_version": "0.3", + "report_schema_version": "0.33", + "packet_schema_version": "0.11", + "verifier_schema_version": "0.4", "verify_run_schema_version": "shipgate.verify_run/v2", - "agent_handoff_schema_version": "shipgate.agent_handoff/v3", - "agent_handoff_schema_path": "docs/agent-handoff-schema.v3.json", + "agent_handoff_schema_version": "shipgate.agent_handoff/v4", + "agent_handoff_schema_path": "docs/agent-handoff-schema.v4.json", "agent_handoff_artifact": "agents-shipgate-reports/agent-handoff.json", - "contract_version": "15", + "contract_version": "16", "minimum_control_contract_version": "14", - "local_agent_contract_schema_version": "4", + "local_agent_contract_schema_version": "5", "inputs": [ "mcp", "openapi", @@ -242,8 +242,8 @@ "scenario": "scenario.json", "governance_benchmark_result": "benchmark/agent-pr-governance/results/result.json" }, - "capability_lock_schema_version": "0.5", - "capability_lock_diff_schema_version": "0.6", + "capability_lock_schema_version": "0.6", + "capability_lock_diff_schema_version": "0.7", "preflight_schema_version": "0.3", "attestation_schema_version": "0.4", "registry_schema_version": "0.3", @@ -252,7 +252,7 @@ "host_grants_baseline_schema_version": "0.2", "host_grants_drift_schema_version": "0.2", "trigger_catalog_schema_version": "0.2", - "capability_standard_version": "0.4", + "capability_standard_version": "0.5", "governance_benchmark_catalog_schema_version": "0.2", "governance_benchmark_result_schema_version": "0.2", "supporting_provisional_surfaces": [ @@ -396,17 +396,17 @@ "static_verdict_disclaimer": "This verdict covers deterministic static evidence only. Agents Shipgate did not execute the agent or prove runtime behavior, tool routing, credential enforcement, or safety.", "schemas": { "manifest": "https://raw.githubusercontent.com/ThreeMoonsLab/agents-shipgate/main/docs/manifest-v0.1.json", - "report": "https://raw.githubusercontent.com/ThreeMoonsLab/agents-shipgate/main/docs/report-schema.v0.32.json", + "report": "https://raw.githubusercontent.com/ThreeMoonsLab/agents-shipgate/main/docs/report-schema.v0.33.json", "agent_result": "https://raw.githubusercontent.com/ThreeMoonsLab/agents-shipgate/main/docs/agent-result-schema.v2.json", "agent_boundary_result": "https://raw.githubusercontent.com/ThreeMoonsLab/agents-shipgate/main/docs/agent-boundary-result-schema.v1.json", "codex_boundary_result": "https://raw.githubusercontent.com/ThreeMoonsLab/agents-shipgate/main/docs/codex-boundary-result-schema.v2.json", - "verifier": "https://raw.githubusercontent.com/ThreeMoonsLab/agents-shipgate/main/docs/verifier-schema.v0.3.json", + "verifier": "https://raw.githubusercontent.com/ThreeMoonsLab/agents-shipgate/main/docs/verifier-schema.v0.4.json", "verify_run": "https://raw.githubusercontent.com/ThreeMoonsLab/agents-shipgate/main/docs/verify-run-schema.v2.json", - "agent_handoff": "https://raw.githubusercontent.com/ThreeMoonsLab/agents-shipgate/main/docs/agent-handoff-schema.v3.json", - "packet": "https://raw.githubusercontent.com/ThreeMoonsLab/agents-shipgate/main/docs/packet-schema.v0.10.json", + "agent_handoff": "https://raw.githubusercontent.com/ThreeMoonsLab/agents-shipgate/main/docs/agent-handoff-schema.v4.json", + "packet": "https://raw.githubusercontent.com/ThreeMoonsLab/agents-shipgate/main/docs/packet-schema.v0.11.json", "preflight": "https://raw.githubusercontent.com/ThreeMoonsLab/agents-shipgate/main/docs/preflight-schema.v0.3.json", - "capability_lock": "https://raw.githubusercontent.com/ThreeMoonsLab/agents-shipgate/main/docs/capability-lock-schema.v0.5.json", - "capability_lock_diff": "https://raw.githubusercontent.com/ThreeMoonsLab/agents-shipgate/main/docs/capability-lock-diff-schema.v0.6.json", + "capability_lock": "https://raw.githubusercontent.com/ThreeMoonsLab/agents-shipgate/main/docs/capability-lock-schema.v0.6.json", + "capability_lock_diff": "https://raw.githubusercontent.com/ThreeMoonsLab/agents-shipgate/main/docs/capability-lock-diff-schema.v0.7.json", "governance_benchmark_catalog": "https://raw.githubusercontent.com/ThreeMoonsLab/agents-shipgate/main/docs/governance-benchmark-catalog-schema.v0.2.json", "governance_benchmark_result": "https://raw.githubusercontent.com/ThreeMoonsLab/agents-shipgate/main/docs/governance-benchmark-result-schema.v0.2.json", "feedback": "https://raw.githubusercontent.com/ThreeMoonsLab/agents-shipgate/main/docs/feedback-schema.v0.1.json", diff --git a/AGENTS.md b/AGENTS.md index 0e0d8fbe..099d513e 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -363,7 +363,7 @@ restate version archaeology here): - Audit envelopes: `release_decision.contribution_rules[]`, `policy_audit`, `privacy_audit`, `heuristics_filter` — explanatory, never a second gate -The current schema is [`docs/report-schema.v0.32.json`](docs/report-schema.v0.32.json). Emitted reports carry `report_schema_version: "0.32"`; v0.32 adds the required Conductor OSS framework summary over v0.31's root-reachable binding contract. A `passed` result requires a complete root-reachable static binding graph plus complete, conflict-free identity, effect, and authority evidence for every reachable action, evaluation of all applicable controls, and no policy condition requiring review. Every release decision explicitly carries `static_analysis_only: true`, `runtime_behavior_verified: false`, and `static_verdict_disclaimer`; packet §1 mirrors them. Binding and semantic gaps are not Findings and cannot be suppressed or baselined. See [`docs/passed-verdict-contract.md`](docs/passed-verdict-contract.md) for the exact claim and [`docs/agent-contract-current.md`](docs/agent-contract-current.md) for version history. v0.31 remains frozen at [`docs/report-schema.v0.31.json`](docs/report-schema.v0.31.json). +The current schema is [`docs/report-schema.v0.33.json`](docs/report-schema.v0.33.json). Emitted reports carry `report_schema_version: "0.33"`; typed predicate support prevents heuristic evidence from being upgraded by policy severity or block metadata. A `passed` result requires a complete root-reachable static binding graph plus complete, conflict-free identity, effect, authority, and applicable-policy evidence for every reachable action. Every release decision explicitly carries `static_analysis_only: true`, `runtime_behavior_verified: false`, and `static_verdict_disclaimer`; packet §1 mirrors them. Binding, semantic, and policy-applicability gaps are not Findings and cannot be suppressed or baselined. See [`docs/passed-verdict-contract.md`](docs/passed-verdict-contract.md) for the exact claim and [`docs/agent-contract-current.md`](docs/agent-contract-current.md) for version history. v0.32 remains frozen at [`docs/report-schema.v0.32.json`](docs/report-schema.v0.32.json). **Release gating signal**: prefer `release_decision.decision` (`"blocked" | "review_required" | "insufficient_evidence" | "passed"`) over `summary.status`. The new field is **baseline-aware** — a baseline-matched critical surfaces in `release_decision.review_items` (accepted debt), not `release_decision.blockers`. `summary.status` stays baseline-blind for v0.7 compatibility, so a baseline-matched-only critical produces both `summary.status = "release_blockers_detected"` AND `release_decision.decision = "review_required"` (intentional divergence — see [STABILITY.md](STABILITY.md#release_decisiondecision-vs-summarystatus)). `insufficient_evidence` (added v0.14) signals that the scan saw too many low-confidence tools or source-loader warnings to be trustworthy; consumers that switch on the enum must fall back to `review_required` for unknown future values. @@ -443,7 +443,7 @@ validation and [`docs/manifest-v0.1.md`](docs/manifest-v0.1.md) for prose. ### Where is the report schema? Parse `agents-shipgate-reports/report.json` and validate against -[`docs/report-schema.v0.32.json`](docs/report-schema.v0.32.json) (current). +[`docs/report-schema.v0.33.json`](docs/report-schema.v0.33.json) (current). Older reports (`report_schema_version: "0.10"`) validate against the frozen [`docs/report-schema.v0.10.json`](docs/report-schema.v0.10.json). Do not scrape Markdown when JSON is available. @@ -481,7 +481,8 @@ For the short, current statement of "which fields to read", see [`docs/agent-con | What | Path | Stable | |---|---|---| | Manifest schema | [`docs/manifest-v0.1.json`](docs/manifest-v0.1.json) | `0.1` | -| Report schema (current) | [`docs/report-schema.v0.32.json`](docs/report-schema.v0.32.json) | `0.32` | +| Report schema (current) | [`docs/report-schema.v0.33.json`](docs/report-schema.v0.33.json) | `0.33` | +| Report schema (v0.32 frozen reference) | [`docs/report-schema.v0.32.json`](docs/report-schema.v0.32.json) | `0.32` | | Report schema (v0.31 frozen reference) | [`docs/report-schema.v0.31.json`](docs/report-schema.v0.31.json) | `0.31` | | Report schema (v0.30 frozen reference) | [`docs/report-schema.v0.30.json`](docs/report-schema.v0.30.json) | `0.30` | | Report schema (v0.29 frozen reference) | [`docs/report-schema.v0.29.json`](docs/report-schema.v0.29.json) | `0.29` | @@ -490,7 +491,7 @@ For the short, current statement of "which fields to read", see [`docs/agent-con | Report schema (v0.26 frozen reference) | [`docs/report-schema.v0.26.json`](docs/report-schema.v0.26.json) | `0.26` | | Report schema (v0.25 frozen reference) | [`docs/report-schema.v0.25.json`](docs/report-schema.v0.25.json) | `0.25` | | Verify-run schema | [`docs/verify-run-schema.v2.json`](docs/verify-run-schema.v2.json) | `shipgate.verify_run/v2` | -| Agent handoff schema | [`docs/agent-handoff-schema.v3.json`](docs/agent-handoff-schema.v3.json) | `shipgate.agent_handoff/v3` | +| Agent handoff schema | [`docs/agent-handoff-schema.v4.json`](docs/agent-handoff-schema.v4.json) | `shipgate.agent_handoff/v4` | | Agent boundary result schema | [`docs/agent-boundary-result-schema.v1.json`](docs/agent-boundary-result-schema.v1.json) | `shipgate.agent_boundary_result/v1` | | Codex boundary result schema (deprecated frozen projection) | [`docs/codex-boundary-result-schema.v2.json`](docs/codex-boundary-result-schema.v2.json) | `shipgate.codex_boundary_result/v2` | | Report schema (v0.24 frozen reference) | [`docs/report-schema.v0.24.json`](docs/report-schema.v0.24.json) | `0.24` | @@ -512,17 +513,17 @@ For the short, current statement of "which fields to read", see [`docs/agent-con | Report schema (v0.8 frozen reference) | [`docs/report-schema.v0.8.json`](docs/report-schema.v0.8.json) | `0.8` | | Report schema (v0.7 frozen reference) | [`docs/report-schema.v0.7.json`](docs/report-schema.v0.7.json) | `0.7` | | Report schema (v0.6 frozen reference) | [`docs/report-schema.v0.6.json`](docs/report-schema.v0.6.json) | `0.6` | -| Packet schema (Release Evidence Packet, latest) | [`docs/packet-schema.v0.10.json`](docs/packet-schema.v0.10.json) | `0.10` | +| Packet schema (Release Evidence Packet, latest) | [`docs/packet-schema.v0.11.json`](docs/packet-schema.v0.11.json) | `0.11` | | Agent result schema (current) | [`docs/agent-result-schema.v2.json`](docs/agent-result-schema.v2.json) | `agent_result_v2` | -| Verifier schema (current) | [`docs/verifier-schema.v0.3.json`](docs/verifier-schema.v0.3.json) | `0.3` | -| Agent handoff schema (current) | [`docs/agent-handoff-schema.v3.json`](docs/agent-handoff-schema.v3.json) | `shipgate.agent_handoff/v3` | +| Verifier schema (current) | [`docs/verifier-schema.v0.4.json`](docs/verifier-schema.v0.4.json) | `0.4` | +| Agent handoff schema (current) | [`docs/agent-handoff-schema.v4.json`](docs/agent-handoff-schema.v4.json) | `shipgate.agent_handoff/v4` | | Preflight schema (current) | [`docs/preflight-schema.v0.3.json`](docs/preflight-schema.v0.3.json) | `0.3` | | Host-grants inventory schema | [`docs/host-grants-inventory-schema.v0.2.json`](docs/host-grants-inventory-schema.v0.2.json) | `0.2` | | Host-grants baseline schema | [`docs/host-grants-baseline-schema.v0.2.json`](docs/host-grants-baseline-schema.v0.2.json) | `0.2` | | Host-grants drift schema | [`docs/host-grants-drift-schema.v0.2.json`](docs/host-grants-drift-schema.v0.2.json) | `0.2` | -| Capability standard | [`docs/capability-standard.md`](docs/capability-standard.md) | `0.4` | -| Capability lock schema | [`docs/capability-lock-schema.v0.5.json`](docs/capability-lock-schema.v0.5.json) | `0.5` | -| Capability lock diff schema | [`docs/capability-lock-diff-schema.v0.6.json`](docs/capability-lock-diff-schema.v0.6.json) | `0.6` | +| Capability standard | [`docs/capability-standard.md`](docs/capability-standard.md) | `0.5` | +| Capability lock schema | [`docs/capability-lock-schema.v0.6.json`](docs/capability-lock-schema.v0.6.json) | `0.6` | +| Capability lock diff schema | [`docs/capability-lock-diff-schema.v0.7.json`](docs/capability-lock-diff-schema.v0.7.json) | `0.7` | | Governance benchmark catalog schema | [`docs/governance-benchmark-catalog-schema.v0.2.json`](docs/governance-benchmark-catalog-schema.v0.2.json) | `0.2` | | Governance benchmark result schema | [`docs/governance-benchmark-result-schema.v0.2.json`](docs/governance-benchmark-result-schema.v0.2.json) | `0.2` | | Check catalog | [`docs/checks.json`](docs/checks.json) | regenerated each release | @@ -582,7 +583,7 @@ NOT prove. Use `--no-packet` / `--packet-format` on `scan`, and `agents-shipgate evidence-packet --from ` to re-render. The full packet contract (fixed sections, disclaimers, `evidence_matrix` rules) lives in -[STABILITY.md §Release Evidence Packet](STABILITY.md#release-evidence-packet-v010) +[STABILITY.md §Release Evidence Packet](STABILITY.md#release-evidence-packet-v011) and [`docs/agent-contract-current.md`](docs/agent-contract-current.md#read-these-for-release-review). Exit codes (stable): diff --git a/CHANGELOG.md b/CHANGELOG.md index 4efe6cd2..a3fd32ff 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,27 @@ ## Unreleased +- **Evidence-basis policy gate (P0, `0.16.0b5`).** Semantic claims and risk + hints now carry a typed evidence basis, stable claim IDs, and derived policy + eligibility. Policy-pack and action-policy predicates evaluate to + `matched | not_matched | indeterminate | conflicting`; rule severity, + confidence, `block: true`, manual tags, and risk overrides cannot upgrade + heuristic or incomplete evidence into an authoritative finding. +- **Non-waivable policy applicability gaps.** Heuristic-only, mixed, unknown, + or conflicting applicability is emitted outside Findings and routes to + `insufficient_evidence`. Baselines, suppressions, severity overrides, + acknowledgements, and `--no-heuristics` cannot hide it. Supported findings + expose deterministic predicate support and a `support_hash`; baseline v0.8 + requires that hash to remain equal. Pre-v0.8 baselines cannot supply that + binding, so supported findings re-gate as new until a human reviews the new + evidence and re-runs `agents-shipgate baseline save`. +- **Evidence contract versions.** Runtime contract advances to v16; report to + v0.33; packet to v0.11; verifier to v0.4; handoff to v4; policy pack to + v0.4; capability standard to v0.5; lock/diff to v0.6/v0.7; action snapshot + to v0.4; downstream local contract to v5; and safety qualification formats + to v3. Existing finding fingerprints and all prior schema files remain + frozen. + - **Complete zero-config multi-host boundary (`0.16.0b4`).** The local boundary check now evaluates every recognized changed Codex, Claude Code, Cursor, VS Code MCP, shared instruction, and GitHub workflow surface through diff --git a/README.md b/README.md index 7529945c..e5fd5df8 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,7 @@ Local-first and static by default — no agent execution, tool calls, LLM calls, > [!IMPORTANT] > **Status: pre-1.0 (beta).** The decision engine is deterministic and stable. -> This source tree is `0.16.0b4`; install and GitHub Action examples remain +> This source tree is `0.16.0b5`; install and GitHub Action examples remain > pinned to the latest published tag, `v0.15.0`, until the beta is released. > Real-history accuracy numbers (small n, published in full in > [`benchmark/miner/README.md`](benchmark/miner/README.md)): across 361 mined @@ -528,8 +528,8 @@ and pre-commit equivalents. When a PR changes what your agent can do, the verify loop writes these artifacts — in read order: -- **`agents-shipgate-reports/agent-handoff.json`** — the **first artifact a coding agent reads**: the compact `shipgate.agent_handoff/v3` object. Lead with `control.state`, then `gate.merge_verdict`; it also projects `blocked_by[]`, `remediation_plan[]`, and verify-run reproducibility from existing artifacts, and it does not introduce a second verdict. -- **`agents-shipgate-reports/verifier.json`** — the **authoritative PR/control evidence substrate** (`verifier_schema_version: "0.3"`). A coding agent switches on `control.state`, then reads `merge_verdict` (`mergeable | human_review_required | insufficient_evidence | blocked | unknown`), `can_merge_without_human`, `control.next_action`, and `fix_task` when producing reviewer evidence for an agent-capability PR. Local control comes from `shipgate check --format agent-boundary-json` and `shipgate.agent_boundary_result/v1`. See [`docs/agent-contract-current.md`](docs/agent-contract-current.md) for the field contract. +- **`agents-shipgate-reports/agent-handoff.json`** — the **first artifact a coding agent reads**: the compact `shipgate.agent_handoff/v4` object. Lead with `control.state`, then `gate.merge_verdict`; it also projects `blocked_by[]`, `remediation_plan[]`, and verify-run reproducibility from existing artifacts, and it does not introduce a second verdict. +- **`agents-shipgate-reports/verifier.json`** — the **authoritative PR/control evidence substrate** (`verifier_schema_version: "0.4"`). A coding agent switches on `control.state`, then reads `merge_verdict` (`mergeable | human_review_required | insufficient_evidence | blocked | unknown`), `can_merge_without_human`, `control.next_action`, and `fix_task` when producing reviewer evidence for an agent-capability PR. Local control comes from `shipgate check --format agent-boundary-json` and `shipgate.agent_boundary_result/v1`. See [`docs/agent-contract-current.md`](docs/agent-contract-current.md) for the field contract. - **`agents-shipgate-reports/verify-run.json`** — the deterministic verify-run reproducibility artifact. It records stable subject/input hashes, policy-pack hashes, outcome, artifact paths, and `run_id` without wall-clock timestamps. - **`agents-shipgate-reports/attestation.json`** + **`agents-shipgate-reports/org-evidence-bundle.json`** — optional organization-governance projections over the same verifier/report artifacts. They are ledger inputs for platform teams, not release gates; `report.json.release_decision.decision` remains the decision engine. - **`agents-shipgate-reports/host-grants.json`** + **`agents-shipgate-reports/org-status.json`** — optional fleet-governance artifacts from `audit --host --out` and `org status --json`, useful for host-grant drift, policy-pack pin state, and exception hygiene. @@ -537,7 +537,7 @@ artifacts — in read order: - **`agents-shipgate-reports/capabilities.lock.json`** + **`agents-shipgate-reports/base.capabilities.lock.json`** + **`agents-shipgate-reports/capability-lock-diff.{json,md}`** — the **capability review primitive**. Verify always emits the head lock after a successful scan; it emits the base lock and diff when the base scan can be materialized, falling back to the reviewed committed lock at `.agents-shipgate/capabilities.lock.json` if needed. - **Gate source of truth** — `report.json.release_decision.decision` (`passed | review_required | insufficient_evidence | blocked`). `merge_verdict` is a deterministic projection of it; the report stays the one decision engine. - **Tool-Use Readiness Report** (supporting) — `agents-shipgate-reports/report.{md,json,sarif}`. Markdown for human release review, JSON for tools and coding agents, SARIF for GitHub code-scanning workflows. This is the underlying check domain the verdict summarizes. -- **Release Evidence Packet** (supporting) — `agents-shipgate-reports/packet.{md,json,html}` (and `packet.pdf` with the `[pdf]` extras). Reviewer-shaped synthesis with fixed sections, including binding and semantic evidence coverage, the compact evidence matrix, and tool/action diffs when available. Packet outputs are locally redacted by default; see [STABILITY.md §Release Evidence Packet](STABILITY.md#release-evidence-packet-v010). +- **Release Evidence Packet** (supporting) — `agents-shipgate-reports/packet.{md,json,html}` (and `packet.pdf` with the `[pdf]` extras). Reviewer-shaped synthesis with fixed sections, including binding and semantic evidence coverage, the compact evidence matrix, and tool/action diffs when available. Packet outputs are locally redacted by default; see [STABILITY.md §Release Evidence Packet](STABILITY.md#release-evidence-packet-v011). ## Exit codes @@ -578,7 +578,7 @@ Agents Shipgate is designed to be agent-friendly. If you're a coding agent (Clau - **`agents-shipgate install-hooks --target claude-code --write`** — deterministic Claude Code hooks: a PreToolUse trust-root guard, a cheap trigger check after `Edit|Write|MultiEdit`, and a full `verify` at `Stop`, so the gate runs even when instruction files lose attention on long sessions. See [`docs/agents/use-with-claude-code.md`](docs/agents/use-with-claude-code.md#hooks-the-deterministic-path-recommended). - **`agents-shipgate mcp-serve`** (`[mcp]` extra) — read-only stdio MCP server exposing `shipgate.check`, `shipgate.preflight`, `shipgate.explain`, `shipgate.capabilities`, and `shipgate.handoff` for agents without comfortable shell access. It is static-only and not a general MCP permission broker. See [`docs/mcp-server.md`](docs/mcp-server.md). - **[`docs/ai-search-summary.md`](docs/ai-search-summary.md)** — human-readable summary for AI search, answer engines, and coding agents -- **[`docs/manifest-v0.1.json`](docs/manifest-v0.1.json)** + **[`docs/report-schema.v0.32.json`](docs/report-schema.v0.32.json)** + **[`docs/agent-handoff-schema.v3.json`](docs/agent-handoff-schema.v3.json)** + **[`docs/preflight-schema.v0.3.json`](docs/preflight-schema.v0.3.json)** — JSON Schemas for live editor validation and agent routing. Reports carry `report_schema_version: "0.32"`; v0.32 adds the required Conductor OSS summary while `passed` still requires a complete root-reachable static binding graph plus complete identity, effect, and authority evidence for each reachable action. `tool_catalog[]` remains visible while `tool_inventory[]` contains only proven reachable tools. Every release decision carries `static_analysis_only: true`, `runtime_behavior_verified: false`, and a canonical static-verdict disclaimer; packet §1 mirrors them. Read `release_decision.decision` for gating and [`docs/passed-verdict-contract.md`](docs/passed-verdict-contract.md) for the exact non-runtime claim. v0.31 is frozen. +- **[`docs/manifest-v0.1.json`](docs/manifest-v0.1.json)** + **[`docs/report-schema.v0.33.json`](docs/report-schema.v0.33.json)** + **[`docs/agent-handoff-schema.v4.json`](docs/agent-handoff-schema.v4.json)** + **[`docs/preflight-schema.v0.3.json`](docs/preflight-schema.v0.3.json)** — JSON Schemas for live editor validation and agent routing. Reports carry `report_schema_version: "0.33"`; every policy finding now records typed predicate support, while heuristic-only, mixed, unknown, or conflicting applicability becomes a non-waivable evidence gap instead of a high-confidence finding. `passed` still requires a complete root-reachable binding graph and complete identity, effect, authority, and policy evidence for each reachable action. `tool_catalog[]` remains visible while `tool_inventory[]` contains only proven reachable tools. Every release decision carries `static_analysis_only: true`, `runtime_behavior_verified: false`, and a canonical static-verdict disclaimer; packet §1 mirrors them. Read `release_decision.decision` for gating and [`docs/passed-verdict-contract.md`](docs/passed-verdict-contract.md) for the exact non-runtime claim. v0.32 is frozen. - **[`docs/capability-lock-schema.v0.5.json`](docs/capability-lock-schema.v0.5.json)** + **[`docs/capability-lock-diff-schema.v0.6.json`](docs/capability-lock-diff-schema.v0.6.json)** — current capability standard v0.4 schemas, including binding hashes, for the static capability envelope and semantic diff; non-gating and separate from `report.json`. - **[`docs/attestation-schema.v0.4.json`](docs/attestation-schema.v0.4.json)** + **[`docs/org-governance-schema.v0.1.json`](docs/org-governance-schema.v0.1.json)** + **[`docs/org-evidence-bundle-schema.v1.json`](docs/org-evidence-bundle-schema.v1.json)** + **[`docs/registry-schema.v0.3.json`](docs/registry-schema.v0.3.json)** + **[`docs/host-grants-inventory-schema.v0.2.json`](docs/host-grants-inventory-schema.v0.2.json)** — deterministic local attestation, organization governance, org evidence bundle, append-only registry, and scope-aware host-grant inventory schemas for multi-repo governance. - **[`docs/governance-benchmark-catalog-schema.v0.2.json`](docs/governance-benchmark-catalog-schema.v0.2.json)** + **[`docs/governance-benchmark-result-schema.v0.2.json`](docs/governance-benchmark-result-schema.v0.2.json)** — stable schemas for the research benchmark catalog and deterministic result artifact. @@ -670,7 +670,7 @@ Agents Shipgate is a static, manifest-first scanner. It is intentionally narrow: - It does not verify runtime behavior, latency, prompt quality, or routing decisions. - It does not replace dynamic security testing or human security review of the underlying systems. - It only inspects what is declared in `shipgate.yaml`, local OpenAPI specs, MCP exports, Anthropic/OpenAI API artifacts, optional SDK AST metadata, static Google ADK/LangChain/CrewAI/n8n/Conductor OSS inputs, Codex repo config, and static Codex plugin package metadata; tools that are not declared or statically discoverable are not scanned. -- The manifest remains `version: "0.1"` so existing configs keep working. Current reports carry `report_schema_version: "0.32"`; binding and semantic gaps are non-waivable release evidence rather than Findings, while v0.31 remains frozen for archived reports. +- The manifest remains `version: "0.1"` so existing configs keep working. Current reports carry `report_schema_version: "0.33"`; binding, semantic, and policy-applicability gaps are non-waivable release evidence rather than Findings, while v0.32 remains frozen for archived reports. See [ROADMAP.md](ROADMAP.md) for what is planned next. @@ -720,7 +720,8 @@ readers and AI search ingest. - [Check catalog](docs/checks.md) - [Policy packs](docs/policy-packs.md) - [Baseline workflow](docs/baseline.md) -- [JSON report schema v0.32](docs/report-schema.v0.32.json) +- [JSON report schema v0.33](docs/report-schema.v0.33.json) +- [JSON report schema v0.32 (frozen)](docs/report-schema.v0.32.json) - [JSON report schema v0.31 (frozen)](docs/report-schema.v0.31.json) - [Capability standard](docs/capability-standard.md) - [Capability lock schema v0.5](docs/capability-lock-schema.v0.5.json) diff --git a/STABILITY.md b/STABILITY.md index 6e8d8f9a..29d1f3f0 100644 --- a/STABILITY.md +++ b/STABILITY.md @@ -1,4 +1,4 @@ -# Stability Contract · 0.16.0b4 +# Stability Contract · 0.16.0b5 What agents and CI integrations can rely on across versions of Agents Shipgate. @@ -6,13 +6,45 @@ This document is the contract. If the runtime ever diverges from what's document Shipgate is pre-1.0. The CLI surface, exit codes, and `contract_version` described here are stable within the `0.x` line, but the `report.json` schema -(`report_schema_version`, currently `0.32`) is still additive-versioned and +(`report_schema_version`, currently `0.33`) is still additive-versioned and not yet frozen. A `1.0` line will not begin until the report schema reaches `1.0` and holds without a breaking change. Pin a version (or the Action tag) for reproducible CI. --- + + +## Migration Note: 0.16.0b5 + +Runtime contract `15 → 16` and report schema `0.32 → 0.33` make policy +applicability evidence explicit. Semantic claims now carry a typed evidence +basis and stable claim ID. Policy predicates carry tri-state status, effective +confidence, contributing claim IDs, and evidence bases; policy findings carry +a stable `support_hash`. + +Rule severity, `block: true`, manual risk escalation, and rule-declared +confidence cannot upgrade heuristic, unknown, partial, or conflicting +evidence. Such applicability creates a non-waivable evidence gap outside the +Finding model and routes to `insufficient_evidence`. `--no-heuristics`, +baselines, suppressions, acknowledgements, and severity overrides cannot hide +these gaps. Strict mode emits exit `20`; advisory mode retains exit `0`. + +Packet advances `0.10 → 0.11`, verifier `0.3 → 0.4`, handoff v3 → v4, +policy pack `0.3 → 0.4`, capability standard `0.4 → 0.5`, capability lock +`0.5 → 0.6`, lock diff `0.6 → 0.7`, action snapshot `0.3 → 0.4`, baseline +`0.7 → 0.8`, and the generated downstream local contract to schema `5`. +The safety corpus, receipt-index, and qualification envelopes advance to v3. +All preceding schemas remain frozen references. + +Finding fingerprints remain stable because typed support is outside legacy +`Finding.evidence`. Baseline v0.8 additionally binds supported findings to +their `support_hash`; an older baseline can still be read, but it cannot accept +a newly supported policy/control finding without being regenerated and +reviewed. + +--- + ## Migration Note: 0.16.0b4 @@ -500,6 +532,7 @@ In `agents-shipgate-reports/report.json`, the following are guaranteed: - `report_schema_version` — bumps minor on additive changes, major on breaking - `release_decision.{decision, reason, blockers, review_items, evidence_coverage, baseline_delta, fail_policy}` (v0.8+) - `release_decision.evidence_coverage.semantic_coverage.{total_actions, pass_eligible_actions, gap_count, review_concern_count, reason_counts}` (v0.29+) — zero-tolerance semantic pass coverage. It is derived from normalized action assessments and contributes directly to the release decision; it is not suppressible or baseline-able. +- `release_decision.evidence_coverage.policy_gap_count` and `policy_evidence_gaps[]` (v0.33+) — zero-tolerance policy-applicability gaps for heuristic-only, mixed, unknown, or conflicting predicates. They remain outside Findings and cannot be suppressed, baselined, acknowledged, severity-overridden, or hidden by `--no-heuristics`. - `release_decision.{static_analysis_only,runtime_behavior_verified,static_verdict_disclaimer}` (v0.29+) — explicit machine boundary for every verdict. Current emitted values are `true`, `false`, and the canonical disclaimer that the static scan did not execute the agent or prove runtime behavior, tool routing, credential enforcement, or safety. - `release_decision.evidence_coverage.evidence_gaps[]` (v0.26+; semantic gap kinds added v0.29) — deterministic, human-routed remediation rows. v0.29 adds `incomplete_surface`, `missing_effect_evidence`, `inferred_effect_only`, `conflicting_effect_evidence`, `missing_authority_evidence`, `partial_authority_evidence`, `conflicting_authority_evidence`, and `invalid_semantic_annotation`, plus next-action kinds `declare_action_effect`, `declare_action_authority`, `provide_complete_inventory`, and `resolve_semantic_conflict`. Semantic declaration placeholders always carry `suggested_patch_kind="manual"`, `auto_apply=false`, and `requires_human_review=true`; they never enter `apply-patches`. - `release_decision.fail_policy.{ci_mode, fail_on, new_findings_only, would_fail_ci, exit_code}` @@ -521,13 +554,14 @@ In `agents-shipgate-reports/report.json`, the following are guaranteed: - `agent_summary.{verdict, headline, blocker_count, review_item_count, auto_appliable_patches, needs_human_review, first_recommended_action}` (v0.12+) — top-level deterministic projection of `release_decision` + per-finding `agent_action`. Lets a coding agent read one block instead of traversing arrays. `first_recommended_action` is `{kind: "command" | "info", command: string | null, why: string}`; the `command` form carries an actual CLI invocation, the `info` form is a "surface this to the user" hint. Same inputs always produce the same output; this block cannot disagree with the underlying `release_decision` and `findings[].agent_action`. - `codex_plugin_surface.{plugins, marketplaces, skills, apps, mcp_server_stubs, hook_stubs, mcp_inventory_files, component_path_issues, warnings}` (v0.13+) — static Codex plugin package and marketplace facts. Only explicit MCP inventory tools enter `tool_inventory[]`; apps, hooks, skills, and MCP server declarations stay in this surface block. - `findings[].provenance_kind` (v0.15+) — records *how a finding was produced*; independent of `confidence`, which records how *sure* we are. It is a reviewer triage/filter signal only: it never changes `release_decision`, severity, fingerprints, baselines, or CI exit behavior. Use `agents-shipgate findings --from agents-shipgate-reports/report.json --provenance-kind keyword_heuristic,regex_heuristic,runtime_trace --json` to filter active findings by provenance class. Enum: `static_declaration | ast_extraction | keyword_heuristic | regex_heuristic | policy_pack | runtime_trace`. `static_declaration` covers manifest, MCP, OpenAPI schema facts, and declarative framework inputs like ADK YAML agent configs or LangChain/CrewAI inventory JSON files — high-trust structural data. `ast_extraction` covers findings against Tools parsed from user Python source by a framework extractor (LangChain function/structured tools, CrewAI function/class tools, ADK Python toolsets); these are subject to extraction error and agents that distrust AST quality can filter them as a class. Framework checks that fire against both AST-extracted and declaratively loaded tools (ADK's per-tool checks) pick the label per tool from `tool.source_type`. `keyword_heuristic` covers token-list matches (broad scope, read-only prompts, free-text parameter names); `regex_heuristic` covers regex matches (secrets, prompt injection); `policy_pack` covers findings emitted by externally loaded policy packs; `runtime_trace` covers findings derived from declared local trace artifacts. Built-in checks set the value via the required kwarg on the `tool_finding`/`agent_finding` helpers; third-party plugin checks that construct `Finding(...)` directly and omit the field are coerced to `static_declaration` by `annotate_remediation` so the wire schema stays satisfied. Required + non-nullable on the wire; the field is Python-Optional only so older v0.12/v0.13 reports loaded by `explain-finding` and minimal synthetic test fixtures keep working. -- `findings[].blocks_release` (v0.16+) — explicit release-policy blocking bit. Built-in and user-defined Action Surface Diff policies, plus declarative policy-pack rules with `block: true`, set it for findings that must block release when active and unbaselined; ordinary severity-based gating still works for existing checks. +- `findings[].blocks_release` (v0.16+) — explicit release-policy blocking bit. Starting in v0.33, a policy may set it only when `finding.support.blocking_eligible=true`; rule metadata cannot upgrade underlying evidence. +- `findings[].support` (v0.33+) — typed predicate status, effective confidence, policy/block eligibility, stable claim IDs, evidence bases, predicate rows, and `support_hash`. Finding fingerprints remain unchanged; baseline v0.8 separately requires an equal support hash for supported findings. - `findings[].capability_refs` and optional `findings[].capability_policy_evidence` (v0.24+) — capability-native policy evidence for built-in policy checks and policy packs. `capability_refs` is required + always present (empty when no capability-policy subject matched). `capability_policy_evidence` is nullable and carries the matched capability identity, effect, authority, controls, hashes, matched predicates, and source provenance when present. These fields are explanatory only: they are not finding fingerprint inputs, do not affect baselines, and do not introduce a second gate. - `findings[].policy_routing` (v0.28+) — optional policy-pack owner, reviewers, and approval-routing metadata. It is non-enforcing reviewer/audit metadata: it is not part of `Finding.evidence`, does not affect fingerprints, suppressions, baselines, `blocks_release`, or `release_decision`. Policy-pack `match` predicates and `block: true` remain the only policy-pack inputs that affect findings and release gating. - `findings[].capability_trace_refs` and top-level `capability_runtime_evidence` (v0.25+) — opt-in local trace/provenance evidence linked to `CapabilityFactV1`. Trace refs are required + always present on findings (empty when no local trace row matched). The top-level block carries deterministic summary counts, matched/unmatched trace rows, source provenance, and notes. It is explanatory only: it is not a finding fingerprint input, does not affect baselines or run IDs, does not change capability lock export/diff schemas, and does not introduce a second gate. - `action_surface_facts.actions[]` (v0.16+) — deterministic current action snapshot: action id, operation, effect, normalized risk tags, scopes, approval policy, safeguards, evidence, input fields, and stable hashes. - `action_surface_diff.{enabled, base, summary, added, removed, modified, notes}` (v0.16+) — reviewer-facing delta for what the agent can do vs. a prior report or v0.4 baseline. Policy findings derived from this diff can set `findings[].blocks_release=true` and affect `release_decision.decision` and strict-mode exit behavior. -- `release_decision.contribution_rules[].{finding_id, fingerprint, check_id, category, rule, rationale}` (v0.17+) — deterministic per-finding audit of how each finding contributed to the release decision. Required + always present (defaults to `[]` for legacy reports loaded via `explain-finding`). Exactly one row per `report.findings` entry, including suppressed findings, so the audit set is exhaustive over the full findings list. `category` enum: `blocker | review_item | excluded`. `rule` enum: `policy_block_new | severity_block_new | policy_baseline_accepted | severity_baseline_accepted | review_required | sub_threshold | suppressed`. The (rule, category) pairs the gate can produce are exhaustively documented in [Release decision truth table](#release-decision-truth-table) below — reading the contribution rule is sufficient to predict the outcome for that finding without re-deriving the decision logic. The audit cannot disagree with `release_decision.{blockers,review_items}[]`: the same classification powers both. Adding `contribution_rules` does not change any existing behavior — `decision`, `blockers[]`, `review_items[]`, `fail_policy.exit_code`, and strict-mode exit codes are byte-identical to v0.16. +- `release_decision.contribution_rules[].{finding_id, fingerprint, check_id, category, rule, rationale}` (v0.17+) — deterministic per-finding audit of how each finding contributed to the release decision. Required + always present. Exactly one row per `report.findings` entry, including suppressed findings. v0.33 adds `rule="unsupported_evidence"`, always with `category="excluded"`, for typed support that is not policy-eligible. - `baseline.{matched_count, new_count, resolved_count, path}` (when `--baseline` is used) - `tool_inventory[].{name, source_type, source_ref, risk_tags, auth_scopes, owner, confidence}` - `loaded_policy_packs[].{id, name, version, path, source, sha256, sha256_status, owner, rule_count}` (v0.27+) — deterministic policy-pack distribution and ownership metadata for organization audit. `sha256_status` is `"verified"` when a manifest pin matched and `"unpinned"` otherwise. Hash mismatch still fails closed during pack loading; this metadata never introduces a second release verdict. @@ -656,7 +690,7 @@ These are **intentionally different signals**, kept apart for backwards compatib #### Release decision truth table -The classification below is the contract for how every active finding lands in `release_decision.{blockers, review_items}[]` and which `contribution_rules[].rule` (v0.17+) fires for it. Same shape as the v0.8 implementation: this section documents existing behavior, it does not change it. Suppressed findings (`finding.suppressed=true`) are excluded entirely from the active set and audited as `category="excluded", rule="suppressed"`. +The classification below is the contract for how every active finding lands in `release_decision.{blockers, review_items}[]` and which `contribution_rules[].rule` (v0.17+) fires for it. Starting in v0.33, a finding with typed `support` is considered active for release contribution only when `support.policy_eligible=true`; otherwise it is excluded with `rule="unsupported_evidence"` before severity or `blocks_release` is consulted. Suppressed findings are excluded with `rule="suppressed"` after that evidence-eligibility check. Legacy findings without typed support retain the table's established behavior. Notation: `fail_on` is `release_decision.fail_policy.fail_on` after `ci_mode` resolution (advisory → empty, strict → `["critical"]`, plus any explicit `--fail-on` override). `blocker_severities` = `{critical} ∪ fail_on`. `review_tier` = `{critical, high, medium}` (or any severity when `requires_human_review=true`). @@ -1187,7 +1221,7 @@ infer runtime routing, or execute tools. Action Surface Diff policy findings can affect release gating through `findings[].blocks_release`; Tool Surface Diff remains explanatory only. -### Release Evidence Packet (v0.10) +### Release Evidence Packet (v0.11) `agents-shipgate-reports/packet.json` is a supporting/provisional reviewer artifact governed by [`docs/packet-schema.v0.9.json`](docs/packet-schema.v0.9.json). diff --git a/adoption-kits/claude-code-skill/.agents-shipgate-kit-metadata.json b/adoption-kits/claude-code-skill/.agents-shipgate-kit-metadata.json index 62bc1de6..b4d732b3 100644 --- a/adoption-kits/claude-code-skill/.agents-shipgate-kit-metadata.json +++ b/adoption-kits/claude-code-skill/.agents-shipgate-kit-metadata.json @@ -33,7 +33,8 @@ "7d2ea9407f248e7eb54113fefe9aac8d9e5dabb4013d1f2327b42442072d0041", "370e7f8890aef88540c847719f62d2e72529f172c8c4a213d7e8b20c6fde1346", "6f7c06b290bd2ae7b0a9ab6e9fd6abe567a9e1051e8ca4a1d5c098a1359825c7", - "e45f9d385f0e7744a5731694f337952682e1849e97be2d0a488ca3cff9db5792" + "e45f9d385f0e7744a5731694f337952682e1849e97be2d0a488ca3cff9db5792", + "98ba22d7518ae4635ed109fd187323da0541281061dd4f259ac7fdb950c7b185" ], "prompts/add-shipgate-to-repo.md": [ "ea3c37cfbbd42c40d164abfe21d468a3a5550d5384125f94a53c947dea6b4b2a", @@ -67,7 +68,8 @@ "82957a521b5914b3e678e6b76e7088306559c8ad6bcf7c2dce7fb1e822b6bec6", "b6f87f58f70b5920442f342b5118419ef685ad9f4ff8b0ff87c2729a92929786", "b2e42bce1eb6892c5207890bd53ab987d0beaac7c9fa2d9de8ba07033a3f2f60", - "2b1d989ee32b8cfee59fb1cc0f51d70277cc16c9f2ad72932a348ecd285b4c38" + "2b1d989ee32b8cfee59fb1cc0f51d70277cc16c9f2ad72932a348ecd285b4c38", + "e35be58338a6343c712a6a63aff826f5785040f201aa0d7f5f6f257c27d12532" ], "prompts/decide-shipgate-relevance.md": [ "1bf8b9d91f081a246dcff14a84810ca5384f8e0987e4e7a8c0c5df56b151564c", diff --git a/adoption-kits/claude-code-skill/SKILL.md b/adoption-kits/claude-code-skill/SKILL.md index 2b8e5670..12caf111 100644 --- a/adoption-kits/claude-code-skill/SKILL.md +++ b/adoption-kits/claude-code-skill/SKILL.md @@ -76,9 +76,9 @@ For non-GitHub CI (GitLab, CircleCI, Jenkins, Azure Pipelines, Buildkite, Bitbuc - **Installed CLI contract**: when available, run `agents-shipgate contract --json` to verify local schema versions, capability/research surfaces, `release_decision.decision`, and manual-review signal fields. Older installs should use [`docs/agent-contract-current.md`](https://github.com/ThreeMoonsLab/agents-shipgate/blob/main/docs/agent-contract-current.md) or upgrade before automating against the local contract command. - **Verifier JSON**: `verifier_schema_version: "0.3"`. Switch on `control.state`, then read `merge_verdict`, `can_merge_without_human`, `control.next_action`, `fix_task`, `capability_review.top_changes`, `trust_root_touched`, and `policy_weakened` before summarizing an AI-generated PR. `merge_verdict` is a deterministic projection; the gate remains `report.json.release_decision.decision`. - **Verify run JSON**: `verify-run.json` uses `schema_version: "shipgate.verify_run/v2"` and records stable run identity, subject refs, input hashes, execution, applicability, release outcome, and artifact hashes. It is the reproducibility artifact for `verify`; do not treat it as a second gate. -- **Report JSON**: `report_schema_version: "0.32"`. Read `release_decision.decision` first. A `passed` decision requires a complete root-reachable static binding graph plus complete, conflict-free identity, effect, and authority evidence for every reachable action; it does not prove runtime behavior. Preserve `release_decision.static_analysis_only=true`, `runtime_behavior_verified=false`, and `static_verdict_disclaimer` in summaries. Read `release_decision.evidence_coverage.binding_coverage`, `semantic_coverage`, and `identity_coverage`, then work every `evidence_gaps[].next_action` in order. Binding and semantic gaps are not Findings and cannot be suppressed, baselined, severity-overridden, cleared by `--no-heuristics`, or satisfied by `human_ack`; binding, effect, and authority declarations are human assertions and must never be auto-written. Use `tool_catalog[]` for diagnostics and `tool_inventory[]` for the proven reachable surface. The current schema is [`docs/report-schema.v0.32.json`](https://github.com/ThreeMoonsLab/agents-shipgate/blob/main/docs/report-schema.v0.32.json); v0.31 is a frozen compatibility reference. See the [current agent contract](https://github.com/ThreeMoonsLab/agents-shipgate/blob/main/docs/agent-contract-current.md#read-these-first-for-release-gating) and [evidence-backed passed contract](https://github.com/ThreeMoonsLab/agents-shipgate/blob/main/docs/passed-verdict-contract.md). +- **Report JSON**: `report_schema_version: "0.33"`. Read `release_decision.decision` first. A `passed` decision requires a complete root-reachable static binding graph plus complete, conflict-free identity, effect, and authority evidence for every reachable action; it does not prove runtime behavior. Preserve `release_decision.static_analysis_only=true`, `runtime_behavior_verified=false`, and `static_verdict_disclaimer` in summaries. Read `release_decision.evidence_coverage.binding_coverage`, `semantic_coverage`, `identity_coverage`, and `policy_gap_count`, then work every `evidence_gaps[].next_action` in order. Binding, semantic, and policy-applicability gaps are not Findings and cannot be suppressed, baselined, severity-overridden, cleared by `--no-heuristics`, or satisfied by `human_ack`; binding, effect, and authority declarations are human assertions and must never be auto-written. Use `tool_catalog[]` for diagnostics and `tool_inventory[]` for the proven reachable surface. The current schema is [`docs/report-schema.v0.33.json`](https://github.com/ThreeMoonsLab/agents-shipgate/blob/main/docs/report-schema.v0.33.json); v0.32 is a frozen compatibility reference. See the [current agent contract](https://github.com/ThreeMoonsLab/agents-shipgate/blob/main/docs/agent-contract-current.md#read-these-first-for-release-gating) and [evidence-backed passed contract](https://github.com/ThreeMoonsLab/agents-shipgate/blob/main/docs/passed-verdict-contract.md). These binding-backed fields require runtime contract v13; the unambiguous `AgentControl` projection requires runtime contract v14. A contract-v12 CLI emits the frozen v0.30 report and must not be described using the v0.31 binding claim. -- **Release Evidence Packet**: `agents-shipgate-reports/packet.{md,json,html}` (and `packet.pdf` with the `[pdf]` extras) is a supporting/provisional reviewer artifact. Packet v0.10 projects binding and semantic coverage under the release decision; it never creates a second gate. See [`docs/packet-schema.v0.10.json`](https://github.com/ThreeMoonsLab/agents-shipgate/blob/main/docs/packet-schema.v0.10.json) and [STABILITY.md §Release Evidence Packet](https://github.com/ThreeMoonsLab/agents-shipgate/blob/main/STABILITY.md#release-evidence-packet-v010). +- **Release Evidence Packet**: `agents-shipgate-reports/packet.{md,json,html}` (and `packet.pdf` with the `[pdf]` extras) is a supporting/provisional reviewer artifact. Packet v0.11 projects binding and semantic coverage under the release decision; it never creates a second gate. See [`docs/packet-schema.v0.11.json`](https://github.com/ThreeMoonsLab/agents-shipgate/blob/main/docs/packet-schema.v0.11.json) and [STABILITY.md §Release Evidence Packet](https://github.com/ThreeMoonsLab/agents-shipgate/blob/main/STABILITY.md#release-evidence-packet-v011). - **Capability standard**: `agents-shipgate capability export` emits a stable static capability lock (`capability_lock_schema_version: "0.5"`) and `agents-shipgate capability diff` emits a stable semantic diff (`capability_lock_diff_schema_version: "0.6"`). These artifacts implement capability standard v0.4, are supporting/provisional and non-gating, exclude runtime trace evidence, and are documented in [`docs/capability-standard.md`](https://github.com/ThreeMoonsLab/agents-shipgate/blob/main/docs/capability-standard.md). - **Governance benchmark**: `benchmark/agent-pr-governance/cases.yaml` and `scripts/run_governance_benchmark.py` are the stable research benchmark substrate (`governance_benchmark_result_schema_version: "0.2"`), not a release gate. See [`docs/governance-benchmark.md`](https://github.com/ThreeMoonsLab/agents-shipgate/blob/main/docs/governance-benchmark.md). - **Single source of truth for the contract**: [`docs/agent-contract-current.md`](https://github.com/ThreeMoonsLab/agents-shipgate/blob/main/docs/agent-contract-current.md). When the schema bumps, that file updates first. diff --git a/benchmark/safety-qualification/README.md b/benchmark/safety-qualification/README.md index c8b59b1d..398603ec 100644 --- a/benchmark/safety-qualification/README.md +++ b/benchmark/safety-qualification/README.md @@ -1,6 +1,6 @@ # Evidence-Backed Pass Safety Qualification -This directory is the runbook for the `0.16.0b4` beta safety qualification. +This directory is the runbook for the `0.16.0b5` beta safety qualification. The repository does **not** ship fabricated human labels or a passing result. Until a real frozen corpus and its verifier receipts exist, `safety-qualification.json` must not be published as qualified. @@ -11,12 +11,12 @@ The runner consumes four independently content-addressed inputs: 1. A built `agents-shipgate` wheel. The runner reads wheel metadata without importing or executing it and records the wheel SHA-256. -2. A `shipgate.safety_corpus/v2` JSON/YAML corpus. Every case has two blind, +2. A `shipgate.safety_corpus/v3` JSON/YAML corpus. Every case has two blind, attributable labels (`security_governance` and `framework_tooling`), an evidence-backed final decision, a remediation condition, and a third-human adjudication for every disagreement. `frozen_labels_sha256` must match the canonical label payload. -3. A `shipgate.safety_receipt_index/v2` JSON/YAML index created only after +3. A `shipgate.safety_receipt_index/v3` JSON/YAML index created only after labels are frozen. It binds the exact wheel, corpus, labels, policy bundle, and one `shipgate.verify_run/v2` receipt per case. 4. The qualification policy file(s) or directory. Directory hashing includes @@ -52,7 +52,7 @@ are marked `qualification_tier: test` and can never set ```bash PYTHONPATH=src python scripts/run_safety_qualification.py \ - --wheel dist/agents_shipgate-0.16.0b4-py3-none-any.whl \ + --wheel dist/agents_shipgate-0.16.0b5-py3-none-any.whl \ --corpus /secure/frozen-corpus.json \ --receipts /secure/receipt-index.json \ --policy /secure/qualification-policy/ \ diff --git a/docs/INDEX.md b/docs/INDEX.md index c432d5a6..49f672a3 100644 --- a/docs/INDEX.md +++ b/docs/INDEX.md @@ -36,7 +36,8 @@ A single entry point for human readers and AI agents walking the `docs/` tree. - [`checks.md`](checks.md) — full check catalog (human-readable) - [`checks.json`](checks.json) — machine-readable check catalog (regenerated each release) - [`manifest-v0.1.json`](manifest-v0.1.json) — JSON Schema for `shipgate.yaml` -- [`report-schema.v0.32.json`](report-schema.v0.32.json) — JSON Schema for `report.json` (current; emitted reports carry `report_schema_version: "0.32"`, the Conductor OSS framework summary, root-reachable binding facts/diffs/coverage, normalized semantic assessments, and an explicit static-only verdict boundary) +- [`report-schema.v0.33.json`](report-schema.v0.33.json) — JSON Schema for `report.json` (current; emitted reports carry `report_schema_version: "0.33"`, typed policy-evidence support and non-waivable applicability gaps, the Conductor OSS summary, root-reachable binding facts, and the static-only verdict boundary) +- [`report-schema.v0.32.json`](report-schema.v0.32.json) — frozen v0.32 Conductor OSS summary reference; pre-v0.33 reports validate against this - [`report-schema.v0.31.json`](report-schema.v0.31.json) — frozen v0.31 root-reachable binding reference; pre-v0.32 reports validate against this - [`report-schema.v0.30.json`](report-schema.v0.30.json) — frozen v0.30 provider-scoped identity reference; pre-v0.31 reports validate against this - [`report-schema.v0.29.json`](report-schema.v0.29.json) — frozen v0.29 reference schema; pre-v0.30 reports validate against this @@ -44,12 +45,14 @@ A single entry point for human readers and AI agents walking the `docs/` tree. - [`report-schema.v0.27.json`](report-schema.v0.27.json) — frozen v0.27 reference schema; pre-v0.28 reports validate against this - [`report-schema.v0.26.json`](report-schema.v0.26.json) — frozen v0.26 reference schema; pre-v0.27 reports validate against this - [`report-schema.v0.25.json`](report-schema.v0.25.json) — frozen v0.25 reference schema; pre-v0.26 reports validate against this -- [`verifier-schema.v0.3.json`](verifier-schema.v0.3.json) — JSON Schema for `verifier.json`, the primary coding-agent control artifact emitted by `agents-shipgate verify` +- [`verifier-schema.v0.4.json`](verifier-schema.v0.4.json) — JSON Schema for `verifier.json`, including typed policy-evidence gaps and finding support +- [`verifier-schema.v0.3.json`](verifier-schema.v0.3.json) — frozen v0.3 verifier reference - [`verifier-schema.v0.2.json`](verifier-schema.v0.2.json) — frozen v0.2 verifier reference - [`verifier-schema.v0.1.json`](verifier-schema.v0.1.json) — frozen v0.1 verifier reference - [`verify-run-schema.v2.json`](verify-run-schema.v2.json) — JSON Schema for `verify-run.json`, the deterministic verify-run reproducibility artifact - [`verify-run-schema.v1.json`](verify-run-schema.v1.json) — frozen verify-run v1 reference -- [`agent-handoff-schema.v3.json`](agent-handoff-schema.v3.json) — current compact verifier handoff schema +- [`agent-handoff-schema.v4.json`](agent-handoff-schema.v4.json) — current compact verifier handoff schema with support hashes on blocking evidence +- [`agent-handoff-schema.v3.json`](agent-handoff-schema.v3.json) — frozen handoff v3 reference - [`agent-handoff-schema.v2.json`](agent-handoff-schema.v2.json) — frozen handoff v2 reference - [`agent-result-schema.v2.json`](agent-result-schema.v2.json) — current shared local-check and MCP result schema - [`agent-boundary-result-schema.v1.json`](agent-boundary-result-schema.v1.json) — current host-neutral JSON Schema for `shipgate check --format agent-boundary-json` @@ -57,7 +60,8 @@ A single entry point for human readers and AI agents walking the `docs/` tree. - [`codex-boundary-result-schema.v1.json`](codex-boundary-result-schema.v1.json) — frozen boundary v1 reference - [`agent-result-schema.v1.json`](agent-result-schema.v1.json) — legacy JSON Schema retained for existing local-agent protocol and MCP surfaces; not emitted by `agents-shipgate verify` - [`preflight-schema.v0.3.json`](preflight-schema.v0.3.json) — current proactive preflight control schema -- [`policy-pack-schema.v0.3.json`](policy-pack-schema.v0.3.json) — JSON Schema for local policy-pack YAML files (current; selectors can qualify tools by canonical identity and provider) +- [`policy-pack-schema.v0.4.json`](policy-pack-schema.v0.4.json) — JSON Schema for local policy-pack YAML files (current; selectors are evaluated against typed predicate evidence) +- [`policy-pack-schema.v0.3.json`](policy-pack-schema.v0.3.json) — frozen v0.3 policy-pack reference - [`policy-pack-schema.v0.2.json`](policy-pack-schema.v0.2.json) — frozen v0.2 policy-pack reference - [`policy-pack-schema.v0.1.json`](policy-pack-schema.v0.1.json) — frozen v0.1 reference schema for the flat match syntax - [`attestation-schema.v0.4.json`](attestation-schema.v0.4.json) — JSON Schema for `attestation.json` emitted by `agents-shipgate attest`; adds verify-run binding, explicit CI event facts, capability-lock/diff summaries, and policy-pack pin records for cross-repo ledgers @@ -72,8 +76,10 @@ A single entry point for human readers and AI agents walking the `docs/` tree. - [`attestation-schema.v0.2.json`](attestation-schema.v0.2.json) — frozen v0.2 attestation reference - [`attestation-schema.v0.1.json`](attestation-schema.v0.1.json) — frozen v0.1 attestation reference - [`registry-schema.v0.2.json`](registry-schema.v0.2.json) — frozen v0.2 registry reference -- [`capability-lock-schema.v0.5.json`](capability-lock-schema.v0.5.json) — current JSON Schema for `capabilities.lock.json`; adds binding hashes and remains non-gating -- [`capability-lock-diff-schema.v0.6.json`](capability-lock-diff-schema.v0.6.json) — current JSON Schema for semantic capability-lock diff artifacts; remains non-gating +- [`capability-lock-schema.v0.6.json`](capability-lock-schema.v0.6.json) — current JSON Schema for `capabilities.lock.json`; carries typed semantic evidence and remains non-gating +- [`capability-lock-diff-schema.v0.7.json`](capability-lock-diff-schema.v0.7.json) — current JSON Schema for semantic capability-lock diff artifacts; remains non-gating +- [`capability-lock-schema.v0.5.json`](capability-lock-schema.v0.5.json) — frozen v0.5 binding-hash reference +- [`capability-lock-diff-schema.v0.6.json`](capability-lock-diff-schema.v0.6.json) — frozen v0.6 diff reference - [`capability-lock-schema.v0.2.json`](capability-lock-schema.v0.2.json) — frozen v0.2 capability-lock reference - [`capability-lock-diff-schema.v0.3.json`](capability-lock-diff-schema.v0.3.json) — frozen v0.3 capability-lock-diff reference - [`capability-lock-schema.v0.1.json`](capability-lock-schema.v0.1.json) — frozen experimental reference for old capability lock and diff artifacts; `capability diff` still accepts old lock inputs @@ -105,7 +111,8 @@ A single entry point for human readers and AI agents walking the `docs/` tree. - [`report-schema.v0.8.json`](report-schema.v0.8.json) — frozen v0.8 reference schema; pre-v0.9 reports validate against this - [`report-schema.v0.7.json`](report-schema.v0.7.json) — frozen v0.7 reference schema; pre-v0.8 reports validate against this - [`report-schema.v0.6.json`](report-schema.v0.6.json) — frozen v0.6 reference schema; pre-v0.7 reports validate against this -- [`packet-schema.v0.10.json`](packet-schema.v0.10.json) — JSON Schema for the Release Evidence Packet (current; emitted packets carry `packet_schema_version: "0.10"` and project current report binding/semantic coverage plus the static-only verdict boundary in §1) +- [`packet-schema.v0.11.json`](packet-schema.v0.11.json) — JSON Schema for the Release Evidence Packet (current; emitted packets project typed policy support, evidence gaps, semantic coverage, and the static-only verdict boundary in §1) +- [`packet-schema.v0.10.json`](packet-schema.v0.10.json) — frozen v0.10 binding-aware packet reference - [`packet-schema.v0.9.json`](packet-schema.v0.9.json) — frozen v0.9 reference packet schema; pre-v0.10 packets validate against this - [`packet-schema.v0.8.json`](packet-schema.v0.8.json) — frozen v0.8 reference packet schema; pre-v0.9 packets validate against this - [`packet-schema.v0.7.json`](packet-schema.v0.7.json) — frozen v0.7 reference packet schema; pre-v0.8 packets validate against this diff --git a/docs/agent-boundary-result-schema.v1.json b/docs/agent-boundary-result-schema.v1.json index fc46b778..fc595b39 100644 --- a/docs/agent-boundary-result-schema.v1.json +++ b/docs/agent-boundary-result-schema.v1.json @@ -401,7 +401,7 @@ "type": "string" }, "version": { - "default": "0.16.0b4", + "default": "0.16.0b5", "title": "Version", "type": "string" } diff --git a/docs/agent-contract-current.md b/docs/agent-contract-current.md index 0f0e113f..8ac0f700 100644 --- a/docs/agent-contract-current.md +++ b/docs/agent-contract-current.md @@ -10,17 +10,17 @@ Verify the installed CLI contract locally before relying on hard-coded docs: agents-shipgate contract --json ``` -Runtime contract v15 retains the v14 unambiguous `AgentControl` and v13 -root-reachable agent-to-tool binding contracts. It adds one host-neutral, -scope-bound boundary assessment across -check, preflight, verify, handoff, MCP, and GitHub Action projections. Agents +Runtime contract v16 retains the v15 host-neutral boundary, v14 unambiguous +`AgentControl`, and v13 root-reachable binding contracts. It adds typed +evidence bases and tri-state policy applicability across scan, verify, packet, +and handoff projections. Agents switch on `control.state`; `decision` remains diagnostic and `release_decision.decision` remains the release gate. Contract v14 requires `completion_allowed == (state == "complete")` and -`must_stop == (state == "human_review_required")`. Report v0.32 adds the -required Conductor OSS summary over frozen v0.31; packet v0.10, capability -standard v0.4, capability lock v0.5, and capability-lock diff v0.6 remain -unchanged by the control-contract milestone. +`must_stop == (state == "human_review_required")`. Report v0.33 prevents rule +metadata from upgrading heuristic evidence; packet v0.11, capability standard +v0.5, capability lock v0.6, and capability-lock diff v0.7 project the same +typed support. The runtime contract also exposes the local agent command spec: `primary_commands{}`, `commands{}`, `default_paths{}`, `artifacts{}`, `agent_read_order[]`, `verifier_read_order[]`, `merge_verdicts[]`, @@ -51,21 +51,21 @@ Downstream repos generated with `.shipgate/agent-contract.json`. - Latest release: `v0.15.0` -- In-tree runtime: `0.16.0b4` — see [pyproject.toml](../pyproject.toml) -- Runtime contract: `15` (minimum control contract: `14`) -- Current report schema: `0.32` — [`docs/report-schema.v0.32.json`](report-schema.v0.32.json) -- Current packet schema: `0.10` — [`docs/packet-schema.v0.10.json`](packet-schema.v0.10.json) +- In-tree runtime: `0.16.0b5` — see [pyproject.toml](../pyproject.toml) +- Runtime contract: `16` (minimum control contract: `14`) +- Current report schema: `0.33` — [`docs/report-schema.v0.33.json`](report-schema.v0.33.json) +- Current packet schema: `0.11` — [`docs/packet-schema.v0.11.json`](packet-schema.v0.11.json) - Current shared agent result schema: `agent_result_v2` — [`docs/agent-result-schema.v2.json`](agent-result-schema.v2.json) -- Current verifier schema: `0.3` — [`docs/verifier-schema.v0.3.json`](verifier-schema.v0.3.json) +- Current verifier schema: `0.4` — [`docs/verifier-schema.v0.4.json`](verifier-schema.v0.4.json) - Current verify-run schema: `shipgate.verify_run/v2` — [`docs/verify-run-schema.v2.json`](verify-run-schema.v2.json) -- Current agent handoff schema: `shipgate.agent_handoff/v3` — [`docs/agent-handoff-schema.v3.json`](agent-handoff-schema.v3.json) +- Current agent handoff schema: `shipgate.agent_handoff/v4` — [`docs/agent-handoff-schema.v4.json`](agent-handoff-schema.v4.json) - Current agent boundary result schema: `shipgate.agent_boundary_result/v1` — [`docs/agent-boundary-result-schema.v1.json`](agent-boundary-result-schema.v1.json) - Frozen deprecated Codex projection: `shipgate.codex_boundary_result/v2` — [`docs/codex-boundary-result-schema.v2.json`](codex-boundary-result-schema.v2.json) - Current preflight schema: `0.3` — [`docs/preflight-schema.v0.3.json`](preflight-schema.v0.3.json) -- Current downstream local agent contract schema: `4` -- Current capability standard: `0.4` — [`docs/capability-standard.md`](capability-standard.md) -- Current capability lock schema: `0.5` — [`docs/capability-lock-schema.v0.5.json`](capability-lock-schema.v0.5.json) -- Current capability lock diff schema: `0.6` — [`docs/capability-lock-diff-schema.v0.6.json`](capability-lock-diff-schema.v0.6.json) +- Current downstream local agent contract schema: `5` +- Current capability standard: `0.5` — [`docs/capability-standard.md`](capability-standard.md) +- Current capability lock schema: `0.6` — [`docs/capability-lock-schema.v0.6.json`](capability-lock-schema.v0.6.json) +- Current capability lock diff schema: `0.7` — [`docs/capability-lock-diff-schema.v0.7.json`](capability-lock-diff-schema.v0.7.json) - Current attestation schema: `0.4` — [`docs/attestation-schema.v0.4.json`](attestation-schema.v0.4.json) - Current registry schema: `0.3` — [`docs/registry-schema.v0.3.json`](registry-schema.v0.3.json) - Current org evidence bundle schema: `shipgate.org_evidence_bundle/v1` — [`docs/org-evidence-bundle-schema.v1.json`](org-evidence-bundle-schema.v1.json) @@ -73,9 +73,9 @@ Downstream repos generated with - Current trigger catalog schema: `0.2` — [`docs/triggers.json`](triggers.json) - Current governance benchmark catalog schema: `0.2` — [`docs/governance-benchmark-catalog-schema.v0.2.json`](governance-benchmark-catalog-schema.v0.2.json) - Current governance benchmark result schema: `0.2` — [`docs/governance-benchmark-result-schema.v0.2.json`](governance-benchmark-result-schema.v0.2.json) -- Frozen-reference report schemas: frozen [`v0.31`](report-schema.v0.31.json), frozen [`v0.30`](report-schema.v0.30.json), frozen [`v0.29`](report-schema.v0.29.json), frozen [`v0.28`](report-schema.v0.28.json), frozen [`v0.27`](report-schema.v0.27.json), frozen [`v0.26`](report-schema.v0.26.json), frozen [`v0.25`](report-schema.v0.25.json), frozen [`v0.24`](report-schema.v0.24.json), frozen [`v0.23`](report-schema.v0.23.json), frozen [`v0.22`](report-schema.v0.22.json), frozen [`v0.21`](report-schema.v0.21.json), frozen [`v0.20`](report-schema.v0.20.json), frozen [`v0.19`](report-schema.v0.19.json), frozen [`v0.18`](report-schema.v0.18.json), frozen [`v0.17`](report-schema.v0.17.json), frozen [`v0.16`](report-schema.v0.16.json), frozen [`v0.15`](report-schema.v0.15.json), frozen [`v0.14`](report-schema.v0.14.json), frozen [`v0.13`](report-schema.v0.13.json), frozen [`v0.12`](report-schema.v0.12.json), frozen [`v0.11`](report-schema.v0.11.json), frozen [`v0.10`](report-schema.v0.10.json), frozen [`v0.9`](report-schema.v0.9.json), frozen [`v0.8`](report-schema.v0.8.json), frozen [`v0.7`](report-schema.v0.7.json), frozen [`v0.6`](report-schema.v0.6.json), older +- Frozen-reference report schemas: frozen [`v0.32`](report-schema.v0.32.json), frozen [`v0.31`](report-schema.v0.31.json), frozen [`v0.30`](report-schema.v0.30.json), frozen [`v0.29`](report-schema.v0.29.json), frozen [`v0.28`](report-schema.v0.28.json), frozen [`v0.27`](report-schema.v0.27.json), frozen [`v0.26`](report-schema.v0.26.json), frozen [`v0.25`](report-schema.v0.25.json), frozen [`v0.24`](report-schema.v0.24.json), frozen [`v0.23`](report-schema.v0.23.json), frozen [`v0.22`](report-schema.v0.22.json), frozen [`v0.21`](report-schema.v0.21.json), frozen [`v0.20`](report-schema.v0.20.json), frozen [`v0.19`](report-schema.v0.19.json), frozen [`v0.18`](report-schema.v0.18.json), frozen [`v0.17`](report-schema.v0.17.json), frozen [`v0.16`](report-schema.v0.16.json), frozen [`v0.15`](report-schema.v0.15.json), frozen [`v0.14`](report-schema.v0.14.json), frozen [`v0.13`](report-schema.v0.13.json), frozen [`v0.12`](report-schema.v0.12.json), frozen [`v0.11`](report-schema.v0.11.json), frozen [`v0.10`](report-schema.v0.10.json), frozen [`v0.9`](report-schema.v0.9.json), frozen [`v0.8`](report-schema.v0.8.json), frozen [`v0.7`](report-schema.v0.7.json), frozen [`v0.6`](report-schema.v0.6.json), older - Frozen-reference packet schemas live in [`docs/INDEX.md`](INDEX.md#reference). -- Boundary v1, verifier v0.1/v0.2, verify-run v1, handoff v1/v2, and preflight +- Boundary v1, verifier v0.1/v0.2/v0.3, verify-run v1, handoff v1/v2/v3, and preflight v0.1/v0.2 remain frozen references for legacy readers. - Frozen experimental capability lock and governance benchmark result schemas live in [`docs/INDEX.md`](INDEX.md#reference). @@ -88,7 +88,7 @@ one decision engine. - **PR / controller flow** — an autonomous coding agent deciding *continue, repair, or stop*. Prefer `agents-shipgate-reports/agent-handoff.json` for the compact - `shipgate.agent_handoff/v3` view: lead with `control.state`, then read + `shipgate.agent_handoff/v4` view: lead with `control.state`, then read `control.next_action`, `gate.merge_verdict`, and `reproducibility.run_id` for the stable verify identity. `verifier.json` remains the authoritative controller substrate and `verify-run.json` remains the reproducibility record; finally @@ -142,17 +142,19 @@ In `agents-shipgate-reports/report.json`: - `release_decision.{blockers,review_items}[].capability_refs` (v0.24+) — stable capability IDs copied from the originating finding when a policy or policy-pack rule matched a `CapabilityFactV1`. Empty for findings that are not capability-policy matches. This is audit metadata only; `release_decision.decision` remains the gate. - `release_decision.{blockers,review_items}[].capability_trace_refs` (v0.25+) — stable local trace-evidence IDs copied from the originating finding when an existing trace/evidence check used declared local trace artifacts. Empty when no local trace row is relevant. This is audit metadata only; `release_decision.decision` remains the gate. - `release_decision.evidence_coverage.semantic_coverage` (v0.29+) — `{total_actions, pass_eligible_actions, gap_count, review_concern_count, reason_counts}`. A non-zero semantic `gap_count` prevents `passed`; a non-zero `review_concern_count` prevents an automatic pass and routes known unscoped/ambient authority to review. Semantic gaps are not Findings and cannot be suppressed, baselined, severity-overridden, waived by `--no-heuristics`, or satisfied by `human_ack`. +- `release_decision.evidence_coverage.policy_gap_count` and top-level `policy_evidence_gaps[]` (v0.33+) — policy applicability that is heuristic-only, mixed, unknown, or conflicting. These rows are outside Findings and cannot be suppressed, baselined, severity-overridden, acknowledged, or removed by `--no-heuristics`; any row prevents `passed`. - `release_decision.evidence_coverage.identity_coverage` (v0.30+) — `{total_observations, canonical_tools, bound_tools, pass_eligible_tools, ambiguous_name_count, gap_count, reason_counts}`. Provider-scoped observations remain separate unless an exact reviewed `tool_identity.bindings[]` entry joins them. Any ambiguous selector, invalid binding, or conflicting identity prevents `passed`. - `release_decision.evidence_coverage.evidence_gaps[]` (v0.26+; semantic kinds added v0.29) — one structured row per measurable gap: `{kind, subject, source_type, source_ref, why, next_action}`. In addition to `low_confidence_tool` and `source_warning`, v0.29 adds `incomplete_surface`, `missing_effect_evidence`, `inferred_effect_only`, `conflicting_effect_evidence`, `missing_authority_evidence`, `partial_authority_evidence`, `conflicting_authority_evidence`, and `invalid_semantic_annotation`. Semantic next actions use `declare_action_effect`, `declare_action_authority`, `provide_complete_inventory`, or `resolve_semantic_conflict`, include accepted values and exact source/manifest pointers, and are always human-routed. Their declaration placeholders carry `suggested_patch_kind="manual"`, `auto_apply=false`, and `requires_human_review=true`; they are not Patch objects. Work the rows in order instead of guessing; Agents Shipgate never auto-asserts effect or authority. - `loaded_policy_packs[].{source,sha256,sha256_status,owner}` (v0.27+) — policy-pack distribution and ownership metadata for organization audit. `sha256_status` is `"verified"` only when the manifest pin matched; otherwise it is `"unpinned"`. This is report metadata; normal pack matching and release gating still come from deterministic rules and `release_decision.decision`. -- `findings[].policy_routing` (v0.28+) — optional policy-pack owner, reviewers, and approval-routing metadata. This is non-enforcing reviewer/audit metadata, not `Finding.evidence`; it does not affect fingerprints, suppressions, baselines, `blocks_release`, or `release_decision`. Policy-pack `match` predicates and `block: true` remain the only policy-pack inputs that affect findings and release gating. +- `findings[].support` (v0.33+) — typed predicate support with status, effective confidence, policy/block eligibility, claim IDs, evidence bases, predicate rows, and `support_hash`. Rule confidence and `block: true` are ceilings/requests; they cannot upgrade the support. Baseline matching for supported findings requires the same support hash. +- `findings[].policy_routing` (v0.28+) — optional policy-pack owner, reviewers, and approval-routing metadata. This is non-enforcing reviewer/audit metadata, not `Finding.evidence`; it does not affect fingerprints, suppressions, baselines, `blocks_release`, or `release_decision`. - `release_decision.fail_policy.would_fail_ci` — `true`/`false`. Matches what the CI process will exit with. For a semantic evidence gap, strict mode emits the consistent tuple `decision="insufficient_evidence"`, `would_fail_ci=true`, `exit_code=20`; advisory mode keeps exit `0` while preserving the same non-pass decision. - `release_decision.reason` — one-sentence explanation suitable for a PR comment. -- `release_decision.contribution_rules[]` (v0.17+) — deterministic per-finding audit explaining how each `report.findings` entry was classified. Exactly one row per finding (including suppressed). Each row carries `{finding_id, fingerprint, check_id, category, rule, rationale}`. `category` ∈ `{blocker, review_item, excluded}`; `rule` ∈ `{policy_block_new, severity_block_new, policy_baseline_accepted, severity_baseline_accepted, review_required, sub_threshold, suppressed}`. Reading the contribution rule is sufficient to predict the gate outcome for that finding without re-deriving the decision logic — the closed grammar of `(rule, category)` pairs is documented in [STABILITY.md "Release decision truth table"](../STABILITY.md#release-decision-truth-table). The audit cannot disagree with `blockers[]` / `review_items[]` (the same classification powers both). +- `release_decision.contribution_rules[]` (v0.17+) — deterministic per-finding audit explaining how each `report.findings` entry was classified. Exactly one row per finding (including suppressed). In v0.33, `unsupported_evidence` records a finding that cannot contribute because its typed support is not policy-eligible. Reading the contribution rule is sufficient to predict the gate outcome without re-deriving the decision logic. - `privacy_audit` (v0.18+) — confirms the default redaction pass ran before public artifacts were written. Read `enabled`, `rules_version`, `sensitive_field_inventory_version`, `redacted_occurrence_count`, `redacted_paths[]`, and `output_surfaces[]`. `redacted_paths[]` contains structural paths and counts only, never raw values or raw hashes. - `reviewer_summary` (v0.20+) — deterministic projection of the reviewer lens surfaces and audit envelopes; the reviewer-side parallel to `agent_summary`. Read this block first when triaging a scan for a human reviewer. Carries `verdict` (mirrors `release_decision.decision`), `headline` (≤200 chars, PR-comment-friendly), per-lens activity counts (`tool_surface_changes`, `capability_misalignments`, `action_surface_changes`, `evidence_matrix_gaps`), per-audit-envelope counts (`severity_overrides_applied`, `severity_overrides_tier_crossed`, `privacy_redactions`, `baseline_integrity_issues`), and `first_recommended_surface: ReviewerSurfacePointer | None` — a deterministic pointer naming which lens/audit to open first (`{kind, name, path, why}` where `kind` ∈ `{release_decision, lens, audit, evidence_matrix}` and `name` ∈ `{tool_surface_diff, capability_intent_diff, action_surface_diff, evidence_matrix, policy_audit, privacy_audit, baseline_integrity, release_decision}`). Same inputs always produce the same output; this block cannot disagree with the underlying lens/audit data. - `heuristics_filter` (v0.21+) — top-level audit envelope describing the `--no-heuristics` CLI filter pass. Always present, even when the flag is unset (`enabled: False` with zero counts), so the report shape is stable. Carries `enabled: bool`, `excluded_provenance_kinds: list[str]` (`["keyword_heuristic", "regex_heuristic"]`), `filtered_finding_count: int`, and `filtered_by_kind: dict[str, int]` (per-kind breakdown). When `enabled: True`, findings whose `provenance_kind` is in the excluded list have been marked `suppressed=True` with `suppression_reason="filtered by --no-heuristics"` BEFORE the release decision was built — they remain in `findings[]` for transparency but no longer gate release. The filter never un-suppresses a finding; manifest-driven suppression reasons are preserved when they overlap with the filter. Useful for security/GRC reviewers who want declared-only findings. @@ -279,7 +281,7 @@ separately), local input hashes (`config_sha256`, `baseline_sha256`, emitted files. It has no wall-clock timestamp and is not a second gate. `agents-shipgate-reports/agent-handoff.json` carries -`schema_version: "shipgate.agent_handoff/v3"` and top-level sections +`schema_version: "shipgate.agent_handoff/v4"` and top-level sections `gate`, `control`, `fix_task`, `blocked_by[]`, `remediation_plan[]`, `capability_review`, `reproducibility`, and `artifacts`. `gate.decision` mirrors `release_decision.decision`; `gate.merge_verdict` @@ -296,8 +298,8 @@ from existing artifacts with: agents-shipgate agent handoff --from agents-shipgate-reports/verifier.json --json ``` -In `agents-shipgate-reports/verifier.json`, read the v0.3 fields below (full -schema [`docs/verifier-schema.v0.3.json`](verifier-schema.v0.3.json)). **Lead +In `agents-shipgate-reports/verifier.json`, read the v0.4 fields below (full +schema [`docs/verifier-schema.v0.4.json`](verifier-schema.v0.4.json)). **Lead with `control.state`.** Every field below is a mirror or deterministic projection of `report.json`; `release_decision.decision` remains the gate. @@ -329,9 +331,9 @@ with `control.state`.** Every field below is a mirror or deterministic projectio - `decision` — mirror of `release_decision.decision` (or `null` when no scan ran). - `headline` — single-sentence, PR-comment-friendly summary (or `null`). - `control.human_review` and `control.next_action` are the only serialized - human-review and next-action authority in verifier v0.3. + human-review and next-action authority in verifier v0.4. - `AgentController`, `VerifierNextAction`, and `VerifierHumanReview` remain - importable only as deprecated v0.1/v0.2 reader models. Verifier v0.3 does not + importable only as deprecated v0.1/v0.2 reader models. Verifier v0.4 does not emit or invoke the retired `build_agent_controller` projector. - `fix_task` — `{actor, safe_to_attempt, instructions[], allowed_repairs[], forbidden_repairs[], forbidden_shortcuts[], verification_command, patches[]}` or `null`. @@ -503,7 +505,7 @@ Per-finding `provenance_kind` enum (v0.15+), additive classification — read th - `ast_extraction` — Tool parsed from user Python source by a framework extractor (LangChain function/structured tools, CrewAI function/class tools, ADK Python toolsets). Subject to extraction errors; agents that distrust AST quality may filter these as a class. - `keyword_heuristic` — matched a keyword list (broad-scope tokens, read-only/approval prompt terms, free-text parameter names). Higher false-positive risk than declarative facts. - `regex_heuristic` — matched a regex (secret-like values in descriptions, prompt-injection patterns). Highest false-positive risk; pair with the recommendation before acting. -- `policy_pack` — emitted by an external policy pack rule. The rule's own confidence applies — Shipgate does not second-guess the pack. +- `policy_pack` — emitted by an external policy pack rule after its predicates have authoritative typed support. Rule confidence can lower, but never raise, evidence confidence. - `runtime_trace` — derived from declared local trace artifacts. Audit evidence only; never filtered by `--no-heuristics`. Provenance generally follows the rule's own trigger (e.g., a rule that checks for a declared manifest field is `static_declaration` even when the underlying Tool was AST-extracted). For framework checks that fire across both AST and declarative tool sources (ADK's per-tool checks against `google_adk_function` AND `google_adk_config` tools), the label tracks the underlying tool's source. Third-party plugin checks that don't yet set the field land at `static_declaration` by default — pre-v0.15 plugins continue to validate against the v0.15 wire schema. Use `findings[].source.type` for the precise underlying tool source. @@ -523,8 +525,8 @@ For reviewer-shaped output, also read the **Release Evidence Packet** at `[pdf]` extras are installed). The packet is a supporting/provisional reviewer projection, not a second gate. Packet outputs are redacted by the same default privacy layer as the report. The packet has fixed reviewer sections governed by -[`docs/packet-schema.v0.10.json`](packet-schema.v0.10.json) — see -[STABILITY.md §Release Evidence Packet](../STABILITY.md#release-evidence-packet-v010). +[`docs/packet-schema.v0.11.json`](packet-schema.v0.11.json) — see +[STABILITY.md §Release Evidence Packet](../STABILITY.md#release-evidence-packet-v011). Packet schema `0.9` carries the report's evidence-backed semantic coverage and gap remediation contract. Packet §1 also mirrors `static_analysis_only=true`, `runtime_behavior_verified=false`, and @@ -577,9 +579,9 @@ Companion prompt: [`prompts/explain-finding-to-user.md`](../prompts/explain-find - [STABILITY.md](../STABILITY.md) — full alpha stability contract. Source of truth for everything above. - [AGENTS.md](../AGENTS.md) — agent-facing instructions: install, run, single-turn flow, error semantics. -- [`docs/report-schema.v0.32.json`](report-schema.v0.32.json) — machine-validatable JSON Schema for the current report. +- [`docs/report-schema.v0.33.json`](report-schema.v0.33.json) — machine-validatable JSON Schema for the current report. - [`docs/privacy.md`](privacy.md) and [`docs/report-sensitive-fields.json`](report-sensitive-fields.json) — default redaction behavior and sensitive-field inventory. -- [`docs/packet-schema.v0.10.json`](packet-schema.v0.10.json) — machine-validatable JSON Schema for the current packet. +- [`docs/packet-schema.v0.11.json`](packet-schema.v0.11.json) — machine-validatable JSON Schema for the current packet. - [`docs/checks.json`](checks.json) — check catalog, including `mvp_tier` for MVP/readiness triage. ## See also diff --git a/docs/agent-handoff-schema.v4.json b/docs/agent-handoff-schema.v4.json new file mode 100644 index 00000000..16cd5671 --- /dev/null +++ b/docs/agent-handoff-schema.v4.json @@ -0,0 +1,1023 @@ +{ + "$defs": { + "AgentActionRequiredControl": { + "additionalProperties": false, + "description": "Non-terminal state with one exact coding-agent-owned next step.", + "properties": { + "allowed_next_commands": { + "items": { + "minLength": 1, + "type": "string" + }, + "title": "Allowed Next Commands", + "type": "array" + }, + "completion_allowed": { + "const": false, + "default": false, + "title": "Completion Allowed", + "type": "boolean" + }, + "human_review": { + "$ref": "#/$defs/NoHumanReview" + }, + "must_stop": { + "const": false, + "default": false, + "title": "Must Stop", + "type": "boolean" + }, + "next_action": { + "$ref": "#/$defs/CodingAgentAction" + }, + "reason": { + "minLength": 1, + "title": "Reason", + "type": "string" + }, + "state": { + "const": "agent_action_required", + "title": "State", + "type": "string" + }, + "stop_reason": { + "default": null, + "title": "Stop Reason", + "type": "null" + }, + "verify_required": { + "default": false, + "title": "Verify Required", + "type": "boolean" + } + }, + "required": [ + "state", + "reason", + "completion_allowed", + "must_stop", + "verify_required", + "next_action", + "allowed_next_commands", + "human_review", + "stop_reason" + ], + "title": "AgentActionRequiredControl", + "type": "object" + }, + "AgentControl": { + "discriminator": { + "mapping": { + "agent_action_required": "#/$defs/AgentActionRequiredControl", + "complete": "#/$defs/CompleteAgentControl", + "human_review_required": "#/$defs/HumanReviewRequiredControl" + }, + "propertyName": "state" + }, + "oneOf": [ + { + "$ref": "#/$defs/CompleteAgentControl" + }, + { + "$ref": "#/$defs/AgentActionRequiredControl" + }, + { + "$ref": "#/$defs/HumanReviewRequiredControl" + } + ] + }, + "AgentHandoffBlockedBy": { + "additionalProperties": false, + "properties": { + "baseline_status": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Baseline Status" + }, + "blocks_release": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Blocks Release" + }, + "bucket": { + "enum": [ + "blocker", + "review_item" + ], + "title": "Bucket", + "type": "string" + }, + "capability_refs": { + "items": { + "type": "string" + }, + "title": "Capability Refs", + "type": "array" + }, + "capability_trace_refs": { + "items": { + "type": "string" + }, + "title": "Capability Trace Refs", + "type": "array" + }, + "check_id": { + "title": "Check Id", + "type": "string" + }, + "fingerprint": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Fingerprint" + }, + "id": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Id" + }, + "severity": { + "title": "Severity", + "type": "string" + }, + "support_hash": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Support Hash" + }, + "title": { + "title": "Title", + "type": "string" + } + }, + "required": [ + "bucket", + "check_id", + "severity", + "title" + ], + "title": "AgentHandoffBlockedBy", + "type": "object" + }, + "AgentHandoffGateV3": { + "additionalProperties": false, + "description": "Current gate projection with explicit execution applicability.", + "properties": { + "applicability": { + "enum": [ + "not_evaluated", + "verified", + "not_applicable", + "failed" + ], + "title": "Applicability", + "type": "string" + }, + "can_merge_without_human": { + "default": false, + "title": "Can Merge Without Human", + "type": "boolean" + }, + "ci_would_fail": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Ci Would Fail" + }, + "decision": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Decision" + }, + "gating_signal": { + "const": "release_decision.decision", + "default": "release_decision.decision", + "title": "Gating Signal", + "type": "string" + }, + "merge_verdict": { + "enum": [ + "mergeable", + "human_review_required", + "insufficient_evidence", + "blocked", + "unknown" + ], + "title": "Merge Verdict", + "type": "string" + }, + "runtime_behavior_verified": { + "const": false, + "default": false, + "title": "Runtime Behavior Verified", + "type": "boolean" + }, + "static_analysis_only": { + "const": true, + "default": true, + "title": "Static Analysis Only", + "type": "boolean" + }, + "static_verdict_disclaimer": { + "default": "This verdict covers deterministic static evidence only. Agents Shipgate did not execute the agent or prove runtime behavior, tool routing, credential enforcement, or safety.", + "title": "Static Verdict Disclaimer", + "type": "string" + } + }, + "required": [ + "merge_verdict", + "applicability" + ], + "title": "AgentHandoffGateV3", + "type": "object" + }, + "AgentHandoffRemediationStep": { + "additionalProperties": false, + "properties": { + "actor": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Actor" + }, + "check_id": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Check Id" + }, + "command": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Command" + }, + "finding_id": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Finding Id" + }, + "id": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Id" + }, + "kind": { + "title": "Kind", + "type": "string" + }, + "patch": { + "anyOf": [ + { + "additionalProperties": true, + "type": "object" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Patch" + }, + "reason": { + "title": "Reason", + "type": "string" + }, + "safety": { + "enum": [ + "allowed", + "forbidden", + "patch" + ], + "title": "Safety", + "type": "string" + }, + "target": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Target" + } + }, + "required": [ + "safety", + "kind", + "reason" + ], + "title": "AgentHandoffRemediationStep", + "type": "object" + }, + "AgentHandoffReproducibility": { + "additionalProperties": false, + "properties": { + "artifact_sha256": { + "additionalProperties": { + "type": "string" + }, + "title": "Artifact Sha256", + "type": "object" + }, + "baseline_sha256": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Baseline Sha256" + }, + "config_sha256": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Config Sha256" + }, + "policy_packs": { + "items": { + "additionalProperties": true, + "type": "object" + }, + "title": "Policy Packs", + "type": "array" + }, + "run_id": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Run Id" + } + }, + "title": "AgentHandoffReproducibility", + "type": "object" + }, + "AgentHandoffSubject": { + "additionalProperties": false, + "properties": { + "base_ref": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Base Ref" + }, + "changed_files": { + "items": { + "type": "string" + }, + "title": "Changed Files", + "type": "array" + }, + "config": { + "title": "Config", + "type": "string" + }, + "head_ref": { + "default": "HEAD", + "title": "Head Ref", + "type": "string" + }, + "workspace": { + "title": "Workspace", + "type": "string" + } + }, + "required": [ + "workspace", + "config" + ], + "title": "AgentHandoffSubject", + "type": "object" + }, + "AgentHandoffTool": { + "additionalProperties": false, + "properties": { + "name": { + "default": "agents-shipgate", + "title": "Name", + "type": "string" + }, + "version": { + "default": "0.16.0b5", + "title": "Version", + "type": "string" + } + }, + "title": "AgentHandoffTool", + "type": "object" + }, + "CodingAgentAction": { + "discriminator": { + "mapping": { + "configure": "#/$defs/CodingAgentCommandAction", + "discover": "#/$defs/CodingAgentCommandAction", + "fetch_base": "#/$defs/CodingAgentFetchBaseAction", + "initialize": "#/$defs/CodingAgentCommandAction", + "install": "#/$defs/CodingAgentCommandAction", + "repair": "#/$defs/CodingAgentCommandAction", + "rerun": "#/$defs/CodingAgentCommandAction", + "verify": "#/$defs/CodingAgentCommandAction" + }, + "propertyName": "kind" + }, + "oneOf": [ + { + "$ref": "#/$defs/CodingAgentCommandAction" + }, + { + "$ref": "#/$defs/CodingAgentFetchBaseAction" + } + ] + }, + "CodingAgentCommandAction": { + "additionalProperties": false, + "description": "An executable, exact next step owned by the coding agent.", + "properties": { + "actor": { + "const": "coding_agent", + "default": "coding_agent", + "title": "Actor", + "type": "string" + }, + "command": { + "minLength": 1, + "title": "Command", + "type": "string" + }, + "expects": { + "default": null, + "title": "Expects", + "type": "null" + }, + "kind": { + "enum": [ + "verify", + "discover", + "configure", + "initialize", + "repair", + "install", + "rerun" + ], + "title": "Kind", + "type": "string" + }, + "why": { + "minLength": 1, + "title": "Why", + "type": "string" + } + }, + "required": [ + "actor", + "kind", + "command", + "expects", + "why" + ], + "title": "CodingAgentCommandAction", + "type": "object" + }, + "CodingAgentFetchBaseAction": { + "additionalProperties": false, + "description": "A structured input request when an exact fetch command is unavailable.\n\nShipgate never fetches refs itself. ``expects`` therefore names the exact\nref or artifact a caller must make available before rerunning verification.", + "properties": { + "actor": { + "const": "coding_agent", + "default": "coding_agent", + "title": "Actor", + "type": "string" + }, + "command": { + "default": null, + "title": "Command", + "type": "null" + }, + "expects": { + "minLength": 1, + "title": "Expects", + "type": "string" + }, + "kind": { + "const": "fetch_base", + "title": "Kind", + "type": "string" + }, + "why": { + "minLength": 1, + "title": "Why", + "type": "string" + } + }, + "required": [ + "actor", + "kind", + "command", + "expects", + "why" + ], + "title": "CodingAgentFetchBaseAction", + "type": "object" + }, + "CompleteAgentControl": { + "additionalProperties": false, + "description": "Terminal state: the coding agent may report the task complete.", + "properties": { + "allowed_next_commands": { + "items": { + "minLength": 1, + "type": "string" + }, + "maxItems": 0, + "title": "Allowed Next Commands", + "type": "array" + }, + "completion_allowed": { + "const": true, + "default": true, + "title": "Completion Allowed", + "type": "boolean" + }, + "human_review": { + "$ref": "#/$defs/NoHumanReview" + }, + "must_stop": { + "const": false, + "default": false, + "title": "Must Stop", + "type": "boolean" + }, + "next_action": { + "default": null, + "title": "Next Action", + "type": "null" + }, + "reason": { + "minLength": 1, + "title": "Reason", + "type": "string" + }, + "state": { + "const": "complete", + "title": "State", + "type": "string" + }, + "stop_reason": { + "default": null, + "title": "Stop Reason", + "type": "null" + }, + "verify_required": { + "const": false, + "default": false, + "title": "Verify Required", + "type": "boolean" + } + }, + "required": [ + "state", + "reason", + "completion_allowed", + "must_stop", + "verify_required", + "next_action", + "allowed_next_commands", + "human_review", + "stop_reason" + ], + "title": "CompleteAgentControl", + "type": "object" + }, + "HumanControlAction": { + "additionalProperties": false, + "description": "A human-owned route. Human actions never expose executable commands.", + "properties": { + "actor": { + "const": "human", + "default": "human", + "title": "Actor", + "type": "string" + }, + "command": { + "default": null, + "title": "Command", + "type": "null" + }, + "expects": { + "default": null, + "title": "Expects", + "type": "null" + }, + "kind": { + "enum": [ + "review", + "stop" + ], + "title": "Kind", + "type": "string" + }, + "why": { + "minLength": 1, + "title": "Why", + "type": "string" + } + }, + "required": [ + "actor", + "kind", + "command", + "expects", + "why" + ], + "title": "HumanControlAction", + "type": "object" + }, + "HumanReviewRequiredControl": { + "additionalProperties": false, + "description": "Stopping state: no further coding-agent action is authorized.", + "properties": { + "allowed_next_commands": { + "items": { + "minLength": 1, + "type": "string" + }, + "maxItems": 0, + "title": "Allowed Next Commands", + "type": "array" + }, + "completion_allowed": { + "const": false, + "default": false, + "title": "Completion Allowed", + "type": "boolean" + }, + "human_review": { + "$ref": "#/$defs/RequiredHumanReview" + }, + "must_stop": { + "const": true, + "default": true, + "title": "Must Stop", + "type": "boolean" + }, + "next_action": { + "$ref": "#/$defs/HumanControlAction" + }, + "reason": { + "minLength": 1, + "title": "Reason", + "type": "string" + }, + "state": { + "const": "human_review_required", + "title": "State", + "type": "string" + }, + "stop_reason": { + "minLength": 1, + "title": "Stop Reason", + "type": "string" + }, + "verify_required": { + "default": false, + "title": "Verify Required", + "type": "boolean" + } + }, + "required": [ + "state", + "reason", + "completion_allowed", + "must_stop", + "verify_required", + "next_action", + "allowed_next_commands", + "human_review", + "stop_reason" + ], + "title": "HumanReviewRequiredControl", + "type": "object" + }, + "NoHumanReview": { + "additionalProperties": false, + "description": "Exact negative human-review projection for non-stopping states.", + "properties": { + "required": { + "const": false, + "default": false, + "title": "Required", + "type": "boolean" + }, + "required_reviewers": { + "items": { + "type": "string" + }, + "maxItems": 0, + "title": "Required Reviewers", + "type": "array" + }, + "why": { + "default": null, + "title": "Why", + "type": "null" + } + }, + "required": [ + "required", + "why", + "required_reviewers" + ], + "title": "NoHumanReview", + "type": "object" + }, + "RequiredHumanReview": { + "additionalProperties": false, + "description": "Human-review evidence carried by the stopping state.", + "properties": { + "required": { + "const": true, + "default": true, + "title": "Required", + "type": "boolean" + }, + "required_reviewers": { + "items": { + "minLength": 1, + "type": "string" + }, + "title": "Required Reviewers", + "type": "array" + }, + "why": { + "minLength": 1, + "title": "Why", + "type": "string" + } + }, + "required": [ + "required", + "why", + "required_reviewers" + ], + "title": "RequiredHumanReview", + "type": "object" + } + }, + "$id": "https://raw.githubusercontent.com/ThreeMoonsLab/agents-shipgate/main/docs/agent-handoff-schema.v4.json", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "additionalProperties": false, + "description": "JSON Schema for agents-shipgate-reports/agent-handoff.json. Generated from agents_shipgate.schemas.agent_handoff.AgentHandoffArtifact. It is a compact projection for coding agents and does not gate releases; release_decision.decision remains the only gate.", + "else": { + "properties": { + "control": { + "properties": { + "state": { + "enum": [ + "agent_action_required", + "human_review_required" + ] + } + }, + "required": [ + "state" + ] + } + } + }, + "if": { + "properties": { + "gate": { + "properties": { + "can_merge_without_human": { + "const": true + } + }, + "required": [ + "can_merge_without_human" + ] + } + }, + "required": [ + "gate" + ] + }, + "properties": { + "artifacts": { + "additionalProperties": { + "type": "string" + }, + "title": "Artifacts", + "type": "object" + }, + "blocked_by": { + "items": { + "$ref": "#/$defs/AgentHandoffBlockedBy" + }, + "title": "Blocked By", + "type": "array" + }, + "capability_review": { + "additionalProperties": true, + "title": "Capability Review", + "type": "object" + }, + "contract_version": { + "title": "Contract Version", + "type": "string" + }, + "control": { + "$ref": "#/$defs/AgentControl" + }, + "fix_task": { + "anyOf": [ + { + "additionalProperties": true, + "type": "object" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Fix Task" + }, + "forbidden_actions": { + "items": { + "type": "string" + }, + "title": "Forbidden Actions", + "type": "array" + }, + "forbidden_file_edits": { + "items": { + "type": "string" + }, + "title": "Forbidden File Edits", + "type": "array" + }, + "gate": { + "$ref": "#/$defs/AgentHandoffGateV3" + }, + "operation": { + "enum": [ + "verify_pr", + "verify_local", + "verify_preview" + ], + "title": "Operation", + "type": "string" + }, + "remediation_plan": { + "items": { + "$ref": "#/$defs/AgentHandoffRemediationStep" + }, + "title": "Remediation Plan", + "type": "array" + }, + "reproducibility": { + "$ref": "#/$defs/AgentHandoffReproducibility" + }, + "schema_version": { + "const": "shipgate.agent_handoff/v4", + "default": "shipgate.agent_handoff/v4", + "title": "Schema Version", + "type": "string" + }, + "subject": { + "$ref": "#/$defs/AgentHandoffSubject" + }, + "tool": { + "$ref": "#/$defs/AgentHandoffTool" + } + }, + "required": [ + "contract_version", + "operation", + "subject", + "gate", + "control" + ], + "then": { + "properties": { + "control": { + "properties": { + "state": { + "const": "complete" + } + }, + "required": [ + "state" + ] + }, + "fix_task": { + "type": "null" + } + } + }, + "title": "Agents Shipgate Agent Handoff v4", + "type": "object" +} diff --git a/docs/agent-result-schema.v2.json b/docs/agent-result-schema.v2.json index 065547bc..1dcbfd6c 100644 --- a/docs/agent-result-schema.v2.json +++ b/docs/agent-result-schema.v2.json @@ -401,7 +401,7 @@ "type": "string" }, "version": { - "default": "0.16.0b4", + "default": "0.16.0b5", "title": "Version", "type": "string" } diff --git a/docs/agents/protocol.md b/docs/agents/protocol.md index bb82b095..51114f64 100644 --- a/docs/agents/protocol.md +++ b/docs/agents/protocol.md @@ -309,7 +309,7 @@ capability evidence requests, and host/MCP permission review. `shipgate.explain` deterministic check/finding explanation JSON. `shipgate.capabilities` returns capability lock or capability lock diff JSON. `shipgate.handoff` reads existing `verifier.json` / `report.json` / `verify-run.json` artifacts and returns exact -`shipgate.agent_handoff/v3`. These are projections only; the +`shipgate.agent_handoff/v4`. These are projections only; the release gate remains `report.json.release_decision.decision`. The MCP server is a static adapter only. It exposes no scan, verify, diff --git a/docs/ai-search-summary.md b/docs/ai-search-summary.md index 622597fa..b8f0c7c5 100644 --- a/docs/ai-search-summary.md +++ b/docs/ai-search-summary.md @@ -90,7 +90,7 @@ Per-agent guides cover [Codex](agents/use-with-codex.md), [Claude Code](agents/use-with-claude-code.md), and [Cursor](agents/use-with-cursor.md). -The current source tree is `0.16.0b4` (runtime contract v15); the latest +The current source tree is `0.16.0b5` (runtime contract v16); the latest published release remains `v0.15.0` until that beta is cut. In report v0.32, `passed` is an evidence-backed static verdict: the configured root has a complete reachable binding graph, every reachable action has complete, @@ -169,6 +169,6 @@ shipgate, and Agents-Shipgate. - Agent instructions: [`../AGENTS.md`](../AGENTS.md) - Machine-readable summary: [`../llms.txt`](../llms.txt) - Discovery metadata: [`../.well-known/agents-shipgate.json`](../.well-known/agents-shipgate.json) -- Report schema (current): [`report-schema.v0.32.json`](report-schema.v0.32.json) (v0.31 frozen at [`report-schema.v0.31.json`](report-schema.v0.31.json), v0.30 frozen at [`report-schema.v0.30.json`](report-schema.v0.30.json)) +- Report schema (current): [`report-schema.v0.33.json`](report-schema.v0.33.json) (v0.31 frozen at [`report-schema.v0.31.json`](report-schema.v0.31.json), v0.30 frozen at [`report-schema.v0.30.json`](report-schema.v0.30.json)) - Packet schema (current): [`packet-schema.v0.10.json`](packet-schema.v0.10.json) (v0.9 frozen at [`packet-schema.v0.9.json`](packet-schema.v0.9.json)) - Check catalog: [`checks.json`](checks.json) diff --git a/docs/architecture.md b/docs/architecture.md index 8fe4c332..04ed6a7a 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -3,7 +3,7 @@ A single-page summary of the `agents-shipgate` codebase for new contributors and AI coding agents extending the project. Current as of 2026-07-09; auto-checked against `agents-shipgate contract --json`: -runtime contract `15`, report schema `v0.32`, packet schema `v0.10`. +runtime contract `16`, report schema `v0.33`, packet schema `v0.11`. For the per-field stability contract, see [`../STABILITY.md`](../STABILITY.md). For the agent-facing field index, @@ -520,7 +520,7 @@ containment); files larger than 10 MB rejected. `scan` emits a reviewer-shaped artifact alongside `report.{md,json,sarif}` whenever `output.packet.enabled` is true (default). The packet has its -own JSON contract ([`packet-schema.v0.10.json`](packet-schema.v0.10.json)) +own JSON contract ([`packet-schema.v0.11.json`](packet-schema.v0.11.json)) so the report schema stays minimal. The packet is derived from the in-memory scan (manifest, tools, @@ -664,10 +664,10 @@ contract. Headlines: - **Manifest schema** stable across `0.x` (`version: "0.1"`). - **Report JSON shape** is additive across the `0.x` line. Current - `report_schema_version: "0.32"`; older schemas frozen as + `report_schema_version: "0.33"`; older schemas frozen as `docs/report-schema.v0.N.json`. - **Packet JSON shape** is additive across the `0.x` line. Current - `packet_schema_version: "0.10"`; older schemas frozen. + `packet_schema_version: "0.11"`; older schemas frozen. - **Exit codes**: `0` pass, `2` manifest config error, `3` input parse error, `4` other error, `6` baseline integrity failure (strict `baseline verify` only), `20` strict-mode gate failure. diff --git a/docs/autofix-policy.md b/docs/autofix-policy.md index 6c18ff2f..b0090997 100644 --- a/docs/autofix-policy.md +++ b/docs/autofix-policy.md @@ -241,7 +241,7 @@ from the hash so toggling `--suggest-patches` doesn't shift it. - [`checks.md`](checks.md) — full check catalog with rationale. - [`minimal-real-configs.md`](minimal-real-configs.md) — per-framework minimal manifests to build from. -- [`report-schema.v0.32.json`](report-schema.v0.32.json) — current JSON +- [`report-schema.v0.33.json`](report-schema.v0.33.json) — current JSON Schema for `report.json`. - [`AGENTS.md`](../AGENTS.md) — top-level agent instructions, install, trigger table. diff --git a/docs/baseline.md b/docs/baseline.md index e2fe98e9..7554e12d 100644 --- a/docs/baseline.md +++ b/docs/baseline.md @@ -70,7 +70,7 @@ fail CI. Reports keep the v0.1 payload contract and add baseline fields: -- `report_schema_version: "0.32"` in current reports +- `report_schema_version: "0.33"` in current reports - `baseline.path` - `baseline.matched_count` - `baseline.new_count` @@ -94,6 +94,21 @@ rendered as `fp_`. It intentionally excludes severity overrides, report paths, warnings, timestamps, `default_severity` audit evidence, and baseline status. +Baseline schema `0.8` additionally records `support_hash` for findings whose +release contribution depends on typed policy evidence. The fingerprint remains +stable, but both fingerprint and support hash must match. A change from +heuristic, mixed, or unknown applicability to authoritative evidence is +therefore reviewed as new evidence rather than silently inheriting old debt. +Pre-`0.8` entries have no support binding, so supported findings intentionally +re-gate as `new` after upgrading to `0.16.0b5`. Review the new evidence, then +re-export accepted debt explicitly with: + +```bash +agents-shipgate baseline save -c shipgate.yaml \ + --out .agents-shipgate/baseline.json \ + --owner --reason "" --expires +``` + Report schema v0.18 computes public fingerprints after the default privacy redaction pass. Findings whose evidence contains a recognized secret-like value therefore get a new public fingerprint. To avoid surprise CI failures during @@ -104,7 +119,9 @@ public fingerprints. ## Baseline Schema Versions -`agents-shipgate baseline save` now writes baseline schema `0.6`, which adds +`agents-shipgate baseline save` now writes baseline schema `0.8`, which binds +supported findings to `support_hash`. Schema `0.7` introduced provider-scoped +finding identity and remains readable. Schema `0.6` adds the optional `provenance.owner` approver field alongside the reviewer-set `reason`/`expires` (all settable from `baseline save` flags). Schema `0.5` added per-entry provenance (`scanner_version`, `run_id`, `recorded_at`). diff --git a/docs/capability-lock-diff-schema.v0.7.json b/docs/capability-lock-diff-schema.v0.7.json new file mode 100644 index 00000000..b8c93c7d --- /dev/null +++ b/docs/capability-lock-diff-schema.v0.7.json @@ -0,0 +1,1321 @@ +{ + "$defs": { + "AuthoritySemanticEvidence": { + "additionalProperties": false, + "properties": { + "auth_type": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Auth Type" + }, + "claims": { + "items": { + "$ref": "#/$defs/SemanticClaimEvidence" + }, + "title": "Claims", + "type": "array" + }, + "credential_mode": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Credential Mode" + }, + "issues": { + "items": { + "$ref": "#/$defs/SemanticIssueEvidence" + }, + "title": "Issues", + "type": "array" + }, + "mode": { + "enum": [ + "none", + "scoped", + "unscoped", + "ambient", + "unknown" + ], + "title": "Mode", + "type": "string" + }, + "scopes": { + "items": { + "type": "string" + }, + "title": "Scopes", + "type": "array" + }, + "status": { + "enum": [ + "declared", + "structural", + "partial", + "unknown", + "conflicting" + ], + "title": "Status", + "type": "string" + } + }, + "required": [ + "status", + "mode" + ], + "title": "AuthoritySemanticEvidence", + "type": "object" + }, + "BindingSemanticEvidence": { + "additionalProperties": false, + "properties": { + "claims": { + "items": { + "$ref": "#/$defs/SemanticClaimEvidence" + }, + "title": "Claims", + "type": "array" + }, + "confidence": { + "enum": [ + "low", + "medium", + "high" + ], + "title": "Confidence", + "type": "string" + }, + "issues": { + "items": { + "$ref": "#/$defs/SemanticIssueEvidence" + }, + "title": "Issues", + "type": "array" + }, + "pass_eligible": { + "default": false, + "title": "Pass Eligible", + "type": "boolean" + }, + "reachable_path": { + "items": { + "type": "string" + }, + "title": "Reachable Path", + "type": "array" + }, + "root_agent_id": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Root Agent Id" + }, + "status": { + "enum": [ + "declared", + "structural", + "partial", + "unknown", + "conflicting" + ], + "title": "Status", + "type": "string" + } + }, + "required": [ + "status", + "confidence" + ], + "title": "BindingSemanticEvidence", + "type": "object" + }, + "CapabilityAuthority": { + "additionalProperties": false, + "description": "Authority source and scope facts for a capability.", + "properties": { + "auth_type": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Auth Type" + }, + "broad_scopes": { + "items": { + "type": "string" + }, + "title": "Broad Scopes", + "type": "array" + }, + "credential_mode": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Credential Mode" + }, + "scopes": { + "items": { + "type": "string" + }, + "title": "Scopes", + "type": "array" + }, + "source": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Source" + } + }, + "title": "CapabilityAuthority", + "type": "object" + }, + "CapabilityControls": { + "additionalProperties": false, + "description": "Policy and safeguard controls already known to the action surface.", + "properties": { + "approval_required": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Approval Required" + }, + "approval_threshold": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Approval Threshold" + }, + "confirmation_required": { + "default": false, + "title": "Confirmation Required", + "type": "boolean" + }, + "evidence_approval_ticket": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Evidence Approval Ticket" + }, + "evidence_owner": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Evidence Owner" + }, + "evidence_runbook": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Evidence Runbook" + }, + "safeguard_audit_log": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Safeguard Audit Log" + }, + "safeguard_dry_run": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Safeguard Dry Run" + }, + "safeguard_idempotency": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Safeguard Idempotency" + }, + "safeguard_rollback": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Safeguard Rollback" + } + }, + "title": "CapabilityControls", + "type": "object" + }, + "CapabilityEffect": { + "additionalProperties": false, + "description": "Normalized side-effect view derived from ``SideEffect``.", + "properties": { + "code_execution": { + "default": false, + "title": "Code Execution", + "type": "boolean" + }, + "effect": { + "enum": [ + "read", + "write", + "destructive", + "external_communication", + "financial_write", + "production_operation", + "privileged_data_access", + "code_execution", + "identity_access" + ], + "title": "Effect", + "type": "string" + }, + "externally_visible": { + "default": false, + "title": "Externally Visible", + "type": "boolean" + }, + "financial": { + "default": false, + "title": "Financial", + "type": "boolean" + }, + "handles_sensitive_data": { + "default": false, + "title": "Handles Sensitive Data", + "type": "boolean" + }, + "high_risk": { + "default": false, + "title": "High Risk", + "type": "boolean" + }, + "idempotency_known": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Idempotency Known" + }, + "reversibility": { + "default": "unknown", + "enum": [ + "reversible", + "irreversible", + "unknown" + ], + "title": "Reversibility", + "type": "string" + } + }, + "required": [ + "effect" + ], + "title": "CapabilityEffect", + "type": "object" + }, + "CapabilityEvidence": { + "additionalProperties": false, + "description": "Structured source provenance for a capability fact.", + "properties": { + "confidence": { + "default": "medium", + "enum": [ + "low", + "medium", + "high" + ], + "title": "Confidence", + "type": "string" + }, + "provenance_kind": { + "default": "static_declaration", + "enum": [ + "static_declaration", + "ast_extraction", + "keyword_heuristic", + "regex_heuristic", + "policy_pack" + ], + "title": "Provenance Kind", + "type": "string" + }, + "source_end_line": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Source End Line" + }, + "source_id": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Source Id" + }, + "source_location": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Source Location" + }, + "source_path": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Source Path" + }, + "source_pointer": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Source Pointer" + }, + "source_ref": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Source Ref" + }, + "source_start_column": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Source Start Column" + }, + "source_start_line": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Source Start Line" + }, + "source_type": { + "title": "Source Type", + "type": "string" + } + }, + "required": [ + "source_type" + ], + "title": "CapabilityEvidence", + "type": "object" + }, + "CapabilityFactV1": { + "additionalProperties": false, + "description": "Durable capability fact used by stable capability locks.", + "properties": { + "authority": { + "$ref": "#/$defs/CapabilityAuthority" + }, + "controls": { + "$ref": "#/$defs/CapabilityControls" + }, + "effect": { + "$ref": "#/$defs/CapabilityEffect" + }, + "evidence": { + "$ref": "#/$defs/CapabilityEvidence" + }, + "hashes": { + "$ref": "#/$defs/CapabilityHashes" + }, + "id": { + "title": "Id", + "type": "string" + }, + "identity": { + "$ref": "#/$defs/CapabilityIdentity" + }, + "risk_tags": { + "items": { + "type": "string" + }, + "title": "Risk Tags", + "type": "array" + }, + "semantic_assessment": { + "anyOf": [ + { + "$ref": "#/$defs/ToolSemanticEvidence" + }, + { + "type": "null" + } + ], + "default": null + } + }, + "required": [ + "id", + "identity", + "effect", + "authority", + "controls", + "evidence", + "hashes" + ], + "title": "CapabilityFactV1", + "type": "object" + }, + "CapabilityHashes": { + "additionalProperties": false, + "description": "Separate hashes for the future lock/diff substrate.", + "properties": { + "authority_hash": { + "title": "Authority Hash", + "type": "string" + }, + "binding_hash": { + "default": "legacy_binding_unknown", + "title": "Binding Hash", + "type": "string" + }, + "control_hash": { + "title": "Control Hash", + "type": "string" + }, + "effect_hash": { + "title": "Effect Hash", + "type": "string" + }, + "evidence_hash": { + "title": "Evidence Hash", + "type": "string" + }, + "identity_hash": { + "title": "Identity Hash", + "type": "string" + }, + "risk_hash": { + "title": "Risk Hash", + "type": "string" + }, + "schema_hash": { + "title": "Schema Hash", + "type": "string" + } + }, + "required": [ + "identity_hash", + "effect_hash", + "authority_hash", + "control_hash", + "schema_hash", + "risk_hash", + "evidence_hash" + ], + "title": "CapabilityHashes", + "type": "object" + }, + "CapabilityIdentity": { + "additionalProperties": false, + "description": "Stable semantic identity for an agent capability.", + "properties": { + "agent_id": { + "title": "Agent Id", + "type": "string" + }, + "operation": { + "title": "Operation", + "type": "string" + }, + "provider": { + "title": "Provider", + "type": "string" + }, + "resource": { + "items": { + "type": "string" + }, + "title": "Resource", + "type": "array" + }, + "scope": { + "items": { + "type": "string" + }, + "title": "Scope", + "type": "array" + }, + "subject_kind": { + "default": "action", + "enum": [ + "tool", + "action", + "scope", + "policy", + "ci", + "baseline", + "agent_instruction", + "manifest", + "unknown" + ], + "title": "Subject Kind", + "type": "string" + }, + "tool_id": { + "title": "Tool Id", + "type": "string" + }, + "tool_name": { + "title": "Tool Name", + "type": "string" + } + }, + "required": [ + "agent_id", + "tool_id", + "tool_name", + "provider", + "operation" + ], + "title": "CapabilityIdentity", + "type": "object" + }, + "CapabilityLockChangedFact": { + "additionalProperties": false, + "properties": { + "after": { + "$ref": "#/$defs/CapabilityFactV1" + }, + "before": { + "$ref": "#/$defs/CapabilityFactV1" + }, + "changed_hashes": { + "items": { + "enum": [ + "identity_hash", + "binding_hash", + "effect_hash", + "authority_hash", + "control_hash", + "schema_hash", + "risk_hash", + "evidence_hash" + ], + "type": "string" + }, + "title": "Changed Hashes", + "type": "array" + }, + "id": { + "title": "Id", + "type": "string" + }, + "operation": { + "title": "Operation", + "type": "string" + }, + "semantic_changes": { + "items": { + "$ref": "#/$defs/CapabilitySemanticChange" + }, + "title": "Semantic Changes", + "type": "array" + }, + "semantic_direction": { + "default": "unknown", + "enum": [ + "added", + "removed", + "broadened", + "narrowed", + "mixed", + "unknown", + "evidence_only" + ], + "title": "Semantic Direction", + "type": "string" + }, + "tool_name": { + "title": "Tool Name", + "type": "string" + } + }, + "required": [ + "id", + "tool_name", + "operation", + "before", + "after" + ], + "title": "CapabilityLockChangedFact", + "type": "object" + }, + "CapabilityLockDiffSummary": { + "additionalProperties": false, + "properties": { + "added": { + "default": 0, + "title": "Added", + "type": "integer" + }, + "changed": { + "default": 0, + "title": "Changed", + "type": "integer" + }, + "evidence_changed": { + "default": 0, + "title": "Evidence Changed", + "type": "integer" + }, + "reidentified": { + "default": 0, + "title": "Reidentified", + "type": "integer" + }, + "removed": { + "default": 0, + "title": "Removed", + "type": "integer" + }, + "unchanged": { + "default": 0, + "title": "Unchanged", + "type": "integer" + } + }, + "title": "CapabilityLockDiffSummary", + "type": "object" + }, + "CapabilityLockDiffV1": { + "additionalProperties": false, + "properties": { + "added": { + "items": { + "$ref": "#/$defs/CapabilityFactV1" + }, + "title": "Added", + "type": "array" + }, + "base": { + "$ref": "#/$defs/CapabilityLockRef" + }, + "capability_lock_diff_schema_version": { + "const": "0.7", + "default": "0.7", + "title": "Capability Lock Diff Schema Version", + "type": "string" + }, + "changed": { + "items": { + "$ref": "#/$defs/CapabilityLockChangedFact" + }, + "title": "Changed", + "type": "array" + }, + "evidence_changed": { + "items": { + "$ref": "#/$defs/CapabilityLockChangedFact" + }, + "title": "Evidence Changed", + "type": "array" + }, + "experimental": { + "const": false, + "default": false, + "title": "Experimental", + "type": "boolean" + }, + "head": { + "$ref": "#/$defs/CapabilityLockRef" + }, + "reidentified": { + "items": { + "$ref": "#/$defs/CapabilityLockChangedFact" + }, + "title": "Reidentified", + "type": "array" + }, + "removed": { + "items": { + "$ref": "#/$defs/CapabilityFactV1" + }, + "title": "Removed", + "type": "array" + }, + "summary": { + "$ref": "#/$defs/CapabilityLockDiffSummary" + } + }, + "required": [ + "base", + "head", + "summary" + ], + "title": "CapabilityLockDiffV1", + "type": "object" + }, + "CapabilityLockRef": { + "additionalProperties": false, + "properties": { + "capability_count": { + "title": "Capability Count", + "type": "integer" + }, + "capability_lock_schema_version": { + "title": "Capability Lock Schema Version", + "type": "string" + }, + "evidence_set_hash": { + "title": "Evidence Set Hash", + "type": "string" + }, + "path": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Path" + }, + "semantic_capability_set_hash": { + "title": "Semantic Capability Set Hash", + "type": "string" + }, + "source_set_hash": { + "title": "Source Set Hash", + "type": "string" + } + }, + "required": [ + "capability_lock_schema_version", + "semantic_capability_set_hash", + "evidence_set_hash", + "source_set_hash", + "capability_count" + ], + "title": "CapabilityLockRef", + "type": "object" + }, + "CapabilitySemanticChange": { + "additionalProperties": false, + "description": "One semantic explanation for a capability delta.", + "properties": { + "after": { + "default": null, + "title": "After" + }, + "before": { + "default": null, + "title": "Before" + }, + "field": { + "title": "Field", + "type": "string" + }, + "kind": { + "title": "Kind", + "type": "string" + }, + "rationale": { + "title": "Rationale", + "type": "string" + } + }, + "required": [ + "kind", + "field", + "rationale" + ], + "title": "CapabilitySemanticChange", + "type": "object" + }, + "EffectSemanticEvidence": { + "additionalProperties": false, + "properties": { + "claims": { + "items": { + "$ref": "#/$defs/SemanticClaimEvidence" + }, + "title": "Claims", + "type": "array" + }, + "confidence": { + "enum": [ + "low", + "medium", + "high" + ], + "title": "Confidence", + "type": "string" + }, + "issues": { + "items": { + "$ref": "#/$defs/SemanticIssueEvidence" + }, + "title": "Issues", + "type": "array" + }, + "status": { + "enum": [ + "declared", + "structural", + "protocol_default", + "inferred", + "unknown", + "conflicting" + ], + "title": "Status", + "type": "string" + } + }, + "required": [ + "status", + "confidence" + ], + "title": "EffectSemanticEvidence", + "type": "object" + }, + "SemanticClaimEvidence": { + "additionalProperties": false, + "properties": { + "basis": { + "default": "unknown", + "enum": [ + "reviewed_declaration", + "protocol_structure", + "typed_provider_fact", + "structural_scope", + "inferred_keyword", + "inferred_regex", + "protocol_default", + "unknown" + ], + "title": "Basis", + "type": "string" + }, + "claim_id": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Claim Id" + }, + "confidence": { + "enum": [ + "low", + "medium", + "high" + ], + "title": "Confidence", + "type": "string" + }, + "dimension": { + "enum": [ + "identity", + "binding", + "effect", + "authority" + ], + "title": "Dimension", + "type": "string" + }, + "evidence": { + "additionalProperties": true, + "title": "Evidence", + "type": "object" + }, + "policy_eligible": { + "default": false, + "title": "Policy Eligible", + "type": "boolean" + }, + "provenance_kind": { + "enum": [ + "static_declaration", + "ast_extraction", + "keyword_heuristic", + "regex_heuristic", + "policy_pack", + "runtime_trace" + ], + "title": "Provenance Kind", + "type": "string" + }, + "source": { + "title": "Source", + "type": "string" + }, + "source_pointer": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Source Pointer" + }, + "value": { + "title": "Value", + "type": "string" + } + }, + "required": [ + "dimension", + "value", + "confidence", + "provenance_kind", + "source" + ], + "title": "SemanticClaimEvidence", + "type": "object" + }, + "SemanticIssueEvidence": { + "additionalProperties": false, + "properties": { + "dimension": { + "enum": [ + "identity", + "binding", + "effect", + "authority" + ], + "title": "Dimension", + "type": "string" + }, + "kind": { + "enum": [ + "incomplete_tool_identity", + "conflicting_tool_identity", + "unresolved_tool_selector", + "ambiguous_tool_selector", + "ambiguous_legacy_tool_identity", + "invalid_tool_binding", + "incomplete_surface", + "missing_effect_evidence", + "inferred_effect_only", + "conflicting_effect_evidence", + "missing_authority_evidence", + "partial_authority_evidence", + "conflicting_authority_evidence", + "invalid_semantic_annotation", + "missing_binding_evidence", + "partial_binding_evidence", + "conflicting_binding_evidence", + "ambiguous_root_agent", + "unresolved_agent_binding", + "unresolved_bound_tool", + "incomplete_handoff_graph", + "invalid_binding_annotation", + "invalid_evidence_provenance" + ], + "title": "Kind", + "type": "string" + }, + "message": { + "title": "Message", + "type": "string" + }, + "source": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Source" + }, + "source_pointer": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Source Pointer" + } + }, + "required": [ + "kind", + "dimension", + "message" + ], + "title": "SemanticIssueEvidence", + "type": "object" + }, + "ToolIdentityEvidence": { + "additionalProperties": false, + "properties": { + "binding_id": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Binding Id" + }, + "claims": { + "items": { + "$ref": "#/$defs/SemanticClaimEvidence" + }, + "title": "Claims", + "type": "array" + }, + "issues": { + "items": { + "$ref": "#/$defs/SemanticIssueEvidence" + }, + "title": "Issues", + "type": "array" + }, + "observation_ids": { + "items": { + "type": "string" + }, + "title": "Observation Ids", + "type": "array" + }, + "pass_eligible": { + "title": "Pass Eligible", + "type": "boolean" + }, + "primary_observation_id": { + "title": "Primary Observation Id", + "type": "string" + }, + "provider": { + "title": "Provider", + "type": "string" + }, + "status": { + "enum": [ + "declared", + "structural", + "partial", + "unknown", + "conflicting" + ], + "title": "Status", + "type": "string" + }, + "tool_id": { + "title": "Tool Id", + "type": "string" + } + }, + "required": [ + "tool_id", + "status", + "provider", + "primary_observation_id", + "pass_eligible" + ], + "title": "ToolIdentityEvidence", + "type": "object" + }, + "ToolSemanticEvidence": { + "additionalProperties": false, + "description": "Public semantic assessment attached to action and capability facts.", + "properties": { + "authority": { + "$ref": "#/$defs/AuthoritySemanticEvidence" + }, + "binding": { + "$ref": "#/$defs/BindingSemanticEvidence" + }, + "conservative_effect": { + "enum": [ + "read", + "write", + "destructive", + "external_communication", + "financial_write", + "production_operation", + "privileged_data_access", + "code_execution", + "identity_access" + ], + "title": "Conservative Effect", + "type": "string" + }, + "effect": { + "$ref": "#/$defs/EffectSemanticEvidence" + }, + "identity": { + "anyOf": [ + { + "$ref": "#/$defs/ToolIdentityEvidence" + }, + { + "type": "null" + } + ], + "default": null + }, + "pass_eligible": { + "title": "Pass Eligible", + "type": "boolean" + } + }, + "required": [ + "conservative_effect", + "effect", + "authority", + "pass_eligible" + ], + "title": "ToolSemanticEvidence", + "type": "object" + } + }, + "$id": "https://raw.githubusercontent.com/ThreeMoonsLab/agents-shipgate/main/docs/capability-lock-diff-schema.v0.7.json", + "$ref": "#/$defs/CapabilityLockDiffV1", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "description": "Stable JSON Schema for semantic capability lock diff artifacts. Generated from agents_shipgate.schemas.capabilities. It is non-gating and is not part of report.json; release_decision.decision remains the only gate.", + "title": "Agents Shipgate Capability Lock Diff v0.7" +} diff --git a/docs/capability-lock-schema.v0.6.json b/docs/capability-lock-schema.v0.6.json new file mode 100644 index 00000000..39d6d7d4 --- /dev/null +++ b/docs/capability-lock-schema.v0.6.json @@ -0,0 +1,1256 @@ +{ + "$defs": { + "AuthoritySemanticEvidence": { + "additionalProperties": false, + "properties": { + "auth_type": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Auth Type" + }, + "claims": { + "items": { + "$ref": "#/$defs/SemanticClaimEvidence" + }, + "title": "Claims", + "type": "array" + }, + "credential_mode": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Credential Mode" + }, + "issues": { + "items": { + "$ref": "#/$defs/SemanticIssueEvidence" + }, + "title": "Issues", + "type": "array" + }, + "mode": { + "enum": [ + "none", + "scoped", + "unscoped", + "ambient", + "unknown" + ], + "title": "Mode", + "type": "string" + }, + "scopes": { + "items": { + "type": "string" + }, + "title": "Scopes", + "type": "array" + }, + "status": { + "enum": [ + "declared", + "structural", + "partial", + "unknown", + "conflicting" + ], + "title": "Status", + "type": "string" + } + }, + "required": [ + "status", + "mode" + ], + "title": "AuthoritySemanticEvidence", + "type": "object" + }, + "BindingSemanticEvidence": { + "additionalProperties": false, + "properties": { + "claims": { + "items": { + "$ref": "#/$defs/SemanticClaimEvidence" + }, + "title": "Claims", + "type": "array" + }, + "confidence": { + "enum": [ + "low", + "medium", + "high" + ], + "title": "Confidence", + "type": "string" + }, + "issues": { + "items": { + "$ref": "#/$defs/SemanticIssueEvidence" + }, + "title": "Issues", + "type": "array" + }, + "pass_eligible": { + "default": false, + "title": "Pass Eligible", + "type": "boolean" + }, + "reachable_path": { + "items": { + "type": "string" + }, + "title": "Reachable Path", + "type": "array" + }, + "root_agent_id": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Root Agent Id" + }, + "status": { + "enum": [ + "declared", + "structural", + "partial", + "unknown", + "conflicting" + ], + "title": "Status", + "type": "string" + } + }, + "required": [ + "status", + "confidence" + ], + "title": "BindingSemanticEvidence", + "type": "object" + }, + "CapabilityAuthority": { + "additionalProperties": false, + "description": "Authority source and scope facts for a capability.", + "properties": { + "auth_type": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Auth Type" + }, + "broad_scopes": { + "items": { + "type": "string" + }, + "title": "Broad Scopes", + "type": "array" + }, + "credential_mode": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Credential Mode" + }, + "scopes": { + "items": { + "type": "string" + }, + "title": "Scopes", + "type": "array" + }, + "source": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Source" + } + }, + "title": "CapabilityAuthority", + "type": "object" + }, + "CapabilityControls": { + "additionalProperties": false, + "description": "Policy and safeguard controls already known to the action surface.", + "properties": { + "approval_required": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Approval Required" + }, + "approval_threshold": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Approval Threshold" + }, + "confirmation_required": { + "default": false, + "title": "Confirmation Required", + "type": "boolean" + }, + "evidence_approval_ticket": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Evidence Approval Ticket" + }, + "evidence_owner": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Evidence Owner" + }, + "evidence_runbook": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Evidence Runbook" + }, + "safeguard_audit_log": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Safeguard Audit Log" + }, + "safeguard_dry_run": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Safeguard Dry Run" + }, + "safeguard_idempotency": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Safeguard Idempotency" + }, + "safeguard_rollback": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Safeguard Rollback" + } + }, + "title": "CapabilityControls", + "type": "object" + }, + "CapabilityEffect": { + "additionalProperties": false, + "description": "Normalized side-effect view derived from ``SideEffect``.", + "properties": { + "code_execution": { + "default": false, + "title": "Code Execution", + "type": "boolean" + }, + "effect": { + "enum": [ + "read", + "write", + "destructive", + "external_communication", + "financial_write", + "production_operation", + "privileged_data_access", + "code_execution", + "identity_access" + ], + "title": "Effect", + "type": "string" + }, + "externally_visible": { + "default": false, + "title": "Externally Visible", + "type": "boolean" + }, + "financial": { + "default": false, + "title": "Financial", + "type": "boolean" + }, + "handles_sensitive_data": { + "default": false, + "title": "Handles Sensitive Data", + "type": "boolean" + }, + "high_risk": { + "default": false, + "title": "High Risk", + "type": "boolean" + }, + "idempotency_known": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Idempotency Known" + }, + "reversibility": { + "default": "unknown", + "enum": [ + "reversible", + "irreversible", + "unknown" + ], + "title": "Reversibility", + "type": "string" + } + }, + "required": [ + "effect" + ], + "title": "CapabilityEffect", + "type": "object" + }, + "CapabilityEvidence": { + "additionalProperties": false, + "description": "Structured source provenance for a capability fact.", + "properties": { + "confidence": { + "default": "medium", + "enum": [ + "low", + "medium", + "high" + ], + "title": "Confidence", + "type": "string" + }, + "provenance_kind": { + "default": "static_declaration", + "enum": [ + "static_declaration", + "ast_extraction", + "keyword_heuristic", + "regex_heuristic", + "policy_pack" + ], + "title": "Provenance Kind", + "type": "string" + }, + "source_end_line": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Source End Line" + }, + "source_id": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Source Id" + }, + "source_location": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Source Location" + }, + "source_path": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Source Path" + }, + "source_pointer": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Source Pointer" + }, + "source_ref": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Source Ref" + }, + "source_start_column": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Source Start Column" + }, + "source_start_line": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Source Start Line" + }, + "source_type": { + "title": "Source Type", + "type": "string" + } + }, + "required": [ + "source_type" + ], + "title": "CapabilityEvidence", + "type": "object" + }, + "CapabilityFactV1": { + "additionalProperties": false, + "description": "Durable capability fact used by stable capability locks.", + "properties": { + "authority": { + "$ref": "#/$defs/CapabilityAuthority" + }, + "controls": { + "$ref": "#/$defs/CapabilityControls" + }, + "effect": { + "$ref": "#/$defs/CapabilityEffect" + }, + "evidence": { + "$ref": "#/$defs/CapabilityEvidence" + }, + "hashes": { + "$ref": "#/$defs/CapabilityHashes" + }, + "id": { + "title": "Id", + "type": "string" + }, + "identity": { + "$ref": "#/$defs/CapabilityIdentity" + }, + "risk_tags": { + "items": { + "type": "string" + }, + "title": "Risk Tags", + "type": "array" + }, + "semantic_assessment": { + "anyOf": [ + { + "$ref": "#/$defs/ToolSemanticEvidence" + }, + { + "type": "null" + } + ], + "default": null + } + }, + "required": [ + "id", + "identity", + "effect", + "authority", + "controls", + "evidence", + "hashes" + ], + "title": "CapabilityFactV1", + "type": "object" + }, + "CapabilityHashes": { + "additionalProperties": false, + "description": "Separate hashes for the future lock/diff substrate.", + "properties": { + "authority_hash": { + "title": "Authority Hash", + "type": "string" + }, + "binding_hash": { + "default": "legacy_binding_unknown", + "title": "Binding Hash", + "type": "string" + }, + "control_hash": { + "title": "Control Hash", + "type": "string" + }, + "effect_hash": { + "title": "Effect Hash", + "type": "string" + }, + "evidence_hash": { + "title": "Evidence Hash", + "type": "string" + }, + "identity_hash": { + "title": "Identity Hash", + "type": "string" + }, + "risk_hash": { + "title": "Risk Hash", + "type": "string" + }, + "schema_hash": { + "title": "Schema Hash", + "type": "string" + } + }, + "required": [ + "identity_hash", + "effect_hash", + "authority_hash", + "control_hash", + "schema_hash", + "risk_hash", + "evidence_hash" + ], + "title": "CapabilityHashes", + "type": "object" + }, + "CapabilityIdentity": { + "additionalProperties": false, + "description": "Stable semantic identity for an agent capability.", + "properties": { + "agent_id": { + "title": "Agent Id", + "type": "string" + }, + "operation": { + "title": "Operation", + "type": "string" + }, + "provider": { + "title": "Provider", + "type": "string" + }, + "resource": { + "items": { + "type": "string" + }, + "title": "Resource", + "type": "array" + }, + "scope": { + "items": { + "type": "string" + }, + "title": "Scope", + "type": "array" + }, + "subject_kind": { + "default": "action", + "enum": [ + "tool", + "action", + "scope", + "policy", + "ci", + "baseline", + "agent_instruction", + "manifest", + "unknown" + ], + "title": "Subject Kind", + "type": "string" + }, + "tool_id": { + "title": "Tool Id", + "type": "string" + }, + "tool_name": { + "title": "Tool Name", + "type": "string" + } + }, + "required": [ + "agent_id", + "tool_id", + "tool_name", + "provider", + "operation" + ], + "title": "CapabilityIdentity", + "type": "object" + }, + "CapabilityLockFileV1": { + "additionalProperties": false, + "properties": { + "capabilities": { + "items": { + "$ref": "#/$defs/CapabilityFactV1" + }, + "title": "Capabilities", + "type": "array" + }, + "capability_lock_schema_version": { + "const": "0.6", + "default": "0.6", + "title": "Capability Lock Schema Version", + "type": "string" + }, + "cli_version": { + "title": "Cli Version", + "type": "string" + }, + "experimental": { + "const": false, + "default": false, + "title": "Experimental", + "type": "boolean" + }, + "hashes": { + "$ref": "#/$defs/CapabilityLockHashes" + }, + "source": { + "$ref": "#/$defs/CapabilityLockSource" + }, + "summary": { + "$ref": "#/$defs/CapabilityLockSummary" + } + }, + "required": [ + "cli_version", + "source", + "summary", + "hashes" + ], + "title": "CapabilityLockFileV1", + "type": "object" + }, + "CapabilityLockHashes": { + "additionalProperties": false, + "properties": { + "evidence_set_hash": { + "title": "Evidence Set Hash", + "type": "string" + }, + "semantic_capability_set_hash": { + "title": "Semantic Capability Set Hash", + "type": "string" + }, + "source_set_hash": { + "title": "Source Set Hash", + "type": "string" + } + }, + "required": [ + "semantic_capability_set_hash", + "evidence_set_hash", + "source_set_hash" + ], + "title": "CapabilityLockHashes", + "type": "object" + }, + "CapabilityLockSource": { + "additionalProperties": false, + "properties": { + "agent_id": { + "title": "Agent Id", + "type": "string" + }, + "agent_name": { + "title": "Agent Name", + "type": "string" + }, + "config_path": { + "title": "Config Path", + "type": "string" + }, + "environment_target": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Environment Target" + }, + "manifest_dir": { + "title": "Manifest Dir", + "type": "string" + }, + "plugins_enabled": { + "default": true, + "title": "Plugins Enabled", + "type": "boolean" + }, + "project_name": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Project Name" + }, + "source_count": { + "default": 0, + "title": "Source Count", + "type": "integer" + }, + "source_warning_count": { + "default": 0, + "title": "Source Warning Count", + "type": "integer" + }, + "tool_count": { + "default": 0, + "title": "Tool Count", + "type": "integer" + }, + "toolkit_bound_count": { + "default": 0, + "title": "Toolkit Bound Count", + "type": "integer" + } + }, + "required": [ + "config_path", + "manifest_dir", + "agent_id", + "agent_name" + ], + "title": "CapabilityLockSource", + "type": "object" + }, + "CapabilityLockSummary": { + "additionalProperties": false, + "properties": { + "broad_scope_count": { + "default": 0, + "title": "Broad Scope Count", + "type": "integer" + }, + "capability_count": { + "default": 0, + "title": "Capability Count", + "type": "integer" + }, + "code_execution_count": { + "default": 0, + "title": "Code Execution Count", + "type": "integer" + }, + "external_communication_count": { + "default": 0, + "title": "External Communication Count", + "type": "integer" + }, + "financial_count": { + "default": 0, + "title": "Financial Count", + "type": "integer" + }, + "high_risk_count": { + "default": 0, + "title": "High Risk Count", + "type": "integer" + }, + "write_count": { + "default": 0, + "title": "Write Count", + "type": "integer" + } + }, + "title": "CapabilityLockSummary", + "type": "object" + }, + "EffectSemanticEvidence": { + "additionalProperties": false, + "properties": { + "claims": { + "items": { + "$ref": "#/$defs/SemanticClaimEvidence" + }, + "title": "Claims", + "type": "array" + }, + "confidence": { + "enum": [ + "low", + "medium", + "high" + ], + "title": "Confidence", + "type": "string" + }, + "issues": { + "items": { + "$ref": "#/$defs/SemanticIssueEvidence" + }, + "title": "Issues", + "type": "array" + }, + "status": { + "enum": [ + "declared", + "structural", + "protocol_default", + "inferred", + "unknown", + "conflicting" + ], + "title": "Status", + "type": "string" + } + }, + "required": [ + "status", + "confidence" + ], + "title": "EffectSemanticEvidence", + "type": "object" + }, + "SemanticClaimEvidence": { + "additionalProperties": false, + "properties": { + "basis": { + "default": "unknown", + "enum": [ + "reviewed_declaration", + "protocol_structure", + "typed_provider_fact", + "structural_scope", + "inferred_keyword", + "inferred_regex", + "protocol_default", + "unknown" + ], + "title": "Basis", + "type": "string" + }, + "claim_id": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Claim Id" + }, + "confidence": { + "enum": [ + "low", + "medium", + "high" + ], + "title": "Confidence", + "type": "string" + }, + "dimension": { + "enum": [ + "identity", + "binding", + "effect", + "authority" + ], + "title": "Dimension", + "type": "string" + }, + "evidence": { + "additionalProperties": true, + "title": "Evidence", + "type": "object" + }, + "policy_eligible": { + "default": false, + "title": "Policy Eligible", + "type": "boolean" + }, + "provenance_kind": { + "enum": [ + "static_declaration", + "ast_extraction", + "keyword_heuristic", + "regex_heuristic", + "policy_pack", + "runtime_trace" + ], + "title": "Provenance Kind", + "type": "string" + }, + "source": { + "title": "Source", + "type": "string" + }, + "source_pointer": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Source Pointer" + }, + "value": { + "title": "Value", + "type": "string" + } + }, + "required": [ + "dimension", + "value", + "confidence", + "provenance_kind", + "source" + ], + "title": "SemanticClaimEvidence", + "type": "object" + }, + "SemanticIssueEvidence": { + "additionalProperties": false, + "properties": { + "dimension": { + "enum": [ + "identity", + "binding", + "effect", + "authority" + ], + "title": "Dimension", + "type": "string" + }, + "kind": { + "enum": [ + "incomplete_tool_identity", + "conflicting_tool_identity", + "unresolved_tool_selector", + "ambiguous_tool_selector", + "ambiguous_legacy_tool_identity", + "invalid_tool_binding", + "incomplete_surface", + "missing_effect_evidence", + "inferred_effect_only", + "conflicting_effect_evidence", + "missing_authority_evidence", + "partial_authority_evidence", + "conflicting_authority_evidence", + "invalid_semantic_annotation", + "missing_binding_evidence", + "partial_binding_evidence", + "conflicting_binding_evidence", + "ambiguous_root_agent", + "unresolved_agent_binding", + "unresolved_bound_tool", + "incomplete_handoff_graph", + "invalid_binding_annotation", + "invalid_evidence_provenance" + ], + "title": "Kind", + "type": "string" + }, + "message": { + "title": "Message", + "type": "string" + }, + "source": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Source" + }, + "source_pointer": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Source Pointer" + } + }, + "required": [ + "kind", + "dimension", + "message" + ], + "title": "SemanticIssueEvidence", + "type": "object" + }, + "ToolIdentityEvidence": { + "additionalProperties": false, + "properties": { + "binding_id": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Binding Id" + }, + "claims": { + "items": { + "$ref": "#/$defs/SemanticClaimEvidence" + }, + "title": "Claims", + "type": "array" + }, + "issues": { + "items": { + "$ref": "#/$defs/SemanticIssueEvidence" + }, + "title": "Issues", + "type": "array" + }, + "observation_ids": { + "items": { + "type": "string" + }, + "title": "Observation Ids", + "type": "array" + }, + "pass_eligible": { + "title": "Pass Eligible", + "type": "boolean" + }, + "primary_observation_id": { + "title": "Primary Observation Id", + "type": "string" + }, + "provider": { + "title": "Provider", + "type": "string" + }, + "status": { + "enum": [ + "declared", + "structural", + "partial", + "unknown", + "conflicting" + ], + "title": "Status", + "type": "string" + }, + "tool_id": { + "title": "Tool Id", + "type": "string" + } + }, + "required": [ + "tool_id", + "status", + "provider", + "primary_observation_id", + "pass_eligible" + ], + "title": "ToolIdentityEvidence", + "type": "object" + }, + "ToolSemanticEvidence": { + "additionalProperties": false, + "description": "Public semantic assessment attached to action and capability facts.", + "properties": { + "authority": { + "$ref": "#/$defs/AuthoritySemanticEvidence" + }, + "binding": { + "$ref": "#/$defs/BindingSemanticEvidence" + }, + "conservative_effect": { + "enum": [ + "read", + "write", + "destructive", + "external_communication", + "financial_write", + "production_operation", + "privileged_data_access", + "code_execution", + "identity_access" + ], + "title": "Conservative Effect", + "type": "string" + }, + "effect": { + "$ref": "#/$defs/EffectSemanticEvidence" + }, + "identity": { + "anyOf": [ + { + "$ref": "#/$defs/ToolIdentityEvidence" + }, + { + "type": "null" + } + ], + "default": null + }, + "pass_eligible": { + "title": "Pass Eligible", + "type": "boolean" + } + }, + "required": [ + "conservative_effect", + "effect", + "authority", + "pass_eligible" + ], + "title": "ToolSemanticEvidence", + "type": "object" + } + }, + "$id": "https://raw.githubusercontent.com/ThreeMoonsLab/agents-shipgate/main/docs/capability-lock-schema.v0.6.json", + "$ref": "#/$defs/CapabilityLockFileV1", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "description": "Stable JSON Schema for static capability lock artifacts. Generated from agents_shipgate.schemas.capabilities. It is non-gating and is not part of report.json; release_decision.decision remains the only gate.", + "title": "Agents Shipgate Capability Lock v0.6" +} diff --git a/docs/capability-standard.md b/docs/capability-standard.md index 4e205ede..517024c9 100644 --- a/docs/capability-standard.md +++ b/docs/capability-standard.md @@ -11,9 +11,9 @@ they do not create a second verdict. ## Current Versions -- Capability standard version: `0.4` -- Capability lock schema: [`capability-lock-schema.v0.5.json`](capability-lock-schema.v0.5.json) -- Capability lock diff schema: [`capability-lock-diff-schema.v0.6.json`](capability-lock-diff-schema.v0.6.json) +- Capability standard version: `0.5` +- Capability lock schema: [`capability-lock-schema.v0.6.json`](capability-lock-schema.v0.6.json) +- Capability lock diff schema: [`capability-lock-diff-schema.v0.7.json`](capability-lock-diff-schema.v0.7.json) - Frozen lock reference: [`capability-lock-schema.v0.2.json`](capability-lock-schema.v0.2.json) - Frozen lock-diff reference: [`capability-lock-diff-schema.v0.3.json`](capability-lock-diff-schema.v0.3.json) - Frozen experimental lock reference: [`capability-lock-schema.v0.1.json`](capability-lock-schema.v0.1.json) @@ -77,8 +77,9 @@ Each fact has: broad scopes. - `controls` — approval, confirmation, safeguard, owner, and runbook signals. - `evidence` — static source provenance for the declaration or extraction. -- `semantic_assessment` (v0.3) — normalized identity, effect, and authority - claims, issues, conservative effect, and pass-eligibility state. Newly emitted v0.4 +- `semantic_assessment` (v0.3; typed evidence basis in v0.5) — normalized + identity, effect, and authority claims, issues, conservative effect, stable + claim IDs, policy eligibility, and pass-eligibility state. Newly emitted v0.6 locks populate it; it is optional only so older fact payloads remain readable. - `risk_tags` — compatibility metadata for existing policy and review surfaces. @@ -139,8 +140,8 @@ Changed rows carry `changed_hashes`, `semantic_direction`, and `semantic_changes` so tools can explain whether the delta is broadened, narrowed, mixed, unknown, or evidence-only. -See [`examples/capability-lock.v0.4.example.json`](examples/capability-lock.v0.4.example.json) -and [`examples/capability-lock-diff.v0.5.example.json`](examples/capability-lock-diff.v0.5.example.json). +See [`examples/capability-lock.v0.6.example.json`](examples/capability-lock.v0.6.example.json) +and [`examples/capability-lock-diff.v0.7.example.json`](examples/capability-lock-diff.v0.7.example.json). ## Provenance Boundaries @@ -155,14 +156,14 @@ static capability envelope. ## Compatibility -New exports use `capability_lock_schema_version: "0.5"` and +New exports use `capability_lock_schema_version: "0.6"` and `experimental: false`. `agents-shipgate capability diff` continues to accept old experimental `0.1` lock inputs and normalizes them before comparison. Diff metadata reports the normalized current lock schema version for such legacy inputs. -New diffs use `capability_lock_diff_schema_version: "0.6"` and -`experimental: false`. The v0.4 lock and v0.5 diff schemas remain frozen +New diffs use `capability_lock_diff_schema_version: "0.7"` and +`experimental: false`. The v0.5 lock and v0.6 diff schemas remain frozen references for archived artifacts; regenerate both sides with 0.16 before a current identity-aware semantic comparison. The older combined [`capability-lock-schema.v0.1.json`](capability-lock-schema.v0.1.json) diff --git a/docs/distribution.md b/docs/distribution.md index 75fe0e3b..a45634e6 100644 --- a/docs/distribution.md +++ b/docs/distribution.md @@ -38,7 +38,7 @@ exact wheel and signs `safety-qualification.json`: | Environment variable | Required value | |---|---| | `SAFETY_QUALIFICATION_WHEEL_URL` | HTTPS URL for the exact qualified wheel | -| `SAFETY_QUALIFICATION_WHEEL_FILENAME` | Safe wheel basename, for example `agents_shipgate-0.16.0b4-py3-none-any.whl` | +| `SAFETY_QUALIFICATION_WHEEL_FILENAME` | Safe wheel basename, for example `agents_shipgate-0.16.0b5-py3-none-any.whl` | | `SAFETY_QUALIFICATION_JSON_URL` | HTTPS URL for the production-qualified JSON artifact | | `SAFETY_QUALIFICATION_SIGSTORE_BUNDLE_URL` | HTTPS URL for that JSON artifact's Sigstore bundle | | `SAFETY_QUALIFICATION_SIGNER_IDENTITY` | Exact trusted certificate identity configured for qualification promotion | diff --git a/docs/examples.md b/docs/examples.md index f80ecf98..860e1503 100644 --- a/docs/examples.md +++ b/docs/examples.md @@ -69,9 +69,9 @@ The static scan fixtures write: - `agents-shipgate-reports/report.sarif` when requested or when using the GitHub Action The JSON output is the stable contract for tools and coding agents. See -[report-schema.v0.32.json](report-schema.v0.32.json) (current; emitted reports -carry `report_schema_version: "0.32"`, adding the Conductor OSS framework -summary over the root-reachable binding graph and retaining normalized semantic assessments +[report-schema.v0.33.json](report-schema.v0.33.json) (current; emitted reports +carry `report_schema_version: "0.33"`, adding typed policy-evidence support +over the root-reachable binding graph and retaining normalized semantic assessments and evidence-backed pass coverage while preserving policy-pack routing metadata -in `findings[].policy_routing`; v0.31 is frozen at -[report-schema.v0.31.json](report-schema.v0.31.json)). +in `findings[].policy_routing`; v0.32 is frozen at +[report-schema.v0.32.json](report-schema.v0.32.json)). diff --git a/docs/examples/capability-lock-diff.v0.7.example.json b/docs/examples/capability-lock-diff.v0.7.example.json new file mode 100644 index 00000000..a4ba0b81 --- /dev/null +++ b/docs/examples/capability-lock-diff.v0.7.example.json @@ -0,0 +1,155 @@ +{ + "capability_lock_diff_schema_version": "0.7", + "experimental": false, + "base": { + "path": ".agents-shipgate/capabilities.lock.json", + "capability_lock_schema_version": "0.6", + "semantic_capability_set_hash": "2222222222222222222222222222222222222222222222222222222222222222", + "evidence_set_hash": "3333333333333333333333333333333333333333333333333333333333333333", + "source_set_hash": "4444444444444444444444444444444444444444444444444444444444444444", + "capability_count": 1 + }, + "head": { + "path": "agents-shipgate-reports/capabilities.lock.json", + "capability_lock_schema_version": "0.6", + "semantic_capability_set_hash": "5555555555555555555555555555555555555555555555555555555555555555", + "evidence_set_hash": "6666666666666666666666666666666666666666666666666666666666666666", + "source_set_hash": "4444444444444444444444444444444444444444444444444444444444444444", + "capability_count": 2 + }, + "summary": { + "added": 1, + "removed": 0, + "reidentified": 0, + "changed": 0, + "evidence_changed": 0, + "unchanged": 1 + }, + "added": [ + { + "id": "cap_999999999999", + "identity": { + "agent_id": "agent:support", + "tool_id": "tool:refunds.create", + "tool_name": "refunds.create", + "provider": "openapi", + "operation": "financial_write", + "subject_kind": "action", + "resource": [ + "refund" + ], + "scope": [ + "refunds:write" + ] + }, + "effect": { + "effect": "financial_write", + "externally_visible": true, + "handles_sensitive_data": true, + "financial": true, + "code_execution": false, + "reversibility": "irreversible", + "idempotency_known": false, + "high_risk": true + }, + "authority": { + "auth_type": "oauth2", + "credential_mode": "delegated", + "source": "manifest", + "scopes": [ + "refunds:write" + ], + "broad_scopes": [] + }, + "controls": { + "approval_required": true, + "approval_threshold": "security", + "confirmation_required": true, + "safeguard_idempotency": true, + "safeguard_audit_log": true, + "safeguard_rollback": false, + "safeguard_dry_run": null, + "evidence_owner": "payments-platform", + "evidence_runbook": "runbooks/refunds.md", + "evidence_approval_ticket": null + }, + "evidence": { + "source_type": "openapi", + "source_id": "support_api", + "source_ref": "tools/support.openapi.yaml", + "source_location": null, + "source_path": "tools/support.openapi.yaml", + "source_start_line": 88, + "source_end_line": null, + "source_start_column": null, + "source_pointer": "/paths/~1refunds/post", + "provenance_kind": "static_declaration", + "confidence": "high" + }, + "semantic_assessment": { + "conservative_effect": "financial_write", + "effect": { + "status": "declared", + "confidence": "high", + "claims": [ + { + "dimension": "effect", + "value": "financial_write", + "confidence": "high", + "provenance_kind": "static_declaration", + "source": "manifest.action_surface", + "source_pointer": "/action_surface/actions/0/effect", + "evidence": { + "effect": "financial_write" + } + } + ], + "issues": [] + }, + "authority": { + "status": "declared", + "mode": "scoped", + "auth_type": "oauth2", + "credential_mode": "delegated", + "scopes": [ + "refunds:write" + ], + "claims": [ + { + "dimension": "authority", + "value": "scoped", + "confidence": "high", + "provenance_kind": "static_declaration", + "source": "manifest.action_surface", + "source_pointer": "/action_surface/actions/0/authority", + "evidence": { + "mode": "scoped" + } + } + ], + "issues": [] + }, + "pass_eligible": true + }, + "risk_tags": [ + "financial_action", + "high_impact", + "write" + ], + "hashes": { + "identity_hash": "9999999999999999999999999999999999999999999999999999999999999999", + "binding_hash": "abababababababababababababababababababababababababababababababab", + "effect_hash": "8888888888888888888888888888888888888888888888888888888888888888", + "authority_hash": "7777777777777777777777777777777777777777777777777777777777777777", + "control_hash": "6666666666666666666666666666666666666666666666666666666666666666", + "schema_hash": "5555555555555555555555555555555555555555555555555555555555555555", + "risk_hash": "4444444444444444444444444444444444444444444444444444444444444444", + "evidence_hash": "3333333333333333333333333333333333333333333333333333333333333333" + } + } + ], + "removed": [], + "reidentified": [], + "changed": [], + "evidence_changed": [] +} diff --git a/docs/examples/capability-lock.v0.6.example.json b/docs/examples/capability-lock.v0.6.example.json new file mode 100644 index 00000000..95723880 --- /dev/null +++ b/docs/examples/capability-lock.v0.6.example.json @@ -0,0 +1,153 @@ +{ + "capability_lock_schema_version": "0.6", + "experimental": false, + "cli_version": "0.16.0b5", + "source": { + "config_path": "shipgate.yaml", + "manifest_dir": ".", + "project_name": "support-agent", + "agent_id": "agent:support", + "agent_name": "Support Agent", + "environment_target": "production", + "tool_count": 1, + "toolkit_bound_count": 0, + "source_count": 1, + "source_warning_count": 0, + "plugins_enabled": true + }, + "summary": { + "capability_count": 1, + "high_risk_count": 0, + "broad_scope_count": 0, + "write_count": 0, + "external_communication_count": 0, + "financial_count": 0, + "code_execution_count": 0 + }, + "hashes": { + "semantic_capability_set_hash": "2222222222222222222222222222222222222222222222222222222222222222", + "evidence_set_hash": "3333333333333333333333333333333333333333333333333333333333333333", + "source_set_hash": "4444444444444444444444444444444444444444444444444444444444444444" + }, + "capabilities": [ + { + "id": "cap_aaaaaaaaaaaa", + "identity": { + "agent_id": "agent:support", + "tool_id": "tool:cases.search", + "tool_name": "cases.search", + "provider": "openapi", + "operation": "read", + "subject_kind": "action", + "resource": [ + "case" + ], + "scope": [ + "cases:read" + ] + }, + "effect": { + "effect": "read", + "externally_visible": false, + "handles_sensitive_data": false, + "financial": false, + "code_execution": false, + "reversibility": "reversible", + "idempotency_known": true, + "high_risk": false + }, + "authority": { + "auth_type": "oauth2", + "credential_mode": "delegated", + "source": "openapi", + "scopes": [ + "cases:read" + ], + "broad_scopes": [] + }, + "controls": { + "approval_required": false, + "approval_threshold": null, + "confirmation_required": false, + "safeguard_idempotency": true, + "safeguard_audit_log": true, + "safeguard_rollback": null, + "safeguard_dry_run": null, + "evidence_owner": "support-platform", + "evidence_runbook": "runbooks/cases.md", + "evidence_approval_ticket": null + }, + "evidence": { + "source_type": "openapi", + "source_id": "support_api", + "source_ref": "tools/support.openapi.yaml", + "source_location": null, + "source_path": "tools/support.openapi.yaml", + "source_start_line": 42, + "source_end_line": null, + "source_start_column": null, + "source_pointer": "/paths/~1cases/get", + "provenance_kind": "static_declaration", + "confidence": "high" + }, + "semantic_assessment": { + "conservative_effect": "read", + "effect": { + "status": "structural", + "confidence": "high", + "claims": [ + { + "dimension": "effect", + "value": "read", + "confidence": "high", + "provenance_kind": "static_declaration", + "source": "openapi.method", + "source_pointer": "/paths/~1cases/get", + "evidence": { + "method": "GET" + } + } + ], + "issues": [] + }, + "authority": { + "status": "structural", + "mode": "scoped", + "auth_type": "oauth2", + "credential_mode": "delegated", + "scopes": [ + "cases:read" + ], + "claims": [ + { + "dimension": "authority", + "value": "scoped", + "confidence": "high", + "provenance_kind": "static_declaration", + "source": "openapi.security", + "source_pointer": "/paths/~1cases/get/security", + "evidence": { + "scopes": [ + "cases:read" + ] + } + } + ], + "issues": [] + }, + "pass_eligible": true + }, + "risk_tags": [], + "hashes": { + "identity_hash": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "binding_hash": "abababababababababababababababababababababababababababababababab", + "effect_hash": "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", + "authority_hash": "cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc", + "control_hash": "dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd", + "schema_hash": "eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee", + "risk_hash": "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "evidence_hash": "1111111111111111111111111111111111111111111111111111111111111111" + } + } + ] +} diff --git a/docs/faq.md b/docs/faq.md index 8ef92257..aadd0ee1 100644 --- a/docs/faq.md +++ b/docs/faq.md @@ -104,7 +104,7 @@ schema. - **Markdown** — `agents-shipgate-reports/report.md`, for human review. - **JSON** — `agents-shipgate-reports/report.json`, machine-readable - (schema v0.32, current). Always parse this for programmatic use. + (schema v0.33, current). Always parse this for programmatic use. For release gating, read `release_decision.decision`; the legacy `summary.status` field is baseline-blind (kept for v0.7 callers). A `passed` decision requires complete, conflict-free static surface, effect, @@ -120,14 +120,14 @@ schema. ## What is the Release Evidence Packet? A reviewer-shaped synthesis of the scan, emitted alongside the report by -default. The packet is governed by [`docs/packet-schema.v0.10.json`](packet-schema.v0.10.json) +default. The packet is governed by [`docs/packet-schema.v0.11.json`](packet-schema.v0.11.json) and has fixed reviewer sections (release decision, evidence matrix, capability/intent, high-risk surface, tool-surface diff, action-surface diff, approval coverage, idempotency risk, scope coverage, memory isolation, human-in-the-loop, dynamic scenarios, and a `not_proven` section that always lists prompt robustness, runtime behavior, model correctness, and adversarial resistance verbatim). See -[STABILITY.md §Release Evidence Packet](../STABILITY.md#release-evidence-packet-v010). +[STABILITY.md §Release Evidence Packet](../STABILITY.md#release-evidence-packet-v011). Packet schema `0.9` adds semantic coverage and gap remediation, and packet §1 mirrors the report's `static_analysis_only: true`, `runtime_behavior_verified: false`, and canonical @@ -148,8 +148,10 @@ Skip emission with `--no-packet`; re-render later with ## Is it production-ready? v0.15.0 is the latest published pre-1.0 beta. The in-tree runtime is -`0.16.0b4`, which adds one schema-enforced multi-host boundary state on top of root-reachable binding proof for beta -qualification. The manifest schema remains stable across the 0.x series; see +`0.16.0b5`, which prevents heuristic evidence from being upgraded into +authoritative policy findings on top of the multi-host boundary and +root-reachable binding contracts for beta qualification. The manifest schema +remains stable across the 0.x series; see [`STABILITY.md`](../STABILITY.md). Public preview. ## How do I add it to GitHub Actions? diff --git a/docs/mcp-server.md b/docs/mcp-server.md index 577a2660..db0200ea 100644 --- a/docs/mcp-server.md +++ b/docs/mcp-server.md @@ -31,7 +31,7 @@ Claude Code registration (`.mcp.json`): | `shipgate.preflight` | `{workspace?, config?, plan?, changed_files?, diff_text?, capability_request?, base_preflight?}` | exact `PreflightResultV3` | | `shipgate.explain` | `{check_id}` or `{fingerprint, report_path}` | deterministic check/finding explanation JSON | | `shipgate.capabilities` | `{config}` or `{base_lock, head_lock}` | capability lock or capability lock diff JSON | -| `shipgate.handoff` | `{verifier_path, report_path?, verify_run_path?}` | exact `shipgate.agent_handoff/v3` | +| `shipgate.handoff` | `{verifier_path, report_path?, verify_run_path?}` | exact `shipgate.agent_handoff/v4` | `shipgate.check` is the same protocol surface documented in [`agents/protocol.md`](agents/protocol.md). `shipgate.preflight` is proactive @@ -43,7 +43,7 @@ but it is not a second release verdict. The release gate remains `shipgate.handoff` is a read-only projection over existing verifier artifacts. It never runs `verify`, shells out to git, or writes `agent-handoff.json`; it -returns the same `shipgate.agent_handoff/v3` shape that `verify` writes for +returns the same `shipgate.agent_handoff/v4` shape that `verify` writes for agents that need a compact control/release-readiness object. ## Trust model diff --git a/docs/overview.md b/docs/overview.md index f526cebe..e295b2b4 100644 --- a/docs/overview.md +++ b/docs/overview.md @@ -45,7 +45,7 @@ surface before production-like permissions are granted. - [Concepts](concepts.md) - [Manifest v0.1](manifest-v0.1.md) - [Check catalog](checks.md) -- [Report schema v0.32](report-schema.v0.32.json) (current; v0.31 frozen at [report-schema.v0.31.json](report-schema.v0.31.json), v0.30 frozen at [report-schema.v0.30.json](report-schema.v0.30.json)) +- [Report schema v0.33](report-schema.v0.33.json) (current; v0.32 frozen at [report-schema.v0.32.json](report-schema.v0.32.json), v0.31 frozen at [report-schema.v0.31.json](report-schema.v0.31.json)) - [Evidence-backed `passed` contract](passed-verdict-contract.md) - [Trust model](trust-model.md) - [Agent instructions](../AGENTS.md) diff --git a/docs/packet-schema.v0.11.json b/docs/packet-schema.v0.11.json new file mode 100644 index 00000000..3f9399ed --- /dev/null +++ b/docs/packet-schema.v0.11.json @@ -0,0 +1,2174 @@ +{ + "$defs": { + "ActionSurfaceDiffSection": { + "additionalProperties": false, + "description": "\u00a73B \u2014 compact action-surface diff for reviewers.", + "properties": { + "base_kind": { + "default": "none", + "title": "Base Kind", + "type": "string" + }, + "blocking_reasons": { + "items": { + "type": "string" + }, + "title": "Blocking Reasons", + "type": "array" + }, + "enabled": { + "default": false, + "title": "Enabled", + "type": "boolean" + }, + "highlights": { + "items": { + "type": "string" + }, + "title": "Highlights", + "type": "array" + }, + "notes": { + "items": { + "type": "string" + }, + "title": "Notes", + "type": "array" + }, + "status": { + "enum": [ + "covered", + "partial", + "not_declared", + "missing", + "informational" + ], + "title": "Status", + "type": "string" + }, + "summary": { + "$ref": "#/$defs/ActionSurfaceDiffSummary" + } + }, + "required": [ + "status" + ], + "title": "ActionSurfaceDiffSection", + "type": "object" + }, + "ActionSurfaceDiffSummary": { + "additionalProperties": false, + "properties": { + "actions_added": { + "default": 0, + "title": "Actions Added", + "type": "integer" + }, + "actions_modified": { + "default": 0, + "title": "Actions Modified", + "type": "integer" + }, + "actions_removed": { + "default": 0, + "title": "Actions Removed", + "type": "integer" + }, + "approvals_removed": { + "default": 0, + "title": "Approvals Removed", + "type": "integer" + }, + "blocking_findings": { + "default": 0, + "title": "Blocking Findings", + "type": "integer" + }, + "effect_escalations": { + "default": 0, + "title": "Effect Escalations", + "type": "integer" + }, + "input_schema_expansions": { + "default": 0, + "title": "Input Schema Expansions", + "type": "integer" + }, + "risk_tags_added": { + "default": 0, + "title": "Risk Tags Added", + "type": "integer" + }, + "safeguards_removed": { + "default": 0, + "title": "Safeguards Removed", + "type": "integer" + }, + "scope_expansions": { + "default": 0, + "title": "Scope Expansions", + "type": "integer" + } + }, + "title": "ActionSurfaceDiffSummary", + "type": "object" + }, + "ApprovalCoverageRow": { + "additionalProperties": false, + "properties": { + "declared": { + "title": "Declared", + "type": "boolean" + }, + "gap_finding_ids": { + "items": { + "type": "string" + }, + "title": "Gap Finding Ids", + "type": "array" + }, + "provider": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Provider" + }, + "source": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Source" + }, + "tool": { + "title": "Tool", + "type": "string" + }, + "tool_id": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Tool Id" + } + }, + "required": [ + "tool", + "declared" + ], + "title": "ApprovalCoverageRow", + "type": "object" + }, + "ApprovalCoverageSection": { + "additionalProperties": false, + "description": "\u00a74 \u2014 approval policy coverage.", + "properties": { + "gap_findings": { + "items": { + "$ref": "#/$defs/ReleaseDecisionItem" + }, + "title": "Gap Findings", + "type": "array" + }, + "rows": { + "items": { + "$ref": "#/$defs/ApprovalCoverageRow" + }, + "title": "Rows", + "type": "array" + }, + "status": { + "enum": [ + "covered", + "partial", + "not_declared", + "missing", + "informational" + ], + "title": "Status", + "type": "string" + } + }, + "required": [ + "status" + ], + "title": "ApprovalCoverageSection", + "type": "object" + }, + "BaselineDelta": { + "properties": { + "enabled": { + "title": "Enabled", + "type": "boolean" + }, + "matched_count": { + "default": 0, + "title": "Matched Count", + "type": "integer" + }, + "new_count": { + "default": 0, + "title": "New Count", + "type": "integer" + }, + "path": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Path" + }, + "resolved_count": { + "default": 0, + "title": "Resolved Count", + "type": "integer" + } + }, + "required": [ + "enabled" + ], + "title": "BaselineDelta", + "type": "object" + }, + "BindingCoverageDecision": { + "properties": { + "gap_count": { + "default": 0, + "title": "Gap Count", + "type": "integer" + }, + "pass_eligible": { + "default": false, + "title": "Pass Eligible", + "type": "boolean" + }, + "possible_tools": { + "default": 0, + "title": "Possible Tools", + "type": "integer" + }, + "reachable_tools": { + "default": 0, + "title": "Reachable Tools", + "type": "integer" + }, + "reason_counts": { + "additionalProperties": { + "type": "integer" + }, + "title": "Reason Counts", + "type": "object" + }, + "total_catalog_tools": { + "default": 0, + "title": "Total Catalog Tools", + "type": "integer" + }, + "unbound_tools": { + "default": 0, + "title": "Unbound Tools", + "type": "integer" + } + }, + "title": "BindingCoverageDecision", + "type": "object" + }, + "CapabilityIntentDiff": { + "additionalProperties": false, + "description": "\u00a72 \u2014 capability \u2194 intent diff.", + "properties": { + "declared_purpose": { + "items": { + "type": "string" + }, + "title": "Declared Purpose", + "type": "array" + }, + "divergence_findings": { + "items": { + "$ref": "#/$defs/ReleaseDecisionItem" + }, + "title": "Divergence Findings", + "type": "array" + }, + "observed_tool_ids": { + "items": { + "type": "string" + }, + "title": "Observed Tool Ids", + "type": "array" + }, + "observed_tools": { + "items": { + "type": "string" + }, + "title": "Observed Tools", + "type": "array" + }, + "prohibited_actions": { + "items": { + "type": "string" + }, + "title": "Prohibited Actions", + "type": "array" + }, + "rows": { + "items": { + "$ref": "#/$defs/CapabilityIntentRow" + }, + "title": "Rows", + "type": "array" + }, + "status": { + "enum": [ + "covered", + "partial", + "not_declared", + "missing", + "informational" + ], + "title": "Status", + "type": "string" + } + }, + "required": [ + "status" + ], + "title": "CapabilityIntentDiff", + "type": "object" + }, + "CapabilityIntentRow": { + "additionalProperties": false, + "description": "One declared/observed pair in the \u00a72 capability \u2194 intent diff.", + "properties": { + "declared": { + "items": { + "type": "string" + }, + "title": "Declared", + "type": "array" + }, + "divergent": { + "items": { + "type": "string" + }, + "title": "Divergent", + "type": "array" + }, + "label": { + "title": "Label", + "type": "string" + }, + "observed": { + "items": { + "type": "string" + }, + "title": "Observed", + "type": "array" + } + }, + "required": [ + "label" + ], + "title": "CapabilityIntentRow", + "type": "object" + }, + "CapabilityTraceEvidenceSummary": { + "additionalProperties": false, + "description": "Deterministic counts for opt-in local runtime trace evidence.", + "properties": { + "agent_trace_count": { + "default": 0, + "title": "Agent Trace Count", + "type": "integer" + }, + "api_trace_count": { + "default": 0, + "title": "Api Trace Count", + "type": "integer" + }, + "approval_trace_count": { + "default": 0, + "title": "Approval Trace Count", + "type": "integer" + }, + "matched_trace_count": { + "default": 0, + "title": "Matched Trace Count", + "type": "integer" + }, + "source_count": { + "default": 0, + "title": "Source Count", + "type": "integer" + }, + "trace_count": { + "default": 0, + "title": "Trace Count", + "type": "integer" + }, + "unmatched_trace_count": { + "default": 0, + "title": "Unmatched Trace Count", + "type": "integer" + }, + "warning_count": { + "default": 0, + "title": "Warning Count", + "type": "integer" + } + }, + "title": "CapabilityTraceEvidenceSummary", + "type": "object" + }, + "DynamicScenarioRequirement": { + "additionalProperties": false, + "properties": { + "finding_ids": { + "items": { + "type": "string" + }, + "title": "Finding Ids", + "type": "array" + }, + "scenario": { + "title": "Scenario", + "type": "string" + }, + "why": { + "title": "Why", + "type": "string" + } + }, + "required": [ + "scenario", + "why" + ], + "title": "DynamicScenarioRequirement", + "type": "object" + }, + "DynamicScenariosSection": { + "additionalProperties": false, + "description": "\u00a79 \u2014 required dynamic scenarios.", + "properties": { + "scenarios": { + "items": { + "$ref": "#/$defs/DynamicScenarioRequirement" + }, + "title": "Scenarios", + "type": "array" + }, + "status": { + "enum": [ + "covered", + "partial", + "not_declared", + "missing", + "informational" + ], + "title": "Status", + "type": "string" + } + }, + "required": [ + "status" + ], + "title": "DynamicScenariosSection", + "type": "object" + }, + "EvidenceCoverageDecision": { + "properties": { + "binding_coverage": { + "$ref": "#/$defs/BindingCoverageDecision" + }, + "evidence_gaps": { + "items": { + "$ref": "#/$defs/EvidenceGap" + }, + "title": "Evidence Gaps", + "type": "array" + }, + "human_review_recommended": { + "title": "Human Review Recommended", + "type": "boolean" + }, + "identity_coverage": { + "$ref": "#/$defs/IdentityCoverageDecision" + }, + "level": { + "title": "Level", + "type": "string" + }, + "low_confidence_tool_count": { + "title": "Low Confidence Tool Count", + "type": "integer" + }, + "policy_gap_count": { + "default": 0, + "title": "Policy Gap Count", + "type": "integer" + }, + "semantic_coverage": { + "$ref": "#/$defs/SemanticCoverageDecision" + }, + "source_warning_count": { + "title": "Source Warning Count", + "type": "integer" + } + }, + "required": [ + "level", + "human_review_recommended", + "source_warning_count", + "low_confidence_tool_count" + ], + "title": "EvidenceCoverageDecision", + "type": "object" + }, + "EvidenceGap": { + "description": "v0.26: one structured row per measurable evidence gap.\n\n``insufficient_evidence`` previously diagnosed without prescribing;\neach gap names the degraded subject and the specific next action\nthat raises extraction confidence. Purely explanatory \u2014 gating\nstill uses only the counts (the gap list is a projection of them).", + "properties": { + "kind": { + "enum": [ + "low_confidence_tool", + "source_warning", + "incomplete_surface", + "missing_effect_evidence", + "inferred_effect_only", + "conflicting_effect_evidence", + "missing_authority_evidence", + "partial_authority_evidence", + "conflicting_authority_evidence", + "invalid_semantic_annotation", + "incomplete_tool_identity", + "conflicting_tool_identity", + "unresolved_tool_selector", + "ambiguous_tool_selector", + "ambiguous_legacy_tool_identity", + "invalid_tool_binding", + "missing_binding_evidence", + "partial_binding_evidence", + "conflicting_binding_evidence", + "ambiguous_root_agent", + "unresolved_agent_binding", + "unresolved_bound_tool", + "incomplete_handoff_graph", + "invalid_binding_annotation", + "invalid_evidence_provenance", + "inferred_policy_applicability", + "mixed_policy_evidence", + "unknown_policy_evidence", + "conflicting_policy_evidence" + ], + "title": "Kind", + "type": "string" + }, + "next_action": { + "$ref": "#/$defs/EvidenceGapAction" + }, + "source_ref": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Source Ref" + }, + "source_type": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Source Type" + }, + "subject": { + "title": "Subject", + "type": "string" + }, + "why": { + "title": "Why", + "type": "string" + } + }, + "required": [ + "kind", + "subject", + "why", + "next_action" + ], + "title": "EvidenceGap", + "type": "object" + }, + "EvidenceGapAction": { + "description": "One concrete, mechanically-executable step that closes a gap.\n\nMirrors the agent-mode ``next_actions[]`` error shape\n(``kind``/``command``/``path``/``why``/``expects``) so agents reuse\none routing vocabulary across error recovery and evidence repair.", + "properties": { + "accepted_values": { + "items": { + "type": "string" + }, + "title": "Accepted Values", + "type": "array" + }, + "auto_apply": { + "const": false, + "default": false, + "title": "Auto Apply", + "type": "boolean" + }, + "command": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Command" + }, + "declaration_template": { + "anyOf": [ + { + "additionalProperties": true, + "type": "object" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Declaration Template" + }, + "expects": { + "title": "Expects", + "type": "string" + }, + "kind": { + "enum": [ + "declare_tool_inventory", + "provide_source", + "review_warning", + "declare_action_effect", + "declare_action_authority", + "provide_complete_inventory", + "resolve_semantic_conflict", + "declare_source_identity", + "qualify_tool_selector", + "provide_tool_binding", + "resolve_tool_identity_conflict", + "regenerate_identity_artifact", + "declare_agent_root", + "declare_agent_bindings", + "provide_static_binding_source", + "provide_complete_binding_graph", + "resolve_binding_conflict", + "regenerate_binding_artifact", + "provide_policy_evidence", + "review_policy_evidence", + "resolve_policy_evidence_conflict" + ], + "title": "Kind", + "type": "string" + }, + "path": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Path" + }, + "requires_human_review": { + "const": true, + "default": true, + "title": "Requires Human Review", + "type": "boolean" + }, + "suggested_patch_kind": { + "const": "manual", + "default": "manual", + "title": "Suggested Patch Kind", + "type": "string" + }, + "why": { + "title": "Why", + "type": "string" + } + }, + "required": [ + "kind", + "why", + "expects" + ], + "title": "EvidenceGapAction", + "type": "object" + }, + "EvidenceMatrixRow": { + "additionalProperties": false, + "description": "One compact row in the packet-only evidence matrix.\n\nThe row is a derived review aid only. ``blocking_findings`` and\n``review_items`` are subsets copied from ``release_decision``; the\nmatrix never reclassifies findings or contributes to gate behavior.", + "properties": { + "blocking_findings": { + "items": { + "$ref": "#/$defs/ReleaseDecisionItem" + }, + "title": "Blocking Findings", + "type": "array" + }, + "confidence": { + "default": "unknown", + "enum": [ + "high", + "medium", + "low", + "mixed", + "unknown" + ], + "title": "Confidence", + "type": "string" + }, + "domain": { + "enum": [ + "Inventory", + "Schema", + "Auth", + "Approval", + "Confirmation", + "Idempotency", + "Side effects", + "Memory isolation", + "Human-in-the-loop evidence", + "Prompt/scope alignment", + "Retry/timeout", + "Baseline debt", + "Action-surface policy" + ], + "title": "Domain", + "type": "string" + }, + "evidence_present": { + "enum": [ + "covered", + "partial", + "not_declared", + "missing", + "informational" + ], + "title": "Evidence Present", + "type": "string" + }, + "evidence_source": { + "items": { + "type": "string" + }, + "title": "Evidence Source", + "type": "array" + }, + "missing_controls": { + "items": { + "type": "string" + }, + "title": "Missing Controls", + "type": "array" + }, + "review_items": { + "items": { + "$ref": "#/$defs/ReleaseDecisionItem" + }, + "title": "Review Items", + "type": "array" + } + }, + "required": [ + "domain", + "evidence_present" + ], + "title": "EvidenceMatrixRow", + "type": "object" + }, + "EvidenceMatrixSection": { + "additionalProperties": false, + "description": "Compact packet-only review matrix derived from public report JSON.", + "properties": { + "notes": { + "items": { + "type": "string" + }, + "title": "Notes", + "type": "array" + }, + "rows": { + "items": { + "$ref": "#/$defs/EvidenceMatrixRow" + }, + "title": "Rows", + "type": "array" + } + }, + "title": "EvidenceMatrixSection", + "type": "object" + }, + "FailPolicy": { + "properties": { + "ci_mode": { + "title": "Ci Mode", + "type": "string" + }, + "exit_code": { + "title": "Exit Code", + "type": "integer" + }, + "fail_on": { + "items": { + "enum": [ + "info", + "low", + "medium", + "high", + "critical" + ], + "type": "string" + }, + "title": "Fail On", + "type": "array" + }, + "new_findings_only": { + "default": false, + "title": "New Findings Only", + "type": "boolean" + }, + "would_fail_ci": { + "title": "Would Fail Ci", + "type": "boolean" + } + }, + "required": [ + "ci_mode", + "would_fail_ci", + "exit_code" + ], + "title": "FailPolicy", + "type": "object" + }, + "FindingSupport": { + "additionalProperties": false, + "description": "Authoritative support for finding confidence and release contribution.\n\nRule metadata may request a severity or block, but it cannot upgrade the\nunderlying evidence. ``support_hash`` binds baselines and audit surfaces\nto the predicate evidence that actually made the finding eligible.", + "properties": { + "blocking_eligible": { + "default": false, + "title": "Blocking Eligible", + "type": "boolean" + }, + "claim_ids": { + "items": { + "type": "string" + }, + "title": "Claim Ids", + "type": "array" + }, + "confidence": { + "default": "low", + "enum": [ + "low", + "medium", + "high" + ], + "title": "Confidence", + "type": "string" + }, + "evidence_bases": { + "items": { + "enum": [ + "reviewed_declaration", + "protocol_structure", + "typed_provider_fact", + "structural_scope", + "inferred_keyword", + "inferred_regex", + "protocol_default", + "unknown" + ], + "type": "string" + }, + "title": "Evidence Bases", + "type": "array" + }, + "policy_eligible": { + "default": false, + "title": "Policy Eligible", + "type": "boolean" + }, + "predicates": { + "items": { + "$ref": "#/$defs/PolicyPredicateEvidence" + }, + "title": "Predicates", + "type": "array" + }, + "status": { + "default": "matched", + "enum": [ + "matched", + "not_matched", + "indeterminate", + "conflicting" + ], + "title": "Status", + "type": "string" + }, + "support_hash": { + "title": "Support Hash", + "type": "string" + } + }, + "required": [ + "support_hash" + ], + "title": "FindingSupport", + "type": "object" + }, + "HighRiskSurfaceSection": { + "additionalProperties": false, + "description": "\u00a73 \u2014 high-risk tool surface.", + "properties": { + "high_risk_count": { + "default": 0, + "title": "High Risk Count", + "type": "integer" + }, + "status": { + "enum": [ + "covered", + "partial", + "not_declared", + "missing", + "informational" + ], + "title": "Status", + "type": "string" + }, + "tools": { + "items": { + "$ref": "#/$defs/HighRiskToolEntry" + }, + "title": "Tools", + "type": "array" + }, + "total_tools": { + "default": 0, + "title": "Total Tools", + "type": "integer" + } + }, + "required": [ + "status" + ], + "title": "HighRiskSurfaceSection", + "type": "object" + }, + "HighRiskToolEntry": { + "additionalProperties": false, + "properties": { + "has_approval_policy": { + "default": false, + "title": "Has Approval Policy", + "type": "boolean" + }, + "has_idempotency_policy": { + "default": false, + "title": "Has Idempotency Policy", + "type": "boolean" + }, + "name": { + "title": "Name", + "type": "string" + }, + "provider": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Provider" + }, + "risk_tags": { + "items": { + "type": "string" + }, + "title": "Risk Tags", + "type": "array" + }, + "source_type": { + "title": "Source Type", + "type": "string" + }, + "tool_id": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Tool Id" + } + }, + "required": [ + "name", + "source_type" + ], + "title": "HighRiskToolEntry", + "type": "object" + }, + "HitlSourceProvenance": { + "additionalProperties": false, + "properties": { + "detail": { + "title": "Detail", + "type": "string" + }, + "location": { + "title": "Location", + "type": "string" + }, + "ref": { + "title": "Ref", + "type": "string" + }, + "status": { + "enum": [ + "requirement_only", + "expected_but_absent", + "source_load_failed", + "loaded", + "loaded_with_warnings" + ], + "title": "Status", + "type": "string" + }, + "type": { + "enum": [ + "approval_trace", + "agent_trace", + "override_log", + "high_risk_exclusion", + "promotion_criteria", + "manifest_requirement" + ], + "title": "Type", + "type": "string" + } + }, + "required": [ + "type", + "ref", + "location", + "status", + "detail" + ], + "title": "HitlSourceProvenance", + "type": "object" + }, + "HumanInTheLoopEvidence": { + "additionalProperties": false, + "description": "\u00a78 \u2014 human-in-the-loop evidence.\n\n``trace_findings`` is kept for packet v0.1 compatibility. It now\ncarries HITL evidence gaps surfaced as findings, including the\noriginal trace-derived gaps and validation-config evidence gaps.", + "properties": { + "approval_required_tools": { + "items": { + "type": "string" + }, + "title": "Approval Required Tools", + "type": "array" + }, + "capability_trace_refs": { + "items": { + "type": "string" + }, + "title": "Capability Trace Refs", + "type": "array" + }, + "capability_trace_summary": { + "$ref": "#/$defs/CapabilityTraceEvidenceSummary" + }, + "confirmation_required_tools": { + "items": { + "type": "string" + }, + "title": "Confirmation Required Tools", + "type": "array" + }, + "human_review_recommended": { + "default": false, + "title": "Human Review Recommended", + "type": "boolean" + }, + "is_configured": { + "default": false, + "title": "Is Configured", + "type": "boolean" + }, + "provenance_mode": { + "default": "fresh_scan", + "enum": [ + "fresh_scan", + "rebuilt_from_findings", + "unavailable" + ], + "title": "Provenance Mode", + "type": "string" + }, + "runtime_control_disclaimer": { + "default": "HITL evidence is local review evidence only. Missing local evidence does not prove a runtime control is absent, and present local evidence does not certify runtime enforcement.", + "title": "Runtime Control Disclaimer", + "type": "string" + }, + "source_provenance": { + "items": { + "$ref": "#/$defs/HitlSourceProvenance" + }, + "title": "Source Provenance", + "type": "array" + }, + "status": { + "enum": [ + "covered", + "partial", + "not_declared", + "missing", + "informational" + ], + "title": "Status", + "type": "string" + }, + "trace_findings": { + "items": { + "$ref": "#/$defs/ReleaseDecisionItem" + }, + "title": "Trace Findings", + "type": "array" + } + }, + "required": [ + "status" + ], + "title": "HumanInTheLoopEvidence", + "type": "object" + }, + "IdempotencyRiskSection": { + "additionalProperties": false, + "description": "\u00a75 \u2014 idempotency / retry risk.", + "properties": { + "gap_findings": { + "items": { + "$ref": "#/$defs/ReleaseDecisionItem" + }, + "title": "Gap Findings", + "type": "array" + }, + "retry_policy_declared": { + "default": false, + "title": "Retry Policy Declared", + "type": "boolean" + }, + "rows": { + "items": { + "$ref": "#/$defs/IdempotencyRow" + }, + "title": "Rows", + "type": "array" + }, + "status": { + "enum": [ + "covered", + "partial", + "not_declared", + "missing", + "informational" + ], + "title": "Status", + "type": "string" + } + }, + "required": [ + "status" + ], + "title": "IdempotencyRiskSection", + "type": "object" + }, + "IdempotencyRow": { + "additionalProperties": false, + "properties": { + "declared": { + "title": "Declared", + "type": "boolean" + }, + "gap_finding_ids": { + "items": { + "type": "string" + }, + "title": "Gap Finding Ids", + "type": "array" + }, + "provider": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Provider" + }, + "source": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Source" + }, + "tool": { + "title": "Tool", + "type": "string" + }, + "tool_id": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Tool Id" + } + }, + "required": [ + "tool", + "declared" + ], + "title": "IdempotencyRow", + "type": "object" + }, + "IdentityCoverageDecision": { + "properties": { + "ambiguous_name_count": { + "default": 0, + "title": "Ambiguous Name Count", + "type": "integer" + }, + "bound_tools": { + "default": 0, + "title": "Bound Tools", + "type": "integer" + }, + "canonical_tools": { + "default": 0, + "title": "Canonical Tools", + "type": "integer" + }, + "gap_count": { + "default": 0, + "title": "Gap Count", + "type": "integer" + }, + "pass_eligible_tools": { + "default": 0, + "title": "Pass Eligible Tools", + "type": "integer" + }, + "reason_counts": { + "additionalProperties": { + "type": "integer" + }, + "title": "Reason Counts", + "type": "object" + }, + "total_observations": { + "default": 0, + "title": "Total Observations", + "type": "integer" + } + }, + "title": "IdentityCoverageDecision", + "type": "object" + }, + "MemoryIsolationStatus": { + "additionalProperties": false, + "description": "\u00a77 \u2014 memory isolation. v0.1 always renders ``is_declared=False``;\na future manifest schema may add ``agent.memory`` and populate this\nsection. Until then the structural slot is preserved so packets\nhave a consistent shape across versions.", + "properties": { + "is_declared": { + "default": false, + "title": "Is Declared", + "type": "boolean" + }, + "notes": { + "default": "Manifest does not declare a memory isolation policy. The current manifest schema (v0.1) has no agent.memory field. See \u00a710 for the residual review item.", + "title": "Notes", + "type": "string" + }, + "status": { + "default": "not_declared", + "enum": [ + "covered", + "partial", + "not_declared", + "missing", + "informational" + ], + "title": "Status", + "type": "string" + } + }, + "title": "MemoryIsolationStatus", + "type": "object" + }, + "NotProvenItem": { + "additionalProperties": false, + "properties": { + "body": { + "title": "Body", + "type": "string" + }, + "label": { + "title": "Label", + "type": "string" + } + }, + "required": [ + "label", + "body" + ], + "title": "NotProvenItem", + "type": "object" + }, + "NotProvenSection": { + "additionalProperties": false, + "description": "\u00a710 \u2014 what Shipgate did NOT prove. Combines unconditional\ndisclaimers with per-run residuals so reviewers can see both the\nstatic scope and what's missing for this specific scan.", + "properties": { + "additional_residuals": { + "items": { + "type": "string" + }, + "title": "Additional Residuals", + "type": "array" + }, + "headline": { + "title": "Headline", + "type": "string" + }, + "low_confidence_tools": { + "items": { + "type": "string" + }, + "title": "Low Confidence Tools", + "type": "array" + }, + "source_warnings": { + "items": { + "type": "string" + }, + "title": "Source Warnings", + "type": "array" + }, + "suppressed_finding_ids": { + "items": { + "type": "string" + }, + "title": "Suppressed Finding Ids", + "type": "array" + }, + "unconditional": { + "items": { + "$ref": "#/$defs/NotProvenItem" + }, + "title": "Unconditional", + "type": "array" + } + }, + "required": [ + "headline" + ], + "title": "NotProvenSection", + "type": "object" + }, + "PolicyPredicateEvidence": { + "additionalProperties": false, + "description": "One tri-state policy predicate and the evidence that supports it.", + "properties": { + "claim_ids": { + "items": { + "type": "string" + }, + "title": "Claim Ids", + "type": "array" + }, + "confidence": { + "default": "low", + "enum": [ + "low", + "medium", + "high" + ], + "title": "Confidence", + "type": "string" + }, + "evidence_bases": { + "items": { + "enum": [ + "reviewed_declaration", + "protocol_structure", + "typed_provider_fact", + "structural_scope", + "inferred_keyword", + "inferred_regex", + "protocol_default", + "unknown" + ], + "type": "string" + }, + "title": "Evidence Bases", + "type": "array" + }, + "expected": { + "default": null, + "title": "Expected" + }, + "observed": { + "default": null, + "title": "Observed" + }, + "policy_eligible": { + "default": false, + "title": "Policy Eligible", + "type": "boolean" + }, + "predicate": { + "title": "Predicate", + "type": "string" + }, + "status": { + "enum": [ + "matched", + "not_matched", + "indeterminate", + "conflicting" + ], + "title": "Status", + "type": "string" + }, + "why": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Why" + } + }, + "required": [ + "predicate", + "status" + ], + "title": "PolicyPredicateEvidence", + "type": "object" + }, + "ReleaseDecisionItem": { + "properties": { + "baseline_status": { + "anyOf": [ + { + "enum": [ + "new", + "matched", + "resolved" + ], + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Baseline Status" + }, + "blocks_release": { + "default": false, + "title": "Blocks Release", + "type": "boolean" + }, + "capability_trace_refs": { + "items": { + "type": "string" + }, + "title": "Capability Trace Refs", + "type": "array" + }, + "check_id": { + "title": "Check Id", + "type": "string" + }, + "fingerprint": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Fingerprint" + }, + "id": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Id" + }, + "policy_evidence_source": { + "anyOf": [ + { + "$ref": "#/$defs/SourceReference" + }, + { + "type": "null" + } + ], + "default": null + }, + "severity": { + "enum": [ + "info", + "low", + "medium", + "high", + "critical" + ], + "title": "Severity", + "type": "string" + }, + "source": { + "anyOf": [ + { + "$ref": "#/$defs/SourceReference" + }, + { + "type": "null" + } + ], + "default": null + }, + "support": { + "anyOf": [ + { + "$ref": "#/$defs/FindingSupport" + }, + { + "type": "null" + } + ], + "default": null + }, + "title": { + "title": "Title", + "type": "string" + } + }, + "required": [ + "check_id", + "severity", + "title" + ], + "title": "ReleaseDecisionItem", + "type": "object" + }, + "ReleaseDecisionSection": { + "additionalProperties": false, + "description": "\u00a71 \u2014 release decision. Verdict derives from\n``release_decision.decision`` only; ``fail_policy`` is rendered as\nseparate CI behavior metadata, never as the verdict source.", + "properties": { + "baseline_delta": { + "$ref": "#/$defs/BaselineDelta" + }, + "blockers": { + "items": { + "$ref": "#/$defs/ReleaseDecisionItem" + }, + "title": "Blockers", + "type": "array" + }, + "decision": { + "enum": [ + "blocked", + "review_required", + "insufficient_evidence", + "passed" + ], + "title": "Decision", + "type": "string" + }, + "evidence_coverage": { + "$ref": "#/$defs/EvidenceCoverageDecision" + }, + "fail_policy": { + "$ref": "#/$defs/FailPolicy" + }, + "reason": { + "title": "Reason", + "type": "string" + }, + "review_items": { + "items": { + "$ref": "#/$defs/ReleaseDecisionItem" + }, + "title": "Review Items", + "type": "array" + }, + "runtime_behavior_verified": { + "const": false, + "default": false, + "title": "Runtime Behavior Verified", + "type": "boolean" + }, + "static_analysis_only": { + "const": true, + "default": true, + "title": "Static Analysis Only", + "type": "boolean" + }, + "static_verdict_disclaimer": { + "default": "This verdict covers deterministic static evidence only. Agents Shipgate did not execute the agent or prove runtime behavior, tool routing, credential enforcement, or safety.", + "title": "Static Verdict Disclaimer", + "type": "string" + }, + "verdict": { + "enum": [ + "PASSED", + "REVIEW REQUIRED", + "INSUFFICIENT EVIDENCE", + "BLOCKED" + ], + "title": "Verdict", + "type": "string" + } + }, + "required": [ + "decision", + "verdict", + "reason", + "evidence_coverage", + "baseline_delta", + "fail_policy" + ], + "title": "ReleaseDecisionSection", + "type": "object" + }, + "ScopeCoverageRow": { + "additionalProperties": false, + "properties": { + "declared": { + "title": "Declared", + "type": "boolean" + }, + "scope": { + "title": "Scope", + "type": "string" + }, + "used_by_tool_ids": { + "items": { + "type": "string" + }, + "title": "Used By Tool Ids", + "type": "array" + }, + "used_by_tools": { + "items": { + "type": "string" + }, + "title": "Used By Tools", + "type": "array" + } + }, + "required": [ + "scope", + "declared" + ], + "title": "ScopeCoverageRow", + "type": "object" + }, + "ScopeCoverageSection": { + "additionalProperties": false, + "description": "\u00a76 \u2014 scope coverage.", + "properties": { + "declared_scopes": { + "items": { + "type": "string" + }, + "title": "Declared Scopes", + "type": "array" + }, + "gap_findings": { + "items": { + "$ref": "#/$defs/ReleaseDecisionItem" + }, + "title": "Gap Findings", + "type": "array" + }, + "missing_declared": { + "items": { + "type": "string" + }, + "title": "Missing Declared", + "type": "array" + }, + "rows": { + "items": { + "$ref": "#/$defs/ScopeCoverageRow" + }, + "title": "Rows", + "type": "array" + }, + "status": { + "enum": [ + "covered", + "partial", + "not_declared", + "missing", + "informational" + ], + "title": "Status", + "type": "string" + }, + "unused_declared": { + "items": { + "type": "string" + }, + "title": "Unused Declared", + "type": "array" + } + }, + "required": [ + "status" + ], + "title": "ScopeCoverageSection", + "type": "object" + }, + "SemanticCoverageDecision": { + "description": "v0.29 pass eligibility across the normalized action surface.\n\nUnlike extraction-confidence thresholds, semantic gaps are\nzero-tolerance: any non-pass-eligible unknown/partial/conflicting\ndimension prevents ``passed``. Known authority review concerns (for\nexample ambient or unscoped credentials) are counted separately so\nthey deterministically route to ``review_required`` rather than\n``insufficient_evidence``.", + "properties": { + "gap_count": { + "default": 0, + "title": "Gap Count", + "type": "integer" + }, + "pass_eligible_actions": { + "default": 0, + "title": "Pass Eligible Actions", + "type": "integer" + }, + "reason_counts": { + "additionalProperties": { + "type": "integer" + }, + "title": "Reason Counts", + "type": "object" + }, + "review_concern_count": { + "default": 0, + "title": "Review Concern Count", + "type": "integer" + }, + "total_actions": { + "default": 0, + "title": "Total Actions", + "type": "integer" + } + }, + "title": "SemanticCoverageDecision", + "type": "object" + }, + "SourceReference": { + "additionalProperties": true, + "properties": { + "end_line": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "default": null, + "title": "End Line" + }, + "location": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Location" + }, + "path": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Path" + }, + "pointer": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Pointer" + }, + "ref": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Ref" + }, + "start_column": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Start Column" + }, + "start_line": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Start Line" + }, + "type": { + "title": "Type", + "type": "string" + } + }, + "required": [ + "type" + ], + "title": "SourceReference", + "type": "object" + }, + "ToolSurfaceDiffSection": { + "additionalProperties": false, + "description": "\u00a73A \u2014 compact tool-surface diff for reviewers.", + "properties": { + "base_kind": { + "default": "none", + "title": "Base Kind", + "type": "string" + }, + "enabled": { + "default": false, + "title": "Enabled", + "type": "boolean" + }, + "highlights": { + "items": { + "type": "string" + }, + "title": "Highlights", + "type": "array" + }, + "notes": { + "items": { + "type": "string" + }, + "title": "Notes", + "type": "array" + }, + "status": { + "enum": [ + "covered", + "partial", + "not_declared", + "missing", + "informational" + ], + "title": "Status", + "type": "string" + }, + "summary": { + "$ref": "#/$defs/ToolSurfaceDiffSummary" + } + }, + "required": [ + "status" + ], + "title": "ToolSurfaceDiffSection", + "type": "object" + }, + "ToolSurfaceDiffSummary": { + "additionalProperties": false, + "properties": { + "accepted_debt": { + "default": 0, + "title": "Accepted Debt", + "type": "integer" + }, + "controls_added": { + "default": 0, + "title": "Controls Added", + "type": "integer" + }, + "controls_removed": { + "default": 0, + "title": "Controls Removed", + "type": "integer" + }, + "metadata_changes": { + "default": 0, + "title": "Metadata Changes", + "type": "integer" + }, + "new_findings": { + "default": 0, + "title": "New Findings", + "type": "integer" + }, + "new_high_risk_effects": { + "default": 0, + "title": "New High Risk Effects", + "type": "integer" + }, + "new_scopes": { + "default": 0, + "title": "New Scopes", + "type": "integer" + }, + "policy_drift_items": { + "default": 0, + "title": "Policy Drift Items", + "type": "integer" + }, + "removed_high_risk_effects": { + "default": 0, + "title": "Removed High Risk Effects", + "type": "integer" + }, + "removed_scopes": { + "default": 0, + "title": "Removed Scopes", + "type": "integer" + }, + "resolved_findings": { + "default": 0, + "title": "Resolved Findings", + "type": "integer" + }, + "tools_added": { + "default": 0, + "title": "Tools Added", + "type": "integer" + }, + "tools_changed": { + "default": 0, + "title": "Tools Changed", + "type": "integer" + }, + "tools_removed": { + "default": 0, + "title": "Tools Removed", + "type": "integer" + }, + "unchanged_findings": { + "default": 0, + "title": "Unchanged Findings", + "type": "integer" + } + }, + "title": "ToolSurfaceDiffSummary", + "type": "object" + } + }, + "$id": "https://raw.githubusercontent.com/ThreeMoonsLab/agents-shipgate/main/docs/packet-schema.v0.11.json", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "additionalProperties": false, + "description": "JSON Schema for packet.json. Generated from agents_shipgate.schemas.packet.EvidencePacket. Do not edit by hand.", + "properties": { + "action_surface_diff": { + "$ref": "#/$defs/ActionSurfaceDiffSection" + }, + "agent": { + "additionalProperties": true, + "title": "Agent", + "type": "object" + }, + "approval_coverage": { + "$ref": "#/$defs/ApprovalCoverageSection" + }, + "capability_intent": { + "$ref": "#/$defs/CapabilityIntentDiff" + }, + "dynamic_scenarios": { + "$ref": "#/$defs/DynamicScenariosSection" + }, + "environment": { + "additionalProperties": true, + "title": "Environment", + "type": "object" + }, + "evidence_matrix": { + "$ref": "#/$defs/EvidenceMatrixSection" + }, + "generated_at": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Generated At" + }, + "high_risk_surface": { + "$ref": "#/$defs/HighRiskSurfaceSection" + }, + "human_in_the_loop": { + "$ref": "#/$defs/HumanInTheLoopEvidence" + }, + "idempotency_risk": { + "$ref": "#/$defs/IdempotencyRiskSection" + }, + "memory_isolation": { + "$ref": "#/$defs/MemoryIsolationStatus" + }, + "not_proven": { + "$ref": "#/$defs/NotProvenSection" + }, + "packet_schema_version": { + "const": "0.11", + "default": "0.11", + "title": "Packet Schema Version", + "type": "string" + }, + "project": { + "additionalProperties": true, + "title": "Project", + "type": "object" + }, + "release_decision": { + "$ref": "#/$defs/ReleaseDecisionSection" + }, + "run_id": { + "title": "Run Id", + "type": "string" + }, + "scope_coverage": { + "$ref": "#/$defs/ScopeCoverageSection" + }, + "tool_surface_diff": { + "$ref": "#/$defs/ToolSurfaceDiffSection" + } + }, + "required": [ + "run_id", + "release_decision", + "capability_intent", + "high_risk_surface", + "approval_coverage", + "idempotency_risk", + "scope_coverage", + "memory_isolation", + "human_in_the_loop", + "dynamic_scenarios", + "not_proven" + ], + "title": "Agents Shipgate Release Evidence Packet v0.11", + "type": "object" +} diff --git a/docs/passed-verdict-contract.md b/docs/passed-verdict-contract.md index fd840eea..1b6f1030 100644 --- a/docs/passed-verdict-contract.md +++ b/docs/passed-verdict-contract.md @@ -1,7 +1,7 @@ # Evidence-backed `passed` verdict -Starting with the Agents Shipgate `0.16.0b2` runtime (contract v13, report -schema v0.32), `release_decision.decision: passed` means the configured root +Starting with the Agents Shipgate `0.16.0b5` runtime (contract v16, report +schema v0.33), `release_decision.decision: passed` means the configured root agent and its complete reachable tool/handoff graph were statically proven, and every reachable capability has complete, conflict-free static identity, binding, effect, and authority evidence, all applicable controls were evaluated, and no @@ -21,7 +21,9 @@ An action is pass-eligible only when all of the following hold: source fact such as an OpenAPI method or an explicit MCP annotation; - its authority is explicitly `none` or concretely scoped; - no semantic claims conflict and all annotation values are valid; and -- controls required by the normalized effect are present. +- controls required by the normalized effect are present; and +- every applicable policy predicate is supported by high-confidence reviewed, + protocol-structural, typed-provider, or structural-scope evidence. Names, descriptions, schema keywords, regular expressions, and protocol defaults may raise the conservative risk bound, but they never establish @@ -29,16 +31,23 @@ safety. A capability with only inferred, defaulted, partial, or missing evidence is `insufficient_evidence`, regardless of how many fully described capabilities are present alongside it. -Semantic evidence is part of the release decision, not a Finding. It cannot be +Semantic and policy-applicability evidence are part of the release decision, +not Findings. They cannot be suppressed, baseline-matched, waived through a severity override, or converted to known evidence by `--no-heuristics` or `human_ack`. Machine consumers should inspect -`release_decision.evidence_coverage.semantic_coverage`, `binding_coverage`, and -`identity_coverage`, then work `evidence_gaps[]` in order. Packet schema v0.10 -mirrors this contract, while capability standard v0.4 carries the same -normalized assessment and binding hash in capability lock v0.5 and lock-diff -v0.6 artifacts. +`release_decision.evidence_coverage.semantic_coverage`, `binding_coverage`, +`identity_coverage`, and `policy_gap_count`, then work `evidence_gaps[]` in +order. Packet schema v0.11 mirrors this contract, while capability standard +v0.5 carries the same normalized assessment and binding hash in capability +lock v0.6 and lock-diff v0.7 artifacts. + +Policy severity, `block: true`, risk overrides, and rule-declared confidence +cannot upgrade underlying evidence. Heuristic-only, mixed, unknown, or +conflicting policy applicability creates a non-waivable evidence gap. The +conservative effect may still increase, but no hard finding is emitted until +the predicate is supported by authoritative static evidence. Catalog membership never implies binding. `tool_catalog[]` contains every canonical extracted declaration; `tool_inventory[]`, actions, checks, and diff --git a/docs/policy-pack-schema.v0.4.json b/docs/policy-pack-schema.v0.4.json new file mode 100644 index 00000000..0f72c5a6 --- /dev/null +++ b/docs/policy-pack-schema.v0.4.json @@ -0,0 +1,647 @@ +{ + "$defs": { + "PolicyPackApproval": { + "additionalProperties": false, + "properties": { + "min_approvals": { + "anyOf": [ + { + "minimum": 1, + "type": "integer" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Requested human approval count for routing/audit metadata only; not enforced by the deterministic rule engine.", + "title": "Min Approvals" + }, + "required": { + "default": false, + "description": "Routing/audit metadata only. Shipgate validates team references but does not verify external approvals or change release gating from this field.", + "title": "Required", + "type": "boolean" + }, + "teams": { + "description": "Organization team names for approval routing metadata. They are not checked against GitHub or any external approval system.", + "items": { + "type": "string" + }, + "title": "Teams", + "type": "array" + } + }, + "title": "PolicyPackApproval", + "type": "object" + }, + "PolicyPackCapabilityMatch": { + "additionalProperties": false, + "properties": { + "auth_types": { + "items": { + "type": "string" + }, + "title": "Auth Types", + "type": "array" + }, + "broad_scope": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Broad Scope" + }, + "code_execution": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Code Execution" + }, + "credential_modes": { + "items": { + "type": "string" + }, + "title": "Credential Modes", + "type": "array" + }, + "effects": { + "items": { + "enum": [ + "read", + "write", + "destructive", + "external_communication", + "financial_write", + "production_operation", + "privileged_data_access", + "code_execution", + "identity_access" + ], + "type": "string" + }, + "title": "Effects", + "type": "array" + }, + "externally_visible": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Externally Visible" + }, + "financial": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Financial" + }, + "handles_sensitive_data": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Handles Sensitive Data" + }, + "high_risk": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ], + "default": null, + "title": "High Risk" + }, + "missing_approval_policy": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Missing Approval Policy" + }, + "missing_auth_scopes": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Missing Auth Scopes" + }, + "missing_confirmation_policy": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Missing Confirmation Policy" + }, + "missing_idempotency_policy": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Missing Idempotency Policy" + }, + "missing_owner": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Missing Owner" + }, + "operations": { + "items": { + "type": "string" + }, + "title": "Operations", + "type": "array" + }, + "parameters": { + "items": { + "$ref": "#/$defs/PolicyPackParameterMatch" + }, + "title": "Parameters", + "type": "array" + }, + "providers": { + "items": { + "type": "string" + }, + "title": "Providers", + "type": "array" + }, + "risk_tags": { + "items": { + "type": "string" + }, + "title": "Risk Tags", + "type": "array" + }, + "scopes": { + "items": { + "type": "string" + }, + "title": "Scopes", + "type": "array" + }, + "source_types": { + "items": { + "type": "string" + }, + "title": "Source Types", + "type": "array" + }, + "tool_names": { + "items": { + "type": "string" + }, + "title": "Tool Names", + "type": "array" + } + }, + "title": "PolicyPackCapabilityMatch", + "type": "object" + }, + "PolicyPackFile": { + "additionalProperties": false, + "properties": { + "id": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Id" + }, + "name": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Name" + }, + "owner": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Owner" + }, + "rules": { + "items": { + "$ref": "#/$defs/PolicyPackRule" + }, + "title": "Rules", + "type": "array" + }, + "version": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Version" + } + }, + "required": [ + "rules" + ], + "title": "PolicyPackFile", + "type": "object" + }, + "PolicyPackMatch": { + "additionalProperties": false, + "properties": { + "all_of": { + "items": { + "$ref": "#/$defs/PolicyPackMatch" + }, + "title": "All Of", + "type": "array" + }, + "any_of": { + "items": { + "$ref": "#/$defs/PolicyPackMatch" + }, + "title": "Any Of", + "type": "array" + }, + "capability": { + "anyOf": [ + { + "$ref": "#/$defs/PolicyPackCapabilityMatch" + }, + { + "type": "null" + } + ], + "default": null + }, + "environment_targets": { + "items": { + "type": "string" + }, + "title": "Environment Targets", + "type": "array" + }, + "missing_approval_policy": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Missing Approval Policy" + }, + "missing_auth_scopes": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Missing Auth Scopes" + }, + "missing_confirmation_policy": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Missing Confirmation Policy" + }, + "missing_idempotency_policy": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Missing Idempotency Policy" + }, + "missing_owner": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Missing Owner" + }, + "none_of": { + "items": { + "$ref": "#/$defs/PolicyPackMatch" + }, + "title": "None Of", + "type": "array" + }, + "parameters": { + "items": { + "$ref": "#/$defs/PolicyPackParameterMatch" + }, + "title": "Parameters", + "type": "array" + }, + "risk_tags": { + "items": { + "type": "string" + }, + "title": "Risk Tags", + "type": "array" + }, + "source_types": { + "items": { + "type": "string" + }, + "title": "Source Types", + "type": "array" + } + }, + "title": "PolicyPackMatch", + "type": "object" + }, + "PolicyPackParameterMatch": { + "additionalProperties": false, + "properties": { + "maximum_above": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Maximum Above" + }, + "minimum_below": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Minimum Below" + }, + "missing_maximum": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Missing Maximum" + }, + "name": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Name" + }, + "names": { + "items": { + "type": "string" + }, + "title": "Names", + "type": "array" + }, + "required": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Required" + }, + "types": { + "items": { + "type": "string" + }, + "title": "Types", + "type": "array" + } + }, + "title": "PolicyPackParameterMatch", + "type": "object" + }, + "PolicyPackRule": { + "additionalProperties": false, + "properties": { + "approval": { + "anyOf": [ + { + "$ref": "#/$defs/PolicyPackApproval" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Optional non-enforcing approval routing metadata. Rule matching and release decisions continue to use deterministic predicates and the rule's severity/block fields." + }, + "block": { + "default": false, + "title": "Block", + "type": "boolean" + }, + "category": { + "default": "policy_pack", + "title": "Category", + "type": "string" + }, + "confidence": { + "default": "medium", + "enum": [ + "low", + "medium", + "high" + ], + "title": "Confidence", + "type": "string" + }, + "description": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Description" + }, + "id": { + "title": "Id", + "type": "string" + }, + "match": { + "$ref": "#/$defs/PolicyPackMatch" + }, + "owner": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Owner" + }, + "recommendation": { + "title": "Recommendation", + "type": "string" + }, + "reviewers": { + "items": { + "type": "string" + }, + "title": "Reviewers", + "type": "array" + }, + "severity": { + "enum": [ + "info", + "low", + "medium", + "high", + "critical" + ], + "title": "Severity", + "type": "string" + }, + "title": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Title" + } + }, + "required": [ + "id", + "severity", + "recommendation", + "match" + ], + "title": "PolicyPackRule", + "type": "object" + } + }, + "$id": "https://raw.githubusercontent.com/ThreeMoonsLab/agents-shipgate/main/docs/policy-pack-schema.v0.4.json", + "$ref": "#/$defs/PolicyPackFile", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "description": "JSON Schema for local Agents Shipgate policy-pack YAML files. Generated from agents_shipgate.schemas.policy_pack.PolicyPackArtifactV1. Do not edit by hand.", + "title": "Agents Shipgate Policy Pack v0.4" +} diff --git a/docs/policy-packs.md b/docs/policy-packs.md index d9149f2f..b9c24f99 100644 --- a/docs/policy-packs.md +++ b/docs/policy-packs.md @@ -3,8 +3,8 @@ Policy packs are local YAML files for organization-specific release rules. They are declarative data, not Python plugins, and are enabled by default only when declared in `shipgate.yaml` or passed on the CLI. The machine-readable schema is -[`policy-pack-schema.v0.2.json`](policy-pack-schema.v0.2.json) (v0.1 stays -frozen for older builds); policy-pack files do not need to declare a +[`policy-pack-schema.v0.4.json`](policy-pack-schema.v0.4.json) (v0.3 and older +stay frozen for older builds); policy-pack files do not need to declare a schema-version key. ```yaml @@ -53,10 +53,12 @@ Supported rule fields: - `description`: optional fallback finding title when `title` is omitted. - `category`: optional finding category; defaults to `policy_pack`. - `severity`: required `info`, `low`, `medium`, `high`, or `critical`. -- `block`: optional boolean; when `true`, matching findings set +- `block`: optional boolean; when `true`, authoritatively matched findings set `findings[].blocks_release` and block strict CI even if severity is below the configured `fail_on` threshold. - `confidence`: optional `low`, `medium`, or `high`; defaults to `medium`. + This is a ceiling requested by the rule, never an upgrade over predicate + evidence confidence. - `recommendation`: required remediation text. - `match`: required static predicate object. - `owner`, `reviewers`, `approval`: optional reviewer/audit routing metadata. @@ -66,7 +68,9 @@ Supported rule fields: Supported legacy match fields: -- `risk_tags`: fires when the tool has any listed medium-or-higher risk tag. +- `risk_tags`: fires only when a listed tag is backed by policy-eligible + semantic claims. Keyword/regex candidates remain visible as indeterminate + applicability and do not create a finding. - `source_types`: fires only for matching normalized tool source types. - `environment_targets`: fires only for matching manifest environment targets. - `missing_owner`, `missing_auth_scopes`, `missing_approval_policy`, @@ -150,6 +154,15 @@ count. Policy-pack findings support suppressions, severity overrides, release-blocking `block: true`, baselines, Markdown, JSON, and SARIF like built-in findings. +Each emitted policy finding carries `support`, including predicate status, +effective confidence, evidence bases, contributing claim IDs, and a stable +`support_hash`. A rule emits a finding only for an authoritative `matched` +result. `indeterminate` or `conflicting` applicability is emitted through +`policy_evidence_gaps[]` and release-decision `evidence_gaps[]`, outside the +Finding model, so suppressions, severity overrides, baselines, +acknowledgements, and `--no-heuristics` cannot hide it. Baseline matching also +requires the support hash; changing the evidence basis makes the finding new. + Routing metadata is non-enforcing. Shipgate validates declared team names against `organization.teams` when present, but it does not call GitHub or any external approval system to verify approvals. Use deterministic `match` @@ -192,10 +205,16 @@ adds explicit combinators — each branch is a complete nested `match` evaluated against the same subject: - `all_of: [, ...]` — every branch must match. -- `any_of: [, ...]` — at least one branch must match; the finding - evidence records which branch hit (`{"index": N, "matched": {...}}`). +- `any_of: [, ...]` — at least one branch must match with + policy-eligible evidence; the finding records the first authoritative branch + that established applicability (`{"index": N, "matched": {...}}`). - `none_of: [, ...]` — no branch may match. +Absence of a risk tag is not authoritative negative evidence. Therefore a +`none_of` branch based only on `risk_tags` remains indeterminate unless the +underlying semantic assessment can prove the predicate from typed evidence; +it produces a non-waivable policy-evidence gap rather than a finding or pass. + Parameter predicates gain declared-bound comparisons: `maximum_above: ` (declared `maximum` exceeds the threshold) and `minimum_below: `. These compare the *declared* schema bounds — @@ -225,8 +244,8 @@ rules: ``` Determinism: branches evaluate in declaration order; `any_of` records the -first matching branch. An empty branch (`{}`) matches every subject — -always give branches at least one predicate. +first policy-eligible matching branch. An empty branch (`{}`) has no evidence +and is indeterminate — always give branches at least one predicate. ## Distributing Org Packs diff --git a/docs/report-reading-for-agents.md b/docs/report-reading-for-agents.md index 66cb7a02..76040765 100644 --- a/docs/report-reading-for-agents.md +++ b/docs/report-reading-for-agents.md @@ -227,7 +227,7 @@ Surface the `next_action` to the user rather than scraping prose. The full diagn | Schema | Current | Frozen references | File | |---|---|---|---| -| Report | `0.32` | `0.31`, `0.30`, `0.29`, `0.28`, `0.27`, `0.26`, `0.25`, `0.24`, `0.23`, `0.22`, `0.21`, `0.20`, `0.19`, `0.18`, `0.17`, `0.16`, `0.15`, `0.14`, `0.13`, `0.12`, `0.11`, `0.10`, `0.9`, `0.8`, `0.7`, `0.6`, `0.5`, `0.4`, `0.3`, `0.2`, `0.1` | [`report-schema.v0.32.json`](report-schema.v0.32.json) | +| Report | `0.33` | `0.32`, `0.31`, `0.30`, `0.29`, `0.28`, `0.27`, `0.26`, `0.25`, `0.24`, `0.23`, `0.22`, `0.21`, `0.20`, `0.19`, `0.18`, `0.17`, `0.16`, `0.15`, `0.14`, `0.13`, `0.12`, `0.11`, `0.10`, `0.9`, `0.8`, `0.7`, `0.6`, `0.5`, `0.4`, `0.3`, `0.2`, `0.1` | [`report-schema.v0.33.json`](report-schema.v0.33.json) | | Packet | `0.10` | `0.9`, `0.8`, `0.7`, `0.6`, `0.5`, `0.4`, `0.3`, `0.2`, `0.1` | [`packet-schema.v0.10.json`](packet-schema.v0.10.json) | | Manifest | `0.1` | — | [`manifest-v0.1.json`](manifest-v0.1.json) | | CLI contract | `5` | — | `agents-shipgate contract --json` | diff --git a/docs/report-schema.v0.33.json b/docs/report-schema.v0.33.json new file mode 100644 index 00000000..744178ae --- /dev/null +++ b/docs/report-schema.v0.33.json @@ -0,0 +1,7352 @@ +{ + "$defs": { + "ActionApprovalFact": { + "additionalProperties": false, + "properties": { + "required": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Required" + }, + "threshold": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Threshold" + } + }, + "required": [ + "required", + "threshold" + ], + "title": "ActionApprovalFact", + "type": "object" + }, + "ActionEvidenceFact": { + "additionalProperties": false, + "properties": { + "approval_ticket": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Approval Ticket" + }, + "owner": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Owner" + }, + "runbook": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Runbook" + } + }, + "required": [ + "approval_ticket", + "owner", + "runbook" + ], + "title": "ActionEvidenceFact", + "type": "object" + }, + "ActionFact": { + "additionalProperties": false, + "properties": { + "action_id": { + "title": "Action Id", + "type": "string" + }, + "agent_id": { + "title": "Agent Id", + "type": "string" + }, + "approval_policy": { + "$ref": "#/$defs/ActionApprovalFact" + }, + "effect": { + "enum": [ + "read", + "write", + "destructive", + "external_communication", + "financial_write", + "production_operation", + "privileged_data_access", + "code_execution", + "identity_access" + ], + "title": "Effect", + "type": "string" + }, + "evidence": { + "$ref": "#/$defs/ActionEvidenceFact" + }, + "hashes": { + "$ref": "#/$defs/ActionSurfaceHashes" + }, + "input_fields": { + "items": { + "type": "string" + }, + "title": "Input Fields", + "type": "array" + }, + "input_schema_hash": { + "title": "Input Schema Hash", + "type": "string" + }, + "operation": { + "title": "Operation", + "type": "string" + }, + "provider": { + "title": "Provider", + "type": "string" + }, + "required_input_fields": { + "items": { + "type": "string" + }, + "title": "Required Input Fields", + "type": "array" + }, + "required_scopes": { + "items": { + "type": "string" + }, + "title": "Required Scopes", + "type": "array" + }, + "risk_tags": { + "items": { + "type": "string" + }, + "title": "Risk Tags", + "type": "array" + }, + "safeguards": { + "$ref": "#/$defs/ActionSafeguardsFact" + }, + "semantic_assessment": { + "anyOf": [ + { + "$ref": "#/$defs/ToolSemanticEvidence" + }, + { + "type": "null" + } + ], + "default": null + }, + "source_end_line": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Source End Line" + }, + "source_id": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Source Id" + }, + "source_location": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Source Location" + }, + "source_path": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Source Path" + }, + "source_pointer": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Source Pointer" + }, + "source_ref": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Source Ref" + }, + "source_start_column": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Source Start Column" + }, + "source_start_line": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Source Start Line" + }, + "source_type": { + "title": "Source Type", + "type": "string" + }, + "tool_id": { + "title": "Tool Id", + "type": "string" + }, + "tool_name": { + "title": "Tool Name", + "type": "string" + } + }, + "required": [ + "action_id", + "agent_id", + "approval_policy", + "effect", + "evidence", + "hashes", + "input_fields", + "input_schema_hash", + "operation", + "provider", + "required_input_fields", + "required_scopes", + "risk_tags", + "safeguards", + "source_id", + "source_type", + "tool_id", + "tool_name" + ], + "title": "ActionFact", + "type": "object" + }, + "ActionSafeguardsFact": { + "additionalProperties": false, + "properties": { + "audit_log": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Audit Log" + }, + "dry_run": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Dry Run" + }, + "idempotency": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Idempotency" + }, + "rollback": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Rollback" + } + }, + "required": [ + "audit_log", + "dry_run", + "idempotency", + "rollback" + ], + "title": "ActionSafeguardsFact", + "type": "object" + }, + "ActionSurfaceChange": { + "additionalProperties": false, + "properties": { + "action_id": { + "title": "Action Id", + "type": "string" + }, + "added": { + "items": { + "type": "string" + }, + "title": "Added", + "type": "array" + }, + "after": { + "default": null, + "title": "After" + }, + "agent_id": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Agent Id" + }, + "before": { + "default": null, + "title": "Before" + }, + "operation": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Operation" + }, + "reason": { + "title": "Reason", + "type": "string" + }, + "removed": { + "items": { + "type": "string" + }, + "title": "Removed", + "type": "array" + }, + "severity": { + "default": "info", + "enum": [ + "info", + "low", + "medium", + "high", + "critical" + ], + "title": "Severity", + "type": "string" + }, + "source_path": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Source Path" + }, + "source_start_line": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Source Start Line" + }, + "tool_id": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Tool Id" + }, + "tool_name": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Tool Name" + }, + "type": { + "enum": [ + "ACTION_ADDED", + "ACTION_REMOVED", + "ACTION_MODIFIED", + "SCOPE_EXPANDED", + "EFFECT_ESCALATED", + "RISK_TAG_ADDED", + "APPROVAL_REMOVED", + "SAFEGUARD_REMOVED", + "INPUT_SCHEMA_EXPANDED" + ], + "title": "Type", + "type": "string" + } + }, + "required": [ + "action_id", + "added", + "after", + "agent_id", + "before", + "operation", + "reason", + "removed", + "severity", + "tool_name", + "type" + ], + "title": "ActionSurfaceChange", + "type": "object" + }, + "ActionSurfaceDiff": { + "additionalProperties": false, + "properties": { + "added": { + "items": { + "$ref": "#/$defs/ActionSurfaceChange" + }, + "title": "Added", + "type": "array" + }, + "base": { + "$ref": "#/$defs/ToolSurfaceDiffBase" + }, + "enabled": { + "default": false, + "title": "Enabled", + "type": "boolean" + }, + "modified": { + "items": { + "$ref": "#/$defs/ActionSurfaceChange" + }, + "title": "Modified", + "type": "array" + }, + "notes": { + "items": { + "type": "string" + }, + "title": "Notes", + "type": "array" + }, + "removed": { + "items": { + "$ref": "#/$defs/ActionSurfaceChange" + }, + "title": "Removed", + "type": "array" + }, + "summary": { + "$ref": "#/$defs/ActionSurfaceDiffSummary" + } + }, + "required": [ + "added", + "base", + "enabled", + "modified", + "notes", + "removed", + "summary" + ], + "title": "ActionSurfaceDiff", + "type": "object" + }, + "ActionSurfaceDiffSummary": { + "additionalProperties": false, + "properties": { + "actions_added": { + "default": 0, + "title": "Actions Added", + "type": "integer" + }, + "actions_modified": { + "default": 0, + "title": "Actions Modified", + "type": "integer" + }, + "actions_removed": { + "default": 0, + "title": "Actions Removed", + "type": "integer" + }, + "approvals_removed": { + "default": 0, + "title": "Approvals Removed", + "type": "integer" + }, + "blocking_findings": { + "default": 0, + "title": "Blocking Findings", + "type": "integer" + }, + "effect_escalations": { + "default": 0, + "title": "Effect Escalations", + "type": "integer" + }, + "input_schema_expansions": { + "default": 0, + "title": "Input Schema Expansions", + "type": "integer" + }, + "risk_tags_added": { + "default": 0, + "title": "Risk Tags Added", + "type": "integer" + }, + "safeguards_removed": { + "default": 0, + "title": "Safeguards Removed", + "type": "integer" + }, + "scope_expansions": { + "default": 0, + "title": "Scope Expansions", + "type": "integer" + } + }, + "required": [ + "actions_added", + "actions_modified", + "actions_removed", + "approvals_removed", + "blocking_findings", + "effect_escalations", + "input_schema_expansions", + "risk_tags_added", + "safeguards_removed", + "scope_expansions" + ], + "title": "ActionSurfaceDiffSummary", + "type": "object" + }, + "ActionSurfaceFacts": { + "additionalProperties": false, + "properties": { + "actions": { + "items": { + "$ref": "#/$defs/ActionFact" + }, + "title": "Actions", + "type": "array" + }, + "snapshot_version": { + "default": "0.4", + "title": "Snapshot Version", + "type": "string" + } + }, + "required": [ + "actions", + "snapshot_version" + ], + "title": "ActionSurfaceFacts", + "type": "object" + }, + "ActionSurfaceHashes": { + "additionalProperties": false, + "properties": { + "identity_hash": { + "title": "Identity Hash", + "type": "string" + }, + "policy_hash": { + "title": "Policy Hash", + "type": "string" + }, + "risk_hash": { + "title": "Risk Hash", + "type": "string" + }, + "schema_hash": { + "title": "Schema Hash", + "type": "string" + } + }, + "required": [ + "identity_hash", + "policy_hash", + "risk_hash", + "schema_hash" + ], + "title": "ActionSurfaceHashes", + "type": "object" + }, + "AgentBindingGraphAssessment": { + "additionalProperties": false, + "properties": { + "agents": { + "items": { + "$ref": "#/$defs/AgentBindingNode" + }, + "title": "Agents", + "type": "array" + }, + "handoff_edges": { + "items": { + "$ref": "#/$defs/AgentHandoffBindingEdge" + }, + "title": "Handoff Edges", + "type": "array" + }, + "issues": { + "items": { + "$ref": "#/$defs/AgentBindingIssue" + }, + "title": "Issues", + "type": "array" + }, + "pass_eligible": { + "default": false, + "title": "Pass Eligible", + "type": "boolean" + }, + "possible_tool_ids": { + "items": { + "type": "string" + }, + "title": "Possible Tool Ids", + "type": "array" + }, + "reachable_tool_ids": { + "items": { + "type": "string" + }, + "title": "Reachable Tool Ids", + "type": "array" + }, + "root_agent_id": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Root Agent Id" + }, + "status": { + "enum": [ + "declared", + "structural", + "partial", + "unknown", + "conflicting" + ], + "title": "Status", + "type": "string" + }, + "tool_edges": { + "items": { + "$ref": "#/$defs/AgentToolBindingEdge" + }, + "title": "Tool Edges", + "type": "array" + }, + "unbound_tool_ids": { + "items": { + "type": "string" + }, + "title": "Unbound Tool Ids", + "type": "array" + } + }, + "required": [ + "status" + ], + "title": "AgentBindingGraphAssessment", + "type": "object" + }, + "AgentBindingIssue": { + "additionalProperties": false, + "properties": { + "agent_id": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Agent Id" + }, + "kind": { + "enum": [ + "missing_binding_evidence", + "partial_binding_evidence", + "conflicting_binding_evidence", + "ambiguous_root_agent", + "unresolved_agent_binding", + "unresolved_bound_tool", + "incomplete_handoff_graph", + "invalid_binding_annotation" + ], + "title": "Kind", + "type": "string" + }, + "message": { + "title": "Message", + "type": "string" + }, + "source": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Source" + }, + "source_pointer": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Source Pointer" + }, + "tool_id": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Tool Id" + } + }, + "required": [ + "kind", + "message" + ], + "title": "AgentBindingIssue", + "type": "object" + }, + "AgentBindingNode": { + "additionalProperties": false, + "properties": { + "agent_id": { + "title": "Agent Id", + "type": "string" + }, + "name": { + "title": "Name", + "type": "string" + }, + "source_id": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Source Id" + }, + "source_pointer": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Source Pointer" + }, + "source_ref": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Source Ref" + } + }, + "required": [ + "agent_id", + "name" + ], + "title": "AgentBindingNode", + "type": "object" + }, + "AgentHandoffBindingEdge": { + "additionalProperties": false, + "properties": { + "complete": { + "default": true, + "title": "Complete", + "type": "boolean" + }, + "confidence": { + "enum": [ + "low", + "medium", + "high" + ], + "title": "Confidence", + "type": "string" + }, + "edge_type": { + "enum": [ + "subagent", + "handoff" + ], + "title": "Edge Type", + "type": "string" + }, + "provenance_kind": { + "enum": [ + "static_declaration", + "ast_extraction", + "keyword_heuristic", + "regex_heuristic", + "policy_pack", + "runtime_trace" + ], + "title": "Provenance Kind", + "type": "string" + }, + "source": { + "title": "Source", + "type": "string" + }, + "source_agent_id": { + "title": "Source Agent Id", + "type": "string" + }, + "source_pointer": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Source Pointer" + }, + "target_agent_id": { + "title": "Target Agent Id", + "type": "string" + } + }, + "required": [ + "source_agent_id", + "target_agent_id", + "edge_type", + "confidence", + "provenance_kind", + "source" + ], + "title": "AgentHandoffBindingEdge", + "type": "object" + }, + "AgentSummary": { + "additionalProperties": false, + "description": "Top-level summary block shaped for one-fetch agent consumption.\n\nDeterministic projection of (``release_decision``, ``findings[].agent_action``).\nA coding agent that wants the headline numbers can read this block\ninstead of traversing arrays. All fields are derived; this block\ncannot disagree with the underlying data.", + "properties": { + "auto_appliable_patches": { + "default": 0, + "title": "Auto Appliable Patches", + "type": "integer" + }, + "blocker_count": { + "default": 0, + "title": "Blocker Count", + "type": "integer" + }, + "first_recommended_action": { + "anyOf": [ + { + "$ref": "#/$defs/AgentSummaryAction" + }, + { + "type": "null" + } + ], + "default": null + }, + "headline": { + "title": "Headline", + "type": "string" + }, + "needs_human_review": { + "default": 0, + "title": "Needs Human Review", + "type": "integer" + }, + "review_item_count": { + "default": 0, + "title": "Review Item Count", + "type": "integer" + }, + "verdict": { + "enum": [ + "blocked", + "review_required", + "insufficient_evidence", + "passed" + ], + "title": "Verdict", + "type": "string" + } + }, + "required": [ + "auto_appliable_patches", + "blocker_count", + "first_recommended_action", + "headline", + "needs_human_review", + "review_item_count", + "verdict" + ], + "title": "AgentSummary", + "type": "object" + }, + "AgentSummaryAction": { + "additionalProperties": false, + "description": "A single recommended next step shaped for direct agent consumption.\n\nMirrors the ``next_actions[]`` shape used elsewhere in the contract\n(kind/command/why) so callers that already handle diagnostic\nnext_actions can reuse the same renderer here.", + "properties": { + "command": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Command" + }, + "kind": { + "default": "command", + "enum": [ + "command", + "info" + ], + "title": "Kind", + "type": "string" + }, + "why": { + "title": "Why", + "type": "string" + } + }, + "required": [ + "command", + "kind", + "why" + ], + "title": "AgentSummaryAction", + "type": "object" + }, + "AgentToolBindingEdge": { + "additionalProperties": false, + "properties": { + "agent_id": { + "title": "Agent Id", + "type": "string" + }, + "complete": { + "default": true, + "title": "Complete", + "type": "boolean" + }, + "confidence": { + "enum": [ + "low", + "medium", + "high" + ], + "title": "Confidence", + "type": "string" + }, + "edge_type": { + "enum": [ + "direct_tool", + "tool_node", + "toolset", + "workflow", + "subagent", + "handoff" + ], + "title": "Edge Type", + "type": "string" + }, + "provenance_kind": { + "enum": [ + "static_declaration", + "ast_extraction", + "keyword_heuristic", + "regex_heuristic", + "policy_pack", + "runtime_trace" + ], + "title": "Provenance Kind", + "type": "string" + }, + "source": { + "title": "Source", + "type": "string" + }, + "source_pointer": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Source Pointer" + }, + "tool_id": { + "title": "Tool Id", + "type": "string" + } + }, + "required": [ + "agent_id", + "tool_id", + "edge_type", + "confidence", + "provenance_kind", + "source" + ], + "title": "AgentToolBindingEdge", + "type": "object" + }, + "AppendPointerPatch": { + "additionalProperties": false, + "description": "Append a value to the list at a JSON pointer.", + "properties": { + "confidence": { + "enum": [ + "low", + "medium", + "high" + ], + "title": "Confidence", + "type": "string" + }, + "kind": { + "const": "append_pointer", + "default": "append_pointer", + "title": "Kind", + "type": "string" + }, + "pointer": { + "title": "Pointer", + "type": "string" + }, + "rationale": { + "title": "Rationale", + "type": "string" + }, + "target_file": { + "title": "Target File", + "type": "string" + }, + "target_format": { + "enum": [ + "yaml", + "json" + ], + "title": "Target Format", + "type": "string" + }, + "target_sha256": { + "title": "Target Sha256", + "type": "string" + }, + "value": { + "title": "Value" + } + }, + "required": [ + "target_file", + "pointer", + "value", + "target_format", + "confidence", + "rationale", + "target_sha256" + ], + "title": "AppendPointerPatch", + "type": "object" + }, + "AuthoritySemanticEvidence": { + "additionalProperties": false, + "properties": { + "auth_type": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Auth Type" + }, + "claims": { + "items": { + "$ref": "#/$defs/SemanticClaimEvidence" + }, + "title": "Claims", + "type": "array" + }, + "credential_mode": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Credential Mode" + }, + "issues": { + "items": { + "$ref": "#/$defs/SemanticIssueEvidence" + }, + "title": "Issues", + "type": "array" + }, + "mode": { + "enum": [ + "none", + "scoped", + "unscoped", + "ambient", + "unknown" + ], + "title": "Mode", + "type": "string" + }, + "scopes": { + "items": { + "type": "string" + }, + "title": "Scopes", + "type": "array" + }, + "status": { + "enum": [ + "declared", + "structural", + "partial", + "unknown", + "conflicting" + ], + "title": "Status", + "type": "string" + } + }, + "required": [ + "status", + "mode" + ], + "title": "AuthoritySemanticEvidence", + "type": "object" + }, + "BaselineDelta": { + "properties": { + "enabled": { + "title": "Enabled", + "type": "boolean" + }, + "matched_count": { + "default": 0, + "title": "Matched Count", + "type": "integer" + }, + "new_count": { + "default": 0, + "title": "New Count", + "type": "integer" + }, + "path": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Path" + }, + "resolved_count": { + "default": 0, + "title": "Resolved Count", + "type": "integer" + } + }, + "required": [ + "enabled", + "matched_count", + "new_count", + "resolved_count" + ], + "title": "BaselineDelta", + "type": "object" + }, + "BaselineSummary": { + "properties": { + "matched_count": { + "default": 0, + "title": "Matched Count", + "type": "integer" + }, + "new_count": { + "default": 0, + "title": "New Count", + "type": "integer" + }, + "path": { + "title": "Path", + "type": "string" + }, + "resolved_count": { + "default": 0, + "title": "Resolved Count", + "type": "integer" + } + }, + "required": [ + "path" + ], + "title": "BaselineSummary", + "type": "object" + }, + "BindingCoverageDecision": { + "properties": { + "gap_count": { + "default": 0, + "title": "Gap Count", + "type": "integer" + }, + "pass_eligible": { + "default": false, + "title": "Pass Eligible", + "type": "boolean" + }, + "possible_tools": { + "default": 0, + "title": "Possible Tools", + "type": "integer" + }, + "reachable_tools": { + "default": 0, + "title": "Reachable Tools", + "type": "integer" + }, + "reason_counts": { + "additionalProperties": { + "type": "integer" + }, + "title": "Reason Counts", + "type": "object" + }, + "total_catalog_tools": { + "default": 0, + "title": "Total Catalog Tools", + "type": "integer" + }, + "unbound_tools": { + "default": 0, + "title": "Unbound Tools", + "type": "integer" + } + }, + "title": "BindingCoverageDecision", + "type": "object" + }, + "BindingSemanticEvidence": { + "additionalProperties": false, + "properties": { + "claims": { + "items": { + "$ref": "#/$defs/SemanticClaimEvidence" + }, + "title": "Claims", + "type": "array" + }, + "confidence": { + "enum": [ + "low", + "medium", + "high" + ], + "title": "Confidence", + "type": "string" + }, + "issues": { + "items": { + "$ref": "#/$defs/SemanticIssueEvidence" + }, + "title": "Issues", + "type": "array" + }, + "pass_eligible": { + "default": false, + "title": "Pass Eligible", + "type": "boolean" + }, + "reachable_path": { + "items": { + "type": "string" + }, + "title": "Reachable Path", + "type": "array" + }, + "root_agent_id": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Root Agent Id" + }, + "status": { + "enum": [ + "declared", + "structural", + "partial", + "unknown", + "conflicting" + ], + "title": "Status", + "type": "string" + } + }, + "required": [ + "status", + "confidence" + ], + "title": "BindingSemanticEvidence", + "type": "object" + }, + "BindingSurfaceDiff": { + "additionalProperties": false, + "properties": { + "added_handoffs": { + "items": { + "type": "string" + }, + "title": "Added Handoffs", + "type": "array" + }, + "added_reachable_tool_ids": { + "items": { + "type": "string" + }, + "title": "Added Reachable Tool Ids", + "type": "array" + }, + "base_report_schema_version": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Base Report Schema Version" + }, + "enabled": { + "default": false, + "title": "Enabled", + "type": "boolean" + }, + "notes": { + "items": { + "type": "string" + }, + "title": "Notes", + "type": "array" + }, + "removed_handoffs": { + "items": { + "type": "string" + }, + "title": "Removed Handoffs", + "type": "array" + }, + "removed_reachable_tool_ids": { + "items": { + "type": "string" + }, + "title": "Removed Reachable Tool Ids", + "type": "array" + } + }, + "title": "BindingSurfaceDiff", + "type": "object" + }, + "CapabilityChangeBlock": { + "additionalProperties": false, + "description": "The diff-derived capability delta, grouped by direction.\n\nReviewer-facing projection over ``action_surface_diff`` /\n``tool_surface_diff`` (roadmap \u00a77.1). Four member lists \u2014\n``added`` / ``removed`` / ``broadened`` / ``narrowed`` \u2014 plus\n``enabled`` (mirrors the surface-diff enabled flag: ``False`` when no\nbase is available, so the block is a stable empty shape rather than\nabsent). Never gates on its own.", + "properties": { + "added": { + "items": { + "$ref": "#/$defs/CapabilityChangeMember" + }, + "title": "Added", + "type": "array" + }, + "broadened": { + "items": { + "$ref": "#/$defs/CapabilityChangeMember" + }, + "title": "Broadened", + "type": "array" + }, + "enabled": { + "default": false, + "title": "Enabled", + "type": "boolean" + }, + "narrowed": { + "items": { + "$ref": "#/$defs/CapabilityChangeMember" + }, + "title": "Narrowed", + "type": "array" + }, + "removed": { + "items": { + "$ref": "#/$defs/CapabilityChangeMember" + }, + "title": "Removed", + "type": "array" + } + }, + "required": [ + "added", + "broadened", + "enabled", + "narrowed", + "removed" + ], + "title": "CapabilityChangeBlock", + "type": "object" + }, + "CapabilityChangeMember": { + "additionalProperties": false, + "description": "One capability member in a base\u2192head delta.\n\nCarries enough to identify the capability \u2014 ``tool`` plus the\n``action`` and ``scope`` it concerns \u2014 and, for ``broadened`` /\n``narrowed`` directions, the ``before_scope`` / ``after_scope`` so a\nreviewer can see exactly how the scope moved. Membership changes\n(``added`` / ``removed``) leave the before/after scope fields ``None``.\n\nA deterministic ``id`` supplied by the capability-change builder keeps\nthe member stable across runs for byte-equivalent output.", + "properties": { + "action": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Action" + }, + "after_capability_id": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "After Capability Id" + }, + "after_scope": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "After Scope" + }, + "before_capability_id": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Before Capability Id" + }, + "before_scope": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Before Scope" + }, + "changed_hashes": { + "items": { + "enum": [ + "identity_hash", + "binding_hash", + "effect_hash", + "authority_hash", + "control_hash", + "schema_hash", + "risk_hash", + "evidence_hash" + ], + "type": "string" + }, + "title": "Changed Hashes", + "type": "array" + }, + "confidence": { + "default": "medium", + "enum": [ + "low", + "medium", + "high" + ], + "title": "Confidence", + "type": "string" + }, + "direction": { + "enum": [ + "added", + "removed", + "broadened", + "narrowed" + ], + "title": "Direction", + "type": "string" + }, + "id": { + "title": "Id", + "type": "string" + }, + "provenance_kind": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Provenance Kind" + }, + "rationale": { + "default": "", + "title": "Rationale", + "type": "string" + }, + "related_finding_ids": { + "items": { + "type": "string" + }, + "title": "Related Finding Ids", + "type": "array" + }, + "release_impact": { + "default": "informational", + "enum": [ + "none", + "informational", + "review_required", + "blocks_release", + "insufficient_evidence" + ], + "title": "Release Impact", + "type": "string" + }, + "risk_tags": { + "items": { + "type": "string" + }, + "title": "Risk Tags", + "type": "array" + }, + "scope": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Scope" + }, + "semantic_changes": { + "items": { + "$ref": "#/$defs/CapabilitySemanticChange" + }, + "title": "Semantic Changes", + "type": "array" + }, + "semantic_direction": { + "default": "unknown", + "enum": [ + "added", + "removed", + "broadened", + "narrowed", + "mixed", + "unknown", + "evidence_only" + ], + "title": "Semantic Direction", + "type": "string" + }, + "subject_kind": { + "default": "unknown", + "enum": [ + "tool", + "action", + "scope", + "policy", + "ci", + "baseline", + "agent_instruction", + "manifest", + "unknown" + ], + "title": "Subject Kind", + "type": "string" + }, + "tool": { + "title": "Tool", + "type": "string" + } + }, + "required": [ + "action", + "after_scope", + "before_scope", + "confidence", + "direction", + "id", + "provenance_kind", + "rationale", + "related_finding_ids", + "release_impact", + "risk_tags", + "scope", + "subject_kind", + "tool" + ], + "title": "CapabilityChangeMember", + "type": "object" + }, + "CapabilityFact": { + "properties": { + "auth_scopes": { + "items": { + "type": "string" + }, + "title": "Auth Scopes", + "type": "array" + }, + "capability": { + "title": "Capability", + "type": "string" + }, + "control_status": { + "enum": [ + "missing", + "partial", + "present", + "unknown" + ], + "title": "Control Status", + "type": "string" + }, + "effect": { + "anyOf": [ + { + "enum": [ + "read", + "write", + "destructive", + "external_communication", + "financial_write", + "production_operation", + "privileged_data_access", + "code_execution", + "identity_access" + ], + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Effect" + }, + "id": { + "title": "Id", + "type": "string" + }, + "included_reason": { + "enum": [ + "high_risk_tag", + "wildcard_exposure", + "referenced_by_critical_finding", + "referenced_by_high_finding", + "referenced_by_medium_finding" + ], + "title": "Included Reason", + "type": "string" + }, + "owner": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Owner" + }, + "related_findings": { + "items": { + "type": "string" + }, + "title": "Related Findings", + "type": "array" + }, + "risk_tags": { + "items": { + "type": "string" + }, + "title": "Risk Tags", + "type": "array" + }, + "semantic_assessment": { + "anyOf": [ + { + "$ref": "#/$defs/ToolSemanticEvidence" + }, + { + "type": "null" + } + ], + "default": null + }, + "source_id": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Source Id" + }, + "source_ref": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Source Ref" + }, + "source_type": { + "title": "Source Type", + "type": "string" + }, + "tool_id": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Tool Id" + }, + "tool_name": { + "title": "Tool Name", + "type": "string" + } + }, + "required": [ + "auth_scopes", + "capability", + "control_status", + "id", + "included_reason", + "owner", + "related_findings", + "risk_tags", + "source_ref", + "source_type", + "tool_name" + ], + "title": "CapabilityFact", + "type": "object" + }, + "CapabilityPolicyEvidence": { + "additionalProperties": false, + "description": "Capability-level audit evidence for a policy match.\n\nThis is explanatory metadata only. It lets reviewers see which durable\ncapability fact matched a policy rule without folding that metadata into\nthe legacy ``Finding.evidence`` fingerprint input.", + "properties": { + "authority": { + "additionalProperties": true, + "title": "Authority", + "type": "object" + }, + "capability_id": { + "title": "Capability Id", + "type": "string" + }, + "controls": { + "additionalProperties": true, + "title": "Controls", + "type": "object" + }, + "effect": { + "additionalProperties": true, + "title": "Effect", + "type": "object" + }, + "hashes": { + "additionalProperties": { + "type": "string" + }, + "title": "Hashes", + "type": "object" + }, + "identity": { + "additionalProperties": true, + "title": "Identity", + "type": "object" + }, + "matched_predicates": { + "additionalProperties": true, + "title": "Matched Predicates", + "type": "object" + }, + "source": { + "anyOf": [ + { + "$ref": "#/$defs/SourceReference" + }, + { + "type": "null" + } + ], + "default": null + } + }, + "required": [ + "capability_id", + "identity", + "effect", + "authority", + "controls", + "hashes" + ], + "title": "CapabilityPolicyEvidence", + "type": "object" + }, + "CapabilityRuntimeEvidence": { + "additionalProperties": false, + "description": "Top-level audit block for declared local runtime trace artifacts.", + "properties": { + "enabled": { + "default": false, + "title": "Enabled", + "type": "boolean" + }, + "matched": { + "items": { + "$ref": "#/$defs/CapabilityTraceEvidenceV1" + }, + "title": "Matched", + "type": "array" + }, + "notes": { + "items": { + "type": "string" + }, + "title": "Notes", + "type": "array" + }, + "source_provenance": { + "items": { + "$ref": "#/$defs/SourceReference" + }, + "title": "Source Provenance", + "type": "array" + }, + "summary": { + "$ref": "#/$defs/CapabilityTraceEvidenceSummary" + }, + "unmatched": { + "items": { + "$ref": "#/$defs/CapabilityTraceEvidenceV1" + }, + "title": "Unmatched", + "type": "array" + } + }, + "title": "CapabilityRuntimeEvidence", + "type": "object" + }, + "CapabilitySemanticChange": { + "additionalProperties": false, + "description": "One semantic explanation for a capability delta.", + "properties": { + "after": { + "default": null, + "title": "After" + }, + "before": { + "default": null, + "title": "Before" + }, + "field": { + "title": "Field", + "type": "string" + }, + "kind": { + "title": "Kind", + "type": "string" + }, + "rationale": { + "title": "Rationale", + "type": "string" + } + }, + "required": [ + "kind", + "field", + "rationale" + ], + "title": "CapabilitySemanticChange", + "type": "object" + }, + "CapabilityTraceEvidenceSummary": { + "additionalProperties": false, + "description": "Deterministic counts for opt-in local runtime trace evidence.", + "properties": { + "agent_trace_count": { + "default": 0, + "title": "Agent Trace Count", + "type": "integer" + }, + "api_trace_count": { + "default": 0, + "title": "Api Trace Count", + "type": "integer" + }, + "approval_trace_count": { + "default": 0, + "title": "Approval Trace Count", + "type": "integer" + }, + "matched_trace_count": { + "default": 0, + "title": "Matched Trace Count", + "type": "integer" + }, + "source_count": { + "default": 0, + "title": "Source Count", + "type": "integer" + }, + "trace_count": { + "default": 0, + "title": "Trace Count", + "type": "integer" + }, + "unmatched_trace_count": { + "default": 0, + "title": "Unmatched Trace Count", + "type": "integer" + }, + "warning_count": { + "default": 0, + "title": "Warning Count", + "type": "integer" + } + }, + "title": "CapabilityTraceEvidenceSummary", + "type": "object" + }, + "CapabilityTraceEvidenceV1": { + "additionalProperties": false, + "description": "Allowlisted local trace event linked to a durable capability fact.\n\nRaw prompts, messages, tool arguments, outputs, and arbitrary payloads\nare intentionally absent. ``observed`` contains only normalized scalar\nevidence from the allowlist enforced by ``inputs.traces``.", + "properties": { + "capability_id": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Capability Id" + }, + "event_hash": { + "title": "Event Hash", + "type": "string" + }, + "id": { + "title": "Id", + "type": "string" + }, + "match_reason": { + "enum": [ + "capability_id", + "tool_name", + "unknown_tool", + "ambiguous_tool", + "invalid_capability_id", + "missing_tool_name" + ], + "title": "Match Reason", + "type": "string" + }, + "matched": { + "default": false, + "title": "Matched", + "type": "boolean" + }, + "matched_capability_id": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Matched Capability Id" + }, + "observed": { + "additionalProperties": true, + "title": "Observed", + "type": "object" + }, + "operation": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Operation" + }, + "provider": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Provider" + }, + "source": { + "anyOf": [ + { + "$ref": "#/$defs/SourceReference" + }, + { + "type": "null" + } + ], + "default": null + }, + "source_hash": { + "title": "Source Hash", + "type": "string" + }, + "source_type": { + "title": "Source Type", + "type": "string" + }, + "tool_name": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Tool Name" + } + }, + "required": [ + "id", + "source_type", + "match_reason", + "event_hash", + "source_hash" + ], + "title": "CapabilityTraceEvidenceV1", + "type": "object" + }, + "CodexPluginAppSummary": { + "additionalProperties": true, + "properties": { + "connector_id": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Connector Id" + }, + "location": { + "$ref": "#/$defs/CodexPluginSourceLocation" + }, + "name": { + "title": "Name", + "type": "string" + }, + "path": { + "title": "Path", + "type": "string" + }, + "plugin": { + "title": "Plugin", + "type": "string" + } + }, + "required": [ + "plugin", + "name", + "path" + ], + "title": "CodexPluginAppSummary", + "type": "object" + }, + "CodexPluginComponentPathIssue": { + "additionalProperties": true, + "properties": { + "component": { + "title": "Component", + "type": "string" + }, + "path": { + "title": "Path", + "type": "string" + }, + "plugin": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Plugin" + }, + "reason": { + "title": "Reason", + "type": "string" + }, + "source_ref": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Source Ref" + } + }, + "required": [ + "component", + "path", + "reason" + ], + "title": "CodexPluginComponentPathIssue", + "type": "object" + }, + "CodexPluginHookStub": { + "additionalProperties": true, + "properties": { + "command": { + "title": "Command", + "type": "string" + }, + "confidence": { + "default": "medium", + "enum": [ + "low", + "medium", + "high" + ], + "title": "Confidence", + "type": "string" + }, + "location": { + "$ref": "#/$defs/CodexPluginSourceLocation" + }, + "name": { + "title": "Name", + "type": "string" + }, + "path": { + "title": "Path", + "type": "string" + }, + "plugin": { + "title": "Plugin", + "type": "string" + }, + "risk_tags": { + "items": { + "type": "string" + }, + "title": "Risk Tags", + "type": "array" + } + }, + "required": [ + "plugin", + "name", + "command", + "path" + ], + "title": "CodexPluginHookStub", + "type": "object" + }, + "CodexPluginMarketplaceSummary": { + "additionalProperties": true, + "properties": { + "missing_policy_entries": { + "items": { + "additionalProperties": true, + "type": "object" + }, + "title": "Missing Policy Entries", + "type": "array" + }, + "name": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Name" + }, + "path": { + "title": "Path", + "type": "string" + }, + "plugin_count": { + "default": 0, + "title": "Plugin Count", + "type": "integer" + }, + "skipped_entries": { + "items": { + "additionalProperties": true, + "type": "object" + }, + "title": "Skipped Entries", + "type": "array" + }, + "source_id": { + "title": "Source Id", + "type": "string" + } + }, + "required": [ + "source_id", + "path" + ], + "title": "CodexPluginMarketplaceSummary", + "type": "object" + }, + "CodexPluginMcpServerStub": { + "additionalProperties": true, + "properties": { + "command": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Command" + }, + "inventory_loaded": { + "default": false, + "title": "Inventory Loaded", + "type": "boolean" + }, + "inventory_path": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Inventory Path" + }, + "location": { + "$ref": "#/$defs/CodexPluginSourceLocation" + }, + "path": { + "title": "Path", + "type": "string" + }, + "plugin": { + "title": "Plugin", + "type": "string" + }, + "server": { + "title": "Server", + "type": "string" + } + }, + "required": [ + "plugin", + "server", + "path" + ], + "title": "CodexPluginMcpServerStub", + "type": "object" + }, + "CodexPluginSkillSummary": { + "additionalProperties": true, + "properties": { + "description": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Description" + }, + "duplicate": { + "default": false, + "title": "Duplicate", + "type": "boolean" + }, + "location": { + "$ref": "#/$defs/CodexPluginSourceLocation" + }, + "missing_fields": { + "items": { + "type": "string" + }, + "title": "Missing Fields", + "type": "array" + }, + "name": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Name" + }, + "path": { + "title": "Path", + "type": "string" + }, + "plugin": { + "title": "Plugin", + "type": "string" + } + }, + "required": [ + "plugin", + "path" + ], + "title": "CodexPluginSkillSummary", + "type": "object" + }, + "CodexPluginSourceLocation": { + "additionalProperties": false, + "properties": { + "source_path": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Source Path" + }, + "source_pointer": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Source Pointer" + }, + "source_ref": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Source Ref" + }, + "source_start_column": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Source Start Column" + }, + "source_start_line": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Source Start Line" + } + }, + "title": "CodexPluginSourceLocation", + "type": "object" + }, + "CodexPluginSummary": { + "additionalProperties": true, + "properties": { + "description": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Description" + }, + "duplicate_name": { + "default": false, + "title": "Duplicate Name", + "type": "boolean" + }, + "duplicate_root": { + "default": false, + "title": "Duplicate Root", + "type": "boolean" + }, + "location": { + "$ref": "#/$defs/CodexPluginSourceLocation" + }, + "manifest_path": { + "title": "Manifest Path", + "type": "string" + }, + "marketplace": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Marketplace" + }, + "missing_fields": { + "items": { + "type": "string" + }, + "title": "Missing Fields", + "type": "array" + }, + "name": { + "title": "Name", + "type": "string" + }, + "name_mismatch": { + "default": false, + "title": "Name Mismatch", + "type": "boolean" + }, + "root_path": { + "title": "Root Path", + "type": "string" + }, + "source_id": { + "title": "Source Id", + "type": "string" + }, + "version": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Version" + } + }, + "required": [ + "source_id", + "name", + "root_path", + "manifest_path" + ], + "title": "CodexPluginSummary", + "type": "object" + }, + "CodexPluginSurface": { + "additionalProperties": true, + "properties": { + "app_count": { + "default": 0, + "title": "App Count", + "type": "integer" + }, + "apps": { + "items": { + "$ref": "#/$defs/CodexPluginAppSummary" + }, + "title": "Apps", + "type": "array" + }, + "component_path_issues": { + "items": { + "$ref": "#/$defs/CodexPluginComponentPathIssue" + }, + "title": "Component Path Issues", + "type": "array" + }, + "hook_stub_count": { + "default": 0, + "title": "Hook Stub Count", + "type": "integer" + }, + "hook_stubs": { + "items": { + "$ref": "#/$defs/CodexPluginHookStub" + }, + "title": "Hook Stubs", + "type": "array" + }, + "marketplace_count": { + "default": 0, + "title": "Marketplace Count", + "type": "integer" + }, + "marketplaces": { + "items": { + "$ref": "#/$defs/CodexPluginMarketplaceSummary" + }, + "title": "Marketplaces", + "type": "array" + }, + "mcp_inventory_file_count": { + "default": 0, + "title": "Mcp Inventory File Count", + "type": "integer" + }, + "mcp_inventory_files": { + "items": { + "type": "string" + }, + "title": "Mcp Inventory Files", + "type": "array" + }, + "mcp_server_stub_count": { + "default": 0, + "title": "Mcp Server Stub Count", + "type": "integer" + }, + "mcp_server_stubs": { + "items": { + "$ref": "#/$defs/CodexPluginMcpServerStub" + }, + "title": "Mcp Server Stubs", + "type": "array" + }, + "plugin_count": { + "default": 0, + "title": "Plugin Count", + "type": "integer" + }, + "plugins": { + "items": { + "$ref": "#/$defs/CodexPluginSummary" + }, + "title": "Plugins", + "type": "array" + }, + "skill_count": { + "default": 0, + "title": "Skill Count", + "type": "integer" + }, + "skills": { + "items": { + "$ref": "#/$defs/CodexPluginSkillSummary" + }, + "title": "Skills", + "type": "array" + }, + "warnings": { + "items": { + "type": "string" + }, + "title": "Warnings", + "type": "array" + } + }, + "title": "CodexPluginSurface", + "type": "object" + }, + "ContributionRule": { + "additionalProperties": false, + "description": "Per-finding audit row explaining how a finding contributed to the\nrelease decision.\n\nAdditive in v0.17. Every finding in `report.findings` produces\nexactly one ContributionRule. Reading the contribution rule is\nsufficient to predict the gate outcome for that finding without\nre-deriving the decision logic; the set of valid `(rule, category)`\npairs is the contract documented in STABILITY.md \"Release decision\ntruth table\".", + "properties": { + "category": { + "enum": [ + "blocker", + "review_item", + "excluded" + ], + "title": "Category", + "type": "string" + }, + "check_id": { + "title": "Check Id", + "type": "string" + }, + "finding_id": { + "title": "Finding Id", + "type": "string" + }, + "fingerprint": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Fingerprint" + }, + "rationale": { + "title": "Rationale", + "type": "string" + }, + "rule": { + "enum": [ + "policy_block_new", + "severity_block_new", + "policy_baseline_accepted", + "severity_baseline_accepted", + "review_required", + "sub_threshold", + "unsupported_evidence", + "suppressed" + ], + "title": "Rule", + "type": "string" + } + }, + "required": [ + "category", + "check_id", + "finding_id", + "fingerprint", + "rationale", + "rule" + ], + "title": "ContributionRule", + "type": "object" + }, + "DeclaredIntention": { + "properties": { + "id": { + "title": "Id", + "type": "string" + }, + "intent_tags": { + "items": { + "type": "string" + }, + "title": "Intent Tags", + "type": "array" + }, + "kind": { + "enum": [ + "declared_purpose", + "prohibited_action", + "instruction_preview" + ], + "title": "Kind", + "type": "string" + }, + "source": { + "title": "Source", + "type": "string" + }, + "text": { + "title": "Text", + "type": "string" + } + }, + "required": [ + "id", + "intent_tags", + "kind", + "source", + "text" + ], + "title": "DeclaredIntention", + "type": "object" + }, + "EffectSemanticEvidence": { + "additionalProperties": false, + "properties": { + "claims": { + "items": { + "$ref": "#/$defs/SemanticClaimEvidence" + }, + "title": "Claims", + "type": "array" + }, + "confidence": { + "enum": [ + "low", + "medium", + "high" + ], + "title": "Confidence", + "type": "string" + }, + "issues": { + "items": { + "$ref": "#/$defs/SemanticIssueEvidence" + }, + "title": "Issues", + "type": "array" + }, + "status": { + "enum": [ + "declared", + "structural", + "protocol_default", + "inferred", + "unknown", + "conflicting" + ], + "title": "Status", + "type": "string" + } + }, + "required": [ + "status", + "confidence" + ], + "title": "EffectSemanticEvidence", + "type": "object" + }, + "EffectivePolicy": { + "additionalProperties": false, + "description": "Normalized effective-policy snapshot block (roadmap \u00a75.3).\n\nA semantic \u2014 not text \u2014 view of the release policy surface, so the\nbase-vs-head comparator can answer \"was the gate weakened?\" without a\ntext diff. Every field is derived deterministically from the manifest\n(plus, for ``baseline_fingerprints``, the accepted-debt findings), and\nevery list/dict is emitted in sorted order so two builds of the same\nmanifest serialize byte-identically.\n\nFields are chosen so the weakening / expansion classifiers can answer\nthe \u00a75.3 questions by set/rank comparison rather than counting:\n\n- ``ci_mode`` \u2014 was the CI gate weakened (strict -> advisory)?\n- ``fail_on`` \u2014 was the fail-on severity set loosened (head \u2282 base)?\n- ``severity_overrides`` \u2014 was a check's severity lowered across a tier?\n- ``suppressed_check_ids`` \u2014 was a blocking check newly suppressed?\n- ``waiver_scopes`` \u2014 was a waiver scope expanded (head \u228b base)?\n- ``baseline_fingerprints`` \u2014 was the accepted-debt baseline expanded?\n- ``baseline_integrity_mode`` \u2014 was baseline tamper-checking weakened?\n- ``ci_gate_present`` \u2014 was Shipgate CI removed from an opted-in repo?", + "properties": { + "baseline_fingerprints": { + "items": { + "type": "string" + }, + "title": "Baseline Fingerprints", + "type": "array" + }, + "baseline_integrity_mode": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Baseline Integrity Mode" + }, + "ci_gate_present": { + "default": true, + "title": "Ci Gate Present", + "type": "boolean" + }, + "ci_mode": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Ci Mode" + }, + "fail_on": { + "items": { + "type": "string" + }, + "title": "Fail On", + "type": "array" + }, + "severity_overrides": { + "additionalProperties": { + "type": "string" + }, + "title": "Severity Overrides", + "type": "object" + }, + "suppressed_check_ids": { + "items": { + "type": "string" + }, + "title": "Suppressed Check Ids", + "type": "array" + }, + "waiver_scopes": { + "items": { + "type": "string" + }, + "title": "Waiver Scopes", + "type": "array" + } + }, + "required": [ + "baseline_fingerprints", + "baseline_integrity_mode", + "ci_gate_present", + "ci_mode", + "fail_on", + "severity_overrides", + "suppressed_check_ids", + "waiver_scopes" + ], + "title": "EffectivePolicy", + "type": "object" + }, + "EvidenceCoverageDecision": { + "properties": { + "binding_coverage": { + "$ref": "#/$defs/BindingCoverageDecision" + }, + "evidence_gaps": { + "items": { + "$ref": "#/$defs/EvidenceGap" + }, + "title": "Evidence Gaps", + "type": "array" + }, + "human_review_recommended": { + "title": "Human Review Recommended", + "type": "boolean" + }, + "identity_coverage": { + "$ref": "#/$defs/IdentityCoverageDecision" + }, + "level": { + "title": "Level", + "type": "string" + }, + "low_confidence_tool_count": { + "title": "Low Confidence Tool Count", + "type": "integer" + }, + "policy_gap_count": { + "default": 0, + "title": "Policy Gap Count", + "type": "integer" + }, + "semantic_coverage": { + "$ref": "#/$defs/SemanticCoverageDecision" + }, + "source_warning_count": { + "title": "Source Warning Count", + "type": "integer" + } + }, + "required": [ + "human_review_recommended", + "level", + "low_confidence_tool_count", + "source_warning_count" + ], + "title": "EvidenceCoverageDecision", + "type": "object" + }, + "EvidenceGap": { + "description": "v0.26: one structured row per measurable evidence gap.\n\n``insufficient_evidence`` previously diagnosed without prescribing;\neach gap names the degraded subject and the specific next action\nthat raises extraction confidence. Purely explanatory \u2014 gating\nstill uses only the counts (the gap list is a projection of them).", + "properties": { + "kind": { + "enum": [ + "low_confidence_tool", + "source_warning", + "incomplete_surface", + "missing_effect_evidence", + "inferred_effect_only", + "conflicting_effect_evidence", + "missing_authority_evidence", + "partial_authority_evidence", + "conflicting_authority_evidence", + "invalid_semantic_annotation", + "incomplete_tool_identity", + "conflicting_tool_identity", + "unresolved_tool_selector", + "ambiguous_tool_selector", + "ambiguous_legacy_tool_identity", + "invalid_tool_binding", + "missing_binding_evidence", + "partial_binding_evidence", + "conflicting_binding_evidence", + "ambiguous_root_agent", + "unresolved_agent_binding", + "unresolved_bound_tool", + "incomplete_handoff_graph", + "invalid_binding_annotation", + "invalid_evidence_provenance", + "inferred_policy_applicability", + "mixed_policy_evidence", + "unknown_policy_evidence", + "conflicting_policy_evidence" + ], + "title": "Kind", + "type": "string" + }, + "next_action": { + "$ref": "#/$defs/EvidenceGapAction" + }, + "source_ref": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Source Ref" + }, + "source_type": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Source Type" + }, + "subject": { + "title": "Subject", + "type": "string" + }, + "why": { + "title": "Why", + "type": "string" + } + }, + "required": [ + "kind", + "subject", + "why", + "next_action" + ], + "title": "EvidenceGap", + "type": "object" + }, + "EvidenceGapAction": { + "description": "One concrete, mechanically-executable step that closes a gap.\n\nMirrors the agent-mode ``next_actions[]`` error shape\n(``kind``/``command``/``path``/``why``/``expects``) so agents reuse\none routing vocabulary across error recovery and evidence repair.", + "properties": { + "accepted_values": { + "items": { + "type": "string" + }, + "title": "Accepted Values", + "type": "array" + }, + "auto_apply": { + "const": false, + "default": false, + "title": "Auto Apply", + "type": "boolean" + }, + "command": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Command" + }, + "declaration_template": { + "anyOf": [ + { + "additionalProperties": true, + "type": "object" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Declaration Template" + }, + "expects": { + "title": "Expects", + "type": "string" + }, + "kind": { + "enum": [ + "declare_tool_inventory", + "provide_source", + "review_warning", + "declare_action_effect", + "declare_action_authority", + "provide_complete_inventory", + "resolve_semantic_conflict", + "declare_source_identity", + "qualify_tool_selector", + "provide_tool_binding", + "resolve_tool_identity_conflict", + "regenerate_identity_artifact", + "declare_agent_root", + "declare_agent_bindings", + "provide_static_binding_source", + "provide_complete_binding_graph", + "resolve_binding_conflict", + "regenerate_binding_artifact", + "provide_policy_evidence", + "review_policy_evidence", + "resolve_policy_evidence_conflict" + ], + "title": "Kind", + "type": "string" + }, + "path": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Path" + }, + "requires_human_review": { + "const": true, + "default": true, + "title": "Requires Human Review", + "type": "boolean" + }, + "suggested_patch_kind": { + "const": "manual", + "default": "manual", + "title": "Suggested Patch Kind", + "type": "string" + }, + "why": { + "title": "Why", + "type": "string" + } + }, + "required": [ + "kind", + "why", + "expects" + ], + "title": "EvidenceGapAction", + "type": "object" + }, + "FailPolicy": { + "properties": { + "ci_mode": { + "title": "Ci Mode", + "type": "string" + }, + "exit_code": { + "title": "Exit Code", + "type": "integer" + }, + "fail_on": { + "items": { + "enum": [ + "info", + "low", + "medium", + "high", + "critical" + ], + "type": "string" + }, + "title": "Fail On", + "type": "array" + }, + "new_findings_only": { + "default": false, + "title": "New Findings Only", + "type": "boolean" + }, + "would_fail_ci": { + "title": "Would Fail Ci", + "type": "boolean" + } + }, + "required": [ + "ci_mode", + "exit_code", + "fail_on", + "new_findings_only", + "would_fail_ci" + ], + "title": "FailPolicy", + "type": "object" + }, + "Finding": { + "additionalProperties": true, + "properties": { + "agent_action": { + "enum": [ + "auto_apply", + "propose_patch_for_review", + "escalate_to_human", + "suppress_with_reason", + "informational" + ], + "type": "string" + }, + "agent_id": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Agent Id" + }, + "autofix_safe": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Autofix Safe" + }, + "baseline_status": { + "anyOf": [ + { + "enum": [ + "new", + "matched", + "resolved" + ], + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Baseline Status" + }, + "blocks_release": { + "default": false, + "title": "Blocks Release", + "type": "boolean" + }, + "capability_policy_evidence": { + "anyOf": [ + { + "$ref": "#/$defs/CapabilityPolicyEvidence" + }, + { + "type": "null" + } + ], + "default": null + }, + "capability_refs": { + "items": { + "type": "string" + }, + "title": "Capability Refs", + "type": "array" + }, + "capability_trace_refs": { + "items": { + "type": "string" + }, + "title": "Capability Trace Refs", + "type": "array" + }, + "category": { + "title": "Category", + "type": "string" + }, + "check_id": { + "title": "Check Id", + "type": "string" + }, + "confidence": { + "default": "medium", + "enum": [ + "low", + "medium", + "high" + ], + "title": "Confidence", + "type": "string" + }, + "docs_url": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Docs Url" + }, + "evidence": { + "additionalProperties": true, + "title": "Evidence", + "type": "object" + }, + "fingerprint": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Fingerprint" + }, + "id": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Id" + }, + "patches": { + "anyOf": [ + { + "items": { + "discriminator": { + "mapping": { + "append_pointer": "#/$defs/AppendPointerPatch", + "manual": "#/$defs/ManualPatch", + "remove_pointer": "#/$defs/RemovePointerPatch", + "set_pointer": "#/$defs/SetPointerPatch" + }, + "propertyName": "kind" + }, + "oneOf": [ + { + "$ref": "#/$defs/SetPointerPatch" + }, + { + "$ref": "#/$defs/AppendPointerPatch" + }, + { + "$ref": "#/$defs/RemovePointerPatch" + }, + { + "$ref": "#/$defs/ManualPatch" + } + ] + }, + "type": "array" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Patches" + }, + "policy_evidence_source": { + "anyOf": [ + { + "$ref": "#/$defs/SourceReference" + }, + { + "type": "null" + } + ], + "default": null + }, + "policy_routing": { + "anyOf": [ + { + "$ref": "#/$defs/PolicyRoutingMetadata" + }, + { + "type": "null" + } + ], + "default": null + }, + "provenance_kind": { + "enum": [ + "static_declaration", + "ast_extraction", + "keyword_heuristic", + "regex_heuristic", + "policy_pack", + "runtime_trace" + ], + "type": "string" + }, + "recommendation": { + "title": "Recommendation", + "type": "string" + }, + "requires_human_review": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Requires Human Review" + }, + "severity": { + "enum": [ + "info", + "low", + "medium", + "high", + "critical" + ], + "title": "Severity", + "type": "string" + }, + "source": { + "anyOf": [ + { + "$ref": "#/$defs/SourceReference" + }, + { + "type": "null" + } + ], + "default": null + }, + "suggested_patch_kind": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Suggested Patch Kind" + }, + "support": { + "anyOf": [ + { + "$ref": "#/$defs/FindingSupport" + }, + { + "type": "null" + } + ], + "default": null + }, + "suppressed": { + "default": false, + "title": "Suppressed", + "type": "boolean" + }, + "suppression_reason": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Suppression Reason" + }, + "title": { + "title": "Title", + "type": "string" + }, + "tool_id": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Tool Id" + }, + "tool_name": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Tool Name" + } + }, + "required": [ + "agent_action", + "baseline_status", + "blocks_release", + "capability_refs", + "category", + "check_id", + "confidence", + "evidence", + "fingerprint", + "id", + "provenance_kind", + "recommendation", + "severity", + "suppressed", + "title" + ], + "title": "Finding", + "type": "object" + }, + "FindingSupport": { + "additionalProperties": false, + "description": "Authoritative support for finding confidence and release contribution.\n\nRule metadata may request a severity or block, but it cannot upgrade the\nunderlying evidence. ``support_hash`` binds baselines and audit surfaces\nto the predicate evidence that actually made the finding eligible.", + "properties": { + "blocking_eligible": { + "default": false, + "title": "Blocking Eligible", + "type": "boolean" + }, + "claim_ids": { + "items": { + "type": "string" + }, + "title": "Claim Ids", + "type": "array" + }, + "confidence": { + "default": "low", + "enum": [ + "low", + "medium", + "high" + ], + "title": "Confidence", + "type": "string" + }, + "evidence_bases": { + "items": { + "enum": [ + "reviewed_declaration", + "protocol_structure", + "typed_provider_fact", + "structural_scope", + "inferred_keyword", + "inferred_regex", + "protocol_default", + "unknown" + ], + "type": "string" + }, + "title": "Evidence Bases", + "type": "array" + }, + "policy_eligible": { + "default": false, + "title": "Policy Eligible", + "type": "boolean" + }, + "predicates": { + "items": { + "$ref": "#/$defs/PolicyPredicateEvidence" + }, + "title": "Predicates", + "type": "array" + }, + "status": { + "default": "matched", + "enum": [ + "matched", + "not_matched", + "indeterminate", + "conflicting" + ], + "title": "Status", + "type": "string" + }, + "support_hash": { + "title": "Support Hash", + "type": "string" + } + }, + "required": [ + "support_hash" + ], + "title": "FindingSupport", + "type": "object" + }, + "HeuristicsFilter": { + "additionalProperties": false, + "description": "v0.21: top-level envelope describing the ``--no-heuristics``\nfilter pass.\n\nEmitted on every report regardless of whether the flag was set, so\nconsumers always read the same shape. When the flag is unset,\n``enabled=False`` and the count fields are zero \u2014 the active\nfinding set is unchanged. When the flag is set, every finding whose\n``provenance_kind`` is in ``excluded_provenance_kinds`` is marked\n``suppressed=True`` with ``suppression_reason=\"filtered by\n--no-heuristics\"`` BEFORE the release decision is built, so heuristic\nfindings can no longer gate release. Filtered findings stay in\n``findings[]`` for transparency; the audit envelope here records\naggregate counts.\n\nEarns the contract weight of ``Finding.provenance_kind`` (shipped\nv0.15) by giving it a first-class consumer. Same shape pattern as\n``PrivacyAudit``: an envelope that proves the filter ran and tells\na reviewer/agent which findings were excluded and why.", + "properties": { + "enabled": { + "default": false, + "title": "Enabled", + "type": "boolean" + }, + "excluded_provenance_kinds": { + "items": { + "type": "string" + }, + "title": "Excluded Provenance Kinds", + "type": "array" + }, + "filtered_by_kind": { + "additionalProperties": { + "type": "integer" + }, + "title": "Filtered By Kind", + "type": "object" + }, + "filtered_finding_count": { + "default": 0, + "title": "Filtered Finding Count", + "type": "integer" + } + }, + "required": [ + "enabled", + "excluded_provenance_kinds", + "filtered_by_kind", + "filtered_finding_count" + ], + "title": "HeuristicsFilter", + "type": "object" + }, + "HumanAck": { + "additionalProperties": false, + "description": "Human-acknowledgement state block (Decision 4 shape).\n\n``required`` is set by the human-ack builder when a trust-root\nweakening finding needs declared human authority; ``satisfied`` is\n``True`` when every required acknowledgement is present. ``acks``\nlists the declared entries; ``outstanding`` lists the surfaces still\nneeding one. The default empty shape is not required and therefore\nsatisfied.", + "properties": { + "acks": { + "items": { + "$ref": "#/$defs/HumanAckEntry" + }, + "title": "Acks", + "type": "array" + }, + "outstanding": { + "items": { + "type": "string" + }, + "title": "Outstanding", + "type": "array" + }, + "required": { + "default": false, + "title": "Required", + "type": "boolean" + }, + "satisfied": { + "default": true, + "title": "Satisfied", + "type": "boolean" + } + }, + "required": [ + "acks", + "outstanding", + "required", + "satisfied" + ], + "title": "HumanAck", + "type": "object" + }, + "HumanAckEntry": { + "additionalProperties": false, + "description": "One declared human-acknowledgement record.\n\nWithin the static boundary acknowledgement can only be *declared*\nevidence (roadmap \u00a75.4) \u2014 never inferred. An entry names the human\n``owner``, the ``reason``, an optional ``expires`` date (copied\nverbatim from the source, never generated), and the\n``affected_surface`` the ack covers.", + "properties": { + "affected_surface": { + "title": "Affected Surface", + "type": "string" + }, + "expires": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Expires" + }, + "owner": { + "title": "Owner", + "type": "string" + }, + "reason": { + "title": "Reason", + "type": "string" + }, + "source": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Source" + } + }, + "required": [ + "affected_surface", + "expires", + "owner", + "reason", + "source" + ], + "title": "HumanAckEntry", + "type": "object" + }, + "IdentityCoverageDecision": { + "properties": { + "ambiguous_name_count": { + "default": 0, + "title": "Ambiguous Name Count", + "type": "integer" + }, + "bound_tools": { + "default": 0, + "title": "Bound Tools", + "type": "integer" + }, + "canonical_tools": { + "default": 0, + "title": "Canonical Tools", + "type": "integer" + }, + "gap_count": { + "default": 0, + "title": "Gap Count", + "type": "integer" + }, + "pass_eligible_tools": { + "default": 0, + "title": "Pass Eligible Tools", + "type": "integer" + }, + "reason_counts": { + "additionalProperties": { + "type": "integer" + }, + "title": "Reason Counts", + "type": "object" + }, + "total_observations": { + "default": 0, + "title": "Total Observations", + "type": "integer" + } + }, + "title": "IdentityCoverageDecision", + "type": "object" + }, + "LoadedPolicyPack": { + "properties": { + "id": { + "title": "Id", + "type": "string" + }, + "name": { + "title": "Name", + "type": "string" + }, + "owner": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Owner" + }, + "path": { + "title": "Path", + "type": "string" + }, + "rule_count": { + "title": "Rule Count", + "type": "integer" + }, + "sha256": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Sha256" + }, + "sha256_status": { + "default": "unpinned", + "enum": [ + "unpinned", + "verified" + ], + "title": "Sha256 Status", + "type": "string" + }, + "source": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Source" + }, + "version": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Version" + } + }, + "required": [ + "id", + "name", + "path", + "rule_count" + ], + "title": "LoadedPolicyPack", + "type": "object" + }, + "ManualPatch": { + "additionalProperties": false, + "description": "No machine-applicable change. Carries human-readable instructions.\n\nUsed for every finding whose check ID has no v0.6 non-manual generator\nand for findings (like trace flips, per C6) that are intentionally\nnever auto-patched.", + "properties": { + "instructions": { + "title": "Instructions", + "type": "string" + }, + "kind": { + "const": "manual", + "default": "manual", + "title": "Kind", + "type": "string" + } + }, + "required": [ + "instructions" + ], + "title": "ManualPatch", + "type": "object" + }, + "Misalignment": { + "properties": { + "capability_refs": { + "items": { + "type": "string" + }, + "title": "Capability Refs", + "type": "array" + }, + "finding_refs": { + "items": { + "type": "string" + }, + "title": "Finding Refs", + "type": "array" + }, + "gap": { + "title": "Gap", + "type": "string" + }, + "id": { + "title": "Id", + "type": "string" + }, + "intention_refs": { + "items": { + "type": "string" + }, + "title": "Intention Refs", + "type": "array" + }, + "kind": { + "enum": [ + "policy_gap", + "scope_drift", + "prohibited_action_present", + "control_missing", + "intent_mismatch", + "undetected_gap" + ], + "title": "Kind", + "type": "string" + }, + "policy_requirement": { + "title": "Policy Requirement", + "type": "string" + }, + "release_implication": { + "title": "Release Implication", + "type": "string" + }, + "severity": { + "enum": [ + "info", + "low", + "medium", + "high", + "critical" + ], + "title": "Severity", + "type": "string" + }, + "tool_name": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Tool Name" + } + }, + "required": [ + "capability_refs", + "finding_refs", + "gap", + "id", + "intention_refs", + "kind", + "policy_requirement", + "release_implication", + "severity", + "tool_name" + ], + "title": "Misalignment", + "type": "object" + }, + "PolicyApprovalRouting": { + "additionalProperties": false, + "description": "Non-enforcing approval-routing metadata from a policy-pack rule.", + "properties": { + "enforced": { + "const": false, + "default": false, + "title": "Enforced", + "type": "boolean" + }, + "min_approvals": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Min Approvals" + }, + "required": { + "default": false, + "title": "Required", + "type": "boolean" + }, + "teams": { + "items": { + "type": "string" + }, + "title": "Teams", + "type": "array" + } + }, + "title": "PolicyApprovalRouting", + "type": "object" + }, + "PolicyAudit": { + "additionalProperties": false, + "description": "v0.17 (M1) top-of-report audit envelope for policy decisions\napplied during scan.\n\nCarries severity-override audit today; M2 (baseline integrity) and\nM5 (plugin validation) will land sibling fields here so the audit\nenvelope stays stable across the trust-hardening releases.", + "properties": { + "severity_overrides_applied": { + "items": { + "$ref": "#/$defs/SeverityOverrideAuditEntry" + }, + "title": "Severity Overrides Applied", + "type": "array" + } + }, + "title": "PolicyAudit", + "type": "object" + }, + "PolicyPredicateEvidence": { + "additionalProperties": false, + "description": "One tri-state policy predicate and the evidence that supports it.", + "properties": { + "claim_ids": { + "items": { + "type": "string" + }, + "title": "Claim Ids", + "type": "array" + }, + "confidence": { + "default": "low", + "enum": [ + "low", + "medium", + "high" + ], + "title": "Confidence", + "type": "string" + }, + "evidence_bases": { + "items": { + "enum": [ + "reviewed_declaration", + "protocol_structure", + "typed_provider_fact", + "structural_scope", + "inferred_keyword", + "inferred_regex", + "protocol_default", + "unknown" + ], + "type": "string" + }, + "title": "Evidence Bases", + "type": "array" + }, + "expected": { + "default": null, + "title": "Expected" + }, + "observed": { + "default": null, + "title": "Observed" + }, + "policy_eligible": { + "default": false, + "title": "Policy Eligible", + "type": "boolean" + }, + "predicate": { + "title": "Predicate", + "type": "string" + }, + "status": { + "enum": [ + "matched", + "not_matched", + "indeterminate", + "conflicting" + ], + "title": "Status", + "type": "string" + }, + "why": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Why" + } + }, + "required": [ + "predicate", + "status" + ], + "title": "PolicyPredicateEvidence", + "type": "object" + }, + "PolicyRoutingMetadata": { + "additionalProperties": false, + "description": "Reviewer routing metadata carried outside finding evidence.", + "properties": { + "approval": { + "$ref": "#/$defs/PolicyApprovalRouting" + }, + "owner": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Owner" + }, + "reviewers": { + "items": { + "type": "string" + }, + "title": "Reviewers", + "type": "array" + } + }, + "title": "PolicyRoutingMetadata", + "type": "object" + }, + "PrivacyAudit": { + "additionalProperties": false, + "description": "Top-level audit envelope proving the default redaction pass ran.", + "properties": { + "enabled": { + "default": true, + "title": "Enabled", + "type": "boolean" + }, + "notes": { + "items": { + "type": "string" + }, + "title": "Notes", + "type": "array" + }, + "output_surfaces": { + "items": { + "type": "string" + }, + "title": "Output Surfaces", + "type": "array" + }, + "redacted_occurrence_count": { + "default": 0, + "title": "Redacted Occurrence Count", + "type": "integer" + }, + "redacted_paths": { + "items": { + "$ref": "#/$defs/RedactedPathSummary" + }, + "title": "Redacted Paths", + "type": "array" + }, + "rules_version": { + "title": "Rules Version", + "type": "string" + }, + "sensitive_field_inventory_version": { + "title": "Sensitive Field Inventory Version", + "type": "string" + } + }, + "required": [ + "enabled", + "notes", + "output_surfaces", + "redacted_occurrence_count", + "redacted_paths", + "rules_version", + "sensitive_field_inventory_version" + ], + "title": "PrivacyAudit", + "type": "object" + }, + "ProtectedSurfaceChange": { + "additionalProperties": false, + "description": "One touched protected path / policy surface (trust root).\n\nTier A trust-root protection (roadmap \u00a75.1/\u00a75.2) records *which*\nprotected surface a PR touched. ``path`` is the changed file; ``kind``\nis the classifier bucket it matched (e.g. ``manifest``, ``ci_gate``,\n``policy``); ``glob`` is the matched pattern. ``related_finding_ids``\nlinks to the ordinary ``SHIP-VERIFY-*`` findings that gate.", + "properties": { + "glob": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Glob" + }, + "kind": { + "default": "unknown", + "title": "Kind", + "type": "string" + }, + "path": { + "title": "Path", + "type": "string" + }, + "related_finding_ids": { + "items": { + "type": "string" + }, + "title": "Related Finding Ids", + "type": "array" + } + }, + "required": [ + "glob", + "kind", + "path", + "related_finding_ids" + ], + "title": "ProtectedSurfaceChange", + "type": "object" + }, + "RedactedPathSummary": { + "additionalProperties": false, + "description": "One privacy-audit row for a structural output path.\n\nThe row intentionally carries only aggregate counts and secret kinds,\nnever the original value or a hash/verifier of that value.", + "properties": { + "count": { + "default": 0, + "title": "Count", + "type": "integer" + }, + "kinds": { + "items": { + "type": "string" + }, + "title": "Kinds", + "type": "array" + }, + "path": { + "title": "Path", + "type": "string" + } + }, + "required": [ + "count", + "kinds", + "path" + ], + "title": "RedactedPathSummary", + "type": "object" + }, + "ReleaseConsequence": { + "properties": { + "blocker_misalignment_count": { + "default": 0, + "title": "Blocker Misalignment Count", + "type": "integer" + }, + "decision": { + "enum": [ + "blocked", + "review_required", + "insufficient_evidence", + "passed" + ], + "title": "Decision", + "type": "string" + }, + "fail_policy": { + "$ref": "#/$defs/FailPolicy" + }, + "review_misalignment_count": { + "default": 0, + "title": "Review Misalignment Count", + "type": "integer" + }, + "summary": { + "title": "Summary", + "type": "string" + } + }, + "required": [ + "blocker_misalignment_count", + "decision", + "fail_policy", + "review_misalignment_count", + "summary" + ], + "title": "ReleaseConsequence", + "type": "object" + }, + "ReleaseDecision": { + "properties": { + "baseline_delta": { + "$ref": "#/$defs/BaselineDelta" + }, + "blockers": { + "items": { + "$ref": "#/$defs/ReleaseDecisionItem" + }, + "title": "Blockers", + "type": "array" + }, + "contribution_rules": { + "items": { + "$ref": "#/$defs/ContributionRule" + }, + "title": "Contribution Rules", + "type": "array" + }, + "decision": { + "enum": [ + "blocked", + "review_required", + "insufficient_evidence", + "passed" + ], + "title": "Decision", + "type": "string" + }, + "evidence_coverage": { + "$ref": "#/$defs/EvidenceCoverageDecision" + }, + "fail_policy": { + "$ref": "#/$defs/FailPolicy" + }, + "reason": { + "title": "Reason", + "type": "string" + }, + "review_items": { + "items": { + "$ref": "#/$defs/ReleaseDecisionItem" + }, + "title": "Review Items", + "type": "array" + }, + "runtime_behavior_verified": { + "const": false, + "default": false, + "title": "Runtime Behavior Verified", + "type": "boolean" + }, + "static_analysis_only": { + "const": true, + "default": true, + "title": "Static Analysis Only", + "type": "boolean" + }, + "static_verdict_disclaimer": { + "default": "This verdict covers deterministic static evidence only. Agents Shipgate did not execute the agent or prove runtime behavior, tool routing, credential enforcement, or safety.", + "title": "Static Verdict Disclaimer", + "type": "string" + } + }, + "required": [ + "baseline_delta", + "blockers", + "contribution_rules", + "decision", + "evidence_coverage", + "fail_policy", + "reason", + "review_items" + ], + "title": "ReleaseDecision", + "type": "object" + }, + "ReleaseDecisionItem": { + "properties": { + "baseline_status": { + "anyOf": [ + { + "enum": [ + "new", + "matched", + "resolved" + ], + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Baseline Status" + }, + "blocks_release": { + "default": false, + "title": "Blocks Release", + "type": "boolean" + }, + "capability_refs": { + "items": { + "type": "string" + }, + "title": "Capability Refs", + "type": "array" + }, + "capability_trace_refs": { + "items": { + "type": "string" + }, + "title": "Capability Trace Refs", + "type": "array" + }, + "check_id": { + "title": "Check Id", + "type": "string" + }, + "fingerprint": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Fingerprint" + }, + "id": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Id" + }, + "policy_evidence_source": { + "anyOf": [ + { + "$ref": "#/$defs/SourceReference" + }, + { + "type": "null" + } + ], + "default": null + }, + "severity": { + "enum": [ + "info", + "low", + "medium", + "high", + "critical" + ], + "title": "Severity", + "type": "string" + }, + "source": { + "anyOf": [ + { + "$ref": "#/$defs/SourceReference" + }, + { + "type": "null" + } + ], + "default": null + }, + "support": { + "anyOf": [ + { + "$ref": "#/$defs/FindingSupport" + }, + { + "type": "null" + } + ], + "default": null + }, + "title": { + "title": "Title", + "type": "string" + } + }, + "required": [ + "baseline_status", + "blocks_release", + "capability_refs", + "check_id", + "fingerprint", + "id", + "severity", + "title" + ], + "title": "ReleaseDecisionItem", + "type": "object" + }, + "RemovePointerPatch": { + "additionalProperties": false, + "description": "Remove the node at a JSON pointer.", + "properties": { + "confidence": { + "enum": [ + "low", + "medium", + "high" + ], + "title": "Confidence", + "type": "string" + }, + "kind": { + "const": "remove_pointer", + "default": "remove_pointer", + "title": "Kind", + "type": "string" + }, + "pointer": { + "title": "Pointer", + "type": "string" + }, + "rationale": { + "title": "Rationale", + "type": "string" + }, + "target_file": { + "title": "Target File", + "type": "string" + }, + "target_format": { + "enum": [ + "yaml", + "json" + ], + "title": "Target Format", + "type": "string" + }, + "target_sha256": { + "title": "Target Sha256", + "type": "string" + } + }, + "required": [ + "target_file", + "pointer", + "target_format", + "confidence", + "rationale", + "target_sha256" + ], + "title": "RemovePointerPatch", + "type": "object" + }, + "ReportSummary": { + "properties": { + "critical_count": { + "default": 0, + "title": "Critical Count", + "type": "integer" + }, + "evidence_coverage": { + "default": "static", + "title": "Evidence Coverage", + "type": "string" + }, + "high_count": { + "default": 0, + "title": "High Count", + "type": "integer" + }, + "human_review_recommended": { + "default": false, + "title": "Human Review Recommended", + "type": "boolean" + }, + "info_count": { + "default": 0, + "title": "Info Count", + "type": "integer" + }, + "low_count": { + "default": 0, + "title": "Low Count", + "type": "integer" + }, + "medium_count": { + "default": 0, + "title": "Medium Count", + "type": "integer" + }, + "status": { + "title": "Status", + "type": "string" + }, + "suppressed_count": { + "default": 0, + "title": "Suppressed Count", + "type": "integer" + } + }, + "required": [ + "status" + ], + "title": "ReportSummary", + "type": "object" + }, + "ReviewerSummary": { + "additionalProperties": false, + "description": "Top-level summary block shaped for one-fetch reviewer consumption.\n\nDeterministic projection of the reviewer lens surfaces\n(``tool_surface_diff``, capability/intent diff, ``action_surface_diff``,\nevidence matrix) and the audit envelopes (``policy_audit``,\n``privacy_audit``, baseline integrity findings). A reviewer who wants\nheadline activity counts and a recommended starting surface can read\nthis block instead of opening every lens / audit envelope.\n\nParallels ``AgentSummary`` but for the audit/lens dimensions:\n``AgentSummary`` answers \"what should an agent do next?\" and\n``ReviewerSummary`` answers \"what should a reviewer look at first?\".\n\nAll fields are derived; this block cannot disagree with the\nunderlying lens/audit data.", + "properties": { + "action_surface_changes": { + "default": 0, + "title": "Action Surface Changes", + "type": "integer" + }, + "baseline_integrity_issues": { + "default": 0, + "title": "Baseline Integrity Issues", + "type": "integer" + }, + "capability_misalignments": { + "default": 0, + "title": "Capability Misalignments", + "type": "integer" + }, + "evidence_matrix_gaps": { + "default": 0, + "title": "Evidence Matrix Gaps", + "type": "integer" + }, + "first_recommended_surface": { + "anyOf": [ + { + "$ref": "#/$defs/ReviewerSurfacePointer" + }, + { + "type": "null" + } + ], + "default": null + }, + "headline": { + "title": "Headline", + "type": "string" + }, + "privacy_redactions": { + "default": 0, + "title": "Privacy Redactions", + "type": "integer" + }, + "severity_overrides_applied": { + "default": 0, + "title": "Severity Overrides Applied", + "type": "integer" + }, + "severity_overrides_tier_crossed": { + "default": 0, + "title": "Severity Overrides Tier Crossed", + "type": "integer" + }, + "tool_surface_changes": { + "default": 0, + "title": "Tool Surface Changes", + "type": "integer" + }, + "verdict": { + "enum": [ + "blocked", + "review_required", + "insufficient_evidence", + "passed" + ], + "title": "Verdict", + "type": "string" + } + }, + "required": [ + "action_surface_changes", + "baseline_integrity_issues", + "capability_misalignments", + "evidence_matrix_gaps", + "first_recommended_surface", + "headline", + "privacy_redactions", + "severity_overrides_applied", + "severity_overrides_tier_crossed", + "tool_surface_changes", + "verdict" + ], + "title": "ReviewerSummary", + "type": "object" + }, + "ReviewerSurfacePointer": { + "additionalProperties": false, + "description": "A single recommended reviewer entry point.\n\nMirrors the ``next_actions[]`` shape (kind/path/why) used by other\ncontract surfaces so the same renderer pattern works here. ``kind``\nclassifies the surface family (``release_decision`` /``lens`` /\n``audit`` /``evidence_matrix``); ``name`` is the canonical\nmachine-readable identifier; ``path`` is a dotted JSON path the\nreviewer can use to navigate. ``why`` is one sentence of reviewer\nrationale, suitable for a PR comment lead.", + "properties": { + "kind": { + "enum": [ + "release_decision", + "lens", + "audit", + "evidence_matrix" + ], + "title": "Kind", + "type": "string" + }, + "name": { + "enum": [ + "tool_surface_diff", + "capability_intent_diff", + "action_surface_diff", + "evidence_matrix", + "policy_audit", + "privacy_audit", + "baseline_integrity", + "release_decision" + ], + "title": "Name", + "type": "string" + }, + "path": { + "title": "Path", + "type": "string" + }, + "why": { + "title": "Why", + "type": "string" + } + }, + "required": [ + "kind", + "name", + "path", + "why" + ], + "title": "ReviewerSurfacePointer", + "type": "object" + }, + "SemanticClaimEvidence": { + "additionalProperties": false, + "properties": { + "basis": { + "default": "unknown", + "enum": [ + "reviewed_declaration", + "protocol_structure", + "typed_provider_fact", + "structural_scope", + "inferred_keyword", + "inferred_regex", + "protocol_default", + "unknown" + ], + "title": "Basis", + "type": "string" + }, + "claim_id": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Claim Id" + }, + "confidence": { + "enum": [ + "low", + "medium", + "high" + ], + "title": "Confidence", + "type": "string" + }, + "dimension": { + "enum": [ + "identity", + "binding", + "effect", + "authority" + ], + "title": "Dimension", + "type": "string" + }, + "evidence": { + "additionalProperties": true, + "title": "Evidence", + "type": "object" + }, + "policy_eligible": { + "default": false, + "title": "Policy Eligible", + "type": "boolean" + }, + "provenance_kind": { + "enum": [ + "static_declaration", + "ast_extraction", + "keyword_heuristic", + "regex_heuristic", + "policy_pack", + "runtime_trace" + ], + "title": "Provenance Kind", + "type": "string" + }, + "source": { + "title": "Source", + "type": "string" + }, + "source_pointer": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Source Pointer" + }, + "value": { + "title": "Value", + "type": "string" + } + }, + "required": [ + "dimension", + "value", + "confidence", + "provenance_kind", + "source" + ], + "title": "SemanticClaimEvidence", + "type": "object" + }, + "SemanticCoverageDecision": { + "description": "v0.29 pass eligibility across the normalized action surface.\n\nUnlike extraction-confidence thresholds, semantic gaps are\nzero-tolerance: any non-pass-eligible unknown/partial/conflicting\ndimension prevents ``passed``. Known authority review concerns (for\nexample ambient or unscoped credentials) are counted separately so\nthey deterministically route to ``review_required`` rather than\n``insufficient_evidence``.", + "properties": { + "gap_count": { + "default": 0, + "title": "Gap Count", + "type": "integer" + }, + "pass_eligible_actions": { + "default": 0, + "title": "Pass Eligible Actions", + "type": "integer" + }, + "reason_counts": { + "additionalProperties": { + "type": "integer" + }, + "title": "Reason Counts", + "type": "object" + }, + "review_concern_count": { + "default": 0, + "title": "Review Concern Count", + "type": "integer" + }, + "total_actions": { + "default": 0, + "title": "Total Actions", + "type": "integer" + } + }, + "title": "SemanticCoverageDecision", + "type": "object" + }, + "SemanticIssueEvidence": { + "additionalProperties": false, + "properties": { + "dimension": { + "enum": [ + "identity", + "binding", + "effect", + "authority" + ], + "title": "Dimension", + "type": "string" + }, + "kind": { + "enum": [ + "incomplete_tool_identity", + "conflicting_tool_identity", + "unresolved_tool_selector", + "ambiguous_tool_selector", + "ambiguous_legacy_tool_identity", + "invalid_tool_binding", + "incomplete_surface", + "missing_effect_evidence", + "inferred_effect_only", + "conflicting_effect_evidence", + "missing_authority_evidence", + "partial_authority_evidence", + "conflicting_authority_evidence", + "invalid_semantic_annotation", + "missing_binding_evidence", + "partial_binding_evidence", + "conflicting_binding_evidence", + "ambiguous_root_agent", + "unresolved_agent_binding", + "unresolved_bound_tool", + "incomplete_handoff_graph", + "invalid_binding_annotation", + "invalid_evidence_provenance" + ], + "title": "Kind", + "type": "string" + }, + "message": { + "title": "Message", + "type": "string" + }, + "source": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Source" + }, + "source_pointer": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Source Pointer" + } + }, + "required": [ + "kind", + "dimension", + "message" + ], + "title": "SemanticIssueEvidence", + "type": "object" + }, + "SetPointerPatch": { + "additionalProperties": false, + "description": "Set the value at a JSON pointer inside a YAML or JSON file.", + "properties": { + "confidence": { + "enum": [ + "low", + "medium", + "high" + ], + "title": "Confidence", + "type": "string" + }, + "kind": { + "const": "set_pointer", + "default": "set_pointer", + "title": "Kind", + "type": "string" + }, + "pointer": { + "title": "Pointer", + "type": "string" + }, + "rationale": { + "title": "Rationale", + "type": "string" + }, + "target_file": { + "title": "Target File", + "type": "string" + }, + "target_format": { + "enum": [ + "yaml", + "json" + ], + "title": "Target Format", + "type": "string" + }, + "target_sha256": { + "title": "Target Sha256", + "type": "string" + }, + "value": { + "title": "Value" + } + }, + "required": [ + "target_file", + "pointer", + "value", + "target_format", + "confidence", + "rationale", + "target_sha256" + ], + "title": "SetPointerPatch", + "type": "object" + }, + "SeverityOverrideAuditEntry": { + "additionalProperties": false, + "description": "One row in ``ReadinessReport.policy_audit.severity_overrides_applied``.\n\nv0.17 (M1). Surfaces every manifest-driven severity override so a\nreviewer can see what was downgraded (or upgraded) without diving\ninto per-finding evidence. Emitted regardless of whether the override\nmatched any active finding \u2014 entries for checks that did not fire\nstill document reviewer intent.", + "properties": { + "applied_severity": { + "enum": [ + "info", + "low", + "medium", + "high", + "critical" + ], + "title": "Applied Severity", + "type": "string" + }, + "check_id": { + "title": "Check Id", + "type": "string" + }, + "default_severity": { + "enum": [ + "info", + "low", + "medium", + "high", + "critical" + ], + "title": "Default Severity", + "type": "string" + }, + "direction": { + "default": "same", + "enum": [ + "downgrade", + "upgrade", + "same" + ], + "title": "Direction", + "type": "string" + }, + "expires": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Expires" + }, + "manifest_path": { + "title": "Manifest Path", + "type": "string" + }, + "reason": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Reason" + }, + "tier_crossed": { + "default": false, + "title": "Tier Crossed", + "type": "boolean" + } + }, + "required": [ + "check_id", + "default_severity", + "applied_severity", + "manifest_path" + ], + "title": "SeverityOverrideAuditEntry", + "type": "object" + }, + "SourceReference": { + "additionalProperties": true, + "properties": { + "end_line": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "default": null, + "title": "End Line" + }, + "location": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Location" + }, + "path": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Path" + }, + "pointer": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Pointer" + }, + "ref": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Ref" + }, + "start_column": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Start Column" + }, + "start_line": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Start Line" + }, + "type": { + "title": "Type", + "type": "string" + } + }, + "required": [ + "type" + ], + "title": "SourceReference", + "type": "object" + }, + "SuggestedScenario": { + "properties": { + "expected_control": { + "title": "Expected Control", + "type": "string" + }, + "given": { + "title": "Given", + "type": "string" + }, + "id": { + "title": "Id", + "type": "string" + }, + "scenario_type": { + "enum": [ + "approval", + "confirmation", + "idempotency_retry", + "least_privilege_scope", + "prohibited_action", + "wildcard_inventory", + "schema_boundary", + "prompt_scope_alignment", + "test_case_coverage" + ], + "title": "Scenario Type", + "type": "string" + }, + "source_findings": { + "items": { + "type": "string" + }, + "title": "Source Findings", + "type": "array" + }, + "source_misalignments": { + "items": { + "type": "string" + }, + "title": "Source Misalignments", + "type": "array" + }, + "title": { + "title": "Title", + "type": "string" + } + }, + "required": [ + "expected_control", + "given", + "id", + "scenario_type", + "source_findings", + "source_misalignments", + "title" + ], + "title": "SuggestedScenario", + "type": "object" + }, + "ToolIdentityEvidence": { + "additionalProperties": false, + "properties": { + "binding_id": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Binding Id" + }, + "claims": { + "items": { + "$ref": "#/$defs/SemanticClaimEvidence" + }, + "title": "Claims", + "type": "array" + }, + "issues": { + "items": { + "$ref": "#/$defs/SemanticIssueEvidence" + }, + "title": "Issues", + "type": "array" + }, + "observation_ids": { + "items": { + "type": "string" + }, + "title": "Observation Ids", + "type": "array" + }, + "pass_eligible": { + "title": "Pass Eligible", + "type": "boolean" + }, + "primary_observation_id": { + "title": "Primary Observation Id", + "type": "string" + }, + "provider": { + "title": "Provider", + "type": "string" + }, + "status": { + "enum": [ + "declared", + "structural", + "partial", + "unknown", + "conflicting" + ], + "title": "Status", + "type": "string" + }, + "tool_id": { + "title": "Tool Id", + "type": "string" + } + }, + "required": [ + "tool_id", + "status", + "provider", + "primary_observation_id", + "pass_eligible" + ], + "title": "ToolIdentityEvidence", + "type": "object" + }, + "ToolSemanticEvidence": { + "additionalProperties": false, + "description": "Public semantic assessment attached to action and capability facts.", + "properties": { + "authority": { + "$ref": "#/$defs/AuthoritySemanticEvidence" + }, + "binding": { + "$ref": "#/$defs/BindingSemanticEvidence" + }, + "conservative_effect": { + "enum": [ + "read", + "write", + "destructive", + "external_communication", + "financial_write", + "production_operation", + "privileged_data_access", + "code_execution", + "identity_access" + ], + "title": "Conservative Effect", + "type": "string" + }, + "effect": { + "$ref": "#/$defs/EffectSemanticEvidence" + }, + "identity": { + "anyOf": [ + { + "$ref": "#/$defs/ToolIdentityEvidence" + }, + { + "type": "null" + } + ], + "default": null + }, + "pass_eligible": { + "title": "Pass Eligible", + "type": "boolean" + } + }, + "required": [ + "conservative_effect", + "effect", + "authority", + "pass_eligible" + ], + "title": "ToolSemanticEvidence", + "type": "object" + }, + "ToolSurfaceControlChange": { + "additionalProperties": false, + "properties": { + "control": { + "enum": [ + "approval_policy", + "confirmation_policy", + "idempotency_evidence" + ], + "title": "Control", + "type": "string" + }, + "kind": { + "enum": [ + "added", + "removed", + "changed" + ], + "title": "Kind", + "type": "string" + }, + "reason": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Reason" + }, + "source": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Source" + }, + "source_path": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Source Path" + }, + "source_start_line": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Source Start Line" + }, + "tool": { + "title": "Tool", + "type": "string" + }, + "tool_id": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Tool Id" + } + }, + "required": [ + "control", + "kind", + "reason", + "source", + "tool" + ], + "title": "ToolSurfaceControlChange", + "type": "object" + }, + "ToolSurfaceControlFact": { + "additionalProperties": false, + "properties": { + "kind": { + "enum": [ + "approval_policy", + "confirmation_policy", + "idempotency_evidence" + ], + "title": "Kind", + "type": "string" + }, + "reason": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Reason" + }, + "source": { + "title": "Source", + "type": "string" + }, + "tool": { + "title": "Tool", + "type": "string" + }, + "tool_id": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Tool Id" + } + }, + "required": [ + "kind", + "reason", + "source", + "tool" + ], + "title": "ToolSurfaceControlFact", + "type": "object" + }, + "ToolSurfaceDiff": { + "additionalProperties": false, + "properties": { + "base": { + "$ref": "#/$defs/ToolSurfaceDiffBase" + }, + "controls": { + "items": { + "$ref": "#/$defs/ToolSurfaceControlChange" + }, + "title": "Controls", + "type": "array" + }, + "enabled": { + "default": false, + "title": "Enabled", + "type": "boolean" + }, + "finding_deltas": { + "$ref": "#/$defs/ToolSurfaceFindingDeltas" + }, + "high_risk_effects": { + "items": { + "$ref": "#/$defs/ToolSurfaceHighRiskEffectChange" + }, + "title": "High Risk Effects", + "type": "array" + }, + "metadata_changes": { + "items": { + "$ref": "#/$defs/ToolSurfaceMetadataChange" + }, + "title": "Metadata Changes", + "type": "array" + }, + "notes": { + "items": { + "type": "string" + }, + "title": "Notes", + "type": "array" + }, + "policy_drift": { + "items": { + "$ref": "#/$defs/ToolSurfacePolicyDrift" + }, + "title": "Policy Drift", + "type": "array" + }, + "scopes": { + "items": { + "$ref": "#/$defs/ToolSurfaceScopeChange" + }, + "title": "Scopes", + "type": "array" + }, + "summary": { + "$ref": "#/$defs/ToolSurfaceDiffSummary" + }, + "tools": { + "items": { + "$ref": "#/$defs/ToolSurfaceToolChange" + }, + "title": "Tools", + "type": "array" + } + }, + "required": [ + "base", + "controls", + "enabled", + "finding_deltas", + "high_risk_effects", + "metadata_changes", + "notes", + "policy_drift", + "scopes", + "summary", + "tools" + ], + "title": "ToolSurfaceDiff", + "type": "object" + }, + "ToolSurfaceDiffBase": { + "additionalProperties": false, + "properties": { + "baseline_schema_version": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Baseline Schema Version" + }, + "kind": { + "default": "none", + "enum": [ + "none", + "report", + "baseline" + ], + "title": "Kind", + "type": "string" + }, + "path": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Path" + }, + "report_schema_version": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Report Schema Version" + } + }, + "required": [ + "baseline_schema_version", + "kind", + "path", + "report_schema_version" + ], + "title": "ToolSurfaceDiffBase", + "type": "object" + }, + "ToolSurfaceDiffSummary": { + "additionalProperties": false, + "properties": { + "accepted_debt": { + "default": 0, + "title": "Accepted Debt", + "type": "integer" + }, + "controls_added": { + "default": 0, + "title": "Controls Added", + "type": "integer" + }, + "controls_removed": { + "default": 0, + "title": "Controls Removed", + "type": "integer" + }, + "metadata_changes": { + "default": 0, + "title": "Metadata Changes", + "type": "integer" + }, + "new_findings": { + "default": 0, + "title": "New Findings", + "type": "integer" + }, + "new_high_risk_effects": { + "default": 0, + "title": "New High Risk Effects", + "type": "integer" + }, + "new_scopes": { + "default": 0, + "title": "New Scopes", + "type": "integer" + }, + "policy_drift_items": { + "default": 0, + "title": "Policy Drift Items", + "type": "integer" + }, + "removed_high_risk_effects": { + "default": 0, + "title": "Removed High Risk Effects", + "type": "integer" + }, + "removed_scopes": { + "default": 0, + "title": "Removed Scopes", + "type": "integer" + }, + "resolved_findings": { + "default": 0, + "title": "Resolved Findings", + "type": "integer" + }, + "tools_added": { + "default": 0, + "title": "Tools Added", + "type": "integer" + }, + "tools_changed": { + "default": 0, + "title": "Tools Changed", + "type": "integer" + }, + "tools_removed": { + "default": 0, + "title": "Tools Removed", + "type": "integer" + }, + "unchanged_findings": { + "default": 0, + "title": "Unchanged Findings", + "type": "integer" + } + }, + "required": [ + "accepted_debt", + "controls_added", + "controls_removed", + "metadata_changes", + "new_findings", + "new_high_risk_effects", + "new_scopes", + "policy_drift_items", + "removed_high_risk_effects", + "removed_scopes", + "resolved_findings", + "tools_added", + "tools_changed", + "tools_removed", + "unchanged_findings" + ], + "title": "ToolSurfaceDiffSummary", + "type": "object" + }, + "ToolSurfaceFacts": { + "additionalProperties": false, + "properties": { + "controls": { + "items": { + "$ref": "#/$defs/ToolSurfaceControlFact" + }, + "title": "Controls", + "type": "array" + }, + "policies": { + "items": { + "$ref": "#/$defs/ToolSurfacePolicyFact" + }, + "title": "Policies", + "type": "array" + }, + "scopes": { + "items": { + "$ref": "#/$defs/ToolSurfaceScopeFact" + }, + "title": "Scopes", + "type": "array" + }, + "tools": { + "items": { + "$ref": "#/$defs/ToolSurfaceToolFact" + }, + "title": "Tools", + "type": "array" + } + }, + "required": [ + "controls", + "policies", + "scopes", + "tools" + ], + "title": "ToolSurfaceFacts", + "type": "object" + }, + "ToolSurfaceFieldChange": { + "additionalProperties": false, + "properties": { + "after": { + "default": null, + "title": "After" + }, + "before": { + "default": null, + "title": "Before" + }, + "field": { + "title": "Field", + "type": "string" + } + }, + "required": [ + "after", + "before", + "field" + ], + "title": "ToolSurfaceFieldChange", + "type": "object" + }, + "ToolSurfaceFindingDeltaItem": { + "additionalProperties": false, + "properties": { + "baseline_status": { + "anyOf": [ + { + "enum": [ + "new", + "matched", + "resolved" + ], + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Baseline Status" + }, + "check_id": { + "title": "Check Id", + "type": "string" + }, + "fingerprint": { + "title": "Fingerprint", + "type": "string" + }, + "severity": { + "enum": [ + "info", + "low", + "medium", + "high", + "critical" + ], + "title": "Severity", + "type": "string" + }, + "title": { + "title": "Title", + "type": "string" + }, + "tool_name": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Tool Name" + } + }, + "required": [ + "baseline_status", + "check_id", + "fingerprint", + "severity", + "title", + "tool_name" + ], + "title": "ToolSurfaceFindingDeltaItem", + "type": "object" + }, + "ToolSurfaceFindingDeltas": { + "additionalProperties": false, + "properties": { + "accepted_debt": { + "items": { + "$ref": "#/$defs/ToolSurfaceFindingDeltaItem" + }, + "title": "Accepted Debt", + "type": "array" + }, + "new_findings": { + "items": { + "$ref": "#/$defs/ToolSurfaceFindingDeltaItem" + }, + "title": "New Findings", + "type": "array" + }, + "resolved_findings": { + "items": { + "$ref": "#/$defs/ToolSurfaceFindingDeltaItem" + }, + "title": "Resolved Findings", + "type": "array" + }, + "unchanged_findings": { + "items": { + "$ref": "#/$defs/ToolSurfaceFindingDeltaItem" + }, + "title": "Unchanged Findings", + "type": "array" + } + }, + "required": [ + "accepted_debt", + "new_findings", + "resolved_findings", + "unchanged_findings" + ], + "title": "ToolSurfaceFindingDeltas", + "type": "object" + }, + "ToolSurfaceHashes": { + "additionalProperties": false, + "properties": { + "annotations": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Annotations" + }, + "description": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Description" + }, + "input_schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Input Schema" + }, + "output_schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Output Schema" + }, + "parameters": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Parameters" + }, + "source_ref": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Source Ref" + } + }, + "required": [ + "annotations", + "description", + "input_schema", + "output_schema", + "parameters", + "source_ref" + ], + "title": "ToolSurfaceHashes", + "type": "object" + }, + "ToolSurfaceHighRiskEffectChange": { + "additionalProperties": false, + "properties": { + "kind": { + "enum": [ + "added", + "removed", + "changed" + ], + "title": "Kind", + "type": "string" + }, + "source_path": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Source Path" + }, + "source_start_line": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Source Start Line" + }, + "tag": { + "title": "Tag", + "type": "string" + }, + "tool": { + "title": "Tool", + "type": "string" + }, + "tool_id": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Tool Id" + } + }, + "required": [ + "kind", + "tag", + "tool" + ], + "title": "ToolSurfaceHighRiskEffectChange", + "type": "object" + }, + "ToolSurfaceMetadataChange": { + "additionalProperties": false, + "properties": { + "after": { + "default": null, + "title": "After" + }, + "before": { + "default": null, + "title": "Before" + }, + "kind": { + "enum": [ + "added", + "removed", + "changed" + ], + "title": "Kind", + "type": "string" + }, + "metadata": { + "title": "Metadata", + "type": "string" + }, + "source_path": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Source Path" + }, + "source_start_line": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Source Start Line" + }, + "tool": { + "title": "Tool", + "type": "string" + }, + "tool_id": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Tool Id" + } + }, + "required": [ + "after", + "before", + "kind", + "metadata", + "tool" + ], + "title": "ToolSurfaceMetadataChange", + "type": "object" + }, + "ToolSurfacePolicyDrift": { + "additionalProperties": false, + "properties": { + "after_hash": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "After Hash" + }, + "after_summary": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "After Summary" + }, + "before_hash": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Before Hash" + }, + "before_summary": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Before Summary" + }, + "key": { + "title": "Key", + "type": "string" + }, + "kind": { + "enum": [ + "added", + "removed", + "changed" + ], + "title": "Kind", + "type": "string" + }, + "policy_kind": { + "title": "Policy Kind", + "type": "string" + } + }, + "required": [ + "after_hash", + "after_summary", + "before_hash", + "before_summary", + "key", + "kind", + "policy_kind" + ], + "title": "ToolSurfacePolicyDrift", + "type": "object" + }, + "ToolSurfacePolicyFact": { + "additionalProperties": false, + "properties": { + "key": { + "title": "Key", + "type": "string" + }, + "kind": { + "title": "Kind", + "type": "string" + }, + "summary": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Summary" + }, + "value_hash": { + "title": "Value Hash", + "type": "string" + } + }, + "required": [ + "key", + "kind", + "summary", + "value_hash" + ], + "title": "ToolSurfacePolicyFact", + "type": "object" + }, + "ToolSurfaceScopeChange": { + "additionalProperties": false, + "properties": { + "broad": { + "default": false, + "title": "Broad", + "type": "boolean" + }, + "kind": { + "enum": [ + "added", + "removed", + "changed" + ], + "title": "Kind", + "type": "string" + }, + "scope": { + "title": "Scope", + "type": "string" + }, + "scope_kind": { + "enum": [ + "tool_required", + "manifest_declared" + ], + "title": "Scope Kind", + "type": "string" + }, + "tool_ids": { + "items": { + "type": "string" + }, + "title": "Tool Ids", + "type": "array" + }, + "tool_names": { + "items": { + "type": "string" + }, + "title": "Tool Names", + "type": "array" + } + }, + "required": [ + "broad", + "kind", + "scope", + "scope_kind", + "tool_names" + ], + "title": "ToolSurfaceScopeChange", + "type": "object" + }, + "ToolSurfaceScopeFact": { + "additionalProperties": false, + "properties": { + "broad": { + "default": false, + "title": "Broad", + "type": "boolean" + }, + "kind": { + "enum": [ + "tool_required", + "manifest_declared" + ], + "title": "Kind", + "type": "string" + }, + "scope": { + "title": "Scope", + "type": "string" + }, + "tool_ids": { + "items": { + "type": "string" + }, + "title": "Tool Ids", + "type": "array" + }, + "tool_names": { + "items": { + "type": "string" + }, + "title": "Tool Names", + "type": "array" + } + }, + "required": [ + "broad", + "kind", + "scope", + "tool_names" + ], + "title": "ToolSurfaceScopeFact", + "type": "object" + }, + "ToolSurfaceSummary": { + "properties": { + "high_risk_tools": { + "title": "High Risk Tools", + "type": "integer" + }, + "missing_descriptions": { + "default": 0, + "title": "Missing Descriptions", + "type": "integer" + }, + "sources": { + "additionalProperties": { + "type": "integer" + }, + "title": "Sources", + "type": "object" + }, + "total_tools": { + "title": "Total Tools", + "type": "integer" + }, + "wildcard_tools": { + "default": 0, + "title": "Wildcard Tools", + "type": "integer" + } + }, + "required": [ + "total_tools", + "high_risk_tools" + ], + "title": "ToolSurfaceSummary", + "type": "object" + }, + "ToolSurfaceToolChange": { + "additionalProperties": false, + "properties": { + "changes": { + "items": { + "$ref": "#/$defs/ToolSurfaceFieldChange" + }, + "title": "Changes", + "type": "array" + }, + "kind": { + "enum": [ + "added", + "removed", + "changed" + ], + "title": "Kind", + "type": "string" + }, + "name": { + "title": "Name", + "type": "string" + }, + "provider": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Provider" + }, + "source_id": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Source Id" + }, + "source_path": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Source Path" + }, + "source_start_line": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Source Start Line" + }, + "source_type": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Source Type" + }, + "tool_id": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Tool Id" + } + }, + "required": [ + "changes", + "kind", + "name", + "source_id", + "source_type" + ], + "title": "ToolSurfaceToolChange", + "type": "object" + }, + "ToolSurfaceToolFact": { + "additionalProperties": false, + "properties": { + "auth_scopes": { + "items": { + "type": "string" + }, + "title": "Auth Scopes", + "type": "array" + }, + "extraction_confidence": { + "default": "low", + "enum": [ + "low", + "medium", + "high" + ], + "title": "Extraction Confidence", + "type": "string" + }, + "has_description": { + "default": false, + "title": "Has Description", + "type": "boolean" + }, + "hashes": { + "$ref": "#/$defs/ToolSurfaceHashes" + }, + "name": { + "title": "Name", + "type": "string" + }, + "owner": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Owner" + }, + "provider": { + "default": "", + "title": "Provider", + "type": "string" + }, + "risk_tags": { + "items": { + "type": "string" + }, + "title": "Risk Tags", + "type": "array" + }, + "source_id": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Source Id" + }, + "source_ref": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Source Ref" + }, + "source_type": { + "title": "Source Type", + "type": "string" + }, + "tool_id": { + "default": "", + "title": "Tool Id", + "type": "string" + } + }, + "required": [ + "auth_scopes", + "extraction_confidence", + "has_description", + "hashes", + "name", + "owner", + "risk_tags", + "source_id", + "source_ref", + "source_type" + ], + "title": "ToolSurfaceToolFact", + "type": "object" + }, + "VerifierCapabilityDeltaSummary": { + "additionalProperties": false, + "description": "Counts of the capability delta, by direction.\n\nEqual by construction to the lengths of the matching\n``CapabilityChangeBlock`` member lists (roadmap \u00a78 required test:\nreviewer/verifier capability counts equal the underlying rollup).", + "properties": { + "added": { + "default": 0, + "title": "Added", + "type": "integer" + }, + "broadened": { + "default": 0, + "title": "Broadened", + "type": "integer" + }, + "narrowed": { + "default": 0, + "title": "Narrowed", + "type": "integer" + }, + "removed": { + "default": 0, + "title": "Removed", + "type": "integer" + } + }, + "required": [ + "added", + "broadened", + "narrowed", + "removed" + ], + "title": "VerifierCapabilityDeltaSummary", + "type": "object" + }, + "VerifierReasonCodeCount": { + "additionalProperties": false, + "description": "One ``(reason_code, count)`` pair for the verifier summary.\n\nA list of these (rather than a dict) keeps the ordering explicit and\nbyte-stable; the builder sorts them deterministically.", + "properties": { + "count": { + "default": 0, + "title": "Count", + "type": "integer" + }, + "reason_code": { + "title": "Reason Code", + "type": "string" + } + }, + "required": [ + "count", + "reason_code" + ], + "title": "VerifierReasonCodeCount", + "type": "object" + }, + "VerifierSummary": { + "additionalProperties": false, + "description": "Top-level verifier composition alias (Decision 5 shape).\n\nA byte-stable projection bundling the release verdict, finding counts,\nthe capability delta summary, and trust-root / acknowledgement flags\nfor one-fetch controller consumption. Composition only \u2014 it cannot\nintroduce a finding-independent blocker, and ``verdict`` MUST mirror\n``release_decision.decision`` (Principle 2; enforced by a property\ntest). Parallels ``agent_summary`` / ``reviewer_summary``.\n\n``top_reason_codes`` is capped at the top five by the builder, ranked\nseverity desc, count desc, then reason code asc.", + "properties": { + "by_reason_code": { + "additionalProperties": { + "type": "integer" + }, + "title": "By Reason Code", + "type": "object" + }, + "by_severity": { + "additionalProperties": { + "type": "integer" + }, + "title": "By Severity", + "type": "object" + }, + "capability_delta_summary": { + "$ref": "#/$defs/VerifierCapabilityDeltaSummary" + }, + "human_ack_required": { + "default": false, + "title": "Human Ack Required", + "type": "boolean" + }, + "human_ack_satisfied": { + "default": true, + "title": "Human Ack Satisfied", + "type": "boolean" + }, + "policy_weakened": { + "default": false, + "title": "Policy Weakened", + "type": "boolean" + }, + "protected_surface_touched": { + "default": false, + "title": "Protected Surface Touched", + "type": "boolean" + }, + "top_reason_codes": { + "items": { + "$ref": "#/$defs/VerifierReasonCodeCount" + }, + "title": "Top Reason Codes", + "type": "array" + }, + "verdict": { + "enum": [ + "blocked", + "review_required", + "insufficient_evidence", + "passed" + ], + "title": "Verdict", + "type": "string" + } + }, + "required": [ + "by_reason_code", + "by_severity", + "capability_delta_summary", + "human_ack_required", + "human_ack_satisfied", + "policy_weakened", + "protected_surface_touched", + "top_reason_codes", + "verdict" + ], + "title": "VerifierSummary", + "type": "object" + } + }, + "$id": "https://raw.githubusercontent.com/ThreeMoonsLab/agents-shipgate/main/docs/report-schema.v0.33.json", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "additionalProperties": true, + "description": "JSON Schema for the Agents Shipgate Tool-Use Readiness Report. Generated from agents_shipgate.schemas.report.ReadinessReport with post-processing to preserve the v0.5 public contract. Do not edit by hand.", + "properties": { + "action_surface_diff": { + "$ref": "#/$defs/ActionSurfaceDiff" + }, + "action_surface_facts": { + "$ref": "#/$defs/ActionSurfaceFacts" + }, + "agent": { + "additionalProperties": true, + "title": "Agent", + "type": "object" + }, + "agent_summary": { + "$ref": "#/$defs/AgentSummary" + }, + "anthropic_surface": { + "anyOf": [ + { + "additionalProperties": true, + "type": "object" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Anthropic Surface" + }, + "api_surface": { + "anyOf": [ + { + "additionalProperties": true, + "type": "object" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Api Surface" + }, + "baseline": { + "anyOf": [ + { + "$ref": "#/$defs/BaselineSummary" + }, + { + "type": "null" + } + ], + "default": null + }, + "binding_surface_diff": { + "$ref": "#/$defs/BindingSurfaceDiff" + }, + "binding_surface_facts": { + "$ref": "#/$defs/AgentBindingGraphAssessment" + }, + "capability_change": { + "$ref": "#/$defs/CapabilityChangeBlock" + }, + "capability_facts": { + "items": { + "$ref": "#/$defs/CapabilityFact" + }, + "title": "Capability Facts", + "type": "array" + }, + "capability_runtime_evidence": { + "$ref": "#/$defs/CapabilityRuntimeEvidence" + }, + "codex_plugin_surface": { + "anyOf": [ + { + "$ref": "#/$defs/CodexPluginSurface" + }, + { + "type": "null" + } + ], + "default": null + }, + "declared_intentions": { + "items": { + "$ref": "#/$defs/DeclaredIntention" + }, + "title": "Declared Intentions", + "type": "array" + }, + "effective_policy": { + "$ref": "#/$defs/EffectivePolicy" + }, + "environment": { + "additionalProperties": true, + "title": "Environment", + "type": "object" + }, + "findings": { + "items": { + "$ref": "#/$defs/Finding" + }, + "title": "Findings", + "type": "array" + }, + "frameworks": { + "additionalProperties": true, + "properties": { + "conductor": { + "additionalProperties": true, + "required": [ + "dynamic_tool_surface_count", + "human_checkpoint_count", + "llm_task_count", + "mcp_call_task_count", + "mcp_discovery_task_count", + "structurally_checkpointed_mcp_call_count", + "sub_workflow_task_count", + "task_count", + "unsupported_capability_count", + "warnings", + "workflow_count", + "workflow_file_count" + ], + "type": "object" + }, + "crewai": { + "additionalProperties": true, + "required": [ + "agent_count", + "class_tool_count", + "crew_count", + "dynamic_tool_surface_count", + "function_tool_count", + "prebuilt_tool_count", + "python_entrypoint_count", + "tool_inventory_file_count", + "warnings" + ], + "type": "object" + }, + "google_adk": { + "additionalProperties": true, + "required": [ + "agent_config_count", + "agent_count", + "callback_count", + "dynamic_toolset_count", + "eval_file_count", + "function_tool_count", + "long_running_tool_count", + "plugin_count", + "python_entrypoint_count", + "sub_agent_count", + "tool_inventory_file_count", + "toolset_count", + "trace_sample_count", + "warnings" + ], + "type": "object" + }, + "langchain": { + "additionalProperties": true, + "required": [ + "agent_tool_binding_count", + "dynamic_tool_surface_count", + "function_tool_count", + "python_entrypoint_count", + "structured_tool_count", + "tool_inventory_file_count", + "tool_node_count", + "warnings" + ], + "type": "object" + } + }, + "title": "Frameworks", + "type": "object" + }, + "generated_reports": { + "additionalProperties": { + "type": "string" + }, + "title": "Generated Reports", + "type": "object" + }, + "heuristics_filter": { + "$ref": "#/$defs/HeuristicsFilter" + }, + "human_ack": { + "$ref": "#/$defs/HumanAck" + }, + "loaded_adapters": { + "items": { + "additionalProperties": true, + "required": [ + "distribution", + "name", + "runtime_errors", + "source_type", + "validation_errors", + "validation_status", + "value", + "version" + ], + "type": "object" + }, + "title": "Loaded Adapters", + "type": "array" + }, + "loaded_plugins": { + "items": { + "additionalProperties": true, + "required": [ + "check_id", + "distribution", + "name", + "runtime_errors", + "validation_errors", + "validation_status", + "value", + "version" + ], + "type": "object" + }, + "title": "Loaded Plugins", + "type": "array" + }, + "loaded_policy_packs": { + "items": { + "$ref": "#/$defs/LoadedPolicyPack" + }, + "title": "Loaded Policy Packs", + "type": "array" + }, + "manifest_dir": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Manifest Dir" + }, + "misalignments": { + "items": { + "$ref": "#/$defs/Misalignment" + }, + "title": "Misalignments", + "type": "array" + }, + "policy_audit": { + "$ref": "#/$defs/PolicyAudit" + }, + "policy_evidence_gaps": { + "items": { + "$ref": "#/$defs/EvidenceGap" + }, + "title": "Policy Evidence Gaps", + "type": "array" + }, + "privacy_audit": { + "$ref": "#/$defs/PrivacyAudit" + }, + "project": { + "additionalProperties": true, + "title": "Project", + "type": "object" + }, + "protected_surface_changes": { + "items": { + "$ref": "#/$defs/ProtectedSurfaceChange" + }, + "title": "Protected Surface Changes", + "type": "array" + }, + "recommended_actions": { + "items": { + "type": "string" + }, + "title": "Recommended Actions", + "type": "array" + }, + "release_consequence": { + "$ref": "#/$defs/ReleaseConsequence" + }, + "release_decision": { + "$ref": "#/$defs/ReleaseDecision" + }, + "report_schema_version": { + "const": "0.33" + }, + "reviewer_summary": { + "$ref": "#/$defs/ReviewerSummary" + }, + "run_id": { + "title": "Run Id", + "type": "string" + }, + "schema_version": { + "const": "0.1" + }, + "source_warnings": { + "items": { + "type": "string" + }, + "title": "Source Warnings", + "type": "array" + }, + "suggested_scenarios": { + "items": { + "$ref": "#/$defs/SuggestedScenario" + }, + "title": "Suggested Scenarios", + "type": "array" + }, + "summary": { + "$ref": "#/$defs/ReportSummary" + }, + "tool_catalog": { + "items": { + "additionalProperties": true, + "type": "object" + }, + "title": "Tool Catalog", + "type": "array" + }, + "tool_inventory": { + "items": { + "additionalProperties": true, + "required": [ + "auth_scopes", + "confidence", + "name", + "risk_tags", + "source_type" + ], + "type": "object" + }, + "title": "Tool Inventory", + "type": "array" + }, + "tool_surface": { + "$ref": "#/$defs/ToolSurfaceSummary" + }, + "tool_surface_diff": { + "$ref": "#/$defs/ToolSurfaceDiff" + }, + "tool_surface_facts": { + "$ref": "#/$defs/ToolSurfaceFacts" + }, + "verifier_summary": { + "$ref": "#/$defs/VerifierSummary" + } + }, + "required": [ + "action_surface_diff", + "action_surface_facts", + "agent", + "agent_summary", + "capability_change", + "capability_facts", + "codex_plugin_surface", + "declared_intentions", + "effective_policy", + "environment", + "findings", + "frameworks", + "generated_reports", + "heuristics_filter", + "human_ack", + "loaded_adapters", + "loaded_plugins", + "loaded_policy_packs", + "misalignments", + "policy_audit", + "privacy_audit", + "project", + "protected_surface_changes", + "recommended_actions", + "release_consequence", + "release_decision", + "report_schema_version", + "reviewer_summary", + "run_id", + "schema_version", + "source_warnings", + "suggested_scenarios", + "summary", + "tool_inventory", + "tool_surface", + "tool_surface_diff", + "tool_surface_facts", + "verifier_summary" + ], + "title": "Agents Shipgate Readiness Report v0.33", + "type": "object" +} diff --git a/docs/report-sensitive-fields.json b/docs/report-sensitive-fields.json index 81319585..244268ca 100644 --- a/docs/report-sensitive-fields.json +++ b/docs/report-sensitive-fields.json @@ -20,6 +20,7 @@ {"surface": "report", "path": "environment", "classification": "public_control_metadata"}, {"surface": "report", "path": "summary", "classification": "public_control_metadata"}, {"surface": "report", "path": "release_decision", "classification": "public_control_metadata"}, + {"surface": "report", "path": "policy_evidence_gaps", "classification": "free_text"}, {"surface": "report", "path": "capability_facts", "classification": "free_text"}, {"surface": "report", "path": "declared_intentions", "classification": "free_text"}, {"surface": "report", "path": "misalignments", "classification": "free_text"}, @@ -68,6 +69,7 @@ {"surface": "report", "path": "findings[].capability_policy_evidence.authority.scopes", "classification": "credential_metadata"}, {"surface": "report", "path": "findings[].capability_policy_evidence.hashes", "classification": "hash_only"}, {"surface": "report", "path": "findings[].capability_policy_evidence.source", "classification": "path_metadata"}, + {"surface": "report", "path": "findings[].support", "classification": "schema_metadata"}, {"surface": "report", "path": "tool_inventory[].auth_scopes", "classification": "credential_metadata"}, {"surface": "packet", "path": "packet.project", "classification": "free_text"}, {"surface": "packet", "path": "packet.agent", "classification": "free_text"}, diff --git a/docs/report-v1-consolidation-rc.md b/docs/report-v1-consolidation-rc.md index ff1872e5..bb858aa6 100644 --- a/docs/report-v1-consolidation-rc.md +++ b/docs/report-v1-consolidation-rc.md @@ -2,7 +2,7 @@ > Status: **proposal** (no behavior change in this document). Target: > freeze a v1.0 report schema whose top-level surface stops growing. -> Written 2026-06; current runtime is `report_schema_version: "0.32"`. +> Written 2026-06; current runtime is `report_schema_version: "0.33"`. ## Problem diff --git a/docs/use-cases/ai-generated-agent-prs.md b/docs/use-cases/ai-generated-agent-prs.md index 4a3f9810..89a2cb83 100644 --- a/docs/use-cases/ai-generated-agent-prs.md +++ b/docs/use-cases/ai-generated-agent-prs.md @@ -203,7 +203,7 @@ Read `agents-shipgate-reports/verifier.json` in this order: `base_status` to understand whether diff enrichment ran — never as a release verdict. The full schema is [`docs/verifier-schema.v0.3.json`](../verifier-schema.v0.3.json) -(`verifier_schema_version: "0.3"`). +(`verifier_schema_version: "0.4"`). After `verifier.json`, read `agents-shipgate-reports/report.json` for the full finding detail. The human PR surface is `agents-shipgate-reports/pr-comment.md`. diff --git a/docs/verifier-schema.v0.4.json b/docs/verifier-schema.v0.4.json new file mode 100644 index 00000000..adfc1288 --- /dev/null +++ b/docs/verifier-schema.v0.4.json @@ -0,0 +1,2233 @@ +{ + "$defs": { + "AgentActionRequiredControl": { + "additionalProperties": false, + "description": "Non-terminal state with one exact coding-agent-owned next step.", + "properties": { + "allowed_next_commands": { + "items": { + "minLength": 1, + "type": "string" + }, + "title": "Allowed Next Commands", + "type": "array" + }, + "completion_allowed": { + "const": false, + "default": false, + "title": "Completion Allowed", + "type": "boolean" + }, + "human_review": { + "$ref": "#/$defs/NoHumanReview" + }, + "must_stop": { + "const": false, + "default": false, + "title": "Must Stop", + "type": "boolean" + }, + "next_action": { + "$ref": "#/$defs/CodingAgentAction" + }, + "reason": { + "minLength": 1, + "title": "Reason", + "type": "string" + }, + "state": { + "const": "agent_action_required", + "title": "State", + "type": "string" + }, + "stop_reason": { + "default": null, + "title": "Stop Reason", + "type": "null" + }, + "verify_required": { + "default": false, + "title": "Verify Required", + "type": "boolean" + } + }, + "required": [ + "state", + "reason", + "completion_allowed", + "must_stop", + "verify_required", + "next_action", + "allowed_next_commands", + "human_review", + "stop_reason" + ], + "title": "AgentActionRequiredControl", + "type": "object" + }, + "AgentControl": { + "discriminator": { + "mapping": { + "agent_action_required": "#/$defs/AgentActionRequiredControl", + "complete": "#/$defs/CompleteAgentControl", + "human_review_required": "#/$defs/HumanReviewRequiredControl" + }, + "propertyName": "state" + }, + "oneOf": [ + { + "$ref": "#/$defs/CompleteAgentControl" + }, + { + "$ref": "#/$defs/AgentActionRequiredControl" + }, + { + "$ref": "#/$defs/HumanReviewRequiredControl" + } + ] + }, + "BaselineDelta": { + "properties": { + "enabled": { + "title": "Enabled", + "type": "boolean" + }, + "matched_count": { + "default": 0, + "title": "Matched Count", + "type": "integer" + }, + "new_count": { + "default": 0, + "title": "New Count", + "type": "integer" + }, + "path": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Path" + }, + "resolved_count": { + "default": 0, + "title": "Resolved Count", + "type": "integer" + } + }, + "required": [ + "enabled" + ], + "title": "BaselineDelta", + "type": "object" + }, + "BindingCoverageDecision": { + "properties": { + "gap_count": { + "default": 0, + "title": "Gap Count", + "type": "integer" + }, + "pass_eligible": { + "default": false, + "title": "Pass Eligible", + "type": "boolean" + }, + "possible_tools": { + "default": 0, + "title": "Possible Tools", + "type": "integer" + }, + "reachable_tools": { + "default": 0, + "title": "Reachable Tools", + "type": "integer" + }, + "reason_counts": { + "additionalProperties": { + "type": "integer" + }, + "title": "Reason Counts", + "type": "object" + }, + "total_catalog_tools": { + "default": 0, + "title": "Total Catalog Tools", + "type": "integer" + }, + "unbound_tools": { + "default": 0, + "title": "Unbound Tools", + "type": "integer" + } + }, + "title": "BindingCoverageDecision", + "type": "object" + }, + "CodingAgentAction": { + "discriminator": { + "mapping": { + "configure": "#/$defs/CodingAgentCommandAction", + "discover": "#/$defs/CodingAgentCommandAction", + "fetch_base": "#/$defs/CodingAgentFetchBaseAction", + "initialize": "#/$defs/CodingAgentCommandAction", + "install": "#/$defs/CodingAgentCommandAction", + "repair": "#/$defs/CodingAgentCommandAction", + "rerun": "#/$defs/CodingAgentCommandAction", + "verify": "#/$defs/CodingAgentCommandAction" + }, + "propertyName": "kind" + }, + "oneOf": [ + { + "$ref": "#/$defs/CodingAgentCommandAction" + }, + { + "$ref": "#/$defs/CodingAgentFetchBaseAction" + } + ] + }, + "CodingAgentCommandAction": { + "additionalProperties": false, + "description": "An executable, exact next step owned by the coding agent.", + "properties": { + "actor": { + "const": "coding_agent", + "default": "coding_agent", + "title": "Actor", + "type": "string" + }, + "command": { + "minLength": 1, + "title": "Command", + "type": "string" + }, + "expects": { + "default": null, + "title": "Expects", + "type": "null" + }, + "kind": { + "enum": [ + "verify", + "discover", + "configure", + "initialize", + "repair", + "install", + "rerun" + ], + "title": "Kind", + "type": "string" + }, + "why": { + "minLength": 1, + "title": "Why", + "type": "string" + } + }, + "required": [ + "actor", + "kind", + "command", + "expects", + "why" + ], + "title": "CodingAgentCommandAction", + "type": "object" + }, + "CodingAgentFetchBaseAction": { + "additionalProperties": false, + "description": "A structured input request when an exact fetch command is unavailable.\n\nShipgate never fetches refs itself. ``expects`` therefore names the exact\nref or artifact a caller must make available before rerunning verification.", + "properties": { + "actor": { + "const": "coding_agent", + "default": "coding_agent", + "title": "Actor", + "type": "string" + }, + "command": { + "default": null, + "title": "Command", + "type": "null" + }, + "expects": { + "minLength": 1, + "title": "Expects", + "type": "string" + }, + "kind": { + "const": "fetch_base", + "title": "Kind", + "type": "string" + }, + "why": { + "minLength": 1, + "title": "Why", + "type": "string" + } + }, + "required": [ + "actor", + "kind", + "command", + "expects", + "why" + ], + "title": "CodingAgentFetchBaseAction", + "type": "object" + }, + "CompleteAgentControl": { + "additionalProperties": false, + "description": "Terminal state: the coding agent may report the task complete.", + "properties": { + "allowed_next_commands": { + "items": { + "minLength": 1, + "type": "string" + }, + "maxItems": 0, + "title": "Allowed Next Commands", + "type": "array" + }, + "completion_allowed": { + "const": true, + "default": true, + "title": "Completion Allowed", + "type": "boolean" + }, + "human_review": { + "$ref": "#/$defs/NoHumanReview" + }, + "must_stop": { + "const": false, + "default": false, + "title": "Must Stop", + "type": "boolean" + }, + "next_action": { + "default": null, + "title": "Next Action", + "type": "null" + }, + "reason": { + "minLength": 1, + "title": "Reason", + "type": "string" + }, + "state": { + "const": "complete", + "title": "State", + "type": "string" + }, + "stop_reason": { + "default": null, + "title": "Stop Reason", + "type": "null" + }, + "verify_required": { + "const": false, + "default": false, + "title": "Verify Required", + "type": "boolean" + } + }, + "required": [ + "state", + "reason", + "completion_allowed", + "must_stop", + "verify_required", + "next_action", + "allowed_next_commands", + "human_review", + "stop_reason" + ], + "title": "CompleteAgentControl", + "type": "object" + }, + "ContributionRule": { + "additionalProperties": false, + "description": "Per-finding audit row explaining how a finding contributed to the\nrelease decision.\n\nAdditive in v0.17. Every finding in `report.findings` produces\nexactly one ContributionRule. Reading the contribution rule is\nsufficient to predict the gate outcome for that finding without\nre-deriving the decision logic; the set of valid `(rule, category)`\npairs is the contract documented in STABILITY.md \"Release decision\ntruth table\".", + "properties": { + "category": { + "enum": [ + "blocker", + "review_item", + "excluded" + ], + "title": "Category", + "type": "string" + }, + "check_id": { + "title": "Check Id", + "type": "string" + }, + "finding_id": { + "title": "Finding Id", + "type": "string" + }, + "fingerprint": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Fingerprint" + }, + "rationale": { + "title": "Rationale", + "type": "string" + }, + "rule": { + "enum": [ + "policy_block_new", + "severity_block_new", + "policy_baseline_accepted", + "severity_baseline_accepted", + "review_required", + "sub_threshold", + "unsupported_evidence", + "suppressed" + ], + "title": "Rule", + "type": "string" + } + }, + "required": [ + "finding_id", + "check_id", + "category", + "rule", + "rationale" + ], + "title": "ContributionRule", + "type": "object" + }, + "EvidenceCoverageDecision": { + "properties": { + "binding_coverage": { + "$ref": "#/$defs/BindingCoverageDecision" + }, + "evidence_gaps": { + "items": { + "$ref": "#/$defs/EvidenceGap" + }, + "title": "Evidence Gaps", + "type": "array" + }, + "human_review_recommended": { + "title": "Human Review Recommended", + "type": "boolean" + }, + "identity_coverage": { + "$ref": "#/$defs/IdentityCoverageDecision" + }, + "level": { + "title": "Level", + "type": "string" + }, + "low_confidence_tool_count": { + "title": "Low Confidence Tool Count", + "type": "integer" + }, + "policy_gap_count": { + "default": 0, + "title": "Policy Gap Count", + "type": "integer" + }, + "semantic_coverage": { + "$ref": "#/$defs/SemanticCoverageDecision" + }, + "source_warning_count": { + "title": "Source Warning Count", + "type": "integer" + } + }, + "required": [ + "level", + "human_review_recommended", + "source_warning_count", + "low_confidence_tool_count" + ], + "title": "EvidenceCoverageDecision", + "type": "object" + }, + "EvidenceGap": { + "description": "v0.26: one structured row per measurable evidence gap.\n\n``insufficient_evidence`` previously diagnosed without prescribing;\neach gap names the degraded subject and the specific next action\nthat raises extraction confidence. Purely explanatory \u2014 gating\nstill uses only the counts (the gap list is a projection of them).", + "properties": { + "kind": { + "enum": [ + "low_confidence_tool", + "source_warning", + "incomplete_surface", + "missing_effect_evidence", + "inferred_effect_only", + "conflicting_effect_evidence", + "missing_authority_evidence", + "partial_authority_evidence", + "conflicting_authority_evidence", + "invalid_semantic_annotation", + "incomplete_tool_identity", + "conflicting_tool_identity", + "unresolved_tool_selector", + "ambiguous_tool_selector", + "ambiguous_legacy_tool_identity", + "invalid_tool_binding", + "missing_binding_evidence", + "partial_binding_evidence", + "conflicting_binding_evidence", + "ambiguous_root_agent", + "unresolved_agent_binding", + "unresolved_bound_tool", + "incomplete_handoff_graph", + "invalid_binding_annotation", + "invalid_evidence_provenance", + "inferred_policy_applicability", + "mixed_policy_evidence", + "unknown_policy_evidence", + "conflicting_policy_evidence" + ], + "title": "Kind", + "type": "string" + }, + "next_action": { + "$ref": "#/$defs/EvidenceGapAction" + }, + "source_ref": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Source Ref" + }, + "source_type": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Source Type" + }, + "subject": { + "title": "Subject", + "type": "string" + }, + "why": { + "title": "Why", + "type": "string" + } + }, + "required": [ + "kind", + "subject", + "why", + "next_action" + ], + "title": "EvidenceGap", + "type": "object" + }, + "EvidenceGapAction": { + "description": "One concrete, mechanically-executable step that closes a gap.\n\nMirrors the agent-mode ``next_actions[]`` error shape\n(``kind``/``command``/``path``/``why``/``expects``) so agents reuse\none routing vocabulary across error recovery and evidence repair.", + "properties": { + "accepted_values": { + "items": { + "type": "string" + }, + "title": "Accepted Values", + "type": "array" + }, + "auto_apply": { + "const": false, + "default": false, + "title": "Auto Apply", + "type": "boolean" + }, + "command": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Command" + }, + "declaration_template": { + "anyOf": [ + { + "additionalProperties": true, + "type": "object" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Declaration Template" + }, + "expects": { + "title": "Expects", + "type": "string" + }, + "kind": { + "enum": [ + "declare_tool_inventory", + "provide_source", + "review_warning", + "declare_action_effect", + "declare_action_authority", + "provide_complete_inventory", + "resolve_semantic_conflict", + "declare_source_identity", + "qualify_tool_selector", + "provide_tool_binding", + "resolve_tool_identity_conflict", + "regenerate_identity_artifact", + "declare_agent_root", + "declare_agent_bindings", + "provide_static_binding_source", + "provide_complete_binding_graph", + "resolve_binding_conflict", + "regenerate_binding_artifact", + "provide_policy_evidence", + "review_policy_evidence", + "resolve_policy_evidence_conflict" + ], + "title": "Kind", + "type": "string" + }, + "path": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Path" + }, + "requires_human_review": { + "const": true, + "default": true, + "title": "Requires Human Review", + "type": "boolean" + }, + "suggested_patch_kind": { + "const": "manual", + "default": "manual", + "title": "Suggested Patch Kind", + "type": "string" + }, + "why": { + "title": "Why", + "type": "string" + } + }, + "required": [ + "kind", + "why", + "expects" + ], + "title": "EvidenceGapAction", + "type": "object" + }, + "FailPolicy": { + "properties": { + "ci_mode": { + "title": "Ci Mode", + "type": "string" + }, + "exit_code": { + "title": "Exit Code", + "type": "integer" + }, + "fail_on": { + "items": { + "enum": [ + "info", + "low", + "medium", + "high", + "critical" + ], + "type": "string" + }, + "title": "Fail On", + "type": "array" + }, + "new_findings_only": { + "default": false, + "title": "New Findings Only", + "type": "boolean" + }, + "would_fail_ci": { + "title": "Would Fail Ci", + "type": "boolean" + } + }, + "required": [ + "ci_mode", + "would_fail_ci", + "exit_code" + ], + "title": "FailPolicy", + "type": "object" + }, + "FindingSupport": { + "additionalProperties": false, + "description": "Authoritative support for finding confidence and release contribution.\n\nRule metadata may request a severity or block, but it cannot upgrade the\nunderlying evidence. ``support_hash`` binds baselines and audit surfaces\nto the predicate evidence that actually made the finding eligible.", + "properties": { + "blocking_eligible": { + "default": false, + "title": "Blocking Eligible", + "type": "boolean" + }, + "claim_ids": { + "items": { + "type": "string" + }, + "title": "Claim Ids", + "type": "array" + }, + "confidence": { + "default": "low", + "enum": [ + "low", + "medium", + "high" + ], + "title": "Confidence", + "type": "string" + }, + "evidence_bases": { + "items": { + "enum": [ + "reviewed_declaration", + "protocol_structure", + "typed_provider_fact", + "structural_scope", + "inferred_keyword", + "inferred_regex", + "protocol_default", + "unknown" + ], + "type": "string" + }, + "title": "Evidence Bases", + "type": "array" + }, + "policy_eligible": { + "default": false, + "title": "Policy Eligible", + "type": "boolean" + }, + "predicates": { + "items": { + "$ref": "#/$defs/PolicyPredicateEvidence" + }, + "title": "Predicates", + "type": "array" + }, + "status": { + "default": "matched", + "enum": [ + "matched", + "not_matched", + "indeterminate", + "conflicting" + ], + "title": "Status", + "type": "string" + }, + "support_hash": { + "title": "Support Hash", + "type": "string" + } + }, + "required": [ + "support_hash" + ], + "title": "FindingSupport", + "type": "object" + }, + "HumanControlAction": { + "additionalProperties": false, + "description": "A human-owned route. Human actions never expose executable commands.", + "properties": { + "actor": { + "const": "human", + "default": "human", + "title": "Actor", + "type": "string" + }, + "command": { + "default": null, + "title": "Command", + "type": "null" + }, + "expects": { + "default": null, + "title": "Expects", + "type": "null" + }, + "kind": { + "enum": [ + "review", + "stop" + ], + "title": "Kind", + "type": "string" + }, + "why": { + "minLength": 1, + "title": "Why", + "type": "string" + } + }, + "required": [ + "actor", + "kind", + "command", + "expects", + "why" + ], + "title": "HumanControlAction", + "type": "object" + }, + "HumanReviewRequiredControl": { + "additionalProperties": false, + "description": "Stopping state: no further coding-agent action is authorized.", + "properties": { + "allowed_next_commands": { + "items": { + "minLength": 1, + "type": "string" + }, + "maxItems": 0, + "title": "Allowed Next Commands", + "type": "array" + }, + "completion_allowed": { + "const": false, + "default": false, + "title": "Completion Allowed", + "type": "boolean" + }, + "human_review": { + "$ref": "#/$defs/RequiredHumanReview" + }, + "must_stop": { + "const": true, + "default": true, + "title": "Must Stop", + "type": "boolean" + }, + "next_action": { + "$ref": "#/$defs/HumanControlAction" + }, + "reason": { + "minLength": 1, + "title": "Reason", + "type": "string" + }, + "state": { + "const": "human_review_required", + "title": "State", + "type": "string" + }, + "stop_reason": { + "minLength": 1, + "title": "Stop Reason", + "type": "string" + }, + "verify_required": { + "default": false, + "title": "Verify Required", + "type": "boolean" + } + }, + "required": [ + "state", + "reason", + "completion_allowed", + "must_stop", + "verify_required", + "next_action", + "allowed_next_commands", + "human_review", + "stop_reason" + ], + "title": "HumanReviewRequiredControl", + "type": "object" + }, + "IdentityCoverageDecision": { + "properties": { + "ambiguous_name_count": { + "default": 0, + "title": "Ambiguous Name Count", + "type": "integer" + }, + "bound_tools": { + "default": 0, + "title": "Bound Tools", + "type": "integer" + }, + "canonical_tools": { + "default": 0, + "title": "Canonical Tools", + "type": "integer" + }, + "gap_count": { + "default": 0, + "title": "Gap Count", + "type": "integer" + }, + "pass_eligible_tools": { + "default": 0, + "title": "Pass Eligible Tools", + "type": "integer" + }, + "reason_counts": { + "additionalProperties": { + "type": "integer" + }, + "title": "Reason Counts", + "type": "object" + }, + "total_observations": { + "default": 0, + "title": "Total Observations", + "type": "integer" + } + }, + "title": "IdentityCoverageDecision", + "type": "object" + }, + "NoHumanReview": { + "additionalProperties": false, + "description": "Exact negative human-review projection for non-stopping states.", + "properties": { + "required": { + "const": false, + "default": false, + "title": "Required", + "type": "boolean" + }, + "required_reviewers": { + "items": { + "type": "string" + }, + "maxItems": 0, + "title": "Required Reviewers", + "type": "array" + }, + "why": { + "default": null, + "title": "Why", + "type": "null" + } + }, + "required": [ + "required", + "why", + "required_reviewers" + ], + "title": "NoHumanReview", + "type": "object" + }, + "PolicyPredicateEvidence": { + "additionalProperties": false, + "description": "One tri-state policy predicate and the evidence that supports it.", + "properties": { + "claim_ids": { + "items": { + "type": "string" + }, + "title": "Claim Ids", + "type": "array" + }, + "confidence": { + "default": "low", + "enum": [ + "low", + "medium", + "high" + ], + "title": "Confidence", + "type": "string" + }, + "evidence_bases": { + "items": { + "enum": [ + "reviewed_declaration", + "protocol_structure", + "typed_provider_fact", + "structural_scope", + "inferred_keyword", + "inferred_regex", + "protocol_default", + "unknown" + ], + "type": "string" + }, + "title": "Evidence Bases", + "type": "array" + }, + "expected": { + "default": null, + "title": "Expected" + }, + "observed": { + "default": null, + "title": "Observed" + }, + "policy_eligible": { + "default": false, + "title": "Policy Eligible", + "type": "boolean" + }, + "predicate": { + "title": "Predicate", + "type": "string" + }, + "status": { + "enum": [ + "matched", + "not_matched", + "indeterminate", + "conflicting" + ], + "title": "Status", + "type": "string" + }, + "why": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Why" + } + }, + "required": [ + "predicate", + "status" + ], + "title": "PolicyPredicateEvidence", + "type": "object" + }, + "ReleaseDecision": { + "properties": { + "baseline_delta": { + "$ref": "#/$defs/BaselineDelta" + }, + "blockers": { + "items": { + "$ref": "#/$defs/ReleaseDecisionItem" + }, + "title": "Blockers", + "type": "array" + }, + "contribution_rules": { + "items": { + "$ref": "#/$defs/ContributionRule" + }, + "title": "Contribution Rules", + "type": "array" + }, + "decision": { + "enum": [ + "blocked", + "review_required", + "insufficient_evidence", + "passed" + ], + "title": "Decision", + "type": "string" + }, + "evidence_coverage": { + "$ref": "#/$defs/EvidenceCoverageDecision" + }, + "fail_policy": { + "$ref": "#/$defs/FailPolicy" + }, + "reason": { + "title": "Reason", + "type": "string" + }, + "review_items": { + "items": { + "$ref": "#/$defs/ReleaseDecisionItem" + }, + "title": "Review Items", + "type": "array" + }, + "runtime_behavior_verified": { + "const": false, + "default": false, + "title": "Runtime Behavior Verified", + "type": "boolean" + }, + "static_analysis_only": { + "const": true, + "default": true, + "title": "Static Analysis Only", + "type": "boolean" + }, + "static_verdict_disclaimer": { + "default": "This verdict covers deterministic static evidence only. Agents Shipgate did not execute the agent or prove runtime behavior, tool routing, credential enforcement, or safety.", + "title": "Static Verdict Disclaimer", + "type": "string" + } + }, + "required": [ + "decision", + "reason", + "evidence_coverage", + "baseline_delta", + "fail_policy" + ], + "title": "ReleaseDecision", + "type": "object" + }, + "ReleaseDecisionItem": { + "properties": { + "baseline_status": { + "anyOf": [ + { + "enum": [ + "new", + "matched", + "resolved" + ], + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Baseline Status" + }, + "blocks_release": { + "default": false, + "title": "Blocks Release", + "type": "boolean" + }, + "capability_refs": { + "items": { + "type": "string" + }, + "title": "Capability Refs", + "type": "array" + }, + "capability_trace_refs": { + "items": { + "type": "string" + }, + "title": "Capability Trace Refs", + "type": "array" + }, + "check_id": { + "title": "Check Id", + "type": "string" + }, + "fingerprint": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Fingerprint" + }, + "id": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Id" + }, + "policy_evidence_source": { + "anyOf": [ + { + "$ref": "#/$defs/SourceReference" + }, + { + "type": "null" + } + ], + "default": null + }, + "severity": { + "enum": [ + "info", + "low", + "medium", + "high", + "critical" + ], + "title": "Severity", + "type": "string" + }, + "source": { + "anyOf": [ + { + "$ref": "#/$defs/SourceReference" + }, + { + "type": "null" + } + ], + "default": null + }, + "support": { + "anyOf": [ + { + "$ref": "#/$defs/FindingSupport" + }, + { + "type": "null" + } + ], + "default": null + }, + "title": { + "title": "Title", + "type": "string" + } + }, + "required": [ + "check_id", + "severity", + "title" + ], + "title": "ReleaseDecisionItem", + "type": "object" + }, + "RequiredHumanReview": { + "additionalProperties": false, + "description": "Human-review evidence carried by the stopping state.", + "properties": { + "required": { + "const": true, + "default": true, + "title": "Required", + "type": "boolean" + }, + "required_reviewers": { + "items": { + "minLength": 1, + "type": "string" + }, + "title": "Required Reviewers", + "type": "array" + }, + "why": { + "minLength": 1, + "title": "Why", + "type": "string" + } + }, + "required": [ + "required", + "why", + "required_reviewers" + ], + "title": "RequiredHumanReview", + "type": "object" + }, + "SemanticCoverageDecision": { + "description": "v0.29 pass eligibility across the normalized action surface.\n\nUnlike extraction-confidence thresholds, semantic gaps are\nzero-tolerance: any non-pass-eligible unknown/partial/conflicting\ndimension prevents ``passed``. Known authority review concerns (for\nexample ambient or unscoped credentials) are counted separately so\nthey deterministically route to ``review_required`` rather than\n``insufficient_evidence``.", + "properties": { + "gap_count": { + "default": 0, + "title": "Gap Count", + "type": "integer" + }, + "pass_eligible_actions": { + "default": 0, + "title": "Pass Eligible Actions", + "type": "integer" + }, + "reason_counts": { + "additionalProperties": { + "type": "integer" + }, + "title": "Reason Counts", + "type": "object" + }, + "review_concern_count": { + "default": 0, + "title": "Review Concern Count", + "type": "integer" + }, + "total_actions": { + "default": 0, + "title": "Total Actions", + "type": "integer" + } + }, + "title": "SemanticCoverageDecision", + "type": "object" + }, + "SourceReference": { + "additionalProperties": true, + "properties": { + "end_line": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "default": null, + "title": "End Line" + }, + "location": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Location" + }, + "path": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Path" + }, + "pointer": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Pointer" + }, + "ref": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Ref" + }, + "start_column": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Start Column" + }, + "start_line": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Start Line" + }, + "type": { + "title": "Type", + "type": "string" + } + }, + "required": [ + "type" + ], + "title": "SourceReference", + "type": "object" + }, + "VerifierCapabilityChange": { + "additionalProperties": false, + "description": "One reviewer-facing capability change projected for verifier output.", + "properties": { + "change_bucket": { + "enum": [ + "added", + "modified", + "removed" + ], + "title": "Change Bucket", + "type": "string" + }, + "change_type": { + "title": "Change Type", + "type": "string" + }, + "id": { + "title": "Id", + "type": "string" + }, + "impact": { + "default": "informational", + "enum": [ + "blocks_release", + "review_required", + "insufficient_evidence", + "informational", + "none" + ], + "title": "Impact", + "type": "string" + }, + "rationale": { + "title": "Rationale", + "type": "string" + }, + "related_finding_ids": { + "items": { + "type": "string" + }, + "title": "Related Finding Ids", + "type": "array" + }, + "source_path": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Source Path" + }, + "source_start_line": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Source Start Line" + }, + "subject": { + "title": "Subject", + "type": "string" + }, + "subject_kind": { + "title": "Subject Kind", + "type": "string" + } + }, + "required": [ + "id", + "change_type", + "change_bucket", + "subject_kind", + "subject", + "rationale" + ], + "title": "VerifierCapabilityChange", + "type": "object" + }, + "VerifierCapabilityReview": { + "additionalProperties": false, + "description": "Derived capability-review rollup for PR comments and Action outputs.\n\nThis is a projection only. It never gates independently of\n``report.json.release_decision.decision``.", + "properties": { + "added": { + "default": 0, + "title": "Added", + "type": "integer" + }, + "modified": { + "default": 0, + "title": "Modified", + "type": "integer" + }, + "notes": { + "items": { + "type": "string" + }, + "title": "Notes", + "type": "array" + }, + "policy_weakened": { + "default": false, + "title": "Policy Weakened", + "type": "boolean" + }, + "removed": { + "default": 0, + "title": "Removed", + "type": "integer" + }, + "top_changes": { + "items": { + "$ref": "#/$defs/VerifierCapabilityChange" + }, + "title": "Top Changes", + "type": "array" + }, + "trust_root_touched": { + "default": false, + "title": "Trust Root Touched", + "type": "boolean" + } + }, + "title": "VerifierCapabilityReview", + "type": "object" + }, + "VerifierFixTask": { + "additionalProperties": false, + "allOf": [ + { + "if": { + "properties": { + "actor": { + "const": "human" + } + }, + "required": [ + "actor" + ] + }, + "then": { + "properties": { + "safe_to_attempt": { + "const": false + } + } + } + }, + { + "if": { + "properties": { + "actor": { + "const": "coding_agent" + }, + "safe_to_attempt": { + "const": true + } + }, + "required": [ + "actor", + "safe_to_attempt" + ] + }, + "then": { + "properties": { + "verification_command": { + "minLength": 1, + "pattern": "\\S", + "type": "string" + } + }, + "required": [ + "verification_command" + ] + } + } + ], + "description": "The single repair task a verify run hands to whoever acts next.\n\nRouting is deterministic and projected from the head scan \u2014 never an LLM\njudgment. ``coding_agent`` + ``safe_to_attempt=True`` means the gating\ngaps are mechanical (every gating finding is ``autofix_safe``): the agent\nmay fix them and re-run ``verification_command``. ``human`` +\n``safe_to_attempt=False`` means an authority gap a coding agent must not\ninvent its way past \u2014 missing approval/idempotency evidence, a weakened\npolicy, or a touched trust root. ``forbidden_shortcuts`` are the\nreward-hacking moves that are never acceptable for either actor.\n``patches`` (v0.12+) carries the machine-applicable suggested patches for\nthe gating findings when verify ran with ``--suggest-patches`` and the\ntask routes to the coding agent.", + "properties": { + "actor": { + "enum": [ + "coding_agent", + "human" + ], + "title": "Actor", + "type": "string" + }, + "allowed_repairs": { + "items": { + "$ref": "#/$defs/VerifierRepair" + }, + "title": "Allowed Repairs", + "type": "array" + }, + "forbidden_repairs": { + "items": { + "$ref": "#/$defs/VerifierRepair" + }, + "title": "Forbidden Repairs", + "type": "array" + }, + "forbidden_shortcuts": { + "items": { + "type": "string" + }, + "title": "Forbidden Shortcuts", + "type": "array" + }, + "instructions": { + "items": { + "type": "string" + }, + "title": "Instructions", + "type": "array" + }, + "patches": { + "items": { + "$ref": "#/$defs/VerifierFixTaskPatch" + }, + "title": "Patches", + "type": "array" + }, + "safe_to_attempt": { + "title": "Safe To Attempt", + "type": "boolean" + }, + "verification_command": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Verification Command" + } + }, + "required": [ + "actor", + "safe_to_attempt" + ], + "title": "VerifierFixTask", + "type": "object" + }, + "VerifierFixTaskPatch": { + "additionalProperties": false, + "description": "A machine-applicable patch projected into the fix task.\n\nRepair aid only \u2014 never a gate input. ``patch`` carries the\ndiscriminated Patch payload (``set_pointer`` / ``append_pointer`` /\n``remove_pointer``) exactly as the head scan emitted it; ``manual``\npatches are intentionally excluded because their guidance already\nappears in ``instructions``.", + "properties": { + "check_id": { + "default": "", + "title": "Check Id", + "type": "string" + }, + "finding_id": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Finding Id" + }, + "patch": { + "additionalProperties": true, + "title": "Patch", + "type": "object" + } + }, + "title": "VerifierFixTaskPatch", + "type": "object" + }, + "VerifierRepair": { + "additionalProperties": false, + "description": "One deterministic repair affordance or prohibition.\n\nThe verifier owns the actor and safety boundary. These rows are not model\nsuggestions: they are a structured projection of remediation metadata and\ntrust-root rules so coding agents can distinguish mechanical fixes from\nhuman-only authority decisions.", + "properties": { + "actor": { + "enum": [ + "coding_agent", + "human" + ], + "title": "Actor", + "type": "string" + }, + "check_id": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Check Id" + }, + "command": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Command" + }, + "finding_id": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Finding Id" + }, + "id": { + "title": "Id", + "type": "string" + }, + "kind": { + "title": "Kind", + "type": "string" + }, + "reason": { + "title": "Reason", + "type": "string" + }, + "target": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Target" + } + }, + "required": [ + "id", + "actor", + "kind", + "reason" + ], + "title": "VerifierRepair", + "type": "object" + } + }, + "$id": "https://raw.githubusercontent.com/ThreeMoonsLab/agents-shipgate/main/docs/verifier-schema.v0.4.json", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "additionalProperties": false, + "allOf": [ + { + "if": { + "properties": { + "decision": { + "const": "passed" + } + }, + "required": [ + "decision" + ] + }, + "then": { + "properties": { + "applicability": { + "const": "verified" + }, + "can_merge_without_human": { + "const": true + }, + "capability_review": { + "properties": { + "policy_weakened": { + "const": false + }, + "trust_root_touched": { + "const": false + } + } + }, + "control": { + "properties": { + "state": { + "const": "complete" + } + }, + "required": [ + "state" + ] + }, + "execution": { + "const": "succeeded" + }, + "fix_task": { + "type": "null" + }, + "head_status": { + "const": "succeeded" + }, + "merge_verdict": { + "const": "mergeable" + }, + "release_decision": { + "properties": { + "blockers": { + "maxItems": 0 + }, + "decision": { + "const": "passed" + }, + "evidence_coverage": { + "properties": { + "evidence_gaps": { + "maxItems": 0 + }, + "human_review_recommended": { + "const": false + } + } + }, + "review_items": { + "maxItems": 0 + } + } + } + } + } + }, + { + "else": { + "properties": { + "control": { + "properties": { + "state": { + "enum": [ + "agent_action_required", + "human_review_required" + ] + } + }, + "required": [ + "state" + ] + } + } + }, + "if": { + "properties": { + "can_merge_without_human": { + "const": true + } + }, + "required": [ + "can_merge_without_human" + ] + }, + "then": { + "oneOf": [ + { + "properties": { + "applicability": { + "const": "verified" + }, + "decision": { + "const": "passed" + }, + "execution": { + "const": "succeeded" + } + } + }, + { + "properties": { + "applicability": { + "const": "not_applicable" + }, + "decision": { + "type": "null" + }, + "execution": { + "const": "skipped" + } + } + } + ], + "properties": { + "control": { + "properties": { + "state": { + "const": "complete" + } + }, + "required": [ + "state" + ] + } + } + } + } + ], + "description": "JSON Schema for verifier.json. Generated from agents_shipgate.schemas.verifier.VerifierArtifact. Do not edit by hand.", + "properties": { + "agent_summary": { + "anyOf": [ + { + "additionalProperties": true, + "type": "object" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Agent Summary" + }, + "applicability": { + "default": "not_evaluated", + "enum": [ + "not_evaluated", + "verified", + "not_applicable", + "failed" + ], + "title": "Applicability", + "type": "string" + }, + "artifacts": { + "additionalProperties": { + "type": "string" + }, + "title": "Artifacts", + "type": "object" + }, + "base_notes": { + "items": { + "type": "string" + }, + "title": "Base Notes", + "type": "array" + }, + "base_ref": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Base Ref" + }, + "base_report_json": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Base Report Json" + }, + "base_status": { + "default": "not_requested", + "enum": [ + "not_requested", + "skipped", + "diff_from_provided", + "ref_missing", + "archive_failed", + "missing_manifest", + "scan_failed", + "cache_hit", + "succeeded" + ], + "title": "Base Status", + "type": "string" + }, + "base_tree_sha": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Base Tree Sha" + }, + "can_merge_without_human": { + "default": false, + "title": "Can Merge Without Human", + "type": "boolean" + }, + "capability_review": { + "$ref": "#/$defs/VerifierCapabilityReview" + }, + "changed_files": { + "items": { + "type": "string" + }, + "title": "Changed Files", + "type": "array" + }, + "config": { + "title": "Config", + "type": "string" + }, + "control": { + "$ref": "#/$defs/AgentControl" + }, + "decision": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Decision" + }, + "diff_text_available": { + "default": false, + "title": "Diff Text Available", + "type": "boolean" + }, + "execution": { + "default": "not_run", + "enum": [ + "not_run", + "succeeded", + "skipped", + "failed" + ], + "title": "Execution", + "type": "string" + }, + "fix_task": { + "anyOf": [ + { + "$ref": "#/$defs/VerifierFixTask" + }, + { + "type": "null" + } + ], + "default": null + }, + "forbidden_actions": { + "items": { + "type": "string" + }, + "title": "Forbidden Actions", + "type": "array" + }, + "forbidden_file_edits": { + "items": { + "type": "string" + }, + "title": "Forbidden File Edits", + "type": "array" + }, + "head_exit_code": { + "default": 0, + "title": "Head Exit Code", + "type": "integer" + }, + "head_ref": { + "default": "HEAD", + "title": "Head Ref", + "type": "string" + }, + "head_report_json": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Head Report Json" + }, + "head_status": { + "default": "not_run", + "enum": [ + "not_run", + "succeeded", + "skipped", + "failed" + ], + "title": "Head Status", + "type": "string" + }, + "head_tree_sha": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Head Tree Sha" + }, + "headline": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Headline" + }, + "merge_verdict": { + "default": "unknown", + "enum": [ + "mergeable", + "human_review_required", + "insufficient_evidence", + "blocked", + "unknown" + ], + "title": "Merge Verdict", + "type": "string" + }, + "mode": { + "default": "advisory", + "title": "Mode", + "type": "string" + }, + "release_decision": { + "anyOf": [ + { + "$ref": "#/$defs/ReleaseDecision" + }, + { + "type": "null" + } + ], + "default": null + }, + "reviewer_summary": { + "anyOf": [ + { + "additionalProperties": true, + "type": "object" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Reviewer Summary" + }, + "runtime_behavior_verified": { + "const": false, + "default": false, + "title": "Runtime Behavior Verified", + "type": "boolean" + }, + "static_analysis_only": { + "const": true, + "default": true, + "title": "Static Analysis Only", + "type": "boolean" + }, + "static_verdict_disclaimer": { + "default": "This verdict covers deterministic static evidence only. Agents Shipgate did not execute the agent or prove runtime behavior, tool routing, credential enforcement, or safety.", + "title": "Static Verdict Disclaimer", + "type": "string" + }, + "trigger": { + "additionalProperties": true, + "title": "Trigger", + "type": "object" + }, + "verifier_schema_version": { + "const": "0.4", + "default": "0.4", + "title": "Verifier Schema Version", + "type": "string" + }, + "workspace": { + "title": "Workspace", + "type": "string" + } + }, + "required": [ + "workspace", + "config", + "control" + ], + "title": "Agents Shipgate Verifier Artifact v0.4", + "type": "object" +} diff --git a/examples/agent-protocol/expected/block-stop.json b/examples/agent-protocol/expected/block-stop.json index f0249b92..9dfcd09e 100644 --- a/examples/agent-protocol/expected/block-stop.json +++ b/examples/agent-protocol/expected/block-stop.json @@ -3,7 +3,7 @@ "agent": "codex", "tool": { "name": "agents-shipgate", - "version": "0.16.0b4" + "version": "0.16.0b5" }, "subject": { "agent": "codex" diff --git a/examples/agent-protocol/expected/missing-install.json b/examples/agent-protocol/expected/missing-install.json index 60484543..e0aaa45d 100644 --- a/examples/agent-protocol/expected/missing-install.json +++ b/examples/agent-protocol/expected/missing-install.json @@ -3,7 +3,7 @@ "agent": "codex", "tool": { "name": "agents-shipgate", - "version": "0.16.0b4" + "version": "0.16.0b5" }, "subject": { "agent": "codex" diff --git a/examples/agent-protocol/expected/policy-bypass.json b/examples/agent-protocol/expected/policy-bypass.json index b8a2484f..b8e8f4d0 100644 --- a/examples/agent-protocol/expected/policy-bypass.json +++ b/examples/agent-protocol/expected/policy-bypass.json @@ -3,7 +3,7 @@ "agent": "codex", "tool": { "name": "agents-shipgate", - "version": "0.16.0b4" + "version": "0.16.0b5" }, "subject": { "agent": "codex" diff --git a/examples/agent-protocol/expected/repair-after.json b/examples/agent-protocol/expected/repair-after.json index 853e40ab..5f35ef77 100644 --- a/examples/agent-protocol/expected/repair-after.json +++ b/examples/agent-protocol/expected/repair-after.json @@ -3,7 +3,7 @@ "agent": "codex", "tool": { "name": "agents-shipgate", - "version": "0.16.0b4" + "version": "0.16.0b5" }, "subject": { "agent": "codex" diff --git a/examples/agent-protocol/expected/repair-before.json b/examples/agent-protocol/expected/repair-before.json index 2655ce7e..e23d2766 100644 --- a/examples/agent-protocol/expected/repair-before.json +++ b/examples/agent-protocol/expected/repair-before.json @@ -3,7 +3,7 @@ "agent": "codex", "tool": { "name": "agents-shipgate", - "version": "0.16.0b4" + "version": "0.16.0b5" }, "subject": { "agent": "codex" diff --git a/examples/agent-protocol/expected/stale-install.json b/examples/agent-protocol/expected/stale-install.json index aebe4532..999c8b36 100644 --- a/examples/agent-protocol/expected/stale-install.json +++ b/examples/agent-protocol/expected/stale-install.json @@ -3,7 +3,7 @@ "agent": "codex", "tool": { "name": "agents-shipgate", - "version": "0.16.0b4" + "version": "0.16.0b5" }, "subject": { "agent": "codex" diff --git a/llms-full.txt b/llms-full.txt index 53d8b0bb..4945b498 100644 --- a/llms-full.txt +++ b/llms-full.txt @@ -388,7 +388,7 @@ restate version archaeology here): - Audit envelopes: `release_decision.contribution_rules[]`, `policy_audit`, `privacy_audit`, `heuristics_filter` — explanatory, never a second gate -The current schema is [`docs/report-schema.v0.32.json`](docs/report-schema.v0.32.json). Emitted reports carry `report_schema_version: "0.32"`; v0.32 adds the required Conductor OSS framework summary over v0.31's root-reachable binding contract. A `passed` result requires a complete root-reachable static binding graph plus complete, conflict-free identity, effect, and authority evidence for every reachable action, evaluation of all applicable controls, and no policy condition requiring review. Every release decision explicitly carries `static_analysis_only: true`, `runtime_behavior_verified: false`, and `static_verdict_disclaimer`; packet §1 mirrors them. Binding and semantic gaps are not Findings and cannot be suppressed or baselined. See [`docs/passed-verdict-contract.md`](docs/passed-verdict-contract.md) for the exact claim and [`docs/agent-contract-current.md`](docs/agent-contract-current.md) for version history. v0.31 remains frozen at [`docs/report-schema.v0.31.json`](docs/report-schema.v0.31.json). +The current schema is [`docs/report-schema.v0.33.json`](docs/report-schema.v0.33.json). Emitted reports carry `report_schema_version: "0.33"`; typed predicate support prevents heuristic evidence from being upgraded by policy severity or block metadata. A `passed` result requires a complete root-reachable static binding graph plus complete, conflict-free identity, effect, authority, and applicable-policy evidence for every reachable action. Every release decision explicitly carries `static_analysis_only: true`, `runtime_behavior_verified: false`, and `static_verdict_disclaimer`; packet §1 mirrors them. Binding, semantic, and policy-applicability gaps are not Findings and cannot be suppressed or baselined. See [`docs/passed-verdict-contract.md`](docs/passed-verdict-contract.md) for the exact claim and [`docs/agent-contract-current.md`](docs/agent-contract-current.md) for version history. v0.32 remains frozen at [`docs/report-schema.v0.32.json`](docs/report-schema.v0.32.json). **Release gating signal**: prefer `release_decision.decision` (`"blocked" | "review_required" | "insufficient_evidence" | "passed"`) over `summary.status`. The new field is **baseline-aware** — a baseline-matched critical surfaces in `release_decision.review_items` (accepted debt), not `release_decision.blockers`. `summary.status` stays baseline-blind for v0.7 compatibility, so a baseline-matched-only critical produces both `summary.status = "release_blockers_detected"` AND `release_decision.decision = "review_required"` (intentional divergence — see [STABILITY.md](STABILITY.md#release_decisiondecision-vs-summarystatus)). `insufficient_evidence` (added v0.14) signals that the scan saw too many low-confidence tools or source-loader warnings to be trustworthy; consumers that switch on the enum must fall back to `review_required` for unknown future values. @@ -468,7 +468,7 @@ validation and [`docs/manifest-v0.1.md`](docs/manifest-v0.1.md) for prose. ### Where is the report schema? Parse `agents-shipgate-reports/report.json` and validate against -[`docs/report-schema.v0.32.json`](docs/report-schema.v0.32.json) (current). +[`docs/report-schema.v0.33.json`](docs/report-schema.v0.33.json) (current). Older reports (`report_schema_version: "0.10"`) validate against the frozen [`docs/report-schema.v0.10.json`](docs/report-schema.v0.10.json). Do not scrape Markdown when JSON is available. @@ -506,7 +506,8 @@ For the short, current statement of "which fields to read", see [`docs/agent-con | What | Path | Stable | |---|---|---| | Manifest schema | [`docs/manifest-v0.1.json`](docs/manifest-v0.1.json) | `0.1` | -| Report schema (current) | [`docs/report-schema.v0.32.json`](docs/report-schema.v0.32.json) | `0.32` | +| Report schema (current) | [`docs/report-schema.v0.33.json`](docs/report-schema.v0.33.json) | `0.33` | +| Report schema (v0.32 frozen reference) | [`docs/report-schema.v0.32.json`](docs/report-schema.v0.32.json) | `0.32` | | Report schema (v0.31 frozen reference) | [`docs/report-schema.v0.31.json`](docs/report-schema.v0.31.json) | `0.31` | | Report schema (v0.30 frozen reference) | [`docs/report-schema.v0.30.json`](docs/report-schema.v0.30.json) | `0.30` | | Report schema (v0.29 frozen reference) | [`docs/report-schema.v0.29.json`](docs/report-schema.v0.29.json) | `0.29` | @@ -515,7 +516,7 @@ For the short, current statement of "which fields to read", see [`docs/agent-con | Report schema (v0.26 frozen reference) | [`docs/report-schema.v0.26.json`](docs/report-schema.v0.26.json) | `0.26` | | Report schema (v0.25 frozen reference) | [`docs/report-schema.v0.25.json`](docs/report-schema.v0.25.json) | `0.25` | | Verify-run schema | [`docs/verify-run-schema.v2.json`](docs/verify-run-schema.v2.json) | `shipgate.verify_run/v2` | -| Agent handoff schema | [`docs/agent-handoff-schema.v3.json`](docs/agent-handoff-schema.v3.json) | `shipgate.agent_handoff/v3` | +| Agent handoff schema | [`docs/agent-handoff-schema.v4.json`](docs/agent-handoff-schema.v4.json) | `shipgate.agent_handoff/v4` | | Agent boundary result schema | [`docs/agent-boundary-result-schema.v1.json`](docs/agent-boundary-result-schema.v1.json) | `shipgate.agent_boundary_result/v1` | | Codex boundary result schema (deprecated frozen projection) | [`docs/codex-boundary-result-schema.v2.json`](docs/codex-boundary-result-schema.v2.json) | `shipgate.codex_boundary_result/v2` | | Report schema (v0.24 frozen reference) | [`docs/report-schema.v0.24.json`](docs/report-schema.v0.24.json) | `0.24` | @@ -537,17 +538,17 @@ For the short, current statement of "which fields to read", see [`docs/agent-con | Report schema (v0.8 frozen reference) | [`docs/report-schema.v0.8.json`](docs/report-schema.v0.8.json) | `0.8` | | Report schema (v0.7 frozen reference) | [`docs/report-schema.v0.7.json`](docs/report-schema.v0.7.json) | `0.7` | | Report schema (v0.6 frozen reference) | [`docs/report-schema.v0.6.json`](docs/report-schema.v0.6.json) | `0.6` | -| Packet schema (Release Evidence Packet, latest) | [`docs/packet-schema.v0.10.json`](docs/packet-schema.v0.10.json) | `0.10` | +| Packet schema (Release Evidence Packet, latest) | [`docs/packet-schema.v0.11.json`](docs/packet-schema.v0.11.json) | `0.11` | | Agent result schema (current) | [`docs/agent-result-schema.v2.json`](docs/agent-result-schema.v2.json) | `agent_result_v2` | -| Verifier schema (current) | [`docs/verifier-schema.v0.3.json`](docs/verifier-schema.v0.3.json) | `0.3` | -| Agent handoff schema (current) | [`docs/agent-handoff-schema.v3.json`](docs/agent-handoff-schema.v3.json) | `shipgate.agent_handoff/v3` | +| Verifier schema (current) | [`docs/verifier-schema.v0.4.json`](docs/verifier-schema.v0.4.json) | `0.4` | +| Agent handoff schema (current) | [`docs/agent-handoff-schema.v4.json`](docs/agent-handoff-schema.v4.json) | `shipgate.agent_handoff/v4` | | Preflight schema (current) | [`docs/preflight-schema.v0.3.json`](docs/preflight-schema.v0.3.json) | `0.3` | | Host-grants inventory schema | [`docs/host-grants-inventory-schema.v0.2.json`](docs/host-grants-inventory-schema.v0.2.json) | `0.2` | | Host-grants baseline schema | [`docs/host-grants-baseline-schema.v0.2.json`](docs/host-grants-baseline-schema.v0.2.json) | `0.2` | | Host-grants drift schema | [`docs/host-grants-drift-schema.v0.2.json`](docs/host-grants-drift-schema.v0.2.json) | `0.2` | -| Capability standard | [`docs/capability-standard.md`](docs/capability-standard.md) | `0.4` | -| Capability lock schema | [`docs/capability-lock-schema.v0.5.json`](docs/capability-lock-schema.v0.5.json) | `0.5` | -| Capability lock diff schema | [`docs/capability-lock-diff-schema.v0.6.json`](docs/capability-lock-diff-schema.v0.6.json) | `0.6` | +| Capability standard | [`docs/capability-standard.md`](docs/capability-standard.md) | `0.5` | +| Capability lock schema | [`docs/capability-lock-schema.v0.6.json`](docs/capability-lock-schema.v0.6.json) | `0.6` | +| Capability lock diff schema | [`docs/capability-lock-diff-schema.v0.7.json`](docs/capability-lock-diff-schema.v0.7.json) | `0.7` | | Governance benchmark catalog schema | [`docs/governance-benchmark-catalog-schema.v0.2.json`](docs/governance-benchmark-catalog-schema.v0.2.json) | `0.2` | | Governance benchmark result schema | [`docs/governance-benchmark-result-schema.v0.2.json`](docs/governance-benchmark-result-schema.v0.2.json) | `0.2` | | Check catalog | [`docs/checks.json`](docs/checks.json) | regenerated each release | @@ -607,7 +608,7 @@ NOT prove. Use `--no-packet` / `--packet-format` on `scan`, and `agents-shipgate evidence-packet --from ` to re-render. The full packet contract (fixed sections, disclaimers, `evidence_matrix` rules) lives in -[STABILITY.md §Release Evidence Packet](STABILITY.md#release-evidence-packet-v010) +[STABILITY.md §Release Evidence Packet](STABILITY.md#release-evidence-packet-v011) and [`docs/agent-contract-current.md`](docs/agent-contract-current.md#read-these-for-release-review). Exit codes (stable): @@ -1028,17 +1029,17 @@ Verify the installed CLI contract locally before relying on hard-coded docs: agents-shipgate contract --json ``` -Runtime contract v15 retains the v14 unambiguous `AgentControl` and v13 -root-reachable agent-to-tool binding contracts. It adds one host-neutral, -scope-bound boundary assessment across -check, preflight, verify, handoff, MCP, and GitHub Action projections. Agents +Runtime contract v16 retains the v15 host-neutral boundary, v14 unambiguous +`AgentControl`, and v13 root-reachable binding contracts. It adds typed +evidence bases and tri-state policy applicability across scan, verify, packet, +and handoff projections. Agents switch on `control.state`; `decision` remains diagnostic and `release_decision.decision` remains the release gate. Contract v14 requires `completion_allowed == (state == "complete")` and -`must_stop == (state == "human_review_required")`. Report v0.32 adds the -required Conductor OSS summary over frozen v0.31; packet v0.10, capability -standard v0.4, capability lock v0.5, and capability-lock diff v0.6 remain -unchanged by the control-contract milestone. +`must_stop == (state == "human_review_required")`. Report v0.33 prevents rule +metadata from upgrading heuristic evidence; packet v0.11, capability standard +v0.5, capability lock v0.6, and capability-lock diff v0.7 project the same +typed support. The runtime contract also exposes the local agent command spec: `primary_commands{}`, `commands{}`, `default_paths{}`, `artifacts{}`, `agent_read_order[]`, `verifier_read_order[]`, `merge_verdicts[]`, @@ -1069,21 +1070,21 @@ Downstream repos generated with `.shipgate/agent-contract.json`. - Latest release: `v0.15.0` -- In-tree runtime: `0.16.0b4` — see [pyproject.toml](../pyproject.toml) -- Runtime contract: `15` (minimum control contract: `14`) -- Current report schema: `0.32` — [`docs/report-schema.v0.32.json`](report-schema.v0.32.json) -- Current packet schema: `0.10` — [`docs/packet-schema.v0.10.json`](packet-schema.v0.10.json) +- In-tree runtime: `0.16.0b5` — see [pyproject.toml](../pyproject.toml) +- Runtime contract: `16` (minimum control contract: `14`) +- Current report schema: `0.33` — [`docs/report-schema.v0.33.json`](report-schema.v0.33.json) +- Current packet schema: `0.11` — [`docs/packet-schema.v0.11.json`](packet-schema.v0.11.json) - Current shared agent result schema: `agent_result_v2` — [`docs/agent-result-schema.v2.json`](agent-result-schema.v2.json) -- Current verifier schema: `0.3` — [`docs/verifier-schema.v0.3.json`](verifier-schema.v0.3.json) +- Current verifier schema: `0.4` — [`docs/verifier-schema.v0.4.json`](verifier-schema.v0.4.json) - Current verify-run schema: `shipgate.verify_run/v2` — [`docs/verify-run-schema.v2.json`](verify-run-schema.v2.json) -- Current agent handoff schema: `shipgate.agent_handoff/v3` — [`docs/agent-handoff-schema.v3.json`](agent-handoff-schema.v3.json) +- Current agent handoff schema: `shipgate.agent_handoff/v4` — [`docs/agent-handoff-schema.v4.json`](agent-handoff-schema.v4.json) - Current agent boundary result schema: `shipgate.agent_boundary_result/v1` — [`docs/agent-boundary-result-schema.v1.json`](agent-boundary-result-schema.v1.json) - Frozen deprecated Codex projection: `shipgate.codex_boundary_result/v2` — [`docs/codex-boundary-result-schema.v2.json`](codex-boundary-result-schema.v2.json) - Current preflight schema: `0.3` — [`docs/preflight-schema.v0.3.json`](preflight-schema.v0.3.json) -- Current downstream local agent contract schema: `4` -- Current capability standard: `0.4` — [`docs/capability-standard.md`](capability-standard.md) -- Current capability lock schema: `0.5` — [`docs/capability-lock-schema.v0.5.json`](capability-lock-schema.v0.5.json) -- Current capability lock diff schema: `0.6` — [`docs/capability-lock-diff-schema.v0.6.json`](capability-lock-diff-schema.v0.6.json) +- Current downstream local agent contract schema: `5` +- Current capability standard: `0.5` — [`docs/capability-standard.md`](capability-standard.md) +- Current capability lock schema: `0.6` — [`docs/capability-lock-schema.v0.6.json`](capability-lock-schema.v0.6.json) +- Current capability lock diff schema: `0.7` — [`docs/capability-lock-diff-schema.v0.7.json`](capability-lock-diff-schema.v0.7.json) - Current attestation schema: `0.4` — [`docs/attestation-schema.v0.4.json`](attestation-schema.v0.4.json) - Current registry schema: `0.3` — [`docs/registry-schema.v0.3.json`](registry-schema.v0.3.json) - Current org evidence bundle schema: `shipgate.org_evidence_bundle/v1` — [`docs/org-evidence-bundle-schema.v1.json`](org-evidence-bundle-schema.v1.json) @@ -1091,9 +1092,9 @@ Downstream repos generated with - Current trigger catalog schema: `0.2` — [`docs/triggers.json`](triggers.json) - Current governance benchmark catalog schema: `0.2` — [`docs/governance-benchmark-catalog-schema.v0.2.json`](governance-benchmark-catalog-schema.v0.2.json) - Current governance benchmark result schema: `0.2` — [`docs/governance-benchmark-result-schema.v0.2.json`](governance-benchmark-result-schema.v0.2.json) -- Frozen-reference report schemas: frozen [`v0.31`](report-schema.v0.31.json), frozen [`v0.30`](report-schema.v0.30.json), frozen [`v0.29`](report-schema.v0.29.json), frozen [`v0.28`](report-schema.v0.28.json), frozen [`v0.27`](report-schema.v0.27.json), frozen [`v0.26`](report-schema.v0.26.json), frozen [`v0.25`](report-schema.v0.25.json), frozen [`v0.24`](report-schema.v0.24.json), frozen [`v0.23`](report-schema.v0.23.json), frozen [`v0.22`](report-schema.v0.22.json), frozen [`v0.21`](report-schema.v0.21.json), frozen [`v0.20`](report-schema.v0.20.json), frozen [`v0.19`](report-schema.v0.19.json), frozen [`v0.18`](report-schema.v0.18.json), frozen [`v0.17`](report-schema.v0.17.json), frozen [`v0.16`](report-schema.v0.16.json), frozen [`v0.15`](report-schema.v0.15.json), frozen [`v0.14`](report-schema.v0.14.json), frozen [`v0.13`](report-schema.v0.13.json), frozen [`v0.12`](report-schema.v0.12.json), frozen [`v0.11`](report-schema.v0.11.json), frozen [`v0.10`](report-schema.v0.10.json), frozen [`v0.9`](report-schema.v0.9.json), frozen [`v0.8`](report-schema.v0.8.json), frozen [`v0.7`](report-schema.v0.7.json), frozen [`v0.6`](report-schema.v0.6.json), older +- Frozen-reference report schemas: frozen [`v0.32`](report-schema.v0.32.json), frozen [`v0.31`](report-schema.v0.31.json), frozen [`v0.30`](report-schema.v0.30.json), frozen [`v0.29`](report-schema.v0.29.json), frozen [`v0.28`](report-schema.v0.28.json), frozen [`v0.27`](report-schema.v0.27.json), frozen [`v0.26`](report-schema.v0.26.json), frozen [`v0.25`](report-schema.v0.25.json), frozen [`v0.24`](report-schema.v0.24.json), frozen [`v0.23`](report-schema.v0.23.json), frozen [`v0.22`](report-schema.v0.22.json), frozen [`v0.21`](report-schema.v0.21.json), frozen [`v0.20`](report-schema.v0.20.json), frozen [`v0.19`](report-schema.v0.19.json), frozen [`v0.18`](report-schema.v0.18.json), frozen [`v0.17`](report-schema.v0.17.json), frozen [`v0.16`](report-schema.v0.16.json), frozen [`v0.15`](report-schema.v0.15.json), frozen [`v0.14`](report-schema.v0.14.json), frozen [`v0.13`](report-schema.v0.13.json), frozen [`v0.12`](report-schema.v0.12.json), frozen [`v0.11`](report-schema.v0.11.json), frozen [`v0.10`](report-schema.v0.10.json), frozen [`v0.9`](report-schema.v0.9.json), frozen [`v0.8`](report-schema.v0.8.json), frozen [`v0.7`](report-schema.v0.7.json), frozen [`v0.6`](report-schema.v0.6.json), older - Frozen-reference packet schemas live in [`docs/INDEX.md`](INDEX.md#reference). -- Boundary v1, verifier v0.1/v0.2, verify-run v1, handoff v1/v2, and preflight +- Boundary v1, verifier v0.1/v0.2/v0.3, verify-run v1, handoff v1/v2/v3, and preflight v0.1/v0.2 remain frozen references for legacy readers. - Frozen experimental capability lock and governance benchmark result schemas live in [`docs/INDEX.md`](INDEX.md#reference). @@ -1106,7 +1107,7 @@ one decision engine. - **PR / controller flow** — an autonomous coding agent deciding *continue, repair, or stop*. Prefer `agents-shipgate-reports/agent-handoff.json` for the compact - `shipgate.agent_handoff/v3` view: lead with `control.state`, then read + `shipgate.agent_handoff/v4` view: lead with `control.state`, then read `control.next_action`, `gate.merge_verdict`, and `reproducibility.run_id` for the stable verify identity. `verifier.json` remains the authoritative controller substrate and `verify-run.json` remains the reproducibility record; finally @@ -1160,17 +1161,19 @@ In `agents-shipgate-reports/report.json`: - `release_decision.{blockers,review_items}[].capability_refs` (v0.24+) — stable capability IDs copied from the originating finding when a policy or policy-pack rule matched a `CapabilityFactV1`. Empty for findings that are not capability-policy matches. This is audit metadata only; `release_decision.decision` remains the gate. - `release_decision.{blockers,review_items}[].capability_trace_refs` (v0.25+) — stable local trace-evidence IDs copied from the originating finding when an existing trace/evidence check used declared local trace artifacts. Empty when no local trace row is relevant. This is audit metadata only; `release_decision.decision` remains the gate. - `release_decision.evidence_coverage.semantic_coverage` (v0.29+) — `{total_actions, pass_eligible_actions, gap_count, review_concern_count, reason_counts}`. A non-zero semantic `gap_count` prevents `passed`; a non-zero `review_concern_count` prevents an automatic pass and routes known unscoped/ambient authority to review. Semantic gaps are not Findings and cannot be suppressed, baselined, severity-overridden, waived by `--no-heuristics`, or satisfied by `human_ack`. +- `release_decision.evidence_coverage.policy_gap_count` and top-level `policy_evidence_gaps[]` (v0.33+) — policy applicability that is heuristic-only, mixed, unknown, or conflicting. These rows are outside Findings and cannot be suppressed, baselined, severity-overridden, acknowledged, or removed by `--no-heuristics`; any row prevents `passed`. - `release_decision.evidence_coverage.identity_coverage` (v0.30+) — `{total_observations, canonical_tools, bound_tools, pass_eligible_tools, ambiguous_name_count, gap_count, reason_counts}`. Provider-scoped observations remain separate unless an exact reviewed `tool_identity.bindings[]` entry joins them. Any ambiguous selector, invalid binding, or conflicting identity prevents `passed`. - `release_decision.evidence_coverage.evidence_gaps[]` (v0.26+; semantic kinds added v0.29) — one structured row per measurable gap: `{kind, subject, source_type, source_ref, why, next_action}`. In addition to `low_confidence_tool` and `source_warning`, v0.29 adds `incomplete_surface`, `missing_effect_evidence`, `inferred_effect_only`, `conflicting_effect_evidence`, `missing_authority_evidence`, `partial_authority_evidence`, `conflicting_authority_evidence`, and `invalid_semantic_annotation`. Semantic next actions use `declare_action_effect`, `declare_action_authority`, `provide_complete_inventory`, or `resolve_semantic_conflict`, include accepted values and exact source/manifest pointers, and are always human-routed. Their declaration placeholders carry `suggested_patch_kind="manual"`, `auto_apply=false`, and `requires_human_review=true`; they are not Patch objects. Work the rows in order instead of guessing; Agents Shipgate never auto-asserts effect or authority. - `loaded_policy_packs[].{source,sha256,sha256_status,owner}` (v0.27+) — policy-pack distribution and ownership metadata for organization audit. `sha256_status` is `"verified"` only when the manifest pin matched; otherwise it is `"unpinned"`. This is report metadata; normal pack matching and release gating still come from deterministic rules and `release_decision.decision`. -- `findings[].policy_routing` (v0.28+) — optional policy-pack owner, reviewers, and approval-routing metadata. This is non-enforcing reviewer/audit metadata, not `Finding.evidence`; it does not affect fingerprints, suppressions, baselines, `blocks_release`, or `release_decision`. Policy-pack `match` predicates and `block: true` remain the only policy-pack inputs that affect findings and release gating. +- `findings[].support` (v0.33+) — typed predicate support with status, effective confidence, policy/block eligibility, claim IDs, evidence bases, predicate rows, and `support_hash`. Rule confidence and `block: true` are ceilings/requests; they cannot upgrade the support. Baseline matching for supported findings requires the same support hash. +- `findings[].policy_routing` (v0.28+) — optional policy-pack owner, reviewers, and approval-routing metadata. This is non-enforcing reviewer/audit metadata, not `Finding.evidence`; it does not affect fingerprints, suppressions, baselines, `blocks_release`, or `release_decision`. - `release_decision.fail_policy.would_fail_ci` — `true`/`false`. Matches what the CI process will exit with. For a semantic evidence gap, strict mode emits the consistent tuple `decision="insufficient_evidence"`, `would_fail_ci=true`, `exit_code=20`; advisory mode keeps exit `0` while preserving the same non-pass decision. - `release_decision.reason` — one-sentence explanation suitable for a PR comment. -- `release_decision.contribution_rules[]` (v0.17+) — deterministic per-finding audit explaining how each `report.findings` entry was classified. Exactly one row per finding (including suppressed). Each row carries `{finding_id, fingerprint, check_id, category, rule, rationale}`. `category` ∈ `{blocker, review_item, excluded}`; `rule` ∈ `{policy_block_new, severity_block_new, policy_baseline_accepted, severity_baseline_accepted, review_required, sub_threshold, suppressed}`. Reading the contribution rule is sufficient to predict the gate outcome for that finding without re-deriving the decision logic — the closed grammar of `(rule, category)` pairs is documented in [STABILITY.md "Release decision truth table"](../STABILITY.md#release-decision-truth-table). The audit cannot disagree with `blockers[]` / `review_items[]` (the same classification powers both). +- `release_decision.contribution_rules[]` (v0.17+) — deterministic per-finding audit explaining how each `report.findings` entry was classified. Exactly one row per finding (including suppressed). In v0.33, `unsupported_evidence` records a finding that cannot contribute because its typed support is not policy-eligible. Reading the contribution rule is sufficient to predict the gate outcome without re-deriving the decision logic. - `privacy_audit` (v0.18+) — confirms the default redaction pass ran before public artifacts were written. Read `enabled`, `rules_version`, `sensitive_field_inventory_version`, `redacted_occurrence_count`, `redacted_paths[]`, and `output_surfaces[]`. `redacted_paths[]` contains structural paths and counts only, never raw values or raw hashes. - `reviewer_summary` (v0.20+) — deterministic projection of the reviewer lens surfaces and audit envelopes; the reviewer-side parallel to `agent_summary`. Read this block first when triaging a scan for a human reviewer. Carries `verdict` (mirrors `release_decision.decision`), `headline` (≤200 chars, PR-comment-friendly), per-lens activity counts (`tool_surface_changes`, `capability_misalignments`, `action_surface_changes`, `evidence_matrix_gaps`), per-audit-envelope counts (`severity_overrides_applied`, `severity_overrides_tier_crossed`, `privacy_redactions`, `baseline_integrity_issues`), and `first_recommended_surface: ReviewerSurfacePointer | None` — a deterministic pointer naming which lens/audit to open first (`{kind, name, path, why}` where `kind` ∈ `{release_decision, lens, audit, evidence_matrix}` and `name` ∈ `{tool_surface_diff, capability_intent_diff, action_surface_diff, evidence_matrix, policy_audit, privacy_audit, baseline_integrity, release_decision}`). Same inputs always produce the same output; this block cannot disagree with the underlying lens/audit data. - `heuristics_filter` (v0.21+) — top-level audit envelope describing the `--no-heuristics` CLI filter pass. Always present, even when the flag is unset (`enabled: False` with zero counts), so the report shape is stable. Carries `enabled: bool`, `excluded_provenance_kinds: list[str]` (`["keyword_heuristic", "regex_heuristic"]`), `filtered_finding_count: int`, and `filtered_by_kind: dict[str, int]` (per-kind breakdown). When `enabled: True`, findings whose `provenance_kind` is in the excluded list have been marked `suppressed=True` with `suppression_reason="filtered by --no-heuristics"` BEFORE the release decision was built — they remain in `findings[]` for transparency but no longer gate release. The filter never un-suppresses a finding; manifest-driven suppression reasons are preserved when they overlap with the filter. Useful for security/GRC reviewers who want declared-only findings. @@ -1297,7 +1300,7 @@ separately), local input hashes (`config_sha256`, `baseline_sha256`, emitted files. It has no wall-clock timestamp and is not a second gate. `agents-shipgate-reports/agent-handoff.json` carries -`schema_version: "shipgate.agent_handoff/v3"` and top-level sections +`schema_version: "shipgate.agent_handoff/v4"` and top-level sections `gate`, `control`, `fix_task`, `blocked_by[]`, `remediation_plan[]`, `capability_review`, `reproducibility`, and `artifacts`. `gate.decision` mirrors `release_decision.decision`; `gate.merge_verdict` @@ -1314,8 +1317,8 @@ from existing artifacts with: agents-shipgate agent handoff --from agents-shipgate-reports/verifier.json --json ``` -In `agents-shipgate-reports/verifier.json`, read the v0.3 fields below (full -schema [`docs/verifier-schema.v0.3.json`](verifier-schema.v0.3.json)). **Lead +In `agents-shipgate-reports/verifier.json`, read the v0.4 fields below (full +schema [`docs/verifier-schema.v0.4.json`](verifier-schema.v0.4.json)). **Lead with `control.state`.** Every field below is a mirror or deterministic projection of `report.json`; `release_decision.decision` remains the gate. @@ -1347,9 +1350,9 @@ with `control.state`.** Every field below is a mirror or deterministic projectio - `decision` — mirror of `release_decision.decision` (or `null` when no scan ran). - `headline` — single-sentence, PR-comment-friendly summary (or `null`). - `control.human_review` and `control.next_action` are the only serialized - human-review and next-action authority in verifier v0.3. + human-review and next-action authority in verifier v0.4. - `AgentController`, `VerifierNextAction`, and `VerifierHumanReview` remain - importable only as deprecated v0.1/v0.2 reader models. Verifier v0.3 does not + importable only as deprecated v0.1/v0.2 reader models. Verifier v0.4 does not emit or invoke the retired `build_agent_controller` projector. - `fix_task` — `{actor, safe_to_attempt, instructions[], allowed_repairs[], forbidden_repairs[], forbidden_shortcuts[], verification_command, patches[]}` or `null`. @@ -1521,7 +1524,7 @@ Per-finding `provenance_kind` enum (v0.15+), additive classification — read th - `ast_extraction` — Tool parsed from user Python source by a framework extractor (LangChain function/structured tools, CrewAI function/class tools, ADK Python toolsets). Subject to extraction errors; agents that distrust AST quality may filter these as a class. - `keyword_heuristic` — matched a keyword list (broad-scope tokens, read-only/approval prompt terms, free-text parameter names). Higher false-positive risk than declarative facts. - `regex_heuristic` — matched a regex (secret-like values in descriptions, prompt-injection patterns). Highest false-positive risk; pair with the recommendation before acting. -- `policy_pack` — emitted by an external policy pack rule. The rule's own confidence applies — Shipgate does not second-guess the pack. +- `policy_pack` — emitted by an external policy pack rule after its predicates have authoritative typed support. Rule confidence can lower, but never raise, evidence confidence. - `runtime_trace` — derived from declared local trace artifacts. Audit evidence only; never filtered by `--no-heuristics`. Provenance generally follows the rule's own trigger (e.g., a rule that checks for a declared manifest field is `static_declaration` even when the underlying Tool was AST-extracted). For framework checks that fire across both AST and declarative tool sources (ADK's per-tool checks against `google_adk_function` AND `google_adk_config` tools), the label tracks the underlying tool's source. Third-party plugin checks that don't yet set the field land at `static_declaration` by default — pre-v0.15 plugins continue to validate against the v0.15 wire schema. Use `findings[].source.type` for the precise underlying tool source. @@ -1541,8 +1544,8 @@ For reviewer-shaped output, also read the **Release Evidence Packet** at `[pdf]` extras are installed). The packet is a supporting/provisional reviewer projection, not a second gate. Packet outputs are redacted by the same default privacy layer as the report. The packet has fixed reviewer sections governed by -[`docs/packet-schema.v0.10.json`](packet-schema.v0.10.json) — see -[STABILITY.md §Release Evidence Packet](../STABILITY.md#release-evidence-packet-v010). +[`docs/packet-schema.v0.11.json`](packet-schema.v0.11.json) — see +[STABILITY.md §Release Evidence Packet](../STABILITY.md#release-evidence-packet-v011). Packet schema `0.9` carries the report's evidence-backed semantic coverage and gap remediation contract. Packet §1 also mirrors `static_analysis_only=true`, `runtime_behavior_verified=false`, and @@ -1595,9 +1598,9 @@ Companion prompt: [`prompts/explain-finding-to-user.md`](../prompts/explain-find - [STABILITY.md](../STABILITY.md) — full alpha stability contract. Source of truth for everything above. - [AGENTS.md](../AGENTS.md) — agent-facing instructions: install, run, single-turn flow, error semantics. -- [`docs/report-schema.v0.32.json`](report-schema.v0.32.json) — machine-validatable JSON Schema for the current report. +- [`docs/report-schema.v0.33.json`](report-schema.v0.33.json) — machine-validatable JSON Schema for the current report. - [`docs/privacy.md`](privacy.md) and [`docs/report-sensitive-fields.json`](report-sensitive-fields.json) — default redaction behavior and sensitive-field inventory. -- [`docs/packet-schema.v0.10.json`](packet-schema.v0.10.json) — machine-validatable JSON Schema for the current packet. +- [`docs/packet-schema.v0.11.json`](packet-schema.v0.11.json) — machine-validatable JSON Schema for the current packet. - [`docs/checks.json`](checks.json) — check catalog, including `mvp_tier` for MVP/readiness triage. ## See also @@ -3139,7 +3142,7 @@ from the hash so toggling `--suggest-patches` doesn't shift it. - [`checks.md`](checks.md) — full check catalog with rationale. - [`minimal-real-configs.md`](minimal-real-configs.md) — per-framework minimal manifests to build from. -- [`report-schema.v0.32.json`](report-schema.v0.32.json) — current JSON +- [`report-schema.v0.33.json`](report-schema.v0.33.json) — current JSON Schema for `report.json`. - [`AGENTS.md`](../AGENTS.md) — top-level agent instructions, install, trigger table. diff --git a/llms.txt b/llms.txt index 10fb377c..94006bad 100644 --- a/llms.txt +++ b/llms.txt @@ -13,7 +13,7 @@ - Publisher URL: https://threemoonslab.com/ - License: Apache-2.0 - Latest public release: v0.15.0 -- Current source-tree runtime: 0.16.0b4 (contract 15; pre-release) +- Current source-tree runtime: 0.16.0b5 (contract 16; pre-release) - Canonical repository: https://github.com/ThreeMoonsLab/agents-shipgate - Do not use: Agent Shipcheck, Agent Shipgate, agents shipgate, Agents-Shipgate @@ -61,23 +61,23 @@ - Markdown report: `agents-shipgate-reports/report.md`. - JSON report: `agents-shipgate-reports/report.json`. -- JSON report schema (current): https://raw.githubusercontent.com/ThreeMoonsLab/agents-shipgate/main/docs/report-schema.v0.32.json +- JSON report schema (current): https://raw.githubusercontent.com/ThreeMoonsLab/agents-shipgate/main/docs/report-schema.v0.33.json - Evidence-backed passed contract: https://raw.githubusercontent.com/ThreeMoonsLab/agents-shipgate/main/docs/passed-verdict-contract.md - Machine verdict boundary: `release_decision.static_analysis_only=true`, `runtime_behavior_verified=false`, plus `static_verdict_disclaimer`; packet §1 mirrors these fields. - Release Evidence Packet (Markdown / JSON / HTML, optional PDF): `agents-shipgate-reports/packet.{md,json,html}`. -- Packet schema (current): https://raw.githubusercontent.com/ThreeMoonsLab/agents-shipgate/main/docs/packet-schema.v0.10.json +- Packet schema (current): https://raw.githubusercontent.com/ThreeMoonsLab/agents-shipgate/main/docs/packet-schema.v0.11.json - SARIF report: `agents-shipgate-reports/report.sarif`. - Verifier orchestration record (ongoing-PR verify): `agents-shipgate-reports/verifier.json`. - Agent handoff artifact (preferred compact agent projection for verify): `agents-shipgate-reports/agent-handoff.json`. -- Agent handoff schema: https://raw.githubusercontent.com/ThreeMoonsLab/agents-shipgate/main/docs/agent-handoff-schema.v3.json +- Agent handoff schema: https://raw.githubusercontent.com/ThreeMoonsLab/agents-shipgate/main/docs/agent-handoff-schema.v4.json - PR comment (ongoing-PR verify): `agents-shipgate-reports/pr-comment.md`. - Proactive preflight routing JSON: `agents-shipgate preflight --workspace . --plan - --json` emits `preflight_schema_version: "0.3"`; switch on `control.state`. It routes protected-surface edits, host permission requests, and high-risk capability evidence gaps but is not a release verdict. - Preflight schema (current): https://raw.githubusercontent.com/ThreeMoonsLab/agents-shipgate/main/docs/preflight-schema.v0.3.json - Capability lock (stable static envelope): `.agents-shipgate/capabilities.lock.json`. - Verify head capability lock: `agents-shipgate-reports/capabilities.lock.json`. - Verify base capability lock and diff, when the base scan can be materialized: `agents-shipgate-reports/base.capabilities.lock.json`, `agents-shipgate-reports/capability-lock-diff.{json,md}`. -- Capability lock schema (current): https://raw.githubusercontent.com/ThreeMoonsLab/agents-shipgate/main/docs/capability-lock-schema.v0.5.json -- Capability lock diff schema (current): https://raw.githubusercontent.com/ThreeMoonsLab/agents-shipgate/main/docs/capability-lock-diff-schema.v0.6.json +- Capability lock schema (current): https://raw.githubusercontent.com/ThreeMoonsLab/agents-shipgate/main/docs/capability-lock-schema.v0.6.json +- Capability lock diff schema (current): https://raw.githubusercontent.com/ThreeMoonsLab/agents-shipgate/main/docs/capability-lock-diff-schema.v0.7.json - Attestation schema (current): https://raw.githubusercontent.com/ThreeMoonsLab/agents-shipgate/main/docs/attestation-schema.v0.4.json - Registry schema (current): https://raw.githubusercontent.com/ThreeMoonsLab/agents-shipgate/main/docs/registry-schema.v0.3.json - Org evidence bundle schema (current): https://raw.githubusercontent.com/ThreeMoonsLab/agents-shipgate/main/docs/org-evidence-bundle-schema.v1.json @@ -155,16 +155,16 @@ - Zero-install detector (stdlib-only Python; same structural verdict as `agents-shipgate detect --json` — emits the canonical `DetectResult` fields plus `script_version`, but NOT the CLI's `diagnostics` or `next_actions` arrays): https://raw.githubusercontent.com/ThreeMoonsLab/agents-shipgate/main/tools/shipgate-detect.py - Zero-install paths overview (single-file detector, uvx, GitHub Action): https://raw.githubusercontent.com/ThreeMoonsLab/agents-shipgate/main/docs/zero-install.md - Manifest schema: https://raw.githubusercontent.com/ThreeMoonsLab/agents-shipgate/main/docs/manifest-v0.1.json -- Report schema (current): https://raw.githubusercontent.com/ThreeMoonsLab/agents-shipgate/main/docs/report-schema.v0.32.json +- Report schema (current): https://raw.githubusercontent.com/ThreeMoonsLab/agents-shipgate/main/docs/report-schema.v0.33.json - Privacy/redaction docs: https://raw.githubusercontent.com/ThreeMoonsLab/agents-shipgate/main/docs/privacy.md -- Packet schema (current): https://raw.githubusercontent.com/ThreeMoonsLab/agents-shipgate/main/docs/packet-schema.v0.10.json +- Packet schema (current): https://raw.githubusercontent.com/ThreeMoonsLab/agents-shipgate/main/docs/packet-schema.v0.11.json - Preflight schema (current): https://raw.githubusercontent.com/ThreeMoonsLab/agents-shipgate/main/docs/preflight-schema.v0.3.json - Capability standard: https://raw.githubusercontent.com/ThreeMoonsLab/agents-shipgate/main/docs/capability-standard.md -- Capability lock schema (current): https://raw.githubusercontent.com/ThreeMoonsLab/agents-shipgate/main/docs/capability-lock-schema.v0.4.json +- Capability lock schema (current): https://raw.githubusercontent.com/ThreeMoonsLab/agents-shipgate/main/docs/capability-lock-schema.v0.6.json - Capability lock diff schema (current): https://raw.githubusercontent.com/ThreeMoonsLab/agents-shipgate/main/docs/capability-lock-diff-schema.v0.5.json - Governance benchmark: https://raw.githubusercontent.com/ThreeMoonsLab/agents-shipgate/main/docs/governance-benchmark.md - Current agent contract: https://raw.githubusercontent.com/ThreeMoonsLab/agents-shipgate/main/docs/agent-contract-current.md -- Agent handoff schema: https://raw.githubusercontent.com/ThreeMoonsLab/agents-shipgate/main/docs/agent-handoff-schema.v3.json +- Agent handoff schema: https://raw.githubusercontent.com/ThreeMoonsLab/agents-shipgate/main/docs/agent-handoff-schema.v4.json ## Category vocabulary diff --git a/plugins/agents-shipgate/.codex-plugin/plugin.json b/plugins/agents-shipgate/.codex-plugin/plugin.json index 68a21b60..7b1ee7d9 100644 --- a/plugins/agents-shipgate/.codex-plugin/plugin.json +++ b/plugins/agents-shipgate/.codex-plugin/plugin.json @@ -1,6 +1,6 @@ { "name": "agents-shipgate", - "version": "0.16.0b4", + "version": "0.16.0b5", "description": "Run Agents Shipgate Tool-Use Readiness workflows from Codex.", "author": { "name": "Three Moons Lab", diff --git a/plugins/claude-code/.claude-plugin/plugin.json b/plugins/claude-code/.claude-plugin/plugin.json index c8ffa5aa..ea734e6c 100644 --- a/plugins/claude-code/.claude-plugin/plugin.json +++ b/plugins/claude-code/.claude-plugin/plugin.json @@ -1,6 +1,6 @@ { "name": "agents-shipgate", - "version": "0.16.0b4", + "version": "0.16.0b5", "description": "Run Agents Shipgate Tool-Use Readiness workflows from Claude Code. Skill-only: the plugin supplies the agents-shipgate skill and the /agents-shipgate:shipgate command; the scanner runs through the agents-shipgate CLI installed in the local environment (pipx install agents-shipgate, runtime contract 15). For the deterministic hooks (trigger check after edits, full verify at Stop), run: agents-shipgate install-hooks --target claude-code --write.", "author": { "name": "Three Moons Lab", diff --git a/plugins/claude-code/skills/agents-shipgate/SKILL.md b/plugins/claude-code/skills/agents-shipgate/SKILL.md index 2b8e5670..12caf111 100644 --- a/plugins/claude-code/skills/agents-shipgate/SKILL.md +++ b/plugins/claude-code/skills/agents-shipgate/SKILL.md @@ -76,9 +76,9 @@ For non-GitHub CI (GitLab, CircleCI, Jenkins, Azure Pipelines, Buildkite, Bitbuc - **Installed CLI contract**: when available, run `agents-shipgate contract --json` to verify local schema versions, capability/research surfaces, `release_decision.decision`, and manual-review signal fields. Older installs should use [`docs/agent-contract-current.md`](https://github.com/ThreeMoonsLab/agents-shipgate/blob/main/docs/agent-contract-current.md) or upgrade before automating against the local contract command. - **Verifier JSON**: `verifier_schema_version: "0.3"`. Switch on `control.state`, then read `merge_verdict`, `can_merge_without_human`, `control.next_action`, `fix_task`, `capability_review.top_changes`, `trust_root_touched`, and `policy_weakened` before summarizing an AI-generated PR. `merge_verdict` is a deterministic projection; the gate remains `report.json.release_decision.decision`. - **Verify run JSON**: `verify-run.json` uses `schema_version: "shipgate.verify_run/v2"` and records stable run identity, subject refs, input hashes, execution, applicability, release outcome, and artifact hashes. It is the reproducibility artifact for `verify`; do not treat it as a second gate. -- **Report JSON**: `report_schema_version: "0.32"`. Read `release_decision.decision` first. A `passed` decision requires a complete root-reachable static binding graph plus complete, conflict-free identity, effect, and authority evidence for every reachable action; it does not prove runtime behavior. Preserve `release_decision.static_analysis_only=true`, `runtime_behavior_verified=false`, and `static_verdict_disclaimer` in summaries. Read `release_decision.evidence_coverage.binding_coverage`, `semantic_coverage`, and `identity_coverage`, then work every `evidence_gaps[].next_action` in order. Binding and semantic gaps are not Findings and cannot be suppressed, baselined, severity-overridden, cleared by `--no-heuristics`, or satisfied by `human_ack`; binding, effect, and authority declarations are human assertions and must never be auto-written. Use `tool_catalog[]` for diagnostics and `tool_inventory[]` for the proven reachable surface. The current schema is [`docs/report-schema.v0.32.json`](https://github.com/ThreeMoonsLab/agents-shipgate/blob/main/docs/report-schema.v0.32.json); v0.31 is a frozen compatibility reference. See the [current agent contract](https://github.com/ThreeMoonsLab/agents-shipgate/blob/main/docs/agent-contract-current.md#read-these-first-for-release-gating) and [evidence-backed passed contract](https://github.com/ThreeMoonsLab/agents-shipgate/blob/main/docs/passed-verdict-contract.md). +- **Report JSON**: `report_schema_version: "0.33"`. Read `release_decision.decision` first. A `passed` decision requires a complete root-reachable static binding graph plus complete, conflict-free identity, effect, and authority evidence for every reachable action; it does not prove runtime behavior. Preserve `release_decision.static_analysis_only=true`, `runtime_behavior_verified=false`, and `static_verdict_disclaimer` in summaries. Read `release_decision.evidence_coverage.binding_coverage`, `semantic_coverage`, `identity_coverage`, and `policy_gap_count`, then work every `evidence_gaps[].next_action` in order. Binding, semantic, and policy-applicability gaps are not Findings and cannot be suppressed, baselined, severity-overridden, cleared by `--no-heuristics`, or satisfied by `human_ack`; binding, effect, and authority declarations are human assertions and must never be auto-written. Use `tool_catalog[]` for diagnostics and `tool_inventory[]` for the proven reachable surface. The current schema is [`docs/report-schema.v0.33.json`](https://github.com/ThreeMoonsLab/agents-shipgate/blob/main/docs/report-schema.v0.33.json); v0.32 is a frozen compatibility reference. See the [current agent contract](https://github.com/ThreeMoonsLab/agents-shipgate/blob/main/docs/agent-contract-current.md#read-these-first-for-release-gating) and [evidence-backed passed contract](https://github.com/ThreeMoonsLab/agents-shipgate/blob/main/docs/passed-verdict-contract.md). These binding-backed fields require runtime contract v13; the unambiguous `AgentControl` projection requires runtime contract v14. A contract-v12 CLI emits the frozen v0.30 report and must not be described using the v0.31 binding claim. -- **Release Evidence Packet**: `agents-shipgate-reports/packet.{md,json,html}` (and `packet.pdf` with the `[pdf]` extras) is a supporting/provisional reviewer artifact. Packet v0.10 projects binding and semantic coverage under the release decision; it never creates a second gate. See [`docs/packet-schema.v0.10.json`](https://github.com/ThreeMoonsLab/agents-shipgate/blob/main/docs/packet-schema.v0.10.json) and [STABILITY.md §Release Evidence Packet](https://github.com/ThreeMoonsLab/agents-shipgate/blob/main/STABILITY.md#release-evidence-packet-v010). +- **Release Evidence Packet**: `agents-shipgate-reports/packet.{md,json,html}` (and `packet.pdf` with the `[pdf]` extras) is a supporting/provisional reviewer artifact. Packet v0.11 projects binding and semantic coverage under the release decision; it never creates a second gate. See [`docs/packet-schema.v0.11.json`](https://github.com/ThreeMoonsLab/agents-shipgate/blob/main/docs/packet-schema.v0.11.json) and [STABILITY.md §Release Evidence Packet](https://github.com/ThreeMoonsLab/agents-shipgate/blob/main/STABILITY.md#release-evidence-packet-v011). - **Capability standard**: `agents-shipgate capability export` emits a stable static capability lock (`capability_lock_schema_version: "0.5"`) and `agents-shipgate capability diff` emits a stable semantic diff (`capability_lock_diff_schema_version: "0.6"`). These artifacts implement capability standard v0.4, are supporting/provisional and non-gating, exclude runtime trace evidence, and are documented in [`docs/capability-standard.md`](https://github.com/ThreeMoonsLab/agents-shipgate/blob/main/docs/capability-standard.md). - **Governance benchmark**: `benchmark/agent-pr-governance/cases.yaml` and `scripts/run_governance_benchmark.py` are the stable research benchmark substrate (`governance_benchmark_result_schema_version: "0.2"`), not a release gate. See [`docs/governance-benchmark.md`](https://github.com/ThreeMoonsLab/agents-shipgate/blob/main/docs/governance-benchmark.md). - **Single source of truth for the contract**: [`docs/agent-contract-current.md`](https://github.com/ThreeMoonsLab/agents-shipgate/blob/main/docs/agent-contract-current.md). When the schema bumps, that file updates first. diff --git a/pyproject.toml b/pyproject.toml index ca63cb7c..86f6ffea 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "hatchling.build" [project] name = "agents-shipgate" -version = "0.16.0b4" +version = "0.16.0b5" description = "The deterministic merge gate for AI-generated agent capability changes. Agent release readiness for tool-using AI agents. CLI + GitHub Action. Scans MCP, OpenAPI, OpenAI Agents SDK, Anthropic, Google ADK, LangChain, CrewAI, OpenAI API, Codex config, Codex plugin, n8n, Conductor OSS workflow JSON." readme = "README.md" requires-python = ">=3.12" diff --git a/samples/conductor_agent/expected/report.json b/samples/conductor_agent/expected/report.json index b47aab22..def63ab6 100644 --- a/samples/conductor_agent/expected/report.json +++ b/samples/conductor_agent/expected/report.json @@ -1,7 +1,7 @@ { "schema_version": "0.1", - "report_schema_version": "0.32", - "run_id": "agents_shipgate_e38d61d6846ad002", + "report_schema_version": "0.33", + "run_id": "agents_shipgate_68a230995652f841", "manifest_dir": "/samples/conductor_agent", "project": { "name": "conductor-agent" @@ -82,7 +82,8 @@ }, "policy_evidence_source": null, "capability_refs": [], - "capability_trace_refs": [] + "capability_trace_refs": [], + "support": null }, { "id": "fp_a5e7b93822939963", @@ -104,7 +105,8 @@ }, "policy_evidence_source": null, "capability_refs": [], - "capability_trace_refs": [] + "capability_trace_refs": [], + "support": null } ], "evidence_coverage": { @@ -299,7 +301,8 @@ "reason_counts": { "partial_binding_evidence": 1 } - } + }, + "policy_gap_count": 0 }, "baseline_delta": { "enabled": false, @@ -472,7 +475,7 @@ ] }, "action_surface_facts": { - "snapshot_version": "0.3", + "snapshot_version": "0.4", "actions": [ { "action_id": "agent:conductor-agent/durable-order-agent:action_v2_e44ea9ad0aa3c7deea46a01e5cbe1f3cf399916633fb94491e14fc6dd523eddd", @@ -504,10 +507,13 @@ ], "claims": [ { + "claim_id": "clm_89427d1ea28437fdb8e5", "dimension": "identity", "value": "obs_v1_347654afa59dfbc52ecf63e6e05663a281baac9d357e6cbbcb904fe34add06db", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "protocol_structure", + "policy_eligible": true, "source": "source_observation", "source_pointer": "/tasks/3", "evidence": { @@ -530,10 +536,13 @@ ], "claims": [ { + "claim_id": "clm_863b47d574d9a43e936d", "dimension": "binding", "value": "agent_v1:7205d836e4b3fee257d90695->tool_v2_d3382b0bdebb027000b5bbda4209994458144a0542a64bcec07f6eeb5a30322e", "confidence": "high", "provenance_kind": "ast_extraction", + "basis": "typed_provider_fact", + "policy_eligible": true, "source": "workflows/order-agent.json", "source_pointer": "/tasks/3", "evidence": { @@ -550,19 +559,25 @@ "confidence": "high", "claims": [ { + "claim_id": "clm_1773da882d243c27d186", "dimension": "effect", "value": "read", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "reviewed_declaration", + "policy_eligible": true, "source": "action_surface_declaration", "source_pointer": "action_surface.actions[tool='lookup_order'].effect", "evidence": {} }, { + "claim_id": "clm_37844699a22bb6fde3e6", "dimension": "effect", "value": "read", "confidence": "medium", "provenance_kind": "keyword_heuristic", + "basis": "inferred_keyword", + "policy_eligible": false, "source": "risk_hint:keyword", "source_pointer": "/tasks/3", "evidence": { @@ -589,10 +604,13 @@ "scopes": [], "claims": [ { + "claim_id": "clm_3bceb57bdfa16b5c7be7", "dimension": "authority", "value": "none", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "reviewed_declaration", + "policy_eligible": true, "source": "action_surface_declaration", "source_pointer": "action_surface.actions[tool='lookup_order'].authority", "evidence": { @@ -797,6 +815,7 @@ "capability_refs": [], "capability_policy_evidence": null, "policy_routing": null, + "support": null, "capability_trace_refs": [], "recommendation": "Use literal MCP server and method bindings, a static LLM tool advertisement, or an exact local sub-workflow target before release review.", "blocks_release": false, @@ -848,6 +867,7 @@ "capability_refs": [], "capability_policy_evidence": null, "policy_routing": null, + "support": null, "capability_trace_refs": [], "recommendation": "Use literal MCP server and method bindings, a static LLM tool advertisement, or an exact local sub-workflow target before release review.", "blocks_release": false, @@ -865,8 +885,8 @@ "Use literal MCP server and method bindings, a static LLM tool advertisement, or an exact local sub-workflow target before release review." ], "generated_reports": { - "markdown": "expected/report.md", - "json": "expected/report.json" + "markdown": "/private/tmp/shipgate-goldens/conductor/report.md", + "json": "/private/tmp/shipgate-goldens/conductor/report.json" }, "loaded_policy_packs": [], "loaded_plugins": [], @@ -906,10 +926,13 @@ ], "claims": [ { + "claim_id": "clm_89427d1ea28437fdb8e5", "dimension": "identity", "value": "obs_v1_347654afa59dfbc52ecf63e6e05663a281baac9d357e6cbbcb904fe34add06db", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "protocol_structure", + "policy_eligible": true, "source": "source_observation", "source_pointer": "/tasks/3", "evidence": { @@ -932,10 +955,13 @@ ], "claims": [ { + "claim_id": "clm_863b47d574d9a43e936d", "dimension": "binding", "value": "agent_v1:7205d836e4b3fee257d90695->tool_v2_d3382b0bdebb027000b5bbda4209994458144a0542a64bcec07f6eeb5a30322e", "confidence": "high", "provenance_kind": "ast_extraction", + "basis": "typed_provider_fact", + "policy_eligible": true, "source": "workflows/order-agent.json", "source_pointer": "/tasks/3", "evidence": { @@ -952,19 +978,25 @@ "confidence": "high", "claims": [ { + "claim_id": "clm_1773da882d243c27d186", "dimension": "effect", "value": "read", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "reviewed_declaration", + "policy_eligible": true, "source": "action_surface_declaration", "source_pointer": "action_surface.actions[tool='lookup_order'].effect", "evidence": {} }, { + "claim_id": "clm_37844699a22bb6fde3e6", "dimension": "effect", "value": "read", "confidence": "medium", "provenance_kind": "keyword_heuristic", + "basis": "inferred_keyword", + "policy_eligible": false, "source": "risk_hint:keyword", "source_pointer": "/tasks/3", "evidence": { @@ -991,10 +1023,13 @@ "scopes": [], "claims": [ { + "claim_id": "clm_3bceb57bdfa16b5c7be7", "dimension": "authority", "value": "none", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "reviewed_declaration", + "policy_eligible": true, "source": "action_surface_declaration", "source_pointer": "action_surface.actions[tool='lookup_order'].authority", "evidence": { @@ -1018,10 +1053,13 @@ ], "claims": [ { + "claim_id": "clm_863b47d574d9a43e936d", "dimension": "binding", "value": "agent_v1:7205d836e4b3fee257d90695->tool_v2_d3382b0bdebb027000b5bbda4209994458144a0542a64bcec07f6eeb5a30322e", "confidence": "high", "provenance_kind": "ast_extraction", + "basis": "typed_provider_fact", + "policy_eligible": true, "source": "workflows/order-agent.json", "source_pointer": "/tasks/3", "evidence": { @@ -1070,10 +1108,13 @@ ], "claims": [ { + "claim_id": "clm_89427d1ea28437fdb8e5", "dimension": "identity", "value": "obs_v1_347654afa59dfbc52ecf63e6e05663a281baac9d357e6cbbcb904fe34add06db", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "protocol_structure", + "policy_eligible": true, "source": "source_observation", "source_pointer": "/tasks/3", "evidence": { @@ -1096,10 +1137,13 @@ ], "claims": [ { + "claim_id": "clm_863b47d574d9a43e936d", "dimension": "binding", "value": "agent_v1:7205d836e4b3fee257d90695->tool_v2_d3382b0bdebb027000b5bbda4209994458144a0542a64bcec07f6eeb5a30322e", "confidence": "high", "provenance_kind": "ast_extraction", + "basis": "typed_provider_fact", + "policy_eligible": true, "source": "workflows/order-agent.json", "source_pointer": "/tasks/3", "evidence": { @@ -1116,19 +1160,25 @@ "confidence": "high", "claims": [ { + "claim_id": "clm_1773da882d243c27d186", "dimension": "effect", "value": "read", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "reviewed_declaration", + "policy_eligible": true, "source": "action_surface_declaration", "source_pointer": "action_surface.actions[tool='lookup_order'].effect", "evidence": {} }, { + "claim_id": "clm_37844699a22bb6fde3e6", "dimension": "effect", "value": "read", "confidence": "medium", "provenance_kind": "keyword_heuristic", + "basis": "inferred_keyword", + "policy_eligible": false, "source": "risk_hint:keyword", "source_pointer": "/tasks/3", "evidence": { @@ -1155,10 +1205,13 @@ "scopes": [], "claims": [ { + "claim_id": "clm_3bceb57bdfa16b5c7be7", "dimension": "authority", "value": "none", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "reviewed_declaration", + "policy_eligible": true, "source": "action_surface_declaration", "source_pointer": "action_surface.actions[tool='lookup_order'].authority", "evidence": { @@ -1182,10 +1235,13 @@ ], "claims": [ { + "claim_id": "clm_863b47d574d9a43e936d", "dimension": "binding", "value": "agent_v1:7205d836e4b3fee257d90695->tool_v2_d3382b0bdebb027000b5bbda4209994458144a0542a64bcec07f6eeb5a30322e", "confidence": "high", "provenance_kind": "ast_extraction", + "basis": "typed_provider_fact", + "policy_eligible": true, "source": "workflows/order-agent.json", "source_pointer": "/tasks/3", "evidence": { @@ -1205,6 +1261,7 @@ "Conductor capability 'HTTP' at workflows/order-agent.json#/tasks/5 is recognized but not enumerated by the MCP-core v1 adapter.", "Conductor capability 'webSearch' at workflows/order-agent.json#/tasks/1 is recognized but not enumerated by the MCP-core v1 adapter." ], + "policy_evidence_gaps": [], "agent_summary": { "verdict": "insufficient_evidence", "headline": "Evidence coverage below threshold (1 semantic evidence gap(s) and 1 low-confidence tool(s) and 4 source warning(s)); scan results are not trustworthy enough to gate release.", diff --git a/samples/simple_crewai_agent/expected/report.json b/samples/simple_crewai_agent/expected/report.json index 6df93106..ca9df653 100644 --- a/samples/simple_crewai_agent/expected/report.json +++ b/samples/simple_crewai_agent/expected/report.json @@ -1,7 +1,7 @@ { "schema_version": "0.1", - "report_schema_version": "0.32", - "run_id": "agents_shipgate_a62c844f928e0aba", + "report_schema_version": "0.33", + "run_id": "agents_shipgate_9702bae6947b59b3", "manifest_dir": "/samples/simple_crewai_agent", "project": { "name": "simple-crewai-agent" @@ -86,7 +86,8 @@ }, "policy_evidence_source": null, "capability_refs": [], - "capability_trace_refs": [] + "capability_trace_refs": [], + "support": null } ], "evidence_coverage": { @@ -141,7 +142,8 @@ "pass_eligible": true, "gap_count": 0, "reason_counts": {} - } + }, + "policy_gap_count": 0 }, "baseline_delta": { "enabled": false, @@ -195,10 +197,13 @@ ], "claims": [ { + "claim_id": "clm_152bd608505071fa6c11", "dimension": "identity", "value": "obs_v1_91dc9ef11aaa2cd63b91fb9e90b6607ecaf5e678f470c54d3520feb74c93aea8", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "reviewed_declaration", + "policy_eligible": true, "source": "tool_identity_binding", "source_pointer": "/tools/2", "evidence": { @@ -208,10 +213,13 @@ } }, { + "claim_id": "clm_cf26c71fba22caf0ba3b", "dimension": "identity", "value": "obs_v1_d47ab93f36b5308c7f1fd05073fdca72147fe034a6ec7da6470753270c29bd32", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "reviewed_declaration", + "policy_eligible": true, "source": "tool_identity_binding", "source_pointer": "crew.py", "evidence": { @@ -235,10 +243,13 @@ ], "claims": [ { + "claim_id": "clm_690d38df3006fc448a65", "dimension": "binding", "value": "agent_v1:71a7997718e73610b0704464->tool_v2_97cdb0ffc03b3a364a1697b362308564d52d44b3dab46f799f1b7b5276adb212", "confidence": "high", "provenance_kind": "ast_extraction", + "basis": "typed_provider_fact", + "policy_eligible": true, "source": "crew.py", "source_pointer": "crew.py:31", "evidence": { @@ -255,19 +266,25 @@ "confidence": "high", "claims": [ { + "claim_id": "clm_5bc827466b89cc7ad878", "dimension": "effect", "value": "read", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "reviewed_declaration", + "policy_eligible": true, "source": "action_surface_declaration", "source_pointer": "action_surface.actions[tool='FileReadTool'].effect", "evidence": {} }, { + "claim_id": "clm_7b810e50aa2ed073a420", "dimension": "effect", "value": "read", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "protocol_structure", + "policy_eligible": true, "source": "mcp_annotation", "source_pointer": "/tools/2", "evidence": { @@ -285,10 +302,13 @@ "scopes": [], "claims": [ { + "claim_id": "clm_843f3f7ad828d36e917f", "dimension": "authority", "value": "ambient", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "reviewed_declaration", + "policy_eligible": true, "source": "action_surface_declaration", "source_pointer": "action_surface.actions[tool='FileReadTool'].authority", "evidence": { @@ -298,10 +318,13 @@ } }, { + "claim_id": "clm_d734d699ce978298e895", "dimension": "authority", "value": "ambient", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "protocol_structure", + "policy_eligible": true, "source": "mcp_authority", "source_pointer": "/tools/2", "evidence": { @@ -509,7 +532,7 @@ ] }, "action_surface_facts": { - "snapshot_version": "0.3", + "snapshot_version": "0.4", "actions": [ { "action_id": "agent:simple-crewai-agent/support-case-crew:action_v2_56f5d7d377130876f26986bdadd25cd1ad47dd0138cba6da8dc11e7d06fe30e6", @@ -542,10 +565,13 @@ ], "claims": [ { + "claim_id": "clm_617a4c03070c7e111158", "dimension": "identity", "value": "obs_v1_4da41a223c5b9819f534ad9f701d469aa605b258c479ae1768a5de3882e45d13", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "reviewed_declaration", + "policy_eligible": true, "source": "tool_identity_binding", "source_pointer": "/tools/1", "evidence": { @@ -555,10 +581,13 @@ } }, { + "claim_id": "clm_190c664ed7b33cd14479", "dimension": "identity", "value": "obs_v1_8107b85e09d544636b259f216489e077ba0c6915829b5e4a0c708d960d9b00df", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "reviewed_declaration", + "policy_eligible": true, "source": "tool_identity_binding", "source_pointer": "crew.py", "evidence": { @@ -582,10 +611,13 @@ ], "claims": [ { + "claim_id": "clm_74e61303c65c843b3114", "dimension": "binding", "value": "agent_v1:71a7997718e73610b0704464->tool_v2_d38c0379eb241f5063861aecb39b242b58e030f320ede64d7c862bfd6f68d638", "confidence": "high", "provenance_kind": "ast_extraction", + "basis": "typed_provider_fact", + "policy_eligible": true, "source": "crew.py", "source_pointer": "crew.py:31", "evidence": { @@ -602,19 +634,25 @@ "confidence": "high", "claims": [ { + "claim_id": "clm_13f60eeda1090905abfa", "dimension": "effect", "value": "read", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "reviewed_declaration", + "policy_eligible": true, "source": "action_surface_declaration", "source_pointer": "action_surface.actions[tool='summarize_case'].effect", "evidence": {} }, { + "claim_id": "clm_84b18c77354a223b68f9", "dimension": "effect", "value": "read", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "protocol_structure", + "policy_eligible": true, "source": "mcp_annotation", "source_pointer": "/tools/1", "evidence": { @@ -632,10 +670,13 @@ "scopes": [], "claims": [ { + "claim_id": "clm_086cd06d298307836911", "dimension": "authority", "value": "none", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "reviewed_declaration", + "policy_eligible": true, "source": "action_surface_declaration", "source_pointer": "action_surface.actions[tool='summarize_case'].authority", "evidence": { @@ -645,10 +686,13 @@ } }, { + "claim_id": "clm_31b93c95a5f16d36e1fc", "dimension": "authority", "value": "none", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "protocol_structure", + "policy_eligible": true, "source": "mcp_authority", "source_pointer": "/tools/1", "evidence": { @@ -726,10 +770,13 @@ ], "claims": [ { + "claim_id": "clm_1af43a9e60aefd8a4694", "dimension": "identity", "value": "obs_v1_75cdf97835473a87a94cced0791d82e93653eb17faa022f9fe39a5156b4de1be", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "reviewed_declaration", + "policy_eligible": true, "source": "tool_identity_binding", "source_pointer": "crew.py", "evidence": { @@ -739,10 +786,13 @@ } }, { + "claim_id": "clm_fdcd52b2ba2493df6c6c", "dimension": "identity", "value": "obs_v1_d80a6855053ad21b53e7781eb52851609ab5e62b9cdde3f3e17bb9bfd16d260c", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "reviewed_declaration", + "policy_eligible": true, "source": "tool_identity_binding", "source_pointer": "/tools/0", "evidence": { @@ -766,10 +816,13 @@ ], "claims": [ { + "claim_id": "clm_7a35a4ab0117daf26f6b", "dimension": "binding", "value": "agent_v1:71a7997718e73610b0704464->tool_v2_8bb9341b31f3731d4ef2337cde788263775c2f388329654c2bc8a8b521170ed1", "confidence": "high", "provenance_kind": "ast_extraction", + "basis": "typed_provider_fact", + "policy_eligible": true, "source": "crew.py", "source_pointer": "crew.py:31", "evidence": { @@ -786,19 +839,25 @@ "confidence": "high", "claims": [ { + "claim_id": "clm_d06c32d64f8f0493a478", "dimension": "effect", "value": "read", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "reviewed_declaration", + "policy_eligible": true, "source": "action_surface_declaration", "source_pointer": "action_surface.actions[tool='lookup_case'].effect", "evidence": {} }, { + "claim_id": "clm_50c4d6abc43dbc7853c5", "dimension": "effect", "value": "read", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "protocol_structure", + "policy_eligible": true, "source": "mcp_annotation", "source_pointer": "/tools/0", "evidence": { @@ -816,10 +875,13 @@ "scopes": [], "claims": [ { + "claim_id": "clm_74d88f35831b62f08e96", "dimension": "authority", "value": "none", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "reviewed_declaration", + "policy_eligible": true, "source": "action_surface_declaration", "source_pointer": "action_surface.actions[tool='lookup_case'].authority", "evidence": { @@ -829,10 +891,13 @@ } }, { + "claim_id": "clm_b1bf872bb994c8ec4e1c", "dimension": "authority", "value": "none", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "protocol_structure", + "policy_eligible": true, "source": "mcp_authority", "source_pointer": "/tools/0", "evidence": { @@ -910,10 +975,13 @@ ], "claims": [ { + "claim_id": "clm_152bd608505071fa6c11", "dimension": "identity", "value": "obs_v1_91dc9ef11aaa2cd63b91fb9e90b6607ecaf5e678f470c54d3520feb74c93aea8", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "reviewed_declaration", + "policy_eligible": true, "source": "tool_identity_binding", "source_pointer": "/tools/2", "evidence": { @@ -923,10 +991,13 @@ } }, { + "claim_id": "clm_cf26c71fba22caf0ba3b", "dimension": "identity", "value": "obs_v1_d47ab93f36b5308c7f1fd05073fdca72147fe034a6ec7da6470753270c29bd32", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "reviewed_declaration", + "policy_eligible": true, "source": "tool_identity_binding", "source_pointer": "crew.py", "evidence": { @@ -950,10 +1021,13 @@ ], "claims": [ { + "claim_id": "clm_690d38df3006fc448a65", "dimension": "binding", "value": "agent_v1:71a7997718e73610b0704464->tool_v2_97cdb0ffc03b3a364a1697b362308564d52d44b3dab46f799f1b7b5276adb212", "confidence": "high", "provenance_kind": "ast_extraction", + "basis": "typed_provider_fact", + "policy_eligible": true, "source": "crew.py", "source_pointer": "crew.py:31", "evidence": { @@ -970,19 +1044,25 @@ "confidence": "high", "claims": [ { + "claim_id": "clm_5bc827466b89cc7ad878", "dimension": "effect", "value": "read", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "reviewed_declaration", + "policy_eligible": true, "source": "action_surface_declaration", "source_pointer": "action_surface.actions[tool='FileReadTool'].effect", "evidence": {} }, { + "claim_id": "clm_7b810e50aa2ed073a420", "dimension": "effect", "value": "read", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "protocol_structure", + "policy_eligible": true, "source": "mcp_annotation", "source_pointer": "/tools/2", "evidence": { @@ -1000,10 +1080,13 @@ "scopes": [], "claims": [ { + "claim_id": "clm_843f3f7ad828d36e917f", "dimension": "authority", "value": "ambient", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "reviewed_declaration", + "policy_eligible": true, "source": "action_surface_declaration", "source_pointer": "action_surface.actions[tool='FileReadTool'].authority", "evidence": { @@ -1013,10 +1096,13 @@ } }, { + "claim_id": "clm_d734d699ce978298e895", "dimension": "authority", "value": "ambient", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "protocol_structure", + "policy_eligible": true, "source": "mcp_authority", "source_pointer": "/tools/2", "evidence": { @@ -1239,6 +1325,7 @@ "capability_refs": [], "capability_policy_evidence": null, "policy_routing": null, + "support": null, "capability_trace_refs": [], "recommendation": "Declare operation-specific auth scopes for FileReadTool, or explicitly declare anonymous authority when the operation requires no credentials.", "blocks_release": false, @@ -1256,8 +1343,8 @@ "Declare operation-specific auth scopes for FileReadTool, or explicitly declare anonymous authority when the operation requires no credentials." ], "generated_reports": { - "markdown": "/private/tmp/shipgate-golden-simple_crewai_agent/report.md", - "json": "/private/tmp/shipgate-golden-simple_crewai_agent/report.json" + "markdown": "/private/tmp/shipgate-goldens/crewai/report.md", + "json": "/private/tmp/shipgate-goldens/crewai/report.json" }, "loaded_policy_packs": [], "loaded_plugins": [], @@ -1299,10 +1386,13 @@ ], "claims": [ { + "claim_id": "clm_1af43a9e60aefd8a4694", "dimension": "identity", "value": "obs_v1_75cdf97835473a87a94cced0791d82e93653eb17faa022f9fe39a5156b4de1be", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "reviewed_declaration", + "policy_eligible": true, "source": "tool_identity_binding", "source_pointer": "crew.py", "evidence": { @@ -1312,10 +1402,13 @@ } }, { + "claim_id": "clm_fdcd52b2ba2493df6c6c", "dimension": "identity", "value": "obs_v1_d80a6855053ad21b53e7781eb52851609ab5e62b9cdde3f3e17bb9bfd16d260c", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "reviewed_declaration", + "policy_eligible": true, "source": "tool_identity_binding", "source_pointer": "/tools/0", "evidence": { @@ -1339,10 +1432,13 @@ ], "claims": [ { + "claim_id": "clm_7a35a4ab0117daf26f6b", "dimension": "binding", "value": "agent_v1:71a7997718e73610b0704464->tool_v2_8bb9341b31f3731d4ef2337cde788263775c2f388329654c2bc8a8b521170ed1", "confidence": "high", "provenance_kind": "ast_extraction", + "basis": "typed_provider_fact", + "policy_eligible": true, "source": "crew.py", "source_pointer": "crew.py:31", "evidence": { @@ -1359,19 +1455,25 @@ "confidence": "high", "claims": [ { + "claim_id": "clm_d06c32d64f8f0493a478", "dimension": "effect", "value": "read", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "reviewed_declaration", + "policy_eligible": true, "source": "action_surface_declaration", "source_pointer": "action_surface.actions[tool='lookup_case'].effect", "evidence": {} }, { + "claim_id": "clm_50c4d6abc43dbc7853c5", "dimension": "effect", "value": "read", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "protocol_structure", + "policy_eligible": true, "source": "mcp_annotation", "source_pointer": "/tools/0", "evidence": { @@ -1389,10 +1491,13 @@ "scopes": [], "claims": [ { + "claim_id": "clm_74d88f35831b62f08e96", "dimension": "authority", "value": "none", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "reviewed_declaration", + "policy_eligible": true, "source": "action_surface_declaration", "source_pointer": "action_surface.actions[tool='lookup_case'].authority", "evidence": { @@ -1402,10 +1507,13 @@ } }, { + "claim_id": "clm_b1bf872bb994c8ec4e1c", "dimension": "authority", "value": "none", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "protocol_structure", + "policy_eligible": true, "source": "mcp_authority", "source_pointer": "/tools/0", "evidence": { @@ -1430,10 +1538,13 @@ ], "claims": [ { + "claim_id": "clm_7a35a4ab0117daf26f6b", "dimension": "binding", "value": "agent_v1:71a7997718e73610b0704464->tool_v2_8bb9341b31f3731d4ef2337cde788263775c2f388329654c2bc8a8b521170ed1", "confidence": "high", "provenance_kind": "ast_extraction", + "basis": "typed_provider_fact", + "policy_eligible": true, "source": "crew.py", "source_pointer": "crew.py:31", "evidence": { @@ -1482,10 +1593,13 @@ ], "claims": [ { + "claim_id": "clm_152bd608505071fa6c11", "dimension": "identity", "value": "obs_v1_91dc9ef11aaa2cd63b91fb9e90b6607ecaf5e678f470c54d3520feb74c93aea8", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "reviewed_declaration", + "policy_eligible": true, "source": "tool_identity_binding", "source_pointer": "/tools/2", "evidence": { @@ -1495,10 +1609,13 @@ } }, { + "claim_id": "clm_cf26c71fba22caf0ba3b", "dimension": "identity", "value": "obs_v1_d47ab93f36b5308c7f1fd05073fdca72147fe034a6ec7da6470753270c29bd32", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "reviewed_declaration", + "policy_eligible": true, "source": "tool_identity_binding", "source_pointer": "crew.py", "evidence": { @@ -1522,10 +1639,13 @@ ], "claims": [ { + "claim_id": "clm_690d38df3006fc448a65", "dimension": "binding", "value": "agent_v1:71a7997718e73610b0704464->tool_v2_97cdb0ffc03b3a364a1697b362308564d52d44b3dab46f799f1b7b5276adb212", "confidence": "high", "provenance_kind": "ast_extraction", + "basis": "typed_provider_fact", + "policy_eligible": true, "source": "crew.py", "source_pointer": "crew.py:31", "evidence": { @@ -1542,19 +1662,25 @@ "confidence": "high", "claims": [ { + "claim_id": "clm_5bc827466b89cc7ad878", "dimension": "effect", "value": "read", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "reviewed_declaration", + "policy_eligible": true, "source": "action_surface_declaration", "source_pointer": "action_surface.actions[tool='FileReadTool'].effect", "evidence": {} }, { + "claim_id": "clm_7b810e50aa2ed073a420", "dimension": "effect", "value": "read", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "protocol_structure", + "policy_eligible": true, "source": "mcp_annotation", "source_pointer": "/tools/2", "evidence": { @@ -1572,10 +1698,13 @@ "scopes": [], "claims": [ { + "claim_id": "clm_843f3f7ad828d36e917f", "dimension": "authority", "value": "ambient", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "reviewed_declaration", + "policy_eligible": true, "source": "action_surface_declaration", "source_pointer": "action_surface.actions[tool='FileReadTool'].authority", "evidence": { @@ -1585,10 +1714,13 @@ } }, { + "claim_id": "clm_d734d699ce978298e895", "dimension": "authority", "value": "ambient", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "protocol_structure", + "policy_eligible": true, "source": "mcp_authority", "source_pointer": "/tools/2", "evidence": { @@ -1613,10 +1745,13 @@ ], "claims": [ { + "claim_id": "clm_690d38df3006fc448a65", "dimension": "binding", "value": "agent_v1:71a7997718e73610b0704464->tool_v2_97cdb0ffc03b3a364a1697b362308564d52d44b3dab46f799f1b7b5276adb212", "confidence": "high", "provenance_kind": "ast_extraction", + "basis": "typed_provider_fact", + "policy_eligible": true, "source": "crew.py", "source_pointer": "crew.py:31", "evidence": { @@ -1665,10 +1800,13 @@ ], "claims": [ { + "claim_id": "clm_617a4c03070c7e111158", "dimension": "identity", "value": "obs_v1_4da41a223c5b9819f534ad9f701d469aa605b258c479ae1768a5de3882e45d13", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "reviewed_declaration", + "policy_eligible": true, "source": "tool_identity_binding", "source_pointer": "/tools/1", "evidence": { @@ -1678,10 +1816,13 @@ } }, { + "claim_id": "clm_190c664ed7b33cd14479", "dimension": "identity", "value": "obs_v1_8107b85e09d544636b259f216489e077ba0c6915829b5e4a0c708d960d9b00df", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "reviewed_declaration", + "policy_eligible": true, "source": "tool_identity_binding", "source_pointer": "crew.py", "evidence": { @@ -1705,10 +1846,13 @@ ], "claims": [ { + "claim_id": "clm_74e61303c65c843b3114", "dimension": "binding", "value": "agent_v1:71a7997718e73610b0704464->tool_v2_d38c0379eb241f5063861aecb39b242b58e030f320ede64d7c862bfd6f68d638", "confidence": "high", "provenance_kind": "ast_extraction", + "basis": "typed_provider_fact", + "policy_eligible": true, "source": "crew.py", "source_pointer": "crew.py:31", "evidence": { @@ -1725,19 +1869,25 @@ "confidence": "high", "claims": [ { + "claim_id": "clm_13f60eeda1090905abfa", "dimension": "effect", "value": "read", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "reviewed_declaration", + "policy_eligible": true, "source": "action_surface_declaration", "source_pointer": "action_surface.actions[tool='summarize_case'].effect", "evidence": {} }, { + "claim_id": "clm_84b18c77354a223b68f9", "dimension": "effect", "value": "read", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "protocol_structure", + "policy_eligible": true, "source": "mcp_annotation", "source_pointer": "/tools/1", "evidence": { @@ -1755,10 +1905,13 @@ "scopes": [], "claims": [ { + "claim_id": "clm_086cd06d298307836911", "dimension": "authority", "value": "none", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "reviewed_declaration", + "policy_eligible": true, "source": "action_surface_declaration", "source_pointer": "action_surface.actions[tool='summarize_case'].authority", "evidence": { @@ -1768,10 +1921,13 @@ } }, { + "claim_id": "clm_31b93c95a5f16d36e1fc", "dimension": "authority", "value": "none", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "protocol_structure", + "policy_eligible": true, "source": "mcp_authority", "source_pointer": "/tools/1", "evidence": { @@ -1796,10 +1952,13 @@ ], "claims": [ { + "claim_id": "clm_74e61303c65c843b3114", "dimension": "binding", "value": "agent_v1:71a7997718e73610b0704464->tool_v2_d38c0379eb241f5063861aecb39b242b58e030f320ede64d7c862bfd6f68d638", "confidence": "high", "provenance_kind": "ast_extraction", + "basis": "typed_provider_fact", + "policy_eligible": true, "source": "crew.py", "source_pointer": "crew.py:31", "evidence": { @@ -1850,10 +2009,13 @@ ], "claims": [ { + "claim_id": "clm_1af43a9e60aefd8a4694", "dimension": "identity", "value": "obs_v1_75cdf97835473a87a94cced0791d82e93653eb17faa022f9fe39a5156b4de1be", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "reviewed_declaration", + "policy_eligible": true, "source": "tool_identity_binding", "source_pointer": "crew.py", "evidence": { @@ -1863,10 +2025,13 @@ } }, { + "claim_id": "clm_fdcd52b2ba2493df6c6c", "dimension": "identity", "value": "obs_v1_d80a6855053ad21b53e7781eb52851609ab5e62b9cdde3f3e17bb9bfd16d260c", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "reviewed_declaration", + "policy_eligible": true, "source": "tool_identity_binding", "source_pointer": "/tools/0", "evidence": { @@ -1890,10 +2055,13 @@ ], "claims": [ { + "claim_id": "clm_7a35a4ab0117daf26f6b", "dimension": "binding", "value": "agent_v1:71a7997718e73610b0704464->tool_v2_8bb9341b31f3731d4ef2337cde788263775c2f388329654c2bc8a8b521170ed1", "confidence": "high", "provenance_kind": "ast_extraction", + "basis": "typed_provider_fact", + "policy_eligible": true, "source": "crew.py", "source_pointer": "crew.py:31", "evidence": { @@ -1910,19 +2078,25 @@ "confidence": "high", "claims": [ { + "claim_id": "clm_d06c32d64f8f0493a478", "dimension": "effect", "value": "read", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "reviewed_declaration", + "policy_eligible": true, "source": "action_surface_declaration", "source_pointer": "action_surface.actions[tool='lookup_case'].effect", "evidence": {} }, { + "claim_id": "clm_50c4d6abc43dbc7853c5", "dimension": "effect", "value": "read", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "protocol_structure", + "policy_eligible": true, "source": "mcp_annotation", "source_pointer": "/tools/0", "evidence": { @@ -1940,10 +2114,13 @@ "scopes": [], "claims": [ { + "claim_id": "clm_74d88f35831b62f08e96", "dimension": "authority", "value": "none", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "reviewed_declaration", + "policy_eligible": true, "source": "action_surface_declaration", "source_pointer": "action_surface.actions[tool='lookup_case'].authority", "evidence": { @@ -1953,10 +2130,13 @@ } }, { + "claim_id": "clm_b1bf872bb994c8ec4e1c", "dimension": "authority", "value": "none", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "protocol_structure", + "policy_eligible": true, "source": "mcp_authority", "source_pointer": "/tools/0", "evidence": { @@ -1981,10 +2161,13 @@ ], "claims": [ { + "claim_id": "clm_7a35a4ab0117daf26f6b", "dimension": "binding", "value": "agent_v1:71a7997718e73610b0704464->tool_v2_8bb9341b31f3731d4ef2337cde788263775c2f388329654c2bc8a8b521170ed1", "confidence": "high", "provenance_kind": "ast_extraction", + "basis": "typed_provider_fact", + "policy_eligible": true, "source": "crew.py", "source_pointer": "crew.py:31", "evidence": { @@ -2033,10 +2216,13 @@ ], "claims": [ { + "claim_id": "clm_152bd608505071fa6c11", "dimension": "identity", "value": "obs_v1_91dc9ef11aaa2cd63b91fb9e90b6607ecaf5e678f470c54d3520feb74c93aea8", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "reviewed_declaration", + "policy_eligible": true, "source": "tool_identity_binding", "source_pointer": "/tools/2", "evidence": { @@ -2046,10 +2232,13 @@ } }, { + "claim_id": "clm_cf26c71fba22caf0ba3b", "dimension": "identity", "value": "obs_v1_d47ab93f36b5308c7f1fd05073fdca72147fe034a6ec7da6470753270c29bd32", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "reviewed_declaration", + "policy_eligible": true, "source": "tool_identity_binding", "source_pointer": "crew.py", "evidence": { @@ -2073,10 +2262,13 @@ ], "claims": [ { + "claim_id": "clm_690d38df3006fc448a65", "dimension": "binding", "value": "agent_v1:71a7997718e73610b0704464->tool_v2_97cdb0ffc03b3a364a1697b362308564d52d44b3dab46f799f1b7b5276adb212", "confidence": "high", "provenance_kind": "ast_extraction", + "basis": "typed_provider_fact", + "policy_eligible": true, "source": "crew.py", "source_pointer": "crew.py:31", "evidence": { @@ -2093,19 +2285,25 @@ "confidence": "high", "claims": [ { + "claim_id": "clm_5bc827466b89cc7ad878", "dimension": "effect", "value": "read", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "reviewed_declaration", + "policy_eligible": true, "source": "action_surface_declaration", "source_pointer": "action_surface.actions[tool='FileReadTool'].effect", "evidence": {} }, { + "claim_id": "clm_7b810e50aa2ed073a420", "dimension": "effect", "value": "read", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "protocol_structure", + "policy_eligible": true, "source": "mcp_annotation", "source_pointer": "/tools/2", "evidence": { @@ -2123,10 +2321,13 @@ "scopes": [], "claims": [ { + "claim_id": "clm_843f3f7ad828d36e917f", "dimension": "authority", "value": "ambient", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "reviewed_declaration", + "policy_eligible": true, "source": "action_surface_declaration", "source_pointer": "action_surface.actions[tool='FileReadTool'].authority", "evidence": { @@ -2136,10 +2337,13 @@ } }, { + "claim_id": "clm_d734d699ce978298e895", "dimension": "authority", "value": "ambient", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "protocol_structure", + "policy_eligible": true, "source": "mcp_authority", "source_pointer": "/tools/2", "evidence": { @@ -2164,10 +2368,13 @@ ], "claims": [ { + "claim_id": "clm_690d38df3006fc448a65", "dimension": "binding", "value": "agent_v1:71a7997718e73610b0704464->tool_v2_97cdb0ffc03b3a364a1697b362308564d52d44b3dab46f799f1b7b5276adb212", "confidence": "high", "provenance_kind": "ast_extraction", + "basis": "typed_provider_fact", + "policy_eligible": true, "source": "crew.py", "source_pointer": "crew.py:31", "evidence": { @@ -2216,10 +2423,13 @@ ], "claims": [ { + "claim_id": "clm_617a4c03070c7e111158", "dimension": "identity", "value": "obs_v1_4da41a223c5b9819f534ad9f701d469aa605b258c479ae1768a5de3882e45d13", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "reviewed_declaration", + "policy_eligible": true, "source": "tool_identity_binding", "source_pointer": "/tools/1", "evidence": { @@ -2229,10 +2439,13 @@ } }, { + "claim_id": "clm_190c664ed7b33cd14479", "dimension": "identity", "value": "obs_v1_8107b85e09d544636b259f216489e077ba0c6915829b5e4a0c708d960d9b00df", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "reviewed_declaration", + "policy_eligible": true, "source": "tool_identity_binding", "source_pointer": "crew.py", "evidence": { @@ -2256,10 +2469,13 @@ ], "claims": [ { + "claim_id": "clm_74e61303c65c843b3114", "dimension": "binding", "value": "agent_v1:71a7997718e73610b0704464->tool_v2_d38c0379eb241f5063861aecb39b242b58e030f320ede64d7c862bfd6f68d638", "confidence": "high", "provenance_kind": "ast_extraction", + "basis": "typed_provider_fact", + "policy_eligible": true, "source": "crew.py", "source_pointer": "crew.py:31", "evidence": { @@ -2276,19 +2492,25 @@ "confidence": "high", "claims": [ { + "claim_id": "clm_13f60eeda1090905abfa", "dimension": "effect", "value": "read", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "reviewed_declaration", + "policy_eligible": true, "source": "action_surface_declaration", "source_pointer": "action_surface.actions[tool='summarize_case'].effect", "evidence": {} }, { + "claim_id": "clm_84b18c77354a223b68f9", "dimension": "effect", "value": "read", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "protocol_structure", + "policy_eligible": true, "source": "mcp_annotation", "source_pointer": "/tools/1", "evidence": { @@ -2306,10 +2528,13 @@ "scopes": [], "claims": [ { + "claim_id": "clm_086cd06d298307836911", "dimension": "authority", "value": "none", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "reviewed_declaration", + "policy_eligible": true, "source": "action_surface_declaration", "source_pointer": "action_surface.actions[tool='summarize_case'].authority", "evidence": { @@ -2319,10 +2544,13 @@ } }, { + "claim_id": "clm_31b93c95a5f16d36e1fc", "dimension": "authority", "value": "none", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "protocol_structure", + "policy_eligible": true, "source": "mcp_authority", "source_pointer": "/tools/1", "evidence": { @@ -2347,10 +2575,13 @@ ], "claims": [ { + "claim_id": "clm_74e61303c65c843b3114", "dimension": "binding", "value": "agent_v1:71a7997718e73610b0704464->tool_v2_d38c0379eb241f5063861aecb39b242b58e030f320ede64d7c862bfd6f68d638", "confidence": "high", "provenance_kind": "ast_extraction", + "basis": "typed_provider_fact", + "policy_eligible": true, "source": "crew.py", "source_pointer": "crew.py:31", "evidence": { @@ -2367,6 +2598,7 @@ "source_warnings": [ "CrewAI prebuilt tool 'FileReadTool' at crew.py:28 was recorded as low-confidence metadata; provide an explicit inventory for full review." ], + "policy_evidence_gaps": [], "agent_summary": { "verdict": "review_required", "headline": "1 finding(s) require human review. 1 finding need review and evidence coverage is incomplete.", @@ -2474,4 +2706,4 @@ } ] } -} +} \ No newline at end of file diff --git a/samples/simple_langchain_agent/expected/report.json b/samples/simple_langchain_agent/expected/report.json index dcf46778..8e7bdb9b 100644 --- a/samples/simple_langchain_agent/expected/report.json +++ b/samples/simple_langchain_agent/expected/report.json @@ -1,7 +1,7 @@ { "schema_version": "0.1", - "report_schema_version": "0.32", - "run_id": "agents_shipgate_5051fb83bfbcadad", + "report_schema_version": "0.33", + "run_id": "agents_shipgate_ad594d77df1d5943", "manifest_dir": "/samples/simple_langchain_agent", "project": { "name": "simple-langchain-agent" @@ -94,7 +94,8 @@ "pass_eligible": true, "gap_count": 0, "reason_counts": {} - } + }, + "policy_gap_count": 0 }, "baseline_delta": { "enabled": false, @@ -244,7 +245,7 @@ ] }, "action_surface_facts": { - "snapshot_version": "0.3", + "snapshot_version": "0.4", "actions": [ { "action_id": "agent:simple-langchain-agent/support-case-reader:action_v2_d0dd3e9ff4e2e56868595ef12e50a97b37c9619cad32007dd4155c3a8b5554a2", @@ -277,10 +278,13 @@ ], "claims": [ { + "claim_id": "clm_dc05444ca9c48fbbc9e4", "dimension": "identity", "value": "obs_v1_5a5ea25e06f7f548a030228c69bb7fcc005eefc80b1c949897eb0a45f6633e74", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "reviewed_declaration", + "policy_eligible": true, "source": "tool_identity_binding", "source_pointer": "/tools/0", "evidence": { @@ -290,10 +294,13 @@ } }, { + "claim_id": "clm_a9ce566a7f8043ad969b", "dimension": "identity", "value": "obs_v1_6b9558cf467f2b12019c3d7494b38e8c31d8d1e7042b6a14318f01d1f7e76ab7", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "reviewed_declaration", + "policy_eligible": true, "source": "tool_identity_binding", "source_pointer": "agent.py", "evidence": { @@ -316,10 +323,13 @@ ], "claims": [ { + "claim_id": "clm_aa77105a77049fc20f76", "dimension": "binding", "value": "agent_v1:e72499e3feb23dae6e706766->tool_v2_d819fcc45add4884625d6fbdfacbb41d221868c520cb1c660e3f9ec5a6020a3c", "confidence": "high", "provenance_kind": "ast_extraction", + "basis": "typed_provider_fact", + "policy_eligible": true, "source": "agent.py", "source_pointer": "agent.py:30", "evidence": { @@ -336,19 +346,25 @@ "confidence": "high", "claims": [ { + "claim_id": "clm_d06c32d64f8f0493a478", "dimension": "effect", "value": "read", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "reviewed_declaration", + "policy_eligible": true, "source": "action_surface_declaration", "source_pointer": "action_surface.actions[tool='lookup_case'].effect", "evidence": {} }, { + "claim_id": "clm_50c4d6abc43dbc7853c5", "dimension": "effect", "value": "read", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "protocol_structure", + "policy_eligible": true, "source": "mcp_annotation", "source_pointer": "/tools/0", "evidence": { @@ -366,10 +382,13 @@ "scopes": [], "claims": [ { + "claim_id": "clm_74d88f35831b62f08e96", "dimension": "authority", "value": "none", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "reviewed_declaration", + "policy_eligible": true, "source": "action_surface_declaration", "source_pointer": "action_surface.actions[tool='lookup_case'].authority", "evidence": { @@ -379,10 +398,13 @@ } }, { + "claim_id": "clm_b1bf872bb994c8ec4e1c", "dimension": "authority", "value": "none", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "protocol_structure", + "policy_eligible": true, "source": "mcp_authority", "source_pointer": "/tools/0", "evidence": { @@ -460,10 +482,13 @@ ], "claims": [ { + "claim_id": "clm_0172107ddeaa0042b429", "dimension": "identity", "value": "obs_v1_cca646e5e6b411bdae0262852ae9b5fc1a9cf8929ada5e68d65a3b8eec7db7c2", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "reviewed_declaration", + "policy_eligible": true, "source": "tool_identity_binding", "source_pointer": "/tools/1", "evidence": { @@ -473,10 +498,13 @@ } }, { + "claim_id": "clm_49ac9d7fada51ef1ee43", "dimension": "identity", "value": "obs_v1_d044cf6a9ba34e66fc888092ff8c7f1ff38b73f4b2f0229fc23d290f6f92cf73", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "reviewed_declaration", + "policy_eligible": true, "source": "tool_identity_binding", "source_pointer": "agent.py", "evidence": { @@ -499,10 +527,13 @@ ], "claims": [ { + "claim_id": "clm_be72ee458b04629530c5", "dimension": "binding", "value": "agent_v1:e72499e3feb23dae6e706766->tool_v2_2fdcf1b1fb2da2ef9bf3310f3998aa0942e03c878df2e00f7f99ba6437a2ce53", "confidence": "high", "provenance_kind": "ast_extraction", + "basis": "typed_provider_fact", + "policy_eligible": true, "source": "agent.py", "source_pointer": "agent.py:30", "evidence": { @@ -519,19 +550,25 @@ "confidence": "high", "claims": [ { + "claim_id": "clm_13f60eeda1090905abfa", "dimension": "effect", "value": "read", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "reviewed_declaration", + "policy_eligible": true, "source": "action_surface_declaration", "source_pointer": "action_surface.actions[tool='summarize_case'].effect", "evidence": {} }, { + "claim_id": "clm_84b18c77354a223b68f9", "dimension": "effect", "value": "read", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "protocol_structure", + "policy_eligible": true, "source": "mcp_annotation", "source_pointer": "/tools/1", "evidence": { @@ -549,10 +586,13 @@ "scopes": [], "claims": [ { + "claim_id": "clm_086cd06d298307836911", "dimension": "authority", "value": "none", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "reviewed_declaration", + "policy_eligible": true, "source": "action_surface_declaration", "source_pointer": "action_surface.actions[tool='summarize_case'].authority", "evidence": { @@ -562,10 +602,13 @@ } }, { + "claim_id": "clm_31b93c95a5f16d36e1fc", "dimension": "authority", "value": "none", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "protocol_structure", + "policy_eligible": true, "source": "mcp_authority", "source_pointer": "/tools/1", "evidence": { @@ -730,8 +773,8 @@ "findings": [], "recommended_actions": [], "generated_reports": { - "markdown": "/private/tmp/shipgate-golden-simple_langchain_agent/report.md", - "json": "/private/tmp/shipgate-golden-simple_langchain_agent/report.json" + "markdown": "/private/tmp/shipgate-goldens/langchain/report.md", + "json": "/private/tmp/shipgate-goldens/langchain/report.json" }, "loaded_policy_packs": [], "loaded_plugins": [], @@ -773,10 +816,13 @@ ], "claims": [ { + "claim_id": "clm_0172107ddeaa0042b429", "dimension": "identity", "value": "obs_v1_cca646e5e6b411bdae0262852ae9b5fc1a9cf8929ada5e68d65a3b8eec7db7c2", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "reviewed_declaration", + "policy_eligible": true, "source": "tool_identity_binding", "source_pointer": "/tools/1", "evidence": { @@ -786,10 +832,13 @@ } }, { + "claim_id": "clm_49ac9d7fada51ef1ee43", "dimension": "identity", "value": "obs_v1_d044cf6a9ba34e66fc888092ff8c7f1ff38b73f4b2f0229fc23d290f6f92cf73", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "reviewed_declaration", + "policy_eligible": true, "source": "tool_identity_binding", "source_pointer": "agent.py", "evidence": { @@ -812,10 +861,13 @@ ], "claims": [ { + "claim_id": "clm_be72ee458b04629530c5", "dimension": "binding", "value": "agent_v1:e72499e3feb23dae6e706766->tool_v2_2fdcf1b1fb2da2ef9bf3310f3998aa0942e03c878df2e00f7f99ba6437a2ce53", "confidence": "high", "provenance_kind": "ast_extraction", + "basis": "typed_provider_fact", + "policy_eligible": true, "source": "agent.py", "source_pointer": "agent.py:30", "evidence": { @@ -832,19 +884,25 @@ "confidence": "high", "claims": [ { + "claim_id": "clm_13f60eeda1090905abfa", "dimension": "effect", "value": "read", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "reviewed_declaration", + "policy_eligible": true, "source": "action_surface_declaration", "source_pointer": "action_surface.actions[tool='summarize_case'].effect", "evidence": {} }, { + "claim_id": "clm_84b18c77354a223b68f9", "dimension": "effect", "value": "read", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "protocol_structure", + "policy_eligible": true, "source": "mcp_annotation", "source_pointer": "/tools/1", "evidence": { @@ -862,10 +920,13 @@ "scopes": [], "claims": [ { + "claim_id": "clm_086cd06d298307836911", "dimension": "authority", "value": "none", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "reviewed_declaration", + "policy_eligible": true, "source": "action_surface_declaration", "source_pointer": "action_surface.actions[tool='summarize_case'].authority", "evidence": { @@ -875,10 +936,13 @@ } }, { + "claim_id": "clm_31b93c95a5f16d36e1fc", "dimension": "authority", "value": "none", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "protocol_structure", + "policy_eligible": true, "source": "mcp_authority", "source_pointer": "/tools/1", "evidence": { @@ -902,10 +966,13 @@ ], "claims": [ { + "claim_id": "clm_be72ee458b04629530c5", "dimension": "binding", "value": "agent_v1:e72499e3feb23dae6e706766->tool_v2_2fdcf1b1fb2da2ef9bf3310f3998aa0942e03c878df2e00f7f99ba6437a2ce53", "confidence": "high", "provenance_kind": "ast_extraction", + "basis": "typed_provider_fact", + "policy_eligible": true, "source": "agent.py", "source_pointer": "agent.py:30", "evidence": { @@ -954,10 +1021,13 @@ ], "claims": [ { + "claim_id": "clm_dc05444ca9c48fbbc9e4", "dimension": "identity", "value": "obs_v1_5a5ea25e06f7f548a030228c69bb7fcc005eefc80b1c949897eb0a45f6633e74", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "reviewed_declaration", + "policy_eligible": true, "source": "tool_identity_binding", "source_pointer": "/tools/0", "evidence": { @@ -967,10 +1037,13 @@ } }, { + "claim_id": "clm_a9ce566a7f8043ad969b", "dimension": "identity", "value": "obs_v1_6b9558cf467f2b12019c3d7494b38e8c31d8d1e7042b6a14318f01d1f7e76ab7", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "reviewed_declaration", + "policy_eligible": true, "source": "tool_identity_binding", "source_pointer": "agent.py", "evidence": { @@ -993,10 +1066,13 @@ ], "claims": [ { + "claim_id": "clm_aa77105a77049fc20f76", "dimension": "binding", "value": "agent_v1:e72499e3feb23dae6e706766->tool_v2_d819fcc45add4884625d6fbdfacbb41d221868c520cb1c660e3f9ec5a6020a3c", "confidence": "high", "provenance_kind": "ast_extraction", + "basis": "typed_provider_fact", + "policy_eligible": true, "source": "agent.py", "source_pointer": "agent.py:30", "evidence": { @@ -1013,19 +1089,25 @@ "confidence": "high", "claims": [ { + "claim_id": "clm_d06c32d64f8f0493a478", "dimension": "effect", "value": "read", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "reviewed_declaration", + "policy_eligible": true, "source": "action_surface_declaration", "source_pointer": "action_surface.actions[tool='lookup_case'].effect", "evidence": {} }, { + "claim_id": "clm_50c4d6abc43dbc7853c5", "dimension": "effect", "value": "read", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "protocol_structure", + "policy_eligible": true, "source": "mcp_annotation", "source_pointer": "/tools/0", "evidence": { @@ -1043,10 +1125,13 @@ "scopes": [], "claims": [ { + "claim_id": "clm_74d88f35831b62f08e96", "dimension": "authority", "value": "none", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "reviewed_declaration", + "policy_eligible": true, "source": "action_surface_declaration", "source_pointer": "action_surface.actions[tool='lookup_case'].authority", "evidence": { @@ -1056,10 +1141,13 @@ } }, { + "claim_id": "clm_b1bf872bb994c8ec4e1c", "dimension": "authority", "value": "none", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "protocol_structure", + "policy_eligible": true, "source": "mcp_authority", "source_pointer": "/tools/0", "evidence": { @@ -1083,10 +1171,13 @@ ], "claims": [ { + "claim_id": "clm_aa77105a77049fc20f76", "dimension": "binding", "value": "agent_v1:e72499e3feb23dae6e706766->tool_v2_d819fcc45add4884625d6fbdfacbb41d221868c520cb1c660e3f9ec5a6020a3c", "confidence": "high", "provenance_kind": "ast_extraction", + "basis": "typed_provider_fact", + "policy_eligible": true, "source": "agent.py", "source_pointer": "agent.py:30", "evidence": { @@ -1137,10 +1228,13 @@ ], "claims": [ { + "claim_id": "clm_0172107ddeaa0042b429", "dimension": "identity", "value": "obs_v1_cca646e5e6b411bdae0262852ae9b5fc1a9cf8929ada5e68d65a3b8eec7db7c2", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "reviewed_declaration", + "policy_eligible": true, "source": "tool_identity_binding", "source_pointer": "/tools/1", "evidence": { @@ -1150,10 +1244,13 @@ } }, { + "claim_id": "clm_49ac9d7fada51ef1ee43", "dimension": "identity", "value": "obs_v1_d044cf6a9ba34e66fc888092ff8c7f1ff38b73f4b2f0229fc23d290f6f92cf73", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "reviewed_declaration", + "policy_eligible": true, "source": "tool_identity_binding", "source_pointer": "agent.py", "evidence": { @@ -1176,10 +1273,13 @@ ], "claims": [ { + "claim_id": "clm_be72ee458b04629530c5", "dimension": "binding", "value": "agent_v1:e72499e3feb23dae6e706766->tool_v2_2fdcf1b1fb2da2ef9bf3310f3998aa0942e03c878df2e00f7f99ba6437a2ce53", "confidence": "high", "provenance_kind": "ast_extraction", + "basis": "typed_provider_fact", + "policy_eligible": true, "source": "agent.py", "source_pointer": "agent.py:30", "evidence": { @@ -1196,19 +1296,25 @@ "confidence": "high", "claims": [ { + "claim_id": "clm_13f60eeda1090905abfa", "dimension": "effect", "value": "read", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "reviewed_declaration", + "policy_eligible": true, "source": "action_surface_declaration", "source_pointer": "action_surface.actions[tool='summarize_case'].effect", "evidence": {} }, { + "claim_id": "clm_84b18c77354a223b68f9", "dimension": "effect", "value": "read", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "protocol_structure", + "policy_eligible": true, "source": "mcp_annotation", "source_pointer": "/tools/1", "evidence": { @@ -1226,10 +1332,13 @@ "scopes": [], "claims": [ { + "claim_id": "clm_086cd06d298307836911", "dimension": "authority", "value": "none", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "reviewed_declaration", + "policy_eligible": true, "source": "action_surface_declaration", "source_pointer": "action_surface.actions[tool='summarize_case'].authority", "evidence": { @@ -1239,10 +1348,13 @@ } }, { + "claim_id": "clm_31b93c95a5f16d36e1fc", "dimension": "authority", "value": "none", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "protocol_structure", + "policy_eligible": true, "source": "mcp_authority", "source_pointer": "/tools/1", "evidence": { @@ -1266,10 +1378,13 @@ ], "claims": [ { + "claim_id": "clm_be72ee458b04629530c5", "dimension": "binding", "value": "agent_v1:e72499e3feb23dae6e706766->tool_v2_2fdcf1b1fb2da2ef9bf3310f3998aa0942e03c878df2e00f7f99ba6437a2ce53", "confidence": "high", "provenance_kind": "ast_extraction", + "basis": "typed_provider_fact", + "policy_eligible": true, "source": "agent.py", "source_pointer": "agent.py:30", "evidence": { @@ -1318,10 +1433,13 @@ ], "claims": [ { + "claim_id": "clm_dc05444ca9c48fbbc9e4", "dimension": "identity", "value": "obs_v1_5a5ea25e06f7f548a030228c69bb7fcc005eefc80b1c949897eb0a45f6633e74", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "reviewed_declaration", + "policy_eligible": true, "source": "tool_identity_binding", "source_pointer": "/tools/0", "evidence": { @@ -1331,10 +1449,13 @@ } }, { + "claim_id": "clm_a9ce566a7f8043ad969b", "dimension": "identity", "value": "obs_v1_6b9558cf467f2b12019c3d7494b38e8c31d8d1e7042b6a14318f01d1f7e76ab7", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "reviewed_declaration", + "policy_eligible": true, "source": "tool_identity_binding", "source_pointer": "agent.py", "evidence": { @@ -1357,10 +1478,13 @@ ], "claims": [ { + "claim_id": "clm_aa77105a77049fc20f76", "dimension": "binding", "value": "agent_v1:e72499e3feb23dae6e706766->tool_v2_d819fcc45add4884625d6fbdfacbb41d221868c520cb1c660e3f9ec5a6020a3c", "confidence": "high", "provenance_kind": "ast_extraction", + "basis": "typed_provider_fact", + "policy_eligible": true, "source": "agent.py", "source_pointer": "agent.py:30", "evidence": { @@ -1377,19 +1501,25 @@ "confidence": "high", "claims": [ { + "claim_id": "clm_d06c32d64f8f0493a478", "dimension": "effect", "value": "read", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "reviewed_declaration", + "policy_eligible": true, "source": "action_surface_declaration", "source_pointer": "action_surface.actions[tool='lookup_case'].effect", "evidence": {} }, { + "claim_id": "clm_50c4d6abc43dbc7853c5", "dimension": "effect", "value": "read", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "protocol_structure", + "policy_eligible": true, "source": "mcp_annotation", "source_pointer": "/tools/0", "evidence": { @@ -1407,10 +1537,13 @@ "scopes": [], "claims": [ { + "claim_id": "clm_74d88f35831b62f08e96", "dimension": "authority", "value": "none", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "reviewed_declaration", + "policy_eligible": true, "source": "action_surface_declaration", "source_pointer": "action_surface.actions[tool='lookup_case'].authority", "evidence": { @@ -1420,10 +1553,13 @@ } }, { + "claim_id": "clm_b1bf872bb994c8ec4e1c", "dimension": "authority", "value": "none", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "protocol_structure", + "policy_eligible": true, "source": "mcp_authority", "source_pointer": "/tools/0", "evidence": { @@ -1447,10 +1583,13 @@ ], "claims": [ { + "claim_id": "clm_aa77105a77049fc20f76", "dimension": "binding", "value": "agent_v1:e72499e3feb23dae6e706766->tool_v2_d819fcc45add4884625d6fbdfacbb41d221868c520cb1c660e3f9ec5a6020a3c", "confidence": "high", "provenance_kind": "ast_extraction", + "basis": "typed_provider_fact", + "policy_eligible": true, "source": "agent.py", "source_pointer": "agent.py:30", "evidence": { @@ -1465,6 +1604,7 @@ } ], "source_warnings": [], + "policy_evidence_gaps": [], "agent_summary": { "verdict": "passed", "headline": "Release ready. All in-scope actions have complete, conflict-free explicit or structural static effect and authority evidence; no active blockers. Runtime behavior was not verified.", @@ -1554,4 +1694,4 @@ "human_ack_satisfied": true, "top_reason_codes": [] } -} +} \ No newline at end of file diff --git a/samples/simple_openai_api_agent/expected/report.json b/samples/simple_openai_api_agent/expected/report.json index afab475a..8f25c66c 100644 --- a/samples/simple_openai_api_agent/expected/report.json +++ b/samples/simple_openai_api_agent/expected/report.json @@ -1,7 +1,7 @@ { "schema_version": "0.1", - "report_schema_version": "0.32", - "run_id": "agents_shipgate_3189f29f2db532fe", + "report_schema_version": "0.33", + "run_id": "agents_shipgate_151b2a90d910c866", "manifest_dir": "/samples/simple_openai_api_agent", "project": { "name": "simple-openai-api-agent", @@ -53,8 +53,8 @@ "summary": { "status": "release_blockers_detected", "critical_count": 1, - "high_count": 15, - "medium_count": 5, + "high_count": 10, + "medium_count": 4, "low_count": 0, "info_count": 0, "suppressed_count": 0, @@ -85,7 +85,33 @@ }, "policy_evidence_source": null, "capability_refs": [], - "capability_trace_refs": [] + "capability_trace_refs": [], + "support": { + "status": "matched", + "confidence": "high", + "policy_eligible": true, + "blocking_eligible": true, + "claim_ids": [], + "evidence_bases": [ + "protocol_structure" + ], + "predicates": [ + { + "predicate": "deterministic_action_rule", + "status": "matched", + "expected": null, + "observed": "agent:simple-openai-api-agent/api-refund-assistant:action_v2_9d10be42545cd7457766e72c52ab9ba754eee7aa97c9613ae60ba4bc4048af82", + "confidence": "high", + "claim_ids": [], + "evidence_bases": [ + "protocol_structure" + ], + "policy_eligible": true, + "why": null + } + ], + "support_hash": "sha256:0b26a3779bfbd1539b1d6c1a4168e605f9bfaafc0af7186a4a7565d49b47d64c" + } }, { "id": "fp_304deac63e13e3c3", @@ -107,54 +133,36 @@ }, "policy_evidence_source": null, "capability_refs": [], - "capability_trace_refs": [] + "capability_trace_refs": [], + "support": { + "status": "matched", + "confidence": "high", + "policy_eligible": true, + "blocking_eligible": true, + "claim_ids": [], + "evidence_bases": [ + "protocol_structure" + ], + "predicates": [ + { + "predicate": "deterministic_action_rule", + "status": "matched", + "expected": null, + "observed": "agent:simple-openai-api-agent/api-refund-assistant:action_v2_b5ba1b1612ff86ebfe4ad43459c8c26a79e44eb61a5a2f7496ab5d9166b4cfd5", + "confidence": "high", + "claim_ids": [], + "evidence_bases": [ + "protocol_structure" + ], + "policy_eligible": true, + "why": null + } + ], + "support_hash": "sha256:7045a6360ba4f27a7b90fc4b3b138b391332f05307f836677791b41ffbd8b972" + } } ], "review_items": [ - { - "id": "fp_3521aa242911af07", - "fingerprint": "fp_3521aa242911af07", - "check_id": "SHIP-SCHEMA-BROAD-FREE-TEXT", - "severity": "high", - "title": "send_customer_email accepts broad free-form action input", - "baseline_status": null, - "blocks_release": false, - "source": { - "type": "openai_api", - "ref": "tools/openai-tools.json#1", - "location": null, - "path": "tools/openai-tools.json", - "start_line": null, - "end_line": null, - "start_column": null, - "pointer": "/tools/1" - }, - "policy_evidence_source": null, - "capability_refs": [], - "capability_trace_refs": [] - }, - { - "id": "fp_5e682e1038239894", - "fingerprint": "fp_5e682e1038239894", - "check_id": "SHIP-SCHEMA-MISSING-BOUNDS", - "severity": "high", - "title": "create_refund.amount has no maximum bound", - "baseline_status": null, - "blocks_release": false, - "source": { - "type": "openai_api", - "ref": "tools/openai-tools.json#0", - "location": null, - "path": "tools/openai-tools.json", - "start_line": null, - "end_line": null, - "start_column": null, - "pointer": "/tools/0" - }, - "policy_evidence_source": null, - "capability_refs": [], - "capability_trace_refs": [] - }, { "id": "fp_13b6198e71d217dd", "fingerprint": "fp_13b6198e71d217dd", @@ -175,7 +183,8 @@ }, "policy_evidence_source": null, "capability_refs": [], - "capability_trace_refs": [] + "capability_trace_refs": [], + "support": null }, { "id": "fp_368e8c29ea686e2e", @@ -197,69 +206,8 @@ }, "policy_evidence_source": null, "capability_refs": [], - "capability_trace_refs": [] - }, - { - "id": "fp_b9007e3ae3ed5f5f", - "fingerprint": "fp_b9007e3ae3ed5f5f", - "check_id": "SHIP-SCOPE-PROHIBITED-TOOL-PRESENT", - "severity": "high", - "title": "send_customer_email appears to overlap with a prohibited action", - "baseline_status": null, - "blocks_release": false, - "source": { - "type": "openai_api", - "ref": "tools/openai-tools.json#1", - "location": null, - "path": "tools/openai-tools.json", - "start_line": null, - "end_line": null, - "start_column": null, - "pointer": "/tools/1" - }, - "policy_evidence_source": { - "type": "manifest", - "ref": "shipgate.yaml#/agent/prohibited_actions/1", - "location": null, - "path": "shipgate.yaml", - "start_line": 11, - "end_line": null, - "start_column": null, - "pointer": "/agent/prohibited_actions/1" - }, - "capability_refs": [], - "capability_trace_refs": [] - }, - { - "id": "fp_4e7eb5aecdf2a82e", - "fingerprint": "fp_4e7eb5aecdf2a82e", - "check_id": "SHIP-SCOPE-PROHIBITED-TOOL-PRESENT", - "severity": "high", - "title": "create_refund appears to overlap with a prohibited action", - "baseline_status": null, - "blocks_release": false, - "source": { - "type": "openai_api", - "ref": "tools/openai-tools.json#0", - "location": null, - "path": "tools/openai-tools.json", - "start_line": null, - "end_line": null, - "start_column": null, - "pointer": "/tools/0" - }, - "policy_evidence_source": { - "type": "manifest", - "ref": "shipgate.yaml#/agent/prohibited_actions/0", - "location": null, - "path": "shipgate.yaml", - "start_line": 10, - "end_line": null, - "start_column": null, - "pointer": "/agent/prohibited_actions/0" - }, - "capability_refs": [], - "capability_trace_refs": [] + "capability_trace_refs": [], + "support": null }, { "id": "fp_d98bf7612c5be651", @@ -290,7 +238,8 @@ "pointer": "/policies/require_idempotency_for_tools" }, "capability_refs": [], - "capability_trace_refs": [] + "capability_trace_refs": [], + "support": null }, { "id": "fp_cfe9353889dee25f", @@ -312,7 +261,8 @@ }, "policy_evidence_source": null, "capability_refs": [], - "capability_trace_refs": [] + "capability_trace_refs": [], + "support": null }, { "id": "fp_fea0fd40167efead", @@ -334,7 +284,8 @@ }, "policy_evidence_source": null, "capability_refs": [], - "capability_trace_refs": [] + "capability_trace_refs": [], + "support": null }, { "id": "fp_36565ae087474960", @@ -356,51 +307,8 @@ }, "policy_evidence_source": null, "capability_refs": [], - "capability_trace_refs": [] - }, - { - "id": "fp_116d02961abe80e2_d6a46917", - "fingerprint": "fp_116d02961abe80e2", - "check_id": "SHIP-API-PROMPT-TOOL-SCOPE-MISMATCH", - "severity": "high", - "title": "Prompt says read-only or advise-only while write/high-risk tools are enabled", - "baseline_status": null, - "blocks_release": false, - "source": { - "type": "manifest", - "ref": "shipgate.yaml", - "location": null, - "path": null, - "start_line": null, - "end_line": null, - "start_column": null, - "pointer": null - }, - "policy_evidence_source": null, - "capability_refs": [], - "capability_trace_refs": [] - }, - { - "id": "fp_116d02961abe80e2_6f6fb033", - "fingerprint": "fp_116d02961abe80e2", - "check_id": "SHIP-API-PROMPT-TOOL-SCOPE-MISMATCH", - "severity": "medium", - "title": "Prompt lacks approval/confirmation language for high-risk tools", - "baseline_status": null, - "blocks_release": false, - "source": { - "type": "manifest", - "ref": "shipgate.yaml", - "location": null, - "path": null, - "start_line": null, - "end_line": null, - "start_column": null, - "pointer": null - }, - "policy_evidence_source": null, - "capability_refs": [], - "capability_trace_refs": [] + "capability_trace_refs": [], + "support": null }, { "id": "fp_b15fbb62fef1c41c", @@ -422,7 +330,8 @@ }, "policy_evidence_source": null, "capability_refs": [], - "capability_trace_refs": [] + "capability_trace_refs": [], + "support": null }, { "id": "fp_886b380dba4cbb51", @@ -444,7 +353,8 @@ }, "policy_evidence_source": null, "capability_refs": [], - "capability_trace_refs": [] + "capability_trace_refs": [], + "support": null }, { "id": "fp_25d66c07cb88ccbc", @@ -466,7 +376,8 @@ }, "policy_evidence_source": null, "capability_refs": [], - "capability_trace_refs": [] + "capability_trace_refs": [], + "support": null }, { "id": "fp_21b95af39e98c806", @@ -488,7 +399,8 @@ }, "policy_evidence_source": null, "capability_refs": [], - "capability_trace_refs": [] + "capability_trace_refs": [], + "support": null }, { "id": "fp_7313c45114c31330", @@ -514,7 +426,8 @@ ], "capability_trace_refs": [ "ctrace_8669c1b40747c28a" - ] + ], + "support": null }, { "id": "fp_0a9a82612142bbfb", @@ -536,7 +449,8 @@ }, "policy_evidence_source": null, "capability_refs": [], - "capability_trace_refs": [] + "capability_trace_refs": [], + "support": null }, { "id": "fp_88b22e5b095fcc05", @@ -558,7 +472,8 @@ }, "policy_evidence_source": null, "capability_refs": [], - "capability_trace_refs": [] + "capability_trace_refs": [], + "support": null } ], "evidence_coverage": { @@ -566,7 +481,152 @@ "human_review_recommended": true, "source_warning_count": 0, "low_confidence_tool_count": 0, - "evidence_gaps": [], + "evidence_gaps": [ + { + "kind": "inferred_policy_applicability", + "subject": "tool_v2_99ba7c14d528f47da6093918bb937aafc5d75dd4f4972797c8e9697c14bcb61b", + "source_type": null, + "source_ref": "tools/openai-tools.json", + "why": "SHIP-SCHEMA-BROAD-FREE-TEXT: Policy applicability is supported only by heuristic evidence.", + "next_action": { + "kind": "provide_policy_evidence", + "command": "agents-shipgate verify --workspace . --config shipgate.yaml --ci-mode advisory --format json", + "path": "/tools/1", + "why": "Heuristic or unknown evidence cannot create a blocking policy finding.", + "expects": "Provide reviewed or structural evidence for every indeterminate predicate.", + "accepted_values": [ + "reviewed_declaration", + "protocol_structure", + "typed_provider_fact", + "structural_scope" + ], + "suggested_patch_kind": "manual", + "declaration_template": null, + "auto_apply": false, + "requires_human_review": true + } + }, + { + "kind": "inferred_policy_applicability", + "subject": "tool_v2_b900644b842a2ec3ce8827653cf7153ac2b7f03e18f92566f5c12058bb368865", + "source_type": null, + "source_ref": "tools/openai-tools.json", + "why": "SHIP-SCHEMA-MISSING-BOUNDS: Policy applicability is supported only by heuristic evidence.", + "next_action": { + "kind": "provide_policy_evidence", + "command": "agents-shipgate verify --workspace . --config shipgate.yaml --ci-mode advisory --format json", + "path": "/tools/0", + "why": "Heuristic or unknown evidence cannot create a blocking policy finding.", + "expects": "Provide reviewed or structural evidence for every indeterminate predicate.", + "accepted_values": [ + "reviewed_declaration", + "protocol_structure", + "typed_provider_fact", + "structural_scope" + ], + "suggested_patch_kind": "manual", + "declaration_template": null, + "auto_apply": false, + "requires_human_review": true + } + }, + { + "kind": "inferred_policy_applicability", + "subject": "tool_v2_99ba7c14d528f47da6093918bb937aafc5d75dd4f4972797c8e9697c14bcb61b", + "source_type": null, + "source_ref": "shipgate.yaml", + "why": "SHIP-SCOPE-PROHIBITED-TOOL-PRESENT: Policy applicability is supported only by heuristic evidence.", + "next_action": { + "kind": "provide_policy_evidence", + "command": "agents-shipgate verify --workspace . --config shipgate.yaml --ci-mode advisory --format json", + "path": "/agent/prohibited_actions/1", + "why": "Heuristic or unknown evidence cannot create a blocking policy finding.", + "expects": "Provide reviewed or structural evidence for every indeterminate predicate.", + "accepted_values": [ + "reviewed_declaration", + "protocol_structure", + "typed_provider_fact", + "structural_scope" + ], + "suggested_patch_kind": "manual", + "declaration_template": null, + "auto_apply": false, + "requires_human_review": true + } + }, + { + "kind": "inferred_policy_applicability", + "subject": "tool_v2_b900644b842a2ec3ce8827653cf7153ac2b7f03e18f92566f5c12058bb368865", + "source_type": null, + "source_ref": "shipgate.yaml", + "why": "SHIP-SCOPE-PROHIBITED-TOOL-PRESENT: Policy applicability is supported only by heuristic evidence.", + "next_action": { + "kind": "provide_policy_evidence", + "command": "agents-shipgate verify --workspace . --config shipgate.yaml --ci-mode advisory --format json", + "path": "/agent/prohibited_actions/0", + "why": "Heuristic or unknown evidence cannot create a blocking policy finding.", + "expects": "Provide reviewed or structural evidence for every indeterminate predicate.", + "accepted_values": [ + "reviewed_declaration", + "protocol_structure", + "typed_provider_fact", + "structural_scope" + ], + "suggested_patch_kind": "manual", + "declaration_template": null, + "auto_apply": false, + "requires_human_review": true + } + }, + { + "kind": "inferred_policy_applicability", + "subject": "agent:simple-openai-api-agent/api-refund-assistant", + "source_type": null, + "source_ref": "shipgate.yaml", + "why": "SHIP-API-PROMPT-TOOL-SCOPE-MISMATCH: Policy applicability is supported only by heuristic evidence.", + "next_action": { + "kind": "provide_policy_evidence", + "command": "agents-shipgate verify --workspace . --config shipgate.yaml --ci-mode advisory --format json", + "path": "/checks/SHIP-API-PROMPT-TOOL-SCOPE-MISMATCH", + "why": "Heuristic or unknown evidence cannot create a blocking policy finding.", + "expects": "Provide reviewed or structural evidence for every indeterminate predicate.", + "accepted_values": [ + "reviewed_declaration", + "protocol_structure", + "typed_provider_fact", + "structural_scope" + ], + "suggested_patch_kind": "manual", + "declaration_template": null, + "auto_apply": false, + "requires_human_review": true + } + }, + { + "kind": "inferred_policy_applicability", + "subject": "agent:simple-openai-api-agent/api-refund-assistant", + "source_type": null, + "source_ref": "shipgate.yaml", + "why": "SHIP-API-PROMPT-TOOL-SCOPE-MISMATCH: Policy applicability is supported only by heuristic evidence.", + "next_action": { + "kind": "provide_policy_evidence", + "command": "agents-shipgate verify --workspace . --config shipgate.yaml --ci-mode advisory --format json", + "path": "/checks/SHIP-API-PROMPT-TOOL-SCOPE-MISMATCH", + "why": "Heuristic or unknown evidence cannot create a blocking policy finding.", + "expects": "Provide reviewed or structural evidence for every indeterminate predicate.", + "accepted_values": [ + "reviewed_declaration", + "protocol_structure", + "typed_provider_fact", + "structural_scope" + ], + "suggested_patch_kind": "manual", + "declaration_template": null, + "auto_apply": false, + "requires_human_review": true + } + } + ], "semantic_coverage": { "total_actions": 2, "pass_eligible_actions": 0, @@ -593,7 +653,8 @@ "pass_eligible": true, "gap_count": 0, "reason_counts": {} - } + }, + "policy_gap_count": 6 }, "baseline_delta": { "enabled": false, @@ -613,22 +674,6 @@ "runtime_behavior_verified": false, "static_verdict_disclaimer": "This verdict covers deterministic static evidence only. Agents Shipgate did not execute the agent or prove runtime behavior, tool routing, credential enforcement, or safety.", "contribution_rules": [ - { - "finding_id": "fp_3521aa242911af07", - "fingerprint": "fp_3521aa242911af07", - "check_id": "SHIP-SCHEMA-BROAD-FREE-TEXT", - "category": "review_item", - "rule": "review_required", - "rationale": "requires_human_review=true (severity=high); routed to review_items." - }, - { - "finding_id": "fp_5e682e1038239894", - "fingerprint": "fp_5e682e1038239894", - "check_id": "SHIP-SCHEMA-MISSING-BOUNDS", - "category": "review_item", - "rule": "review_required", - "rationale": "requires_human_review=true (severity=high); routed to review_items." - }, { "finding_id": "fp_13b6198e71d217dd", "fingerprint": "fp_13b6198e71d217dd", @@ -645,22 +690,6 @@ "rule": "review_required", "rationale": "requires_human_review=true (severity=high); routed to review_items." }, - { - "finding_id": "fp_b9007e3ae3ed5f5f", - "fingerprint": "fp_b9007e3ae3ed5f5f", - "check_id": "SHIP-SCOPE-PROHIBITED-TOOL-PRESENT", - "category": "review_item", - "rule": "review_required", - "rationale": "requires_human_review=true (severity=high); routed to review_items." - }, - { - "finding_id": "fp_4e7eb5aecdf2a82e", - "fingerprint": "fp_4e7eb5aecdf2a82e", - "check_id": "SHIP-SCOPE-PROHIBITED-TOOL-PRESENT", - "category": "review_item", - "rule": "review_required", - "rationale": "requires_human_review=true (severity=high); routed to review_items." - }, { "finding_id": "fp_d98bf7612c5be651", "fingerprint": "fp_d98bf7612c5be651", @@ -693,22 +722,6 @@ "rule": "review_required", "rationale": "requires_human_review=true (severity=medium); routed to review_items." }, - { - "finding_id": "fp_116d02961abe80e2_d6a46917", - "fingerprint": "fp_116d02961abe80e2", - "check_id": "SHIP-API-PROMPT-TOOL-SCOPE-MISMATCH", - "category": "review_item", - "rule": "review_required", - "rationale": "requires_human_review=true (severity=high); routed to review_items." - }, - { - "finding_id": "fp_116d02961abe80e2_6f6fb033", - "fingerprint": "fp_116d02961abe80e2", - "check_id": "SHIP-API-PROMPT-TOOL-SCOPE-MISMATCH", - "category": "review_item", - "rule": "review_required", - "rationale": "requires_human_review=true (severity=medium); routed to review_items." - }, { "finding_id": "fp_b15fbb62fef1c41c", "fingerprint": "fp_b15fbb62fef1c41c", @@ -806,10 +819,13 @@ ], "claims": [ { + "claim_id": "clm_41a9ca17cde05bd77645", "dimension": "identity", "value": "obs_v1_650c1e14249a3ef5447890a74dbb1d7b0c71682d4a1c61a888343a6381d6a10e", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "protocol_structure", + "policy_eligible": true, "source": "source_observation", "source_pointer": "/tools/0", "evidence": { @@ -832,10 +848,13 @@ ], "claims": [ { + "claim_id": "clm_a1ab0cfe6d16d737fc1b", "dimension": "binding", "value": "agent_v1:3e1354866b28ba54f69c8e73->tool_v2_b900644b842a2ec3ce8827653cf7153ac2b7f03e18f92566f5c12058bb368865", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "reviewed_declaration", + "policy_eligible": true, "source": "agent_binding_declaration", "source_pointer": "/agent_bindings/declarations/0/tools/0", "evidence": { @@ -852,19 +871,25 @@ "confidence": "high", "claims": [ { + "claim_id": "clm_5a97ce2724d487c4af0c", "dimension": "effect", "value": "financial_write", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "reviewed_declaration", + "policy_eligible": true, "source": "action_surface_declaration", "source_pointer": "action_surface.actions[tool='create_refund'].effect", "evidence": {} }, { + "claim_id": "clm_f7877a49e8f5ad2d9883", "dimension": "effect", "value": "financial_write", "confidence": "high", "provenance_kind": "keyword_heuristic", + "basis": "inferred_keyword", + "policy_eligible": false, "source": "risk_hint:keyword", "source_pointer": "/tools/0", "evidence": { @@ -874,10 +899,13 @@ } }, { + "claim_id": "clm_6ccec9883e72e188e513", "dimension": "effect", "value": "write", "confidence": "medium", "provenance_kind": "keyword_heuristic", + "basis": "inferred_keyword", + "policy_eligible": false, "source": "risk_hint:openai_api_keyword", "source_pointer": "/tools/0", "evidence": { @@ -896,10 +924,13 @@ "scopes": [], "claims": [ { + "claim_id": "clm_e2eb14edf5f5dd49f6a3", "dimension": "authority", "value": "unscoped", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "reviewed_declaration", + "policy_eligible": true, "source": "action_surface_declaration", "source_pointer": "action_surface.actions[tool='create_refund'].authority", "evidence": { @@ -922,14 +953,10 @@ "included_reason": "referenced_by_critical_finding", "control_status": "missing", "related_findings": [ - "fp_116d02961abe80e2_6f6fb033", - "fp_116d02961abe80e2_d6a46917", "fp_21b95af39e98c806", "fp_25d66c07cb88ccbc", "fp_304deac63e13e3c3", "fp_368e8c29ea686e2e", - "fp_4e7eb5aecdf2a82e", - "fp_5e682e1038239894", "fp_7313c45114c31330", "fp_88b22e5b095fcc05", "fp_b15fbb62fef1c41c", @@ -959,10 +986,13 @@ ], "claims": [ { + "claim_id": "clm_41beac269652852478eb", "dimension": "identity", "value": "obs_v1_6975b831c4f5ddcc793bb748ab778dfb271e6372197d26849a3c6a45ac5aefe8", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "protocol_structure", + "policy_eligible": true, "source": "source_observation", "source_pointer": "/tools/1", "evidence": { @@ -985,10 +1015,13 @@ ], "claims": [ { + "claim_id": "clm_dff05deb93bb1d4b42d8", "dimension": "binding", "value": "agent_v1:3e1354866b28ba54f69c8e73->tool_v2_99ba7c14d528f47da6093918bb937aafc5d75dd4f4972797c8e9697c14bcb61b", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "reviewed_declaration", + "policy_eligible": true, "source": "agent_binding_declaration", "source_pointer": "/agent_bindings/declarations/0/tools/1", "evidence": { @@ -1005,19 +1038,25 @@ "confidence": "high", "claims": [ { + "claim_id": "clm_d7302d32eaefab6d707f", "dimension": "effect", "value": "external_communication", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "reviewed_declaration", + "policy_eligible": true, "source": "action_surface_declaration", "source_pointer": "action_surface.actions[tool='send_customer_email'].effect", "evidence": {} }, { + "claim_id": "clm_f0196c0c4caab8cd8ecd", "dimension": "effect", "value": "external_communication", "confidence": "high", "provenance_kind": "keyword_heuristic", + "basis": "inferred_keyword", + "policy_eligible": false, "source": "risk_hint:keyword", "source_pointer": "/tools/1", "evidence": { @@ -1026,10 +1065,13 @@ } }, { + "claim_id": "clm_b56eed6a901658bae650", "dimension": "effect", "value": "external_communication", "confidence": "high", "provenance_kind": "keyword_heuristic", + "basis": "inferred_keyword", + "policy_eligible": false, "source": "risk_hint:keyword", "source_pointer": "/tools/1", "evidence": { @@ -1038,10 +1080,13 @@ } }, { + "claim_id": "clm_54ec9c771e8cb69be726", "dimension": "effect", "value": "write", "confidence": "medium", "provenance_kind": "keyword_heuristic", + "basis": "inferred_keyword", + "policy_eligible": false, "source": "risk_hint:openai_api_keyword", "source_pointer": "/tools/1", "evidence": { @@ -1060,10 +1105,13 @@ "scopes": [], "claims": [ { + "claim_id": "clm_96d9a2e0d17836d10435", "dimension": "authority", "value": "unscoped", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "reviewed_declaration", + "policy_eligible": true, "source": "action_surface_declaration", "source_pointer": "action_surface.actions[tool='send_customer_email'].authority", "evidence": { @@ -1088,14 +1136,10 @@ "control_status": "missing", "related_findings": [ "fp_0a9a82612142bbfb", - "fp_116d02961abe80e2_6f6fb033", - "fp_116d02961abe80e2_d6a46917", "fp_13b6198e71d217dd", - "fp_3521aa242911af07", "fp_3cbb2883d00d87fd", "fp_886b380dba4cbb51", "fp_b15fbb62fef1c41c", - "fp_b9007e3ae3ed5f5f", "fp_cfe9353889dee25f" ] } @@ -1169,25 +1213,6 @@ "gap": "create_refund function schema is not strict enough.", "release_implication": "The model may send ambiguous or overbroad tool arguments." }, - { - "id": "mis_6c4f0ff021e3ac53", - "kind": "control_missing", - "severity": "high", - "tool_name": "create_refund", - "capability_refs": [ - "cap_1936b581aee1c411" - ], - "intention_refs": [ - "int_07ec701205b21b96", - "int_788db5032dd9f920" - ], - "finding_refs": [ - "fp_5e682e1038239894" - ], - "policy_requirement": "Risky numeric parameters must declare a maximum or equivalent limit.", - "gap": "create_refund.amount has no maximum bound.", - "release_implication": "Release reviewers cannot verify blast-radius limits." - }, { "id": "mis_8b25565f5f7c1166", "kind": "control_missing", @@ -1244,24 +1269,6 @@ "gap": "send_customer_email function schema is not strict enough.", "release_implication": "The model may send ambiguous or overbroad tool arguments." }, - { - "id": "mis_6b1cc1d993e16942", - "kind": "control_missing", - "severity": "high", - "tool_name": "send_customer_email", - "capability_refs": [ - "cap_e809417ee4da1160" - ], - "intention_refs": [ - "int_bf9d4ad9100fcc59" - ], - "finding_refs": [ - "fp_3521aa242911af07" - ], - "policy_requirement": "Action-like tool inputs must constrain high-blast-radius fields.", - "gap": "send_customer_email accepts broad free-form action input.", - "release_implication": "Release reviewers cannot bound the operation payload safely." - }, { "id": "mis_e6652a7ff2eb280f", "kind": "control_missing", @@ -1280,81 +1287,6 @@ "gap": "send_customer_email is high-risk but has no owner.", "release_implication": "Release review metadata is incomplete or stale." }, - { - "id": "mis_bd44d302d017f84e", - "kind": "intent_mismatch", - "severity": "high", - "tool_name": "create_refund", - "capability_refs": [ - "cap_1936b581aee1c411" - ], - "intention_refs": [ - "int_07ec701205b21b96", - "int_788db5032dd9f920" - ], - "finding_refs": [ - "fp_116d02961abe80e2_d6a46917" - ], - "policy_requirement": "Prompt scope must align with enabled high-risk tools.", - "gap": "Prompt says read-only or advise-only while write/high-risk tools are enabled.", - "release_implication": "The agent instructions and actual tool surface disagree." - }, - { - "id": "mis_23bc4995db369fe1", - "kind": "intent_mismatch", - "severity": "high", - "tool_name": "send_customer_email", - "capability_refs": [ - "cap_e809417ee4da1160" - ], - "intention_refs": [ - "int_788db5032dd9f920", - "int_bf9d4ad9100fcc59" - ], - "finding_refs": [ - "fp_116d02961abe80e2_d6a46917" - ], - "policy_requirement": "Prompt scope must align with enabled high-risk tools.", - "gap": "Prompt says read-only or advise-only while write/high-risk tools are enabled.", - "release_implication": "The agent instructions and actual tool surface disagree." - }, - { - "id": "mis_fa1f6b701727dcfe", - "kind": "prohibited_action_present", - "severity": "high", - "tool_name": "create_refund", - "capability_refs": [ - "cap_1936b581aee1c411" - ], - "intention_refs": [ - "int_07ec701205b21b96", - "int_788db5032dd9f920" - ], - "finding_refs": [ - "fp_4e7eb5aecdf2a82e" - ], - "policy_requirement": "Prohibited actions must not be contradicted by enabled capabilities.", - "gap": "create_refund appears to overlap with a prohibited action.", - "release_implication": "The tool surface appears to enable behavior the manifest prohibits." - }, - { - "id": "mis_eda491e9c3a9426a", - "kind": "prohibited_action_present", - "severity": "high", - "tool_name": "send_customer_email", - "capability_refs": [ - "cap_e809417ee4da1160" - ], - "intention_refs": [ - "int_bf9d4ad9100fcc59" - ], - "finding_refs": [ - "fp_b9007e3ae3ed5f5f" - ], - "policy_requirement": "Prohibited actions must not be contradicted by enabled capabilities.", - "gap": "send_customer_email appears to overlap with a prohibited action.", - "release_implication": "The tool surface appears to enable behavior the manifest prohibits." - }, { "id": "mis_e212b63830ca8bd8", "kind": "scope_drift", @@ -1461,44 +1393,6 @@ "gap": "Response format schemas/refund_decision.schema.json is under-specified.", "release_implication": "Downstream release behavior may depend on under-specified output." }, - { - "id": "mis_567ca836b95cfc68", - "kind": "intent_mismatch", - "severity": "medium", - "tool_name": "create_refund", - "capability_refs": [ - "cap_1936b581aee1c411" - ], - "intention_refs": [ - "int_07ec701205b21b96", - "int_788db5032dd9f920" - ], - "finding_refs": [ - "fp_116d02961abe80e2_6f6fb033" - ], - "policy_requirement": "Prompt scope must align with enabled high-risk tools.", - "gap": "Prompt lacks approval/confirmation language for high-risk tools.", - "release_implication": "The agent instructions and actual tool surface disagree." - }, - { - "id": "mis_62e630163211b4aa", - "kind": "intent_mismatch", - "severity": "medium", - "tool_name": "send_customer_email", - "capability_refs": [ - "cap_e809417ee4da1160" - ], - "intention_refs": [ - "int_788db5032dd9f920", - "int_bf9d4ad9100fcc59" - ], - "finding_refs": [ - "fp_116d02961abe80e2_6f6fb033" - ], - "policy_requirement": "Prompt scope must align with enabled high-risk tools.", - "gap": "Prompt lacks approval/confirmation language for high-risk tools.", - "release_implication": "The agent instructions and actual tool surface disagree." - }, { "id": "mis_7d3973f4e8d6251c", "kind": "policy_gap", @@ -1579,7 +1473,7 @@ "decision": "blocked", "summary": "2 release-relevant finding(s) map to active release blockers; resolve required controls or remove the capability.", "blocker_misalignment_count": 2, - "review_misalignment_count": 19, + "review_misalignment_count": 13, "fail_policy": { "ci_mode": "advisory", "fail_on": [], @@ -1590,7 +1484,7 @@ }, "suggested_scenarios": [ { - "id": "scn_2ac9a71849417173", + "id": "scn_6144e245f4f1c21b", "scenario_type": "schema_boundary", "title": "Tool schema boundary check", "given": "Exercise the release path for create_refund, send_customer_email.", @@ -1598,14 +1492,10 @@ "source_misalignments": [ "mis_3c326236a9b058df", "mis_493a3a6e433ad920", - "mis_6b1cc1d993e16942", - "mis_6c4f0ff021e3ac53", "mis_81db85b32933b861" ], "source_findings": [ - "fp_3521aa242911af07", "fp_36565ae087474960", - "fp_5e682e1038239894", "fp_cfe9353889dee25f", "fp_fea0fd40167efead" ] @@ -1617,57 +1507,25 @@ "given": "Exercise the release path for create_refund, send_customer_email.", "expected_control": "A declared test or review scenario covers the high-risk tool path.", "source_misalignments": [ - "mis_8b25565f5f7c1166", - "mis_e6652a7ff2eb280f" - ], - "source_findings": [ - "fp_0a9a82612142bbfb", - "fp_88b22e5b095fcc05" - ] - }, - { - "id": "scn_aabf50ff30d89f7c", - "scenario_type": "idempotency_retry", - "title": "Retry behavior for risky write", - "given": "Exercise the release path for create_refund.", - "expected_control": "Retries use idempotency evidence or the side effect is not retried.", - "source_misalignments": [ - "mis_fa76175f53f594fa" - ], - "source_findings": [ - "fp_d98bf7612c5be651" - ] - }, - { - "id": "scn_b11df1fc7cdbb896", - "scenario_type": "prompt_scope_alignment", - "title": "Prompt and tool-surface alignment", - "given": "Exercise the release path for create_refund, send_customer_email.", - "expected_control": "The agent instructions match the enabled write and high-risk capabilities.", - "source_misalignments": [ - "mis_23bc4995db369fe1", - "mis_567ca836b95cfc68", - "mis_62e630163211b4aa", - "mis_bd44d302d017f84e" + "mis_8b25565f5f7c1166", + "mis_e6652a7ff2eb280f" ], "source_findings": [ - "fp_116d02961abe80e2_6f6fb033", - "fp_116d02961abe80e2_d6a46917" + "fp_0a9a82612142bbfb", + "fp_88b22e5b095fcc05" ] }, { - "id": "scn_236342f5c0b2ee9b", - "scenario_type": "prohibited_action", - "title": "Prohibited-action guard", - "given": "Exercise the release path for create_refund, send_customer_email.", - "expected_control": "The prohibited action is blocked, removed, or covered by the stated control.", + "id": "scn_aabf50ff30d89f7c", + "scenario_type": "idempotency_retry", + "title": "Retry behavior for risky write", + "given": "Exercise the release path for create_refund.", + "expected_control": "Retries use idempotency evidence or the side effect is not retried.", "source_misalignments": [ - "mis_eda491e9c3a9426a", - "mis_fa1f6b701727dcfe" + "mis_fa76175f53f594fa" ], "source_findings": [ - "fp_4e7eb5aecdf2a82e", - "fp_b9007e3ae3ed5f5f" + "fp_d98bf7612c5be651" ] }, { @@ -1846,7 +1704,7 @@ ] }, "action_surface_facts": { - "snapshot_version": "0.3", + "snapshot_version": "0.4", "actions": [ { "action_id": "agent:simple-openai-api-agent/api-refund-assistant:action_v2_9d10be42545cd7457766e72c52ab9ba754eee7aa97c9613ae60ba4bc4048af82", @@ -1878,10 +1736,13 @@ ], "claims": [ { + "claim_id": "clm_41beac269652852478eb", "dimension": "identity", "value": "obs_v1_6975b831c4f5ddcc793bb748ab778dfb271e6372197d26849a3c6a45ac5aefe8", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "protocol_structure", + "policy_eligible": true, "source": "source_observation", "source_pointer": "/tools/1", "evidence": { @@ -1904,10 +1765,13 @@ ], "claims": [ { + "claim_id": "clm_dff05deb93bb1d4b42d8", "dimension": "binding", "value": "agent_v1:3e1354866b28ba54f69c8e73->tool_v2_99ba7c14d528f47da6093918bb937aafc5d75dd4f4972797c8e9697c14bcb61b", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "reviewed_declaration", + "policy_eligible": true, "source": "agent_binding_declaration", "source_pointer": "/agent_bindings/declarations/0/tools/1", "evidence": { @@ -1924,19 +1788,25 @@ "confidence": "high", "claims": [ { + "claim_id": "clm_d7302d32eaefab6d707f", "dimension": "effect", "value": "external_communication", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "reviewed_declaration", + "policy_eligible": true, "source": "action_surface_declaration", "source_pointer": "action_surface.actions[tool='send_customer_email'].effect", "evidence": {} }, { + "claim_id": "clm_f0196c0c4caab8cd8ecd", "dimension": "effect", "value": "external_communication", "confidence": "high", "provenance_kind": "keyword_heuristic", + "basis": "inferred_keyword", + "policy_eligible": false, "source": "risk_hint:keyword", "source_pointer": "/tools/1", "evidence": { @@ -1945,10 +1815,13 @@ } }, { + "claim_id": "clm_b56eed6a901658bae650", "dimension": "effect", "value": "external_communication", "confidence": "high", "provenance_kind": "keyword_heuristic", + "basis": "inferred_keyword", + "policy_eligible": false, "source": "risk_hint:keyword", "source_pointer": "/tools/1", "evidence": { @@ -1957,10 +1830,13 @@ } }, { + "claim_id": "clm_54ec9c771e8cb69be726", "dimension": "effect", "value": "write", "confidence": "medium", "provenance_kind": "keyword_heuristic", + "basis": "inferred_keyword", + "policy_eligible": false, "source": "risk_hint:openai_api_keyword", "source_pointer": "/tools/1", "evidence": { @@ -1979,10 +1855,13 @@ "scopes": [], "claims": [ { + "claim_id": "clm_96d9a2e0d17836d10435", "dimension": "authority", "value": "unscoped", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "reviewed_declaration", + "policy_eligible": true, "source": "action_surface_declaration", "source_pointer": "action_surface.actions[tool='send_customer_email'].authority", "evidence": { @@ -2062,10 +1941,13 @@ ], "claims": [ { + "claim_id": "clm_41a9ca17cde05bd77645", "dimension": "identity", "value": "obs_v1_650c1e14249a3ef5447890a74dbb1d7b0c71682d4a1c61a888343a6381d6a10e", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "protocol_structure", + "policy_eligible": true, "source": "source_observation", "source_pointer": "/tools/0", "evidence": { @@ -2088,10 +1970,13 @@ ], "claims": [ { + "claim_id": "clm_a1ab0cfe6d16d737fc1b", "dimension": "binding", "value": "agent_v1:3e1354866b28ba54f69c8e73->tool_v2_b900644b842a2ec3ce8827653cf7153ac2b7f03e18f92566f5c12058bb368865", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "reviewed_declaration", + "policy_eligible": true, "source": "agent_binding_declaration", "source_pointer": "/agent_bindings/declarations/0/tools/0", "evidence": { @@ -2108,19 +1993,25 @@ "confidence": "high", "claims": [ { + "claim_id": "clm_5a97ce2724d487c4af0c", "dimension": "effect", "value": "financial_write", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "reviewed_declaration", + "policy_eligible": true, "source": "action_surface_declaration", "source_pointer": "action_surface.actions[tool='create_refund'].effect", "evidence": {} }, { + "claim_id": "clm_f7877a49e8f5ad2d9883", "dimension": "effect", "value": "financial_write", "confidence": "high", "provenance_kind": "keyword_heuristic", + "basis": "inferred_keyword", + "policy_eligible": false, "source": "risk_hint:keyword", "source_pointer": "/tools/0", "evidence": { @@ -2130,10 +2021,13 @@ } }, { + "claim_id": "clm_6ccec9883e72e188e513", "dimension": "effect", "value": "write", "confidence": "medium", "provenance_kind": "keyword_heuristic", + "basis": "inferred_keyword", + "policy_eligible": false, "source": "risk_hint:openai_api_keyword", "source_pointer": "/tools/0", "evidence": { @@ -2152,10 +2046,13 @@ "scopes": [], "claims": [ { + "claim_id": "clm_e2eb14edf5f5dd49f6a3", "dimension": "authority", "value": "unscoped", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "reviewed_declaration", + "policy_eligible": true, "source": "action_surface_declaration", "source_pointer": "action_surface.actions[tool='create_refund'].authority", "evidence": { @@ -2362,84 +2259,6 @@ "codex_plugin_surface": null, "baseline": null, "findings": [ - { - "id": "fp_3521aa242911af07", - "fingerprint": "fp_3521aa242911af07", - "check_id": "SHIP-SCHEMA-BROAD-FREE-TEXT", - "title": "send_customer_email accepts broad free-form action input", - "severity": "high", - "category": "schema", - "tool_id": "tool_v2_99ba7c14d528f47da6093918bb937aafc5d75dd4f4972797c8e9697c14bcb61b", - "tool_name": "send_customer_email", - "agent_id": "agent:simple-openai-api-agent/api-refund-assistant", - "evidence": { - "parameter": "message", - "type": "string" - }, - "confidence": "medium", - "provenance_kind": "keyword_heuristic", - "source": { - "type": "openai_api", - "ref": "tools/openai-tools.json#1", - "location": null, - "path": "tools/openai-tools.json", - "pointer": "/tools/1" - }, - "policy_evidence_source": null, - "capability_refs": [], - "capability_policy_evidence": null, - "policy_routing": null, - "capability_trace_refs": [], - "recommendation": "Constrain send_customer_email.message with an enum, structured schema, or narrower field-specific parameters.", - "blocks_release": false, - "suppressed": false, - "suppression_reason": null, - "baseline_status": null, - "autofix_safe": false, - "requires_human_review": true, - "suggested_patch_kind": "manual", - "docs_url": "https://github.com/ThreeMoonsLab/agents-shipgate/blob/main/docs/checks.md#ship-schema-broad-free-text", - "agent_action": "escalate_to_human" - }, - { - "id": "fp_5e682e1038239894", - "fingerprint": "fp_5e682e1038239894", - "check_id": "SHIP-SCHEMA-MISSING-BOUNDS", - "title": "create_refund.amount has no maximum bound", - "severity": "high", - "category": "schema", - "tool_id": "tool_v2_b900644b842a2ec3ce8827653cf7153ac2b7f03e18f92566f5c12058bb368865", - "tool_name": "create_refund", - "agent_id": "agent:simple-openai-api-agent/api-refund-assistant", - "evidence": { - "parameter": "amount", - "type": "number" - }, - "confidence": "high", - "provenance_kind": "keyword_heuristic", - "source": { - "type": "openai_api", - "ref": "tools/openai-tools.json#0", - "location": null, - "path": "tools/openai-tools.json", - "pointer": "/tools/0" - }, - "policy_evidence_source": null, - "capability_refs": [], - "capability_policy_evidence": null, - "policy_routing": null, - "capability_trace_refs": [], - "recommendation": "Add a maximum bound to create_refund.amount or document an equivalent limit in the tool policy.", - "blocks_release": false, - "suppressed": false, - "suppression_reason": null, - "baseline_status": null, - "autofix_safe": false, - "requires_human_review": true, - "suggested_patch_kind": "manual", - "docs_url": "https://github.com/ThreeMoonsLab/agents-shipgate/blob/main/docs/checks.md#ship-schema-missing-bounds", - "agent_action": "escalate_to_human" - }, { "id": "fp_13b6198e71d217dd", "fingerprint": "fp_13b6198e71d217dd", @@ -2470,6 +2289,7 @@ "capability_refs": [], "capability_policy_evidence": null, "policy_routing": null, + "support": null, "capability_trace_refs": [], "recommendation": "Declare operation-specific auth scopes for send_customer_email, or explicitly declare anonymous authority when the operation requires no credentials.", "blocks_release": false, @@ -2511,6 +2331,7 @@ "capability_refs": [], "capability_policy_evidence": null, "policy_routing": null, + "support": null, "capability_trace_refs": [], "recommendation": "Declare operation-specific auth scopes for create_refund, or explicitly declare anonymous authority when the operation requires no credentials.", "blocks_release": false, @@ -2523,109 +2344,6 @@ "docs_url": "https://github.com/ThreeMoonsLab/agents-shipgate/blob/main/docs/checks.md#ship-auth-missing-scope", "agent_action": "escalate_to_human" }, - { - "id": "fp_b9007e3ae3ed5f5f", - "fingerprint": "fp_b9007e3ae3ed5f5f", - "check_id": "SHIP-SCOPE-PROHIBITED-TOOL-PRESENT", - "title": "send_customer_email appears to overlap with a prohibited action", - "severity": "high", - "category": "scope", - "tool_id": "tool_v2_99ba7c14d528f47da6093918bb937aafc5d75dd4f4972797c8e9697c14bcb61b", - "tool_name": "send_customer_email", - "agent_id": "agent:simple-openai-api-agent/api-refund-assistant", - "evidence": { - "prohibited_action": "send customer email without confirmation", - "risk_tags": [ - "customer_communication", - "external_write", - "write" - ] - }, - "confidence": "medium", - "provenance_kind": "keyword_heuristic", - "source": { - "type": "openai_api", - "ref": "tools/openai-tools.json#1", - "location": null, - "path": "tools/openai-tools.json", - "pointer": "/tools/1" - }, - "policy_evidence_source": { - "type": "manifest", - "ref": "shipgate.yaml#/agent/prohibited_actions/1", - "location": null, - "path": "shipgate.yaml", - "start_line": 11, - "end_line": null, - "start_column": null, - "pointer": "/agent/prohibited_actions/1" - }, - "capability_refs": [], - "capability_policy_evidence": null, - "policy_routing": null, - "capability_trace_refs": [], - "recommendation": "Remove send_customer_email, narrow its policy, or revise prohibited_actions so the manifest and tool surface do not contradict each other.", - "blocks_release": false, - "suppressed": false, - "suppression_reason": null, - "baseline_status": null, - "autofix_safe": false, - "requires_human_review": true, - "suggested_patch_kind": "manual", - "docs_url": "https://github.com/ThreeMoonsLab/agents-shipgate/blob/main/docs/checks.md#ship-scope-prohibited-tool-present", - "agent_action": "escalate_to_human" - }, - { - "id": "fp_4e7eb5aecdf2a82e", - "fingerprint": "fp_4e7eb5aecdf2a82e", - "check_id": "SHIP-SCOPE-PROHIBITED-TOOL-PRESENT", - "title": "create_refund appears to overlap with a prohibited action", - "severity": "high", - "category": "scope", - "tool_id": "tool_v2_b900644b842a2ec3ce8827653cf7153ac2b7f03e18f92566f5c12058bb368865", - "tool_name": "create_refund", - "agent_id": "agent:simple-openai-api-agent/api-refund-assistant", - "evidence": { - "prohibited_action": "issue refund without approval", - "risk_tags": [ - "financial_action", - "write" - ] - }, - "confidence": "medium", - "provenance_kind": "keyword_heuristic", - "source": { - "type": "openai_api", - "ref": "tools/openai-tools.json#0", - "location": null, - "path": "tools/openai-tools.json", - "pointer": "/tools/0" - }, - "policy_evidence_source": { - "type": "manifest", - "ref": "shipgate.yaml#/agent/prohibited_actions/0", - "location": null, - "path": "shipgate.yaml", - "start_line": 10, - "end_line": null, - "start_column": null, - "pointer": "/agent/prohibited_actions/0" - }, - "capability_refs": [], - "capability_policy_evidence": null, - "policy_routing": null, - "capability_trace_refs": [], - "recommendation": "Remove create_refund, narrow its policy, or revise prohibited_actions so the manifest and tool surface do not contradict each other.", - "blocks_release": false, - "suppressed": false, - "suppression_reason": null, - "baseline_status": null, - "autofix_safe": false, - "requires_human_review": true, - "suggested_patch_kind": "manual", - "docs_url": "https://github.com/ThreeMoonsLab/agents-shipgate/blob/main/docs/checks.md#ship-scope-prohibited-tool-present", - "agent_action": "escalate_to_human" - }, { "id": "fp_d98bf7612c5be651", "fingerprint": "fp_d98bf7612c5be651", @@ -2665,6 +2383,7 @@ "capability_refs": [], "capability_policy_evidence": null, "policy_routing": null, + "support": null, "capability_trace_refs": [], "recommendation": "Add an idempotency key, idempotent annotation, or declared idempotency policy for create_refund.", "blocks_release": false, @@ -2710,6 +2429,7 @@ "capability_refs": [], "capability_policy_evidence": null, "policy_routing": null, + "support": null, "capability_trace_refs": [], "recommendation": "Make send_customer_email a strict function schema: object parameters, additionalProperties=false, complete required list, and bounded risky fields.", "blocks_release": false, @@ -2757,6 +2477,7 @@ "capability_refs": [], "capability_policy_evidence": null, "policy_routing": null, + "support": null, "capability_trace_refs": [], "recommendation": "Make create_refund a strict function schema: object parameters, additionalProperties=false, complete required list, and bounded risky fields.", "blocks_release": false, @@ -2802,6 +2523,7 @@ "capability_refs": [], "capability_policy_evidence": null, "policy_routing": null, + "support": null, "capability_trace_refs": [], "recommendation": "Tighten the structured output schema with enums, needs_review/refusal/error modeling, and declared critical fields.", "blocks_release": false, @@ -2814,84 +2536,6 @@ "docs_url": "https://github.com/ThreeMoonsLab/agents-shipgate/blob/main/docs/checks.md#ship-api-structured-output-readiness", "agent_action": "escalate_to_human" }, - { - "id": "fp_116d02961abe80e2_d6a46917", - "fingerprint": "fp_116d02961abe80e2", - "check_id": "SHIP-API-PROMPT-TOOL-SCOPE-MISMATCH", - "title": "Prompt says read-only or advise-only while write/high-risk tools are enabled", - "severity": "high", - "category": "api", - "tool_id": null, - "tool_name": null, - "agent_id": "agent:simple-openai-api-agent/api-refund-assistant", - "evidence": { - "tools": [ - "send_customer_email", - "create_refund" - ] - }, - "confidence": "high", - "provenance_kind": "keyword_heuristic", - "source": { - "type": "manifest", - "ref": "shipgate.yaml", - "location": null - }, - "policy_evidence_source": null, - "capability_refs": [], - "capability_policy_evidence": null, - "policy_routing": null, - "capability_trace_refs": [], - "recommendation": "Align prompt scope with enabled tools or remove write/high-risk tools.", - "blocks_release": false, - "suppressed": false, - "suppression_reason": null, - "baseline_status": null, - "autofix_safe": false, - "requires_human_review": true, - "suggested_patch_kind": "manual", - "docs_url": "https://github.com/ThreeMoonsLab/agents-shipgate/blob/main/docs/checks.md#ship-api-prompt-tool-scope-mismatch", - "agent_action": "escalate_to_human" - }, - { - "id": "fp_116d02961abe80e2_6f6fb033", - "fingerprint": "fp_116d02961abe80e2", - "check_id": "SHIP-API-PROMPT-TOOL-SCOPE-MISMATCH", - "title": "Prompt lacks approval/confirmation language for high-risk tools", - "severity": "medium", - "category": "api", - "tool_id": null, - "tool_name": null, - "agent_id": "agent:simple-openai-api-agent/api-refund-assistant", - "evidence": { - "tools": [ - "send_customer_email", - "create_refund" - ] - }, - "confidence": "medium", - "provenance_kind": "keyword_heuristic", - "source": { - "type": "manifest", - "ref": "shipgate.yaml", - "location": null - }, - "policy_evidence_source": null, - "capability_refs": [], - "capability_policy_evidence": null, - "policy_routing": null, - "capability_trace_refs": [], - "recommendation": "Add prompt instructions requiring human approval and explicit confirmation before financial, destructive, or external customer actions.", - "blocks_release": false, - "suppressed": false, - "suppression_reason": null, - "baseline_status": null, - "autofix_safe": false, - "requires_human_review": true, - "suggested_patch_kind": "manual", - "docs_url": "https://github.com/ThreeMoonsLab/agents-shipgate/blob/main/docs/checks.md#ship-api-prompt-tool-scope-mismatch", - "agent_action": "escalate_to_human" - }, { "id": "fp_b15fbb62fef1c41c", "fingerprint": "fp_b15fbb62fef1c41c", @@ -2919,6 +2563,7 @@ "capability_refs": [], "capability_policy_evidence": null, "policy_routing": null, + "support": null, "capability_trace_refs": [], "recommendation": "Declare tool-call timeout metadata for high-risk OpenAI API flows.", "blocks_release": false, @@ -2964,6 +2609,7 @@ "capability_refs": [], "capability_policy_evidence": null, "policy_routing": null, + "support": null, "capability_trace_refs": [], "recommendation": "Add idempotency evidence for send_customer_email or avoid retrying this side effect.", "blocks_release": false, @@ -3004,6 +2650,7 @@ "capability_refs": [], "capability_policy_evidence": null, "policy_routing": null, + "support": null, "capability_trace_refs": [], "recommendation": "Declare success_fields and failure_fields for create_refund in openai_api policy rules.", "blocks_release": false, @@ -3048,6 +2695,7 @@ "capability_refs": [], "capability_policy_evidence": null, "policy_routing": null, + "support": null, "capability_trace_refs": [], "recommendation": "Add idempotency evidence for create_refund or avoid retrying this side effect.", "blocks_release": false, @@ -3087,6 +2735,7 @@ ], "capability_policy_evidence": null, "policy_routing": null, + "support": null, "capability_trace_refs": [ "ctrace_8669c1b40747c28a" ], @@ -3132,6 +2781,7 @@ "capability_refs": [], "capability_policy_evidence": null, "policy_routing": null, + "support": null, "capability_trace_refs": [], "recommendation": "Declare an owner for each high-risk production tool in risk_overrides.tools.", "blocks_release": false, @@ -3174,6 +2824,7 @@ "capability_refs": [], "capability_policy_evidence": null, "policy_routing": null, + "support": null, "capability_trace_refs": [], "recommendation": "Declare an owner for each high-risk production tool in risk_overrides.tools.", "blocks_release": false, @@ -3216,6 +2867,32 @@ "capability_refs": [], "capability_policy_evidence": null, "policy_routing": null, + "support": { + "status": "matched", + "confidence": "high", + "policy_eligible": true, + "blocking_eligible": true, + "claim_ids": [], + "evidence_bases": [ + "protocol_structure" + ], + "predicates": [ + { + "predicate": "deterministic_action_rule", + "status": "matched", + "expected": null, + "observed": "agent:simple-openai-api-agent/api-refund-assistant:action_v2_9d10be42545cd7457766e72c52ab9ba754eee7aa97c9613ae60ba4bc4048af82", + "confidence": "high", + "claim_ids": [], + "evidence_bases": [ + "protocol_structure" + ], + "policy_eligible": true, + "why": null + } + ], + "support_hash": "sha256:0b26a3779bfbd1539b1d6c1a4168e605f9bfaafc0af7186a4a7565d49b47d64c" + }, "capability_trace_refs": [], "recommendation": "Declare confirmation policy and safeguards.audit_log for this external communication action.", "blocks_release": true, @@ -3259,6 +2936,32 @@ "capability_refs": [], "capability_policy_evidence": null, "policy_routing": null, + "support": { + "status": "matched", + "confidence": "high", + "policy_eligible": true, + "blocking_eligible": true, + "claim_ids": [], + "evidence_bases": [ + "protocol_structure" + ], + "predicates": [ + { + "predicate": "deterministic_action_rule", + "status": "matched", + "expected": null, + "observed": "agent:simple-openai-api-agent/api-refund-assistant:action_v2_b5ba1b1612ff86ebfe4ad43459c8c26a79e44eb61a5a2f7496ab5d9166b4cfd5", + "confidence": "high", + "claim_ids": [], + "evidence_bases": [ + "protocol_structure" + ], + "policy_eligible": true, + "why": null + } + ], + "support_hash": "sha256:7045a6360ba4f27a7b90fc4b3b138b391332f05307f836677791b41ffbd8b972" + }, "capability_trace_refs": [], "recommendation": "Declare approval.required, safeguards.audit_log, and safeguards.idempotency for this financial write action.", "blocks_release": true, @@ -3277,14 +2980,14 @@ "Declare confirmation policy and safeguards.audit_log for this external communication action.", "Make send_customer_email a strict function schema: object parameters, additionalProperties=false, complete required list, and bounded risky fields.", "Make create_refund a strict function schema: object parameters, additionalProperties=false, complete required list, and bounded risky fields.", - "Align prompt scope with enabled tools or remove write/high-risk tools.", "Add idempotency evidence for send_customer_email or avoid retrying this side effect.", "Add idempotency evidence for create_refund or avoid retrying this side effect.", - "Declare operation-specific auth scopes for send_customer_email, or explicitly declare anonymous authority when the operation requires no credentials." + "Declare operation-specific auth scopes for send_customer_email, or explicitly declare anonymous authority when the operation requires no credentials.", + "Declare operation-specific auth scopes for create_refund, or explicitly declare anonymous authority when the operation requires no credentials." ], "generated_reports": { - "markdown": "/private/tmp/shipgate-golden-simple_openai_api_agent/report.md", - "json": "/private/tmp/shipgate-golden-simple_openai_api_agent/report.json" + "markdown": "/private/tmp/shipgate-goldens/openai/report.md", + "json": "/private/tmp/shipgate-goldens/openai/report.json" }, "loaded_policy_packs": [], "loaded_plugins": [], @@ -3328,10 +3031,13 @@ ], "claims": [ { + "claim_id": "clm_41beac269652852478eb", "dimension": "identity", "value": "obs_v1_6975b831c4f5ddcc793bb748ab778dfb271e6372197d26849a3c6a45ac5aefe8", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "protocol_structure", + "policy_eligible": true, "source": "source_observation", "source_pointer": "/tools/1", "evidence": { @@ -3354,10 +3060,13 @@ ], "claims": [ { + "claim_id": "clm_dff05deb93bb1d4b42d8", "dimension": "binding", "value": "agent_v1:3e1354866b28ba54f69c8e73->tool_v2_99ba7c14d528f47da6093918bb937aafc5d75dd4f4972797c8e9697c14bcb61b", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "reviewed_declaration", + "policy_eligible": true, "source": "agent_binding_declaration", "source_pointer": "/agent_bindings/declarations/0/tools/1", "evidence": { @@ -3374,19 +3083,25 @@ "confidence": "high", "claims": [ { + "claim_id": "clm_d7302d32eaefab6d707f", "dimension": "effect", "value": "external_communication", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "reviewed_declaration", + "policy_eligible": true, "source": "action_surface_declaration", "source_pointer": "action_surface.actions[tool='send_customer_email'].effect", "evidence": {} }, { + "claim_id": "clm_f0196c0c4caab8cd8ecd", "dimension": "effect", "value": "external_communication", "confidence": "high", "provenance_kind": "keyword_heuristic", + "basis": "inferred_keyword", + "policy_eligible": false, "source": "risk_hint:keyword", "source_pointer": "/tools/1", "evidence": { @@ -3395,10 +3110,13 @@ } }, { + "claim_id": "clm_b56eed6a901658bae650", "dimension": "effect", "value": "external_communication", "confidence": "high", "provenance_kind": "keyword_heuristic", + "basis": "inferred_keyword", + "policy_eligible": false, "source": "risk_hint:keyword", "source_pointer": "/tools/1", "evidence": { @@ -3407,10 +3125,13 @@ } }, { + "claim_id": "clm_54ec9c771e8cb69be726", "dimension": "effect", "value": "write", "confidence": "medium", "provenance_kind": "keyword_heuristic", + "basis": "inferred_keyword", + "policy_eligible": false, "source": "risk_hint:openai_api_keyword", "source_pointer": "/tools/1", "evidence": { @@ -3429,10 +3150,13 @@ "scopes": [], "claims": [ { + "claim_id": "clm_96d9a2e0d17836d10435", "dimension": "authority", "value": "unscoped", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "reviewed_declaration", + "policy_eligible": true, "source": "action_surface_declaration", "source_pointer": "action_surface.actions[tool='send_customer_email'].authority", "evidence": { @@ -3456,10 +3180,13 @@ ], "claims": [ { + "claim_id": "clm_dff05deb93bb1d4b42d8", "dimension": "binding", "value": "agent_v1:3e1354866b28ba54f69c8e73->tool_v2_99ba7c14d528f47da6093918bb937aafc5d75dd4f4972797c8e9697c14bcb61b", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "reviewed_declaration", + "policy_eligible": true, "source": "agent_binding_declaration", "source_pointer": "/agent_bindings/declarations/0/tools/1", "evidence": { @@ -3508,10 +3235,13 @@ ], "claims": [ { + "claim_id": "clm_41a9ca17cde05bd77645", "dimension": "identity", "value": "obs_v1_650c1e14249a3ef5447890a74dbb1d7b0c71682d4a1c61a888343a6381d6a10e", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "protocol_structure", + "policy_eligible": true, "source": "source_observation", "source_pointer": "/tools/0", "evidence": { @@ -3534,10 +3264,13 @@ ], "claims": [ { + "claim_id": "clm_a1ab0cfe6d16d737fc1b", "dimension": "binding", "value": "agent_v1:3e1354866b28ba54f69c8e73->tool_v2_b900644b842a2ec3ce8827653cf7153ac2b7f03e18f92566f5c12058bb368865", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "reviewed_declaration", + "policy_eligible": true, "source": "agent_binding_declaration", "source_pointer": "/agent_bindings/declarations/0/tools/0", "evidence": { @@ -3554,19 +3287,25 @@ "confidence": "high", "claims": [ { + "claim_id": "clm_5a97ce2724d487c4af0c", "dimension": "effect", "value": "financial_write", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "reviewed_declaration", + "policy_eligible": true, "source": "action_surface_declaration", "source_pointer": "action_surface.actions[tool='create_refund'].effect", "evidence": {} }, { + "claim_id": "clm_f7877a49e8f5ad2d9883", "dimension": "effect", "value": "financial_write", "confidence": "high", "provenance_kind": "keyword_heuristic", + "basis": "inferred_keyword", + "policy_eligible": false, "source": "risk_hint:keyword", "source_pointer": "/tools/0", "evidence": { @@ -3576,10 +3315,13 @@ } }, { + "claim_id": "clm_6ccec9883e72e188e513", "dimension": "effect", "value": "write", "confidence": "medium", "provenance_kind": "keyword_heuristic", + "basis": "inferred_keyword", + "policy_eligible": false, "source": "risk_hint:openai_api_keyword", "source_pointer": "/tools/0", "evidence": { @@ -3598,10 +3340,13 @@ "scopes": [], "claims": [ { + "claim_id": "clm_e2eb14edf5f5dd49f6a3", "dimension": "authority", "value": "unscoped", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "reviewed_declaration", + "policy_eligible": true, "source": "action_surface_declaration", "source_pointer": "action_surface.actions[tool='create_refund'].authority", "evidence": { @@ -3625,10 +3370,13 @@ ], "claims": [ { + "claim_id": "clm_a1ab0cfe6d16d737fc1b", "dimension": "binding", "value": "agent_v1:3e1354866b28ba54f69c8e73->tool_v2_b900644b842a2ec3ce8827653cf7153ac2b7f03e18f92566f5c12058bb368865", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "reviewed_declaration", + "policy_eligible": true, "source": "agent_binding_declaration", "source_pointer": "/agent_bindings/declarations/0/tools/0", "evidence": { @@ -3681,10 +3429,13 @@ ], "claims": [ { + "claim_id": "clm_41beac269652852478eb", "dimension": "identity", "value": "obs_v1_6975b831c4f5ddcc793bb748ab778dfb271e6372197d26849a3c6a45ac5aefe8", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "protocol_structure", + "policy_eligible": true, "source": "source_observation", "source_pointer": "/tools/1", "evidence": { @@ -3707,10 +3458,13 @@ ], "claims": [ { + "claim_id": "clm_dff05deb93bb1d4b42d8", "dimension": "binding", "value": "agent_v1:3e1354866b28ba54f69c8e73->tool_v2_99ba7c14d528f47da6093918bb937aafc5d75dd4f4972797c8e9697c14bcb61b", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "reviewed_declaration", + "policy_eligible": true, "source": "agent_binding_declaration", "source_pointer": "/agent_bindings/declarations/0/tools/1", "evidence": { @@ -3727,19 +3481,25 @@ "confidence": "high", "claims": [ { + "claim_id": "clm_d7302d32eaefab6d707f", "dimension": "effect", "value": "external_communication", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "reviewed_declaration", + "policy_eligible": true, "source": "action_surface_declaration", "source_pointer": "action_surface.actions[tool='send_customer_email'].effect", "evidence": {} }, { + "claim_id": "clm_f0196c0c4caab8cd8ecd", "dimension": "effect", "value": "external_communication", "confidence": "high", "provenance_kind": "keyword_heuristic", + "basis": "inferred_keyword", + "policy_eligible": false, "source": "risk_hint:keyword", "source_pointer": "/tools/1", "evidence": { @@ -3748,10 +3508,13 @@ } }, { + "claim_id": "clm_b56eed6a901658bae650", "dimension": "effect", "value": "external_communication", "confidence": "high", "provenance_kind": "keyword_heuristic", + "basis": "inferred_keyword", + "policy_eligible": false, "source": "risk_hint:keyword", "source_pointer": "/tools/1", "evidence": { @@ -3760,10 +3523,13 @@ } }, { + "claim_id": "clm_54ec9c771e8cb69be726", "dimension": "effect", "value": "write", "confidence": "medium", "provenance_kind": "keyword_heuristic", + "basis": "inferred_keyword", + "policy_eligible": false, "source": "risk_hint:openai_api_keyword", "source_pointer": "/tools/1", "evidence": { @@ -3782,10 +3548,13 @@ "scopes": [], "claims": [ { + "claim_id": "clm_96d9a2e0d17836d10435", "dimension": "authority", "value": "unscoped", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "reviewed_declaration", + "policy_eligible": true, "source": "action_surface_declaration", "source_pointer": "action_surface.actions[tool='send_customer_email'].authority", "evidence": { @@ -3809,10 +3578,13 @@ ], "claims": [ { + "claim_id": "clm_dff05deb93bb1d4b42d8", "dimension": "binding", "value": "agent_v1:3e1354866b28ba54f69c8e73->tool_v2_99ba7c14d528f47da6093918bb937aafc5d75dd4f4972797c8e9697c14bcb61b", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "reviewed_declaration", + "policy_eligible": true, "source": "agent_binding_declaration", "source_pointer": "/agent_bindings/declarations/0/tools/1", "evidence": { @@ -3861,10 +3633,13 @@ ], "claims": [ { + "claim_id": "clm_41a9ca17cde05bd77645", "dimension": "identity", "value": "obs_v1_650c1e14249a3ef5447890a74dbb1d7b0c71682d4a1c61a888343a6381d6a10e", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "protocol_structure", + "policy_eligible": true, "source": "source_observation", "source_pointer": "/tools/0", "evidence": { @@ -3887,10 +3662,13 @@ ], "claims": [ { + "claim_id": "clm_a1ab0cfe6d16d737fc1b", "dimension": "binding", "value": "agent_v1:3e1354866b28ba54f69c8e73->tool_v2_b900644b842a2ec3ce8827653cf7153ac2b7f03e18f92566f5c12058bb368865", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "reviewed_declaration", + "policy_eligible": true, "source": "agent_binding_declaration", "source_pointer": "/agent_bindings/declarations/0/tools/0", "evidence": { @@ -3907,19 +3685,25 @@ "confidence": "high", "claims": [ { + "claim_id": "clm_5a97ce2724d487c4af0c", "dimension": "effect", "value": "financial_write", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "reviewed_declaration", + "policy_eligible": true, "source": "action_surface_declaration", "source_pointer": "action_surface.actions[tool='create_refund'].effect", "evidence": {} }, { + "claim_id": "clm_f7877a49e8f5ad2d9883", "dimension": "effect", "value": "financial_write", "confidence": "high", "provenance_kind": "keyword_heuristic", + "basis": "inferred_keyword", + "policy_eligible": false, "source": "risk_hint:keyword", "source_pointer": "/tools/0", "evidence": { @@ -3929,10 +3713,13 @@ } }, { + "claim_id": "clm_6ccec9883e72e188e513", "dimension": "effect", "value": "write", "confidence": "medium", "provenance_kind": "keyword_heuristic", + "basis": "inferred_keyword", + "policy_eligible": false, "source": "risk_hint:openai_api_keyword", "source_pointer": "/tools/0", "evidence": { @@ -3951,10 +3738,13 @@ "scopes": [], "claims": [ { + "claim_id": "clm_e2eb14edf5f5dd49f6a3", "dimension": "authority", "value": "unscoped", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "reviewed_declaration", + "policy_eligible": true, "source": "action_surface_declaration", "source_pointer": "action_surface.actions[tool='create_refund'].authority", "evidence": { @@ -3978,10 +3768,13 @@ ], "claims": [ { + "claim_id": "clm_a1ab0cfe6d16d737fc1b", "dimension": "binding", "value": "agent_v1:3e1354866b28ba54f69c8e73->tool_v2_b900644b842a2ec3ce8827653cf7153ac2b7f03e18f92566f5c12058bb368865", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "reviewed_declaration", + "policy_eligible": true, "source": "agent_binding_declaration", "source_pointer": "/agent_bindings/declarations/0/tools/0", "evidence": { @@ -3996,13 +3789,159 @@ } ], "source_warnings": [], + "policy_evidence_gaps": [ + { + "kind": "inferred_policy_applicability", + "subject": "tool_v2_99ba7c14d528f47da6093918bb937aafc5d75dd4f4972797c8e9697c14bcb61b", + "source_type": null, + "source_ref": "tools/openai-tools.json", + "why": "SHIP-SCHEMA-BROAD-FREE-TEXT: Policy applicability is supported only by heuristic evidence.", + "next_action": { + "kind": "provide_policy_evidence", + "command": "agents-shipgate verify --workspace . --config shipgate.yaml --ci-mode advisory --format json", + "path": "/tools/1", + "why": "Heuristic or unknown evidence cannot create a blocking policy finding.", + "expects": "Provide reviewed or structural evidence for every indeterminate predicate.", + "accepted_values": [ + "reviewed_declaration", + "protocol_structure", + "typed_provider_fact", + "structural_scope" + ], + "suggested_patch_kind": "manual", + "declaration_template": null, + "auto_apply": false, + "requires_human_review": true + } + }, + { + "kind": "inferred_policy_applicability", + "subject": "tool_v2_b900644b842a2ec3ce8827653cf7153ac2b7f03e18f92566f5c12058bb368865", + "source_type": null, + "source_ref": "tools/openai-tools.json", + "why": "SHIP-SCHEMA-MISSING-BOUNDS: Policy applicability is supported only by heuristic evidence.", + "next_action": { + "kind": "provide_policy_evidence", + "command": "agents-shipgate verify --workspace . --config shipgate.yaml --ci-mode advisory --format json", + "path": "/tools/0", + "why": "Heuristic or unknown evidence cannot create a blocking policy finding.", + "expects": "Provide reviewed or structural evidence for every indeterminate predicate.", + "accepted_values": [ + "reviewed_declaration", + "protocol_structure", + "typed_provider_fact", + "structural_scope" + ], + "suggested_patch_kind": "manual", + "declaration_template": null, + "auto_apply": false, + "requires_human_review": true + } + }, + { + "kind": "inferred_policy_applicability", + "subject": "tool_v2_99ba7c14d528f47da6093918bb937aafc5d75dd4f4972797c8e9697c14bcb61b", + "source_type": null, + "source_ref": "shipgate.yaml", + "why": "SHIP-SCOPE-PROHIBITED-TOOL-PRESENT: Policy applicability is supported only by heuristic evidence.", + "next_action": { + "kind": "provide_policy_evidence", + "command": "agents-shipgate verify --workspace . --config shipgate.yaml --ci-mode advisory --format json", + "path": "/agent/prohibited_actions/1", + "why": "Heuristic or unknown evidence cannot create a blocking policy finding.", + "expects": "Provide reviewed or structural evidence for every indeterminate predicate.", + "accepted_values": [ + "reviewed_declaration", + "protocol_structure", + "typed_provider_fact", + "structural_scope" + ], + "suggested_patch_kind": "manual", + "declaration_template": null, + "auto_apply": false, + "requires_human_review": true + } + }, + { + "kind": "inferred_policy_applicability", + "subject": "tool_v2_b900644b842a2ec3ce8827653cf7153ac2b7f03e18f92566f5c12058bb368865", + "source_type": null, + "source_ref": "shipgate.yaml", + "why": "SHIP-SCOPE-PROHIBITED-TOOL-PRESENT: Policy applicability is supported only by heuristic evidence.", + "next_action": { + "kind": "provide_policy_evidence", + "command": "agents-shipgate verify --workspace . --config shipgate.yaml --ci-mode advisory --format json", + "path": "/agent/prohibited_actions/0", + "why": "Heuristic or unknown evidence cannot create a blocking policy finding.", + "expects": "Provide reviewed or structural evidence for every indeterminate predicate.", + "accepted_values": [ + "reviewed_declaration", + "protocol_structure", + "typed_provider_fact", + "structural_scope" + ], + "suggested_patch_kind": "manual", + "declaration_template": null, + "auto_apply": false, + "requires_human_review": true + } + }, + { + "kind": "inferred_policy_applicability", + "subject": "agent:simple-openai-api-agent/api-refund-assistant", + "source_type": null, + "source_ref": "shipgate.yaml", + "why": "SHIP-API-PROMPT-TOOL-SCOPE-MISMATCH: Policy applicability is supported only by heuristic evidence.", + "next_action": { + "kind": "provide_policy_evidence", + "command": "agents-shipgate verify --workspace . --config shipgate.yaml --ci-mode advisory --format json", + "path": "/checks/SHIP-API-PROMPT-TOOL-SCOPE-MISMATCH", + "why": "Heuristic or unknown evidence cannot create a blocking policy finding.", + "expects": "Provide reviewed or structural evidence for every indeterminate predicate.", + "accepted_values": [ + "reviewed_declaration", + "protocol_structure", + "typed_provider_fact", + "structural_scope" + ], + "suggested_patch_kind": "manual", + "declaration_template": null, + "auto_apply": false, + "requires_human_review": true + } + }, + { + "kind": "inferred_policy_applicability", + "subject": "agent:simple-openai-api-agent/api-refund-assistant", + "source_type": null, + "source_ref": "shipgate.yaml", + "why": "SHIP-API-PROMPT-TOOL-SCOPE-MISMATCH: Policy applicability is supported only by heuristic evidence.", + "next_action": { + "kind": "provide_policy_evidence", + "command": "agents-shipgate verify --workspace . --config shipgate.yaml --ci-mode advisory --format json", + "path": "/checks/SHIP-API-PROMPT-TOOL-SCOPE-MISMATCH", + "why": "Heuristic or unknown evidence cannot create a blocking policy finding.", + "expects": "Provide reviewed or structural evidence for every indeterminate predicate.", + "accepted_values": [ + "reviewed_declaration", + "protocol_structure", + "typed_provider_fact", + "structural_scope" + ], + "suggested_patch_kind": "manual", + "declaration_template": null, + "auto_apply": false, + "requires_human_review": true + } + } + ], "agent_summary": { "verdict": "blocked", - "headline": "2 active finding(s) block release; 19 review item(s) accepted as debt.", + "headline": "2 active finding(s) block release; 13 review item(s) accepted as debt.", "blocker_count": 2, - "review_item_count": 19, + "review_item_count": 13, "auto_appliable_patches": 0, - "needs_human_review": 21, + "needs_human_review": 15, "first_recommended_action": { "kind": "info", "command": null, @@ -4038,9 +3977,9 @@ }, "reviewer_summary": { "verdict": "blocked", - "headline": "Release blocked: 24 lens changes. Start at release_decision.", + "headline": "Release blocked: 16 lens changes. Start at release_decision.", "tool_surface_changes": 0, - "capability_misalignments": 24, + "capability_misalignments": 16, "action_surface_changes": 0, "evidence_matrix_gaps": 0, "severity_overrides_applied": 0, @@ -4082,14 +4021,13 @@ "verdict": "blocked", "by_severity": { "critical": 1, - "high": 15, - "medium": 5 + "high": 10, + "medium": 4 }, "by_reason_code": { "SHIP-ACTION-EXTERNAL-COMMUNICATION-AUDIT-MISSING": 1, "SHIP-ACTION-FINANCIAL-WRITE-CONTROL-MISSING": 1, "SHIP-API-FUNCTION-SCHEMA-STRICTNESS": 2, - "SHIP-API-PROMPT-TOOL-SCOPE-MISMATCH": 2, "SHIP-API-RETRY-WITHOUT-IDEMPOTENCY": 2, "SHIP-API-STRUCTURED-OUTPUT-READINESS": 1, "SHIP-API-TIMEOUT-MISSING": 1, @@ -4097,9 +4035,6 @@ "SHIP-API-TRACE-APPROVAL-MISSING": 1, "SHIP-AUTH-MISSING-SCOPE": 2, "SHIP-MANIFEST-HIGH-RISK-OWNER-MISSING": 2, - "SHIP-SCHEMA-BROAD-FREE-TEXT": 1, - "SHIP-SCHEMA-MISSING-BOUNDS": 1, - "SHIP-SCOPE-PROHIBITED-TOOL-PRESENT": 2, "SHIP-SIDEFX-IDEMPOTENCY-MISSING": 1 }, "capability_delta_summary": { @@ -4122,17 +4057,17 @@ "count": 2 }, { - "reason_code": "SHIP-API-PROMPT-TOOL-SCOPE-MISMATCH", + "reason_code": "SHIP-API-RETRY-WITHOUT-IDEMPOTENCY", "count": 2 }, { - "reason_code": "SHIP-API-RETRY-WITHOUT-IDEMPOTENCY", + "reason_code": "SHIP-AUTH-MISSING-SCOPE", "count": 2 }, { - "reason_code": "SHIP-AUTH-MISSING-SCOPE", + "reason_code": "SHIP-MANIFEST-HIGH-RISK-OWNER-MISSING", "count": 2 } ] } -} +} \ No newline at end of file diff --git a/samples/simple_openai_api_agent/expected/report.md b/samples/simple_openai_api_agent/expected/report.md index 8b373786..629b61aa 100644 --- a/samples/simple_openai_api_agent/expected/report.md +++ b/samples/simple_openai_api_agent/expected/report.md @@ -13,19 +13,13 @@ Blockers (2): - HIGH SHIP-ACTION-EXTERNAL-COMMUNICATION-AUDIT-MISSING — send\_customer\_email has external communication capability without required controls - CRITICAL SHIP-ACTION-FINANCIAL-WRITE-CONTROL-MISSING — create\_refund has financial write capability without required controls -Review items (19): -- HIGH SHIP-SCHEMA-BROAD-FREE-TEXT — send\_customer\_email accepts broad free-form action input -- HIGH SHIP-SCHEMA-MISSING-BOUNDS — create\_refund.amount has no maximum bound +Review items (13): - HIGH SHIP-AUTH-MISSING-SCOPE — send\_customer\_email lacks declared auth scopes - HIGH SHIP-AUTH-MISSING-SCOPE — create\_refund lacks declared auth scopes -- HIGH SHIP-SCOPE-PROHIBITED-TOOL-PRESENT — send\_customer\_email appears to overlap with a prohibited action -- HIGH SHIP-SCOPE-PROHIBITED-TOOL-PRESENT — create\_refund appears to overlap with a prohibited action - HIGH SHIP-SIDEFX-IDEMPOTENCY-MISSING — create\_refund lacks idempotency evidence - HIGH SHIP-API-FUNCTION-SCHEMA-STRICTNESS — send\_customer\_email function schema is not strict enough - HIGH SHIP-API-FUNCTION-SCHEMA-STRICTNESS — create\_refund function schema is not strict enough - MEDIUM SHIP-API-STRUCTURED-OUTPUT-READINESS — Response format schemas/refund\_decision.schema.json is under-specified -- HIGH SHIP-API-PROMPT-TOOL-SCOPE-MISMATCH — Prompt says read-only or advise-only while write/high-risk tools are enabled -- MEDIUM SHIP-API-PROMPT-TOOL-SCOPE-MISMATCH — Prompt lacks approval/confirmation language for high-risk tools - MEDIUM SHIP-API-TIMEOUT-MISSING — OpenAI API flow lacks timeout metadata - HIGH SHIP-API-RETRY-WITHOUT-IDEMPOTENCY — send\_customer\_email may be retried without idempotency evidence - MEDIUM SHIP-API-TOOL-OUTPUT-SCHEMA-MISSING — create\_refund lacks success/failure output modeling @@ -43,8 +37,8 @@ Fail policy: ci_mode=advisory, fail_on=[none], new_findings_only=false, would_fa ## Summary - Critical: 1 -- High: 15 -- Medium: 5 +- High: 10 +- Medium: 4 - Low: 0 - Suppressed: 0 - Status: Release blockers detected (legacy; see Release Decision above) @@ -67,9 +61,9 @@ Fail policy: ci_mode=advisory, fail_on=[none], new_findings_only=false, would_fa Evidence: issues=\['missing\_strict\_true', 'additional\_properties\_not\_false', 'properties\_missing\_from\_required:amount,reason', 'risky\_field\_unbounded:amount'\]; risk\_tags=\['financial\_action', 'write'\] Recommendation: Make create\_refund a strict function schema: object parameters, additionalProperties=false, complete required list, and bounded risky fields. -5. Prompt says read-only or advise-only while write/high-risk tools are enabled - Evidence: tools=\['send\_customer\_email', 'create\_refund'\] - Recommendation: Align prompt scope with enabled tools or remove write/high-risk tools. +5. send\_customer\_email may be retried without idempotency evidence + Evidence: retry\_policy=\{'max\_attempts': 2\}; risk\_tags=\['customer\_communication', 'external\_write', 'write'\] + Recommendation: Add idempotency evidence for send\_customer\_email or avoid retrying this side effect. ## Finding Provenance @@ -79,7 +73,7 @@ Reviewer triage signal only. Provenance kind does not change severity, release d | --- | ---: | | `static_declaration` | 14 | | `ast_extraction` | 0 | -| `keyword_heuristic` | 6 | +| `keyword_heuristic` | 0 | | `regex_heuristic` | 0 | | `policy_pack` | 0 | | `runtime_trace` | 1 | @@ -107,16 +101,16 @@ Policy/control gaps: - HIGH control\_missing \[create\_refund\]: create\_refund function schema is not strict enough. (at tools/openai-tools.json) Requires: API function schemas must be strict enough for reliable tool calls. Release implication: The model may send ambiguous or overbroad tool arguments. -- HIGH control\_missing \[create\_refund\]: create\_refund.amount has no maximum bound. (at tools/openai-tools.json) - Requires: Risky numeric parameters must declare a maximum or equivalent limit. - Release implication: Release reviewers cannot verify blast-radius limits. - HIGH control\_missing \[create\_refund\]: create\_refund is high-risk but has no owner. (at tools/openai-tools.json) Requires: Manifest metadata must match the active release surface. Release implication: Release review metadata is incomplete or stale. - HIGH control\_missing \[create\_refund\]: create\_refund lacks idempotency evidence. (at tools/openai-tools.json) Requires: Risky write tools need idempotency evidence before retryable release. Release implication: Retries could duplicate financial, destructive, or external effects. -- 19 more in report.json +- HIGH control\_missing \[send\_customer\_email\]: send\_customer\_email function schema is not strict enough. (at tools/openai-tools.json) + Requires: API function schemas must be strict enough for reliable tool calls. + Release implication: The model may send ambiguous or overbroad tool arguments. +- 11 more in report.json Release implication: @@ -128,9 +122,8 @@ Next validation: - Tool schema boundary check: The tool accepts bounded structured inputs and returns structured outputs where needed. - High-risk tool validation case: A declared test or review scenario covers the high-risk tool path. - Retry behavior for risky write: Retries use idempotency evidence or the side effect is not retried. -- Prompt and tool-surface alignment: The agent instructions match the enabled write and high-risk capabilities. -- Prohibited-action guard: The prohibited action is blocked, removed, or covered by the stated control. -- 2 more in report.json +- Least-privilege scope review: Manifest and tool scopes match the narrow permissions needed for the release. +- Approval gate for high-risk action: The run records human approval before the tool call and denies calls without approval. ## Recommended Next Actions @@ -138,10 +131,10 @@ Next validation: - Declare confirmation policy and safeguards.audit\_log for this external communication action. - Make send\_customer\_email a strict function schema: object parameters, additionalProperties=false, complete required list, and bounded risky fields. - Make create\_refund a strict function schema: object parameters, additionalProperties=false, complete required list, and bounded risky fields. -- Align prompt scope with enabled tools or remove write/high-risk tools. - Add idempotency evidence for send\_customer\_email or avoid retrying this side effect. - Add idempotency evidence for create\_refund or avoid retrying this side effect. - Declare operation-specific auth scopes for send\_customer\_email, or explicitly declare anonymous authority when the operation requires no credentials. +- Declare operation-specific auth scopes for create\_refund, or explicitly declare anonymous authority when the operation requires no credentials. ## Tool Surface Summary @@ -196,10 +189,8 @@ Matched trace rows: - HIGH: SHIP-API-FUNCTION-SCHEMA-STRICTNESS [create\_refund] - create\_refund function schema is not strict enough - HIGH: SHIP-API-FUNCTION-SCHEMA-STRICTNESS [send\_customer\_email] - send\_customer\_email function schema is not strict enough -- HIGH: SHIP-API-PROMPT-TOOL-SCOPE-MISMATCH - Prompt says read-only or advise-only while write/high-risk tools are enabled - HIGH: SHIP-API-RETRY-WITHOUT-IDEMPOTENCY [create\_refund] - create\_refund may be retried without idempotency evidence - HIGH: SHIP-API-RETRY-WITHOUT-IDEMPOTENCY [send\_customer\_email] - send\_customer\_email may be retried without idempotency evidence -- MEDIUM: SHIP-API-PROMPT-TOOL-SCOPE-MISMATCH - Prompt lacks approval/confirmation language for high-risk tools - MEDIUM: SHIP-API-STRUCTURED-OUTPUT-READINESS - Response format schemas/refund\_decision.schema.json is under-specified - MEDIUM: SHIP-API-TIMEOUT-MISSING - OpenAI API flow lacks timeout metadata - MEDIUM: SHIP-API-TOOL-OUTPUT-SCHEMA-MISSING [create\_refund] - create\_refund lacks success/failure output modeling @@ -215,16 +206,6 @@ Matched trace rows: - HIGH: SHIP-MANIFEST-HIGH-RISK-OWNER-MISSING [create\_refund] - create\_refund is high-risk but has no owner - HIGH: SHIP-MANIFEST-HIGH-RISK-OWNER-MISSING [send\_customer\_email] - send\_customer\_email is high-risk but has no owner -### Schema - -- HIGH: SHIP-SCHEMA-BROAD-FREE-TEXT [send\_customer\_email] - send\_customer\_email accepts broad free-form action input -- HIGH: SHIP-SCHEMA-MISSING-BOUNDS [create\_refund] - create\_refund.amount has no maximum bound - -### Scope - -- HIGH: SHIP-SCOPE-PROHIBITED-TOOL-PRESENT [create\_refund] - create\_refund appears to overlap with a prohibited action -- HIGH: SHIP-SCOPE-PROHIBITED-TOOL-PRESENT [send\_customer\_email] - send\_customer\_email appears to overlap with a prohibited action - ### Side Effects - HIGH: SHIP-SIDEFX-IDEMPOTENCY-MISSING [create\_refund] - create\_refund lacks idempotency evidence diff --git a/samples/support_refund_agent/expected/packet.html b/samples/support_refund_agent/expected/packet.html index ebe6783a..75a89a84 100644 --- a/samples/support_refund_agent/expected/packet.html +++ b/samples/support_refund_agent/expected/packet.html @@ -26,4 +26,4 @@ .status-missing { color: #7f1d1d; } .status-informational { color: #555; } .meta { color: #555; font-size: 0.92rem; } -

Release Evidence Packet

Project: support-refund-agent · Agent: refund-assistant · Environment: production_like
Run id: agents_shipgate_5ad1a92a984aacac · Generated at: 2026-01-01T00:00:00+00:00 · Packet schema: 0.10

This packet is a reviewer-shaped synthesis of a static Agents Shipgate scan. See §10 for what the packet does not prove.

§1 Release decision — BLOCKED

  • Decision: blocked
  • Reason: 5 active findings block release.
  • Blockers: 5
  • Review items: 14

CI gate behavior (informational)

  • ci_mode: advisory, would_fail_ci: false, exit code: 0
  • Note: CI behavior is metadata about the run gate, not the verdict. The verdict above derives from release_decision.decision.

Static semantic coverage

  • Pass-eligible actions: 0/7
  • Evidence gaps: 7
  • Known authority review concerns: 1
  • Reasons: conflicting_binding_evidence=7, unscoped_authority=1

Blockers

  • SHIP-POLICY-APPROVAL-MISSING (critical): stripe.create_refund lacks a declared approval policy
  • SHIP-SIDEFX-IDEMPOTENCY-MISSING (critical): stripe.create_refund lacks idempotency evidence
  • SHIP-ACTION-EXTERNAL-COMMUNICATION-AUDIT-MISSING (high): gmail.send_customer_email has external communication capability without required controls
  • SHIP-ACTION-EXTERNAL-COMMUNICATION-AUDIT-MISSING (high): stripe.create_refund has external communication capability without required controls
  • SHIP-ACTION-FINANCIAL-WRITE-CONTROL-MISSING (critical): stripe.create_refund has financial write capability without required controls

Review items

  • SHIP-SCHEMA-MISSING-BOUNDS (high): stripe.create_refund.amount has no maximum bound
  • SHIP-SCHEMA-BROAD-FREE-TEXT (high): zendesk.update_ticket accepts broad free-form action input
  • SHIP-SCHEMA-BROAD-FREE-TEXT (high): gmail.send_customer_email accepts broad free-form action input
  • SHIP-AUTH-MANIFEST-BROAD-SCOPE (high): Manifest declares broad permission scopes
  • SHIP-AUTH-SCOPE-COVERAGE-MISSING (high): shopify.cancel_order requires scopes not declared in the manifest
  • SHIP-AUTH-SCOPE-COVERAGE-MISSING (high): support.search_kb requires scopes not declared in the manifest
  • SHIP-AUTH-MISSING-SCOPE (high): refund_status_lookup lacks declared auth scopes
  • SHIP-AUTH-SCOPE-COVERAGE-MISSING (high): gmail.send_customer_email requires scopes not declared in the manifest
  • SHIP-SCOPE-PROHIBITED-TOOL-PRESENT (high): stripe.create_refund appears to overlap with a prohibited action
  • SHIP-SCOPE-PROHIBITED-TOOL-PRESENT (high): gmail.send_customer_email appears to overlap with a prohibited action
  • SHIP-POLICY-CONFIRMATION-MISSING (high): stripe.create_refund lacks a declared confirmation policy
  • SHIP-POLICY-CONFIRMATION-MISSING (high): gmail.send_customer_email lacks a declared confirmation policy
  • SHIP-MANIFEST-HIGH-RISK-OWNER-MISSING (high): shopify.cancel_order is high-risk but has no owner
  • SHIP-MANIFEST-UNUSED-SCOPE (medium): Manifest declares unused permission scope zendesk:tickets:read

§1A Evidence matrix — compact review summary

  • Evidence Matrix Light is derived from public report.json only. Release decisions, CI exit behavior, and baseline semantics remain owned by release_decision. Domain rows intentionally overlap; a single finding can appear in multiple rows when it is relevant to each review lens.
DomainEvidence presentEvidence sourceConfidenceMissing controlsBlocking findingsReview items
Inventorycoveredtool_inventory; tool_surface; +1 moremedium
Schemapartialtool_surface_facts.tools[].hashes; findings[]mixedSHIP-SCHEMA-MISSING-BOUNDS on stripe.create_refund: stripe.create_refund.amount has no maximum bound; SHIP-SCHEMA-BROAD-FREE-TEXT on zendesk.update_ticket: zendesk.update_ticket accepts broad free-form action input; +1 moreSHIP-SCHEMA-MISSING-BOUNDS (high); SHIP-SCHEMA-BROAD-FREE-TEXT (high); +1 more
Authpartialtool_surface_facts.scopes; tool_inventory[].auth_scopes; +1 moremixedSHIP-AUTH-MANIFEST-BROAD-SCOPE: Manifest declares broad permission scopes; SHIP-AUTH-SCOPE-COVERAGE-MISSING on shopify.cancel_order: shopify.cancel_order requires scopes not declared in the manifest; +4 moreSHIP-AUTH-MANIFEST-BROAD-SCOPE (high); SHIP-AUTH-SCOPE-COVERAGE-MISSING (high); +4 more
Approvalpartialtool_surface_facts.controls[kind=approval_policy]; findings[]highSHIP-POLICY-APPROVAL-MISSING on stripe.create_refund: stripe.create_refund lacks a declared approval policy; SHIP-ACTION-FINANCIAL-WRITE-CONTROL-MISSING on stripe.create_refund: stripe.create_refund has financial write capability without required controlsSHIP-POLICY-APPROVAL-MISSING (critical); SHIP-ACTION-FINANCIAL-WRITE-CONTROL-MISSING (critical)
Confirmationpartialtool_surface_facts.controls[kind=confirmation_policy]; findings[]highSHIP-POLICY-CONFIRMATION-MISSING on stripe.create_refund: stripe.create_refund lacks a declared confirmation policy; SHIP-POLICY-CONFIRMATION-MISSING on gmail.send_customer_email: gmail.send_customer_email lacks a declared confirmation policySHIP-POLICY-CONFIRMATION-MISSING (high); SHIP-POLICY-CONFIRMATION-MISSING (high)
Idempotencypartialtool_surface_facts.controls[kind=idempotency_evidence]; action_surface_facts.actions[].safeguards.idempotency; +1 morehighSHIP-SIDEFX-IDEMPOTENCY-MISSING on stripe.create_refund: stripe.create_refund lacks idempotency evidence; SHIP-ACTION-FINANCIAL-WRITE-CONTROL-MISSING on stripe.create_refund: stripe.create_refund has financial write capability without required controlsSHIP-SIDEFX-IDEMPOTENCY-MISSING (critical); SHIP-ACTION-FINANCIAL-WRITE-CONTROL-MISSING (critical)
Side effectspartialtool_inventory[].risk_tags; action_surface_facts.actions[].effect; +1 moremixedSHIP-SCHEMA-BROAD-FREE-TEXT on zendesk.update_ticket: zendesk.update_ticket accepts broad free-form action input; SHIP-SCHEMA-BROAD-FREE-TEXT on gmail.send_customer_email: gmail.send_customer_email accepts broad free-form action input; +7 moreSHIP-POLICY-APPROVAL-MISSING (critical); SHIP-SIDEFX-IDEMPOTENCY-MISSING (critical); +3 moreSHIP-SCHEMA-BROAD-FREE-TEXT (high); SHIP-SCHEMA-BROAD-FREE-TEXT (high); +2 more
Memory isolationnot_declaredunknown
Human-in-the-loop evidencenot_declaredunknown
Prompt/scope alignmentpartialdeclared_intentions; misalignments; +2 moremediumSHIP-SCOPE-PROHIBITED-TOOL-PRESENT on stripe.create_refund: stripe.create_refund appears to overlap with a prohibited action; SHIP-SCOPE-PROHIBITED-TOOL-PRESENT on gmail.send_customer_email: gmail.send_customer_email appears to overlap with a prohibited actionSHIP-SCOPE-PROHIBITED-TOOL-PRESENT (high); SHIP-SCOPE-PROHIBITED-TOOL-PRESENT (high)
Retry/timeoutnot_declaredunknown
Baseline debtinformationalunknown
Action-surface policypartialaction_surface_facts.actions; findings[].blocks_release; +1 morehighSHIP-ACTION-EXTERNAL-COMMUNICATION-AUDIT-MISSING on gmail.send_customer_email: gmail.send_customer_email has external communication capability without required controls; SHIP-ACTION-EXTERNAL-COMMUNICATION-AUDIT-MISSING on stripe.create_refund: stripe.create_refund has external communication capability without required controls; +1 moreSHIP-ACTION-EXTERNAL-COMMUNICATION-AUDIT-MISSING (high); SHIP-ACTION-EXTERNAL-COMMUNICATION-AUDIT-MISSING (high); +1 more

§2 Capability ↔ Intent diff — missing

Declared

  • Purpose: answer refund policy questions
  • Purpose: prepare refund requests for human review
  • Purpose: update support ticket notes
  • Prohibited: issue refund without approval
  • Prohibited: cancel order without explicit confirmation
  • Prohibited: send external email without preview

Observed tools

  • gmail.send_customer_email
  • refund_status_lookup
  • send_email_preview
  • shopify.cancel_order
  • stripe.create_refund
  • support.search_kb
  • zendesk.update_ticket

Divergences

  • SHIP-SCOPE-PROHIBITED-TOOL-PRESENT: stripe.create_refund appears to overlap with a prohibited action
  • SHIP-SCOPE-PROHIBITED-TOOL-PRESENT: gmail.send_customer_email appears to overlap with a prohibited action

§3 High-risk tool surface — partial

Total tools: 7 · High-risk: 3

ToolSourceRisk tagsApprovalIdempotency
gmail.send_customer_emailmcpcustomer_communication, external_writenono
shopify.cancel_orderopenapidestructive, writeyesyes
stripe.create_refundopenapiexternal_write, financial_action, writenono

§3A Tool-surface diff — not declared

Status: disabled — No --diff-from report or v0.3 baseline snapshot was provided.
Base: none

§3B Action-surface diff — not declared

Status: disabled — No action-surface comparison source was provided.
Base: none

§4 Approval policy coverage — partial

ToolDeclaredSourceGap finding(s)
shopify.cancel_orderyespolicies
stripe.create_refundnofp_973ea0ef2110ca9a

Gap findings

  • SHIP-POLICY-APPROVAL-MISSING (critical): stripe.create_refund lacks a declared approval policy

§5 Idempotency / retry risk — partial

Retry policy: not declared

ToolDeclaredSourceGap finding(s)
shopify.cancel_orderyespolicies
stripe.create_refundnofp_2cf0d6c77d9c3eee

Gap findings

  • SHIP-SIDEFX-IDEMPOTENCY-MISSING (critical): stripe.create_refund lacks idempotency evidence

§6 Scope coverage — missing

Declared scopes

  • zendesk:tickets:read
  • zendesk:tickets:write
  • stripe:*
ScopeDeclaredUsed by tools
gmail:sendnogmail.send_customer_email
shopify:orders:writenoshopify.cancel_order
stripe:*yes
stripe:refunds:writeyesstripe.create_refund
support:kb:readnosupport.search_kb
zendesk:tickets:readyes
zendesk:tickets:writeyeszendesk.update_ticket

Unused declared scopes

  • zendesk:tickets:read

Used by tools but not declared

  • gmail:send
  • shopify:orders:write
  • support:kb:read

Gap findings

  • SHIP-AUTH-SCOPE-COVERAGE-MISSING (high): shopify.cancel_order requires scopes not declared in the manifest
  • SHIP-AUTH-SCOPE-COVERAGE-MISSING (high): support.search_kb requires scopes not declared in the manifest
  • SHIP-AUTH-SCOPE-COVERAGE-MISSING (high): gmail.send_customer_email requires scopes not declared in the manifest
  • SHIP-MANIFEST-UNUSED-SCOPE (medium): Manifest declares unused permission scope zendesk:tickets:read

§7 Memory isolation — not declared

Manifest does not declare a memory isolation policy. The current manifest schema (v0.1) has no agent.memory field. See §10 for the residual review item.

§8 Human-in-the-loop evidence — covered

  • Configured: yes
  • Human review recommended: yes
  • Provenance mode: fresh_scan
  • Capability-linked trace rows: 0/0 matched (0 source(s))
  • HITL evidence is local review evidence only. Missing local evidence does not prove a runtime control is absent, and present local evidence does not certify runtime enforcement.

Approval-required tools

  • shopify.cancel_order

Confirmation-required tools

  • shopify.cancel_order

§9 Required dynamic scenarios — partial

  • Manual review for SHIP-ACTION-EXTERNAL-COMMUNICATION-AUDIT-MISSING — Declare confirmation policy and safeguards.audit_log for this external communication action.
    Related finding(s): fp_1c94d2d2693dccdf, fp_e042ce7813b97a2d
  • Manual review for SHIP-ACTION-FINANCIAL-WRITE-CONTROL-MISSING — Declare approval.required, safeguards.audit_log, and safeguards.idempotency for this financial write action.
    Related finding(s): fp_dfa27ad5b52d8fd6
  • Manual review for SHIP-AUTH-MANIFEST-BROAD-SCOPE — Replace broad manifest permission scopes with the narrowest scopes needed for this release.
    Related finding(s): fp_df4a990cca9f936b
  • Manual review for SHIP-AUTH-MISSING-SCOPE — Declare operation-specific auth scopes for refund_status_lookup, or explicitly declare anonymous authority when the operation requires no credentials.
    Related finding(s): fp_519cb82f038efd10
  • Manual review for SHIP-AUTH-SCOPE-COVERAGE-MISSING — Add the required scopes for shopify.cancel_order to permissions.scopes or narrow the tool's declared auth requirements.
    Related finding(s): fp_095f3a5337124f6e, fp_1fd01b4ed2e41d51, fp_24d610b0d4324190
  • Manual review for SHIP-MANIFEST-HIGH-RISK-OWNER-MISSING — Declare an owner for each high-risk production tool in risk_overrides.tools.
    Related finding(s): fp_674fa79ae9993422
  • Manual review for SHIP-MANIFEST-UNUSED-SCOPE — Remove unused manifest scopes or add tool metadata showing why they are required.
    Related finding(s): fp_609d62f4dc434961
  • Manual review for SHIP-POLICY-APPROVAL-MISSING — Declare an approval policy for stripe.create_refund or remove this tool from the release.
    Related finding(s): fp_973ea0ef2110ca9a
  • Manual review for SHIP-POLICY-CONFIRMATION-MISSING — Declare a user confirmation policy for stripe.create_refund or remove this action from the release.
    Related finding(s): fp_c762eebfadaf39d9, fp_fae2921fd2d0cbd5
  • Manual review for SHIP-SCHEMA-BROAD-FREE-TEXT — Constrain zendesk.update_ticket.updates with an enum, structured schema, or narrower field-specific parameters.
    Related finding(s): fp_d58657d9b1c91a84, fp_ea4f18e4707f8e1d
  • Manual review for SHIP-SCHEMA-MISSING-BOUNDS — Add a maximum bound to stripe.create_refund.amount or document an equivalent limit in the tool policy.
    Related finding(s): fp_60fdf92126ba8844
  • Manual review for SHIP-SCOPE-PROHIBITED-TOOL-PRESENT — Remove stripe.create_refund, narrow its policy, or revise prohibited_actions so the manifest and tool surface do not contradict each other.
    Related finding(s): fp_5faaaee860bcec8b, fp_6f8e1ebab65f3607
  • Manual review for SHIP-SIDEFX-IDEMPOTENCY-MISSING — Add an idempotency key, idempotent annotation, or declared idempotency policy for stripe.create_refund.
    Related finding(s): fp_2cf0d6c77d9c3eee
  • Re-run scan after resolving source warnings — Source loaders emitted warnings; some tool surfaces may have been parsed with reduced confidence.

§10 What this packet did NOT prove

Agents Shipgate is an advisory tool: the deterministic merge gate for AI-generated agent capability changes, run as a local-first, static Tool-Use Readiness review. The packet below is derived from a scan; it does not, by itself, prove the following properties:

  • Prompt robustness. Whether the agent's prompt holds up under jailbreaks, persona drift, indirect prompt injection, or adversarial inputs.
  • Runtime behavior. Whether the agent actually invokes only the declared tools, respects approval gates at runtime, or follows policy under load. Static config is not runtime evidence.
  • Model correctness. Whether the underlying model produces correct outputs, calls the right tools, or stays within the declared scope. The packet does not benchmark the model.
  • Adversarial resistance. Whether the agent withstands red-team or penetration testing. The packet does not run scenarios; it organizes evidence.

Per-run residuals

  • Source warnings:
    • MCP source declares wildcard tool exposure
  • Low-confidence tool extractions: none
  • Suppressed findings in effect: none
  • Memory isolation is not modeled by the v0.1 manifest schema; no static evidence is available.
  • 5 active finding(s) came from heuristic provenance (keyword_heuristic=5, regex_heuristic=0); review the finding evidence before acting.
+

Release Evidence Packet

Project: support-refund-agent · Agent: refund-assistant · Environment: production_like
Run id: agents_shipgate_e25de5ef59b7d500 · Generated at: 2026-01-01T00:00:00+00:00 · Packet schema: 0.11

This packet is a reviewer-shaped synthesis of a static Agents Shipgate scan. See §10 for what the packet does not prove.

§1 Release decision — BLOCKED

  • Decision: blocked
  • Reason: 5 active findings block release.
  • Blockers: 5
  • Review items: 9

CI gate behavior (informational)

  • ci_mode: advisory, would_fail_ci: false, exit code: 0
  • Note: CI behavior is metadata about the run gate, not the verdict. The verdict above derives from release_decision.decision.

Static semantic coverage

  • Pass-eligible actions: 0/7
  • Evidence gaps: 7
  • Known authority review concerns: 1
  • Reasons: conflicting_binding_evidence=7, unscoped_authority=1

Blockers

  • SHIP-POLICY-APPROVAL-MISSING (critical): stripe.create_refund lacks a declared approval policy
  • SHIP-SIDEFX-IDEMPOTENCY-MISSING (critical): stripe.create_refund lacks idempotency evidence
  • SHIP-ACTION-EXTERNAL-COMMUNICATION-AUDIT-MISSING (high): gmail.send_customer_email has external communication capability without required controls
  • SHIP-ACTION-EXTERNAL-COMMUNICATION-AUDIT-MISSING (high): stripe.create_refund has external communication capability without required controls
  • SHIP-ACTION-FINANCIAL-WRITE-CONTROL-MISSING (critical): stripe.create_refund has financial write capability without required controls

Review items

  • SHIP-AUTH-MANIFEST-BROAD-SCOPE (high): Manifest declares broad permission scopes
  • SHIP-AUTH-SCOPE-COVERAGE-MISSING (high): shopify.cancel_order requires scopes not declared in the manifest
  • SHIP-AUTH-SCOPE-COVERAGE-MISSING (high): support.search_kb requires scopes not declared in the manifest
  • SHIP-AUTH-MISSING-SCOPE (high): refund_status_lookup lacks declared auth scopes
  • SHIP-AUTH-SCOPE-COVERAGE-MISSING (high): gmail.send_customer_email requires scopes not declared in the manifest
  • SHIP-POLICY-CONFIRMATION-MISSING (high): stripe.create_refund lacks a declared confirmation policy
  • SHIP-POLICY-CONFIRMATION-MISSING (high): gmail.send_customer_email lacks a declared confirmation policy
  • SHIP-MANIFEST-HIGH-RISK-OWNER-MISSING (high): shopify.cancel_order is high-risk but has no owner
  • SHIP-MANIFEST-UNUSED-SCOPE (medium): Manifest declares unused permission scope zendesk:tickets:read

§1A Evidence matrix — compact review summary

  • Evidence Matrix Light is derived from public report.json only. Release decisions, CI exit behavior, and baseline semantics remain owned by release_decision. Domain rows intentionally overlap; a single finding can appear in multiple rows when it is relevant to each review lens.
DomainEvidence presentEvidence sourceConfidenceMissing controlsBlocking findingsReview items
Inventorycoveredtool_inventory; tool_surface; +1 moremedium
Schemacoveredtool_surface_facts.tools[].hashesmedium
Authpartialtool_surface_facts.scopes; tool_inventory[].auth_scopes; +1 moremixedSHIP-AUTH-MANIFEST-BROAD-SCOPE: Manifest declares broad permission scopes; SHIP-AUTH-SCOPE-COVERAGE-MISSING on shopify.cancel_order: shopify.cancel_order requires scopes not declared in the manifest; +4 moreSHIP-AUTH-MANIFEST-BROAD-SCOPE (high); SHIP-AUTH-SCOPE-COVERAGE-MISSING (high); +4 more
Approvalpartialtool_surface_facts.controls[kind=approval_policy]; findings[]highSHIP-POLICY-APPROVAL-MISSING on stripe.create_refund: stripe.create_refund lacks a declared approval policy; SHIP-ACTION-FINANCIAL-WRITE-CONTROL-MISSING on stripe.create_refund: stripe.create_refund has financial write capability without required controlsSHIP-POLICY-APPROVAL-MISSING (critical); SHIP-ACTION-FINANCIAL-WRITE-CONTROL-MISSING (critical)
Confirmationpartialtool_surface_facts.controls[kind=confirmation_policy]; findings[]highSHIP-POLICY-CONFIRMATION-MISSING on stripe.create_refund: stripe.create_refund lacks a declared confirmation policy; SHIP-POLICY-CONFIRMATION-MISSING on gmail.send_customer_email: gmail.send_customer_email lacks a declared confirmation policySHIP-POLICY-CONFIRMATION-MISSING (high); SHIP-POLICY-CONFIRMATION-MISSING (high)
Idempotencypartialtool_surface_facts.controls[kind=idempotency_evidence]; action_surface_facts.actions[].safeguards.idempotency; +1 morehighSHIP-SIDEFX-IDEMPOTENCY-MISSING on stripe.create_refund: stripe.create_refund lacks idempotency evidence; SHIP-ACTION-FINANCIAL-WRITE-CONTROL-MISSING on stripe.create_refund: stripe.create_refund has financial write capability without required controlsSHIP-SIDEFX-IDEMPOTENCY-MISSING (critical); SHIP-ACTION-FINANCIAL-WRITE-CONTROL-MISSING (critical)
Side effectspartialtool_inventory[].risk_tags; action_surface_facts.actions[].effect; +1 morehighSHIP-POLICY-APPROVAL-MISSING on stripe.create_refund: stripe.create_refund lacks a declared approval policy; SHIP-POLICY-CONFIRMATION-MISSING on stripe.create_refund: stripe.create_refund lacks a declared confirmation policy; +5 moreSHIP-POLICY-APPROVAL-MISSING (critical); SHIP-SIDEFX-IDEMPOTENCY-MISSING (critical); +3 moreSHIP-POLICY-CONFIRMATION-MISSING (high); SHIP-POLICY-CONFIRMATION-MISSING (high)
Memory isolationnot_declaredunknown
Human-in-the-loop evidencenot_declaredunknown
Prompt/scope alignmentcovereddeclared_intentions; misalignments; +1 moremedium
Retry/timeoutnot_declaredunknown
Baseline debtinformationalunknown
Action-surface policypartialaction_surface_facts.actions; findings[].blocks_release; +1 morehighSHIP-ACTION-EXTERNAL-COMMUNICATION-AUDIT-MISSING on gmail.send_customer_email: gmail.send_customer_email has external communication capability without required controls; SHIP-ACTION-EXTERNAL-COMMUNICATION-AUDIT-MISSING on stripe.create_refund: stripe.create_refund has external communication capability without required controls; +1 moreSHIP-ACTION-EXTERNAL-COMMUNICATION-AUDIT-MISSING (high); SHIP-ACTION-EXTERNAL-COMMUNICATION-AUDIT-MISSING (high); +1 more

§2 Capability ↔ Intent diff — covered

Declared

  • Purpose: answer refund policy questions
  • Purpose: prepare refund requests for human review
  • Purpose: update support ticket notes
  • Prohibited: issue refund without approval
  • Prohibited: cancel order without explicit confirmation
  • Prohibited: send external email without preview

Observed tools

  • gmail.send_customer_email
  • refund_status_lookup
  • send_email_preview
  • shopify.cancel_order
  • stripe.create_refund
  • support.search_kb
  • zendesk.update_ticket

§3 High-risk tool surface — partial

Total tools: 7 · High-risk: 5

ToolSourceRisk tagsApprovalIdempotency
gmail.send_customer_emailmcpcustomer_communication, external_writenono
send_email_previewmcpread_onlynono
shopify.cancel_orderopenapidestructive, writeyesyes
stripe.create_refundopenapiexternal_write, financial_action, writenono
support.search_kbmcpread_onlynono

§3A Tool-surface diff — not declared

Status: disabled — No --diff-from report or v0.3 baseline snapshot was provided.
Base: none

§3B Action-surface diff — not declared

Status: disabled — No action-surface comparison source was provided.
Base: none

§4 Approval policy coverage — partial

ToolDeclaredSourceGap finding(s)
shopify.cancel_orderyespolicies
stripe.create_refundnofp_973ea0ef2110ca9a

Gap findings

  • SHIP-POLICY-APPROVAL-MISSING (critical): stripe.create_refund lacks a declared approval policy

§5 Idempotency / retry risk — partial

Retry policy: not declared

ToolDeclaredSourceGap finding(s)
shopify.cancel_orderyespolicies
stripe.create_refundnofp_2cf0d6c77d9c3eee

Gap findings

  • SHIP-SIDEFX-IDEMPOTENCY-MISSING (critical): stripe.create_refund lacks idempotency evidence

§6 Scope coverage — missing

Declared scopes

  • zendesk:tickets:read
  • zendesk:tickets:write
  • stripe:*
ScopeDeclaredUsed by tools
gmail:sendnogmail.send_customer_email
shopify:orders:writenoshopify.cancel_order
stripe:*yes
stripe:refunds:writeyesstripe.create_refund
support:kb:readnosupport.search_kb
zendesk:tickets:readyes
zendesk:tickets:writeyeszendesk.update_ticket

Unused declared scopes

  • zendesk:tickets:read

Used by tools but not declared

  • gmail:send
  • shopify:orders:write
  • support:kb:read

Gap findings

  • SHIP-AUTH-SCOPE-COVERAGE-MISSING (high): shopify.cancel_order requires scopes not declared in the manifest
  • SHIP-AUTH-SCOPE-COVERAGE-MISSING (high): support.search_kb requires scopes not declared in the manifest
  • SHIP-AUTH-SCOPE-COVERAGE-MISSING (high): gmail.send_customer_email requires scopes not declared in the manifest
  • SHIP-MANIFEST-UNUSED-SCOPE (medium): Manifest declares unused permission scope zendesk:tickets:read

§7 Memory isolation — not declared

Manifest does not declare a memory isolation policy. The current manifest schema (v0.1) has no agent.memory field. See §10 for the residual review item.

§8 Human-in-the-loop evidence — covered

  • Configured: yes
  • Human review recommended: yes
  • Provenance mode: fresh_scan
  • Capability-linked trace rows: 0/0 matched (0 source(s))
  • HITL evidence is local review evidence only. Missing local evidence does not prove a runtime control is absent, and present local evidence does not certify runtime enforcement.

Approval-required tools

  • shopify.cancel_order

Confirmation-required tools

  • shopify.cancel_order

§9 Required dynamic scenarios — partial

  • Manual review for SHIP-ACTION-EXTERNAL-COMMUNICATION-AUDIT-MISSING — Declare confirmation policy and safeguards.audit_log for this external communication action.
    Related finding(s): fp_1c94d2d2693dccdf, fp_e042ce7813b97a2d
  • Manual review for SHIP-ACTION-FINANCIAL-WRITE-CONTROL-MISSING — Declare approval.required, safeguards.audit_log, and safeguards.idempotency for this financial write action.
    Related finding(s): fp_dfa27ad5b52d8fd6
  • Manual review for SHIP-AUTH-MANIFEST-BROAD-SCOPE — Replace broad manifest permission scopes with the narrowest scopes needed for this release.
    Related finding(s): fp_df4a990cca9f936b
  • Manual review for SHIP-AUTH-MISSING-SCOPE — Declare operation-specific auth scopes for refund_status_lookup, or explicitly declare anonymous authority when the operation requires no credentials.
    Related finding(s): fp_519cb82f038efd10
  • Manual review for SHIP-AUTH-SCOPE-COVERAGE-MISSING — Add the required scopes for shopify.cancel_order to permissions.scopes or narrow the tool's declared auth requirements.
    Related finding(s): fp_095f3a5337124f6e, fp_1fd01b4ed2e41d51, fp_24d610b0d4324190
  • Manual review for SHIP-MANIFEST-HIGH-RISK-OWNER-MISSING — Declare an owner for each high-risk production tool in risk_overrides.tools.
    Related finding(s): fp_674fa79ae9993422
  • Manual review for SHIP-MANIFEST-UNUSED-SCOPE — Remove unused manifest scopes or add tool metadata showing why they are required.
    Related finding(s): fp_609d62f4dc434961
  • Manual review for SHIP-POLICY-APPROVAL-MISSING — Declare an approval policy for stripe.create_refund or remove this tool from the release.
    Related finding(s): fp_973ea0ef2110ca9a
  • Manual review for SHIP-POLICY-CONFIRMATION-MISSING — Declare a user confirmation policy for stripe.create_refund or remove this action from the release.
    Related finding(s): fp_c762eebfadaf39d9, fp_fae2921fd2d0cbd5
  • Manual review for SHIP-SIDEFX-IDEMPOTENCY-MISSING — Add an idempotency key, idempotent annotation, or declared idempotency policy for stripe.create_refund.
    Related finding(s): fp_2cf0d6c77d9c3eee
  • Re-run scan after resolving source warnings — Source loaders emitted warnings; some tool surfaces may have been parsed with reduced confidence.

§10 What this packet did NOT prove

Agents Shipgate is an advisory tool: the deterministic merge gate for AI-generated agent capability changes, run as a local-first, static Tool-Use Readiness review. The packet below is derived from a scan; it does not, by itself, prove the following properties:

  • Prompt robustness. Whether the agent's prompt holds up under jailbreaks, persona drift, indirect prompt injection, or adversarial inputs.
  • Runtime behavior. Whether the agent actually invokes only the declared tools, respects approval gates at runtime, or follows policy under load. Static config is not runtime evidence.
  • Model correctness. Whether the underlying model produces correct outputs, calls the right tools, or stays within the declared scope. The packet does not benchmark the model.
  • Adversarial resistance. Whether the agent withstands red-team or penetration testing. The packet does not run scenarios; it organizes evidence.

Per-run residuals

  • Source warnings:
    • MCP source declares wildcard tool exposure
  • Low-confidence tool extractions: none
  • Suppressed findings in effect: none
  • Memory isolation is not modeled by the v0.1 manifest schema; no static evidence is available.
diff --git a/samples/support_refund_agent/expected/packet.json b/samples/support_refund_agent/expected/packet.json index 1b340f1a..c7540271 100644 --- a/samples/support_refund_agent/expected/packet.json +++ b/samples/support_refund_agent/expected/packet.json @@ -114,6 +114,7 @@ "start_line": 97, "type": "openapi" }, + "support": null, "title": "stripe.create_refund lacks a declared approval policy" } ], @@ -145,70 +146,7 @@ "prepare refund requests for human review", "update support ticket notes" ], - "divergence_findings": [ - { - "baseline_status": null, - "blocks_release": false, - "capability_refs": [], - "capability_trace_refs": [], - "check_id": "SHIP-SCOPE-PROHIBITED-TOOL-PRESENT", - "fingerprint": "fp_6f8e1ebab65f3607", - "id": "fp_6f8e1ebab65f3607", - "policy_evidence_source": { - "end_line": null, - "location": null, - "path": "shipgate.yaml", - "pointer": "/agent/prohibited_actions/0", - "ref": "shipgate.yaml#/agent/prohibited_actions/0", - "start_column": null, - "start_line": 22, - "type": "manifest" - }, - "severity": "high", - "source": { - "end_line": null, - "location": null, - "path": "specs/support-tools.openapi.yaml", - "pointer": "/paths/~1refunds/post", - "ref": "specs/support-tools.openapi.yaml#/paths/~1refunds/post", - "start_column": 5, - "start_line": 97, - "type": "openapi" - }, - "title": "stripe.create_refund appears to overlap with a prohibited action" - }, - { - "baseline_status": null, - "blocks_release": false, - "capability_refs": [], - "capability_trace_refs": [], - "check_id": "SHIP-SCOPE-PROHIBITED-TOOL-PRESENT", - "fingerprint": "fp_5faaaee860bcec8b", - "id": "fp_5faaaee860bcec8b", - "policy_evidence_source": { - "end_line": null, - "location": null, - "path": "shipgate.yaml", - "pointer": "/agent/prohibited_actions/2", - "ref": "shipgate.yaml#/agent/prohibited_actions/2", - "start_column": null, - "start_line": 24, - "type": "manifest" - }, - "severity": "high", - "source": { - "end_line": null, - "location": null, - "path": ".agents-shipgate/mcp-tools.json", - "pointer": "/tools/1", - "ref": ".agents-shipgate/mcp-tools.json", - "start_column": null, - "start_line": null, - "type": "mcp" - }, - "title": "gmail.send_customer_email appears to overlap with a prohibited action" - } - ], + "divergence_findings": [], "observed_tool_ids": [ "tool_v2_08f6c9642ff3da07bd4dd33f949b747ad5fe79c245d1f76d450a81ff86cb1f6e", "tool_v2_2c9ee6ae2b9f0259ba2dad40a30fb18a8bdc3a732ffc207936fdd49c80e1bc9e", @@ -239,10 +177,7 @@ "prepare refund requests for human review", "update support ticket notes" ], - "divergent": [ - "gmail.send_customer_email", - "stripe.create_refund" - ], + "divergent": [], "label": "Declared purpose", "observed": [ "gmail.send_customer_email", @@ -260,15 +195,12 @@ "cancel order without explicit confirmation", "send external email without preview" ], - "divergent": [ - "gmail.send_customer_email", - "stripe.create_refund" - ], + "divergent": [], "label": "Prohibited actions", "observed": [] } ], - "status": "missing" + "status": "covered" }, "dynamic_scenarios": { "scenarios": [ @@ -339,29 +271,6 @@ "scenario": "Manual review for SHIP-POLICY-CONFIRMATION-MISSING", "why": "Declare a user confirmation policy for stripe.create_refund or remove this action from the release." }, - { - "finding_ids": [ - "fp_d58657d9b1c91a84", - "fp_ea4f18e4707f8e1d" - ], - "scenario": "Manual review for SHIP-SCHEMA-BROAD-FREE-TEXT", - "why": "Constrain zendesk.update_ticket.updates with an enum, structured schema, or narrower field-specific parameters." - }, - { - "finding_ids": [ - "fp_60fdf92126ba8844" - ], - "scenario": "Manual review for SHIP-SCHEMA-MISSING-BOUNDS", - "why": "Add a maximum bound to stripe.create_refund.amount or document an equivalent limit in the tool policy." - }, - { - "finding_ids": [ - "fp_5faaaee860bcec8b", - "fp_6f8e1ebab65f3607" - ], - "scenario": "Manual review for SHIP-SCOPE-PROHIBITED-TOOL-PRESENT", - "why": "Remove stripe.create_refund, narrow its policy, or revise prohibited_actions so the manifest and tool surface do not contradict each other." - }, { "finding_ids": [ "fp_2cf0d6c77d9c3eee" @@ -402,86 +311,14 @@ }, { "blocking_findings": [], - "confidence": "mixed", + "confidence": "medium", "domain": "Schema", - "evidence_present": "partial", + "evidence_present": "covered", "evidence_source": [ - "tool_surface_facts.tools[].hashes", - "findings[]" - ], - "missing_controls": [ - "SHIP-SCHEMA-MISSING-BOUNDS on stripe.create_refund: stripe.create_refund.amount has no maximum bound", - "SHIP-SCHEMA-BROAD-FREE-TEXT on zendesk.update_ticket: zendesk.update_ticket accepts broad free-form action input", - "SHIP-SCHEMA-BROAD-FREE-TEXT on gmail.send_customer_email: gmail.send_customer_email accepts broad free-form action input" + "tool_surface_facts.tools[].hashes" ], - "review_items": [ - { - "baseline_status": null, - "blocks_release": false, - "capability_refs": [], - "capability_trace_refs": [], - "check_id": "SHIP-SCHEMA-MISSING-BOUNDS", - "fingerprint": "fp_60fdf92126ba8844", - "id": "fp_60fdf92126ba8844", - "policy_evidence_source": null, - "severity": "high", - "source": { - "end_line": null, - "location": null, - "path": "specs/support-tools.openapi.yaml", - "pointer": "/paths/~1refunds/post", - "ref": "specs/support-tools.openapi.yaml#/paths/~1refunds/post", - "start_column": 5, - "start_line": 97, - "type": "openapi" - }, - "title": "stripe.create_refund.amount has no maximum bound" - }, - { - "baseline_status": null, - "blocks_release": false, - "capability_refs": [], - "capability_trace_refs": [], - "check_id": "SHIP-SCHEMA-BROAD-FREE-TEXT", - "fingerprint": "fp_ea4f18e4707f8e1d", - "id": "fp_ea4f18e4707f8e1d", - "policy_evidence_source": null, - "severity": "high", - "source": { - "end_line": null, - "location": null, - "path": "specs/support-tools.openapi.yaml", - "pointer": "/paths/~1tickets~1{ticket_id}/patch", - "ref": "specs/support-tools.openapi.yaml#/paths/~1tickets~1{ticket_id}/patch", - "start_column": 5, - "start_line": 142, - "type": "openapi" - }, - "title": "zendesk.update_ticket accepts broad free-form action input" - }, - { - "baseline_status": null, - "blocks_release": false, - "capability_refs": [], - "capability_trace_refs": [], - "check_id": "SHIP-SCHEMA-BROAD-FREE-TEXT", - "fingerprint": "fp_d58657d9b1c91a84", - "id": "fp_d58657d9b1c91a84", - "policy_evidence_source": null, - "severity": "high", - "source": { - "end_line": null, - "location": null, - "path": ".agents-shipgate/mcp-tools.json", - "pointer": "/tools/1", - "ref": ".agents-shipgate/mcp-tools.json", - "start_column": null, - "start_line": null, - "type": "mcp" - }, - "title": "gmail.send_customer_email accepts broad free-form action input" - } - ] + "missing_controls": [], + "review_items": [] }, { "blocking_findings": [], @@ -522,6 +359,7 @@ "start_line": 91, "type": "manifest" }, + "support": null, "title": "Manifest declares broad permission scopes" }, { @@ -544,6 +382,7 @@ "start_line": 116, "type": "openapi" }, + "support": null, "title": "shopify.cancel_order requires scopes not declared in the manifest" }, { @@ -566,6 +405,7 @@ "start_line": null, "type": "mcp" }, + "support": null, "title": "support.search_kb requires scopes not declared in the manifest" }, { @@ -588,6 +428,7 @@ "start_line": 72, "type": "openapi" }, + "support": null, "title": "refund_status_lookup lacks declared auth scopes" }, { @@ -610,6 +451,7 @@ "start_line": null, "type": "mcp" }, + "support": null, "title": "gmail.send_customer_email requires scopes not declared in the manifest" }, { @@ -632,6 +474,7 @@ "start_line": null, "type": "manifest" }, + "support": null, "title": "Manifest declares unused permission scope zendesk:tickets:read" } ] @@ -669,6 +512,7 @@ "start_line": 97, "type": "openapi" }, + "support": null, "title": "stripe.create_refund lacks a declared approval policy" }, { @@ -691,6 +535,60 @@ "start_line": 97, "type": "openapi" }, + "support": { + "blocking_eligible": true, + "claim_ids": [ + "clm_019b1bf05a33e2269c31", + "clm_c8e45c8de58cf645c026" + ], + "confidence": "high", + "evidence_bases": [ + "protocol_structure", + "reviewed_declaration" + ], + "policy_eligible": true, + "predicates": [ + { + "claim_ids": [ + "clm_019b1bf05a33e2269c31", + "clm_c8e45c8de58cf645c026" + ], + "confidence": "high", + "evidence_bases": [ + "reviewed_declaration" + ], + "expected": [ + "financial_write" + ], + "observed": [ + "financial_write" + ], + "policy_eligible": true, + "predicate": "builtin_control_effect", + "status": "matched", + "why": null + }, + { + "claim_ids": [], + "confidence": "high", + "evidence_bases": [ + "protocol_structure" + ], + "expected": "all required controls present", + "observed": [ + "approval.required", + "safeguards.audit_log", + "safeguards.idempotency" + ], + "policy_eligible": true, + "predicate": "missing_builtin_controls", + "status": "matched", + "why": null + } + ], + "status": "matched", + "support_hash": "sha256:624f2e0f92ae88c456cfad5b76f245bf721c166c9f0cc25afe0a0c06b68f7718" + }, "title": "stripe.create_refund has financial write capability without required controls" } ], @@ -752,6 +650,7 @@ "start_line": 97, "type": "openapi" }, + "support": null, "title": "stripe.create_refund lacks a declared confirmation policy" }, { @@ -785,6 +684,7 @@ "start_line": null, "type": "mcp" }, + "support": null, "title": "gmail.send_customer_email lacks a declared confirmation policy" } ] @@ -820,6 +720,7 @@ "start_line": 97, "type": "openapi" }, + "support": null, "title": "stripe.create_refund lacks idempotency evidence" }, { @@ -842,6 +743,60 @@ "start_line": 97, "type": "openapi" }, + "support": { + "blocking_eligible": true, + "claim_ids": [ + "clm_019b1bf05a33e2269c31", + "clm_c8e45c8de58cf645c026" + ], + "confidence": "high", + "evidence_bases": [ + "protocol_structure", + "reviewed_declaration" + ], + "policy_eligible": true, + "predicates": [ + { + "claim_ids": [ + "clm_019b1bf05a33e2269c31", + "clm_c8e45c8de58cf645c026" + ], + "confidence": "high", + "evidence_bases": [ + "reviewed_declaration" + ], + "expected": [ + "financial_write" + ], + "observed": [ + "financial_write" + ], + "policy_eligible": true, + "predicate": "builtin_control_effect", + "status": "matched", + "why": null + }, + { + "claim_ids": [], + "confidence": "high", + "evidence_bases": [ + "protocol_structure" + ], + "expected": "all required controls present", + "observed": [ + "approval.required", + "safeguards.audit_log", + "safeguards.idempotency" + ], + "policy_eligible": true, + "predicate": "missing_builtin_controls", + "status": "matched", + "why": null + } + ], + "status": "matched", + "support_hash": "sha256:624f2e0f92ae88c456cfad5b76f245bf721c166c9f0cc25afe0a0c06b68f7718" + }, "title": "stripe.create_refund has financial write capability without required controls" } ], @@ -892,6 +847,7 @@ "start_line": 97, "type": "openapi" }, + "support": null, "title": "stripe.create_refund lacks a declared approval policy" }, { @@ -923,6 +879,7 @@ "start_line": 97, "type": "openapi" }, + "support": null, "title": "stripe.create_refund lacks idempotency evidence" }, { @@ -945,6 +902,61 @@ "start_line": null, "type": "mcp" }, + "support": { + "blocking_eligible": true, + "claim_ids": [ + "clm_188aecba394542ac226f", + "clm_7ec6f046c34a18b719f2", + "clm_e6c199e33c2159754789" + ], + "confidence": "high", + "evidence_bases": [ + "protocol_structure", + "reviewed_declaration" + ], + "policy_eligible": true, + "predicates": [ + { + "claim_ids": [ + "clm_188aecba394542ac226f", + "clm_7ec6f046c34a18b719f2", + "clm_e6c199e33c2159754789" + ], + "confidence": "high", + "evidence_bases": [ + "reviewed_declaration" + ], + "expected": [ + "external_communication" + ], + "observed": [ + "external_communication" + ], + "policy_eligible": true, + "predicate": "builtin_control_effect", + "status": "matched", + "why": null + }, + { + "claim_ids": [], + "confidence": "high", + "evidence_bases": [ + "protocol_structure" + ], + "expected": "all required controls present", + "observed": [ + "confirmation.required", + "safeguards.audit_log" + ], + "policy_eligible": true, + "predicate": "missing_builtin_controls", + "status": "matched", + "why": null + } + ], + "status": "matched", + "support_hash": "sha256:0ca4ffebad9baaac2e0095108c61fdf4722cfcc4c6e68932eb47dee4583d7df2" + }, "title": "gmail.send_customer_email has external communication capability without required controls" }, { @@ -967,6 +979,57 @@ "start_line": 97, "type": "openapi" }, + "support": { + "blocking_eligible": true, + "claim_ids": [ + "clm_1fee9ee6ea224541b56d" + ], + "confidence": "high", + "evidence_bases": [ + "protocol_structure", + "reviewed_declaration" + ], + "policy_eligible": true, + "predicates": [ + { + "claim_ids": [ + "clm_1fee9ee6ea224541b56d" + ], + "confidence": "high", + "evidence_bases": [ + "reviewed_declaration" + ], + "expected": [ + "external_communication" + ], + "observed": [ + "external_communication" + ], + "policy_eligible": true, + "predicate": "builtin_control_effect", + "status": "matched", + "why": null + }, + { + "claim_ids": [], + "confidence": "high", + "evidence_bases": [ + "protocol_structure" + ], + "expected": "all required controls present", + "observed": [ + "confirmation.required", + "safeguards.audit_log" + ], + "policy_eligible": true, + "predicate": "missing_builtin_controls", + "status": "matched", + "why": null + } + ], + "status": "matched", + "support_hash": "sha256:335b539322d07aad72eebeff04ace89b40c354da4ee45ba40b52ef885296fe4d" + }, "title": "stripe.create_refund has external communication capability without required controls" }, { @@ -989,10 +1052,64 @@ "start_line": 97, "type": "openapi" }, + "support": { + "blocking_eligible": true, + "claim_ids": [ + "clm_019b1bf05a33e2269c31", + "clm_c8e45c8de58cf645c026" + ], + "confidence": "high", + "evidence_bases": [ + "protocol_structure", + "reviewed_declaration" + ], + "policy_eligible": true, + "predicates": [ + { + "claim_ids": [ + "clm_019b1bf05a33e2269c31", + "clm_c8e45c8de58cf645c026" + ], + "confidence": "high", + "evidence_bases": [ + "reviewed_declaration" + ], + "expected": [ + "financial_write" + ], + "observed": [ + "financial_write" + ], + "policy_eligible": true, + "predicate": "builtin_control_effect", + "status": "matched", + "why": null + }, + { + "claim_ids": [], + "confidence": "high", + "evidence_bases": [ + "protocol_structure" + ], + "expected": "all required controls present", + "observed": [ + "approval.required", + "safeguards.audit_log", + "safeguards.idempotency" + ], + "policy_eligible": true, + "predicate": "missing_builtin_controls", + "status": "matched", + "why": null + } + ], + "status": "matched", + "support_hash": "sha256:624f2e0f92ae88c456cfad5b76f245bf721c166c9f0cc25afe0a0c06b68f7718" + }, "title": "stripe.create_refund has financial write capability without required controls" } ], - "confidence": "mixed", + "confidence": "high", "domain": "Side effects", "evidence_present": "partial", "evidence_source": [ @@ -1001,8 +1118,6 @@ "findings[]" ], "missing_controls": [ - "SHIP-SCHEMA-BROAD-FREE-TEXT on zendesk.update_ticket: zendesk.update_ticket accepts broad free-form action input", - "SHIP-SCHEMA-BROAD-FREE-TEXT on gmail.send_customer_email: gmail.send_customer_email accepts broad free-form action input", "SHIP-POLICY-APPROVAL-MISSING on stripe.create_refund: stripe.create_refund lacks a declared approval policy", "SHIP-POLICY-CONFIRMATION-MISSING on stripe.create_refund: stripe.create_refund lacks a declared confirmation policy", "SHIP-POLICY-CONFIRMATION-MISSING on gmail.send_customer_email: gmail.send_customer_email lacks a declared confirmation policy", @@ -1012,50 +1127,6 @@ "SHIP-ACTION-FINANCIAL-WRITE-CONTROL-MISSING on stripe.create_refund: stripe.create_refund has financial write capability without required controls" ], "review_items": [ - { - "baseline_status": null, - "blocks_release": false, - "capability_refs": [], - "capability_trace_refs": [], - "check_id": "SHIP-SCHEMA-BROAD-FREE-TEXT", - "fingerprint": "fp_ea4f18e4707f8e1d", - "id": "fp_ea4f18e4707f8e1d", - "policy_evidence_source": null, - "severity": "high", - "source": { - "end_line": null, - "location": null, - "path": "specs/support-tools.openapi.yaml", - "pointer": "/paths/~1tickets~1{ticket_id}/patch", - "ref": "specs/support-tools.openapi.yaml#/paths/~1tickets~1{ticket_id}/patch", - "start_column": 5, - "start_line": 142, - "type": "openapi" - }, - "title": "zendesk.update_ticket accepts broad free-form action input" - }, - { - "baseline_status": null, - "blocks_release": false, - "capability_refs": [], - "capability_trace_refs": [], - "check_id": "SHIP-SCHEMA-BROAD-FREE-TEXT", - "fingerprint": "fp_d58657d9b1c91a84", - "id": "fp_d58657d9b1c91a84", - "policy_evidence_source": null, - "severity": "high", - "source": { - "end_line": null, - "location": null, - "path": ".agents-shipgate/mcp-tools.json", - "pointer": "/tools/1", - "ref": ".agents-shipgate/mcp-tools.json", - "start_column": null, - "start_line": null, - "type": "mcp" - }, - "title": "gmail.send_customer_email accepts broad free-form action input" - }, { "baseline_status": null, "blocks_release": false, @@ -1087,6 +1158,7 @@ "start_line": 97, "type": "openapi" }, + "support": null, "title": "stripe.create_refund lacks a declared confirmation policy" }, { @@ -1120,6 +1192,7 @@ "start_line": null, "type": "mcp" }, + "support": null, "title": "gmail.send_customer_email lacks a declared confirmation policy" } ] @@ -1146,81 +1219,14 @@ "blocking_findings": [], "confidence": "medium", "domain": "Prompt/scope alignment", - "evidence_present": "partial", + "evidence_present": "covered", "evidence_source": [ "declared_intentions", "misalignments", - "capability_facts", - "findings[]" - ], - "missing_controls": [ - "SHIP-SCOPE-PROHIBITED-TOOL-PRESENT on stripe.create_refund: stripe.create_refund appears to overlap with a prohibited action", - "SHIP-SCOPE-PROHIBITED-TOOL-PRESENT on gmail.send_customer_email: gmail.send_customer_email appears to overlap with a prohibited action" + "capability_facts" ], - "review_items": [ - { - "baseline_status": null, - "blocks_release": false, - "capability_refs": [], - "capability_trace_refs": [], - "check_id": "SHIP-SCOPE-PROHIBITED-TOOL-PRESENT", - "fingerprint": "fp_6f8e1ebab65f3607", - "id": "fp_6f8e1ebab65f3607", - "policy_evidence_source": { - "end_line": null, - "location": null, - "path": "shipgate.yaml", - "pointer": "/agent/prohibited_actions/0", - "ref": "shipgate.yaml#/agent/prohibited_actions/0", - "start_column": null, - "start_line": 22, - "type": "manifest" - }, - "severity": "high", - "source": { - "end_line": null, - "location": null, - "path": "specs/support-tools.openapi.yaml", - "pointer": "/paths/~1refunds/post", - "ref": "specs/support-tools.openapi.yaml#/paths/~1refunds/post", - "start_column": 5, - "start_line": 97, - "type": "openapi" - }, - "title": "stripe.create_refund appears to overlap with a prohibited action" - }, - { - "baseline_status": null, - "blocks_release": false, - "capability_refs": [], - "capability_trace_refs": [], - "check_id": "SHIP-SCOPE-PROHIBITED-TOOL-PRESENT", - "fingerprint": "fp_5faaaee860bcec8b", - "id": "fp_5faaaee860bcec8b", - "policy_evidence_source": { - "end_line": null, - "location": null, - "path": "shipgate.yaml", - "pointer": "/agent/prohibited_actions/2", - "ref": "shipgate.yaml#/agent/prohibited_actions/2", - "start_column": null, - "start_line": 24, - "type": "manifest" - }, - "severity": "high", - "source": { - "end_line": null, - "location": null, - "path": ".agents-shipgate/mcp-tools.json", - "pointer": "/tools/1", - "ref": ".agents-shipgate/mcp-tools.json", - "start_column": null, - "start_line": null, - "type": "mcp" - }, - "title": "gmail.send_customer_email appears to overlap with a prohibited action" - } - ] + "missing_controls": [], + "review_items": [] }, { "blocking_findings": [], @@ -1262,6 +1268,61 @@ "start_line": null, "type": "mcp" }, + "support": { + "blocking_eligible": true, + "claim_ids": [ + "clm_188aecba394542ac226f", + "clm_7ec6f046c34a18b719f2", + "clm_e6c199e33c2159754789" + ], + "confidence": "high", + "evidence_bases": [ + "protocol_structure", + "reviewed_declaration" + ], + "policy_eligible": true, + "predicates": [ + { + "claim_ids": [ + "clm_188aecba394542ac226f", + "clm_7ec6f046c34a18b719f2", + "clm_e6c199e33c2159754789" + ], + "confidence": "high", + "evidence_bases": [ + "reviewed_declaration" + ], + "expected": [ + "external_communication" + ], + "observed": [ + "external_communication" + ], + "policy_eligible": true, + "predicate": "builtin_control_effect", + "status": "matched", + "why": null + }, + { + "claim_ids": [], + "confidence": "high", + "evidence_bases": [ + "protocol_structure" + ], + "expected": "all required controls present", + "observed": [ + "confirmation.required", + "safeguards.audit_log" + ], + "policy_eligible": true, + "predicate": "missing_builtin_controls", + "status": "matched", + "why": null + } + ], + "status": "matched", + "support_hash": "sha256:0ca4ffebad9baaac2e0095108c61fdf4722cfcc4c6e68932eb47dee4583d7df2" + }, "title": "gmail.send_customer_email has external communication capability without required controls" }, { @@ -1284,6 +1345,57 @@ "start_line": 97, "type": "openapi" }, + "support": { + "blocking_eligible": true, + "claim_ids": [ + "clm_1fee9ee6ea224541b56d" + ], + "confidence": "high", + "evidence_bases": [ + "protocol_structure", + "reviewed_declaration" + ], + "policy_eligible": true, + "predicates": [ + { + "claim_ids": [ + "clm_1fee9ee6ea224541b56d" + ], + "confidence": "high", + "evidence_bases": [ + "reviewed_declaration" + ], + "expected": [ + "external_communication" + ], + "observed": [ + "external_communication" + ], + "policy_eligible": true, + "predicate": "builtin_control_effect", + "status": "matched", + "why": null + }, + { + "claim_ids": [], + "confidence": "high", + "evidence_bases": [ + "protocol_structure" + ], + "expected": "all required controls present", + "observed": [ + "confirmation.required", + "safeguards.audit_log" + ], + "policy_eligible": true, + "predicate": "missing_builtin_controls", + "status": "matched", + "why": null + } + ], + "status": "matched", + "support_hash": "sha256:335b539322d07aad72eebeff04ace89b40c354da4ee45ba40b52ef885296fe4d" + }, "title": "stripe.create_refund has external communication capability without required controls" }, { @@ -1306,6 +1418,60 @@ "start_line": 97, "type": "openapi" }, + "support": { + "blocking_eligible": true, + "claim_ids": [ + "clm_019b1bf05a33e2269c31", + "clm_c8e45c8de58cf645c026" + ], + "confidence": "high", + "evidence_bases": [ + "protocol_structure", + "reviewed_declaration" + ], + "policy_eligible": true, + "predicates": [ + { + "claim_ids": [ + "clm_019b1bf05a33e2269c31", + "clm_c8e45c8de58cf645c026" + ], + "confidence": "high", + "evidence_bases": [ + "reviewed_declaration" + ], + "expected": [ + "financial_write" + ], + "observed": [ + "financial_write" + ], + "policy_eligible": true, + "predicate": "builtin_control_effect", + "status": "matched", + "why": null + }, + { + "claim_ids": [], + "confidence": "high", + "evidence_bases": [ + "protocol_structure" + ], + "expected": "all required controls present", + "observed": [ + "approval.required", + "safeguards.audit_log", + "safeguards.idempotency" + ], + "policy_eligible": true, + "predicate": "missing_builtin_controls", + "status": "matched", + "why": null + } + ], + "status": "matched", + "support_hash": "sha256:624f2e0f92ae88c456cfad5b76f245bf721c166c9f0cc25afe0a0c06b68f7718" + }, "title": "stripe.create_refund has financial write capability without required controls" } ], @@ -1328,7 +1494,7 @@ }, "generated_at": "2026-01-01T00:00:00+00:00", "high_risk_surface": { - "high_risk_count": 3, + "high_risk_count": 5, "status": "partial", "tools": [ { @@ -1343,6 +1509,17 @@ "source_type": "mcp", "tool_id": "tool_v2_c69ebf76910efa80a2024a15d836f20366effdbd12f7a69b660ce4f4e5870ed9" }, + { + "has_approval_policy": false, + "has_idempotency_policy": false, + "name": "send_email_preview", + "provider": "openai_sdk_static", + "risk_tags": [ + "read_only" + ], + "source_type": "mcp", + "tool_id": "tool_v2_2c9ee6ae2b9f0259ba2dad40a30fb18a8bdc3a732ffc207936fdd49c80e1bc9e" + }, { "has_approval_policy": true, "has_idempotency_policy": true, @@ -1367,6 +1544,17 @@ ], "source_type": "openapi", "tool_id": "tool_v2_8b559376c3236346b33a64fb9d460d342e6956579a68a0d5a331b2d2adf68ac8" + }, + { + "has_approval_policy": false, + "has_idempotency_policy": false, + "name": "support.search_kb", + "provider": "support_mcp_tools", + "risk_tags": [ + "read_only" + ], + "source_type": "mcp", + "tool_id": "tool_v2_445a251aa6adc1c69f7b318b68f7ad47f01b629d3c24d723331a661735cd353a" } ], "total_tools": 7 @@ -1428,6 +1616,7 @@ "start_line": 97, "type": "openapi" }, + "support": null, "title": "stripe.create_refund lacks idempotency evidence" } ], @@ -1461,8 +1650,7 @@ }, "not_proven": { "additional_residuals": [ - "Memory isolation is not modeled by the v0.1 manifest schema; no static evidence is available.", - "5 active finding(s) came from heuristic provenance (keyword_heuristic=5, regex_heuristic=0); review the finding evidence before acting." + "Memory isolation is not modeled by the v0.1 manifest schema; no static evidence is available." ], "headline": "Agents Shipgate is an advisory tool: the deterministic merge gate for AI-generated agent capability changes, run as a local-first, static Tool-Use Readiness review. The packet below is derived from a scan; it does not, by itself, prove the following properties:", "low_confidence_tools": [], @@ -1489,7 +1677,7 @@ } ] }, - "packet_schema_version": "0.10", + "packet_schema_version": "0.11", "project": { "name": "support-refund-agent", "owner": "support-platform", @@ -1535,6 +1723,7 @@ "start_line": 97, "type": "openapi" }, + "support": null, "title": "stripe.create_refund lacks a declared approval policy" }, { @@ -1566,6 +1755,7 @@ "start_line": 97, "type": "openapi" }, + "support": null, "title": "stripe.create_refund lacks idempotency evidence" }, { @@ -1588,6 +1778,61 @@ "start_line": null, "type": "mcp" }, + "support": { + "blocking_eligible": true, + "claim_ids": [ + "clm_188aecba394542ac226f", + "clm_7ec6f046c34a18b719f2", + "clm_e6c199e33c2159754789" + ], + "confidence": "high", + "evidence_bases": [ + "protocol_structure", + "reviewed_declaration" + ], + "policy_eligible": true, + "predicates": [ + { + "claim_ids": [ + "clm_188aecba394542ac226f", + "clm_7ec6f046c34a18b719f2", + "clm_e6c199e33c2159754789" + ], + "confidence": "high", + "evidence_bases": [ + "reviewed_declaration" + ], + "expected": [ + "external_communication" + ], + "observed": [ + "external_communication" + ], + "policy_eligible": true, + "predicate": "builtin_control_effect", + "status": "matched", + "why": null + }, + { + "claim_ids": [], + "confidence": "high", + "evidence_bases": [ + "protocol_structure" + ], + "expected": "all required controls present", + "observed": [ + "confirmation.required", + "safeguards.audit_log" + ], + "policy_eligible": true, + "predicate": "missing_builtin_controls", + "status": "matched", + "why": null + } + ], + "status": "matched", + "support_hash": "sha256:0ca4ffebad9baaac2e0095108c61fdf4722cfcc4c6e68932eb47dee4583d7df2" + }, "title": "gmail.send_customer_email has external communication capability without required controls" }, { @@ -1610,6 +1855,57 @@ "start_line": 97, "type": "openapi" }, + "support": { + "blocking_eligible": true, + "claim_ids": [ + "clm_1fee9ee6ea224541b56d" + ], + "confidence": "high", + "evidence_bases": [ + "protocol_structure", + "reviewed_declaration" + ], + "policy_eligible": true, + "predicates": [ + { + "claim_ids": [ + "clm_1fee9ee6ea224541b56d" + ], + "confidence": "high", + "evidence_bases": [ + "reviewed_declaration" + ], + "expected": [ + "external_communication" + ], + "observed": [ + "external_communication" + ], + "policy_eligible": true, + "predicate": "builtin_control_effect", + "status": "matched", + "why": null + }, + { + "claim_ids": [], + "confidence": "high", + "evidence_bases": [ + "protocol_structure" + ], + "expected": "all required controls present", + "observed": [ + "confirmation.required", + "safeguards.audit_log" + ], + "policy_eligible": true, + "predicate": "missing_builtin_controls", + "status": "matched", + "why": null + } + ], + "status": "matched", + "support_hash": "sha256:335b539322d07aad72eebeff04ace89b40c354da4ee45ba40b52ef885296fe4d" + }, "title": "stripe.create_refund has external communication capability without required controls" }, { @@ -1632,6 +1928,60 @@ "start_line": 97, "type": "openapi" }, + "support": { + "blocking_eligible": true, + "claim_ids": [ + "clm_019b1bf05a33e2269c31", + "clm_c8e45c8de58cf645c026" + ], + "confidence": "high", + "evidence_bases": [ + "protocol_structure", + "reviewed_declaration" + ], + "policy_eligible": true, + "predicates": [ + { + "claim_ids": [ + "clm_019b1bf05a33e2269c31", + "clm_c8e45c8de58cf645c026" + ], + "confidence": "high", + "evidence_bases": [ + "reviewed_declaration" + ], + "expected": [ + "financial_write" + ], + "observed": [ + "financial_write" + ], + "policy_eligible": true, + "predicate": "builtin_control_effect", + "status": "matched", + "why": null + }, + { + "claim_ids": [], + "confidence": "high", + "evidence_bases": [ + "protocol_structure" + ], + "expected": "all required controls present", + "observed": [ + "approval.required", + "safeguards.audit_log", + "safeguards.idempotency" + ], + "policy_eligible": true, + "predicate": "missing_builtin_controls", + "status": "matched", + "why": null + } + ], + "status": "matched", + "support_hash": "sha256:624f2e0f92ae88c456cfad5b76f245bf721c166c9f0cc25afe0a0c06b68f7718" + }, "title": "stripe.create_refund has financial write capability without required controls" } ], @@ -1841,6 +2191,222 @@ "subject": "zendesk.update_ticket [support_openapi]", "why": "Closed-world declaration for 'root' does not match the complete structural tool set." }, + { + "kind": "mixed_policy_evidence", + "next_action": { + "accepted_values": [ + "reviewed_declaration", + "protocol_structure", + "typed_provider_fact", + "structural_scope" + ], + "auto_apply": false, + "command": "agents-shipgate verify --workspace . --config shipgate.yaml --ci-mode advisory --format json", + "declaration_template": null, + "expects": "Provide reviewed or structural evidence for every indeterminate predicate.", + "kind": "review_policy_evidence", + "path": "shipgate.yaml#action_surface.actions[tool='support.search_kb'].effect", + "requires_human_review": true, + "suggested_patch_kind": "manual", + "why": "Heuristic or unknown evidence cannot create a blocking policy finding." + }, + "source_ref": ".agents-shipgate/mcp-tools.json", + "source_type": null, + "subject": "support.search_kb [tool_v2_445a251aa6adc1c69f7b318b68f7ad47f01b629d3c24d723331a661735cd353a]", + "why": "builtin-effect-control-applicability: Policy applicability mixes authoritative and heuristic evidence." + }, + { + "kind": "mixed_policy_evidence", + "next_action": { + "accepted_values": [ + "reviewed_declaration", + "protocol_structure", + "typed_provider_fact", + "structural_scope" + ], + "auto_apply": false, + "command": "agents-shipgate verify --workspace . --config shipgate.yaml --ci-mode advisory --format json", + "declaration_template": null, + "expects": "Provide reviewed or structural evidence for every indeterminate predicate.", + "kind": "review_policy_evidence", + "path": "shipgate.yaml#action_surface.actions[tool='send_email_preview'].effect", + "requires_human_review": true, + "suggested_patch_kind": "manual", + "why": "Heuristic or unknown evidence cannot create a blocking policy finding." + }, + "source_ref": "inventories/sdk-tools.json", + "source_type": null, + "subject": "send_email_preview [tool_v2_2c9ee6ae2b9f0259ba2dad40a30fb18a8bdc3a732ffc207936fdd49c80e1bc9e]", + "why": "builtin-effect-control-applicability: Policy applicability mixes authoritative and heuristic evidence." + }, + { + "kind": "inferred_policy_applicability", + "next_action": { + "accepted_values": [ + "reviewed_declaration", + "protocol_structure", + "typed_provider_fact", + "structural_scope" + ], + "auto_apply": false, + "command": "agents-shipgate verify --workspace . --config shipgate.yaml --ci-mode advisory --format json", + "declaration_template": null, + "expects": "Provide reviewed or structural evidence for every indeterminate predicate.", + "kind": "provide_policy_evidence", + "path": "/tools/0", + "requires_human_review": true, + "suggested_patch_kind": "manual", + "why": "Heuristic or unknown evidence cannot create a blocking policy finding." + }, + "source_ref": "inventories/sdk-tools.json", + "source_type": null, + "subject": "tool_v2_2c9ee6ae2b9f0259ba2dad40a30fb18a8bdc3a732ffc207936fdd49c80e1bc9e", + "why": "SHIP-SCHEMA-BROAD-FREE-TEXT: Policy applicability is supported only by heuristic evidence." + }, + { + "kind": "inferred_policy_applicability", + "next_action": { + "accepted_values": [ + "reviewed_declaration", + "protocol_structure", + "typed_provider_fact", + "structural_scope" + ], + "auto_apply": false, + "command": "agents-shipgate verify --workspace . --config shipgate.yaml --ci-mode advisory --format json", + "declaration_template": null, + "expects": "Provide reviewed or structural evidence for every indeterminate predicate.", + "kind": "provide_policy_evidence", + "path": "/paths/~1refunds/post", + "requires_human_review": true, + "suggested_patch_kind": "manual", + "why": "Heuristic or unknown evidence cannot create a blocking policy finding." + }, + "source_ref": "specs/support-tools.openapi.yaml", + "source_type": null, + "subject": "tool_v2_8b559376c3236346b33a64fb9d460d342e6956579a68a0d5a331b2d2adf68ac8", + "why": "SHIP-SCHEMA-MISSING-BOUNDS: Policy applicability is supported only by heuristic evidence." + }, + { + "kind": "inferred_policy_applicability", + "next_action": { + "accepted_values": [ + "reviewed_declaration", + "protocol_structure", + "typed_provider_fact", + "structural_scope" + ], + "auto_apply": false, + "command": "agents-shipgate verify --workspace . --config shipgate.yaml --ci-mode advisory --format json", + "declaration_template": null, + "expects": "Provide reviewed or structural evidence for every indeterminate predicate.", + "kind": "provide_policy_evidence", + "path": "/paths/~1tickets~1{ticket_id}/patch", + "requires_human_review": true, + "suggested_patch_kind": "manual", + "why": "Heuristic or unknown evidence cannot create a blocking policy finding." + }, + "source_ref": "specs/support-tools.openapi.yaml", + "source_type": null, + "subject": "tool_v2_aa488250dc1213a6a8bb76a7e695da294f93052f3181d42817b6cd2cf83cfa33", + "why": "SHIP-SCHEMA-BROAD-FREE-TEXT: Policy applicability is supported only by heuristic evidence." + }, + { + "kind": "inferred_policy_applicability", + "next_action": { + "accepted_values": [ + "reviewed_declaration", + "protocol_structure", + "typed_provider_fact", + "structural_scope" + ], + "auto_apply": false, + "command": "agents-shipgate verify --workspace . --config shipgate.yaml --ci-mode advisory --format json", + "declaration_template": null, + "expects": "Provide reviewed or structural evidence for every indeterminate predicate.", + "kind": "provide_policy_evidence", + "path": "/tools/1", + "requires_human_review": true, + "suggested_patch_kind": "manual", + "why": "Heuristic or unknown evidence cannot create a blocking policy finding." + }, + "source_ref": ".agents-shipgate/mcp-tools.json", + "source_type": null, + "subject": "tool_v2_c69ebf76910efa80a2024a15d836f20366effdbd12f7a69b660ce4f4e5870ed9", + "why": "SHIP-SCHEMA-BROAD-FREE-TEXT: Policy applicability is supported only by heuristic evidence." + }, + { + "kind": "inferred_policy_applicability", + "next_action": { + "accepted_values": [ + "reviewed_declaration", + "protocol_structure", + "typed_provider_fact", + "structural_scope" + ], + "auto_apply": false, + "command": "agents-shipgate verify --workspace . --config shipgate.yaml --ci-mode advisory --format json", + "declaration_template": null, + "expects": "Provide reviewed or structural evidence for every indeterminate predicate.", + "kind": "provide_policy_evidence", + "path": "/agent/prohibited_actions/2", + "requires_human_review": true, + "suggested_patch_kind": "manual", + "why": "Heuristic or unknown evidence cannot create a blocking policy finding." + }, + "source_ref": "shipgate.yaml", + "source_type": null, + "subject": "tool_v2_2c9ee6ae2b9f0259ba2dad40a30fb18a8bdc3a732ffc207936fdd49c80e1bc9e", + "why": "SHIP-SCOPE-PROHIBITED-TOOL-PRESENT: Policy applicability is supported only by heuristic evidence." + }, + { + "kind": "inferred_policy_applicability", + "next_action": { + "accepted_values": [ + "reviewed_declaration", + "protocol_structure", + "typed_provider_fact", + "structural_scope" + ], + "auto_apply": false, + "command": "agents-shipgate verify --workspace . --config shipgate.yaml --ci-mode advisory --format json", + "declaration_template": null, + "expects": "Provide reviewed or structural evidence for every indeterminate predicate.", + "kind": "provide_policy_evidence", + "path": "/agent/prohibited_actions/0", + "requires_human_review": true, + "suggested_patch_kind": "manual", + "why": "Heuristic or unknown evidence cannot create a blocking policy finding." + }, + "source_ref": "shipgate.yaml", + "source_type": null, + "subject": "tool_v2_8b559376c3236346b33a64fb9d460d342e6956579a68a0d5a331b2d2adf68ac8", + "why": "SHIP-SCOPE-PROHIBITED-TOOL-PRESENT: Policy applicability is supported only by heuristic evidence." + }, + { + "kind": "inferred_policy_applicability", + "next_action": { + "accepted_values": [ + "reviewed_declaration", + "protocol_structure", + "typed_provider_fact", + "structural_scope" + ], + "auto_apply": false, + "command": "agents-shipgate verify --workspace . --config shipgate.yaml --ci-mode advisory --format json", + "declaration_template": null, + "expects": "Provide reviewed or structural evidence for every indeterminate predicate.", + "kind": "provide_policy_evidence", + "path": "/agent/prohibited_actions/2", + "requires_human_review": true, + "suggested_patch_kind": "manual", + "why": "Heuristic or unknown evidence cannot create a blocking policy finding." + }, + "source_ref": "shipgate.yaml", + "source_type": null, + "subject": "tool_v2_c69ebf76910efa80a2024a15d836f20366effdbd12f7a69b660ce4f4e5870ed9", + "why": "SHIP-SCOPE-PROHIBITED-TOOL-PRESENT: Policy applicability is supported only by heuristic evidence." + }, { "kind": "source_warning", "next_action": { @@ -1873,6 +2439,7 @@ }, "level": "static", "low_confidence_tool_count": 0, + "policy_gap_count": 9, "semantic_coverage": { "gap_count": 7, "pass_eligible_actions": 0, @@ -1894,72 +2461,6 @@ }, "reason": "5 active findings block release.", "review_items": [ - { - "baseline_status": null, - "blocks_release": false, - "capability_refs": [], - "capability_trace_refs": [], - "check_id": "SHIP-SCHEMA-MISSING-BOUNDS", - "fingerprint": "fp_60fdf92126ba8844", - "id": "fp_60fdf92126ba8844", - "policy_evidence_source": null, - "severity": "high", - "source": { - "end_line": null, - "location": null, - "path": "specs/support-tools.openapi.yaml", - "pointer": "/paths/~1refunds/post", - "ref": "specs/support-tools.openapi.yaml#/paths/~1refunds/post", - "start_column": 5, - "start_line": 97, - "type": "openapi" - }, - "title": "stripe.create_refund.amount has no maximum bound" - }, - { - "baseline_status": null, - "blocks_release": false, - "capability_refs": [], - "capability_trace_refs": [], - "check_id": "SHIP-SCHEMA-BROAD-FREE-TEXT", - "fingerprint": "fp_ea4f18e4707f8e1d", - "id": "fp_ea4f18e4707f8e1d", - "policy_evidence_source": null, - "severity": "high", - "source": { - "end_line": null, - "location": null, - "path": "specs/support-tools.openapi.yaml", - "pointer": "/paths/~1tickets~1{ticket_id}/patch", - "ref": "specs/support-tools.openapi.yaml#/paths/~1tickets~1{ticket_id}/patch", - "start_column": 5, - "start_line": 142, - "type": "openapi" - }, - "title": "zendesk.update_ticket accepts broad free-form action input" - }, - { - "baseline_status": null, - "blocks_release": false, - "capability_refs": [], - "capability_trace_refs": [], - "check_id": "SHIP-SCHEMA-BROAD-FREE-TEXT", - "fingerprint": "fp_d58657d9b1c91a84", - "id": "fp_d58657d9b1c91a84", - "policy_evidence_source": null, - "severity": "high", - "source": { - "end_line": null, - "location": null, - "path": ".agents-shipgate/mcp-tools.json", - "pointer": "/tools/1", - "ref": ".agents-shipgate/mcp-tools.json", - "start_column": null, - "start_line": null, - "type": "mcp" - }, - "title": "gmail.send_customer_email accepts broad free-form action input" - }, { "baseline_status": null, "blocks_release": false, @@ -1980,6 +2481,7 @@ "start_line": 91, "type": "manifest" }, + "support": null, "title": "Manifest declares broad permission scopes" }, { @@ -2002,6 +2504,7 @@ "start_line": 116, "type": "openapi" }, + "support": null, "title": "shopify.cancel_order requires scopes not declared in the manifest" }, { @@ -2024,6 +2527,7 @@ "start_line": null, "type": "mcp" }, + "support": null, "title": "support.search_kb requires scopes not declared in the manifest" }, { @@ -2046,6 +2550,7 @@ "start_line": 72, "type": "openapi" }, + "support": null, "title": "refund_status_lookup lacks declared auth scopes" }, { @@ -2068,70 +2573,9 @@ "start_line": null, "type": "mcp" }, + "support": null, "title": "gmail.send_customer_email requires scopes not declared in the manifest" }, - { - "baseline_status": null, - "blocks_release": false, - "capability_refs": [], - "capability_trace_refs": [], - "check_id": "SHIP-SCOPE-PROHIBITED-TOOL-PRESENT", - "fingerprint": "fp_6f8e1ebab65f3607", - "id": "fp_6f8e1ebab65f3607", - "policy_evidence_source": { - "end_line": null, - "location": null, - "path": "shipgate.yaml", - "pointer": "/agent/prohibited_actions/0", - "ref": "shipgate.yaml#/agent/prohibited_actions/0", - "start_column": null, - "start_line": 22, - "type": "manifest" - }, - "severity": "high", - "source": { - "end_line": null, - "location": null, - "path": "specs/support-tools.openapi.yaml", - "pointer": "/paths/~1refunds/post", - "ref": "specs/support-tools.openapi.yaml#/paths/~1refunds/post", - "start_column": 5, - "start_line": 97, - "type": "openapi" - }, - "title": "stripe.create_refund appears to overlap with a prohibited action" - }, - { - "baseline_status": null, - "blocks_release": false, - "capability_refs": [], - "capability_trace_refs": [], - "check_id": "SHIP-SCOPE-PROHIBITED-TOOL-PRESENT", - "fingerprint": "fp_5faaaee860bcec8b", - "id": "fp_5faaaee860bcec8b", - "policy_evidence_source": { - "end_line": null, - "location": null, - "path": "shipgate.yaml", - "pointer": "/agent/prohibited_actions/2", - "ref": "shipgate.yaml#/agent/prohibited_actions/2", - "start_column": null, - "start_line": 24, - "type": "manifest" - }, - "severity": "high", - "source": { - "end_line": null, - "location": null, - "path": ".agents-shipgate/mcp-tools.json", - "pointer": "/tools/1", - "ref": ".agents-shipgate/mcp-tools.json", - "start_column": null, - "start_line": null, - "type": "mcp" - }, - "title": "gmail.send_customer_email appears to overlap with a prohibited action" - }, { "baseline_status": null, "blocks_release": false, @@ -2163,6 +2607,7 @@ "start_line": 97, "type": "openapi" }, + "support": null, "title": "stripe.create_refund lacks a declared confirmation policy" }, { @@ -2196,6 +2641,7 @@ "start_line": null, "type": "mcp" }, + "support": null, "title": "gmail.send_customer_email lacks a declared confirmation policy" }, { @@ -2218,6 +2664,7 @@ "start_line": 116, "type": "openapi" }, + "support": null, "title": "shopify.cancel_order is high-risk but has no owner" }, { @@ -2240,6 +2687,7 @@ "start_line": null, "type": "manifest" }, + "support": null, "title": "Manifest declares unused permission scope zendesk:tickets:read" } ], @@ -2248,7 +2696,7 @@ "static_verdict_disclaimer": "This verdict covers deterministic static evidence only. Agents Shipgate did not execute the agent or prove runtime behavior, tool routing, credential enforcement, or safety.", "verdict": "BLOCKED" }, - "run_id": "agents_shipgate_5ad1a92a984aacac", + "run_id": "agents_shipgate_e25de5ef59b7d500", "scope_coverage": { "declared_scopes": [ "zendesk:tickets:read", @@ -2276,6 +2724,7 @@ "start_line": 116, "type": "openapi" }, + "support": null, "title": "shopify.cancel_order requires scopes not declared in the manifest" }, { @@ -2298,6 +2747,7 @@ "start_line": null, "type": "mcp" }, + "support": null, "title": "support.search_kb requires scopes not declared in the manifest" }, { @@ -2320,6 +2770,7 @@ "start_line": null, "type": "mcp" }, + "support": null, "title": "gmail.send_customer_email requires scopes not declared in the manifest" }, { @@ -2342,6 +2793,7 @@ "start_line": null, "type": "manifest" }, + "support": null, "title": "Manifest declares unused permission scope zendesk:tickets:read" } ], diff --git a/samples/support_refund_agent/expected/packet.md b/samples/support_refund_agent/expected/packet.md index d6ffdd19..137664d1 100644 --- a/samples/support_refund_agent/expected/packet.md +++ b/samples/support_refund_agent/expected/packet.md @@ -3,9 +3,9 @@ - Project: support-refund-agent - Agent: refund-assistant - Environment: production\_like -- Run id: agents\_shipgate\_5ad1a92a984aacac +- Run id: agents\_shipgate\_e25de5ef59b7d500 - Generated at: 2026-01-01T00:00:00\+00:00 -- Packet schema: 0\.10 +- Packet schema: 0\.11 This packet is a reviewer-shaped synthesis of a static Agents Shipgate scan. See §10 for what the packet does *not* prove. @@ -14,7 +14,7 @@ This packet is a reviewer-shaped synthesis of a static Agents Shipgate scan. See - Decision: `blocked` - Reason: 5 active findings block release. - Blockers: 5 -- Review items: 14 +- Review items: 9 ### CI gate behavior (informational) @@ -38,16 +38,11 @@ This packet is a reviewer-shaped synthesis of a static Agents Shipgate scan. See ### Review items -- `SHIP-SCHEMA-MISSING-BOUNDS` (high): stripe.create\_refund.amount has no maximum bound — `specs/support-tools.openapi.yaml:97` -- `SHIP-SCHEMA-BROAD-FREE-TEXT` (high): zendesk.update\_ticket accepts broad free-form action input — `specs/support-tools.openapi.yaml:142` -- `SHIP-SCHEMA-BROAD-FREE-TEXT` (high): gmail.send\_customer\_email accepts broad free-form action input — `.agents-shipgate/mcp-tools.json\#/tools/1` - `SHIP-AUTH-MANIFEST-BROAD-SCOPE` (high): Manifest declares broad permission scopes — `shipgate.yaml:91` - `SHIP-AUTH-SCOPE-COVERAGE-MISSING` (high): shopify.cancel\_order requires scopes not declared in the manifest — `specs/support-tools.openapi.yaml:116` - `SHIP-AUTH-SCOPE-COVERAGE-MISSING` (high): support.search\_kb requires scopes not declared in the manifest — `.agents-shipgate/mcp-tools.json\#/tools/0` - `SHIP-AUTH-MISSING-SCOPE` (high): refund\_status\_lookup lacks declared auth scopes — `specs/support-tools.openapi.yaml:72` - `SHIP-AUTH-SCOPE-COVERAGE-MISSING` (high): gmail.send\_customer\_email requires scopes not declared in the manifest — `.agents-shipgate/mcp-tools.json\#/tools/1` -- `SHIP-SCOPE-PROHIBITED-TOOL-PRESENT` (high): stripe.create\_refund appears to overlap with a prohibited action — `specs/support-tools.openapi.yaml:97` — `shipgate.yaml:22` -- `SHIP-SCOPE-PROHIBITED-TOOL-PRESENT` (high): gmail.send\_customer\_email appears to overlap with a prohibited action — `.agents-shipgate/mcp-tools.json\#/tools/1` — `shipgate.yaml:24` - `SHIP-POLICY-CONFIRMATION-MISSING` (high): stripe.create\_refund lacks a declared confirmation policy — `specs/support-tools.openapi.yaml:97` — `shipgate.yaml:85` - `SHIP-POLICY-CONFIRMATION-MISSING` (high): gmail.send\_customer\_email lacks a declared confirmation policy — `.agents-shipgate/mcp-tools.json\#/tools/1` — `shipgate.yaml:85` - `SHIP-MANIFEST-HIGH-RISK-OWNER-MISSING` (high): shopify.cancel\_order is high-risk but has no owner — `specs/support-tools.openapi.yaml:116` @@ -60,20 +55,20 @@ This packet is a reviewer-shaped synthesis of a static Agents Shipgate scan. See | Domain | Evidence present | Evidence source | Confidence | Missing controls | Blocking findings | Review items | |---|---|---|---|---|---|---| | Inventory | covered | tool\_inventory; tool\_surface; \+1 more | medium | — | — | — | -| Schema | partial | tool\_surface\_facts.tools\[\].hashes; findings\[\] | mixed | SHIP-SCHEMA-MISSING-BOUNDS on stripe.create\_refund: stripe.create\_refund.amount has no maximum bound; SHIP-SCHEMA-BROAD-FREE-TEXT on zendesk.update\_ticket: zendesk.update\_ticket accepts broad free-form action input; \+1 more | — | SHIP-SCHEMA-MISSING-BOUNDS \(high\); SHIP-SCHEMA-BROAD-FREE-TEXT \(high\); \+1 more | +| Schema | covered | tool\_surface\_facts.tools\[\].hashes | medium | — | — | — | | Auth | partial | tool\_surface\_facts.scopes; tool\_inventory\[\].auth\_scopes; \+1 more | mixed | SHIP-AUTH-MANIFEST-BROAD-SCOPE: Manifest declares broad permission scopes; SHIP-AUTH-SCOPE-COVERAGE-MISSING on shopify.cancel\_order: shopify.cancel\_order requires scopes not declared in the manifest; \+4 more | — | SHIP-AUTH-MANIFEST-BROAD-SCOPE \(high\); SHIP-AUTH-SCOPE-COVERAGE-MISSING \(high\); \+4 more | | Approval | partial | tool\_surface\_facts.controls\[kind=approval\_policy\]; findings\[\] | high | SHIP-POLICY-APPROVAL-MISSING on stripe.create\_refund: stripe.create\_refund lacks a declared approval policy; SHIP-ACTION-FINANCIAL-WRITE-CONTROL-MISSING on stripe.create\_refund: stripe.create\_refund has financial write capability without required controls | SHIP-POLICY-APPROVAL-MISSING \(critical\); SHIP-ACTION-FINANCIAL-WRITE-CONTROL-MISSING \(critical\) | — | | Confirmation | partial | tool\_surface\_facts.controls\[kind=confirmation\_policy\]; findings\[\] | high | SHIP-POLICY-CONFIRMATION-MISSING on stripe.create\_refund: stripe.create\_refund lacks a declared confirmation policy; SHIP-POLICY-CONFIRMATION-MISSING on gmail.send\_customer\_email: gmail.send\_customer\_email lacks a declared confirmation policy | — | SHIP-POLICY-CONFIRMATION-MISSING \(high\); SHIP-POLICY-CONFIRMATION-MISSING \(high\) | | Idempotency | partial | tool\_surface\_facts.controls\[kind=idempotency\_evidence\]; action\_surface\_facts.actions\[\].safeguards.idempotency; \+1 more | high | SHIP-SIDEFX-IDEMPOTENCY-MISSING on stripe.create\_refund: stripe.create\_refund lacks idempotency evidence; SHIP-ACTION-FINANCIAL-WRITE-CONTROL-MISSING on stripe.create\_refund: stripe.create\_refund has financial write capability without required controls | SHIP-SIDEFX-IDEMPOTENCY-MISSING \(critical\); SHIP-ACTION-FINANCIAL-WRITE-CONTROL-MISSING \(critical\) | — | -| Side effects | partial | tool\_inventory\[\].risk\_tags; action\_surface\_facts.actions\[\].effect; \+1 more | mixed | SHIP-SCHEMA-BROAD-FREE-TEXT on zendesk.update\_ticket: zendesk.update\_ticket accepts broad free-form action input; SHIP-SCHEMA-BROAD-FREE-TEXT on gmail.send\_customer\_email: gmail.send\_customer\_email accepts broad free-form action input; \+7 more | SHIP-POLICY-APPROVAL-MISSING \(critical\); SHIP-SIDEFX-IDEMPOTENCY-MISSING \(critical\); \+3 more | SHIP-SCHEMA-BROAD-FREE-TEXT \(high\); SHIP-SCHEMA-BROAD-FREE-TEXT \(high\); \+2 more | +| Side effects | partial | tool\_inventory\[\].risk\_tags; action\_surface\_facts.actions\[\].effect; \+1 more | high | SHIP-POLICY-APPROVAL-MISSING on stripe.create\_refund: stripe.create\_refund lacks a declared approval policy; SHIP-POLICY-CONFIRMATION-MISSING on stripe.create\_refund: stripe.create\_refund lacks a declared confirmation policy; \+5 more | SHIP-POLICY-APPROVAL-MISSING \(critical\); SHIP-SIDEFX-IDEMPOTENCY-MISSING \(critical\); \+3 more | SHIP-POLICY-CONFIRMATION-MISSING \(high\); SHIP-POLICY-CONFIRMATION-MISSING \(high\) | | Memory isolation | not\_declared | — | unknown | — | — | — | | Human-in-the-loop evidence | not\_declared | — | unknown | — | — | — | -| Prompt/scope alignment | partial | declared\_intentions; misalignments; \+2 more | medium | SHIP-SCOPE-PROHIBITED-TOOL-PRESENT on stripe.create\_refund: stripe.create\_refund appears to overlap with a prohibited action; SHIP-SCOPE-PROHIBITED-TOOL-PRESENT on gmail.send\_customer\_email: gmail.send\_customer\_email appears to overlap with a prohibited action | — | SHIP-SCOPE-PROHIBITED-TOOL-PRESENT \(high\); SHIP-SCOPE-PROHIBITED-TOOL-PRESENT \(high\) | +| Prompt/scope alignment | covered | declared\_intentions; misalignments; \+1 more | medium | — | — | — | | Retry/timeout | not\_declared | — | unknown | — | — | — | | Baseline debt | informational | — | unknown | — | — | — | | Action-surface policy | partial | action\_surface\_facts.actions; findings\[\].blocks\_release; \+1 more | high | SHIP-ACTION-EXTERNAL-COMMUNICATION-AUDIT-MISSING on gmail.send\_customer\_email: gmail.send\_customer\_email has external communication capability without required controls; SHIP-ACTION-EXTERNAL-COMMUNICATION-AUDIT-MISSING on stripe.create\_refund: stripe.create\_refund has external communication capability without required controls; \+1 more | SHIP-ACTION-EXTERNAL-COMMUNICATION-AUDIT-MISSING \(high\); SHIP-ACTION-EXTERNAL-COMMUNICATION-AUDIT-MISSING \(high\); \+1 more | — | -## §2 Capability ↔ Intent diff — missing +## §2 Capability ↔ Intent diff — covered ### Declared @@ -94,20 +89,17 @@ This packet is a reviewer-shaped synthesis of a static Agents Shipgate scan. See - support.search\_kb - zendesk.update\_ticket -### Divergences - -- `SHIP-SCOPE-PROHIBITED-TOOL-PRESENT` on `gmail.send\_customer\_email, stripe.create\_refund`: stripe.create\_refund appears to overlap with a prohibited action — `specs/support-tools.openapi.yaml:97` — `shipgate.yaml:22` -- `SHIP-SCOPE-PROHIBITED-TOOL-PRESENT` on `gmail.send\_customer\_email, stripe.create\_refund`: gmail.send\_customer\_email appears to overlap with a prohibited action — `.agents-shipgate/mcp-tools.json\#/tools/1` — `shipgate.yaml:24` - ## §3 High-risk tool surface — partial -- Total tools: 7 · High-risk: 3 +- Total tools: 7 · High-risk: 5 | Tool | Source | Risk tags | Approval | Idempotency | |---|---|---|---|---| | `gmail.send\_customer\_email` | mcp | customer\_communication, external\_write | no | no | +| `send\_email\_preview` | mcp | read\_only | no | no | | `shopify.cancel\_order` | openapi | destructive, write | yes | yes | | `stripe.create\_refund` | openapi | external\_write, financial\_action, write | no | no | +| `support.search\_kb` | mcp | read\_only | no | no | ## §3A Tool-surface diff — not declared @@ -218,12 +210,6 @@ This packet is a reviewer-shaped synthesis of a static Agents Shipgate scan. See - Related finding(s): fp\_973ea0ef2110ca9a - **Manual review for SHIP-POLICY-CONFIRMATION-MISSING** — Declare a user confirmation policy for stripe.create\_refund or remove this action from the release. - Related finding(s): fp\_c762eebfadaf39d9, fp\_fae2921fd2d0cbd5 -- **Manual review for SHIP-SCHEMA-BROAD-FREE-TEXT** — Constrain zendesk.update\_ticket.updates with an enum, structured schema, or narrower field-specific parameters. - - Related finding(s): fp\_d58657d9b1c91a84, fp\_ea4f18e4707f8e1d -- **Manual review for SHIP-SCHEMA-MISSING-BOUNDS** — Add a maximum bound to stripe.create\_refund.amount or document an equivalent limit in the tool policy. - - Related finding(s): fp\_60fdf92126ba8844 -- **Manual review for SHIP-SCOPE-PROHIBITED-TOOL-PRESENT** — Remove stripe.create\_refund, narrow its policy, or revise prohibited\_actions so the manifest and tool surface do not contradict each other. - - Related finding(s): fp\_5faaaee860bcec8b, fp\_6f8e1ebab65f3607 - **Manual review for SHIP-SIDEFX-IDEMPOTENCY-MISSING** — Add an idempotency key, idempotent annotation, or declared idempotency policy for stripe.create\_refund. - Related finding(s): fp\_2cf0d6c77d9c3eee - **Re-run scan after resolving source warnings** — Source loaders emitted warnings; some tool surfaces may have been parsed with reduced confidence. @@ -244,4 +230,3 @@ Agents Shipgate is an advisory tool: the deterministic merge gate for AI-generat - Low-confidence tool extractions: none - Suppressed findings in effect: none - Memory isolation is not modeled by the v0.1 manifest schema; no static evidence is available. -- 5 active finding\(s\) came from heuristic provenance \(keyword\_heuristic=5, regex\_heuristic=0\); review the finding evidence before acting. diff --git a/samples/support_refund_agent/expected/report.json b/samples/support_refund_agent/expected/report.json index b0c16853..48e84572 100644 --- a/samples/support_refund_agent/expected/report.json +++ b/samples/support_refund_agent/expected/report.json @@ -1,7 +1,7 @@ { "schema_version": "0.1", - "report_schema_version": "0.32", - "run_id": "agents_shipgate_5ad1a92a984aacac", + "report_schema_version": "0.33", + "run_id": "agents_shipgate_ff9302d24e1b6891", "manifest_dir": "/samples/support_refund_agent", "project": { "name": "support-refund-agent", @@ -76,7 +76,7 @@ "summary": { "status": "release_blockers_detected", "critical_count": 3, - "high_count": 15, + "high_count": 10, "medium_count": 1, "low_count": 0, "info_count": 0, @@ -119,7 +119,8 @@ "capability_refs": [ "cap_578e492c26fd4656" ], - "capability_trace_refs": [] + "capability_trace_refs": [], + "support": null }, { "id": "fp_2cf0d6c77d9c3eee", @@ -150,7 +151,8 @@ "pointer": "/policies/require_idempotency_for_tools" }, "capability_refs": [], - "capability_trace_refs": [] + "capability_trace_refs": [], + "support": null }, { "id": "fp_1c94d2d2693dccdf", @@ -172,7 +174,33 @@ }, "policy_evidence_source": null, "capability_refs": [], - "capability_trace_refs": [] + "capability_trace_refs": [], + "support": { + "status": "matched", + "confidence": "high", + "policy_eligible": true, + "blocking_eligible": true, + "claim_ids": [], + "evidence_bases": [ + "protocol_structure" + ], + "predicates": [ + { + "predicate": "deterministic_action_rule", + "status": "matched", + "expected": null, + "observed": "agent:support-refund-agent/refund-assistant:action_v2_49bfadd2eb7605a1065c24081f890481ee2f37e9718152ae533c8e43ddaab17a", + "confidence": "high", + "claim_ids": [], + "evidence_bases": [ + "protocol_structure" + ], + "policy_eligible": true, + "why": null + } + ], + "support_hash": "sha256:2da4af706588e6b22a8dcb7b25419cf3fa22214d1fb76ea543afef8f4936a3f4" + } }, { "id": "fp_e042ce7813b97a2d", @@ -194,7 +222,33 @@ }, "policy_evidence_source": null, "capability_refs": [], - "capability_trace_refs": [] + "capability_trace_refs": [], + "support": { + "status": "matched", + "confidence": "high", + "policy_eligible": true, + "blocking_eligible": true, + "claim_ids": [], + "evidence_bases": [ + "protocol_structure" + ], + "predicates": [ + { + "predicate": "deterministic_action_rule", + "status": "matched", + "expected": null, + "observed": "agent:support-refund-agent/refund-assistant:action_v2_6f2d18a55f2189a165089034897d52ada547bb19afe20036cb7ec3119c2d95ca", + "confidence": "high", + "claim_ids": [], + "evidence_bases": [ + "protocol_structure" + ], + "policy_eligible": true, + "why": null + } + ], + "support_hash": "sha256:742ea8da905eef5df5d2f9a3e9bea9a3bbc84eb4710409e8f41c5389f86af494" + } }, { "id": "fp_dfa27ad5b52d8fd6", @@ -216,76 +270,36 @@ }, "policy_evidence_source": null, "capability_refs": [], - "capability_trace_refs": [] + "capability_trace_refs": [], + "support": { + "status": "matched", + "confidence": "high", + "policy_eligible": true, + "blocking_eligible": true, + "claim_ids": [], + "evidence_bases": [ + "protocol_structure" + ], + "predicates": [ + { + "predicate": "deterministic_action_rule", + "status": "matched", + "expected": null, + "observed": "agent:support-refund-agent/refund-assistant:action_v2_6f2d18a55f2189a165089034897d52ada547bb19afe20036cb7ec3119c2d95ca", + "confidence": "high", + "claim_ids": [], + "evidence_bases": [ + "protocol_structure" + ], + "policy_eligible": true, + "why": null + } + ], + "support_hash": "sha256:742ea8da905eef5df5d2f9a3e9bea9a3bbc84eb4710409e8f41c5389f86af494" + } } ], "review_items": [ - { - "id": "fp_60fdf92126ba8844", - "fingerprint": "fp_60fdf92126ba8844", - "check_id": "SHIP-SCHEMA-MISSING-BOUNDS", - "severity": "high", - "title": "stripe.create_refund.amount has no maximum bound", - "baseline_status": null, - "blocks_release": false, - "source": { - "type": "openapi", - "ref": "specs/support-tools.openapi.yaml#/paths/~1refunds/post", - "location": null, - "path": "specs/support-tools.openapi.yaml", - "start_line": 97, - "end_line": null, - "start_column": 5, - "pointer": "/paths/~1refunds/post" - }, - "policy_evidence_source": null, - "capability_refs": [], - "capability_trace_refs": [] - }, - { - "id": "fp_ea4f18e4707f8e1d", - "fingerprint": "fp_ea4f18e4707f8e1d", - "check_id": "SHIP-SCHEMA-BROAD-FREE-TEXT", - "severity": "high", - "title": "zendesk.update_ticket accepts broad free-form action input", - "baseline_status": null, - "blocks_release": false, - "source": { - "type": "openapi", - "ref": "specs/support-tools.openapi.yaml#/paths/~1tickets~1{ticket_id}/patch", - "location": null, - "path": "specs/support-tools.openapi.yaml", - "start_line": 142, - "end_line": null, - "start_column": 5, - "pointer": "/paths/~1tickets~1{ticket_id}/patch" - }, - "policy_evidence_source": null, - "capability_refs": [], - "capability_trace_refs": [] - }, - { - "id": "fp_d58657d9b1c91a84", - "fingerprint": "fp_d58657d9b1c91a84", - "check_id": "SHIP-SCHEMA-BROAD-FREE-TEXT", - "severity": "high", - "title": "gmail.send_customer_email accepts broad free-form action input", - "baseline_status": null, - "blocks_release": false, - "source": { - "type": "mcp", - "ref": ".agents-shipgate/mcp-tools.json", - "location": null, - "path": ".agents-shipgate/mcp-tools.json", - "start_line": null, - "end_line": null, - "start_column": null, - "pointer": "/tools/1" - }, - "policy_evidence_source": null, - "capability_refs": [], - "capability_trace_refs": [] - }, { "id": "fp_df4a990cca9f936b", "fingerprint": "fp_df4a990cca9f936b", @@ -306,7 +320,8 @@ }, "policy_evidence_source": null, "capability_refs": [], - "capability_trace_refs": [] + "capability_trace_refs": [], + "support": null }, { "id": "fp_1fd01b4ed2e41d51", @@ -328,7 +343,8 @@ }, "policy_evidence_source": null, "capability_refs": [], - "capability_trace_refs": [] + "capability_trace_refs": [], + "support": null }, { "id": "fp_24d610b0d4324190", @@ -350,7 +366,8 @@ }, "policy_evidence_source": null, "capability_refs": [], - "capability_trace_refs": [] + "capability_trace_refs": [], + "support": null }, { "id": "fp_519cb82f038efd10", @@ -372,7 +389,8 @@ }, "policy_evidence_source": null, "capability_refs": [], - "capability_trace_refs": [] + "capability_trace_refs": [], + "support": null }, { "id": "fp_095f3a5337124f6e", @@ -394,69 +412,8 @@ }, "policy_evidence_source": null, "capability_refs": [], - "capability_trace_refs": [] - }, - { - "id": "fp_6f8e1ebab65f3607", - "fingerprint": "fp_6f8e1ebab65f3607", - "check_id": "SHIP-SCOPE-PROHIBITED-TOOL-PRESENT", - "severity": "high", - "title": "stripe.create_refund appears to overlap with a prohibited action", - "baseline_status": null, - "blocks_release": false, - "source": { - "type": "openapi", - "ref": "specs/support-tools.openapi.yaml#/paths/~1refunds/post", - "location": null, - "path": "specs/support-tools.openapi.yaml", - "start_line": 97, - "end_line": null, - "start_column": 5, - "pointer": "/paths/~1refunds/post" - }, - "policy_evidence_source": { - "type": "manifest", - "ref": "shipgate.yaml#/agent/prohibited_actions/0", - "location": null, - "path": "shipgate.yaml", - "start_line": 22, - "end_line": null, - "start_column": null, - "pointer": "/agent/prohibited_actions/0" - }, - "capability_refs": [], - "capability_trace_refs": [] - }, - { - "id": "fp_5faaaee860bcec8b", - "fingerprint": "fp_5faaaee860bcec8b", - "check_id": "SHIP-SCOPE-PROHIBITED-TOOL-PRESENT", - "severity": "high", - "title": "gmail.send_customer_email appears to overlap with a prohibited action", - "baseline_status": null, - "blocks_release": false, - "source": { - "type": "mcp", - "ref": ".agents-shipgate/mcp-tools.json", - "location": null, - "path": ".agents-shipgate/mcp-tools.json", - "start_line": null, - "end_line": null, - "start_column": null, - "pointer": "/tools/1" - }, - "policy_evidence_source": { - "type": "manifest", - "ref": "shipgate.yaml#/agent/prohibited_actions/2", - "location": null, - "path": "shipgate.yaml", - "start_line": 24, - "end_line": null, - "start_column": null, - "pointer": "/agent/prohibited_actions/2" - }, - "capability_refs": [], - "capability_trace_refs": [] + "capability_trace_refs": [], + "support": null }, { "id": "fp_fae2921fd2d0cbd5", @@ -489,7 +446,8 @@ "capability_refs": [ "cap_578e492c26fd4656" ], - "capability_trace_refs": [] + "capability_trace_refs": [], + "support": null }, { "id": "fp_c762eebfadaf39d9", @@ -522,7 +480,8 @@ "capability_refs": [ "cap_e9991ea05213172f" ], - "capability_trace_refs": [] + "capability_trace_refs": [], + "support": null }, { "id": "fp_674fa79ae9993422", @@ -544,7 +503,8 @@ }, "policy_evidence_source": null, "capability_refs": [], - "capability_trace_refs": [] + "capability_trace_refs": [], + "support": null }, { "id": "fp_609d62f4dc434961", @@ -566,7 +526,8 @@ }, "policy_evidence_source": null, "capability_refs": [], - "capability_trace_refs": [] + "capability_trace_refs": [], + "support": null } ], "evidence_coverage": { @@ -767,6 +728,174 @@ "requires_human_review": true } }, + { + "kind": "inferred_policy_applicability", + "subject": "tool_v2_2c9ee6ae2b9f0259ba2dad40a30fb18a8bdc3a732ffc207936fdd49c80e1bc9e", + "source_type": null, + "source_ref": "inventories/sdk-tools.json", + "why": "SHIP-SCHEMA-BROAD-FREE-TEXT: Policy applicability is supported only by heuristic evidence.", + "next_action": { + "kind": "provide_policy_evidence", + "command": "agents-shipgate verify --workspace . --config shipgate.yaml --ci-mode advisory --format json", + "path": "/tools/0", + "why": "Heuristic or unknown evidence cannot create a blocking policy finding.", + "expects": "Provide reviewed or structural evidence for every indeterminate predicate.", + "accepted_values": [ + "reviewed_declaration", + "protocol_structure", + "typed_provider_fact", + "structural_scope" + ], + "suggested_patch_kind": "manual", + "declaration_template": null, + "auto_apply": false, + "requires_human_review": true + } + }, + { + "kind": "inferred_policy_applicability", + "subject": "tool_v2_8b559376c3236346b33a64fb9d460d342e6956579a68a0d5a331b2d2adf68ac8", + "source_type": null, + "source_ref": "specs/support-tools.openapi.yaml", + "why": "SHIP-SCHEMA-MISSING-BOUNDS: Policy applicability is supported only by heuristic evidence.", + "next_action": { + "kind": "provide_policy_evidence", + "command": "agents-shipgate verify --workspace . --config shipgate.yaml --ci-mode advisory --format json", + "path": "/paths/~1refunds/post", + "why": "Heuristic or unknown evidence cannot create a blocking policy finding.", + "expects": "Provide reviewed or structural evidence for every indeterminate predicate.", + "accepted_values": [ + "reviewed_declaration", + "protocol_structure", + "typed_provider_fact", + "structural_scope" + ], + "suggested_patch_kind": "manual", + "declaration_template": null, + "auto_apply": false, + "requires_human_review": true + } + }, + { + "kind": "inferred_policy_applicability", + "subject": "tool_v2_aa488250dc1213a6a8bb76a7e695da294f93052f3181d42817b6cd2cf83cfa33", + "source_type": null, + "source_ref": "specs/support-tools.openapi.yaml", + "why": "SHIP-SCHEMA-BROAD-FREE-TEXT: Policy applicability is supported only by heuristic evidence.", + "next_action": { + "kind": "provide_policy_evidence", + "command": "agents-shipgate verify --workspace . --config shipgate.yaml --ci-mode advisory --format json", + "path": "/paths/~1tickets~1{ticket_id}/patch", + "why": "Heuristic or unknown evidence cannot create a blocking policy finding.", + "expects": "Provide reviewed or structural evidence for every indeterminate predicate.", + "accepted_values": [ + "reviewed_declaration", + "protocol_structure", + "typed_provider_fact", + "structural_scope" + ], + "suggested_patch_kind": "manual", + "declaration_template": null, + "auto_apply": false, + "requires_human_review": true + } + }, + { + "kind": "inferred_policy_applicability", + "subject": "tool_v2_c69ebf76910efa80a2024a15d836f20366effdbd12f7a69b660ce4f4e5870ed9", + "source_type": null, + "source_ref": ".agents-shipgate/mcp-tools.json", + "why": "SHIP-SCHEMA-BROAD-FREE-TEXT: Policy applicability is supported only by heuristic evidence.", + "next_action": { + "kind": "provide_policy_evidence", + "command": "agents-shipgate verify --workspace . --config shipgate.yaml --ci-mode advisory --format json", + "path": "/tools/1", + "why": "Heuristic or unknown evidence cannot create a blocking policy finding.", + "expects": "Provide reviewed or structural evidence for every indeterminate predicate.", + "accepted_values": [ + "reviewed_declaration", + "protocol_structure", + "typed_provider_fact", + "structural_scope" + ], + "suggested_patch_kind": "manual", + "declaration_template": null, + "auto_apply": false, + "requires_human_review": true + } + }, + { + "kind": "inferred_policy_applicability", + "subject": "tool_v2_2c9ee6ae2b9f0259ba2dad40a30fb18a8bdc3a732ffc207936fdd49c80e1bc9e", + "source_type": null, + "source_ref": "shipgate.yaml", + "why": "SHIP-SCOPE-PROHIBITED-TOOL-PRESENT: Policy applicability is supported only by heuristic evidence.", + "next_action": { + "kind": "provide_policy_evidence", + "command": "agents-shipgate verify --workspace . --config shipgate.yaml --ci-mode advisory --format json", + "path": "/agent/prohibited_actions/2", + "why": "Heuristic or unknown evidence cannot create a blocking policy finding.", + "expects": "Provide reviewed or structural evidence for every indeterminate predicate.", + "accepted_values": [ + "reviewed_declaration", + "protocol_structure", + "typed_provider_fact", + "structural_scope" + ], + "suggested_patch_kind": "manual", + "declaration_template": null, + "auto_apply": false, + "requires_human_review": true + } + }, + { + "kind": "inferred_policy_applicability", + "subject": "tool_v2_8b559376c3236346b33a64fb9d460d342e6956579a68a0d5a331b2d2adf68ac8", + "source_type": null, + "source_ref": "shipgate.yaml", + "why": "SHIP-SCOPE-PROHIBITED-TOOL-PRESENT: Policy applicability is supported only by heuristic evidence.", + "next_action": { + "kind": "provide_policy_evidence", + "command": "agents-shipgate verify --workspace . --config shipgate.yaml --ci-mode advisory --format json", + "path": "/agent/prohibited_actions/0", + "why": "Heuristic or unknown evidence cannot create a blocking policy finding.", + "expects": "Provide reviewed or structural evidence for every indeterminate predicate.", + "accepted_values": [ + "reviewed_declaration", + "protocol_structure", + "typed_provider_fact", + "structural_scope" + ], + "suggested_patch_kind": "manual", + "declaration_template": null, + "auto_apply": false, + "requires_human_review": true + } + }, + { + "kind": "inferred_policy_applicability", + "subject": "tool_v2_c69ebf76910efa80a2024a15d836f20366effdbd12f7a69b660ce4f4e5870ed9", + "source_type": null, + "source_ref": "shipgate.yaml", + "why": "SHIP-SCOPE-PROHIBITED-TOOL-PRESENT: Policy applicability is supported only by heuristic evidence.", + "next_action": { + "kind": "provide_policy_evidence", + "command": "agents-shipgate verify --workspace . --config shipgate.yaml --ci-mode advisory --format json", + "path": "/agent/prohibited_actions/2", + "why": "Heuristic or unknown evidence cannot create a blocking policy finding.", + "expects": "Provide reviewed or structural evidence for every indeterminate predicate.", + "accepted_values": [ + "reviewed_declaration", + "protocol_structure", + "typed_provider_fact", + "structural_scope" + ], + "suggested_patch_kind": "manual", + "declaration_template": null, + "auto_apply": false, + "requires_human_review": true + } + }, { "kind": "source_warning", "subject": "MCP source declares wildcard tool exposure", @@ -816,7 +945,8 @@ "reason_counts": { "conflicting_binding_evidence": 1 } - } + }, + "policy_gap_count": 7 }, "baseline_delta": { "enabled": false, @@ -836,30 +966,6 @@ "runtime_behavior_verified": false, "static_verdict_disclaimer": "This verdict covers deterministic static evidence only. Agents Shipgate did not execute the agent or prove runtime behavior, tool routing, credential enforcement, or safety.", "contribution_rules": [ - { - "finding_id": "fp_60fdf92126ba8844", - "fingerprint": "fp_60fdf92126ba8844", - "check_id": "SHIP-SCHEMA-MISSING-BOUNDS", - "category": "review_item", - "rule": "review_required", - "rationale": "requires_human_review=true (severity=high); routed to review_items." - }, - { - "finding_id": "fp_ea4f18e4707f8e1d", - "fingerprint": "fp_ea4f18e4707f8e1d", - "check_id": "SHIP-SCHEMA-BROAD-FREE-TEXT", - "category": "review_item", - "rule": "review_required", - "rationale": "requires_human_review=true (severity=high); routed to review_items." - }, - { - "finding_id": "fp_d58657d9b1c91a84", - "fingerprint": "fp_d58657d9b1c91a84", - "check_id": "SHIP-SCHEMA-BROAD-FREE-TEXT", - "category": "review_item", - "rule": "review_required", - "rationale": "requires_human_review=true (severity=high); routed to review_items." - }, { "finding_id": "fp_df4a990cca9f936b", "fingerprint": "fp_df4a990cca9f936b", @@ -900,22 +1006,6 @@ "rule": "review_required", "rationale": "requires_human_review=true (severity=high); routed to review_items." }, - { - "finding_id": "fp_6f8e1ebab65f3607", - "fingerprint": "fp_6f8e1ebab65f3607", - "check_id": "SHIP-SCOPE-PROHIBITED-TOOL-PRESENT", - "category": "review_item", - "rule": "review_required", - "rationale": "requires_human_review=true (severity=high); routed to review_items." - }, - { - "finding_id": "fp_5faaaee860bcec8b", - "fingerprint": "fp_5faaaee860bcec8b", - "check_id": "SHIP-SCOPE-PROHIBITED-TOOL-PRESENT", - "category": "review_item", - "rule": "review_required", - "rationale": "requires_human_review=true (severity=high); routed to review_items." - }, { "finding_id": "fp_973ea0ef2110ca9a", "fingerprint": "fp_973ea0ef2110ca9a", @@ -1013,10 +1103,13 @@ ], "claims": [ { + "claim_id": "clm_e016125bf4102d2231d3", "dimension": "identity", "value": "obs_v1_6a598e295d2d845f05a72faecd97fcf971b67c96a0aad092d9082c8e573df553", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "protocol_structure", + "policy_eligible": true, "source": "source_observation", "source_pointer": "/tools/1", "evidence": { @@ -1039,10 +1132,13 @@ ], "claims": [ { + "claim_id": "clm_1d843e3958c618456a2f", "dimension": "binding", "value": "agent_v1:7cb237a00d64b7400f4adc3b->tool_v2_c69ebf76910efa80a2024a15d836f20366effdbd12f7a69b660ce4f4e5870ed9", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "reviewed_declaration", + "policy_eligible": true, "source": "agent_binding_declaration", "source_pointer": "/agent_bindings/declarations/0/tools/1", "evidence": { @@ -1067,10 +1163,13 @@ "confidence": "high", "claims": [ { + "claim_id": "clm_a4818bc49faaf8f1ea61", "dimension": "effect", "value": "write", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "structural_scope", + "policy_eligible": true, "source": "action_scope", "source_pointer": "/tools/1", "evidence": { @@ -1078,19 +1177,25 @@ } }, { + "claim_id": "clm_e6c199e33c2159754789", "dimension": "effect", "value": "external_communication", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "reviewed_declaration", + "policy_eligible": true, "source": "action_surface_declaration", "source_pointer": "action_surface.actions[tool='gmail.send_customer_email'].effect", "evidence": {} }, { + "claim_id": "clm_26ae80b049a84be11ab1", "dimension": "effect", "value": "write", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "structural_scope", + "policy_eligible": true, "source": "auth_scope", "source_pointer": "/tools/1", "evidence": { @@ -1098,10 +1203,13 @@ } }, { + "claim_id": "clm_f0196c0c4caab8cd8ecd", "dimension": "effect", "value": "external_communication", "confidence": "high", "provenance_kind": "keyword_heuristic", + "basis": "inferred_keyword", + "policy_eligible": false, "source": "risk_hint:keyword", "source_pointer": "/tools/1", "evidence": { @@ -1110,10 +1218,13 @@ } }, { + "claim_id": "clm_b56eed6a901658bae650", "dimension": "effect", "value": "external_communication", "confidence": "high", "provenance_kind": "keyword_heuristic", + "basis": "inferred_keyword", + "policy_eligible": false, "source": "risk_hint:keyword", "source_pointer": "/tools/1", "evidence": { @@ -1122,10 +1233,13 @@ } }, { + "claim_id": "clm_7ec6f046c34a18b719f2", "dimension": "effect", "value": "external_communication", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "reviewed_declaration", + "policy_eligible": true, "source": "risk_hint:manual", "source_pointer": "/tools/1", "evidence": { @@ -1136,10 +1250,13 @@ } }, { + "claim_id": "clm_188aecba394542ac226f", "dimension": "effect", "value": "external_communication", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "reviewed_declaration", + "policy_eligible": true, "source": "risk_hint:manual", "source_pointer": "/tools/1", "evidence": { @@ -1162,10 +1279,13 @@ ], "claims": [ { + "claim_id": "clm_932e397ceff5ab3cb4df", "dimension": "authority", "value": "scoped", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "reviewed_declaration", + "policy_eligible": true, "source": "action_surface_declaration", "source_pointer": "action_surface.actions[tool='gmail.send_customer_email'].authority", "evidence": { @@ -1177,10 +1297,13 @@ } }, { + "claim_id": "clm_c1c0614054dbba4bfa4e", "dimension": "authority", "value": "scoped", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "protocol_structure", + "policy_eligible": true, "source": "mcp_authority", "source_pointer": "/tools/1", "evidence": { @@ -1209,9 +1332,7 @@ "related_findings": [ "fp_095f3a5337124f6e", "fp_1c94d2d2693dccdf", - "fp_5faaaee860bcec8b", - "fp_c762eebfadaf39d9", - "fp_d58657d9b1c91a84" + "fp_c762eebfadaf39d9" ] }, { @@ -1236,10 +1357,13 @@ ], "claims": [ { + "claim_id": "clm_ee6f9f9b928e98d8e34f", "dimension": "identity", "value": "obs_v1_abe0748ca63b6b1f48d758ee5395281d80ad99b49e4766dda9f717c57aa6a3c0", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "protocol_structure", + "policy_eligible": true, "source": "source_observation", "source_pointer": "/paths/~1refunds~1{payment_id}~1status/get", "evidence": { @@ -1262,10 +1386,13 @@ ], "claims": [ { + "claim_id": "clm_f55c3a2e30069f6699fb", "dimension": "binding", "value": "agent_v1:7cb237a00d64b7400f4adc3b->tool_v2_61891eb768365da25408e00518646b94299be76989cba28407f47015112f86be", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "reviewed_declaration", + "policy_eligible": true, "source": "agent_binding_declaration", "source_pointer": "/agent_bindings/declarations/0/tools/2", "evidence": { @@ -1290,19 +1417,25 @@ "confidence": "high", "claims": [ { + "claim_id": "clm_e94b0e48a585e4b07dca", "dimension": "effect", "value": "read", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "reviewed_declaration", + "policy_eligible": true, "source": "action_surface_declaration", "source_pointer": "action_surface.actions[tool='refund_status_lookup'].effect", "evidence": {} }, { + "claim_id": "clm_bdc89f8aeb4887e717f4", "dimension": "effect", "value": "read", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "protocol_structure", + "policy_eligible": true, "source": "mcp_annotation", "source_pointer": "/paths/~1refunds~1{payment_id}~1status/get", "evidence": { @@ -1310,10 +1443,13 @@ } }, { + "claim_id": "clm_53c53a5e6459571424da", "dimension": "effect", "value": "read", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "protocol_structure", + "policy_eligible": true, "source": "openapi_method", "source_pointer": "/paths/~1refunds~1{payment_id}~1status/get", "evidence": { @@ -1321,10 +1457,13 @@ } }, { + "claim_id": "clm_834a693cde6cc06f269b", "dimension": "effect", "value": "read", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "reviewed_declaration", + "policy_eligible": true, "source": "risk_hint:manual", "source_pointer": "/paths/~1refunds~1{payment_id}~1status/get", "evidence": { @@ -1345,10 +1484,13 @@ "scopes": [], "claims": [ { + "claim_id": "clm_da401a8a8ab43662894f", "dimension": "authority", "value": "unscoped", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "reviewed_declaration", + "policy_eligible": true, "source": "action_surface_declaration", "source_pointer": "action_surface.actions[tool='refund_status_lookup'].authority", "evidence": { @@ -1358,10 +1500,13 @@ } }, { + "claim_id": "clm_aa0bf67594fae796aa00", "dimension": "authority", "value": "unscoped", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "protocol_structure", + "policy_eligible": true, "source": "openapi_authority", "source_pointer": "/paths/~1refunds~1{payment_id}~1status/get", "evidence": { @@ -1371,10 +1516,13 @@ } }, { + "claim_id": "clm_40cc6737ceee31a1ac60", "dimension": "authority", "value": "unscoped", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "protocol_structure", + "policy_eligible": true, "source": "openapi_security_alternative", "source_pointer": "/paths/~1refunds~1{payment_id}~1status/get/security/0", "evidence": { @@ -1405,37 +1553,265 @@ ] }, { - "id": "cap_fd7aa0505dafb1dc", - "tool_id": "tool_v2_08f6c9642ff3da07bd4dd33f949b747ad5fe79c245d1f76d450a81ff86cb1f6e", - "tool_name": "shopify.cancel_order", - "source_type": "openapi", - "source_id": "support_openapi", - "source_ref": "specs/support-tools.openapi.yaml#/paths/~1orders~1{order_id}~1cancel/post", - "capability": "destructive", - "effect": "destructive", + "id": "cap_26b4e442907a45af", + "tool_id": "tool_v2_2c9ee6ae2b9f0259ba2dad40a30fb18a8bdc3a732ffc207936fdd49c80e1bc9e", + "tool_name": "send_email_preview", + "source_type": "mcp", + "source_id": "reviewed_sdk_inventory", + "source_ref": "inventories/sdk-tools.json", + "capability": "read_only", + "effect": "external_communication", "semantic_assessment": { - "conservative_effect": "destructive", + "conservative_effect": "external_communication", "identity": { - "tool_id": "tool_v2_08f6c9642ff3da07bd4dd33f949b747ad5fe79c245d1f76d450a81ff86cb1f6e", - "status": "structural", - "provider": "support_openapi", - "binding_id": null, - "primary_observation_id": "obs_v1_fc50761fdbf05db319a85b8ac0b828b8522967a4273d1c0f70d4cfabf0304300", + "tool_id": "tool_v2_2c9ee6ae2b9f0259ba2dad40a30fb18a8bdc3a732ffc207936fdd49c80e1bc9e", + "status": "declared", + "provider": "openai_sdk_static", + "binding_id": "sdk_send_email_preview", + "primary_observation_id": "obs_v1_8835a4a63d13e804fae8ab9c175ece24e865d923e65457698593c0532254b0a6", "observation_ids": [ - "obs_v1_fc50761fdbf05db319a85b8ac0b828b8522967a4273d1c0f70d4cfabf0304300" + "obs_v1_68344b9fde158f18db1f3476cf06003414968e6d82488d8f376eebfecbc8bfb3", + "obs_v1_8835a4a63d13e804fae8ab9c175ece24e865d923e65457698593c0532254b0a6" ], "claims": [ { + "claim_id": "clm_de7969c4a04dff4a5514", "dimension": "identity", - "value": "obs_v1_fc50761fdbf05db319a85b8ac0b828b8522967a4273d1c0f70d4cfabf0304300", + "value": "obs_v1_68344b9fde158f18db1f3476cf06003414968e6d82488d8f376eebfecbc8bfb3", "confidence": "high", "provenance_kind": "static_declaration", - "source": "source_observation", - "source_pointer": "/paths/~1orders~1{order_id}~1cancel/post", + "basis": "reviewed_declaration", + "policy_eligible": true, + "source": "tool_identity_binding", + "source_pointer": "agents/refund_agent.py", "evidence": { - "source_type": "openapi", - "source_id": "support_openapi", - "native_locator": "POST /orders/{order_id}/cancel" + "source_type": "sdk_function", + "source_id": "openai_sdk_static", + "native_locator": "agents/refund_agent.py#send_email_preview" + } + }, + { + "claim_id": "clm_dba9cd1e78c894219426", + "dimension": "identity", + "value": "obs_v1_8835a4a63d13e804fae8ab9c175ece24e865d923e65457698593c0532254b0a6", + "confidence": "high", + "provenance_kind": "static_declaration", + "basis": "reviewed_declaration", + "policy_eligible": true, + "source": "tool_identity_binding", + "source_pointer": "/tools/0", + "evidence": { + "source_type": "mcp", + "source_id": "reviewed_sdk_inventory", + "native_locator": "send_email_preview" + } + } + ], + "issues": [], + "pass_eligible": true + }, + "binding": { + "status": "declared", + "confidence": "high", + "root_agent_id": "agent_v1:7cb237a00d64b7400f4adc3b", + "reachable_path": [ + "agent_v1:7cb237a00d64b7400f4adc3b", + "tool_v2_2c9ee6ae2b9f0259ba2dad40a30fb18a8bdc3a732ffc207936fdd49c80e1bc9e" + ], + "claims": [ + { + "claim_id": "clm_38bd6ab43966135164c2", + "dimension": "binding", + "value": "agent_v1:7cb237a00d64b7400f4adc3b->tool_v2_2c9ee6ae2b9f0259ba2dad40a30fb18a8bdc3a732ffc207936fdd49c80e1bc9e", + "confidence": "high", + "provenance_kind": "static_declaration", + "basis": "reviewed_declaration", + "policy_eligible": true, + "source": "agent_binding_declaration", + "source_pointer": "/agent_bindings/declarations/0/tools/6", + "evidence": { + "edge_type": "direct_tool", + "complete": true + } + }, + { + "claim_id": "clm_c5e560fe7f75b8b49ce7", + "dimension": "binding", + "value": "agent_v1:7cb237a00d64b7400f4adc3b->tool_v2_2c9ee6ae2b9f0259ba2dad40a30fb18a8bdc3a732ffc207936fdd49c80e1bc9e", + "confidence": "high", + "provenance_kind": "ast_extraction", + "basis": "typed_provider_fact", + "policy_eligible": true, + "source": "agents/refund_agent.py", + "source_pointer": "agents/refund_agent.py:10", + "evidence": { + "edge_type": "direct_tool", + "complete": true + } + } + ], + "issues": [ + { + "kind": "conflicting_binding_evidence", + "dimension": "binding", + "message": "Closed-world declaration for 'root' does not match the complete structural tool set.", + "source": "shipgate.yaml", + "source_pointer": "/agent_bindings/declarations/0" + } + ], + "pass_eligible": false + }, + "effect": { + "status": "declared", + "confidence": "high", + "claims": [ + { + "claim_id": "clm_d147c0c853cf0af5ae61", + "dimension": "effect", + "value": "read", + "confidence": "high", + "provenance_kind": "static_declaration", + "basis": "reviewed_declaration", + "policy_eligible": true, + "source": "action_surface_declaration", + "source_pointer": "action_surface.actions[tool='send_email_preview'].effect", + "evidence": {} + }, + { + "claim_id": "clm_50c4d6abc43dbc7853c5", + "dimension": "effect", + "value": "read", + "confidence": "high", + "provenance_kind": "static_declaration", + "basis": "protocol_structure", + "policy_eligible": true, + "source": "mcp_annotation", + "source_pointer": "/tools/0", + "evidence": { + "readOnlyHint": true + } + }, + { + "claim_id": "clm_cfc3dd4156cdbd1469ed", + "dimension": "effect", + "value": "external_communication", + "confidence": "low", + "provenance_kind": "keyword_heuristic", + "basis": "inferred_keyword", + "policy_eligible": false, + "source": "risk_hint:keyword", + "source_pointer": "/tools/0", + "evidence": { + "tag": "customer_communication", + "hint_source": "keyword" + } + }, + { + "claim_id": "clm_b77048e00c8bab300132", + "dimension": "effect", + "value": "external_communication", + "confidence": "low", + "provenance_kind": "keyword_heuristic", + "basis": "inferred_keyword", + "policy_eligible": false, + "source": "risk_hint:keyword", + "source_pointer": "/tools/0", + "evidence": { + "tag": "external_write", + "hint_source": "keyword" + } + } + ], + "issues": [] + }, + "authority": { + "status": "declared", + "mode": "none", + "auth_type": null, + "credential_mode": null, + "scopes": [], + "claims": [ + { + "claim_id": "clm_6a7e0f2012d1e4cee027", + "dimension": "authority", + "value": "none", + "confidence": "high", + "provenance_kind": "static_declaration", + "basis": "reviewed_declaration", + "policy_eligible": true, + "source": "action_surface_declaration", + "source_pointer": "action_surface.actions[tool='send_email_preview'].authority", + "evidence": { + "auth_type": null, + "credential_mode": null, + "scopes": [] + } + }, + { + "claim_id": "clm_b1bf872bb994c8ec4e1c", + "dimension": "authority", + "value": "none", + "confidence": "high", + "provenance_kind": "static_declaration", + "basis": "protocol_structure", + "policy_eligible": true, + "source": "mcp_authority", + "source_pointer": "/tools/0", + "evidence": { + "auth_type": null, + "scopes": [], + "alternative_count": 0 + } + } + ], + "issues": [] + }, + "pass_eligible": false + }, + "risk_tags": [ + "read_only" + ], + "auth_scopes": [], + "owner": null, + "included_reason": "high_risk_tag", + "control_status": "present", + "related_findings": [] + }, + { + "id": "cap_fd7aa0505dafb1dc", + "tool_id": "tool_v2_08f6c9642ff3da07bd4dd33f949b747ad5fe79c245d1f76d450a81ff86cb1f6e", + "tool_name": "shopify.cancel_order", + "source_type": "openapi", + "source_id": "support_openapi", + "source_ref": "specs/support-tools.openapi.yaml#/paths/~1orders~1{order_id}~1cancel/post", + "capability": "destructive", + "effect": "destructive", + "semantic_assessment": { + "conservative_effect": "destructive", + "identity": { + "tool_id": "tool_v2_08f6c9642ff3da07bd4dd33f949b747ad5fe79c245d1f76d450a81ff86cb1f6e", + "status": "structural", + "provider": "support_openapi", + "binding_id": null, + "primary_observation_id": "obs_v1_fc50761fdbf05db319a85b8ac0b828b8522967a4273d1c0f70d4cfabf0304300", + "observation_ids": [ + "obs_v1_fc50761fdbf05db319a85b8ac0b828b8522967a4273d1c0f70d4cfabf0304300" + ], + "claims": [ + { + "claim_id": "clm_87b20f443f41d8daa79a", + "dimension": "identity", + "value": "obs_v1_fc50761fdbf05db319a85b8ac0b828b8522967a4273d1c0f70d4cfabf0304300", + "confidence": "high", + "provenance_kind": "static_declaration", + "basis": "protocol_structure", + "policy_eligible": true, + "source": "source_observation", + "source_pointer": "/paths/~1orders~1{order_id}~1cancel/post", + "evidence": { + "source_type": "openapi", + "source_id": "support_openapi", + "native_locator": "POST /orders/{order_id}/cancel" } } ], @@ -1452,10 +1828,13 @@ ], "claims": [ { + "claim_id": "clm_e551bd6b7ec256d44d83", "dimension": "binding", "value": "agent_v1:7cb237a00d64b7400f4adc3b->tool_v2_08f6c9642ff3da07bd4dd33f949b747ad5fe79c245d1f76d450a81ff86cb1f6e", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "reviewed_declaration", + "policy_eligible": true, "source": "agent_binding_declaration", "source_pointer": "/agent_bindings/declarations/0/tools/4", "evidence": { @@ -1480,10 +1859,13 @@ "confidence": "high", "claims": [ { + "claim_id": "clm_eff630fcc9ed89e3934a", "dimension": "effect", "value": "write", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "structural_scope", + "policy_eligible": true, "source": "action_scope", "source_pointer": "/paths/~1orders~1{order_id}~1cancel/post", "evidence": { @@ -1491,19 +1873,25 @@ } }, { + "claim_id": "clm_952866402a040f319732", "dimension": "effect", "value": "destructive", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "reviewed_declaration", + "policy_eligible": true, "source": "action_surface_declaration", "source_pointer": "action_surface.actions[tool='shopify.cancel_order'].effect", "evidence": {} }, { + "claim_id": "clm_918a970310039b946dfb", "dimension": "effect", "value": "write", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "structural_scope", + "policy_eligible": true, "source": "auth_scope", "source_pointer": "/paths/~1orders~1{order_id}~1cancel/post", "evidence": { @@ -1511,10 +1899,13 @@ } }, { + "claim_id": "clm_9ed79e0a0a7bc0ae34b1", "dimension": "effect", "value": "destructive", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "protocol_structure", + "policy_eligible": true, "source": "mcp_annotation", "source_pointer": "/paths/~1orders~1{order_id}~1cancel/post", "evidence": { @@ -1522,10 +1913,13 @@ } }, { + "claim_id": "clm_2b798cc5828bc13ecdb7", "dimension": "effect", "value": "write", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "protocol_structure", + "policy_eligible": true, "source": "openapi_method", "source_pointer": "/paths/~1orders~1{order_id}~1cancel/post", "evidence": { @@ -1533,10 +1927,13 @@ } }, { + "claim_id": "clm_23276120fa073ed6a21c", "dimension": "effect", "value": "destructive", "confidence": "medium", "provenance_kind": "keyword_heuristic", + "basis": "inferred_keyword", + "policy_eligible": false, "source": "risk_hint:keyword", "source_pointer": "/paths/~1orders~1{order_id}~1cancel/post", "evidence": { @@ -1558,10 +1955,13 @@ ], "claims": [ { + "claim_id": "clm_daa31c9b3b2b5dbd6e93", "dimension": "authority", "value": "scoped", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "reviewed_declaration", + "policy_eligible": true, "source": "action_surface_declaration", "source_pointer": "action_surface.actions[tool='shopify.cancel_order'].authority", "evidence": { @@ -1573,10 +1973,13 @@ } }, { + "claim_id": "clm_a6e1c41fd4ce119db816", "dimension": "authority", "value": "scoped", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "protocol_structure", + "policy_eligible": true, "source": "openapi_authority", "source_pointer": "/paths/~1orders~1{order_id}~1cancel/post", "evidence": { @@ -1588,10 +1991,13 @@ } }, { + "claim_id": "clm_6b221c1060349f334ae5", "dimension": "authority", "value": "scoped", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "protocol_structure", + "policy_eligible": true, "source": "openapi_security_alternative", "source_pointer": "/paths/~1orders~1{order_id}~1cancel/post/security/0", "evidence": { @@ -1649,10 +2055,13 @@ ], "claims": [ { + "claim_id": "clm_4f9e87f34a3612e16582", "dimension": "identity", "value": "obs_v1_2dc88a16ab175fa6d937d4fd2047076fa2a6dd20131796d31e71b9998386eb57", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "protocol_structure", + "policy_eligible": true, "source": "source_observation", "source_pointer": "/paths/~1refunds/post", "evidence": { @@ -1675,10 +2084,13 @@ ], "claims": [ { + "claim_id": "clm_cc55b0f2f90750432ea4", "dimension": "binding", "value": "agent_v1:7cb237a00d64b7400f4adc3b->tool_v2_8b559376c3236346b33a64fb9d460d342e6956579a68a0d5a331b2d2adf68ac8", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "reviewed_declaration", + "policy_eligible": true, "source": "agent_binding_declaration", "source_pointer": "/agent_bindings/declarations/0/tools/5", "evidence": { @@ -1703,10 +2115,13 @@ "confidence": "high", "claims": [ { + "claim_id": "clm_7748fa5e0a211a5ddf4c", "dimension": "effect", "value": "write", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "structural_scope", + "policy_eligible": true, "source": "action_scope", "source_pointer": "/paths/~1refunds/post", "evidence": { @@ -1714,19 +2129,25 @@ } }, { + "claim_id": "clm_019b1bf05a33e2269c31", "dimension": "effect", "value": "financial_write", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "reviewed_declaration", + "policy_eligible": true, "source": "action_surface_declaration", "source_pointer": "action_surface.actions[tool='stripe.create_refund'].effect", "evidence": {} }, { + "claim_id": "clm_5552183b9622f510f19c", "dimension": "effect", "value": "write", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "structural_scope", + "policy_eligible": true, "source": "auth_scope", "source_pointer": "/paths/~1refunds/post", "evidence": { @@ -1734,10 +2155,13 @@ } }, { + "claim_id": "clm_82bc01360271203cdf4a", "dimension": "effect", "value": "write", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "protocol_structure", + "policy_eligible": true, "source": "openapi_method", "source_pointer": "/paths/~1refunds/post", "evidence": { @@ -1745,10 +2169,13 @@ } }, { + "claim_id": "clm_085e955c58f7858e5e89", "dimension": "effect", "value": "financial_write", "confidence": "high", - "provenance_kind": "static_declaration", + "provenance_kind": "keyword_heuristic", + "basis": "inferred_keyword", + "policy_eligible": false, "source": "risk_hint:auth_scope", "source_pointer": "/paths/~1refunds/post", "evidence": { @@ -1761,10 +2188,13 @@ } }, { + "claim_id": "clm_1fee9ee6ea224541b56d", "dimension": "effect", "value": "external_communication", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "reviewed_declaration", + "policy_eligible": true, "source": "risk_hint:manual", "source_pointer": "/paths/~1refunds/post", "evidence": { @@ -1775,10 +2205,13 @@ } }, { + "claim_id": "clm_c8e45c8de58cf645c026", "dimension": "effect", "value": "financial_write", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "reviewed_declaration", + "policy_eligible": true, "source": "risk_hint:manual", "source_pointer": "/paths/~1refunds/post", "evidence": { @@ -1801,10 +2234,13 @@ ], "claims": [ { + "claim_id": "clm_fb25ed2f6f5f9f7963e3", "dimension": "authority", "value": "scoped", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "reviewed_declaration", + "policy_eligible": true, "source": "action_surface_declaration", "source_pointer": "action_surface.actions[tool='stripe.create_refund'].authority", "evidence": { @@ -1816,10 +2252,13 @@ } }, { + "claim_id": "clm_c87f8f49c702ca2de75b", "dimension": "authority", "value": "scoped", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "protocol_structure", + "policy_eligible": true, "source": "openapi_authority", "source_pointer": "/paths/~1refunds/post", "evidence": { @@ -1831,10 +2270,13 @@ } }, { + "claim_id": "clm_16a1ff9b04c06e9fa685", "dimension": "authority", "value": "scoped", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "protocol_structure", + "policy_eligible": true, "source": "openapi_security_alternative", "source_pointer": "/paths/~1refunds/post/security/0", "evidence": { @@ -1868,8 +2310,6 @@ "control_status": "missing", "related_findings": [ "fp_2cf0d6c77d9c3eee", - "fp_60fdf92126ba8844", - "fp_6f8e1ebab65f3607", "fp_973ea0ef2110ca9a", "fp_dfa27ad5b52d8fd6", "fp_e042ce7813b97a2d", @@ -1884,9 +2324,9 @@ "source_id": "support_mcp_tools", "source_ref": ".agents-shipgate/mcp-tools.json", "capability": "read_only", - "effect": "read", + "effect": "financial_write", "semantic_assessment": { - "conservative_effect": "read", + "conservative_effect": "financial_write", "identity": { "tool_id": "tool_v2_445a251aa6adc1c69f7b318b68f7ad47f01b629d3c24d723331a661735cd353a", "status": "structural", @@ -1898,10 +2338,13 @@ ], "claims": [ { + "claim_id": "clm_2069927e39cfc559e23d", "dimension": "identity", "value": "obs_v1_0df5fda3ee9f82ce968548fd66bcff348164c98aa8f25ca72cf8cea840ca4024", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "protocol_structure", + "policy_eligible": true, "source": "source_observation", "source_pointer": "/tools/0", "evidence": { @@ -1924,10 +2367,13 @@ ], "claims": [ { + "claim_id": "clm_814f5f934d8108e4f872", "dimension": "binding", "value": "agent_v1:7cb237a00d64b7400f4adc3b->tool_v2_445a251aa6adc1c69f7b318b68f7ad47f01b629d3c24d723331a661735cd353a", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "reviewed_declaration", + "policy_eligible": true, "source": "agent_binding_declaration", "source_pointer": "/agent_bindings/declarations/0/tools/0", "evidence": { @@ -1952,19 +2398,25 @@ "confidence": "high", "claims": [ { + "claim_id": "clm_386dd78b81e32aabd24f", "dimension": "effect", "value": "read", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "reviewed_declaration", + "policy_eligible": true, "source": "action_surface_declaration", "source_pointer": "action_surface.actions[tool='support.search_kb'].effect", "evidence": {} }, { + "claim_id": "clm_50c4d6abc43dbc7853c5", "dimension": "effect", "value": "read", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "protocol_structure", + "policy_eligible": true, "source": "mcp_annotation", "source_pointer": "/tools/0", "evidence": { @@ -1972,10 +2424,13 @@ } }, { + "claim_id": "clm_db44eeb09b0fff2d9e05", "dimension": "effect", "value": "financial_write", "confidence": "low", "provenance_kind": "keyword_heuristic", + "basis": "inferred_keyword", + "policy_eligible": false, "source": "risk_hint:keyword", "source_pointer": "/tools/0", "evidence": { @@ -1999,10 +2454,13 @@ ], "claims": [ { + "claim_id": "clm_943e9b080077bf3f7adb", "dimension": "authority", "value": "scoped", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "reviewed_declaration", + "policy_eligible": true, "source": "action_surface_declaration", "source_pointer": "action_surface.actions[tool='support.search_kb'].authority", "evidence": { @@ -2014,10 +2472,13 @@ } }, { + "claim_id": "clm_39039a28e82a637c05e5", "dimension": "authority", "value": "scoped", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "protocol_structure", + "policy_eligible": true, "source": "mcp_authority", "source_pointer": "/tools/0", "evidence": { @@ -2045,203 +2506,6 @@ "related_findings": [ "fp_24d610b0d4324190" ] - }, - { - "id": "cap_0da3f29d7a7c1ee8", - "tool_id": "tool_v2_aa488250dc1213a6a8bb76a7e695da294f93052f3181d42817b6cd2cf83cfa33", - "tool_name": "zendesk.update_ticket", - "source_type": "openapi", - "source_id": "support_openapi", - "source_ref": "specs/support-tools.openapi.yaml#/paths/~1tickets~1{ticket_id}/patch", - "capability": "write", - "effect": "write", - "semantic_assessment": { - "conservative_effect": "write", - "identity": { - "tool_id": "tool_v2_aa488250dc1213a6a8bb76a7e695da294f93052f3181d42817b6cd2cf83cfa33", - "status": "structural", - "provider": "support_openapi", - "binding_id": null, - "primary_observation_id": "obs_v1_0767af2d9bbe222adf666b94824bba1a94f3f2a146fa6d5198b2ef348da3fcc9", - "observation_ids": [ - "obs_v1_0767af2d9bbe222adf666b94824bba1a94f3f2a146fa6d5198b2ef348da3fcc9" - ], - "claims": [ - { - "dimension": "identity", - "value": "obs_v1_0767af2d9bbe222adf666b94824bba1a94f3f2a146fa6d5198b2ef348da3fcc9", - "confidence": "high", - "provenance_kind": "static_declaration", - "source": "source_observation", - "source_pointer": "/paths/~1tickets~1{ticket_id}/patch", - "evidence": { - "source_type": "openapi", - "source_id": "support_openapi", - "native_locator": "PATCH /tickets/{ticket_id}" - } - } - ], - "issues": [], - "pass_eligible": true - }, - "binding": { - "status": "declared", - "confidence": "high", - "root_agent_id": "agent_v1:7cb237a00d64b7400f4adc3b", - "reachable_path": [ - "agent_v1:7cb237a00d64b7400f4adc3b", - "tool_v2_aa488250dc1213a6a8bb76a7e695da294f93052f3181d42817b6cd2cf83cfa33" - ], - "claims": [ - { - "dimension": "binding", - "value": "agent_v1:7cb237a00d64b7400f4adc3b->tool_v2_aa488250dc1213a6a8bb76a7e695da294f93052f3181d42817b6cd2cf83cfa33", - "confidence": "high", - "provenance_kind": "static_declaration", - "source": "agent_binding_declaration", - "source_pointer": "/agent_bindings/declarations/0/tools/3", - "evidence": { - "edge_type": "direct_tool", - "complete": true - } - } - ], - "issues": [ - { - "kind": "conflicting_binding_evidence", - "dimension": "binding", - "message": "Closed-world declaration for 'root' does not match the complete structural tool set.", - "source": "shipgate.yaml", - "source_pointer": "/agent_bindings/declarations/0" - } - ], - "pass_eligible": false - }, - "effect": { - "status": "declared", - "confidence": "high", - "claims": [ - { - "dimension": "effect", - "value": "write", - "confidence": "high", - "provenance_kind": "static_declaration", - "source": "action_scope", - "source_pointer": "/paths/~1tickets~1{ticket_id}/patch", - "evidence": { - "scope": "zendesk:tickets:write" - } - }, - { - "dimension": "effect", - "value": "write", - "confidence": "high", - "provenance_kind": "static_declaration", - "source": "action_surface_declaration", - "source_pointer": "action_surface.actions[tool='zendesk.update_ticket'].effect", - "evidence": {} - }, - { - "dimension": "effect", - "value": "write", - "confidence": "high", - "provenance_kind": "static_declaration", - "source": "auth_scope", - "source_pointer": "/paths/~1tickets~1{ticket_id}/patch", - "evidence": { - "scope": "zendesk:tickets:write" - } - }, - { - "dimension": "effect", - "value": "write", - "confidence": "high", - "provenance_kind": "static_declaration", - "source": "openapi_method", - "source_pointer": "/paths/~1tickets~1{ticket_id}/patch", - "evidence": { - "method": "PATCH" - } - } - ], - "issues": [] - }, - "authority": { - "status": "declared", - "mode": "scoped", - "auth_type": "oauth2", - "credential_mode": "service_account", - "scopes": [ - "zendesk:tickets:write" - ], - "claims": [ - { - "dimension": "authority", - "value": "scoped", - "confidence": "high", - "provenance_kind": "static_declaration", - "source": "action_surface_declaration", - "source_pointer": "action_surface.actions[tool='zendesk.update_ticket'].authority", - "evidence": { - "auth_type": "oauth2", - "credential_mode": "service_account", - "scopes": [ - "zendesk:tickets:write" - ] - } - }, - { - "dimension": "authority", - "value": "scoped", - "confidence": "high", - "provenance_kind": "static_declaration", - "source": "openapi_authority", - "source_pointer": "/paths/~1tickets~1{ticket_id}/patch", - "evidence": { - "auth_type": "oauth2", - "scopes": [ - "zendesk:tickets:write" - ], - "alternative_count": 1 - } - }, - { - "dimension": "authority", - "value": "scoped", - "confidence": "high", - "provenance_kind": "static_declaration", - "source": "openapi_security_alternative", - "source_pointer": "/paths/~1tickets~1{ticket_id}/patch/security/0", - "evidence": { - "anonymous": false, - "schemes": [ - { - "name": "zendeskOAuth", - "type": "oauth2", - "scopes": [ - "zendesk:tickets:write" - ] - } - ] - } - } - ], - "issues": [] - }, - "pass_eligible": false - }, - "risk_tags": [ - "write" - ], - "auth_scopes": [ - "zendesk:tickets:write" - ], - "owner": null, - "included_reason": "referenced_by_high_finding", - "control_status": "partial", - "related_findings": [ - "fp_ea4f18e4707f8e1d" - ] } ], "declared_intentions": [ @@ -2363,24 +2627,6 @@ "gap": "stripe.create_refund has financial write capability without required controls.", "release_implication": "Human review is required to interpret this finding." }, - { - "id": "mis_75bd19ee834a918c", - "kind": "control_missing", - "severity": "high", - "tool_name": "gmail.send_customer_email", - "capability_refs": [ - "cap_3250ca3c56d98471" - ], - "intention_refs": [ - "int_1739e168eb9ad398" - ], - "finding_refs": [ - "fp_d58657d9b1c91a84" - ], - "policy_requirement": "Action-like tool inputs must constrain high-blast-radius fields.", - "gap": "gmail.send_customer_email accepts broad free-form action input.", - "release_implication": "Release reviewers cannot bound the operation payload safely." - }, { "id": "mis_5f51edc4792c19f9", "kind": "control_missing", @@ -2399,43 +2645,6 @@ "gap": "shopify.cancel_order is high-risk but has no owner.", "release_implication": "Release review metadata is incomplete or stale." }, - { - "id": "mis_7c7ef852951e58cd", - "kind": "control_missing", - "severity": "high", - "tool_name": "stripe.create_refund", - "capability_refs": [ - "cap_4df422ae44a17657" - ], - "intention_refs": [ - "int_004d51021dfae86d", - "int_07ec701205b21b96", - "int_1739e168eb9ad398", - "int_d7bbd0543afdd316" - ], - "finding_refs": [ - "fp_60fdf92126ba8844" - ], - "policy_requirement": "Risky numeric parameters must declare a maximum or equivalent limit.", - "gap": "stripe.create_refund.amount has no maximum bound.", - "release_implication": "Release reviewers cannot verify blast-radius limits." - }, - { - "id": "mis_7cbbb0b3afb9bf61", - "kind": "control_missing", - "severity": "high", - "tool_name": "zendesk.update_ticket", - "capability_refs": [ - "cap_0da3f29d7a7c1ee8" - ], - "intention_refs": [], - "finding_refs": [ - "fp_ea4f18e4707f8e1d" - ], - "policy_requirement": "Action-like tool inputs must constrain high-blast-radius fields.", - "gap": "zendesk.update_ticket accepts broad free-form action input.", - "release_implication": "Release reviewers cannot bound the operation payload safely." - }, { "id": "mis_223de69a145fe7a1", "kind": "policy_gap", @@ -2475,45 +2684,6 @@ "gap": "stripe.create_refund lacks a declared confirmation policy.", "release_implication": "Release review must verify explicit user confirmation before shipping." }, - { - "id": "mis_2a55ee8ed989b721", - "kind": "prohibited_action_present", - "severity": "high", - "tool_name": "gmail.send_customer_email", - "capability_refs": [ - "cap_3250ca3c56d98471" - ], - "intention_refs": [ - "int_1739e168eb9ad398" - ], - "finding_refs": [ - "fp_5faaaee860bcec8b" - ], - "policy_requirement": "Prohibited actions must not be contradicted by enabled capabilities.", - "gap": "gmail.send_customer_email appears to overlap with a prohibited action.", - "release_implication": "The tool surface appears to enable behavior the manifest prohibits." - }, - { - "id": "mis_0803964b2b9760b1", - "kind": "prohibited_action_present", - "severity": "high", - "tool_name": "stripe.create_refund", - "capability_refs": [ - "cap_4df422ae44a17657" - ], - "intention_refs": [ - "int_004d51021dfae86d", - "int_07ec701205b21b96", - "int_1739e168eb9ad398", - "int_d7bbd0543afdd316" - ], - "finding_refs": [ - "fp_6f8e1ebab65f3607" - ], - "policy_requirement": "Prohibited actions must not be contradicted by enabled capabilities.", - "gap": "stripe.create_refund appears to overlap with a prohibited action.", - "release_implication": "The tool surface appears to enable behavior the manifest prohibits." - }, { "id": "mis_ac533ab48d7c49ff", "kind": "scope_drift", @@ -2654,7 +2824,7 @@ "decision": "blocked", "summary": "5 release-relevant finding(s) map to active release blockers; resolve required controls or remove the capability.", "blocker_misalignment_count": 5, - "review_misalignment_count": 14, + "review_misalignment_count": 9, "fail_policy": { "ci_mode": "advisory", "fail_on": [], @@ -2681,30 +2851,13 @@ "id": "scn_e6b388512509ca5b", "scenario_type": "approval", "title": "Approval gate for high-risk action", - "given": "Exercise the release path for stripe.create_refund.", - "expected_control": "The run records human approval before the tool call and denies calls without approval.", - "source_misalignments": [ - "mis_a06283be39b5829a" - ], - "source_findings": [ - "fp_973ea0ef2110ca9a" - ] - }, - { - "id": "scn_912a4fadbef9b0d2", - "scenario_type": "schema_boundary", - "title": "Tool schema boundary check", - "given": "Exercise the release path for gmail.send_customer_email, stripe.create_refund, zendesk.update_ticket.", - "expected_control": "The tool accepts bounded structured inputs and returns structured outputs where needed.", + "given": "Exercise the release path for stripe.create_refund.", + "expected_control": "The run records human approval before the tool call and denies calls without approval.", "source_misalignments": [ - "mis_75bd19ee834a918c", - "mis_7c7ef852951e58cd", - "mis_7cbbb0b3afb9bf61" + "mis_a06283be39b5829a" ], "source_findings": [ - "fp_60fdf92126ba8844", - "fp_d58657d9b1c91a84", - "fp_ea4f18e4707f8e1d" + "fp_973ea0ef2110ca9a" ] }, { @@ -2735,21 +2888,6 @@ "fp_fae2921fd2d0cbd5" ] }, - { - "id": "scn_647fd7b7b0f95cc3", - "scenario_type": "prohibited_action", - "title": "Prohibited-action guard", - "given": "Exercise the release path for gmail.send_customer_email, stripe.create_refund.", - "expected_control": "The prohibited action is blocked, removed, or covered by the stated control.", - "source_misalignments": [ - "mis_0803964b2b9760b1", - "mis_2a55ee8ed989b721" - ], - "source_findings": [ - "fp_5faaaee860bcec8b", - "fp_6f8e1ebab65f3607" - ] - }, { "id": "scn_4371913f66998c95", "scenario_type": "least_privilege_scope", @@ -2776,7 +2914,7 @@ ], "tool_surface": { "total_tools": 7, - "high_risk_tools": 3, + "high_risk_tools": 5, "sources": { "mcp": 3, "openapi": 4 @@ -2834,7 +2972,7 @@ "input_schema": "f4cfd236c5b193ec", "output_schema": "44136fa355b3678a", "parameters": "4f1df264bfa081ee", - "annotations": "cbcbcf3476705e05" + "annotations": "ee3a832fe30bb286" } }, { @@ -3153,7 +3291,7 @@ ] }, "action_surface_facts": { - "snapshot_version": "0.3", + "snapshot_version": "0.4", "actions": [ { "action_id": "agent:support-refund-agent/refund-assistant:action_v2_053ad7751660e41c082a5e2abdf8c206eeb1336154bc764256a667121508143d", @@ -3185,10 +3323,13 @@ ], "claims": [ { + "claim_id": "clm_b891a97946af75e5184c", "dimension": "identity", "value": "obs_v1_0767af2d9bbe222adf666b94824bba1a94f3f2a146fa6d5198b2ef348da3fcc9", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "protocol_structure", + "policy_eligible": true, "source": "source_observation", "source_pointer": "/paths/~1tickets~1{ticket_id}/patch", "evidence": { @@ -3211,10 +3352,13 @@ ], "claims": [ { + "claim_id": "clm_ec0367c75ab7296716ec", "dimension": "binding", "value": "agent_v1:7cb237a00d64b7400f4adc3b->tool_v2_aa488250dc1213a6a8bb76a7e695da294f93052f3181d42817b6cd2cf83cfa33", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "reviewed_declaration", + "policy_eligible": true, "source": "agent_binding_declaration", "source_pointer": "/agent_bindings/declarations/0/tools/3", "evidence": { @@ -3239,10 +3383,13 @@ "confidence": "high", "claims": [ { + "claim_id": "clm_5df15421976a933fde44", "dimension": "effect", "value": "write", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "structural_scope", + "policy_eligible": true, "source": "action_scope", "source_pointer": "/paths/~1tickets~1{ticket_id}/patch", "evidence": { @@ -3250,19 +3397,25 @@ } }, { + "claim_id": "clm_1a3eca00dc1b01426fc9", "dimension": "effect", "value": "write", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "reviewed_declaration", + "policy_eligible": true, "source": "action_surface_declaration", "source_pointer": "action_surface.actions[tool='zendesk.update_ticket'].effect", "evidence": {} }, { + "claim_id": "clm_d1a0de7992b99f6c7213", "dimension": "effect", "value": "write", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "structural_scope", + "policy_eligible": true, "source": "auth_scope", "source_pointer": "/paths/~1tickets~1{ticket_id}/patch", "evidence": { @@ -3270,10 +3423,13 @@ } }, { + "claim_id": "clm_25b5a215b288b2acec14", "dimension": "effect", "value": "write", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "protocol_structure", + "policy_eligible": true, "source": "openapi_method", "source_pointer": "/paths/~1tickets~1{ticket_id}/patch", "evidence": { @@ -3293,10 +3449,13 @@ ], "claims": [ { + "claim_id": "clm_f331d8d5b8113ee080f4", "dimension": "authority", "value": "scoped", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "reviewed_declaration", + "policy_eligible": true, "source": "action_surface_declaration", "source_pointer": "action_surface.actions[tool='zendesk.update_ticket'].authority", "evidence": { @@ -3308,10 +3467,13 @@ } }, { + "claim_id": "clm_1e4cedf300862be020b7", "dimension": "authority", "value": "scoped", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "protocol_structure", + "policy_eligible": true, "source": "openapi_authority", "source_pointer": "/paths/~1tickets~1{ticket_id}/patch", "evidence": { @@ -3323,10 +3485,13 @@ } }, { + "claim_id": "clm_f50137eae5e02958d168", "dimension": "authority", "value": "scoped", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "protocol_structure", + "policy_eligible": true, "source": "openapi_security_alternative", "source_pointer": "/paths/~1tickets~1{ticket_id}/patch/security/0", "evidence": { @@ -3414,10 +3579,13 @@ ], "claims": [ { + "claim_id": "clm_87b20f443f41d8daa79a", "dimension": "identity", "value": "obs_v1_fc50761fdbf05db319a85b8ac0b828b8522967a4273d1c0f70d4cfabf0304300", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "protocol_structure", + "policy_eligible": true, "source": "source_observation", "source_pointer": "/paths/~1orders~1{order_id}~1cancel/post", "evidence": { @@ -3440,10 +3608,13 @@ ], "claims": [ { + "claim_id": "clm_e551bd6b7ec256d44d83", "dimension": "binding", "value": "agent_v1:7cb237a00d64b7400f4adc3b->tool_v2_08f6c9642ff3da07bd4dd33f949b747ad5fe79c245d1f76d450a81ff86cb1f6e", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "reviewed_declaration", + "policy_eligible": true, "source": "agent_binding_declaration", "source_pointer": "/agent_bindings/declarations/0/tools/4", "evidence": { @@ -3468,10 +3639,13 @@ "confidence": "high", "claims": [ { + "claim_id": "clm_eff630fcc9ed89e3934a", "dimension": "effect", "value": "write", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "structural_scope", + "policy_eligible": true, "source": "action_scope", "source_pointer": "/paths/~1orders~1{order_id}~1cancel/post", "evidence": { @@ -3479,19 +3653,25 @@ } }, { + "claim_id": "clm_952866402a040f319732", "dimension": "effect", "value": "destructive", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "reviewed_declaration", + "policy_eligible": true, "source": "action_surface_declaration", "source_pointer": "action_surface.actions[tool='shopify.cancel_order'].effect", "evidence": {} }, { + "claim_id": "clm_918a970310039b946dfb", "dimension": "effect", "value": "write", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "structural_scope", + "policy_eligible": true, "source": "auth_scope", "source_pointer": "/paths/~1orders~1{order_id}~1cancel/post", "evidence": { @@ -3499,10 +3679,13 @@ } }, { + "claim_id": "clm_9ed79e0a0a7bc0ae34b1", "dimension": "effect", "value": "destructive", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "protocol_structure", + "policy_eligible": true, "source": "mcp_annotation", "source_pointer": "/paths/~1orders~1{order_id}~1cancel/post", "evidence": { @@ -3510,10 +3693,13 @@ } }, { + "claim_id": "clm_2b798cc5828bc13ecdb7", "dimension": "effect", "value": "write", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "protocol_structure", + "policy_eligible": true, "source": "openapi_method", "source_pointer": "/paths/~1orders~1{order_id}~1cancel/post", "evidence": { @@ -3521,10 +3707,13 @@ } }, { + "claim_id": "clm_23276120fa073ed6a21c", "dimension": "effect", "value": "destructive", "confidence": "medium", "provenance_kind": "keyword_heuristic", + "basis": "inferred_keyword", + "policy_eligible": false, "source": "risk_hint:keyword", "source_pointer": "/paths/~1orders~1{order_id}~1cancel/post", "evidence": { @@ -3546,10 +3735,13 @@ ], "claims": [ { + "claim_id": "clm_daa31c9b3b2b5dbd6e93", "dimension": "authority", "value": "scoped", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "reviewed_declaration", + "policy_eligible": true, "source": "action_surface_declaration", "source_pointer": "action_surface.actions[tool='shopify.cancel_order'].authority", "evidence": { @@ -3561,10 +3753,13 @@ } }, { + "claim_id": "clm_a6e1c41fd4ce119db816", "dimension": "authority", "value": "scoped", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "protocol_structure", + "policy_eligible": true, "source": "openapi_authority", "source_pointer": "/paths/~1orders~1{order_id}~1cancel/post", "evidence": { @@ -3576,10 +3771,13 @@ } }, { + "claim_id": "clm_6b221c1060349f334ae5", "dimension": "authority", "value": "scoped", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "protocol_structure", + "policy_eligible": true, "source": "openapi_security_alternative", "source_pointer": "/paths/~1orders~1{order_id}~1cancel/post/security/0", "evidence": { @@ -3668,10 +3866,13 @@ ], "claims": [ { + "claim_id": "clm_e016125bf4102d2231d3", "dimension": "identity", "value": "obs_v1_6a598e295d2d845f05a72faecd97fcf971b67c96a0aad092d9082c8e573df553", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "protocol_structure", + "policy_eligible": true, "source": "source_observation", "source_pointer": "/tools/1", "evidence": { @@ -3694,10 +3895,13 @@ ], "claims": [ { + "claim_id": "clm_1d843e3958c618456a2f", "dimension": "binding", "value": "agent_v1:7cb237a00d64b7400f4adc3b->tool_v2_c69ebf76910efa80a2024a15d836f20366effdbd12f7a69b660ce4f4e5870ed9", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "reviewed_declaration", + "policy_eligible": true, "source": "agent_binding_declaration", "source_pointer": "/agent_bindings/declarations/0/tools/1", "evidence": { @@ -3722,10 +3926,13 @@ "confidence": "high", "claims": [ { + "claim_id": "clm_a4818bc49faaf8f1ea61", "dimension": "effect", "value": "write", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "structural_scope", + "policy_eligible": true, "source": "action_scope", "source_pointer": "/tools/1", "evidence": { @@ -3733,19 +3940,25 @@ } }, { + "claim_id": "clm_e6c199e33c2159754789", "dimension": "effect", "value": "external_communication", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "reviewed_declaration", + "policy_eligible": true, "source": "action_surface_declaration", "source_pointer": "action_surface.actions[tool='gmail.send_customer_email'].effect", "evidence": {} }, { + "claim_id": "clm_26ae80b049a84be11ab1", "dimension": "effect", "value": "write", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "structural_scope", + "policy_eligible": true, "source": "auth_scope", "source_pointer": "/tools/1", "evidence": { @@ -3753,10 +3966,13 @@ } }, { + "claim_id": "clm_f0196c0c4caab8cd8ecd", "dimension": "effect", "value": "external_communication", "confidence": "high", "provenance_kind": "keyword_heuristic", + "basis": "inferred_keyword", + "policy_eligible": false, "source": "risk_hint:keyword", "source_pointer": "/tools/1", "evidence": { @@ -3765,10 +3981,13 @@ } }, { + "claim_id": "clm_b56eed6a901658bae650", "dimension": "effect", "value": "external_communication", "confidence": "high", "provenance_kind": "keyword_heuristic", + "basis": "inferred_keyword", + "policy_eligible": false, "source": "risk_hint:keyword", "source_pointer": "/tools/1", "evidence": { @@ -3777,10 +3996,13 @@ } }, { + "claim_id": "clm_7ec6f046c34a18b719f2", "dimension": "effect", "value": "external_communication", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "reviewed_declaration", + "policy_eligible": true, "source": "risk_hint:manual", "source_pointer": "/tools/1", "evidence": { @@ -3791,10 +4013,13 @@ } }, { + "claim_id": "clm_188aecba394542ac226f", "dimension": "effect", "value": "external_communication", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "reviewed_declaration", + "policy_eligible": true, "source": "risk_hint:manual", "source_pointer": "/tools/1", "evidence": { @@ -3817,10 +4042,13 @@ ], "claims": [ { + "claim_id": "clm_932e397ceff5ab3cb4df", "dimension": "authority", "value": "scoped", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "reviewed_declaration", + "policy_eligible": true, "source": "action_surface_declaration", "source_pointer": "action_surface.actions[tool='gmail.send_customer_email'].authority", "evidence": { @@ -3832,10 +4060,13 @@ } }, { + "claim_id": "clm_c1c0614054dbba4bfa4e", "dimension": "authority", "value": "scoped", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "protocol_structure", + "policy_eligible": true, "source": "mcp_authority", "source_pointer": "/tools/1", "evidence": { @@ -3921,10 +4152,13 @@ ], "claims": [ { + "claim_id": "clm_ee6f9f9b928e98d8e34f", "dimension": "identity", "value": "obs_v1_abe0748ca63b6b1f48d758ee5395281d80ad99b49e4766dda9f717c57aa6a3c0", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "protocol_structure", + "policy_eligible": true, "source": "source_observation", "source_pointer": "/paths/~1refunds~1{payment_id}~1status/get", "evidence": { @@ -3947,10 +4181,13 @@ ], "claims": [ { + "claim_id": "clm_f55c3a2e30069f6699fb", "dimension": "binding", "value": "agent_v1:7cb237a00d64b7400f4adc3b->tool_v2_61891eb768365da25408e00518646b94299be76989cba28407f47015112f86be", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "reviewed_declaration", + "policy_eligible": true, "source": "agent_binding_declaration", "source_pointer": "/agent_bindings/declarations/0/tools/2", "evidence": { @@ -3975,19 +4212,25 @@ "confidence": "high", "claims": [ { + "claim_id": "clm_e94b0e48a585e4b07dca", "dimension": "effect", "value": "read", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "reviewed_declaration", + "policy_eligible": true, "source": "action_surface_declaration", "source_pointer": "action_surface.actions[tool='refund_status_lookup'].effect", "evidence": {} }, { + "claim_id": "clm_bdc89f8aeb4887e717f4", "dimension": "effect", "value": "read", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "protocol_structure", + "policy_eligible": true, "source": "mcp_annotation", "source_pointer": "/paths/~1refunds~1{payment_id}~1status/get", "evidence": { @@ -3995,10 +4238,13 @@ } }, { + "claim_id": "clm_53c53a5e6459571424da", "dimension": "effect", "value": "read", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "protocol_structure", + "policy_eligible": true, "source": "openapi_method", "source_pointer": "/paths/~1refunds~1{payment_id}~1status/get", "evidence": { @@ -4006,10 +4252,13 @@ } }, { + "claim_id": "clm_834a693cde6cc06f269b", "dimension": "effect", "value": "read", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "reviewed_declaration", + "policy_eligible": true, "source": "risk_hint:manual", "source_pointer": "/paths/~1refunds~1{payment_id}~1status/get", "evidence": { @@ -4030,10 +4279,13 @@ "scopes": [], "claims": [ { + "claim_id": "clm_da401a8a8ab43662894f", "dimension": "authority", "value": "unscoped", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "reviewed_declaration", + "policy_eligible": true, "source": "action_surface_declaration", "source_pointer": "action_surface.actions[tool='refund_status_lookup'].authority", "evidence": { @@ -4043,10 +4295,13 @@ } }, { + "claim_id": "clm_aa0bf67594fae796aa00", "dimension": "authority", "value": "unscoped", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "protocol_structure", + "policy_eligible": true, "source": "openapi_authority", "source_pointer": "/paths/~1refunds~1{payment_id}~1status/get", "evidence": { @@ -4056,10 +4311,13 @@ } }, { + "claim_id": "clm_40cc6737ceee31a1ac60", "dimension": "authority", "value": "unscoped", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "protocol_structure", + "policy_eligible": true, "source": "openapi_security_alternative", "source_pointer": "/paths/~1refunds~1{payment_id}~1status/get/security/0", "evidence": { @@ -4127,9 +4385,9 @@ "source_start_column": null, "source_pointer": "/tools/0", "operation": "support.search_kb", - "effect": "read", + "effect": "financial_write", "semantic_assessment": { - "conservative_effect": "read", + "conservative_effect": "financial_write", "identity": { "tool_id": "tool_v2_445a251aa6adc1c69f7b318b68f7ad47f01b629d3c24d723331a661735cd353a", "status": "structural", @@ -4141,10 +4399,13 @@ ], "claims": [ { + "claim_id": "clm_2069927e39cfc559e23d", "dimension": "identity", "value": "obs_v1_0df5fda3ee9f82ce968548fd66bcff348164c98aa8f25ca72cf8cea840ca4024", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "protocol_structure", + "policy_eligible": true, "source": "source_observation", "source_pointer": "/tools/0", "evidence": { @@ -4167,10 +4428,13 @@ ], "claims": [ { + "claim_id": "clm_814f5f934d8108e4f872", "dimension": "binding", "value": "agent_v1:7cb237a00d64b7400f4adc3b->tool_v2_445a251aa6adc1c69f7b318b68f7ad47f01b629d3c24d723331a661735cd353a", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "reviewed_declaration", + "policy_eligible": true, "source": "agent_binding_declaration", "source_pointer": "/agent_bindings/declarations/0/tools/0", "evidence": { @@ -4195,19 +4459,25 @@ "confidence": "high", "claims": [ { + "claim_id": "clm_386dd78b81e32aabd24f", "dimension": "effect", "value": "read", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "reviewed_declaration", + "policy_eligible": true, "source": "action_surface_declaration", "source_pointer": "action_surface.actions[tool='support.search_kb'].effect", "evidence": {} }, { + "claim_id": "clm_50c4d6abc43dbc7853c5", "dimension": "effect", "value": "read", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "protocol_structure", + "policy_eligible": true, "source": "mcp_annotation", "source_pointer": "/tools/0", "evidence": { @@ -4215,10 +4485,13 @@ } }, { + "claim_id": "clm_db44eeb09b0fff2d9e05", "dimension": "effect", "value": "financial_write", "confidence": "low", "provenance_kind": "keyword_heuristic", + "basis": "inferred_keyword", + "policy_eligible": false, "source": "risk_hint:keyword", "source_pointer": "/tools/0", "evidence": { @@ -4242,10 +4515,13 @@ ], "claims": [ { + "claim_id": "clm_943e9b080077bf3f7adb", "dimension": "authority", "value": "scoped", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "reviewed_declaration", + "policy_eligible": true, "source": "action_surface_declaration", "source_pointer": "action_surface.actions[tool='support.search_kb'].authority", "evidence": { @@ -4257,10 +4533,13 @@ } }, { + "claim_id": "clm_39039a28e82a637c05e5", "dimension": "authority", "value": "scoped", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "protocol_structure", + "policy_eligible": true, "source": "mcp_authority", "source_pointer": "/tools/0", "evidence": { @@ -4309,7 +4588,7 @@ "identity_hash": "5efa3b9b2c6b92cb", "schema_hash": "a04fa83b2b4e95b6", "policy_hash": "1e20e61c1e17120f", - "risk_hash": "e3ad1ce09d90db6b" + "risk_hash": "05c2a00c8cf607ea" } }, { @@ -4342,10 +4621,13 @@ ], "claims": [ { + "claim_id": "clm_4f9e87f34a3612e16582", "dimension": "identity", "value": "obs_v1_2dc88a16ab175fa6d937d4fd2047076fa2a6dd20131796d31e71b9998386eb57", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "protocol_structure", + "policy_eligible": true, "source": "source_observation", "source_pointer": "/paths/~1refunds/post", "evidence": { @@ -4368,10 +4650,13 @@ ], "claims": [ { + "claim_id": "clm_cc55b0f2f90750432ea4", "dimension": "binding", "value": "agent_v1:7cb237a00d64b7400f4adc3b->tool_v2_8b559376c3236346b33a64fb9d460d342e6956579a68a0d5a331b2d2adf68ac8", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "reviewed_declaration", + "policy_eligible": true, "source": "agent_binding_declaration", "source_pointer": "/agent_bindings/declarations/0/tools/5", "evidence": { @@ -4396,10 +4681,13 @@ "confidence": "high", "claims": [ { + "claim_id": "clm_7748fa5e0a211a5ddf4c", "dimension": "effect", "value": "write", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "structural_scope", + "policy_eligible": true, "source": "action_scope", "source_pointer": "/paths/~1refunds/post", "evidence": { @@ -4407,19 +4695,25 @@ } }, { + "claim_id": "clm_019b1bf05a33e2269c31", "dimension": "effect", "value": "financial_write", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "reviewed_declaration", + "policy_eligible": true, "source": "action_surface_declaration", "source_pointer": "action_surface.actions[tool='stripe.create_refund'].effect", "evidence": {} }, { + "claim_id": "clm_5552183b9622f510f19c", "dimension": "effect", "value": "write", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "structural_scope", + "policy_eligible": true, "source": "auth_scope", "source_pointer": "/paths/~1refunds/post", "evidence": { @@ -4427,10 +4721,13 @@ } }, { + "claim_id": "clm_82bc01360271203cdf4a", "dimension": "effect", "value": "write", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "protocol_structure", + "policy_eligible": true, "source": "openapi_method", "source_pointer": "/paths/~1refunds/post", "evidence": { @@ -4438,10 +4735,13 @@ } }, { + "claim_id": "clm_085e955c58f7858e5e89", "dimension": "effect", "value": "financial_write", "confidence": "high", - "provenance_kind": "static_declaration", + "provenance_kind": "keyword_heuristic", + "basis": "inferred_keyword", + "policy_eligible": false, "source": "risk_hint:auth_scope", "source_pointer": "/paths/~1refunds/post", "evidence": { @@ -4454,10 +4754,13 @@ } }, { + "claim_id": "clm_1fee9ee6ea224541b56d", "dimension": "effect", "value": "external_communication", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "reviewed_declaration", + "policy_eligible": true, "source": "risk_hint:manual", "source_pointer": "/paths/~1refunds/post", "evidence": { @@ -4468,10 +4771,13 @@ } }, { + "claim_id": "clm_c8e45c8de58cf645c026", "dimension": "effect", "value": "financial_write", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "reviewed_declaration", + "policy_eligible": true, "source": "risk_hint:manual", "source_pointer": "/paths/~1refunds/post", "evidence": { @@ -4494,10 +4800,13 @@ ], "claims": [ { + "claim_id": "clm_fb25ed2f6f5f9f7963e3", "dimension": "authority", "value": "scoped", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "reviewed_declaration", + "policy_eligible": true, "source": "action_surface_declaration", "source_pointer": "action_surface.actions[tool='stripe.create_refund'].authority", "evidence": { @@ -4509,10 +4818,13 @@ } }, { + "claim_id": "clm_c87f8f49c702ca2de75b", "dimension": "authority", "value": "scoped", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "protocol_structure", + "policy_eligible": true, "source": "openapi_authority", "source_pointer": "/paths/~1refunds/post", "evidence": { @@ -4524,10 +4836,13 @@ } }, { + "claim_id": "clm_16a1ff9b04c06e9fa685", "dimension": "authority", "value": "scoped", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "protocol_structure", + "policy_eligible": true, "source": "openapi_security_alternative", "source_pointer": "/paths/~1refunds/post/security/0", "evidence": { @@ -4605,9 +4920,9 @@ "source_start_column": null, "source_pointer": "/tools/0", "operation": "send_email_preview", - "effect": "read", + "effect": "external_communication", "semantic_assessment": { - "conservative_effect": "read", + "conservative_effect": "external_communication", "identity": { "tool_id": "tool_v2_2c9ee6ae2b9f0259ba2dad40a30fb18a8bdc3a732ffc207936fdd49c80e1bc9e", "status": "declared", @@ -4620,10 +4935,13 @@ ], "claims": [ { + "claim_id": "clm_de7969c4a04dff4a5514", "dimension": "identity", "value": "obs_v1_68344b9fde158f18db1f3476cf06003414968e6d82488d8f376eebfecbc8bfb3", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "reviewed_declaration", + "policy_eligible": true, "source": "tool_identity_binding", "source_pointer": "agents/refund_agent.py", "evidence": { @@ -4633,10 +4951,13 @@ } }, { + "claim_id": "clm_dba9cd1e78c894219426", "dimension": "identity", "value": "obs_v1_8835a4a63d13e804fae8ab9c175ece24e865d923e65457698593c0532254b0a6", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "reviewed_declaration", + "policy_eligible": true, "source": "tool_identity_binding", "source_pointer": "/tools/0", "evidence": { @@ -4659,10 +4980,13 @@ ], "claims": [ { + "claim_id": "clm_38bd6ab43966135164c2", "dimension": "binding", "value": "agent_v1:7cb237a00d64b7400f4adc3b->tool_v2_2c9ee6ae2b9f0259ba2dad40a30fb18a8bdc3a732ffc207936fdd49c80e1bc9e", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "reviewed_declaration", + "policy_eligible": true, "source": "agent_binding_declaration", "source_pointer": "/agent_bindings/declarations/0/tools/6", "evidence": { @@ -4671,10 +4995,13 @@ } }, { + "claim_id": "clm_c5e560fe7f75b8b49ce7", "dimension": "binding", "value": "agent_v1:7cb237a00d64b7400f4adc3b->tool_v2_2c9ee6ae2b9f0259ba2dad40a30fb18a8bdc3a732ffc207936fdd49c80e1bc9e", "confidence": "high", "provenance_kind": "ast_extraction", + "basis": "typed_provider_fact", + "policy_eligible": true, "source": "agents/refund_agent.py", "source_pointer": "agents/refund_agent.py:10", "evidence": { @@ -4699,19 +5026,25 @@ "confidence": "high", "claims": [ { + "claim_id": "clm_d147c0c853cf0af5ae61", "dimension": "effect", "value": "read", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "reviewed_declaration", + "policy_eligible": true, "source": "action_surface_declaration", "source_pointer": "action_surface.actions[tool='send_email_preview'].effect", "evidence": {} }, { + "claim_id": "clm_50c4d6abc43dbc7853c5", "dimension": "effect", "value": "read", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "protocol_structure", + "policy_eligible": true, "source": "mcp_annotation", "source_pointer": "/tools/0", "evidence": { @@ -4719,10 +5052,13 @@ } }, { + "claim_id": "clm_cfc3dd4156cdbd1469ed", "dimension": "effect", "value": "external_communication", "confidence": "low", "provenance_kind": "keyword_heuristic", + "basis": "inferred_keyword", + "policy_eligible": false, "source": "risk_hint:keyword", "source_pointer": "/tools/0", "evidence": { @@ -4731,10 +5067,13 @@ } }, { + "claim_id": "clm_b77048e00c8bab300132", "dimension": "effect", "value": "external_communication", "confidence": "low", "provenance_kind": "keyword_heuristic", + "basis": "inferred_keyword", + "policy_eligible": false, "source": "risk_hint:keyword", "source_pointer": "/tools/0", "evidence": { @@ -4753,10 +5092,13 @@ "scopes": [], "claims": [ { + "claim_id": "clm_6a7e0f2012d1e4cee027", "dimension": "authority", "value": "none", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "reviewed_declaration", + "policy_eligible": true, "source": "action_surface_declaration", "source_pointer": "action_surface.actions[tool='send_email_preview'].authority", "evidence": { @@ -4766,10 +5108,13 @@ } }, { + "claim_id": "clm_b1bf872bb994c8ec4e1c", "dimension": "authority", "value": "none", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "protocol_structure", + "policy_eligible": true, "source": "mcp_authority", "source_pointer": "/tools/0", "evidence": { @@ -4818,7 +5163,7 @@ "identity_hash": "7115b0a0fbf6dea6", "schema_hash": "a118ab43d7a42fa2", "policy_hash": "99aa27a5e2ab49d6", - "risk_hash": "a2ddef8c5875b92d" + "risk_hash": "ea7fc5b0104ac208" } } ] @@ -5002,127 +5347,6 @@ "codex_plugin_surface": null, "baseline": null, "findings": [ - { - "id": "fp_60fdf92126ba8844", - "fingerprint": "fp_60fdf92126ba8844", - "check_id": "SHIP-SCHEMA-MISSING-BOUNDS", - "title": "stripe.create_refund.amount has no maximum bound", - "severity": "high", - "category": "schema", - "tool_id": "tool_v2_8b559376c3236346b33a64fb9d460d342e6956579a68a0d5a331b2d2adf68ac8", - "tool_name": "stripe.create_refund", - "agent_id": "agent:support-refund-agent/refund-assistant", - "evidence": { - "parameter": "amount", - "type": "number" - }, - "confidence": "high", - "provenance_kind": "keyword_heuristic", - "source": { - "type": "openapi", - "ref": "specs/support-tools.openapi.yaml#/paths/~1refunds/post", - "location": null, - "path": "specs/support-tools.openapi.yaml", - "start_line": 97, - "start_column": 5, - "pointer": "/paths/~1refunds/post" - }, - "policy_evidence_source": null, - "capability_refs": [], - "capability_policy_evidence": null, - "policy_routing": null, - "capability_trace_refs": [], - "recommendation": "Add a maximum bound to stripe.create_refund.amount or document an equivalent limit in the tool policy.", - "blocks_release": false, - "suppressed": false, - "suppression_reason": null, - "baseline_status": null, - "autofix_safe": false, - "requires_human_review": true, - "suggested_patch_kind": "manual", - "docs_url": "https://github.com/ThreeMoonsLab/agents-shipgate/blob/main/docs/checks.md#ship-schema-missing-bounds", - "agent_action": "escalate_to_human" - }, - { - "id": "fp_ea4f18e4707f8e1d", - "fingerprint": "fp_ea4f18e4707f8e1d", - "check_id": "SHIP-SCHEMA-BROAD-FREE-TEXT", - "title": "zendesk.update_ticket accepts broad free-form action input", - "severity": "high", - "category": "schema", - "tool_id": "tool_v2_aa488250dc1213a6a8bb76a7e695da294f93052f3181d42817b6cd2cf83cfa33", - "tool_name": "zendesk.update_ticket", - "agent_id": "agent:support-refund-agent/refund-assistant", - "evidence": { - "parameter": "updates", - "type": "object" - }, - "confidence": "medium", - "provenance_kind": "keyword_heuristic", - "source": { - "type": "openapi", - "ref": "specs/support-tools.openapi.yaml#/paths/~1tickets~1{ticket_id}/patch", - "location": null, - "path": "specs/support-tools.openapi.yaml", - "start_line": 142, - "start_column": 5, - "pointer": "/paths/~1tickets~1{ticket_id}/patch" - }, - "policy_evidence_source": null, - "capability_refs": [], - "capability_policy_evidence": null, - "policy_routing": null, - "capability_trace_refs": [], - "recommendation": "Constrain zendesk.update_ticket.updates with an enum, structured schema, or narrower field-specific parameters.", - "blocks_release": false, - "suppressed": false, - "suppression_reason": null, - "baseline_status": null, - "autofix_safe": false, - "requires_human_review": true, - "suggested_patch_kind": "manual", - "docs_url": "https://github.com/ThreeMoonsLab/agents-shipgate/blob/main/docs/checks.md#ship-schema-broad-free-text", - "agent_action": "escalate_to_human" - }, - { - "id": "fp_d58657d9b1c91a84", - "fingerprint": "fp_d58657d9b1c91a84", - "check_id": "SHIP-SCHEMA-BROAD-FREE-TEXT", - "title": "gmail.send_customer_email accepts broad free-form action input", - "severity": "high", - "category": "schema", - "tool_id": "tool_v2_c69ebf76910efa80a2024a15d836f20366effdbd12f7a69b660ce4f4e5870ed9", - "tool_name": "gmail.send_customer_email", - "agent_id": "agent:support-refund-agent/refund-assistant", - "evidence": { - "parameter": "body", - "type": "string" - }, - "confidence": "medium", - "provenance_kind": "keyword_heuristic", - "source": { - "type": "mcp", - "ref": ".agents-shipgate/mcp-tools.json", - "location": null, - "path": ".agents-shipgate/mcp-tools.json", - "pointer": "/tools/1" - }, - "policy_evidence_source": null, - "capability_refs": [], - "capability_policy_evidence": null, - "policy_routing": null, - "capability_trace_refs": [], - "recommendation": "Constrain gmail.send_customer_email.body with an enum, structured schema, or narrower field-specific parameters.", - "blocks_release": false, - "suppressed": false, - "suppression_reason": null, - "baseline_status": null, - "autofix_safe": false, - "requires_human_review": true, - "suggested_patch_kind": "manual", - "docs_url": "https://github.com/ThreeMoonsLab/agents-shipgate/blob/main/docs/checks.md#ship-schema-broad-free-text", - "agent_action": "escalate_to_human" - }, { "id": "fp_df4a990cca9f936b", "fingerprint": "fp_df4a990cca9f936b", @@ -5152,6 +5376,7 @@ "capability_refs": [], "capability_policy_evidence": null, "policy_routing": null, + "support": null, "capability_trace_refs": [], "recommendation": "Replace broad manifest permission scopes with the narrowest scopes needed for this release.", "blocks_release": false, @@ -5202,6 +5427,7 @@ "capability_refs": [], "capability_policy_evidence": null, "policy_routing": null, + "support": null, "capability_trace_refs": [], "recommendation": "Add the required scopes for shopify.cancel_order to permissions.scopes or narrow the tool's declared auth requirements.", "blocks_release": false, @@ -5250,6 +5476,7 @@ "capability_refs": [], "capability_policy_evidence": null, "policy_routing": null, + "support": null, "capability_trace_refs": [], "recommendation": "Add the required scopes for support.search_kb to permissions.scopes or narrow the tool's declared auth requirements.", "blocks_release": false, @@ -5292,6 +5519,7 @@ "capability_refs": [], "capability_policy_evidence": null, "policy_routing": null, + "support": null, "capability_trace_refs": [], "recommendation": "Declare operation-specific auth scopes for refund_status_lookup, or explicitly declare anonymous authority when the operation requires no credentials.", "blocks_release": false, @@ -5340,6 +5568,7 @@ "capability_refs": [], "capability_policy_evidence": null, "policy_routing": null, + "support": null, "capability_trace_refs": [], "recommendation": "Add the required scopes for gmail.send_customer_email to permissions.scopes or narrow the tool's declared auth requirements.", "blocks_release": false, @@ -5352,111 +5581,6 @@ "docs_url": "https://github.com/ThreeMoonsLab/agents-shipgate/blob/main/docs/checks.md#ship-auth-scope-coverage-missing", "agent_action": "escalate_to_human" }, - { - "id": "fp_6f8e1ebab65f3607", - "fingerprint": "fp_6f8e1ebab65f3607", - "check_id": "SHIP-SCOPE-PROHIBITED-TOOL-PRESENT", - "title": "stripe.create_refund appears to overlap with a prohibited action", - "severity": "high", - "category": "scope", - "tool_id": "tool_v2_8b559376c3236346b33a64fb9d460d342e6956579a68a0d5a331b2d2adf68ac8", - "tool_name": "stripe.create_refund", - "agent_id": "agent:support-refund-agent/refund-assistant", - "evidence": { - "prohibited_action": "issue refund without approval", - "risk_tags": [ - "external_write", - "financial_action", - "write" - ] - }, - "confidence": "medium", - "provenance_kind": "keyword_heuristic", - "source": { - "type": "openapi", - "ref": "specs/support-tools.openapi.yaml#/paths/~1refunds/post", - "location": null, - "path": "specs/support-tools.openapi.yaml", - "start_line": 97, - "start_column": 5, - "pointer": "/paths/~1refunds/post" - }, - "policy_evidence_source": { - "type": "manifest", - "ref": "shipgate.yaml#/agent/prohibited_actions/0", - "location": null, - "path": "shipgate.yaml", - "start_line": 22, - "end_line": null, - "start_column": null, - "pointer": "/agent/prohibited_actions/0" - }, - "capability_refs": [], - "capability_policy_evidence": null, - "policy_routing": null, - "capability_trace_refs": [], - "recommendation": "Remove stripe.create_refund, narrow its policy, or revise prohibited_actions so the manifest and tool surface do not contradict each other.", - "blocks_release": false, - "suppressed": false, - "suppression_reason": null, - "baseline_status": null, - "autofix_safe": false, - "requires_human_review": true, - "suggested_patch_kind": "manual", - "docs_url": "https://github.com/ThreeMoonsLab/agents-shipgate/blob/main/docs/checks.md#ship-scope-prohibited-tool-present", - "agent_action": "escalate_to_human" - }, - { - "id": "fp_5faaaee860bcec8b", - "fingerprint": "fp_5faaaee860bcec8b", - "check_id": "SHIP-SCOPE-PROHIBITED-TOOL-PRESENT", - "title": "gmail.send_customer_email appears to overlap with a prohibited action", - "severity": "high", - "category": "scope", - "tool_id": "tool_v2_c69ebf76910efa80a2024a15d836f20366effdbd12f7a69b660ce4f4e5870ed9", - "tool_name": "gmail.send_customer_email", - "agent_id": "agent:support-refund-agent/refund-assistant", - "evidence": { - "prohibited_action": "send external email without preview", - "risk_tags": [ - "customer_communication", - "external_write" - ] - }, - "confidence": "medium", - "provenance_kind": "keyword_heuristic", - "source": { - "type": "mcp", - "ref": ".agents-shipgate/mcp-tools.json", - "location": null, - "path": ".agents-shipgate/mcp-tools.json", - "pointer": "/tools/1" - }, - "policy_evidence_source": { - "type": "manifest", - "ref": "shipgate.yaml#/agent/prohibited_actions/2", - "location": null, - "path": "shipgate.yaml", - "start_line": 24, - "end_line": null, - "start_column": null, - "pointer": "/agent/prohibited_actions/2" - }, - "capability_refs": [], - "capability_policy_evidence": null, - "policy_routing": null, - "capability_trace_refs": [], - "recommendation": "Remove gmail.send_customer_email, narrow its policy, or revise prohibited_actions so the manifest and tool surface do not contradict each other.", - "blocks_release": false, - "suppressed": false, - "suppression_reason": null, - "baseline_status": null, - "autofix_safe": false, - "requires_human_review": true, - "suggested_patch_kind": "manual", - "docs_url": "https://github.com/ThreeMoonsLab/agents-shipgate/blob/main/docs/checks.md#ship-scope-prohibited-tool-present", - "agent_action": "escalate_to_human" - }, { "id": "fp_973ea0ef2110ca9a", "fingerprint": "fp_973ea0ef2110ca9a", @@ -5551,13 +5675,13 @@ }, "hashes": { "identity_hash": "578e492c26fd4656", - "binding_hash": "3797b951d702db48", + "binding_hash": "9c630695195670c9", "effect_hash": "3c5583f4a13cb1e3", "authority_hash": "fd53df901af7c2ec", "control_hash": "66ca58b2f8d2e5bb", "schema_hash": "81d9efbadca5ea8c", "risk_hash": "b9302728ac0d5ac2", - "evidence_hash": "a1876a84a6357d6c" + "evidence_hash": "2a9fc4c06f18e80b" }, "matched_predicates": { "missing_approval_policy": true, @@ -5577,6 +5701,7 @@ } }, "policy_routing": null, + "support": null, "capability_trace_refs": [], "recommendation": "Declare an approval policy for stripe.create_refund or remove this tool from the release.", "blocks_release": false, @@ -5683,13 +5808,13 @@ }, "hashes": { "identity_hash": "578e492c26fd4656", - "binding_hash": "3797b951d702db48", + "binding_hash": "9c630695195670c9", "effect_hash": "3c5583f4a13cb1e3", "authority_hash": "fd53df901af7c2ec", "control_hash": "66ca58b2f8d2e5bb", "schema_hash": "81d9efbadca5ea8c", "risk_hash": "b9302728ac0d5ac2", - "evidence_hash": "a1876a84a6357d6c" + "evidence_hash": "2a9fc4c06f18e80b" }, "matched_predicates": { "missing_confirmation_policy": true, @@ -5709,6 +5834,7 @@ } }, "policy_routing": null, + "support": null, "capability_trace_refs": [], "recommendation": "Declare a user confirmation policy for stripe.create_refund or remove this action from the release.", "blocks_release": false, @@ -5810,13 +5936,13 @@ }, "hashes": { "identity_hash": "e9991ea05213172f", - "binding_hash": "bc550fe7894d443e", + "binding_hash": "0afaf4d4df4c4348", "effect_hash": "0b01d252d3c04ac9", "authority_hash": "4fac84f970e42bce", "control_hash": "1230dbbe2d78c7d5", "schema_hash": "8c76077daf4af98a", "risk_hash": "89d9cf142700dd69", - "evidence_hash": "8cbbe01d45d3da05" + "evidence_hash": "e82694e26a4f85f5" }, "matched_predicates": { "missing_confirmation_policy": true, @@ -5836,6 +5962,7 @@ } }, "policy_routing": null, + "support": null, "capability_trace_refs": [], "recommendation": "Declare a user confirmation policy for gmail.send_customer_email or remove this action from the release.", "blocks_release": false, @@ -5890,6 +6017,7 @@ "capability_refs": [], "capability_policy_evidence": null, "policy_routing": null, + "support": null, "capability_trace_refs": [], "recommendation": "Add an idempotency key, idempotent annotation, or declared idempotency policy for stripe.create_refund.", "blocks_release": false, @@ -5934,6 +6062,7 @@ "capability_refs": [], "capability_policy_evidence": null, "policy_routing": null, + "support": null, "capability_trace_refs": [], "recommendation": "Declare an owner for each high-risk production tool in risk_overrides.tools.", "blocks_release": false, @@ -5977,6 +6106,7 @@ "capability_refs": [], "capability_policy_evidence": null, "policy_routing": null, + "support": null, "capability_trace_refs": [], "recommendation": "Remove unused manifest scopes or add tool metadata showing why they are required.", "blocks_release": false, @@ -6019,6 +6149,32 @@ "capability_refs": [], "capability_policy_evidence": null, "policy_routing": null, + "support": { + "status": "matched", + "confidence": "high", + "policy_eligible": true, + "blocking_eligible": true, + "claim_ids": [], + "evidence_bases": [ + "protocol_structure" + ], + "predicates": [ + { + "predicate": "deterministic_action_rule", + "status": "matched", + "expected": null, + "observed": "agent:support-refund-agent/refund-assistant:action_v2_49bfadd2eb7605a1065c24081f890481ee2f37e9718152ae533c8e43ddaab17a", + "confidence": "high", + "claim_ids": [], + "evidence_bases": [ + "protocol_structure" + ], + "policy_eligible": true, + "why": null + } + ], + "support_hash": "sha256:2da4af706588e6b22a8dcb7b25419cf3fa22214d1fb76ea543afef8f4936a3f4" + }, "capability_trace_refs": [], "recommendation": "Declare confirmation policy and safeguards.audit_log for this external communication action.", "blocks_release": true, @@ -6063,6 +6219,32 @@ "capability_refs": [], "capability_policy_evidence": null, "policy_routing": null, + "support": { + "status": "matched", + "confidence": "high", + "policy_eligible": true, + "blocking_eligible": true, + "claim_ids": [], + "evidence_bases": [ + "protocol_structure" + ], + "predicates": [ + { + "predicate": "deterministic_action_rule", + "status": "matched", + "expected": null, + "observed": "agent:support-refund-agent/refund-assistant:action_v2_6f2d18a55f2189a165089034897d52ada547bb19afe20036cb7ec3119c2d95ca", + "confidence": "high", + "claim_ids": [], + "evidence_bases": [ + "protocol_structure" + ], + "policy_eligible": true, + "why": null + } + ], + "support_hash": "sha256:742ea8da905eef5df5d2f9a3e9bea9a3bbc84eb4710409e8f41c5389f86af494" + }, "capability_trace_refs": [], "recommendation": "Declare confirmation policy and safeguards.audit_log for this external communication action.", "blocks_release": true, @@ -6108,6 +6290,32 @@ "capability_refs": [], "capability_policy_evidence": null, "policy_routing": null, + "support": { + "status": "matched", + "confidence": "high", + "policy_eligible": true, + "blocking_eligible": true, + "claim_ids": [], + "evidence_bases": [ + "protocol_structure" + ], + "predicates": [ + { + "predicate": "deterministic_action_rule", + "status": "matched", + "expected": null, + "observed": "agent:support-refund-agent/refund-assistant:action_v2_6f2d18a55f2189a165089034897d52ada547bb19afe20036cb7ec3119c2d95ca", + "confidence": "high", + "claim_ids": [], + "evidence_bases": [ + "protocol_structure" + ], + "policy_eligible": true, + "why": null + } + ], + "support_hash": "sha256:742ea8da905eef5df5d2f9a3e9bea9a3bbc84eb4710409e8f41c5389f86af494" + }, "capability_trace_refs": [], "recommendation": "Declare approval.required, safeguards.audit_log, and safeguards.idempotency for this financial write action.", "blocks_release": true, @@ -6132,8 +6340,8 @@ "Add the required scopes for support.search_kb to permissions.scopes or narrow the tool's declared auth requirements." ], "generated_reports": { - "markdown": "/private/tmp/shipgate-golden-support_refund_agent/report.md", - "json": "/private/tmp/shipgate-golden-support_refund_agent/report.json" + "markdown": "/private/tmp/shipgate-goldens/support/report.md", + "json": "/private/tmp/shipgate-goldens/support/report.json" }, "loaded_policy_packs": [], "loaded_plugins": [], @@ -6177,10 +6385,13 @@ ], "claims": [ { + "claim_id": "clm_87b20f443f41d8daa79a", "dimension": "identity", "value": "obs_v1_fc50761fdbf05db319a85b8ac0b828b8522967a4273d1c0f70d4cfabf0304300", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "protocol_structure", + "policy_eligible": true, "source": "source_observation", "source_pointer": "/paths/~1orders~1{order_id}~1cancel/post", "evidence": { @@ -6203,10 +6414,13 @@ ], "claims": [ { + "claim_id": "clm_e551bd6b7ec256d44d83", "dimension": "binding", "value": "agent_v1:7cb237a00d64b7400f4adc3b->tool_v2_08f6c9642ff3da07bd4dd33f949b747ad5fe79c245d1f76d450a81ff86cb1f6e", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "reviewed_declaration", + "policy_eligible": true, "source": "agent_binding_declaration", "source_pointer": "/agent_bindings/declarations/0/tools/4", "evidence": { @@ -6231,10 +6445,13 @@ "confidence": "high", "claims": [ { + "claim_id": "clm_eff630fcc9ed89e3934a", "dimension": "effect", "value": "write", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "structural_scope", + "policy_eligible": true, "source": "action_scope", "source_pointer": "/paths/~1orders~1{order_id}~1cancel/post", "evidence": { @@ -6242,19 +6459,25 @@ } }, { + "claim_id": "clm_952866402a040f319732", "dimension": "effect", "value": "destructive", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "reviewed_declaration", + "policy_eligible": true, "source": "action_surface_declaration", "source_pointer": "action_surface.actions[tool='shopify.cancel_order'].effect", "evidence": {} }, { + "claim_id": "clm_918a970310039b946dfb", "dimension": "effect", "value": "write", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "structural_scope", + "policy_eligible": true, "source": "auth_scope", "source_pointer": "/paths/~1orders~1{order_id}~1cancel/post", "evidence": { @@ -6262,10 +6485,13 @@ } }, { + "claim_id": "clm_9ed79e0a0a7bc0ae34b1", "dimension": "effect", "value": "destructive", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "protocol_structure", + "policy_eligible": true, "source": "mcp_annotation", "source_pointer": "/paths/~1orders~1{order_id}~1cancel/post", "evidence": { @@ -6273,10 +6499,13 @@ } }, { + "claim_id": "clm_2b798cc5828bc13ecdb7", "dimension": "effect", "value": "write", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "protocol_structure", + "policy_eligible": true, "source": "openapi_method", "source_pointer": "/paths/~1orders~1{order_id}~1cancel/post", "evidence": { @@ -6284,10 +6513,13 @@ } }, { + "claim_id": "clm_23276120fa073ed6a21c", "dimension": "effect", "value": "destructive", "confidence": "medium", "provenance_kind": "keyword_heuristic", + "basis": "inferred_keyword", + "policy_eligible": false, "source": "risk_hint:keyword", "source_pointer": "/paths/~1orders~1{order_id}~1cancel/post", "evidence": { @@ -6309,10 +6541,13 @@ ], "claims": [ { + "claim_id": "clm_daa31c9b3b2b5dbd6e93", "dimension": "authority", "value": "scoped", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "reviewed_declaration", + "policy_eligible": true, "source": "action_surface_declaration", "source_pointer": "action_surface.actions[tool='shopify.cancel_order'].authority", "evidence": { @@ -6324,10 +6559,13 @@ } }, { + "claim_id": "clm_a6e1c41fd4ce119db816", "dimension": "authority", "value": "scoped", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "protocol_structure", + "policy_eligible": true, "source": "openapi_authority", "source_pointer": "/paths/~1orders~1{order_id}~1cancel/post", "evidence": { @@ -6339,10 +6577,13 @@ } }, { + "claim_id": "clm_6b221c1060349f334ae5", "dimension": "authority", "value": "scoped", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "protocol_structure", + "policy_eligible": true, "source": "openapi_security_alternative", "source_pointer": "/paths/~1orders~1{order_id}~1cancel/post/security/0", "evidence": { @@ -6373,10 +6614,13 @@ ], "claims": [ { + "claim_id": "clm_e551bd6b7ec256d44d83", "dimension": "binding", "value": "agent_v1:7cb237a00d64b7400f4adc3b->tool_v2_08f6c9642ff3da07bd4dd33f949b747ad5fe79c245d1f76d450a81ff86cb1f6e", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "reviewed_declaration", + "policy_eligible": true, "source": "agent_binding_declaration", "source_pointer": "/agent_bindings/declarations/0/tools/4", "evidence": { @@ -6420,7 +6664,7 @@ "owner": null, "confidence": "high", "semantic_assessment": { - "conservative_effect": "read", + "conservative_effect": "external_communication", "identity": { "tool_id": "tool_v2_2c9ee6ae2b9f0259ba2dad40a30fb18a8bdc3a732ffc207936fdd49c80e1bc9e", "status": "declared", @@ -6433,10 +6677,13 @@ ], "claims": [ { + "claim_id": "clm_de7969c4a04dff4a5514", "dimension": "identity", "value": "obs_v1_68344b9fde158f18db1f3476cf06003414968e6d82488d8f376eebfecbc8bfb3", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "reviewed_declaration", + "policy_eligible": true, "source": "tool_identity_binding", "source_pointer": "agents/refund_agent.py", "evidence": { @@ -6446,10 +6693,13 @@ } }, { + "claim_id": "clm_dba9cd1e78c894219426", "dimension": "identity", "value": "obs_v1_8835a4a63d13e804fae8ab9c175ece24e865d923e65457698593c0532254b0a6", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "reviewed_declaration", + "policy_eligible": true, "source": "tool_identity_binding", "source_pointer": "/tools/0", "evidence": { @@ -6472,10 +6722,13 @@ ], "claims": [ { + "claim_id": "clm_38bd6ab43966135164c2", "dimension": "binding", "value": "agent_v1:7cb237a00d64b7400f4adc3b->tool_v2_2c9ee6ae2b9f0259ba2dad40a30fb18a8bdc3a732ffc207936fdd49c80e1bc9e", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "reviewed_declaration", + "policy_eligible": true, "source": "agent_binding_declaration", "source_pointer": "/agent_bindings/declarations/0/tools/6", "evidence": { @@ -6484,10 +6737,13 @@ } }, { + "claim_id": "clm_c5e560fe7f75b8b49ce7", "dimension": "binding", "value": "agent_v1:7cb237a00d64b7400f4adc3b->tool_v2_2c9ee6ae2b9f0259ba2dad40a30fb18a8bdc3a732ffc207936fdd49c80e1bc9e", "confidence": "high", "provenance_kind": "ast_extraction", + "basis": "typed_provider_fact", + "policy_eligible": true, "source": "agents/refund_agent.py", "source_pointer": "agents/refund_agent.py:10", "evidence": { @@ -6512,19 +6768,25 @@ "confidence": "high", "claims": [ { + "claim_id": "clm_d147c0c853cf0af5ae61", "dimension": "effect", "value": "read", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "reviewed_declaration", + "policy_eligible": true, "source": "action_surface_declaration", "source_pointer": "action_surface.actions[tool='send_email_preview'].effect", "evidence": {} }, { + "claim_id": "clm_50c4d6abc43dbc7853c5", "dimension": "effect", "value": "read", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "protocol_structure", + "policy_eligible": true, "source": "mcp_annotation", "source_pointer": "/tools/0", "evidence": { @@ -6532,10 +6794,13 @@ } }, { + "claim_id": "clm_cfc3dd4156cdbd1469ed", "dimension": "effect", "value": "external_communication", "confidence": "low", "provenance_kind": "keyword_heuristic", + "basis": "inferred_keyword", + "policy_eligible": false, "source": "risk_hint:keyword", "source_pointer": "/tools/0", "evidence": { @@ -6544,10 +6809,13 @@ } }, { + "claim_id": "clm_b77048e00c8bab300132", "dimension": "effect", "value": "external_communication", "confidence": "low", "provenance_kind": "keyword_heuristic", + "basis": "inferred_keyword", + "policy_eligible": false, "source": "risk_hint:keyword", "source_pointer": "/tools/0", "evidence": { @@ -6566,10 +6834,13 @@ "scopes": [], "claims": [ { + "claim_id": "clm_6a7e0f2012d1e4cee027", "dimension": "authority", "value": "none", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "reviewed_declaration", + "policy_eligible": true, "source": "action_surface_declaration", "source_pointer": "action_surface.actions[tool='send_email_preview'].authority", "evidence": { @@ -6579,10 +6850,13 @@ } }, { + "claim_id": "clm_b1bf872bb994c8ec4e1c", "dimension": "authority", "value": "none", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "protocol_structure", + "policy_eligible": true, "source": "mcp_authority", "source_pointer": "/tools/0", "evidence": { @@ -6606,10 +6880,13 @@ ], "claims": [ { + "claim_id": "clm_38bd6ab43966135164c2", "dimension": "binding", "value": "agent_v1:7cb237a00d64b7400f4adc3b->tool_v2_2c9ee6ae2b9f0259ba2dad40a30fb18a8bdc3a732ffc207936fdd49c80e1bc9e", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "reviewed_declaration", + "policy_eligible": true, "source": "agent_binding_declaration", "source_pointer": "/agent_bindings/declarations/0/tools/6", "evidence": { @@ -6618,10 +6895,13 @@ } }, { + "claim_id": "clm_c5e560fe7f75b8b49ce7", "dimension": "binding", "value": "agent_v1:7cb237a00d64b7400f4adc3b->tool_v2_2c9ee6ae2b9f0259ba2dad40a30fb18a8bdc3a732ffc207936fdd49c80e1bc9e", "confidence": "high", "provenance_kind": "ast_extraction", + "basis": "typed_provider_fact", + "policy_eligible": true, "source": "agents/refund_agent.py", "source_pointer": "agents/refund_agent.py:10", "evidence": { @@ -6666,7 +6946,7 @@ "owner": "support-platform", "confidence": "high", "semantic_assessment": { - "conservative_effect": "read", + "conservative_effect": "financial_write", "identity": { "tool_id": "tool_v2_445a251aa6adc1c69f7b318b68f7ad47f01b629d3c24d723331a661735cd353a", "status": "structural", @@ -6678,10 +6958,13 @@ ], "claims": [ { + "claim_id": "clm_2069927e39cfc559e23d", "dimension": "identity", "value": "obs_v1_0df5fda3ee9f82ce968548fd66bcff348164c98aa8f25ca72cf8cea840ca4024", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "protocol_structure", + "policy_eligible": true, "source": "source_observation", "source_pointer": "/tools/0", "evidence": { @@ -6704,10 +6987,13 @@ ], "claims": [ { + "claim_id": "clm_814f5f934d8108e4f872", "dimension": "binding", "value": "agent_v1:7cb237a00d64b7400f4adc3b->tool_v2_445a251aa6adc1c69f7b318b68f7ad47f01b629d3c24d723331a661735cd353a", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "reviewed_declaration", + "policy_eligible": true, "source": "agent_binding_declaration", "source_pointer": "/agent_bindings/declarations/0/tools/0", "evidence": { @@ -6732,19 +7018,25 @@ "confidence": "high", "claims": [ { + "claim_id": "clm_386dd78b81e32aabd24f", "dimension": "effect", "value": "read", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "reviewed_declaration", + "policy_eligible": true, "source": "action_surface_declaration", "source_pointer": "action_surface.actions[tool='support.search_kb'].effect", "evidence": {} }, { + "claim_id": "clm_50c4d6abc43dbc7853c5", "dimension": "effect", "value": "read", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "protocol_structure", + "policy_eligible": true, "source": "mcp_annotation", "source_pointer": "/tools/0", "evidence": { @@ -6752,10 +7044,13 @@ } }, { + "claim_id": "clm_db44eeb09b0fff2d9e05", "dimension": "effect", "value": "financial_write", "confidence": "low", "provenance_kind": "keyword_heuristic", + "basis": "inferred_keyword", + "policy_eligible": false, "source": "risk_hint:keyword", "source_pointer": "/tools/0", "evidence": { @@ -6779,10 +7074,13 @@ ], "claims": [ { + "claim_id": "clm_943e9b080077bf3f7adb", "dimension": "authority", "value": "scoped", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "reviewed_declaration", + "policy_eligible": true, "source": "action_surface_declaration", "source_pointer": "action_surface.actions[tool='support.search_kb'].authority", "evidence": { @@ -6794,10 +7092,13 @@ } }, { + "claim_id": "clm_39039a28e82a637c05e5", "dimension": "authority", "value": "scoped", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "protocol_structure", + "policy_eligible": true, "source": "mcp_authority", "source_pointer": "/tools/0", "evidence": { @@ -6823,10 +7124,13 @@ ], "claims": [ { + "claim_id": "clm_814f5f934d8108e4f872", "dimension": "binding", "value": "agent_v1:7cb237a00d64b7400f4adc3b->tool_v2_445a251aa6adc1c69f7b318b68f7ad47f01b629d3c24d723331a661735cd353a", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "reviewed_declaration", + "policy_eligible": true, "source": "agent_binding_declaration", "source_pointer": "/agent_bindings/declarations/0/tools/0", "evidence": { @@ -6881,10 +7185,13 @@ ], "claims": [ { + "claim_id": "clm_ee6f9f9b928e98d8e34f", "dimension": "identity", "value": "obs_v1_abe0748ca63b6b1f48d758ee5395281d80ad99b49e4766dda9f717c57aa6a3c0", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "protocol_structure", + "policy_eligible": true, "source": "source_observation", "source_pointer": "/paths/~1refunds~1{payment_id}~1status/get", "evidence": { @@ -6907,10 +7214,13 @@ ], "claims": [ { + "claim_id": "clm_f55c3a2e30069f6699fb", "dimension": "binding", "value": "agent_v1:7cb237a00d64b7400f4adc3b->tool_v2_61891eb768365da25408e00518646b94299be76989cba28407f47015112f86be", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "reviewed_declaration", + "policy_eligible": true, "source": "agent_binding_declaration", "source_pointer": "/agent_bindings/declarations/0/tools/2", "evidence": { @@ -6935,19 +7245,25 @@ "confidence": "high", "claims": [ { + "claim_id": "clm_e94b0e48a585e4b07dca", "dimension": "effect", "value": "read", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "reviewed_declaration", + "policy_eligible": true, "source": "action_surface_declaration", "source_pointer": "action_surface.actions[tool='refund_status_lookup'].effect", "evidence": {} }, { + "claim_id": "clm_bdc89f8aeb4887e717f4", "dimension": "effect", "value": "read", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "protocol_structure", + "policy_eligible": true, "source": "mcp_annotation", "source_pointer": "/paths/~1refunds~1{payment_id}~1status/get", "evidence": { @@ -6955,10 +7271,13 @@ } }, { + "claim_id": "clm_53c53a5e6459571424da", "dimension": "effect", "value": "read", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "protocol_structure", + "policy_eligible": true, "source": "openapi_method", "source_pointer": "/paths/~1refunds~1{payment_id}~1status/get", "evidence": { @@ -6966,10 +7285,13 @@ } }, { + "claim_id": "clm_834a693cde6cc06f269b", "dimension": "effect", "value": "read", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "reviewed_declaration", + "policy_eligible": true, "source": "risk_hint:manual", "source_pointer": "/paths/~1refunds~1{payment_id}~1status/get", "evidence": { @@ -6990,10 +7312,13 @@ "scopes": [], "claims": [ { + "claim_id": "clm_da401a8a8ab43662894f", "dimension": "authority", "value": "unscoped", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "reviewed_declaration", + "policy_eligible": true, "source": "action_surface_declaration", "source_pointer": "action_surface.actions[tool='refund_status_lookup'].authority", "evidence": { @@ -7003,10 +7328,13 @@ } }, { + "claim_id": "clm_aa0bf67594fae796aa00", "dimension": "authority", "value": "unscoped", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "protocol_structure", + "policy_eligible": true, "source": "openapi_authority", "source_pointer": "/paths/~1refunds~1{payment_id}~1status/get", "evidence": { @@ -7016,10 +7344,13 @@ } }, { + "claim_id": "clm_40cc6737ceee31a1ac60", "dimension": "authority", "value": "unscoped", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "protocol_structure", + "policy_eligible": true, "source": "openapi_security_alternative", "source_pointer": "/paths/~1refunds~1{payment_id}~1status/get/security/0", "evidence": { @@ -7048,10 +7379,13 @@ ], "claims": [ { + "claim_id": "clm_f55c3a2e30069f6699fb", "dimension": "binding", "value": "agent_v1:7cb237a00d64b7400f4adc3b->tool_v2_61891eb768365da25408e00518646b94299be76989cba28407f47015112f86be", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "reviewed_declaration", + "policy_eligible": true, "source": "agent_binding_declaration", "source_pointer": "/agent_bindings/declarations/0/tools/2", "evidence": { @@ -7112,10 +7446,13 @@ ], "claims": [ { + "claim_id": "clm_4f9e87f34a3612e16582", "dimension": "identity", "value": "obs_v1_2dc88a16ab175fa6d937d4fd2047076fa2a6dd20131796d31e71b9998386eb57", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "protocol_structure", + "policy_eligible": true, "source": "source_observation", "source_pointer": "/paths/~1refunds/post", "evidence": { @@ -7138,10 +7475,13 @@ ], "claims": [ { + "claim_id": "clm_cc55b0f2f90750432ea4", "dimension": "binding", "value": "agent_v1:7cb237a00d64b7400f4adc3b->tool_v2_8b559376c3236346b33a64fb9d460d342e6956579a68a0d5a331b2d2adf68ac8", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "reviewed_declaration", + "policy_eligible": true, "source": "agent_binding_declaration", "source_pointer": "/agent_bindings/declarations/0/tools/5", "evidence": { @@ -7166,10 +7506,13 @@ "confidence": "high", "claims": [ { + "claim_id": "clm_7748fa5e0a211a5ddf4c", "dimension": "effect", "value": "write", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "structural_scope", + "policy_eligible": true, "source": "action_scope", "source_pointer": "/paths/~1refunds/post", "evidence": { @@ -7177,19 +7520,25 @@ } }, { + "claim_id": "clm_019b1bf05a33e2269c31", "dimension": "effect", "value": "financial_write", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "reviewed_declaration", + "policy_eligible": true, "source": "action_surface_declaration", "source_pointer": "action_surface.actions[tool='stripe.create_refund'].effect", "evidence": {} }, { + "claim_id": "clm_5552183b9622f510f19c", "dimension": "effect", "value": "write", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "structural_scope", + "policy_eligible": true, "source": "auth_scope", "source_pointer": "/paths/~1refunds/post", "evidence": { @@ -7197,10 +7546,13 @@ } }, { + "claim_id": "clm_82bc01360271203cdf4a", "dimension": "effect", "value": "write", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "protocol_structure", + "policy_eligible": true, "source": "openapi_method", "source_pointer": "/paths/~1refunds/post", "evidence": { @@ -7208,10 +7560,13 @@ } }, { + "claim_id": "clm_085e955c58f7858e5e89", "dimension": "effect", "value": "financial_write", "confidence": "high", - "provenance_kind": "static_declaration", + "provenance_kind": "keyword_heuristic", + "basis": "inferred_keyword", + "policy_eligible": false, "source": "risk_hint:auth_scope", "source_pointer": "/paths/~1refunds/post", "evidence": { @@ -7224,10 +7579,13 @@ } }, { + "claim_id": "clm_1fee9ee6ea224541b56d", "dimension": "effect", "value": "external_communication", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "reviewed_declaration", + "policy_eligible": true, "source": "risk_hint:manual", "source_pointer": "/paths/~1refunds/post", "evidence": { @@ -7238,10 +7596,13 @@ } }, { + "claim_id": "clm_c8e45c8de58cf645c026", "dimension": "effect", "value": "financial_write", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "reviewed_declaration", + "policy_eligible": true, "source": "risk_hint:manual", "source_pointer": "/paths/~1refunds/post", "evidence": { @@ -7264,10 +7625,13 @@ ], "claims": [ { + "claim_id": "clm_fb25ed2f6f5f9f7963e3", "dimension": "authority", "value": "scoped", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "reviewed_declaration", + "policy_eligible": true, "source": "action_surface_declaration", "source_pointer": "action_surface.actions[tool='stripe.create_refund'].authority", "evidence": { @@ -7279,10 +7643,13 @@ } }, { + "claim_id": "clm_c87f8f49c702ca2de75b", "dimension": "authority", "value": "scoped", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "protocol_structure", + "policy_eligible": true, "source": "openapi_authority", "source_pointer": "/paths/~1refunds/post", "evidence": { @@ -7294,10 +7661,13 @@ } }, { + "claim_id": "clm_16a1ff9b04c06e9fa685", "dimension": "authority", "value": "scoped", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "protocol_structure", + "policy_eligible": true, "source": "openapi_security_alternative", "source_pointer": "/paths/~1refunds/post/security/0", "evidence": { @@ -7328,10 +7698,13 @@ ], "claims": [ { + "claim_id": "clm_cc55b0f2f90750432ea4", "dimension": "binding", "value": "agent_v1:7cb237a00d64b7400f4adc3b->tool_v2_8b559376c3236346b33a64fb9d460d342e6956579a68a0d5a331b2d2adf68ac8", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "reviewed_declaration", + "policy_eligible": true, "source": "agent_binding_declaration", "source_pointer": "/agent_bindings/declarations/0/tools/5", "evidence": { @@ -7388,10 +7761,13 @@ ], "claims": [ { + "claim_id": "clm_b891a97946af75e5184c", "dimension": "identity", "value": "obs_v1_0767af2d9bbe222adf666b94824bba1a94f3f2a146fa6d5198b2ef348da3fcc9", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "protocol_structure", + "policy_eligible": true, "source": "source_observation", "source_pointer": "/paths/~1tickets~1{ticket_id}/patch", "evidence": { @@ -7414,10 +7790,13 @@ ], "claims": [ { + "claim_id": "clm_ec0367c75ab7296716ec", "dimension": "binding", "value": "agent_v1:7cb237a00d64b7400f4adc3b->tool_v2_aa488250dc1213a6a8bb76a7e695da294f93052f3181d42817b6cd2cf83cfa33", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "reviewed_declaration", + "policy_eligible": true, "source": "agent_binding_declaration", "source_pointer": "/agent_bindings/declarations/0/tools/3", "evidence": { @@ -7442,10 +7821,13 @@ "confidence": "high", "claims": [ { + "claim_id": "clm_5df15421976a933fde44", "dimension": "effect", "value": "write", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "structural_scope", + "policy_eligible": true, "source": "action_scope", "source_pointer": "/paths/~1tickets~1{ticket_id}/patch", "evidence": { @@ -7453,19 +7835,25 @@ } }, { + "claim_id": "clm_1a3eca00dc1b01426fc9", "dimension": "effect", "value": "write", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "reviewed_declaration", + "policy_eligible": true, "source": "action_surface_declaration", "source_pointer": "action_surface.actions[tool='zendesk.update_ticket'].effect", "evidence": {} }, { + "claim_id": "clm_d1a0de7992b99f6c7213", "dimension": "effect", "value": "write", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "structural_scope", + "policy_eligible": true, "source": "auth_scope", "source_pointer": "/paths/~1tickets~1{ticket_id}/patch", "evidence": { @@ -7473,10 +7861,13 @@ } }, { + "claim_id": "clm_25b5a215b288b2acec14", "dimension": "effect", "value": "write", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "protocol_structure", + "policy_eligible": true, "source": "openapi_method", "source_pointer": "/paths/~1tickets~1{ticket_id}/patch", "evidence": { @@ -7496,10 +7887,13 @@ ], "claims": [ { + "claim_id": "clm_f331d8d5b8113ee080f4", "dimension": "authority", "value": "scoped", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "reviewed_declaration", + "policy_eligible": true, "source": "action_surface_declaration", "source_pointer": "action_surface.actions[tool='zendesk.update_ticket'].authority", "evidence": { @@ -7511,10 +7905,13 @@ } }, { + "claim_id": "clm_1e4cedf300862be020b7", "dimension": "authority", "value": "scoped", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "protocol_structure", + "policy_eligible": true, "source": "openapi_authority", "source_pointer": "/paths/~1tickets~1{ticket_id}/patch", "evidence": { @@ -7526,10 +7923,13 @@ } }, { + "claim_id": "clm_f50137eae5e02958d168", "dimension": "authority", "value": "scoped", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "protocol_structure", + "policy_eligible": true, "source": "openapi_security_alternative", "source_pointer": "/paths/~1tickets~1{ticket_id}/patch/security/0", "evidence": { @@ -7560,10 +7960,13 @@ ], "claims": [ { + "claim_id": "clm_ec0367c75ab7296716ec", "dimension": "binding", "value": "agent_v1:7cb237a00d64b7400f4adc3b->tool_v2_aa488250dc1213a6a8bb76a7e695da294f93052f3181d42817b6cd2cf83cfa33", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "reviewed_declaration", + "policy_eligible": true, "source": "agent_binding_declaration", "source_pointer": "/agent_bindings/declarations/0/tools/3", "evidence": { @@ -7622,10 +8025,13 @@ ], "claims": [ { + "claim_id": "clm_e016125bf4102d2231d3", "dimension": "identity", "value": "obs_v1_6a598e295d2d845f05a72faecd97fcf971b67c96a0aad092d9082c8e573df553", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "protocol_structure", + "policy_eligible": true, "source": "source_observation", "source_pointer": "/tools/1", "evidence": { @@ -7648,10 +8054,13 @@ ], "claims": [ { + "claim_id": "clm_1d843e3958c618456a2f", "dimension": "binding", "value": "agent_v1:7cb237a00d64b7400f4adc3b->tool_v2_c69ebf76910efa80a2024a15d836f20366effdbd12f7a69b660ce4f4e5870ed9", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "reviewed_declaration", + "policy_eligible": true, "source": "agent_binding_declaration", "source_pointer": "/agent_bindings/declarations/0/tools/1", "evidence": { @@ -7676,10 +8085,13 @@ "confidence": "high", "claims": [ { + "claim_id": "clm_a4818bc49faaf8f1ea61", "dimension": "effect", "value": "write", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "structural_scope", + "policy_eligible": true, "source": "action_scope", "source_pointer": "/tools/1", "evidence": { @@ -7687,19 +8099,25 @@ } }, { + "claim_id": "clm_e6c199e33c2159754789", "dimension": "effect", "value": "external_communication", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "reviewed_declaration", + "policy_eligible": true, "source": "action_surface_declaration", "source_pointer": "action_surface.actions[tool='gmail.send_customer_email'].effect", "evidence": {} }, { + "claim_id": "clm_26ae80b049a84be11ab1", "dimension": "effect", "value": "write", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "structural_scope", + "policy_eligible": true, "source": "auth_scope", "source_pointer": "/tools/1", "evidence": { @@ -7707,10 +8125,13 @@ } }, { + "claim_id": "clm_f0196c0c4caab8cd8ecd", "dimension": "effect", "value": "external_communication", "confidence": "high", "provenance_kind": "keyword_heuristic", + "basis": "inferred_keyword", + "policy_eligible": false, "source": "risk_hint:keyword", "source_pointer": "/tools/1", "evidence": { @@ -7719,10 +8140,13 @@ } }, { + "claim_id": "clm_b56eed6a901658bae650", "dimension": "effect", "value": "external_communication", "confidence": "high", "provenance_kind": "keyword_heuristic", + "basis": "inferred_keyword", + "policy_eligible": false, "source": "risk_hint:keyword", "source_pointer": "/tools/1", "evidence": { @@ -7731,10 +8155,13 @@ } }, { + "claim_id": "clm_7ec6f046c34a18b719f2", "dimension": "effect", "value": "external_communication", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "reviewed_declaration", + "policy_eligible": true, "source": "risk_hint:manual", "source_pointer": "/tools/1", "evidence": { @@ -7745,10 +8172,13 @@ } }, { + "claim_id": "clm_188aecba394542ac226f", "dimension": "effect", "value": "external_communication", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "reviewed_declaration", + "policy_eligible": true, "source": "risk_hint:manual", "source_pointer": "/tools/1", "evidence": { @@ -7771,10 +8201,13 @@ ], "claims": [ { + "claim_id": "clm_932e397ceff5ab3cb4df", "dimension": "authority", "value": "scoped", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "reviewed_declaration", + "policy_eligible": true, "source": "action_surface_declaration", "source_pointer": "action_surface.actions[tool='gmail.send_customer_email'].authority", "evidence": { @@ -7786,10 +8219,13 @@ } }, { + "claim_id": "clm_c1c0614054dbba4bfa4e", "dimension": "authority", "value": "scoped", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "protocol_structure", + "policy_eligible": true, "source": "mcp_authority", "source_pointer": "/tools/1", "evidence": { @@ -7815,10 +8251,13 @@ ], "claims": [ { + "claim_id": "clm_1d843e3958c618456a2f", "dimension": "binding", "value": "agent_v1:7cb237a00d64b7400f4adc3b->tool_v2_c69ebf76910efa80a2024a15d836f20366effdbd12f7a69b660ce4f4e5870ed9", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "reviewed_declaration", + "policy_eligible": true, "source": "agent_binding_declaration", "source_pointer": "/agent_bindings/declarations/0/tools/1", "evidence": { @@ -7879,10 +8318,13 @@ ], "claims": [ { + "claim_id": "clm_87b20f443f41d8daa79a", "dimension": "identity", "value": "obs_v1_fc50761fdbf05db319a85b8ac0b828b8522967a4273d1c0f70d4cfabf0304300", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "protocol_structure", + "policy_eligible": true, "source": "source_observation", "source_pointer": "/paths/~1orders~1{order_id}~1cancel/post", "evidence": { @@ -7905,10 +8347,13 @@ ], "claims": [ { + "claim_id": "clm_e551bd6b7ec256d44d83", "dimension": "binding", "value": "agent_v1:7cb237a00d64b7400f4adc3b->tool_v2_08f6c9642ff3da07bd4dd33f949b747ad5fe79c245d1f76d450a81ff86cb1f6e", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "reviewed_declaration", + "policy_eligible": true, "source": "agent_binding_declaration", "source_pointer": "/agent_bindings/declarations/0/tools/4", "evidence": { @@ -7933,10 +8378,13 @@ "confidence": "high", "claims": [ { + "claim_id": "clm_eff630fcc9ed89e3934a", "dimension": "effect", "value": "write", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "structural_scope", + "policy_eligible": true, "source": "action_scope", "source_pointer": "/paths/~1orders~1{order_id}~1cancel/post", "evidence": { @@ -7944,19 +8392,25 @@ } }, { + "claim_id": "clm_952866402a040f319732", "dimension": "effect", "value": "destructive", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "reviewed_declaration", + "policy_eligible": true, "source": "action_surface_declaration", "source_pointer": "action_surface.actions[tool='shopify.cancel_order'].effect", "evidence": {} }, { + "claim_id": "clm_918a970310039b946dfb", "dimension": "effect", "value": "write", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "structural_scope", + "policy_eligible": true, "source": "auth_scope", "source_pointer": "/paths/~1orders~1{order_id}~1cancel/post", "evidence": { @@ -7964,10 +8418,13 @@ } }, { + "claim_id": "clm_9ed79e0a0a7bc0ae34b1", "dimension": "effect", "value": "destructive", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "protocol_structure", + "policy_eligible": true, "source": "mcp_annotation", "source_pointer": "/paths/~1orders~1{order_id}~1cancel/post", "evidence": { @@ -7975,10 +8432,13 @@ } }, { + "claim_id": "clm_2b798cc5828bc13ecdb7", "dimension": "effect", "value": "write", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "protocol_structure", + "policy_eligible": true, "source": "openapi_method", "source_pointer": "/paths/~1orders~1{order_id}~1cancel/post", "evidence": { @@ -7986,10 +8446,13 @@ } }, { + "claim_id": "clm_23276120fa073ed6a21c", "dimension": "effect", "value": "destructive", "confidence": "medium", "provenance_kind": "keyword_heuristic", + "basis": "inferred_keyword", + "policy_eligible": false, "source": "risk_hint:keyword", "source_pointer": "/paths/~1orders~1{order_id}~1cancel/post", "evidence": { @@ -8011,10 +8474,13 @@ ], "claims": [ { + "claim_id": "clm_daa31c9b3b2b5dbd6e93", "dimension": "authority", "value": "scoped", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "reviewed_declaration", + "policy_eligible": true, "source": "action_surface_declaration", "source_pointer": "action_surface.actions[tool='shopify.cancel_order'].authority", "evidence": { @@ -8026,10 +8492,13 @@ } }, { + "claim_id": "clm_a6e1c41fd4ce119db816", "dimension": "authority", "value": "scoped", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "protocol_structure", + "policy_eligible": true, "source": "openapi_authority", "source_pointer": "/paths/~1orders~1{order_id}~1cancel/post", "evidence": { @@ -8041,10 +8510,13 @@ } }, { + "claim_id": "clm_6b221c1060349f334ae5", "dimension": "authority", "value": "scoped", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "protocol_structure", + "policy_eligible": true, "source": "openapi_security_alternative", "source_pointer": "/paths/~1orders~1{order_id}~1cancel/post/security/0", "evidence": { @@ -8075,10 +8547,13 @@ ], "claims": [ { + "claim_id": "clm_e551bd6b7ec256d44d83", "dimension": "binding", "value": "agent_v1:7cb237a00d64b7400f4adc3b->tool_v2_08f6c9642ff3da07bd4dd33f949b747ad5fe79c245d1f76d450a81ff86cb1f6e", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "reviewed_declaration", + "policy_eligible": true, "source": "agent_binding_declaration", "source_pointer": "/agent_bindings/declarations/0/tools/4", "evidence": { @@ -8122,7 +8597,7 @@ "owner": null, "confidence": "high", "semantic_assessment": { - "conservative_effect": "read", + "conservative_effect": "external_communication", "identity": { "tool_id": "tool_v2_2c9ee6ae2b9f0259ba2dad40a30fb18a8bdc3a732ffc207936fdd49c80e1bc9e", "status": "declared", @@ -8135,10 +8610,13 @@ ], "claims": [ { + "claim_id": "clm_de7969c4a04dff4a5514", "dimension": "identity", "value": "obs_v1_68344b9fde158f18db1f3476cf06003414968e6d82488d8f376eebfecbc8bfb3", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "reviewed_declaration", + "policy_eligible": true, "source": "tool_identity_binding", "source_pointer": "agents/refund_agent.py", "evidence": { @@ -8148,10 +8626,13 @@ } }, { + "claim_id": "clm_dba9cd1e78c894219426", "dimension": "identity", "value": "obs_v1_8835a4a63d13e804fae8ab9c175ece24e865d923e65457698593c0532254b0a6", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "reviewed_declaration", + "policy_eligible": true, "source": "tool_identity_binding", "source_pointer": "/tools/0", "evidence": { @@ -8174,10 +8655,13 @@ ], "claims": [ { + "claim_id": "clm_38bd6ab43966135164c2", "dimension": "binding", "value": "agent_v1:7cb237a00d64b7400f4adc3b->tool_v2_2c9ee6ae2b9f0259ba2dad40a30fb18a8bdc3a732ffc207936fdd49c80e1bc9e", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "reviewed_declaration", + "policy_eligible": true, "source": "agent_binding_declaration", "source_pointer": "/agent_bindings/declarations/0/tools/6", "evidence": { @@ -8186,10 +8670,13 @@ } }, { + "claim_id": "clm_c5e560fe7f75b8b49ce7", "dimension": "binding", "value": "agent_v1:7cb237a00d64b7400f4adc3b->tool_v2_2c9ee6ae2b9f0259ba2dad40a30fb18a8bdc3a732ffc207936fdd49c80e1bc9e", "confidence": "high", "provenance_kind": "ast_extraction", + "basis": "typed_provider_fact", + "policy_eligible": true, "source": "agents/refund_agent.py", "source_pointer": "agents/refund_agent.py:10", "evidence": { @@ -8214,19 +8701,25 @@ "confidence": "high", "claims": [ { + "claim_id": "clm_d147c0c853cf0af5ae61", "dimension": "effect", "value": "read", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "reviewed_declaration", + "policy_eligible": true, "source": "action_surface_declaration", "source_pointer": "action_surface.actions[tool='send_email_preview'].effect", "evidence": {} }, { + "claim_id": "clm_50c4d6abc43dbc7853c5", "dimension": "effect", "value": "read", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "protocol_structure", + "policy_eligible": true, "source": "mcp_annotation", "source_pointer": "/tools/0", "evidence": { @@ -8234,10 +8727,13 @@ } }, { + "claim_id": "clm_cfc3dd4156cdbd1469ed", "dimension": "effect", "value": "external_communication", "confidence": "low", "provenance_kind": "keyword_heuristic", + "basis": "inferred_keyword", + "policy_eligible": false, "source": "risk_hint:keyword", "source_pointer": "/tools/0", "evidence": { @@ -8246,10 +8742,13 @@ } }, { + "claim_id": "clm_b77048e00c8bab300132", "dimension": "effect", "value": "external_communication", "confidence": "low", "provenance_kind": "keyword_heuristic", + "basis": "inferred_keyword", + "policy_eligible": false, "source": "risk_hint:keyword", "source_pointer": "/tools/0", "evidence": { @@ -8268,10 +8767,13 @@ "scopes": [], "claims": [ { + "claim_id": "clm_6a7e0f2012d1e4cee027", "dimension": "authority", "value": "none", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "reviewed_declaration", + "policy_eligible": true, "source": "action_surface_declaration", "source_pointer": "action_surface.actions[tool='send_email_preview'].authority", "evidence": { @@ -8281,10 +8783,13 @@ } }, { + "claim_id": "clm_b1bf872bb994c8ec4e1c", "dimension": "authority", "value": "none", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "protocol_structure", + "policy_eligible": true, "source": "mcp_authority", "source_pointer": "/tools/0", "evidence": { @@ -8308,10 +8813,13 @@ ], "claims": [ { + "claim_id": "clm_38bd6ab43966135164c2", "dimension": "binding", "value": "agent_v1:7cb237a00d64b7400f4adc3b->tool_v2_2c9ee6ae2b9f0259ba2dad40a30fb18a8bdc3a732ffc207936fdd49c80e1bc9e", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "reviewed_declaration", + "policy_eligible": true, "source": "agent_binding_declaration", "source_pointer": "/agent_bindings/declarations/0/tools/6", "evidence": { @@ -8320,10 +8828,13 @@ } }, { + "claim_id": "clm_c5e560fe7f75b8b49ce7", "dimension": "binding", "value": "agent_v1:7cb237a00d64b7400f4adc3b->tool_v2_2c9ee6ae2b9f0259ba2dad40a30fb18a8bdc3a732ffc207936fdd49c80e1bc9e", "confidence": "high", "provenance_kind": "ast_extraction", + "basis": "typed_provider_fact", + "policy_eligible": true, "source": "agents/refund_agent.py", "source_pointer": "agents/refund_agent.py:10", "evidence": { @@ -8368,7 +8879,7 @@ "owner": "support-platform", "confidence": "high", "semantic_assessment": { - "conservative_effect": "read", + "conservative_effect": "financial_write", "identity": { "tool_id": "tool_v2_445a251aa6adc1c69f7b318b68f7ad47f01b629d3c24d723331a661735cd353a", "status": "structural", @@ -8380,10 +8891,13 @@ ], "claims": [ { + "claim_id": "clm_2069927e39cfc559e23d", "dimension": "identity", "value": "obs_v1_0df5fda3ee9f82ce968548fd66bcff348164c98aa8f25ca72cf8cea840ca4024", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "protocol_structure", + "policy_eligible": true, "source": "source_observation", "source_pointer": "/tools/0", "evidence": { @@ -8406,10 +8920,13 @@ ], "claims": [ { + "claim_id": "clm_814f5f934d8108e4f872", "dimension": "binding", "value": "agent_v1:7cb237a00d64b7400f4adc3b->tool_v2_445a251aa6adc1c69f7b318b68f7ad47f01b629d3c24d723331a661735cd353a", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "reviewed_declaration", + "policy_eligible": true, "source": "agent_binding_declaration", "source_pointer": "/agent_bindings/declarations/0/tools/0", "evidence": { @@ -8434,19 +8951,25 @@ "confidence": "high", "claims": [ { + "claim_id": "clm_386dd78b81e32aabd24f", "dimension": "effect", "value": "read", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "reviewed_declaration", + "policy_eligible": true, "source": "action_surface_declaration", "source_pointer": "action_surface.actions[tool='support.search_kb'].effect", "evidence": {} }, { + "claim_id": "clm_50c4d6abc43dbc7853c5", "dimension": "effect", "value": "read", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "protocol_structure", + "policy_eligible": true, "source": "mcp_annotation", "source_pointer": "/tools/0", "evidence": { @@ -8454,10 +8977,13 @@ } }, { + "claim_id": "clm_db44eeb09b0fff2d9e05", "dimension": "effect", "value": "financial_write", "confidence": "low", "provenance_kind": "keyword_heuristic", + "basis": "inferred_keyword", + "policy_eligible": false, "source": "risk_hint:keyword", "source_pointer": "/tools/0", "evidence": { @@ -8481,10 +9007,13 @@ ], "claims": [ { + "claim_id": "clm_943e9b080077bf3f7adb", "dimension": "authority", "value": "scoped", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "reviewed_declaration", + "policy_eligible": true, "source": "action_surface_declaration", "source_pointer": "action_surface.actions[tool='support.search_kb'].authority", "evidence": { @@ -8496,10 +9025,13 @@ } }, { + "claim_id": "clm_39039a28e82a637c05e5", "dimension": "authority", "value": "scoped", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "protocol_structure", + "policy_eligible": true, "source": "mcp_authority", "source_pointer": "/tools/0", "evidence": { @@ -8525,10 +9057,13 @@ ], "claims": [ { + "claim_id": "clm_814f5f934d8108e4f872", "dimension": "binding", "value": "agent_v1:7cb237a00d64b7400f4adc3b->tool_v2_445a251aa6adc1c69f7b318b68f7ad47f01b629d3c24d723331a661735cd353a", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "reviewed_declaration", + "policy_eligible": true, "source": "agent_binding_declaration", "source_pointer": "/agent_bindings/declarations/0/tools/0", "evidence": { @@ -8583,10 +9118,13 @@ ], "claims": [ { + "claim_id": "clm_ee6f9f9b928e98d8e34f", "dimension": "identity", "value": "obs_v1_abe0748ca63b6b1f48d758ee5395281d80ad99b49e4766dda9f717c57aa6a3c0", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "protocol_structure", + "policy_eligible": true, "source": "source_observation", "source_pointer": "/paths/~1refunds~1{payment_id}~1status/get", "evidence": { @@ -8609,10 +9147,13 @@ ], "claims": [ { + "claim_id": "clm_f55c3a2e30069f6699fb", "dimension": "binding", "value": "agent_v1:7cb237a00d64b7400f4adc3b->tool_v2_61891eb768365da25408e00518646b94299be76989cba28407f47015112f86be", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "reviewed_declaration", + "policy_eligible": true, "source": "agent_binding_declaration", "source_pointer": "/agent_bindings/declarations/0/tools/2", "evidence": { @@ -8637,19 +9178,25 @@ "confidence": "high", "claims": [ { + "claim_id": "clm_e94b0e48a585e4b07dca", "dimension": "effect", "value": "read", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "reviewed_declaration", + "policy_eligible": true, "source": "action_surface_declaration", "source_pointer": "action_surface.actions[tool='refund_status_lookup'].effect", "evidence": {} }, { + "claim_id": "clm_bdc89f8aeb4887e717f4", "dimension": "effect", "value": "read", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "protocol_structure", + "policy_eligible": true, "source": "mcp_annotation", "source_pointer": "/paths/~1refunds~1{payment_id}~1status/get", "evidence": { @@ -8657,10 +9204,13 @@ } }, { + "claim_id": "clm_53c53a5e6459571424da", "dimension": "effect", "value": "read", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "protocol_structure", + "policy_eligible": true, "source": "openapi_method", "source_pointer": "/paths/~1refunds~1{payment_id}~1status/get", "evidence": { @@ -8668,10 +9218,13 @@ } }, { + "claim_id": "clm_834a693cde6cc06f269b", "dimension": "effect", "value": "read", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "reviewed_declaration", + "policy_eligible": true, "source": "risk_hint:manual", "source_pointer": "/paths/~1refunds~1{payment_id}~1status/get", "evidence": { @@ -8692,10 +9245,13 @@ "scopes": [], "claims": [ { + "claim_id": "clm_da401a8a8ab43662894f", "dimension": "authority", "value": "unscoped", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "reviewed_declaration", + "policy_eligible": true, "source": "action_surface_declaration", "source_pointer": "action_surface.actions[tool='refund_status_lookup'].authority", "evidence": { @@ -8705,10 +9261,13 @@ } }, { + "claim_id": "clm_aa0bf67594fae796aa00", "dimension": "authority", "value": "unscoped", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "protocol_structure", + "policy_eligible": true, "source": "openapi_authority", "source_pointer": "/paths/~1refunds~1{payment_id}~1status/get", "evidence": { @@ -8718,10 +9277,13 @@ } }, { + "claim_id": "clm_40cc6737ceee31a1ac60", "dimension": "authority", "value": "unscoped", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "protocol_structure", + "policy_eligible": true, "source": "openapi_security_alternative", "source_pointer": "/paths/~1refunds~1{payment_id}~1status/get/security/0", "evidence": { @@ -8750,10 +9312,13 @@ ], "claims": [ { + "claim_id": "clm_f55c3a2e30069f6699fb", "dimension": "binding", "value": "agent_v1:7cb237a00d64b7400f4adc3b->tool_v2_61891eb768365da25408e00518646b94299be76989cba28407f47015112f86be", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "reviewed_declaration", + "policy_eligible": true, "source": "agent_binding_declaration", "source_pointer": "/agent_bindings/declarations/0/tools/2", "evidence": { @@ -8814,10 +9379,13 @@ ], "claims": [ { + "claim_id": "clm_4f9e87f34a3612e16582", "dimension": "identity", "value": "obs_v1_2dc88a16ab175fa6d937d4fd2047076fa2a6dd20131796d31e71b9998386eb57", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "protocol_structure", + "policy_eligible": true, "source": "source_observation", "source_pointer": "/paths/~1refunds/post", "evidence": { @@ -8840,10 +9408,13 @@ ], "claims": [ { + "claim_id": "clm_cc55b0f2f90750432ea4", "dimension": "binding", "value": "agent_v1:7cb237a00d64b7400f4adc3b->tool_v2_8b559376c3236346b33a64fb9d460d342e6956579a68a0d5a331b2d2adf68ac8", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "reviewed_declaration", + "policy_eligible": true, "source": "agent_binding_declaration", "source_pointer": "/agent_bindings/declarations/0/tools/5", "evidence": { @@ -8868,10 +9439,13 @@ "confidence": "high", "claims": [ { + "claim_id": "clm_7748fa5e0a211a5ddf4c", "dimension": "effect", "value": "write", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "structural_scope", + "policy_eligible": true, "source": "action_scope", "source_pointer": "/paths/~1refunds/post", "evidence": { @@ -8879,19 +9453,25 @@ } }, { + "claim_id": "clm_019b1bf05a33e2269c31", "dimension": "effect", "value": "financial_write", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "reviewed_declaration", + "policy_eligible": true, "source": "action_surface_declaration", "source_pointer": "action_surface.actions[tool='stripe.create_refund'].effect", "evidence": {} }, { + "claim_id": "clm_5552183b9622f510f19c", "dimension": "effect", "value": "write", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "structural_scope", + "policy_eligible": true, "source": "auth_scope", "source_pointer": "/paths/~1refunds/post", "evidence": { @@ -8899,10 +9479,13 @@ } }, { + "claim_id": "clm_82bc01360271203cdf4a", "dimension": "effect", "value": "write", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "protocol_structure", + "policy_eligible": true, "source": "openapi_method", "source_pointer": "/paths/~1refunds/post", "evidence": { @@ -8910,10 +9493,13 @@ } }, { + "claim_id": "clm_085e955c58f7858e5e89", "dimension": "effect", "value": "financial_write", "confidence": "high", - "provenance_kind": "static_declaration", + "provenance_kind": "keyword_heuristic", + "basis": "inferred_keyword", + "policy_eligible": false, "source": "risk_hint:auth_scope", "source_pointer": "/paths/~1refunds/post", "evidence": { @@ -8926,10 +9512,13 @@ } }, { + "claim_id": "clm_1fee9ee6ea224541b56d", "dimension": "effect", "value": "external_communication", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "reviewed_declaration", + "policy_eligible": true, "source": "risk_hint:manual", "source_pointer": "/paths/~1refunds/post", "evidence": { @@ -8940,10 +9529,13 @@ } }, { + "claim_id": "clm_c8e45c8de58cf645c026", "dimension": "effect", "value": "financial_write", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "reviewed_declaration", + "policy_eligible": true, "source": "risk_hint:manual", "source_pointer": "/paths/~1refunds/post", "evidence": { @@ -8966,10 +9558,13 @@ ], "claims": [ { + "claim_id": "clm_fb25ed2f6f5f9f7963e3", "dimension": "authority", "value": "scoped", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "reviewed_declaration", + "policy_eligible": true, "source": "action_surface_declaration", "source_pointer": "action_surface.actions[tool='stripe.create_refund'].authority", "evidence": { @@ -8981,10 +9576,13 @@ } }, { + "claim_id": "clm_c87f8f49c702ca2de75b", "dimension": "authority", "value": "scoped", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "protocol_structure", + "policy_eligible": true, "source": "openapi_authority", "source_pointer": "/paths/~1refunds/post", "evidence": { @@ -8996,10 +9594,13 @@ } }, { + "claim_id": "clm_16a1ff9b04c06e9fa685", "dimension": "authority", "value": "scoped", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "protocol_structure", + "policy_eligible": true, "source": "openapi_security_alternative", "source_pointer": "/paths/~1refunds/post/security/0", "evidence": { @@ -9030,10 +9631,13 @@ ], "claims": [ { + "claim_id": "clm_cc55b0f2f90750432ea4", "dimension": "binding", "value": "agent_v1:7cb237a00d64b7400f4adc3b->tool_v2_8b559376c3236346b33a64fb9d460d342e6956579a68a0d5a331b2d2adf68ac8", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "reviewed_declaration", + "policy_eligible": true, "source": "agent_binding_declaration", "source_pointer": "/agent_bindings/declarations/0/tools/5", "evidence": { @@ -9090,10 +9694,13 @@ ], "claims": [ { + "claim_id": "clm_b891a97946af75e5184c", "dimension": "identity", "value": "obs_v1_0767af2d9bbe222adf666b94824bba1a94f3f2a146fa6d5198b2ef348da3fcc9", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "protocol_structure", + "policy_eligible": true, "source": "source_observation", "source_pointer": "/paths/~1tickets~1{ticket_id}/patch", "evidence": { @@ -9116,10 +9723,13 @@ ], "claims": [ { + "claim_id": "clm_ec0367c75ab7296716ec", "dimension": "binding", "value": "agent_v1:7cb237a00d64b7400f4adc3b->tool_v2_aa488250dc1213a6a8bb76a7e695da294f93052f3181d42817b6cd2cf83cfa33", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "reviewed_declaration", + "policy_eligible": true, "source": "agent_binding_declaration", "source_pointer": "/agent_bindings/declarations/0/tools/3", "evidence": { @@ -9144,10 +9754,13 @@ "confidence": "high", "claims": [ { + "claim_id": "clm_5df15421976a933fde44", "dimension": "effect", "value": "write", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "structural_scope", + "policy_eligible": true, "source": "action_scope", "source_pointer": "/paths/~1tickets~1{ticket_id}/patch", "evidence": { @@ -9155,19 +9768,25 @@ } }, { + "claim_id": "clm_1a3eca00dc1b01426fc9", "dimension": "effect", "value": "write", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "reviewed_declaration", + "policy_eligible": true, "source": "action_surface_declaration", "source_pointer": "action_surface.actions[tool='zendesk.update_ticket'].effect", "evidence": {} }, { + "claim_id": "clm_d1a0de7992b99f6c7213", "dimension": "effect", "value": "write", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "structural_scope", + "policy_eligible": true, "source": "auth_scope", "source_pointer": "/paths/~1tickets~1{ticket_id}/patch", "evidence": { @@ -9175,10 +9794,13 @@ } }, { + "claim_id": "clm_25b5a215b288b2acec14", "dimension": "effect", "value": "write", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "protocol_structure", + "policy_eligible": true, "source": "openapi_method", "source_pointer": "/paths/~1tickets~1{ticket_id}/patch", "evidence": { @@ -9198,10 +9820,13 @@ ], "claims": [ { + "claim_id": "clm_f331d8d5b8113ee080f4", "dimension": "authority", "value": "scoped", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "reviewed_declaration", + "policy_eligible": true, "source": "action_surface_declaration", "source_pointer": "action_surface.actions[tool='zendesk.update_ticket'].authority", "evidence": { @@ -9213,10 +9838,13 @@ } }, { + "claim_id": "clm_1e4cedf300862be020b7", "dimension": "authority", "value": "scoped", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "protocol_structure", + "policy_eligible": true, "source": "openapi_authority", "source_pointer": "/paths/~1tickets~1{ticket_id}/patch", "evidence": { @@ -9228,10 +9856,13 @@ } }, { + "claim_id": "clm_f50137eae5e02958d168", "dimension": "authority", "value": "scoped", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "protocol_structure", + "policy_eligible": true, "source": "openapi_security_alternative", "source_pointer": "/paths/~1tickets~1{ticket_id}/patch/security/0", "evidence": { @@ -9262,10 +9893,13 @@ ], "claims": [ { + "claim_id": "clm_ec0367c75ab7296716ec", "dimension": "binding", "value": "agent_v1:7cb237a00d64b7400f4adc3b->tool_v2_aa488250dc1213a6a8bb76a7e695da294f93052f3181d42817b6cd2cf83cfa33", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "reviewed_declaration", + "policy_eligible": true, "source": "agent_binding_declaration", "source_pointer": "/agent_bindings/declarations/0/tools/3", "evidence": { @@ -9324,10 +9958,13 @@ ], "claims": [ { + "claim_id": "clm_e016125bf4102d2231d3", "dimension": "identity", "value": "obs_v1_6a598e295d2d845f05a72faecd97fcf971b67c96a0aad092d9082c8e573df553", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "protocol_structure", + "policy_eligible": true, "source": "source_observation", "source_pointer": "/tools/1", "evidence": { @@ -9350,10 +9987,13 @@ ], "claims": [ { + "claim_id": "clm_1d843e3958c618456a2f", "dimension": "binding", "value": "agent_v1:7cb237a00d64b7400f4adc3b->tool_v2_c69ebf76910efa80a2024a15d836f20366effdbd12f7a69b660ce4f4e5870ed9", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "reviewed_declaration", + "policy_eligible": true, "source": "agent_binding_declaration", "source_pointer": "/agent_bindings/declarations/0/tools/1", "evidence": { @@ -9378,10 +10018,13 @@ "confidence": "high", "claims": [ { + "claim_id": "clm_a4818bc49faaf8f1ea61", "dimension": "effect", "value": "write", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "structural_scope", + "policy_eligible": true, "source": "action_scope", "source_pointer": "/tools/1", "evidence": { @@ -9389,19 +10032,25 @@ } }, { + "claim_id": "clm_e6c199e33c2159754789", "dimension": "effect", "value": "external_communication", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "reviewed_declaration", + "policy_eligible": true, "source": "action_surface_declaration", "source_pointer": "action_surface.actions[tool='gmail.send_customer_email'].effect", "evidence": {} }, { + "claim_id": "clm_26ae80b049a84be11ab1", "dimension": "effect", "value": "write", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "structural_scope", + "policy_eligible": true, "source": "auth_scope", "source_pointer": "/tools/1", "evidence": { @@ -9409,10 +10058,13 @@ } }, { + "claim_id": "clm_f0196c0c4caab8cd8ecd", "dimension": "effect", "value": "external_communication", "confidence": "high", "provenance_kind": "keyword_heuristic", + "basis": "inferred_keyword", + "policy_eligible": false, "source": "risk_hint:keyword", "source_pointer": "/tools/1", "evidence": { @@ -9421,10 +10073,13 @@ } }, { + "claim_id": "clm_b56eed6a901658bae650", "dimension": "effect", "value": "external_communication", "confidence": "high", "provenance_kind": "keyword_heuristic", + "basis": "inferred_keyword", + "policy_eligible": false, "source": "risk_hint:keyword", "source_pointer": "/tools/1", "evidence": { @@ -9433,10 +10088,13 @@ } }, { + "claim_id": "clm_7ec6f046c34a18b719f2", "dimension": "effect", "value": "external_communication", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "reviewed_declaration", + "policy_eligible": true, "source": "risk_hint:manual", "source_pointer": "/tools/1", "evidence": { @@ -9447,10 +10105,13 @@ } }, { + "claim_id": "clm_188aecba394542ac226f", "dimension": "effect", "value": "external_communication", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "reviewed_declaration", + "policy_eligible": true, "source": "risk_hint:manual", "source_pointer": "/tools/1", "evidence": { @@ -9473,10 +10134,13 @@ ], "claims": [ { + "claim_id": "clm_932e397ceff5ab3cb4df", "dimension": "authority", "value": "scoped", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "reviewed_declaration", + "policy_eligible": true, "source": "action_surface_declaration", "source_pointer": "action_surface.actions[tool='gmail.send_customer_email'].authority", "evidence": { @@ -9488,10 +10152,13 @@ } }, { + "claim_id": "clm_c1c0614054dbba4bfa4e", "dimension": "authority", "value": "scoped", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "protocol_structure", + "policy_eligible": true, "source": "mcp_authority", "source_pointer": "/tools/1", "evidence": { @@ -9517,10 +10184,13 @@ ], "claims": [ { + "claim_id": "clm_1d843e3958c618456a2f", "dimension": "binding", "value": "agent_v1:7cb237a00d64b7400f4adc3b->tool_v2_c69ebf76910efa80a2024a15d836f20366effdbd12f7a69b660ce4f4e5870ed9", "confidence": "high", "provenance_kind": "static_declaration", + "basis": "reviewed_declaration", + "policy_eligible": true, "source": "agent_binding_declaration", "source_pointer": "/agent_bindings/declarations/0/tools/1", "evidence": { @@ -9573,13 +10243,183 @@ "source_warnings": [ "MCP source declares wildcard tool exposure" ], + "policy_evidence_gaps": [ + { + "kind": "inferred_policy_applicability", + "subject": "tool_v2_2c9ee6ae2b9f0259ba2dad40a30fb18a8bdc3a732ffc207936fdd49c80e1bc9e", + "source_type": null, + "source_ref": "inventories/sdk-tools.json", + "why": "SHIP-SCHEMA-BROAD-FREE-TEXT: Policy applicability is supported only by heuristic evidence.", + "next_action": { + "kind": "provide_policy_evidence", + "command": "agents-shipgate verify --workspace . --config shipgate.yaml --ci-mode advisory --format json", + "path": "/tools/0", + "why": "Heuristic or unknown evidence cannot create a blocking policy finding.", + "expects": "Provide reviewed or structural evidence for every indeterminate predicate.", + "accepted_values": [ + "reviewed_declaration", + "protocol_structure", + "typed_provider_fact", + "structural_scope" + ], + "suggested_patch_kind": "manual", + "declaration_template": null, + "auto_apply": false, + "requires_human_review": true + } + }, + { + "kind": "inferred_policy_applicability", + "subject": "tool_v2_8b559376c3236346b33a64fb9d460d342e6956579a68a0d5a331b2d2adf68ac8", + "source_type": null, + "source_ref": "specs/support-tools.openapi.yaml", + "why": "SHIP-SCHEMA-MISSING-BOUNDS: Policy applicability is supported only by heuristic evidence.", + "next_action": { + "kind": "provide_policy_evidence", + "command": "agents-shipgate verify --workspace . --config shipgate.yaml --ci-mode advisory --format json", + "path": "/paths/~1refunds/post", + "why": "Heuristic or unknown evidence cannot create a blocking policy finding.", + "expects": "Provide reviewed or structural evidence for every indeterminate predicate.", + "accepted_values": [ + "reviewed_declaration", + "protocol_structure", + "typed_provider_fact", + "structural_scope" + ], + "suggested_patch_kind": "manual", + "declaration_template": null, + "auto_apply": false, + "requires_human_review": true + } + }, + { + "kind": "inferred_policy_applicability", + "subject": "tool_v2_aa488250dc1213a6a8bb76a7e695da294f93052f3181d42817b6cd2cf83cfa33", + "source_type": null, + "source_ref": "specs/support-tools.openapi.yaml", + "why": "SHIP-SCHEMA-BROAD-FREE-TEXT: Policy applicability is supported only by heuristic evidence.", + "next_action": { + "kind": "provide_policy_evidence", + "command": "agents-shipgate verify --workspace . --config shipgate.yaml --ci-mode advisory --format json", + "path": "/paths/~1tickets~1{ticket_id}/patch", + "why": "Heuristic or unknown evidence cannot create a blocking policy finding.", + "expects": "Provide reviewed or structural evidence for every indeterminate predicate.", + "accepted_values": [ + "reviewed_declaration", + "protocol_structure", + "typed_provider_fact", + "structural_scope" + ], + "suggested_patch_kind": "manual", + "declaration_template": null, + "auto_apply": false, + "requires_human_review": true + } + }, + { + "kind": "inferred_policy_applicability", + "subject": "tool_v2_c69ebf76910efa80a2024a15d836f20366effdbd12f7a69b660ce4f4e5870ed9", + "source_type": null, + "source_ref": ".agents-shipgate/mcp-tools.json", + "why": "SHIP-SCHEMA-BROAD-FREE-TEXT: Policy applicability is supported only by heuristic evidence.", + "next_action": { + "kind": "provide_policy_evidence", + "command": "agents-shipgate verify --workspace . --config shipgate.yaml --ci-mode advisory --format json", + "path": "/tools/1", + "why": "Heuristic or unknown evidence cannot create a blocking policy finding.", + "expects": "Provide reviewed or structural evidence for every indeterminate predicate.", + "accepted_values": [ + "reviewed_declaration", + "protocol_structure", + "typed_provider_fact", + "structural_scope" + ], + "suggested_patch_kind": "manual", + "declaration_template": null, + "auto_apply": false, + "requires_human_review": true + } + }, + { + "kind": "inferred_policy_applicability", + "subject": "tool_v2_2c9ee6ae2b9f0259ba2dad40a30fb18a8bdc3a732ffc207936fdd49c80e1bc9e", + "source_type": null, + "source_ref": "shipgate.yaml", + "why": "SHIP-SCOPE-PROHIBITED-TOOL-PRESENT: Policy applicability is supported only by heuristic evidence.", + "next_action": { + "kind": "provide_policy_evidence", + "command": "agents-shipgate verify --workspace . --config shipgate.yaml --ci-mode advisory --format json", + "path": "/agent/prohibited_actions/2", + "why": "Heuristic or unknown evidence cannot create a blocking policy finding.", + "expects": "Provide reviewed or structural evidence for every indeterminate predicate.", + "accepted_values": [ + "reviewed_declaration", + "protocol_structure", + "typed_provider_fact", + "structural_scope" + ], + "suggested_patch_kind": "manual", + "declaration_template": null, + "auto_apply": false, + "requires_human_review": true + } + }, + { + "kind": "inferred_policy_applicability", + "subject": "tool_v2_8b559376c3236346b33a64fb9d460d342e6956579a68a0d5a331b2d2adf68ac8", + "source_type": null, + "source_ref": "shipgate.yaml", + "why": "SHIP-SCOPE-PROHIBITED-TOOL-PRESENT: Policy applicability is supported only by heuristic evidence.", + "next_action": { + "kind": "provide_policy_evidence", + "command": "agents-shipgate verify --workspace . --config shipgate.yaml --ci-mode advisory --format json", + "path": "/agent/prohibited_actions/0", + "why": "Heuristic or unknown evidence cannot create a blocking policy finding.", + "expects": "Provide reviewed or structural evidence for every indeterminate predicate.", + "accepted_values": [ + "reviewed_declaration", + "protocol_structure", + "typed_provider_fact", + "structural_scope" + ], + "suggested_patch_kind": "manual", + "declaration_template": null, + "auto_apply": false, + "requires_human_review": true + } + }, + { + "kind": "inferred_policy_applicability", + "subject": "tool_v2_c69ebf76910efa80a2024a15d836f20366effdbd12f7a69b660ce4f4e5870ed9", + "source_type": null, + "source_ref": "shipgate.yaml", + "why": "SHIP-SCOPE-PROHIBITED-TOOL-PRESENT: Policy applicability is supported only by heuristic evidence.", + "next_action": { + "kind": "provide_policy_evidence", + "command": "agents-shipgate verify --workspace . --config shipgate.yaml --ci-mode advisory --format json", + "path": "/agent/prohibited_actions/2", + "why": "Heuristic or unknown evidence cannot create a blocking policy finding.", + "expects": "Provide reviewed or structural evidence for every indeterminate predicate.", + "accepted_values": [ + "reviewed_declaration", + "protocol_structure", + "typed_provider_fact", + "structural_scope" + ], + "suggested_patch_kind": "manual", + "declaration_template": null, + "auto_apply": false, + "requires_human_review": true + } + } + ], "agent_summary": { "verdict": "blocked", - "headline": "5 active finding(s) block release; 14 review item(s) accepted as debt.", + "headline": "5 active finding(s) block release; 9 review item(s) accepted as debt.", "blocker_count": 5, - "review_item_count": 14, + "review_item_count": 9, "auto_appliable_patches": 0, - "needs_human_review": 19, + "needs_human_review": 14, "first_recommended_action": { "kind": "info", "command": null, @@ -9615,9 +10455,9 @@ }, "reviewer_summary": { "verdict": "blocked", - "headline": "Release blocked: 19 lens changes. Start at release_decision.", + "headline": "Release blocked: 14 lens changes. Start at release_decision.", "tool_surface_changes": 0, - "capability_misalignments": 19, + "capability_misalignments": 14, "action_surface_changes": 0, "evidence_matrix_gaps": 0, "severity_overrides_applied": 0, @@ -9663,7 +10503,7 @@ "verdict": "blocked", "by_severity": { "critical": 3, - "high": 15, + "high": 10, "medium": 1 }, "by_reason_code": { @@ -9676,9 +10516,6 @@ "SHIP-MANIFEST-UNUSED-SCOPE": 1, "SHIP-POLICY-APPROVAL-MISSING": 1, "SHIP-POLICY-CONFIRMATION-MISSING": 2, - "SHIP-SCHEMA-BROAD-FREE-TEXT": 2, - "SHIP-SCHEMA-MISSING-BOUNDS": 1, - "SHIP-SCOPE-PROHIBITED-TOOL-PRESENT": 2, "SHIP-SIDEFX-IDEMPOTENCY-MISSING": 1 }, "capability_delta_summary": { @@ -9714,4 +10551,4 @@ } ] } -} +} \ No newline at end of file diff --git a/samples/support_refund_agent/expected/report.md b/samples/support_refund_agent/expected/report.md index 9999929f..2834dfc4 100644 --- a/samples/support_refund_agent/expected/report.md +++ b/samples/support_refund_agent/expected/report.md @@ -16,17 +16,12 @@ Blockers (5): - HIGH SHIP-ACTION-EXTERNAL-COMMUNICATION-AUDIT-MISSING — stripe.create\_refund has external communication capability without required controls - CRITICAL SHIP-ACTION-FINANCIAL-WRITE-CONTROL-MISSING — stripe.create\_refund has financial write capability without required controls -Review items (14): -- HIGH SHIP-SCHEMA-MISSING-BOUNDS — stripe.create\_refund.amount has no maximum bound -- HIGH SHIP-SCHEMA-BROAD-FREE-TEXT — zendesk.update\_ticket accepts broad free-form action input -- HIGH SHIP-SCHEMA-BROAD-FREE-TEXT — gmail.send\_customer\_email accepts broad free-form action input +Review items (9): - HIGH SHIP-AUTH-MANIFEST-BROAD-SCOPE — Manifest declares broad permission scopes - HIGH SHIP-AUTH-SCOPE-COVERAGE-MISSING — shopify.cancel\_order requires scopes not declared in the manifest - HIGH SHIP-AUTH-SCOPE-COVERAGE-MISSING — support.search\_kb requires scopes not declared in the manifest - HIGH SHIP-AUTH-MISSING-SCOPE — refund\_status\_lookup lacks declared auth scopes - HIGH SHIP-AUTH-SCOPE-COVERAGE-MISSING — gmail.send\_customer\_email requires scopes not declared in the manifest -- HIGH SHIP-SCOPE-PROHIBITED-TOOL-PRESENT — stripe.create\_refund appears to overlap with a prohibited action -- HIGH SHIP-SCOPE-PROHIBITED-TOOL-PRESENT — gmail.send\_customer\_email appears to overlap with a prohibited action - HIGH SHIP-POLICY-CONFIRMATION-MISSING — stripe.create\_refund lacks a declared confirmation policy - HIGH SHIP-POLICY-CONFIRMATION-MISSING — gmail.send\_customer\_email lacks a declared confirmation policy - HIGH SHIP-MANIFEST-HIGH-RISK-OWNER-MISSING — shopify.cancel\_order is high-risk but has no owner @@ -41,7 +36,7 @@ Fail policy: ci_mode=advisory, fail_on=[none], new_findings_only=false, would_fa ## Summary - Critical: 3 -- High: 15 +- High: 10 - Medium: 1 - Low: 0 - Suppressed: 0 @@ -77,7 +72,7 @@ Reviewer triage signal only. Provenance kind does not change severity, release d | --- | ---: | | `static_declaration` | 14 | | `ast_extraction` | 0 | -| `keyword_heuristic` | 5 | +| `keyword_heuristic` | 0 | | `regex_heuristic` | 0 | | `policy_pack` | 0 | | `runtime_trace` | 0 | @@ -99,9 +94,9 @@ Actual capabilities: - gmail.send\_customer\_email: capability=external\_write, risk=customer\_communication, external\_write, control=missing - refund\_status\_lookup: capability=read\_only, risk=read\_only, control=partial +- send\_email\_preview: capability=read\_only, risk=read\_only, control=present - shopify.cancel\_order: capability=destructive, risk=destructive, write, control=missing - stripe.create\_refund: capability=financial\_action, risk=external\_write, financial\_action, write, control=missing -- support.search\_kb: capability=read\_only, risk=read\_only, control=missing - 1 more in report.json Policy/control gaps: @@ -115,13 +110,13 @@ Policy/control gaps: - CRITICAL undetected\_gap \[stripe.create\_refund\]: stripe.create\_refund has financial write capability without required controls. (at specs/support-tools.openapi.yaml:97) Requires: Static review requires deterministic evidence for release gaps. Release implication: Human review is required to interpret this finding. -- HIGH control\_missing \[gmail.send\_customer\_email\]: gmail.send\_customer\_email accepts broad free-form action input. (at .agents-shipgate/mcp-tools.json) - Requires: Action-like tool inputs must constrain high-blast-radius fields. - Release implication: Release reviewers cannot bound the operation payload safely. - HIGH control\_missing \[shopify.cancel\_order\]: shopify.cancel\_order is high-risk but has no owner. (at specs/support-tools.openapi.yaml:116) Requires: Manifest metadata must match the active release surface. Release implication: Release review metadata is incomplete or stale. -- 14 more in report.json +- HIGH policy\_gap \[gmail.send\_customer\_email\]: gmail.send\_customer\_email lacks a declared confirmation policy. (at .agents-shipgate/mcp-tools.json) + Requires: Destructive, external, or customer actions require confirmation. + Release implication: Release review must verify explicit user confirmation before shipping. +- 9 more in report.json Release implication: @@ -132,10 +127,9 @@ Next validation: - Retry behavior for risky write: Retries use idempotency evidence or the side effect is not retried. - Approval gate for high-risk action: The run records human approval before the tool call and denies calls without approval. -- Tool schema boundary check: The tool accepts bounded structured inputs and returns structured outputs where needed. - High-risk tool validation case: A declared test or review scenario covers the high-risk tool path. - Confirmation gate for external or destructive action: The run records explicit confirmation before the side effect occurs. -- 2 more in report.json +- Least-privilege scope review: Manifest and tool scopes match the narrow permissions needed for the release. ## Recommended Next Actions @@ -155,7 +149,7 @@ Next validation: ## Tool Surface Summary - Total tools: 7 -- High-risk tools: 3 +- High-risk tools: 5 - Wildcard tools: 0 - Missing descriptions: 0 - Sources: mcp=3, openapi=4 @@ -201,17 +195,6 @@ No local runtime trace artifacts were declared for capability evidence. - HIGH: SHIP-POLICY-CONFIRMATION-MISSING [gmail.send\_customer\_email] - gmail.send\_customer\_email lacks a declared confirmation policy - HIGH: SHIP-POLICY-CONFIRMATION-MISSING [stripe.create\_refund] - stripe.create\_refund lacks a declared confirmation policy -### Schema - -- HIGH: SHIP-SCHEMA-BROAD-FREE-TEXT [gmail.send\_customer\_email] - gmail.send\_customer\_email accepts broad free-form action input -- HIGH: SHIP-SCHEMA-BROAD-FREE-TEXT [zendesk.update\_ticket] - zendesk.update\_ticket accepts broad free-form action input -- HIGH: SHIP-SCHEMA-MISSING-BOUNDS [stripe.create\_refund] - stripe.create\_refund.amount has no maximum bound - -### Scope - -- HIGH: SHIP-SCOPE-PROHIBITED-TOOL-PRESENT [gmail.send\_customer\_email] - gmail.send\_customer\_email appears to overlap with a prohibited action -- HIGH: SHIP-SCOPE-PROHIBITED-TOOL-PRESENT [stripe.create\_refund] - stripe.create\_refund appears to overlap with a prohibited action - ### Side Effects - CRITICAL: SHIP-SIDEFX-IDEMPOTENCY-MISSING [stripe.create\_refund] - stripe.create\_refund lacks idempotency evidence diff --git a/skills/agents-shipgate/SKILL.md b/skills/agents-shipgate/SKILL.md index 2b8e5670..12caf111 100644 --- a/skills/agents-shipgate/SKILL.md +++ b/skills/agents-shipgate/SKILL.md @@ -76,9 +76,9 @@ For non-GitHub CI (GitLab, CircleCI, Jenkins, Azure Pipelines, Buildkite, Bitbuc - **Installed CLI contract**: when available, run `agents-shipgate contract --json` to verify local schema versions, capability/research surfaces, `release_decision.decision`, and manual-review signal fields. Older installs should use [`docs/agent-contract-current.md`](https://github.com/ThreeMoonsLab/agents-shipgate/blob/main/docs/agent-contract-current.md) or upgrade before automating against the local contract command. - **Verifier JSON**: `verifier_schema_version: "0.3"`. Switch on `control.state`, then read `merge_verdict`, `can_merge_without_human`, `control.next_action`, `fix_task`, `capability_review.top_changes`, `trust_root_touched`, and `policy_weakened` before summarizing an AI-generated PR. `merge_verdict` is a deterministic projection; the gate remains `report.json.release_decision.decision`. - **Verify run JSON**: `verify-run.json` uses `schema_version: "shipgate.verify_run/v2"` and records stable run identity, subject refs, input hashes, execution, applicability, release outcome, and artifact hashes. It is the reproducibility artifact for `verify`; do not treat it as a second gate. -- **Report JSON**: `report_schema_version: "0.32"`. Read `release_decision.decision` first. A `passed` decision requires a complete root-reachable static binding graph plus complete, conflict-free identity, effect, and authority evidence for every reachable action; it does not prove runtime behavior. Preserve `release_decision.static_analysis_only=true`, `runtime_behavior_verified=false`, and `static_verdict_disclaimer` in summaries. Read `release_decision.evidence_coverage.binding_coverage`, `semantic_coverage`, and `identity_coverage`, then work every `evidence_gaps[].next_action` in order. Binding and semantic gaps are not Findings and cannot be suppressed, baselined, severity-overridden, cleared by `--no-heuristics`, or satisfied by `human_ack`; binding, effect, and authority declarations are human assertions and must never be auto-written. Use `tool_catalog[]` for diagnostics and `tool_inventory[]` for the proven reachable surface. The current schema is [`docs/report-schema.v0.32.json`](https://github.com/ThreeMoonsLab/agents-shipgate/blob/main/docs/report-schema.v0.32.json); v0.31 is a frozen compatibility reference. See the [current agent contract](https://github.com/ThreeMoonsLab/agents-shipgate/blob/main/docs/agent-contract-current.md#read-these-first-for-release-gating) and [evidence-backed passed contract](https://github.com/ThreeMoonsLab/agents-shipgate/blob/main/docs/passed-verdict-contract.md). +- **Report JSON**: `report_schema_version: "0.33"`. Read `release_decision.decision` first. A `passed` decision requires a complete root-reachable static binding graph plus complete, conflict-free identity, effect, and authority evidence for every reachable action; it does not prove runtime behavior. Preserve `release_decision.static_analysis_only=true`, `runtime_behavior_verified=false`, and `static_verdict_disclaimer` in summaries. Read `release_decision.evidence_coverage.binding_coverage`, `semantic_coverage`, `identity_coverage`, and `policy_gap_count`, then work every `evidence_gaps[].next_action` in order. Binding, semantic, and policy-applicability gaps are not Findings and cannot be suppressed, baselined, severity-overridden, cleared by `--no-heuristics`, or satisfied by `human_ack`; binding, effect, and authority declarations are human assertions and must never be auto-written. Use `tool_catalog[]` for diagnostics and `tool_inventory[]` for the proven reachable surface. The current schema is [`docs/report-schema.v0.33.json`](https://github.com/ThreeMoonsLab/agents-shipgate/blob/main/docs/report-schema.v0.33.json); v0.32 is a frozen compatibility reference. See the [current agent contract](https://github.com/ThreeMoonsLab/agents-shipgate/blob/main/docs/agent-contract-current.md#read-these-first-for-release-gating) and [evidence-backed passed contract](https://github.com/ThreeMoonsLab/agents-shipgate/blob/main/docs/passed-verdict-contract.md). These binding-backed fields require runtime contract v13; the unambiguous `AgentControl` projection requires runtime contract v14. A contract-v12 CLI emits the frozen v0.30 report and must not be described using the v0.31 binding claim. -- **Release Evidence Packet**: `agents-shipgate-reports/packet.{md,json,html}` (and `packet.pdf` with the `[pdf]` extras) is a supporting/provisional reviewer artifact. Packet v0.10 projects binding and semantic coverage under the release decision; it never creates a second gate. See [`docs/packet-schema.v0.10.json`](https://github.com/ThreeMoonsLab/agents-shipgate/blob/main/docs/packet-schema.v0.10.json) and [STABILITY.md §Release Evidence Packet](https://github.com/ThreeMoonsLab/agents-shipgate/blob/main/STABILITY.md#release-evidence-packet-v010). +- **Release Evidence Packet**: `agents-shipgate-reports/packet.{md,json,html}` (and `packet.pdf` with the `[pdf]` extras) is a supporting/provisional reviewer artifact. Packet v0.11 projects binding and semantic coverage under the release decision; it never creates a second gate. See [`docs/packet-schema.v0.11.json`](https://github.com/ThreeMoonsLab/agents-shipgate/blob/main/docs/packet-schema.v0.11.json) and [STABILITY.md §Release Evidence Packet](https://github.com/ThreeMoonsLab/agents-shipgate/blob/main/STABILITY.md#release-evidence-packet-v011). - **Capability standard**: `agents-shipgate capability export` emits a stable static capability lock (`capability_lock_schema_version: "0.5"`) and `agents-shipgate capability diff` emits a stable semantic diff (`capability_lock_diff_schema_version: "0.6"`). These artifacts implement capability standard v0.4, are supporting/provisional and non-gating, exclude runtime trace evidence, and are documented in [`docs/capability-standard.md`](https://github.com/ThreeMoonsLab/agents-shipgate/blob/main/docs/capability-standard.md). - **Governance benchmark**: `benchmark/agent-pr-governance/cases.yaml` and `scripts/run_governance_benchmark.py` are the stable research benchmark substrate (`governance_benchmark_result_schema_version: "0.2"`), not a release gate. See [`docs/governance-benchmark.md`](https://github.com/ThreeMoonsLab/agents-shipgate/blob/main/docs/governance-benchmark.md). - **Single source of truth for the contract**: [`docs/agent-contract-current.md`](https://github.com/ThreeMoonsLab/agents-shipgate/blob/main/docs/agent-contract-current.md). When the schema bumps, that file updates first. diff --git a/src/agents_shipgate/__init__.py b/src/agents_shipgate/__init__.py index 500360b7..0a5a12b5 100644 --- a/src/agents_shipgate/__init__.py +++ b/src/agents_shipgate/__init__.py @@ -1,3 +1,3 @@ """Agents Shipgate package.""" -__version__ = "0.16.0b4" +__version__ = "0.16.0b5" diff --git a/src/agents_shipgate/checks/adk.py b/src/agents_shipgate/checks/adk.py index d7023695..ff5e3174 100644 --- a/src/agents_shipgate/checks/adk.py +++ b/src/agents_shipgate/checks/adk.py @@ -14,7 +14,7 @@ GoogleAdkToolset, ) from agents_shipgate.core.context import ScanContext -from agents_shipgate.core.risk_hints import is_high_risk_tool +from agents_shipgate.core.risk_hints import is_policy_eligible_high_risk_tool def run(context: ScanContext): @@ -135,7 +135,8 @@ def run(context: ScanContext): high_risk_adk_tools = [ tool.name for tool in context.tools - if tool.source_type.startswith("google_adk") and is_high_risk_tool(tool) + if tool.source_type.startswith("google_adk") + and is_policy_eligible_high_risk_tool(tool) ] if high_risk_adk_tools: findings.append( diff --git a/src/agents_shipgate/checks/api.py b/src/agents_shipgate/checks/api.py index 2daf9b4c..2dcc51c0 100644 --- a/src/agents_shipgate/checks/api.py +++ b/src/agents_shipgate/checks/api.py @@ -23,7 +23,10 @@ from agents_shipgate.core.risk_hints import ( has_risk_tag, is_high_risk_tool, + is_policy_eligible_high_risk_tool, + is_policy_eligible_write_tool, is_write_tool, + policy_eligible_effects, risk_tags, ) @@ -59,7 +62,9 @@ def _function_schema_strictness(context: ScanContext): issues = _function_schema_issues(tool) if not issues: continue - high_risk = is_write_tool(tool) or is_high_risk_tool(tool) + high_risk = is_policy_eligible_write_tool( + tool + ) or is_policy_eligible_high_risk_tool(tool) findings.append( tool_finding( tool=tool, @@ -90,7 +95,9 @@ def _structured_output_readiness(context: ScanContext): if artifacts is None: return [] high_risk_tools = [ - tool.name for tool in _openai_api_tools(context) if is_high_risk_tool(tool) + tool.name + for tool in _openai_api_tools(context) + if is_policy_eligible_high_risk_tool(tool) ] if not artifacts.response_formats: return [ @@ -220,7 +227,9 @@ def _operational_readiness(context: ScanContext): return [] findings = [] api_tools = _openai_api_tools(context) - high_risk_tools = [tool for tool in api_tools if is_high_risk_tool(tool)] + high_risk_tools = [ + tool for tool in api_tools if is_policy_eligible_high_risk_tool(tool) + ] retry_policy = artifacts.retry_policy() timeouts = artifacts.timeouts() output_schemas = artifacts.tool_output_schemas() @@ -424,10 +433,9 @@ def _needs_idempotency(tool: Tool, artifacts) -> bool: return False if any(parameter.name == "idempotency_key" for parameter in tool.parameters): return False - return is_write_tool(tool) and has_risk_tag( - tool, - {"financial_action", "destructive", "external_write"}, - min_confidence="medium", + return bool( + policy_eligible_effects(tool) + & {"financial_write", "destructive", "external_communication"} ) diff --git a/src/agents_shipgate/checks/auth.py b/src/agents_shipgate/checks/auth.py index 8eb97034..06178162 100644 --- a/src/agents_shipgate/checks/auth.py +++ b/src/agents_shipgate/checks/auth.py @@ -3,7 +3,7 @@ from agents_shipgate.checks.base import agent_finding, tool_finding from agents_shipgate.core.context import ScanContext from agents_shipgate.core.heuristics import is_broad_scope -from agents_shipgate.core.risk_hints import is_write_tool, risk_tags +from agents_shipgate.core.risk_hints import is_policy_eligible_write_tool, risk_tags def run(context: ScanContext): @@ -110,7 +110,7 @@ def run(context: ScanContext): return findings def _tool_requires_scope(tool) -> bool: - return is_write_tool(tool) + return is_policy_eligible_write_tool(tool) def _scope_covered(required_scope: str, manifest_scopes: list[str]) -> bool: diff --git a/src/agents_shipgate/checks/base.py b/src/agents_shipgate/checks/base.py index eb7c1376..c0067b49 100644 --- a/src/agents_shipgate/checks/base.py +++ b/src/agents_shipgate/checks/base.py @@ -4,6 +4,7 @@ from agents_shipgate.core.context import ScanContext from agents_shipgate.core.domain import Tool +from agents_shipgate.core.policy_evidence import finding_support, predicate_evidence from agents_shipgate.schemas.common import ( ProvenanceKind, SourceReference, @@ -32,6 +33,7 @@ def tool_finding( capability_policy_evidence: CapabilityPolicyEvidence | None = None, capability_trace_refs: list[str] | None = None, ) -> Finding: + support = _heuristic_support(provenance_kind) return Finding( check_id=check_id, title=title, @@ -59,6 +61,7 @@ def tool_finding( capability_refs=capability_refs or [], capability_policy_evidence=capability_policy_evidence, capability_trace_refs=capability_trace_refs or [], + support=support, recommendation=recommendation, patches=patches, ) @@ -89,6 +92,7 @@ def agent_finding( # the secondary pointer only when it genuinely differs from the # primary (the ``tool_finding`` case, where source is the tool's # location and policy_evidence is the manifest). + support = _heuristic_support(provenance_kind) return Finding( check_id=check_id, title=title, @@ -102,11 +106,34 @@ def agent_finding( policy_evidence_source=None, capability_refs=capability_refs or [], capability_trace_refs=capability_trace_refs or [], + support=support, recommendation=recommendation, patches=patches, ) +def _heuristic_support(provenance_kind: ProvenanceKind): + if provenance_kind not in {"keyword_heuristic", "regex_heuristic"}: + return None + basis = ( + "inferred_keyword" + if provenance_kind == "keyword_heuristic" + else "inferred_regex" + ) + return finding_support( + [ + predicate_evidence( + "heuristic_check_applicability", + "indeterminate", + confidence="medium", + evidence_bases=[basis], + policy_eligible=False, + why="Heuristic evidence is advisory and cannot contribute to release gating.", + ) + ] + ) + + def _manifest_ref(config_path: Path) -> str: return config_path.name diff --git a/src/agents_shipgate/checks/evidence.py b/src/agents_shipgate/checks/evidence.py index 0a4031f9..a700e254 100644 --- a/src/agents_shipgate/checks/evidence.py +++ b/src/agents_shipgate/checks/evidence.py @@ -14,7 +14,7 @@ ) from agents_shipgate.core.context import ScanContext from agents_shipgate.core.domain import Tool -from agents_shipgate.core.risk_hints import is_high_risk_tool, risk_tags +from agents_shipgate.core.risk_hints import is_policy_eligible_high_risk_tool, risk_tags from agents_shipgate.schemas.common import ( HitlProvenanceType, HitlSourceProvenance, @@ -240,7 +240,7 @@ def _high_risk_exclusion_findings(context: ScanContext) -> list[Finding]: findings: list[Finding] = [] reason = "file_missing" if not exclusion_files else "tool_missing" for tool in sorted(context.tools, key=lambda item: item.name): - if not is_high_risk_tool(tool): + if not is_policy_eligible_high_risk_tool(tool): continue if tool.name not in approval_declared: continue diff --git a/src/agents_shipgate/checks/manifest_consistency.py b/src/agents_shipgate/checks/manifest_consistency.py index eaae06c0..b9d95070 100644 --- a/src/agents_shipgate/checks/manifest_consistency.py +++ b/src/agents_shipgate/checks/manifest_consistency.py @@ -3,7 +3,7 @@ from agents_shipgate.checks.base import agent_finding, tool_finding from agents_shipgate.core.context import ScanContext from agents_shipgate.core.heuristics import is_broad_scope -from agents_shipgate.core.risk_hints import is_high_risk_tool, risk_tags +from agents_shipgate.core.risk_hints import is_policy_eligible_high_risk_tool, risk_tags from agents_shipgate.schemas.manifest import PolicyToolEntry @@ -103,7 +103,7 @@ def _missing_high_risk_owners(context: ScanContext) -> list: return [] findings = [] for tool in context.tools: - if not is_high_risk_tool(tool) or tool.owner: + if not is_policy_eligible_high_risk_tool(tool) or tool.owner: continue findings.append( tool_finding( diff --git a/src/agents_shipgate/checks/side_effects.py b/src/agents_shipgate/checks/side_effects.py index 16ba7216..db34448c 100644 --- a/src/agents_shipgate/checks/side_effects.py +++ b/src/agents_shipgate/checks/side_effects.py @@ -42,7 +42,11 @@ def run(context: ScanContext): confidence="high" if retry_known else "medium", recommendation=f"Add an idempotency key, idempotent annotation, or declared idempotency policy for {tool.name}.", context=context, - provenance_kind="static_declaration", + provenance_kind=( + "static_declaration" + if tool.semantic_assessment is not None + else "keyword_heuristic" + ), policy_evidence_pointer="/policies/require_idempotency_for_tools", ) ) @@ -57,10 +61,6 @@ def _needs_idempotency(tool) -> bool: return "financial_action" in risk_tags(tool, min_confidence="medium") return any( claim.value == "financial_write" - and claim.confidence == "high" - and claim.provenance_kind not in {"keyword_heuristic", "regex_heuristic"} + and claim.policy_eligible for claim in assessment.effect.claims - ) or ( - assessment.effect.status in {"declared", "structural"} - and assessment.conservative_effect == "financial_write" ) diff --git a/src/agents_shipgate/ci/exit_policy.py b/src/agents_shipgate/ci/exit_policy.py index 0194c59a..013afd27 100644 --- a/src/agents_shipgate/ci/exit_policy.py +++ b/src/agents_shipgate/ci/exit_policy.py @@ -19,8 +19,19 @@ def effective_fail_on(ci_mode: str, fail_on: list[Severity] | None) -> list[Seve def baseline_filtered_active(report: ReadinessReport, *, new_findings_only: bool) -> list[Finding]: - """Active (non-suppressed) findings, filtered by baseline new-only.""" - active = [finding for finding in report.findings if not finding.suppressed] + """Gate-eligible findings, filtered by suppression and baseline mode. + + A rule's requested severity or block bit cannot upgrade predicate evidence. + Findings with typed support therefore contribute to process policy only + when that support is policy-eligible. Legacy findings without support keep + their established behavior for frozen-report compatibility. + """ + active = [ + finding + for finding in report.findings + if not finding.suppressed + and (finding.support is None or finding.support.policy_eligible) + ] if new_findings_only: active = [finding for finding in active if finding.baseline_status in {None, "new"}] return active diff --git a/src/agents_shipgate/ci/release_decision.py b/src/agents_shipgate/ci/release_decision.py index 747f8058..1b1ad07f 100644 --- a/src/agents_shipgate/ci/release_decision.py +++ b/src/agents_shipgate/ci/release_decision.py @@ -60,6 +60,7 @@ def evidence_below_ie_threshold(evidence: EvidenceCoverageDecision, *, tool_coun evidence.binding_coverage.gap_count > 0 or evidence.semantic_coverage.gap_count > 0 + or evidence.policy_gap_count > 0 or any( gap.kind == "source_warning" and gap.next_action.path == "--diff-from" @@ -108,6 +109,19 @@ def build_release_decision( # which branch fired (or, for the silent-drop tail, which baseline # acceptance silently consumed it). for finding in report.findings: + if finding.support is not None and not finding.support.policy_eligible: + contribution_rules.append( + _rule( + finding, + category="excluded", + rule="unsupported_evidence", + rationale=( + "Finding lacks policy-eligible predicate support; it cannot " + "become a blocker or named review concern." + ), + ) + ) + continue if finding.suppressed: if _is_mandatory_current_control(finding): # A suppression explains accepted noise; it cannot satisfy a @@ -217,10 +231,16 @@ def build_release_decision( low_confidence_tool_count=low_confidence_tool_count, # Semantic gaps lead because they are zero-tolerance gate inputs; # extraction/source gaps retain their existing deterministic order. - evidence_gaps=[*binding_gaps, *semantic_gaps, *_evidence_gaps(report, tools)], + evidence_gaps=[ + *binding_gaps, + *semantic_gaps, + *report.policy_evidence_gaps, + *_evidence_gaps(report, tools), + ], semantic_coverage=semantic_coverage, identity_coverage=identity_coverage, binding_coverage=binding_coverage, + policy_gap_count=len(report.policy_evidence_gaps), ) if report.baseline is None: @@ -237,6 +257,7 @@ def build_release_decision( evidence_is_degraded = evidence_below_ie_threshold(evidence, tool_count=len(tools)) has_semantic_gaps = semantic_coverage.gap_count > 0 has_binding_gaps = binding_coverage.gap_count > 0 + has_policy_gaps = bool(report.policy_evidence_gaps) has_semantic_review_concerns = semantic_coverage.review_concern_count > 0 decision: ReleaseDecisionStatus @@ -263,6 +284,8 @@ def build_release_decision( # and --no-heuristics. One unresolved action is sufficient; healthy # actions never dilute it. decision = "insufficient_evidence" + elif has_policy_gaps: + decision = "insufficient_evidence" elif evidence_is_degraded: decision = "insufficient_evidence" elif ( @@ -292,7 +315,7 @@ def build_release_decision( fail_on=fail_on, new_findings_only=new_findings_only, release_decision=decision, - has_semantic_gaps=has_semantic_gaps or has_binding_gaps, + has_semantic_gaps=has_semantic_gaps or has_binding_gaps or has_policy_gaps, ) fail_policy = FailPolicy( ci_mode=ci_mode, @@ -648,6 +671,16 @@ def _semantic_gap( accepted_values = ["split_binding", "align_schema", "align_authority", "align_annotations"] action_why = "Conflicting bound observations cannot share one canonical capability." expects = "Split the binding or reconcile its structural evidence, then rerun verification." + elif kind == "invalid_evidence_provenance": + action_kind = "provide_policy_evidence" + accepted_values = [ + "reviewed_declaration", + "protocol_structure", + "typed_provider_fact", + "structural_scope", + ] + action_why = "Free-form source labels are not evidence of policy eligibility." + expects = "Use a typed first-party evidence producer and rerun verification." elif kind in { "missing_binding_evidence", "partial_binding_evidence", @@ -1001,6 +1034,7 @@ def _to_item(finding: Finding) -> ReleaseDecisionItem: policy_evidence_source=finding.policy_evidence_source, capability_refs=list(finding.capability_refs), capability_trace_refs=list(finding.capability_trace_refs), + support=finding.support, ) diff --git a/src/agents_shipgate/cli/discovery/local_contract.py b/src/agents_shipgate/cli/discovery/local_contract.py index 926bf1be..eba89a75 100644 --- a/src/agents_shipgate/cli/discovery/local_contract.py +++ b/src/agents_shipgate/cli/discovery/local_contract.py @@ -44,7 +44,7 @@ ) from agents_shipgate.schemas.verifier import VerifierArtifact -LOCAL_CONTRACT_SCHEMA_VERSION = "4" +LOCAL_CONTRACT_SCHEMA_VERSION = "5" LOCAL_CONTRACT_RELATIVE_PATH = ".shipgate/agent-contract.json" diff --git a/src/agents_shipgate/cli/mcp.py b/src/agents_shipgate/cli/mcp.py index 9fc67f8a..af5ada62 100644 --- a/src/agents_shipgate/cli/mcp.py +++ b/src/agents_shipgate/cli/mcp.py @@ -567,8 +567,7 @@ def _has_structural_side_effect(tool: Tool) -> bool: assessment = tool.semantic_assessment or assess_tool_semantics(tool) return any( claim.value != "read" - and claim.confidence == "high" - and claim.provenance_kind not in {"keyword_heuristic", "regex_heuristic"} + and claim.policy_eligible for claim in assessment.effect.claims ) diff --git a/src/agents_shipgate/cli/scan/decision.py b/src/agents_shipgate/cli/scan/decision.py index de8421a4..3573ac8c 100644 --- a/src/agents_shipgate/cli/scan/decision.py +++ b/src/agents_shipgate/cli/scan/decision.py @@ -22,6 +22,7 @@ compute_action_surface_diff, evaluate_action_surface_policies, ) +from agents_shipgate.core.policy_evidence import policy_evidence_gap from agents_shipgate.core.severity_overrides import resolve_severity_overrides from agents_shipgate.inputs.policy_packs import run_policy_pack_rules from agents_shipgate.schemas.manifest import AgentsShipgateManifest @@ -128,9 +129,32 @@ def _run_checks_and_decide( action_surface_diff, agent_id=tools_and_agent.agent.id, tools=tools_and_agent.tools, + policy_evidence_gaps=context.policy_evidence_gaps, ) ) findings = dedupe_findings(findings) + policy_eligible_findings = [] + for finding in findings: + support = finding.support + if support is None or support.policy_eligible: + policy_eligible_findings.append(finding) + continue + source = finding.policy_evidence_source or finding.source + context.policy_evidence_gaps.append( + policy_evidence_gap( + status=support.status, + subject=finding.tool_id or finding.tool_name or finding.agent_id or finding.check_id, + policy_id=finding.check_id, + source_ref=(source.path or source.ref) if source is not None else None, + support=support, + manifest_path=( + source.pointer + if source is not None and source.pointer + else f"/checks/{finding.check_id}" + ), + ) + ) + findings = policy_eligible_findings # v0.17 (M1) + v0.18 (PR #1): centralized aggregator covers every # catalog check with ``dynamic_default=True``. See # ``core/dynamic_defaults.py`` and ``severity_overrides.py`` for the diff --git a/src/agents_shipgate/cli/scan/final_report.py b/src/agents_shipgate/cli/scan/final_report.py index b8704e1d..a7a13072 100644 --- a/src/agents_shipgate/cli/scan/final_report.py +++ b/src/agents_shipgate/cli/scan/final_report.py @@ -88,6 +88,7 @@ def _build_final_report( policy_audit=sanitized.policy_audit, privacy_audit=sanitized.privacy_audit, heuristics_filter=sanitized.heuristics_filter, + policy_evidence_gaps=sanitized.policy_evidence_gaps, ) apply_capability_diff(report, sanitized.tools) # v0.20: reviewer_summary is built HERE — after apply_capability_diff diff --git a/src/agents_shipgate/cli/scan/models.py b/src/agents_shipgate/cli/scan/models.py index 4b8ac6ae..c314d450 100644 --- a/src/agents_shipgate/cli/scan/models.py +++ b/src/agents_shipgate/cli/scan/models.py @@ -182,3 +182,4 @@ class _SanitizedSurfaces: baseline_summary: Any privacy_audit: Any heuristics_filter: Any + policy_evidence_gaps: list[Any] diff --git a/src/agents_shipgate/cli/scan/sanitization.py b/src/agents_shipgate/cli/scan/sanitization.py index 32eed863..1aad9507 100644 --- a/src/agents_shipgate/cli/scan/sanitization.py +++ b/src/agents_shipgate/cli/scan/sanitization.py @@ -44,6 +44,7 @@ from agents_shipgate.schemas.report import ( BaselineSummary, CapabilityRuntimeEvidence, + EvidenceGap, LoadedPolicyPack, PolicyAudit, ) @@ -185,6 +186,15 @@ def _sanitize_for_output( stats=privacy_stats, path="capability_runtime_evidence", ) + public_policy_evidence_gaps = [ + sanitize_model( + gap, + EvidenceGap, + stats=privacy_stats, + path="policy_evidence_gaps[]", + ) + for gap in decision.context.policy_evidence_gaps + ] public_project = redact_data( public_manifest.project.model_dump(exclude_none=True), @@ -379,6 +389,7 @@ def _sanitize_for_output( baseline_summary=baseline_summary, privacy_audit=privacy_audit, heuristics_filter=decision.heuristics_filter, + policy_evidence_gaps=public_policy_evidence_gaps, ) diff --git a/src/agents_shipgate/core/agent_bindings.py b/src/agents_shipgate/core/agent_bindings.py index a82154ed..43b059f2 100644 --- a/src/agents_shipgate/core/agent_bindings.py +++ b/src/agents_shipgate/core/agent_bindings.py @@ -867,6 +867,11 @@ def _attach_binding_assessments( value=f"{item.agent_id}->{tool.id}", confidence=item.confidence, provenance_kind=item.provenance_kind, + basis=( + "reviewed_declaration" + if item.provenance_kind == "static_declaration" + else "typed_provider_fact" + ), source=item.source, source_pointer=item.source_pointer, evidence={"edge_type": item.edge_type, "complete": item.complete}, diff --git a/src/agents_shipgate/core/agent_handoff.py b/src/agents_shipgate/core/agent_handoff.py index cc833d72..fdac08bf 100644 --- a/src/agents_shipgate/core/agent_handoff.py +++ b/src/agents_shipgate/core/agent_handoff.py @@ -167,6 +167,9 @@ def _blocked_by(release_decision: dict[str, Any]) -> list[AgentHandoffBlockedBy] blocks_release=_bool_or_none(item.get("blocks_release")), capability_refs=_str_list(item.get("capability_refs")), capability_trace_refs=_str_list(item.get("capability_trace_refs")), + support_hash=_str_or_none( + _dict(item.get("support")).get("support_hash") + ), ) ) return out diff --git a/src/agents_shipgate/core/baseline.py b/src/agents_shipgate/core/baseline.py index d65a50c5..295ebad5 100644 --- a/src/agents_shipgate/core/baseline.py +++ b/src/agents_shipgate/core/baseline.py @@ -97,7 +97,12 @@ def baseline_from_report( if not fingerprint: continue prior_entry = prior_by_fp.get(fingerprint) - if prior_entry is not None and prior_entry.provenance is not None: + support_hash = finding.support.support_hash if finding.support is not None else None + if ( + prior_entry is not None + and prior_entry.provenance is not None + and prior_entry.support_hash == support_hash + ): provenance = prior_entry.provenance if apply_to_existing: provenance = provenance.model_copy( @@ -125,6 +130,7 @@ def baseline_from_report( fingerprint_version="2", severity=finding.severity, title=finding.title, + support_hash=support_hash, provenance=provenance, ) ) @@ -358,9 +364,12 @@ def apply_baseline( display_path: str, legacy_fingerprints: Sequence[LegacyFingerprintEntry] | None = None, ) -> BaselineSummary: - baseline_fingerprints = { - finding.fingerprint for finding in baseline.findings if finding.fingerprint + baseline_by_fingerprint = { + finding.fingerprint: finding + for finding in baseline.findings + if finding.fingerprint } + baseline_fingerprints = set(baseline_by_fingerprint) current_active_fingerprints: set[str] = set() matched_legacy_fingerprints: set[str] = set() tool_ids_by_name: dict[str, set[str]] = {} @@ -389,10 +398,21 @@ def apply_baseline( if not fingerprint: continue current_active_fingerprints.add(fingerprint) - if fingerprint in baseline_fingerprints: + entry = baseline_by_fingerprint.get(fingerprint) + current_support_hash = ( + finding.support.support_hash if finding.support is not None else None + ) + support_matches = ( + entry is not None and entry.support_hash == current_support_hash + ) + if fingerprint in baseline_fingerprints and support_matches: finding.baseline_status = "matched" matched += 1 - elif legacy_matches := baseline_fingerprints & legacy_candidates: + elif legacy_matches := { + candidate + for candidate in baseline_fingerprints & legacy_candidates + if baseline_by_fingerprint[candidate].support_hash == current_support_hash + }: finding.baseline_status = "matched" matched += 1 matched_legacy_fingerprints.update(legacy_matches) diff --git a/src/agents_shipgate/core/capability_lattice.py b/src/agents_shipgate/core/capability_lattice.py index 31a2b9b0..590132c7 100644 --- a/src/agents_shipgate/core/capability_lattice.py +++ b/src/agents_shipgate/core/capability_lattice.py @@ -205,17 +205,25 @@ def mcp_permission_risk_hints(tool: Tool) -> list[ToolRiskHint]: tag = _risk_tag_for_class(permission_class) if tag is None: continue + source = _permission_hint_source(tool, permission_class) + supporting_scopes = sorted( + scope + for scope in tool.auth.scopes + if permission_class in _classes_from_scopes([scope]) + ) hints.append( ToolRiskHint( tag=tag, - source=_permission_hint_source(tool, permission_class), + source=source, confidence=parse_confidence( _read_only_confidence(tool) if permission_class == "read" else "medium" ), + basis=_permission_hint_basis(source), evidence={ "permission_class": permission_class, "risk_score": profile.risk_score, "reasons": list(profile.reasons), + **({"scopes": supporting_scopes} if source == "auth_scope" else {}), }, ) ) @@ -225,12 +233,23 @@ def mcp_permission_risk_hints(tool: Tool) -> list[ToolRiskHint]: tag="unknown_side_effect", source="mcp_permission_lattice", confidence=parse_confidence("high"), + basis="protocol_default", evidence={"risk_score": profile.risk_score, "reasons": list(profile.reasons)}, ) ) return hints +def _permission_hint_basis(source: str) -> str: + if source == "mcp_annotation": + return "protocol_structure" + if source == "auth_scope": + return "structural_scope" + if source == "mcp_config": + return "protocol_default" + return "inferred_keyword" + + def is_secret_env_name(name: str) -> bool: return bool(_SECRET_NAME_RE.search(name or "")) diff --git a/src/agents_shipgate/core/capability_lock.py b/src/agents_shipgate/core/capability_lock.py index 4a348a90..a32d78f7 100644 --- a/src/agents_shipgate/core/capability_lock.py +++ b/src/agents_shipgate/core/capability_lock.py @@ -255,6 +255,7 @@ def _normalize_capability_lock_payload(payload: dict[str, Any]) -> dict[str, Any "0.2": "0.1", "0.3": "0.2", "0.4": "0.3", + "0.5": "0.4", CAPABILITY_LOCK_SCHEMA_VERSION: CAPABILITY_STANDARD_VERSION, } diff --git a/src/agents_shipgate/core/capability_policy.py b/src/agents_shipgate/core/capability_policy.py index 6dbcb0df..84fb1880 100644 --- a/src/agents_shipgate/core/capability_policy.py +++ b/src/agents_shipgate/core/capability_policy.py @@ -11,19 +11,31 @@ from agents_shipgate.core.artifacts import ArtifactBag from agents_shipgate.core.capabilities import build_capability_facts from agents_shipgate.core.domain import Tool, ToolParameter +from agents_shipgate.core.policy_evidence import ( + conjunction_status, + disjunction_status, + finding_support, + negated_disjunction_status, + predicate_evidence, +) from agents_shipgate.core.risk_hints import ( CANONICAL_RISK_TAG_MAP, risk_tags, ) from agents_shipgate.schemas.capabilities import CapabilityFactV1 -from agents_shipgate.schemas.common import SourceReference +from agents_shipgate.schemas.common import Confidence, SourceReference, confidence_rank from agents_shipgate.schemas.manifest import AgentsShipgateManifest from agents_shipgate.schemas.policy_pack import ( PolicyPackCapabilityMatch, PolicyPackMatch, PolicyPackParameterMatch, ) -from agents_shipgate.schemas.report import CapabilityPolicyEvidence +from agents_shipgate.schemas.report import ( + CapabilityPolicyEvidence, + FindingSupport, + PolicyMatchStatus, + PolicyPredicateEvidence, +) from agents_shipgate.schemas.surfaces import ActionFact, ActionSurfaceFacts @@ -50,9 +62,11 @@ class CapabilityPolicySubject: @dataclass(frozen=True) class CapabilityPolicyMatch: subject: CapabilityPolicySubject + status: PolicyMatchStatus evidence: dict[str, Any] matched_predicates: dict[str, Any] capability_policy_evidence: CapabilityPolicyEvidence + support: FindingSupport def build_capability_policy_subjects( @@ -106,6 +120,81 @@ def match_policy_pack_subject( *, environment_target: str | None, base_evidence: dict[str, Any] | None = None, +) -> CapabilityPolicyMatch: + """Evaluate a policy-pack selector without converting uncertainty to true. + + The legacy matcher remains the exact value comparator. This wrapper first + assesses whether every predicate has policy-eligible support. A lexical or + otherwise incomplete possible match is returned as ``indeterminate`` and + is routed to an evidence gap by the caller, never to a Finding. + """ + + predicate_rows = _assess_match_support( + subject, + rule_match, + environment_target=environment_target, + ) + support = finding_support(predicate_rows) + if support.status != "matched": + return CapabilityPolicyMatch( + subject=subject, + status=support.status, + evidence=dict(base_evidence or {}), + matched_predicates={}, + capability_policy_evidence=_capability_policy_evidence( + subject, + matched_predicates={}, + ), + support=support, + ) + + matched = _binary_match_policy_pack_subject( + subject, + rule_match, + environment_target=environment_target, + base_evidence=base_evidence, + ) + if matched is None: + rejected_support = finding_support( + [ + *predicate_rows, + predicate_evidence( + "exact_value_comparison", + "not_matched", + confidence="high", + evidence_bases=["protocol_structure"], + policy_eligible=True, + ), + ], + status="not_matched", + ) + return CapabilityPolicyMatch( + subject=subject, + status="not_matched", + evidence=dict(base_evidence or {}), + matched_predicates={}, + capability_policy_evidence=_capability_policy_evidence( + subject, + matched_predicates={}, + ), + support=rejected_support, + ) + return CapabilityPolicyMatch( + subject=subject, + status="matched", + evidence=matched.evidence, + matched_predicates=matched.matched_predicates, + capability_policy_evidence=matched.capability_policy_evidence, + support=support, + ) + + +def _binary_match_policy_pack_subject( + subject: CapabilityPolicySubject, + rule_match: PolicyPackMatch, + *, + environment_target: str | None, + base_evidence: dict[str, Any] | None = None, ) -> CapabilityPolicyMatch | None: evidence: dict[str, Any] = dict(base_evidence or {}) matched_predicates: dict[str, Any] = {} @@ -175,7 +264,7 @@ def match_policy_pack_subject( if rule_match.all_of: all_branches: list[dict[str, Any]] = [] for sub_match in rule_match.all_of: - sub = match_policy_pack_subject( + sub = _binary_match_policy_pack_subject( subject, sub_match, environment_target=environment_target ) if sub is None: @@ -185,13 +274,26 @@ def match_policy_pack_subject( matched_predicates["all_of"] = all_branches if rule_match.any_of: any_hit: dict[str, Any] | None = None + fallback_hit: dict[str, Any] | None = None for index, sub_match in enumerate(rule_match.any_of): - sub = match_policy_pack_subject( + sub = _binary_match_policy_pack_subject( subject, sub_match, environment_target=environment_target ) - if sub is not None: - any_hit = {"index": index, "matched": sub.matched_predicates} + if sub is None: + continue + candidate = {"index": index, "matched": sub.matched_predicates} + fallback_hit = fallback_hit or candidate + branch_support = finding_support( + _assess_match_support( + subject, + sub_match, + environment_target=environment_target, + ) + ) + if branch_support.status == "matched" and branch_support.policy_eligible: + any_hit = candidate break + any_hit = any_hit or fallback_hit if any_hit is None: return None evidence["any_of"] = any_hit @@ -199,7 +301,9 @@ def match_policy_pack_subject( if rule_match.none_of: for sub_match in rule_match.none_of: if ( - match_policy_pack_subject(subject, sub_match, environment_target=environment_target) + _binary_match_policy_pack_subject( + subject, sub_match, environment_target=environment_target + ) is not None ): return None @@ -207,15 +311,464 @@ def match_policy_pack_subject( return CapabilityPolicyMatch( subject=subject, + status="matched", evidence=evidence, matched_predicates=matched_predicates, capability_policy_evidence=_capability_policy_evidence( subject, matched_predicates=matched_predicates, ), + support=finding_support([]), + ) + + +def _assess_match_support( + subject: CapabilityPolicySubject, + rule_match: PolicyPackMatch, + *, + environment_target: str | None, +) -> list[PolicyPredicateEvidence]: + rows: list[PolicyPredicateEvidence] = [] + + if rule_match.risk_tags: + rows.append(_risk_tag_predicate(subject, "risk_tags", rule_match.risk_tags)) + if rule_match.source_types: + rows.append( + _exact_predicate( + "source_types", + subject.fact.evidence.source_type in rule_match.source_types, + expected=rule_match.source_types, + observed=subject.fact.evidence.source_type, + ) + ) + if rule_match.environment_targets: + rows.append( + _exact_predicate( + "environment_targets", + environment_target in rule_match.environment_targets, + expected=rule_match.environment_targets, + observed=environment_target, + basis="reviewed_declaration", + ) + ) + for field, actual in ( + ("missing_owner", _missing_owner(subject)), + ("missing_approval_policy", _missing_approval_policy(subject)), + ("missing_confirmation_policy", _missing_confirmation_policy(subject)), + ("missing_idempotency_policy", _missing_idempotency_policy(subject)), + ): + expected = getattr(rule_match, field) + if expected is not None: + rows.append( + _exact_predicate( + field, + actual is expected, + expected=expected, + observed=actual, + basis="reviewed_declaration", + ) + ) + if rule_match.missing_auth_scopes is not None: + rows.append( + _authority_predicate( + subject, + "missing_auth_scopes", + expected=rule_match.missing_auth_scopes, + observed=_missing_auth_scopes(subject), + ) + ) + if rule_match.parameters: + matched = matched_parameters_for_subject(subject, rule_match.parameters) + rows.append( + _exact_predicate( + "parameters", + len(matched) == len(rule_match.parameters), + expected=[item.model_dump(mode="json") for item in rule_match.parameters], + observed=matched, + ) + ) + if rule_match.capability is not None: + rows.extend(_assess_capability_support(subject, rule_match.capability)) + + for name, branches, reducer in ( + ("all_of", rule_match.all_of, conjunction_status), + ("any_of", rule_match.any_of, disjunction_status), + ("none_of", rule_match.none_of, negated_disjunction_status), + ): + if not branches: + continue + branch_support = [ + finding_support( + _assess_match_support( + subject, + branch, + environment_target=environment_target, + ) + ) + for branch in branches + ] + status = reducer(item.status for item in branch_support) + contributing = _contributing_branches(name, branch_support, status) + rows.append( + predicate_evidence( + name, + status, + expected={"branch_count": len(branches)}, + observed=[item.status for item in branch_support], + confidence=_weakest_confidence(contributing), + claim_ids=[ + claim_id for item in contributing for claim_id in item.claim_ids + ], + evidence_bases=[ + basis for item in contributing for basis in item.evidence_bases + ], + policy_eligible=( + status in {"matched", "not_matched"} + and all(_support_is_resolved(item) for item in contributing) + ), + why=( + None + if status in {"matched", "not_matched"} + else "at least one boolean-composition branch is not statically decidable" + ), + ) + ) + + if not rows: + rows.append( + predicate_evidence( + "capability_subject", + "matched", + observed=subject.fact.id, + confidence="high", + evidence_bases=["protocol_structure"], + policy_eligible=True, + ) + ) + return rows + + +def _assess_capability_support( + subject: CapabilityPolicySubject, + selector: PolicyPackCapabilityMatch, +) -> list[PolicyPredicateEvidence]: + fact = subject.fact + rows: list[PolicyPredicateEvidence] = [] + for field, actual in ( + ("tool_names", fact.identity.tool_name), + ("providers", fact.identity.provider), + ("operations", fact.identity.operation), + ("source_types", fact.evidence.source_type), + ("auth_types", fact.authority.auth_type), + ("credential_modes", fact.authority.credential_mode), + ): + expected = getattr(selector, field) + if expected: + rows.append( + _exact_predicate( + f"capability.{field}", + actual in expected, + expected=expected, + observed=actual, + ) + ) + if selector.effects: + rows.append(_effect_predicate(subject, "capability.effects", selector.effects)) + if selector.risk_tags: + rows.append( + _risk_tag_predicate(subject, "capability.risk_tags", selector.risk_tags) + ) + if selector.scopes: + rows.append( + _authority_predicate( + subject, + "capability.scopes", + expected=selector.scopes, + observed=sorted(set(fact.identity.scope).intersection(selector.scopes)), + matched=bool(set(fact.identity.scope).intersection(selector.scopes)), + ) + ) + if selector.broad_scope is not None: + actual = bool(fact.authority.broad_scopes) + rows.append( + _authority_predicate( + subject, + "capability.broad_scope", + expected=selector.broad_scope, + observed=actual, + matched=actual is selector.broad_scope, + ) + ) + facet_effects = { + "externally_visible": {"external_communication"}, + "handles_sensitive_data": {"privileged_data_access"}, + "financial": {"financial_write"}, + "code_execution": {"code_execution"}, + "high_risk": { + "destructive", + "external_communication", + "financial_write", + "production_operation", + "privileged_data_access", + "code_execution", + "identity_access", + }, + } + for field, effects in facet_effects.items(): + expected = getattr(selector, field) + if expected is not None: + rows.append( + _effect_predicate( + subject, + f"capability.{field}", + effects, + expected_boolean=expected, + ) + ) + for field, actual in ( + ("missing_owner", _missing_owner(subject)), + ("missing_approval_policy", _missing_approval_policy(subject)), + ("missing_confirmation_policy", _missing_confirmation_policy(subject)), + ("missing_idempotency_policy", _missing_idempotency_policy(subject)), + ): + expected = getattr(selector, field) + if expected is not None: + rows.append( + _exact_predicate( + f"capability.{field}", + actual is expected, + expected=expected, + observed=actual, + basis="reviewed_declaration", + ) + ) + if selector.missing_auth_scopes is not None: + rows.append( + _authority_predicate( + subject, + "capability.missing_auth_scopes", + expected=selector.missing_auth_scopes, + observed=_missing_auth_scopes(subject), + ) + ) + if selector.parameters: + matched = matched_parameters_for_subject(subject, selector.parameters) + rows.append( + _exact_predicate( + "capability.parameters", + len(matched) == len(selector.parameters), + expected=[item.model_dump(mode="json") for item in selector.parameters], + observed=matched, + ) + ) + return rows + + +def _effect_predicate( + subject: CapabilityPolicySubject, + predicate: str, + requested: Iterable[str], + *, + expected_boolean: bool | None = None, +) -> PolicyPredicateEvidence: + assessment = subject.tool.semantic_assessment + requested_values = set(requested) + claims = list(assessment.effect.claims) if assessment is not None else [] + eligible = [ + claim for claim in claims if claim.policy_eligible and claim.value in requested_values + ] + possible = [claim for claim in claims if claim.value in requested_values] + if assessment is None: + return predicate_evidence( + predicate, + "indeterminate", + expected=expected_boolean if expected_boolean is not None else sorted(requested_values), + observed=subject.fact.effect.effect, + confidence="low", + evidence_bases=["unknown"], + why="semantic assessment is missing", + ) + positive = bool(eligible) + expected_positive = True if expected_boolean is None else expected_boolean + if positive: + status: PolicyMatchStatus = "matched" if expected_positive else "not_matched" + selected = eligible + elif possible or assessment.effect.status in { + "inferred", + "unknown", + "protocol_default", + "conflicting", + }: + status = "conflicting" if assessment.effect.status == "conflicting" else "indeterminate" + selected = possible or claims + else: + status = "not_matched" if expected_positive else "matched" + selected = claims + return predicate_evidence( + predicate, + status, + expected=expected_boolean if expected_boolean is not None else sorted(requested_values), + observed=sorted({claim.value for claim in selected}), + confidence=_claim_confidence(selected), + claim_ids=[claim.claim_id for claim in selected], + evidence_bases=[claim.basis for claim in selected], + policy_eligible=( + status in {"matched", "not_matched"} + and bool(selected) + and all(claim.policy_eligible for claim in selected) + ), + why=( + None + if status in {"matched", "not_matched"} + else "effect classification is heuristic, unknown, or conflicting" + ), + ) + + +def _risk_tag_predicate( + subject: CapabilityPolicySubject, + predicate: str, + requested: Iterable[str], +) -> PolicyPredicateEvidence: + requested_values = set(requested) + requested_canonical = {_canonical_risk_tag(tag) for tag in requested_values} + assessment = subject.tool.semantic_assessment + claims = list(assessment.effect.claims) if assessment is not None else [] + + def claim_tag(claim: Any) -> str: + raw = claim.evidence.get("tag") if isinstance(claim.evidence, dict) else None + return _canonical_risk_tag(str(raw or claim.value)) + + matching = [claim for claim in claims if claim_tag(claim) in requested_canonical] + eligible = [claim for claim in matching if claim.policy_eligible] + if eligible: + status: PolicyMatchStatus = "matched" + selected = eligible + elif matching: + status = "indeterminate" + selected = matching + elif assessment is None or assessment.effect.status in { + "inferred", + "unknown", + "protocol_default", + "conflicting", + }: + status = "indeterminate" + selected = [] + else: + status = "not_matched" + selected = [] + return predicate_evidence( + predicate, + status, + expected=sorted(requested_values), + observed=sorted({claim_tag(claim) for claim in selected}), + confidence=_claim_confidence(selected), + claim_ids=[claim.claim_id for claim in selected], + evidence_bases=[claim.basis for claim in selected] or ["unknown"], + policy_eligible=status == "matched" and bool(eligible), + why=( + None + if status in {"matched", "not_matched"} + else "risk-tag classification is heuristic or semantically incomplete" + ), + ) + + +def _authority_predicate( + subject: CapabilityPolicySubject, + predicate: str, + *, + expected: Any, + observed: Any, + matched: bool | None = None, +) -> PolicyPredicateEvidence: + assessment = subject.tool.semantic_assessment + authority = assessment.authority if assessment is not None else None + if authority is None or authority.status in {"partial", "unknown", "conflicting"}: + return predicate_evidence( + predicate, + "conflicting" if authority is not None and authority.status == "conflicting" else "indeterminate", + expected=expected, + observed=observed, + confidence="low", + claim_ids=(claim.claim_id for claim in authority.claims) if authority else (), + evidence_bases=(claim.basis for claim in authority.claims) if authority else ["unknown"], + why="authority evidence is incomplete or conflicting", + ) + actual_match = (observed is expected) if matched is None else matched + return predicate_evidence( + predicate, + "matched" if actual_match else "not_matched", + expected=expected, + observed=observed, + confidence="high", + claim_ids=[claim.claim_id for claim in authority.claims], + evidence_bases=[claim.basis for claim in authority.claims], + policy_eligible=all(claim.policy_eligible for claim in authority.claims), ) +def _exact_predicate( + predicate: str, + matched: bool, + *, + expected: Any, + observed: Any, + basis: str = "protocol_structure", +) -> PolicyPredicateEvidence: + return predicate_evidence( + predicate, + "matched" if matched else "not_matched", + expected=expected, + observed=observed, + confidence="high", + evidence_bases=[basis], + policy_eligible=True, + ) + + +def _claim_confidence(claims: Iterable[Any]) -> Confidence: + values = [claim.confidence for claim in claims] + return min(values, key=confidence_rank) if values else "low" + + +def _weakest_confidence(supports: Iterable[FindingSupport]) -> Confidence: + values = [item.confidence for item in supports] + return min(values, key=confidence_rank) if values else "high" + + +def _support_is_resolved(support: FindingSupport) -> bool: + return support.status in {"matched", "not_matched"} and all( + row.policy_eligible for row in support.predicates + ) + + +def _contributing_branches( + name: str, + supports: list[FindingSupport], + status: PolicyMatchStatus, +) -> list[FindingSupport]: + if name == "any_of" and status == "matched": + authoritative = next( + ( + item + for item in supports + if item.status == "matched" and _support_is_resolved(item) + ), + None, + ) + return [ + authoritative + or next(item for item in supports if item.status == "matched") + ] + if name == "all_of" and status == "not_matched": + return [next(item for item in supports if item.status == "not_matched")] + if name == "none_of" and status == "not_matched": + return [next(item for item in supports if item.status == "matched")] + return supports + + def subject_requires_approval_review(subject: CapabilityPolicySubject) -> bool: return bool( _subject_control_effects(subject) @@ -241,12 +794,9 @@ def _subject_control_effects(subject: CapabilityPolicySubject) -> set[str]: if assessment is None: return {subject.fact.effect.effect} effects: set[str] = set() - if assessment.effect.status in {"declared", "structural"}: - effects.add(assessment.conservative_effect) for claim in assessment.effect.claims: if ( - claim.confidence == "high" - and claim.provenance_kind not in {"keyword_heuristic", "regex_heuristic"} + claim.policy_eligible and claim.value in { "read", diff --git a/src/agents_shipgate/core/context.py b/src/agents_shipgate/core/context.py index b18667a1..bc4b2fd0 100644 --- a/src/agents_shipgate/core/context.py +++ b/src/agents_shipgate/core/context.py @@ -28,7 +28,7 @@ from agents_shipgate.schemas.bindings import AgentBindingGraphAssessment from agents_shipgate.schemas.capabilities import CapabilityFactV1 from agents_shipgate.schemas.manifest import AgentsShipgateManifest -from agents_shipgate.schemas.report import CapabilityRuntimeEvidence +from agents_shipgate.schemas.report import CapabilityRuntimeEvidence, EvidenceGap from agents_shipgate.schemas.surfaces import ActionSurfaceFacts from agents_shipgate.schemas.verification import VerificationContext @@ -62,6 +62,7 @@ class ScanContext: capability_policy_subjects: list[CapabilityPolicySubject] = field( default_factory=list ) + policy_evidence_gaps: list[EvidenceGap] = field(default_factory=list) capability_runtime_evidence: CapabilityRuntimeEvidence = field( default_factory=CapabilityRuntimeEvidence ) diff --git a/src/agents_shipgate/core/domain.py b/src/agents_shipgate/core/domain.py index 2e6bf79e..87d14328 100644 --- a/src/agents_shipgate/core/domain.py +++ b/src/agents_shipgate/core/domain.py @@ -1,5 +1,7 @@ from __future__ import annotations +import hashlib +import json from typing import Any, Literal, get_args from pydantic import BaseModel, ConfigDict, Field, model_validator @@ -9,6 +11,39 @@ from agents_shipgate.schemas.surfaces import ActionEffect SemanticDimension = Literal["identity", "binding", "effect", "authority"] +EvidenceBasis = Literal[ + "reviewed_declaration", + "protocol_structure", + "typed_provider_fact", + "structural_scope", + "inferred_keyword", + "inferred_regex", + "protocol_default", + "unknown", +] +POLICY_ELIGIBLE_EVIDENCE_BASES = frozenset( + { + "reviewed_declaration", + "protocol_structure", + "typed_provider_fact", + "structural_scope", + } +) + + +def provenance_for_evidence_basis( + basis: EvidenceBasis, + fallback: ProvenanceKind = "static_declaration", +) -> ProvenanceKind: + if basis == "inferred_keyword": + return "keyword_heuristic" + if basis == "inferred_regex": + return "regex_heuristic" + if basis == "typed_provider_fact": + return "ast_extraction" + if basis in POLICY_ELIGIBLE_EVIDENCE_BASES or basis == "protocol_default": + return "static_declaration" + return fallback IdentityEvidenceStatus = Literal[ "declared", "structural", @@ -55,6 +90,7 @@ "unresolved_bound_tool", "incomplete_handoff_graph", "invalid_binding_annotation", + "invalid_evidence_provenance", ] @@ -63,14 +99,50 @@ class SemanticClaim(BaseModel): model_config = ConfigDict(extra="forbid", frozen=True) + claim_id: str | None = None dimension: SemanticDimension value: str confidence: Confidence provenance_kind: ProvenanceKind + basis: EvidenceBasis = "unknown" + policy_eligible: bool = False source: str source_pointer: str | None = None evidence: dict[str, Any] = Field(default_factory=dict) + @model_validator(mode="before") + @classmethod + def derive_evidence_contract(cls, value: Any) -> Any: + if not isinstance(value, dict): + return value + payload = dict(value) + basis = payload.get("basis", "unknown") + confidence = payload.get("confidence", "low") + payload["provenance_kind"] = provenance_for_evidence_basis( + basis, + payload.get("provenance_kind", "static_declaration"), + ) + eligible = basis in POLICY_ELIGIBLE_EVIDENCE_BASES and confidence == "high" + payload["policy_eligible"] = eligible + if not payload.get("claim_id"): + identity = { + "dimension": payload.get("dimension"), + "value": payload.get("value"), + "confidence": confidence, + "basis": basis, + "source": payload.get("source"), + "source_pointer": payload.get("source_pointer"), + "evidence": payload.get("evidence") or {}, + } + rendered = json.dumps( + identity, + sort_keys=True, + separators=(",", ":"), + default=str, + ) + payload["claim_id"] = "clm_" + hashlib.sha256(rendered.encode()).hexdigest()[:20] + return payload + class SemanticIssue(BaseModel): """An unsatisfied evidence condition that prevents an evidence-backed pass.""" @@ -225,8 +297,23 @@ class ToolRiskHint(BaseModel): tag: str source: str confidence: Confidence + basis: EvidenceBasis = "unknown" + provenance_kind: ProvenanceKind = "static_declaration" evidence: dict[str, Any] = Field(default_factory=dict) + @model_validator(mode="before") + @classmethod + def bind_provenance_to_basis(cls, value: Any) -> Any: + if not isinstance(value, dict): + return value + payload = dict(value) + basis = payload.get("basis", "unknown") + payload["provenance_kind"] = provenance_for_evidence_basis( + basis, + payload.get("provenance_kind", "static_declaration"), + ) + return payload + class Tool(BaseModel): model_config = ConfigDict(extra="allow") diff --git a/src/agents_shipgate/core/findings/report_builder.py b/src/agents_shipgate/core/findings/report_builder.py index e5adc0e2..c77f3fa5 100644 --- a/src/agents_shipgate/core/findings/report_builder.py +++ b/src/agents_shipgate/core/findings/report_builder.py @@ -9,6 +9,7 @@ from agents_shipgate.schemas.report import ( BaselineSummary, CapabilityRuntimeEvidence, + EvidenceGap, Finding, HeuristicsFilter, LoadedPolicyPack, @@ -66,6 +67,7 @@ def build_report( policy_audit: PolicyAudit | None = None, privacy_audit: PrivacyAudit | None = None, heuristics_filter: HeuristicsFilter | None = None, + policy_evidence_gaps: list[EvidenceGap] | None = None, ) -> ReadinessReport: report = ReadinessReport( run_id=run_id, @@ -105,6 +107,7 @@ def build_report( tool_inventory=tool_inventory(tools), tool_catalog=tool_inventory(tool_catalog if tool_catalog is not None else tools), source_warnings=source_warnings or [], + policy_evidence_gaps=policy_evidence_gaps or [], # v0.17 (M1): policy audit envelope. Always present on emitted # scans (empty when no overrides applied) so consumers can read # ``report.policy_audit.severity_overrides_applied`` without a diff --git a/src/agents_shipgate/core/lenses/action_surface.py b/src/agents_shipgate/core/lenses/action_surface.py index 9711cb8f..a2240c4d 100644 --- a/src/agents_shipgate/core/lenses/action_surface.py +++ b/src/agents_shipgate/core/lenses/action_surface.py @@ -12,6 +12,11 @@ from agents_shipgate.core.errors import ConfigError from agents_shipgate.core.heuristics import is_broad_scope from agents_shipgate.core.lenses.tool_surface import ToolSurfaceDiffReference, _stable_hash +from agents_shipgate.core.policy_evidence import ( + finding_support, + policy_evidence_gap, + predicate_evidence, +) from agents_shipgate.core.risk_hints import ( derive_side_effect, is_effectively_read_only, @@ -22,13 +27,19 @@ from agents_shipgate.schemas.common import ( Severity, SourceReference, + confidence_rank, ) from agents_shipgate.schemas.manifest import ( ActionDeclarationConfig, ActionPolicyConfig, AgentsShipgateManifest, ) -from agents_shipgate.schemas.report import Finding +from agents_shipgate.schemas.report import ( + EvidenceGap, + Finding, + FindingSupport, + PolicyPredicateEvidence, +) from agents_shipgate.schemas.semantic import ToolSemanticEvidence from agents_shipgate.schemas.surfaces import ( ActionApprovalFact, @@ -273,6 +284,7 @@ def evaluate_action_surface_policies( *, agent_id: str, tools: list[Tool] | None = None, + policy_evidence_gaps: list[EvidenceGap] | None = None, ) -> list[Finding]: """Evaluate action-surface policies for a current action snapshot. @@ -328,6 +340,24 @@ def evaluate_action_surface_policies( ) ) + if policy_evidence_gaps is not None: + for action in facts.actions: + support = _non_authoritative_effect_escalation_support(action) + if support is None: + continue + policy_evidence_gaps.append( + policy_evidence_gap( + status=support.status, + subject=f"{action.tool_name} [{action.tool_id}]", + policy_id="builtin-effect-control-applicability", + source_ref=action.source_ref, + support=support, + manifest_path=( + f"shipgate.yaml#action_surface.actions[tool={action.tool_name!r}].effect" + ), + ) + ) + findings.extend( _builtin_policy_findings( manifest, @@ -340,7 +370,21 @@ def evaluate_action_surface_policies( ) for policy in manifest.action_surface.policies: for action in facts.actions: - if not _policy_matches(policy, action): + match_status, support = _assess_action_policy_match(policy, action) + if match_status == "not_matched": + continue + if match_status != "matched" or not support.policy_eligible: + if policy_evidence_gaps is not None: + policy_evidence_gaps.append( + policy_evidence_gap( + status=match_status, + subject=f"{action.tool_name} [{action.tool_id}]", + policy_id=policy.id, + source_ref=action.source_ref, + support=support, + manifest_path=f"shipgate.yaml#action_surface.policies/{policy.id}/match", + ) + ) continue missing, observed = _missing_requirements(policy, action) if not missing: @@ -365,7 +409,8 @@ def evaluate_action_surface_policies( evidence=evidence, recommendation=policy.recommendation or (f"Satisfy action surface policy {policy.id} for {action.tool_name}."), - blocks_release=policy.block, + blocks_release=policy.block and support.blocking_eligible, + support=support, ) ) return _dedupe_findings(findings) @@ -445,9 +490,7 @@ def build_action( semantic_effects = { claim.value for claim in semantic_assessment.effect.claims - if claim.confidence == "high" - and claim.provenance_kind not in {"keyword_heuristic", "regex_heuristic"} - and claim.value in ACTION_EFFECT_RANK + if claim.policy_eligible and claim.value in ACTION_EFFECT_RANK } risk_tag_values = sorted( set(risk_tag_values) @@ -1436,6 +1479,11 @@ def _current_action_policy_findings( "safeguards.idempotency for this financial write action." ), blocks_release=True, + support=_builtin_control_support( + action, + effects={"financial_write"}, + missing=missing, + ), ) ) if "external_communication" in control_effects: @@ -1465,6 +1513,11 @@ def _current_action_policy_findings( "this external communication action." ), blocks_release=True, + support=_builtin_control_support( + action, + effects={"external_communication"}, + missing=missing, + ), ) ) if "destructive" in control_effects: @@ -1494,6 +1547,11 @@ def _current_action_policy_findings( "safeguards.rollback for this destructive action." ), blocks_release=True, + support=_builtin_control_support( + action, + effects={"destructive"}, + missing=missing, + ), ) ) high_impact_effects = control_effects.intersection({"production_operation", "code_execution"}) @@ -1530,6 +1588,11 @@ def _current_action_policy_findings( "code-execution actions." ), blocks_release=True, + support=_builtin_control_support( + action, + effects=high_impact_effects, + missing=missing, + ), ) ) return findings @@ -1566,18 +1629,171 @@ def _control_effects(action: ActionFact) -> set[str]: if assessment is None: return {action.effect} effects: set[str] = set() - if assessment.effect.status in {"declared", "structural"}: - effects.add(action.effect) for claim in assessment.effect.claims: - if ( - claim.confidence == "high" - and claim.provenance_kind not in {"keyword_heuristic", "regex_heuristic"} - and claim.value in ACTION_EFFECT_RANK - ): + if claim.policy_eligible and claim.value in ACTION_EFFECT_RANK: effects.add(claim.value) return effects +def _non_authoritative_effect_escalation_support( + action: ActionFact, +) -> FindingSupport | None: + """Return unresolved support when weaker evidence outranks proven semantics. + + Hard controls intentionally consume only policy-eligible claims. Silently + discarding a higher-risk non-authoritative claim would turn uncertainty + into a clean pass, so this support record routes the action to insufficient + evidence without laundering that claim into a blocker. + """ + + assessment = action.semantic_assessment + if assessment is None: + return None + authoritative = [ + claim + for claim in assessment.effect.claims + if claim.policy_eligible and claim.value in ACTION_EFFECT_RANK + ] + if not authoritative: + return None + authoritative_rank = max( + ACTION_EFFECT_RANK[claim.value] for claim in authoritative + ) + inferred_escalations = [ + claim + for claim in assessment.effect.claims + if not claim.policy_eligible + and claim.value in ACTION_EFFECT_RANK + and ACTION_EFFECT_RANK[claim.value] > authoritative_rank + ] + if not inferred_escalations: + return None + strongest_rank = max( + ACTION_EFFECT_RANK[claim.value] for claim in inferred_escalations + ) + strongest_authoritative = [ + claim + for claim in authoritative + if ACTION_EFFECT_RANK[claim.value] == authoritative_rank + ] + strongest_inferred = [ + claim + for claim in inferred_escalations + if ACTION_EFFECT_RANK[claim.value] == strongest_rank + ] + return finding_support( + [ + predicate_evidence( + "authoritative_effect_bound", + "matched", + expected="reviewed or structural effect evidence", + observed=sorted( + {claim.value for claim in strongest_authoritative} + ), + confidence=min( + (claim.confidence for claim in strongest_authoritative), + key=confidence_rank, + ), + claim_ids=[claim.claim_id for claim in strongest_authoritative], + evidence_bases=[ + claim.basis for claim in strongest_authoritative + ], + policy_eligible=True, + ), + predicate_evidence( + "higher_effect_control_applicability", + "indeterminate", + expected="reviewed or structural evidence for the higher effect", + observed=sorted({claim.value for claim in strongest_inferred}), + confidence=min( + (claim.confidence for claim in strongest_inferred), + key=confidence_rank, + ), + claim_ids=[claim.claim_id for claim in strongest_inferred], + evidence_bases=[claim.basis for claim in strongest_inferred], + policy_eligible=False, + why=( + "higher-risk effect evidence is heuristic and cannot be " + "silently excluded from control applicability" + ), + ), + ], + status="indeterminate", + ) + + +def _builtin_control_support( + action: ActionFact, + *, + effects: set[str], + missing: list[str], +) -> FindingSupport: + assessment = action.semantic_assessment + claims = ( + [ + claim + for claim in assessment.effect.claims + if claim.policy_eligible and claim.value in effects + ] + if assessment is not None + else [] + ) + rows: list[PolicyPredicateEvidence] = [] + if claims: + rows.append( + predicate_evidence( + "builtin_control_effect", + "matched", + expected=sorted(effects), + observed=sorted({claim.value for claim in claims}), + confidence=min( + (claim.confidence for claim in claims), + key=confidence_rank, + ), + claim_ids=[claim.claim_id for claim in claims], + evidence_bases=[claim.basis for claim in claims], + policy_eligible=True, + ) + ) + elif assessment is None and action.effect in effects: + rows.append( + predicate_evidence( + "builtin_control_effect", + "matched", + expected=sorted(effects), + observed=action.effect, + confidence="high", + evidence_bases=["protocol_structure"], + policy_eligible=True, + why="compatibility projection from a normalized action fact", + ) + ) + else: + rows.append( + predicate_evidence( + "builtin_control_effect", + "indeterminate", + expected=sorted(effects), + observed=action.effect, + confidence="low", + evidence_bases=["unknown"], + why="no authoritative effect claim supports this built-in control", + ) + ) + rows.append( + predicate_evidence( + "missing_builtin_controls", + "matched", + expected="all required controls present", + observed=sorted(missing), + confidence="high", + evidence_bases=["protocol_structure"], + policy_eligible=True, + ) + ) + return finding_support(rows) + + def _effect_can_drive_hard_controls( action: ActionFact, tool: Tool | None, @@ -1610,23 +1826,144 @@ def _missing_builtin_requirements( return missing -def _policy_matches(policy: ActionPolicyConfig, action: ActionFact) -> bool: +def _assess_action_policy_match( + policy: ActionPolicyConfig, + action: ActionFact, +) -> tuple[str, FindingSupport]: match = policy.match - if match.action_ids and action.action_id not in match.action_ids: - return False - if match.tool_ids and action.tool_id not in match.tool_ids: - return False - if match.tools and action.tool_name not in match.tools: - return False - if match.effects and action.effect not in match.effects: - return False - if match.risk_tags and not set(_normalize_risk_tag_values(match.risk_tags)).intersection( - action.risk_tags + rows = [] + for name, expected, observed in ( + ("action_ids", match.action_ids, action.action_id), + ("tool_ids", match.tool_ids, action.tool_id), + ("tools", match.tools, action.tool_name), ): - return False - if match.scopes and not set(match.scopes).intersection(action.required_scopes): - return False - return True + if expected: + rows.append( + predicate_evidence( + name, + "matched" if observed in expected else "not_matched", + expected=expected, + observed=observed, + confidence="high", + evidence_bases=["protocol_structure"], + policy_eligible=True, + ) + ) + + assessment = action.semantic_assessment + claims = list(assessment.effect.claims) if assessment is not None else [] + if match.effects: + eligible = [ + claim + for claim in claims + if claim.policy_eligible and claim.value in match.effects + ] + possible = [claim for claim in claims if claim.value in match.effects] + rows.append( + _semantic_action_predicate( + "effects", + expected=list(match.effects), + eligible=eligible, + possible=possible, + assessment_status=(assessment.effect.status if assessment else "unknown"), + ) + ) + if match.risk_tags: + requested = set(_normalize_risk_tag_values(match.risk_tags)) + eligible = [ + claim + for claim in claims + if claim.policy_eligible and _RISK_TAG_MAP.get(claim.value, claim.value) in requested + ] + possible = [ + claim + for claim in claims + if _RISK_TAG_MAP.get(claim.value, claim.value) in requested + ] + rows.append( + _semantic_action_predicate( + "risk_tags", + expected=sorted(requested), + eligible=eligible, + possible=possible, + assessment_status=(assessment.effect.status if assessment else "unknown"), + ) + ) + if match.scopes: + authority = assessment.authority if assessment is not None else None + matched_scopes = sorted(set(match.scopes).intersection(action.required_scopes)) + if authority is None or authority.status in {"partial", "unknown", "conflicting"}: + rows.append( + predicate_evidence( + "scopes", + "conflicting" + if authority is not None and authority.status == "conflicting" + else "indeterminate", + expected=match.scopes, + observed=matched_scopes, + confidence="low", + claim_ids=[claim.claim_id for claim in authority.claims] if authority else [], + evidence_bases=[claim.basis for claim in authority.claims] + if authority + else ["unknown"], + why="authority scope evidence is incomplete or conflicting", + ) + ) + else: + rows.append( + predicate_evidence( + "scopes", + "matched" if matched_scopes else "not_matched", + expected=match.scopes, + observed=matched_scopes, + confidence="high", + claim_ids=[claim.claim_id for claim in authority.claims], + evidence_bases=[claim.basis for claim in authority.claims], + policy_eligible=all(claim.policy_eligible for claim in authority.claims), + ) + ) + support = finding_support(rows) + return support.status, support + + +def _semantic_action_predicate( + predicate: str, + *, + expected: list[str], + eligible: list[Any], + possible: list[Any], + assessment_status: str, +) -> PolicyPredicateEvidence: + if eligible: + status = "matched" + selected = eligible + elif possible or assessment_status in {"inferred", "unknown", "protocol_default", "conflicting"}: + status = "conflicting" if assessment_status == "conflicting" else "indeterminate" + selected = possible + else: + status = "not_matched" + selected = [] + return predicate_evidence( + predicate, + status, + expected=expected, + observed=sorted({claim.value for claim in selected}), + confidence=( + min( + (claim.confidence for claim in selected), + key=confidence_rank, + default="low", + ) + ), + claim_ids=[claim.claim_id for claim in selected], + evidence_bases=[claim.basis for claim in selected] or ["unknown"], + policy_eligible=status == "matched" and all(claim.policy_eligible for claim in selected), + why=( + None + if status in {"matched", "not_matched"} + else "action semantics are heuristic, unknown, or conflicting" + ), + ) def _missing_requirements( @@ -1686,7 +2023,20 @@ def _finding( evidence: dict[str, Any], recommendation: str, blocks_release: bool, + support: FindingSupport | None = None, ) -> Finding: + support = support or finding_support( + [ + predicate_evidence( + "deterministic_action_rule", + "matched", + observed=action.action_id, + confidence="high", + evidence_bases=["protocol_structure"], + policy_eligible=True, + ) + ] + ) return Finding( check_id=check_id, title=title, @@ -1696,7 +2046,7 @@ def _finding( tool_name=action.tool_name, agent_id=agent_id, evidence=evidence, - confidence="high", + confidence=support.confidence, provenance_kind="static_declaration", source=SourceReference( type=action.source_type or "action_surface", @@ -1709,7 +2059,8 @@ def _finding( pointer=action.source_pointer, ), recommendation=recommendation, - blocks_release=blocks_release, + blocks_release=blocks_release and support.blocking_eligible, + support=support, ) diff --git a/src/agents_shipgate/core/policy_evidence.py b/src/agents_shipgate/core/policy_evidence.py new file mode 100644 index 00000000..72592df4 --- /dev/null +++ b/src/agents_shipgate/core/policy_evidence.py @@ -0,0 +1,198 @@ +from __future__ import annotations + +import hashlib +import json +from collections.abc import Iterable +from typing import Any, cast + +from agents_shipgate.schemas.common import Confidence, confidence_rank +from agents_shipgate.schemas.report import ( + EvidenceGap, + EvidenceGapAction, + FindingSupport, + PolicyMatchStatus, + PolicyPredicateEvidence, +) + +_CONFIDENCE_BY_RANK: dict[int, Confidence] = { + confidence_rank("low"): "low", + confidence_rank("medium"): "medium", + confidence_rank("high"): "high", +} + + +def predicate_evidence( + predicate: str, + status: PolicyMatchStatus, + *, + expected: Any = None, + observed: Any = None, + confidence: Confidence = "low", + claim_ids: Iterable[str | None] = (), + evidence_bases: Iterable[str] = (), + policy_eligible: bool = False, + why: str | None = None, +) -> PolicyPredicateEvidence: + return PolicyPredicateEvidence( + predicate=predicate, + status=status, + expected=expected, + observed=observed, + confidence=confidence, + claim_ids=sorted({value for value in claim_ids if value}), + evidence_bases=sorted(set(evidence_bases)), + policy_eligible=policy_eligible, + why=why, + ) + + +def finding_support( + predicates: Iterable[PolicyPredicateEvidence], + *, + requested_confidence: Confidence = "high", + status: PolicyMatchStatus | None = None, +) -> FindingSupport: + rows = sorted( + predicates, + key=lambda item: ( + item.predicate, + item.status, + json.dumps(item.expected, sort_keys=True, default=str), + json.dumps(item.observed, sort_keys=True, default=str), + ), + ) + if not rows: + rows = [ + predicate_evidence( + "capability_subject", + "indeterminate", + observed=None, + confidence="low", + evidence_bases=["unknown"], + policy_eligible=False, + why="no predicate evidence was supplied", + ) + ] + resolved_status = status or conjunction_status(row.status for row in rows) + evidence_confidence_rank = min(confidence_rank(row.confidence) for row in rows) + effective_rank = min(confidence_rank(requested_confidence), evidence_confidence_rank) + confidence = _CONFIDENCE_BY_RANK[effective_rank] + eligible = ( + resolved_status == "matched" + and all(row.status == "matched" and row.policy_eligible for row in rows) + ) + claim_ids = sorted({claim_id for row in rows for claim_id in row.claim_ids}) + bases = sorted({basis for row in rows for basis in row.evidence_bases}) + payload = { + "status": resolved_status, + "confidence": confidence, + "policy_eligible": eligible, + "claim_ids": claim_ids, + "evidence_bases": bases, + "predicates": [row.model_dump(mode="json") for row in rows], + } + digest = hashlib.sha256( + json.dumps(payload, sort_keys=True, separators=(",", ":"), default=str).encode() + ).hexdigest() + return FindingSupport( + **payload, + blocking_eligible=eligible, + support_hash=f"sha256:{digest}", + ) + + +def conjunction_status(statuses: Iterable[PolicyMatchStatus]) -> PolicyMatchStatus: + values = list(statuses) + if any(value == "not_matched" for value in values): + return "not_matched" + if any(value == "conflicting" for value in values): + return "conflicting" + if any(value == "indeterminate" for value in values): + return "indeterminate" + return "matched" + + +def disjunction_status(statuses: Iterable[PolicyMatchStatus]) -> PolicyMatchStatus: + values = list(statuses) + if any(value == "matched" for value in values): + return "matched" + if any(value == "conflicting" for value in values): + return "conflicting" + if any(value == "indeterminate" for value in values): + return "indeterminate" + return "not_matched" + + +def negated_disjunction_status(statuses: Iterable[PolicyMatchStatus]) -> PolicyMatchStatus: + value = disjunction_status(statuses) + if value == "matched": + return "not_matched" + if value == "not_matched": + return "matched" + return value + + +def policy_evidence_gap( + *, + status: PolicyMatchStatus, + subject: str, + policy_id: str, + source_ref: str | None, + support: FindingSupport, + manifest_path: str, + rerun_command: str = ( + "agents-shipgate verify --workspace . --config shipgate.yaml " + "--ci-mode advisory --format json" + ), +) -> EvidenceGap: + bases = set(support.evidence_bases) + if status == "conflicting": + kind = "conflicting_policy_evidence" + action_kind = "resolve_policy_evidence_conflict" + why = "Policy applicability has conflicting authoritative evidence." + elif bases and bases <= {"inferred_keyword", "inferred_regex"}: + kind = "inferred_policy_applicability" + action_kind = "provide_policy_evidence" + why = "Policy applicability is supported only by heuristic evidence." + elif bases & {"inferred_keyword", "inferred_regex"} and bases & { + "reviewed_declaration", + "protocol_structure", + "typed_provider_fact", + "structural_scope", + }: + kind = "mixed_policy_evidence" + action_kind = "review_policy_evidence" + why = "Policy applicability mixes authoritative and heuristic evidence." + else: + kind = "unknown_policy_evidence" + action_kind = "provide_policy_evidence" + why = "Policy applicability cannot be established from complete static evidence." + return EvidenceGap( + kind=cast(Any, kind), + subject=subject, + source_ref=source_ref, + why=f"{policy_id}: {why}", + next_action=EvidenceGapAction( + kind=cast(Any, action_kind), + command=rerun_command, + path=manifest_path, + why="Heuristic or unknown evidence cannot create a blocking policy finding.", + expects="Provide reviewed or structural evidence for every indeterminate predicate.", + accepted_values=[ + "reviewed_declaration", + "protocol_structure", + "typed_provider_fact", + "structural_scope", + ], + ), + ) + + +__all__ = [ + "conjunction_status", + "disjunction_status", + "finding_support", + "negated_disjunction_status", + "policy_evidence_gap", + "predicate_evidence", +] diff --git a/src/agents_shipgate/core/risk_hints.py b/src/agents_shipgate/core/risk_hints.py index 07f02f6b..b07b6c7b 100644 --- a/src/agents_shipgate/core/risk_hints.py +++ b/src/agents_shipgate/core/risk_hints.py @@ -192,6 +192,47 @@ def is_write_tool(tool: Tool) -> bool: return assessment.conservative_effect != "read" +def policy_eligible_effects(tool: Tool) -> set[str]: + """Effects allowed to drive hard controls and release policy. + + The conservative bound may include lexical evidence for safety routing, + but those values are deliberately absent here. This helper is the only + compatibility projection policy consumers should use when a match can + affect the release decision. Conservative diagnostic helpers deliberately + continue to include inferred upper bounds. + """ + + from agents_shipgate.core.semantic_assessment import assess_tool_semantics + + assessment = tool.semantic_assessment or assess_tool_semantics(tool) + if assessment.effect.status not in {"declared", "structural"}: + return set() + return { + claim.value + for claim in assessment.effect.claims + if claim.policy_eligible + } + + +def is_policy_eligible_high_risk_tool(tool: Tool) -> bool: + return bool( + policy_eligible_effects(tool) + & { + "destructive", + "external_communication", + "financial_write", + "production_operation", + "privileged_data_access", + "code_execution", + "identity_access", + } + ) + + +def is_policy_eligible_write_tool(tool: Tool) -> bool: + return bool(policy_eligible_effects(tool) - {"read"}) + + def _tokenize(text: str) -> set[str]: return set(re.findall(r"[a-z]+", text.lower())) @@ -208,7 +249,14 @@ def _add_automatic_hints(tool: Tool) -> None: # at high confidence and exempt from name-keyword write inference. is_sdk_preview = tool.source_type == "sdk_function" and "preview" in tokens and not method if is_sdk_preview: - _add_hint(tool, "read_only", "keyword", "high", {"preview_only": True}) + _add_hint( + tool, + "read_only", + "keyword", + "high", + {"preview_only": True}, + basis="inferred_keyword", + ) keyword_eligible = tool.source_type in _KEYWORD_GATED_SOURCE_TYPES and not is_sdk_preview keyword_source = ( @@ -222,14 +270,37 @@ def _add_automatic_hints(tool: Tool) -> None: ) if keyword_eligible and READ_ONLY_KEYWORDS & tokens: - _add_hint(tool, "read_only", keyword_source, "medium", {}) + _add_hint( + tool, "read_only", keyword_source, "medium", {}, basis="inferred_keyword" + ) if keyword_eligible and WRITE_KEYWORDS & tokens: - _add_hint(tool, "write", keyword_source, "medium", {}) + _add_hint(tool, "write", keyword_source, "medium", {}, basis="inferred_keyword") if tool.annotations.get("readOnlyHint") is True: - _add_hint(tool, "read_only", "mcp_annotation", "high", {"readOnlyHint": True}) + _add_hint( + tool, + "read_only", + "mcp_annotation", + "high", + {"readOnlyHint": True}, + basis="protocol_structure", + ) if tool.annotations.get("destructiveHint") is True: - _add_hint(tool, "destructive", "mcp_annotation", "high", {"destructiveHint": True}) - _add_hint(tool, "write", "mcp_annotation", "high", {"destructiveHint": True}) + _add_hint( + tool, + "destructive", + "mcp_annotation", + "high", + {"destructiveHint": True}, + basis="protocol_structure", + ) + _add_hint( + tool, + "write", + "mcp_annotation", + "high", + {"destructiveHint": True}, + basis="protocol_structure", + ) if method == "DELETE" or DESTRUCTIVE_KEYWORDS & tokens: _add_hint( @@ -242,9 +313,17 @@ def _add_automatic_hints(tool: Tool) -> None: "openapi_method" if method == "DELETE" else "keyword", "high" if method == "DELETE" else "medium", {"method": method or None}, + basis=("protocol_structure" if method == "DELETE" else "inferred_keyword"), ) if method in {"POST", "PUT", "PATCH", "DELETE"}: - _add_hint(tool, "write", "openapi_method", "high", {"method": method}) + _add_hint( + tool, + "write", + "openapi_method", + "high", + {"method": method}, + basis="protocol_structure", + ) # Hint generation precedes the one declaration-aware semantic assessment. # Calling the full resolver from inside its own input-enrichment phase used @@ -276,6 +355,10 @@ def _add_automatic_hints(tool: Tool) -> None: "auth_scope" if financial_in_scope else "keyword", confidence, {"scopes": tool.auth.scopes, "method": method or None}, + # The write verb itself is structural evidence. A topical word + # such as `refund` inside a scope is still lexical classification + # and must never become authoritative financial evidence. + basis="inferred_keyword", ) if COMMS_KEYWORDS & (tokens | scope_tokens): # The previous staged resolver saw a just-added financial hint as a @@ -288,25 +371,62 @@ def _add_automatic_hints(tool: Tool) -> None: confidence = ( "high" if comms_write else "low" if comms_read_only else "medium" ) - _add_hint(tool, "customer_communication", "keyword", confidence, {"method": method or None}) + _add_hint( + tool, + "customer_communication", + "keyword", + confidence, + {"method": method or None}, + basis="inferred_keyword", + ) # Once customer_communication existed, the former staged assessment # was no longer effectively read-only, even when its confidence was # low. Preserve that exact hint set without invoking the resolver. if EXTERNAL_ACTION_KEYWORDS & tokens: - _add_hint(tool, "external_write", "keyword", confidence, {"method": method or None}) + _add_hint( + tool, + "external_write", + "keyword", + confidence, + {"method": method or None}, + basis="inferred_keyword", + ) if SENSITIVE_KEYWORDS & (tokens | scope_tokens): - _add_hint(tool, "sensitive_data_access", "keyword", "medium", {}) + _add_hint( + tool, + "sensitive_data_access", + "keyword", + "medium", + {}, + basis="inferred_keyword", + ) if CODE_EXEC_KEYWORDS & tokens: - _add_hint(tool, "code_execution", "keyword", "medium", {}) + _add_hint( + tool, "code_execution", "keyword", "medium", {}, basis="inferred_keyword" + ) if INFRASTRUCTURE_KEYWORDS & tokens: - _add_hint(tool, "infrastructure_change", "keyword", "medium", {}) + _add_hint( + tool, + "infrastructure_change", + "keyword", + "medium", + {}, + basis="inferred_keyword", + ) # GET endpoints without any mutation evidence are read-only with high # confidence. Bumping from medium to high lets is_effectively_read_only # short-circuit policy/scope checks for clear reads, even when they pick # up a topical keyword like "kubernetes" elsewhere in the path. if method == "GET" and not has_risk_tag(tool, WRITE_TAGS): - _add_hint(tool, "read_only", "openapi_method", "high", {"method": method}) + _add_hint( + tool, + "read_only", + "openapi_method", + "high", + {"method": method}, + basis="protocol_structure", + ) def _apply_manual_override(tool: Tool, override) -> None: @@ -341,16 +461,22 @@ def _apply_manual_override(tool: Tool, override) -> None: "manual", "high", {"reason": override.reason, "confidence": override.confidence}, + basis="reviewed_declaration", ) def _is_removable_heuristic_hint(hint: ToolRiskHint) -> bool: - source = hint.source.lower() - return source == "keyword" or source == "regex" or source.endswith("_keyword") + return hint.basis in {"inferred_keyword", "inferred_regex"} def _add_hint( - tool: Tool, tag: str, source: str, confidence: str, evidence: dict[str, object] + tool: Tool, + tag: str, + source: str, + confidence: str, + evidence: dict[str, object], + *, + basis: str, ) -> None: confidence_value = confidence if confidence in {"low", "medium", "high"} else "medium" for existing in tool.risk_hints: @@ -364,6 +490,14 @@ def _add_hint( tag=tag, source=source, confidence=parse_confidence(confidence_value), + basis=basis, + provenance_kind=( + "keyword_heuristic" + if basis == "inferred_keyword" + else "regex_heuristic" + if basis == "inferred_regex" + else "static_declaration" + ), evidence={key: value for key, value in evidence.items() if value is not None}, ) ) diff --git a/src/agents_shipgate/core/semantic_assessment.py b/src/agents_shipgate/core/semantic_assessment.py index 4f5362ed..536ebc63 100644 --- a/src/agents_shipgate/core/semantic_assessment.py +++ b/src/agents_shipgate/core/semantic_assessment.py @@ -9,6 +9,7 @@ AuthoritySemanticAssessment, BindingSemanticAssessment, EffectSemanticAssessment, + EvidenceBasis, Scope, SemanticClaim, SemanticIssue, @@ -145,6 +146,7 @@ def _compat_binding_assessment(tool: Tool) -> BindingSemanticAssessment: value=f"legacy_direct->{tool.id}", confidence="high", provenance_kind="static_declaration", + basis="reviewed_declaration", source="compat_direct_binding", source_pointer=tool.source_pointer or tool.source_ref, ) @@ -193,6 +195,7 @@ def _compat_identity_assessment(tool: Tool) -> ToolIdentityAssessment: value=observation_id, confidence="high", provenance_kind="static_declaration", + basis="reviewed_declaration", source="compat_tool_identity", source_pointer=tool.source_pointer or tool.source_ref, evidence={"source_type": tool.source_type, "source_id": tool.source_id}, @@ -223,6 +226,7 @@ def _assess_effect( declaration.effect, "high", "static_declaration", + "reviewed_declaration", "action_surface_declaration", f"action_surface.actions[tool={tool.name!r}].effect", ) @@ -238,6 +242,7 @@ def _assess_effect( effect, "high", "static_declaration", + "reviewed_declaration", "action_risk_tag_declaration", f"action_surface.actions[tool={tool.name!r}].risk_tags", {"tag": tag}, @@ -261,6 +266,7 @@ def _assess_effect( method_effect, "high", "static_declaration", + "protocol_structure", "openapi_method", pointer, {"method": method}, @@ -285,6 +291,7 @@ def _assess_effect( "read", "high", "static_declaration", + "protocol_structure", "mcp_annotation", pointer, {"readOnlyHint": True}, @@ -297,6 +304,7 @@ def _assess_effect( "destructive", "high", "static_declaration", + "protocol_structure", "mcp_annotation", pointer, {"destructiveHint": True}, @@ -337,6 +345,7 @@ def _assess_effect( effect, "high", "static_declaration", + "protocol_structure", "permission_class", pointer, {"permission_class": value}, @@ -359,6 +368,7 @@ def _assess_effect( scope_effect, "high", "static_declaration", + "structural_scope", scope_source, pointer, {"scope": raw_scope}, @@ -370,15 +380,25 @@ def _assess_effect( effect = _TAG_EFFECTS.get(hint.tag) if effect is None or hint.source in direct_sources: continue - typed_provider_fact = _is_typed_provider_fact(tool, hint) - provenance = "static_declaration" if typed_provider_fact else _hint_provenance(hint.source) + hint_basis = _validated_hint_basis(tool, hint, declaration) + if hint_basis == "unknown": + issues.append( + _issue( + "invalid_evidence_provenance", + "effect", + f"risk hint {hint.tag!r} has no typed evidence basis", + hint.source, + pointer, + ) + ) claims.append( _claim( "effect", effect, hint.confidence, - provenance, - "typed_provider_fact" if typed_provider_fact else f"risk_hint:{hint.source}", + hint.provenance_kind, + hint_basis, + f"risk_hint:{hint.source}", pointer, {"tag": hint.tag, "hint_source": hint.source, **hint.evidence}, ) @@ -387,7 +407,8 @@ def _assess_effect( authoritative = [ claim for claim in claims - if claim.source + if claim.policy_eligible + and claim.source in { "action_surface_declaration", "openapi_method", @@ -395,8 +416,8 @@ def _assess_effect( "permission_class", "auth_scope", "action_scope", - "typed_provider_fact", } + or (claim.policy_eligible and claim.basis == "typed_provider_fact") ] # A reviewed manual risk tag may refine positive risk once another source has # established the action semantics. It must never independently prove a @@ -420,6 +441,7 @@ def _assess_effect( "write", "low", "static_declaration", + "protocol_default", "mcp_protocol_default", pointer, {"reason": "missing effect annotations"}, @@ -438,7 +460,7 @@ def _assess_effect( contradictory = [ claim for claim in [*structural, *inferred] - if confidence_rank(claim.confidence) >= confidence_rank("high") + if claim.policy_eligible and _EFFECT_RANK[_as_effect(claim.value)] > _EFFECT_RANK[declared_effect] ] if has_read and has_non_read: @@ -468,7 +490,6 @@ def _assess_effect( else: status = "declared" confidence = "high" - conservative = declared_effect elif structural: high_structural = [claim for claim in structural if claim.confidence == "high"] has_read = any(claim.value == "read" for claim in high_structural) @@ -593,6 +614,7 @@ def _assess_authority( alternative_mode, "high" if alternative_mode != "unknown" else "low", "static_declaration", + "protocol_structure", "openapi_security_alternative", f"{pointer or ''}/security/{index}", { @@ -609,6 +631,7 @@ def _assess_authority( source_mode, "high" if source_status == "structural" else "low", "static_declaration", + "protocol_structure", f"{tool.auth.source or tool.source_type}_authority", pointer, { @@ -627,6 +650,7 @@ def _assess_authority( authority.mode, "high", "static_declaration", + "reviewed_declaration", "action_surface_declaration", f"action_surface.actions[tool={tool.name!r}].authority", { @@ -828,6 +852,7 @@ def _claim( value: str, confidence: Confidence, provenance_kind: ProvenanceKind, + basis: EvidenceBasis, source: str, source_pointer: str | None, evidence: dict[str, Any] | None = None, @@ -837,6 +862,7 @@ def _claim( value=value, confidence=confidence, provenance_kind=provenance_kind, + basis=basis, source=source, source_pointer=source_pointer, evidence=evidence or {}, @@ -859,30 +885,6 @@ def _issue( ) -def _hint_provenance(source: str) -> ProvenanceKind: - lowered = source.lower() - if "regex" in lowered: - return "regex_heuristic" - if "keyword" in lowered or source in {"manual", "mcp_permission_lattice"}: - return "keyword_heuristic" if source != "manual" else "static_declaration" - return "static_declaration" - - -def _is_typed_provider_fact(tool: Tool, hint: Any) -> bool: - if ( - hint.source == "anthropic_client_tool_type" - and tool.annotations.get("anthropicClientTool") is True - and hint.confidence == "high" - ): - return True - return ( - hint.source == "n8n_static" - and tool.source_type == "n8n_code_tool" - and hint.tag == "code_execution" - and hint.confidence == "high" - ) - - def _surface_is_complete(tool: Tool) -> bool: return not ( tool.source_type in _AST_ONLY_SOURCE_TYPES @@ -892,6 +894,58 @@ def _surface_is_complete(tool: Tool) -> bool: ) +def _validated_hint_basis( + tool: Tool, + hint: Any, + declaration: ActionDeclarationConfig | None, +) -> EvidenceBasis: + """Validate producer-owned evidence basis without guessing from labels.""" + + if hint.basis in {"inferred_keyword", "inferred_regex", "protocol_default"}: + return cast(EvidenceBasis, hint.basis) + if hint.basis == "reviewed_declaration" and hint.source == "manual": + return "reviewed_declaration" + if hint.basis == "structural_scope" and hint.source in { + "auth_scope", + "action_scope", + }: + raw_scopes = hint.evidence.get("scopes") + scopes = ( + [value for value in raw_scopes if isinstance(value, str) and value.strip()] + if isinstance(raw_scopes, list) + else [] + ) + declared_scopes = ( + set(tool.auth.scopes) + if hint.source == "auth_scope" + else set(declaration.scopes if declaration is not None else []) + ) + if scopes and set(scopes).issubset(declared_scopes): + return "structural_scope" + if hint.basis == "typed_provider_fact": + if ( + hint.source == "anthropic_client_tool_type" + and tool.annotations.get("anthropicClientTool") is True + and hint.confidence == "high" + ): + return "typed_provider_fact" + if ( + hint.source == "n8n_static" + and tool.source_type == "n8n_code_tool" + and hint.tag == "code_execution" + and hint.confidence == "high" + ): + return "typed_provider_fact" + if ( + hint.basis == "protocol_structure" + and hint.source == "n8n_static" + and tool.source_type.startswith("n8n_") + and bool(hint.evidence.get("method")) + ): + return "protocol_structure" + return "unknown" + + def _as_effect(value: str) -> ActionEffect: return cast(ActionEffect, value) diff --git a/src/agents_shipgate/core/semantic_consistency.py b/src/agents_shipgate/core/semantic_consistency.py index 13113291..d2b76d49 100644 --- a/src/agents_shipgate/core/semantic_consistency.py +++ b/src/agents_shipgate/core/semantic_consistency.py @@ -125,10 +125,30 @@ def validate_semantic_consistency( or identity_coverage.gap_count or identity_eligible != len(assessments) or pass_eligible != len(assessments) + or bool(report.policy_evidence_gaps) + or decision.evidence_coverage.policy_gap_count ): raise SemanticConsistencyError( "passed requires every semantic assessment to be pass-eligible" ) + if decision.evidence_coverage.policy_gap_count != len(report.policy_evidence_gaps): + raise SemanticConsistencyError("policy evidence gap coverage drift") + if any( + finding.support is not None + and not finding.support.blocking_eligible + and finding.blocks_release + for finding in report.findings + ): + raise SemanticConsistencyError( + "unsupported finding cannot assert blocks_release" + ) + if any( + item.support is not None and not item.support.blocking_eligible + for item in decision.blockers + ): + raise SemanticConsistencyError( + "release blocker lacks authoritative predicate support" + ) if ( decision.decision == "insufficient_evidence" and decision.fail_policy.ci_mode == "strict" diff --git a/src/agents_shipgate/core/tool_identity.py b/src/agents_shipgate/core/tool_identity.py index 63fa50dc..c64a1c8e 100644 --- a/src/agents_shipgate/core/tool_identity.py +++ b/src/agents_shipgate/core/tool_identity.py @@ -478,6 +478,7 @@ def _assessment( value=tool.observation_id or "", confidence="high", provenance_kind="static_declaration", + basis="reviewed_declaration" if binding_id else "protocol_structure", source="tool_identity_binding" if binding_id else "source_observation", source_pointer=tool.source_pointer or tool.source_ref, evidence={ diff --git a/src/agents_shipgate/inputs/anthropic_api.py b/src/agents_shipgate/inputs/anthropic_api.py index 967986d4..fc09424b 100644 --- a/src/agents_shipgate/inputs/anthropic_api.py +++ b/src/agents_shipgate/inputs/anthropic_api.py @@ -367,6 +367,8 @@ def _tool_from_anthropic_definition( tag=tag, source="anthropic_client_tool_type", confidence="high", + basis="typed_provider_fact", + provenance_kind="ast_extraction", evidence={"anthropic_tool_type": typed_client_kind}, ) ) diff --git a/src/agents_shipgate/inputs/n8n/_auth_risk.py b/src/agents_shipgate/inputs/n8n/_auth_risk.py index b1e13bdf..1cfa277c 100644 --- a/src/agents_shipgate/inputs/n8n/_auth_risk.py +++ b/src/agents_shipgate/inputs/n8n/_auth_risk.py @@ -80,13 +80,31 @@ def _risk_hints(item: _NodeItem, *, method: str | None) -> list[ToolRiskHint]: hints: list[ToolRiskHint] = [] kind = _tool_node_kind(item) if kind == "code_tool": - _add_hint(hints, "code_execution", "high", {"node_type": item.node_type}) + _add_hint( + hints, + "code_execution", + "high", + {"node_type": item.node_type}, + basis="typed_provider_fact", + ) if method and method not in {"GET", "HEAD", "OPTIONS"}: - _add_hint(hints, "external_write", "medium", {"method": method}) + _add_hint( + hints, + "external_write", + "medium", + {"method": method}, + basis="protocol_structure", + ) for ref in _credential_refs(item): credential_type = str(ref.get("type") or "").lower() if any(token in credential_type for token in ("stripe", "paypal", "billing")): - _add_hint(hints, "financial_action", "medium", {"credential_type": ref.get("type")}) + _add_hint( + hints, + "financial_action", + "medium", + {"credential_type": ref.get("type")}, + basis="inferred_keyword", + ) if any( token in credential_type for token in ("gmail", "mail", "slack", "twilio", "sms", "discord") @@ -96,6 +114,7 @@ def _risk_hints(item: _NodeItem, *, method: str | None) -> list[ToolRiskHint]: "customer_communication", "medium", {"credential_type": ref.get("type")}, + basis="inferred_keyword", ) if any( token in credential_type for token in ("aws", "azure", "gcp", "kubernetes", "github") @@ -105,6 +124,7 @@ def _risk_hints(item: _NodeItem, *, method: str | None) -> list[ToolRiskHint]: "infrastructure_change", "medium", {"credential_type": ref.get("type")}, + basis="inferred_keyword", ) if any( token in credential_type @@ -115,6 +135,7 @@ def _risk_hints(item: _NodeItem, *, method: str | None) -> list[ToolRiskHint]: "sensitive_data_access", "medium", {"credential_type": ref.get("type")}, + basis="inferred_keyword", ) return hints @@ -124,6 +145,8 @@ def _add_hint( tag: str, confidence: str, evidence: dict[str, Any], + *, + basis: str, ) -> None: if any(hint.tag == tag and hint.confidence == confidence for hint in hints): return @@ -132,6 +155,10 @@ def _add_hint( tag=tag, source="n8n_static", confidence=confidence, + basis=basis, + provenance_kind=( + "keyword_heuristic" if basis == "inferred_keyword" else "ast_extraction" + ), evidence=evidence, ) ) diff --git a/src/agents_shipgate/inputs/policy_packs.py b/src/agents_shipgate/inputs/policy_packs.py index e33ab5ea..491cd108 100644 --- a/src/agents_shipgate/inputs/policy_packs.py +++ b/src/agents_shipgate/inputs/policy_packs.py @@ -9,6 +9,7 @@ from agents_shipgate.core.capability_policy import match_policy_pack_subject from agents_shipgate.core.context import ScanContext from agents_shipgate.core.errors import ConfigError, InputParseError +from agents_shipgate.core.policy_evidence import finding_support, policy_evidence_gap from agents_shipgate.inputs.common import load_structured_file, resolve_input_path from agents_shipgate.schemas.common import SourceReference from agents_shipgate.schemas.manifest import AgentsShipgateManifest, PolicyPackConfig @@ -107,9 +108,26 @@ def run_policy_pack_rules( "policy_pack_sha256_status": resolved.pack.sha256_status, }, ) - if match is None: + if match.status == "not_matched": continue rule = resolved.rule + support = finding_support( + match.support.predicates, + requested_confidence=rule.confidence, + status=match.status, + ) + if match.status != "matched" or not support.policy_eligible: + context.policy_evidence_gaps.append( + policy_evidence_gap( + status=match.status, + subject=f"{subject.tool.name} [{subject.tool.id}]", + policy_id=rule.id, + source_ref=resolved.pack.path, + support=support, + manifest_path=f"{resolved.pack.path}#rules/{rule.id}/match", + ) + ) + continue title = rule.title or rule.description or f"Policy pack rule {rule.id} matched" findings.append( Finding( @@ -121,14 +139,15 @@ def run_policy_pack_rules( tool_name=subject.tool.name, agent_id=context.agent.id, evidence=match.evidence, - confidence=rule.confidence, + confidence=support.confidence, provenance_kind="policy_pack", source=SourceReference(type="policy_pack", ref=resolved.pack.path), capability_refs=[subject.fact.id], capability_policy_evidence=match.capability_policy_evidence, policy_routing=_policy_routing_metadata(resolved), + support=support, recommendation=rule.recommendation, - blocks_release=rule.block, + blocks_release=rule.block and support.blocking_eligible, ) ) return findings diff --git a/src/agents_shipgate/packet/json_packet.py b/src/agents_shipgate/packet/json_packet.py index 53c86f44..377c2f76 100644 --- a/src/agents_shipgate/packet/json_packet.py +++ b/src/agents_shipgate/packet/json_packet.py @@ -84,13 +84,13 @@ def load_packet_json(payload: dict[str, Any] | str | bytes) -> EvidencePacket: version = payload_dict.get("packet_schema_version") legacy_version = ( version - if version in {"0.1", "0.2", "0.3", "0.4", "0.5", "0.6", "0.7", "0.8", "0.9"} + if version in {"0.1", "0.2", "0.3", "0.4", "0.5", "0.6", "0.7", "0.8", "0.9", "0.10"} else None ) if version == "0.1": payload_dict = { **payload_dict, - "packet_schema_version": "0.10", + "packet_schema_version": "0.11", "tool_surface_diff": { "status": "not_declared", "enabled": False, @@ -105,39 +105,41 @@ def load_packet_json(payload: dict[str, Any] | str | bytes) -> EvidencePacket: _upgrade_evidence_matrix_v06(payload_dict) _upgrade_hitl_v07(payload_dict) elif version == "0.2": - payload_dict = {**payload_dict, "packet_schema_version": "0.10"} + payload_dict = {**payload_dict, "packet_schema_version": "0.11"} _upgrade_hitl_v03(payload_dict) _upgrade_action_surface_v05(payload_dict) _upgrade_evidence_matrix_v06(payload_dict) _upgrade_hitl_v07(payload_dict) elif version == "0.3": - payload_dict = {**payload_dict, "packet_schema_version": "0.10"} + payload_dict = {**payload_dict, "packet_schema_version": "0.11"} _upgrade_action_surface_v05(payload_dict) _upgrade_evidence_matrix_v06(payload_dict) _upgrade_hitl_v07(payload_dict) elif version == "0.4": - payload_dict = {**payload_dict, "packet_schema_version": "0.10"} + payload_dict = {**payload_dict, "packet_schema_version": "0.11"} _upgrade_action_surface_v05(payload_dict) _upgrade_evidence_matrix_v06(payload_dict) _upgrade_hitl_v07(payload_dict) elif version == "0.5": - payload_dict = {**payload_dict, "packet_schema_version": "0.10"} + payload_dict = {**payload_dict, "packet_schema_version": "0.11"} _upgrade_evidence_matrix_v06(payload_dict) _upgrade_hitl_v07(payload_dict) elif version == "0.6": - payload_dict = {**payload_dict, "packet_schema_version": "0.10"} + payload_dict = {**payload_dict, "packet_schema_version": "0.11"} _upgrade_hitl_v07(payload_dict) elif version == "0.7": - payload_dict = {**payload_dict, "packet_schema_version": "0.10"} + payload_dict = {**payload_dict, "packet_schema_version": "0.11"} elif version == "0.8": - payload_dict = {**payload_dict, "packet_schema_version": "0.10"} + payload_dict = {**payload_dict, "packet_schema_version": "0.11"} elif version == "0.9": - payload_dict = {**payload_dict, "packet_schema_version": "0.10"} - elif version != "0.10": + payload_dict = {**payload_dict, "packet_schema_version": "0.11"} + elif version == "0.10": + payload_dict = {**payload_dict, "packet_schema_version": "0.11"} + elif version != "0.11": raise PacketSchemaError( "unsupported packet_schema_version: " f"{version!r}; expected '0.1', '0.2', '0.3', '0.4', '0.5', " - "'0.6', '0.7', '0.8', '0.9', or '0.10'" + "'0.6', '0.7', '0.8', '0.9', '0.10', or '0.11'" ) if legacy_version is not None: diff --git a/src/agents_shipgate/schemas/agent_handoff.py b/src/agents_shipgate/schemas/agent_handoff.py index 98ae7f76..34e2d9dc 100644 --- a/src/agents_shipgate/schemas/agent_handoff.py +++ b/src/agents_shipgate/schemas/agent_handoff.py @@ -9,8 +9,8 @@ from agents_shipgate.schemas.disclaimers import STATIC_VERDICT_DISCLAIMER from agents_shipgate.schemas.verifier import Applicability, MergeVerdict, map_merge_verdict -AGENT_HANDOFF_SCHEMA_VERSION = "shipgate.agent_handoff/v3" -AGENT_HANDOFF_SCHEMA_PATH = "docs/agent-handoff-schema.v3.json" +AGENT_HANDOFF_SCHEMA_VERSION = "shipgate.agent_handoff/v4" +AGENT_HANDOFF_SCHEMA_PATH = "docs/agent-handoff-schema.v4.json" AgentHandoffOperation = Literal["verify_pr", "verify_local", "verify_preview"] RemediationPlanSafety = Literal["allowed", "forbidden", "patch"] @@ -77,6 +77,7 @@ class AgentHandoffBlockedBy(BaseModel): blocks_release: bool | None = None capability_refs: list[str] = Field(default_factory=list) capability_trace_refs: list[str] = Field(default_factory=list) + support_hash: str | None = None class AgentHandoffRemediationStep(BaseModel): @@ -204,7 +205,7 @@ class AgentHandoffArtifact(BaseModel): }, ) - schema_version: Literal["shipgate.agent_handoff/v3"] = AGENT_HANDOFF_SCHEMA_VERSION + schema_version: Literal["shipgate.agent_handoff/v4"] = AGENT_HANDOFF_SCHEMA_VERSION contract_version: str tool: AgentHandoffTool = Field(default_factory=AgentHandoffTool) operation: AgentHandoffOperation diff --git a/src/agents_shipgate/schemas/baseline.py b/src/agents_shipgate/schemas/baseline.py index 2ed0cd08..e2af4a1b 100644 --- a/src/agents_shipgate/schemas/baseline.py +++ b/src/agents_shipgate/schemas/baseline.py @@ -8,7 +8,7 @@ from agents_shipgate.schemas.common import Severity from agents_shipgate.schemas.surfaces import ActionSurfaceFacts, ToolSurfaceFacts -BASELINE_SCHEMA_VERSION = "0.7" +BASELINE_SCHEMA_VERSION = "0.8" # v0.6 adds optional `provenance.owner` — the human who accepted the # debt — settable via `baseline save --owner/--reason/--expires` # (v0.5 documented reviewer-set `reason`/`expires` but offered no CLI @@ -55,6 +55,7 @@ class BaselineFinding(BaseModel): fingerprint_version: Literal["1", "2"] = "1" severity: Severity title: str + support_hash: str | None = None # v0.5 additive: when None, the entry pre-dates the v0.5 provenance # contract (loaded from 0.2/0.3/0.4) or was constructed by a test # helper. Re-saving via `baseline save` populates it. @@ -64,7 +65,7 @@ class BaselineFinding(BaseModel): class BaselineFile(BaseModel): model_config = ConfigDict(extra="forbid") - schema_version: Literal["0.2", "0.3", "0.4", "0.5", "0.6", "0.7"] = BASELINE_SCHEMA_VERSION + schema_version: Literal["0.2", "0.3", "0.4", "0.5", "0.6", "0.7", "0.8"] = BASELINE_SCHEMA_VERSION project: dict[str, object] = Field(default_factory=dict) agent: dict[str, object] = Field(default_factory=dict) created_at: str diff --git a/src/agents_shipgate/schemas/capabilities.py b/src/agents_shipgate/schemas/capabilities.py index 5e514bd6..56046108 100644 --- a/src/agents_shipgate/schemas/capabilities.py +++ b/src/agents_shipgate/schemas/capabilities.py @@ -14,9 +14,9 @@ from agents_shipgate.schemas.semantic import ToolSemanticEvidence from agents_shipgate.schemas.surfaces import ActionEffect -CAPABILITY_LOCK_SCHEMA_VERSION = "0.5" -CAPABILITY_LOCK_DIFF_SCHEMA_VERSION = "0.6" -CAPABILITY_STANDARD_VERSION = "0.4" +CAPABILITY_LOCK_SCHEMA_VERSION = "0.6" +CAPABILITY_LOCK_DIFF_SCHEMA_VERSION = "0.7" +CAPABILITY_STANDARD_VERSION = "0.5" CapabilityEvidenceProvenanceKind = Literal[ "static_declaration", "ast_extraction", @@ -211,7 +211,7 @@ class CapabilityLockHashes(BaseModel): class CapabilityLockFileV1(BaseModel): model_config = ConfigDict(extra="forbid") - capability_lock_schema_version: Literal["0.5"] = CAPABILITY_LOCK_SCHEMA_VERSION + capability_lock_schema_version: Literal["0.6"] = CAPABILITY_LOCK_SCHEMA_VERSION experimental: Literal[False] = False cli_version: str source: CapabilityLockSource @@ -263,7 +263,7 @@ class CapabilityLockChangedFact(BaseModel): class CapabilityLockDiffV1(BaseModel): model_config = ConfigDict(extra="forbid") - capability_lock_diff_schema_version: Literal["0.6"] = ( + capability_lock_diff_schema_version: Literal["0.7"] = ( CAPABILITY_LOCK_DIFF_SCHEMA_VERSION ) experimental: Literal[False] = False diff --git a/src/agents_shipgate/schemas/contract.py b/src/agents_shipgate/schemas/contract.py index 8c7bbafb..e342ed15 100644 --- a/src/agents_shipgate/schemas/contract.py +++ b/src/agents_shipgate/schemas/contract.py @@ -40,7 +40,7 @@ from agents_shipgate.schemas.verifier import VerifierArtifact from agents_shipgate.schemas.verify_run import VERIFY_RUN_SCHEMA_VERSION -CONTRACT_VERSION: Literal["15"] = "15" +CONTRACT_VERSION: Literal["16"] = "16" MINIMUM_CONTROL_CONTRACT_VERSION: Literal["14"] = "14" GATING_SIGNAL: Literal["release_decision.decision"] = "release_decision.decision" AGENT_RESULT_SCHEMA_VERSION: Literal["agent_result_v2"] = "agent_result_v2" diff --git a/src/agents_shipgate/schemas/packet.py b/src/agents_shipgate/schemas/packet.py index 7631a98b..bc580c89 100644 --- a/src/agents_shipgate/schemas/packet.py +++ b/src/agents_shipgate/schemas/packet.py @@ -359,7 +359,7 @@ class EvidencePacket(BaseModel): # human_in_the_loop. release_decision.decision remains the only gate. # v0.8: release-decision evidence coverage carries the additive # evidence-backed semantic coverage and gap remediation contract. - packet_schema_version: Literal["0.10"] = "0.10" + packet_schema_version: Literal["0.11"] = "0.11" generated_at: str | None = None run_id: str project: dict[str, Any] = Field(default_factory=dict) diff --git a/src/agents_shipgate/schemas/policy_pack.py b/src/agents_shipgate/schemas/policy_pack.py index 9738e712..3f2278d9 100644 --- a/src/agents_shipgate/schemas/policy_pack.py +++ b/src/agents_shipgate/schemas/policy_pack.py @@ -5,7 +5,7 @@ from agents_shipgate.schemas.common import Confidence, Severity from agents_shipgate.schemas.surfaces import ActionEffect -POLICY_PACK_SCHEMA_VERSION = "0.3" +POLICY_PACK_SCHEMA_VERSION = "0.4" class PolicyPackParameterMatch(BaseModel): diff --git a/src/agents_shipgate/schemas/report.py b/src/agents_shipgate/schemas/report.py index c0eb62e1..f1e44468 100644 --- a/src/agents_shipgate/schemas/report.py +++ b/src/agents_shipgate/schemas/report.py @@ -57,6 +57,55 @@ class CapabilityPolicyEvidence(BaseModel): source: SourceReference | None = None +PolicyMatchStatus = Literal["matched", "not_matched", "indeterminate", "conflicting"] +EvidenceBasis = Literal[ + "reviewed_declaration", + "protocol_structure", + "typed_provider_fact", + "structural_scope", + "inferred_keyword", + "inferred_regex", + "protocol_default", + "unknown", +] + + +class PolicyPredicateEvidence(BaseModel): + """One tri-state policy predicate and the evidence that supports it.""" + + model_config = ConfigDict(extra="forbid") + + predicate: str + status: PolicyMatchStatus + expected: Any = None + observed: Any = None + confidence: Confidence = "low" + claim_ids: list[str] = Field(default_factory=list) + evidence_bases: list[EvidenceBasis] = Field(default_factory=list) + policy_eligible: bool = False + why: str | None = None + + +class FindingSupport(BaseModel): + """Authoritative support for finding confidence and release contribution. + + Rule metadata may request a severity or block, but it cannot upgrade the + underlying evidence. ``support_hash`` binds baselines and audit surfaces + to the predicate evidence that actually made the finding eligible. + """ + + model_config = ConfigDict(extra="forbid") + + status: PolicyMatchStatus = "matched" + confidence: Confidence = "low" + policy_eligible: bool = False + blocking_eligible: bool = False + claim_ids: list[str] = Field(default_factory=list) + evidence_bases: list[EvidenceBasis] = Field(default_factory=list) + predicates: list[PolicyPredicateEvidence] = Field(default_factory=list) + support_hash: str + + CapabilityTraceMatchReason = Literal[ "capability_id", "tool_name", @@ -193,6 +242,9 @@ class Finding(BaseModel): # ``evidence`` so owner/reviewer/approval routing changes do not # affect fingerprints, suppressions, baselines, or release gating. policy_routing: PolicyRoutingMetadata | None = None + # v0.33: authoritative predicate support. Kept outside legacy evidence so + # fingerprints stay stable; baselines use support_hash separately. + support: FindingSupport | None = None # v0.25: opt-in local runtime trace/provenance evidence linked to # capability facts. Kept outside ``evidence`` so fingerprints, # baselines, run IDs, and de-dupe identity do not churn. @@ -303,6 +355,7 @@ class ReleaseDecisionItem(BaseModel): capability_refs: list[str] = Field(default_factory=list) # v0.25: mirror Finding.capability_trace_refs for blocker/review rows. capability_trace_refs: list[str] = Field(default_factory=list) + support: FindingSupport | None = None class EvidenceGapAction(BaseModel): @@ -332,6 +385,9 @@ class EvidenceGapAction(BaseModel): "provide_complete_binding_graph", "resolve_binding_conflict", "regenerate_binding_artifact", + "provide_policy_evidence", + "review_policy_evidence", + "resolve_policy_evidence_conflict", ] command: str | None = None path: str | None = None @@ -385,6 +441,11 @@ class EvidenceGap(BaseModel): "unresolved_bound_tool", "incomplete_handoff_graph", "invalid_binding_annotation", + "invalid_evidence_provenance", + "inferred_policy_applicability", + "mixed_policy_evidence", + "unknown_policy_evidence", + "conflicting_policy_evidence", ] subject: str source_type: str | None = None @@ -446,6 +507,7 @@ class EvidenceCoverageDecision(BaseModel): semantic_coverage: SemanticCoverageDecision = Field(default_factory=SemanticCoverageDecision) identity_coverage: IdentityCoverageDecision = Field(default_factory=IdentityCoverageDecision) binding_coverage: BindingCoverageDecision = Field(default_factory=BindingCoverageDecision) + policy_gap_count: int = 0 class BaselineDelta(BaseModel): @@ -486,6 +548,7 @@ class FailPolicy(BaseModel): # for completeness so the audit table is exhaustive over # report.findings. "sub_threshold", + "unsupported_evidence", # Suppressed via `checks.ignore[]` in the manifest; excluded from # the active set entirely. "suppressed", @@ -943,7 +1006,9 @@ class ReadinessReport(BaseModel): # v0.30: provider-scoped canonical tool identity. # v0.31: root-reachable agent binding facts, diffs, and coverage. # v0.32: required Conductor OSS workflow summary fields. - report_schema_version: str = "0.32" + # v0.33: typed evidence basis, predicate support, and unsuppressible + # indeterminate-policy evidence gaps. + report_schema_version: str = "0.33" run_id: str # v0.6 (per C13): absolute path to the directory containing # shipgate.yaml. apply-patches uses this to enforce a containment @@ -1006,6 +1071,9 @@ class ReadinessReport(BaseModel): tool_inventory: list[dict[str, Any]] = Field(default_factory=list) tool_catalog: list[dict[str, Any]] = Field(default_factory=list) source_warnings: list[str] = Field(default_factory=list) + # v0.33: indeterminate policy applicability stays outside Finding so it + # cannot be baselined, suppressed, severity-overridden, or acknowledged. + policy_evidence_gaps: list[EvidenceGap] = Field(default_factory=list) # v0.12: top-level agent summary. Deterministic projection of # release_decision + findings[].agent_action. Optional at Python # level so older test helpers can construct minimal reports; diff --git a/src/agents_shipgate/schemas/safety_qualification.py b/src/agents_shipgate/schemas/safety_qualification.py index 98c1d666..a153a2b5 100644 --- a/src/agents_shipgate/schemas/safety_qualification.py +++ b/src/agents_shipgate/schemas/safety_qualification.py @@ -9,9 +9,9 @@ from agents_shipgate.schemas.common import ReleaseDecisionStatus from agents_shipgate.schemas.disclaimers import STATIC_VERDICT_DISCLAIMER -SAFETY_CORPUS_SCHEMA_VERSION = "shipgate.safety_corpus/v2" -SAFETY_RECEIPT_INDEX_SCHEMA_VERSION = "shipgate.safety_receipt_index/v2" -SAFETY_QUALIFICATION_SCHEMA_VERSION = "shipgate.safety_qualification/v2" +SAFETY_CORPUS_SCHEMA_VERSION = "shipgate.safety_corpus/v3" +SAFETY_RECEIPT_INDEX_SCHEMA_VERSION = "shipgate.safety_receipt_index/v3" +SAFETY_QUALIFICATION_SCHEMA_VERSION = "shipgate.safety_qualification/v3" SafetyProfile = Literal[ "mcp", @@ -190,7 +190,7 @@ class FrozenSafetyCorpusV1(BaseModel): model_config = ConfigDict(extra="forbid") - schema_version: Literal["shipgate.safety_corpus/v2"] = SAFETY_CORPUS_SCHEMA_VERSION + schema_version: Literal["shipgate.safety_corpus/v3"] = SAFETY_CORPUS_SCHEMA_VERSION corpus_id: str = Field(min_length=1) labels_frozen_before_evaluation: Literal[True] outputs_hidden_from_labelers: Literal[True] @@ -200,7 +200,11 @@ class FrozenSafetyCorpusV1(BaseModel): @field_validator("schema_version", mode="before") @classmethod def _upgrade_v1_schema(cls, value: str) -> str: - return SAFETY_CORPUS_SCHEMA_VERSION if value == "shipgate.safety_corpus/v1" else value + return ( + SAFETY_CORPUS_SCHEMA_VERSION + if value in {"shipgate.safety_corpus/v1", "shipgate.safety_corpus/v2"} + else value + ) @field_validator("corpus_id") @classmethod @@ -265,7 +269,7 @@ class SafetyReceiptIndexV1(BaseModel): model_config = ConfigDict(extra="forbid") - schema_version: Literal["shipgate.safety_receipt_index/v2"] = ( + schema_version: Literal["shipgate.safety_receipt_index/v3"] = ( SAFETY_RECEIPT_INDEX_SCHEMA_VERSION ) wheel_sha256: str = Field(pattern=SHA256_PATTERN) @@ -281,7 +285,10 @@ class SafetyReceiptIndexV1(BaseModel): def _upgrade_v1_schema(cls, value: str) -> str: return ( SAFETY_RECEIPT_INDEX_SCHEMA_VERSION - if value == "shipgate.safety_receipt_index/v1" + if value in { + "shipgate.safety_receipt_index/v1", + "shipgate.safety_receipt_index/v2", + } else value ) @@ -398,7 +405,7 @@ def production_safety_requirements() -> SafetyQualificationRequirementsV1: minimum_blocked_exact=30, minimum_review_exact=19, minimum_insufficient_evidence_exact=19, - required_report_schema_version="0.32", + required_report_schema_version="0.33", ) @@ -510,7 +517,7 @@ class SafetyQualificationResultV1(BaseModel): model_config = ConfigDict(extra="forbid") - schema_version: Literal["shipgate.safety_qualification/v2"] = ( + schema_version: Literal["shipgate.safety_qualification/v3"] = ( SAFETY_QUALIFICATION_SCHEMA_VERSION ) qualification_tier: QualificationTier @@ -533,7 +540,10 @@ class SafetyQualificationResultV1(BaseModel): def _upgrade_v1_schema(cls, value: str) -> str: return ( SAFETY_QUALIFICATION_SCHEMA_VERSION - if value == "shipgate.safety_qualification/v1" + if value in { + "shipgate.safety_qualification/v1", + "shipgate.safety_qualification/v2", + } else value ) diff --git a/src/agents_shipgate/schemas/semantic.py b/src/agents_shipgate/schemas/semantic.py index 5549fcbd..76af871a 100644 --- a/src/agents_shipgate/schemas/semantic.py +++ b/src/agents_shipgate/schemas/semantic.py @@ -27,6 +27,16 @@ "identity_access", ] SemanticDimension = Literal["identity", "binding", "effect", "authority"] +EvidenceBasis = Literal[ + "reviewed_declaration", + "protocol_structure", + "typed_provider_fact", + "structural_scope", + "inferred_keyword", + "inferred_regex", + "protocol_default", + "unknown", +] IdentityEvidenceStatus = Literal[ "declared", "structural", @@ -73,16 +83,20 @@ "unresolved_bound_tool", "incomplete_handoff_graph", "invalid_binding_annotation", + "invalid_evidence_provenance", ] class SemanticClaimEvidence(BaseModel): model_config = ConfigDict(extra="forbid", frozen=True) + claim_id: str | None = None dimension: SemanticDimension value: str confidence: Confidence provenance_kind: ProvenanceKind + basis: EvidenceBasis = "unknown" + policy_eligible: bool = False source: str source_pointer: str | None = None evidence: dict[str, Any] = Field(default_factory=dict) diff --git a/src/agents_shipgate/schemas/surfaces.py b/src/agents_shipgate/schemas/surfaces.py index 7b80e780..2c9bf90e 100644 --- a/src/agents_shipgate/schemas/surfaces.py +++ b/src/agents_shipgate/schemas/surfaces.py @@ -349,7 +349,7 @@ class ActionFact(BaseModel): class ActionSurfaceFacts(BaseModel): model_config = ConfigDict(extra="forbid") - snapshot_version: str = "0.3" + snapshot_version: str = "0.4" actions: list[ActionFact] = Field(default_factory=list) diff --git a/src/agents_shipgate/schemas/verifier.py b/src/agents_shipgate/schemas/verifier.py index 8030bc63..af09d345 100644 --- a/src/agents_shipgate/schemas/verifier.py +++ b/src/agents_shipgate/schemas/verifier.py @@ -424,7 +424,7 @@ class VerifierArtifact(BaseModel): }, ) - verifier_schema_version: Literal["0.3"] = "0.3" + verifier_schema_version: Literal["0.4"] = "0.4" static_analysis_only: Literal[True] = True runtime_behavior_verified: Literal[False] = False static_verdict_disclaimer: str = STATIC_VERDICT_DISCLAIMER @@ -471,14 +471,14 @@ def _normalize_legacy_control(cls, data: Any) -> Any: return data normalized = dict(data) legacy_version = normalized.get("verifier_schema_version") - legacy = legacy_version in {"0.1", "0.2"} + legacy = legacy_version in {"0.1", "0.2", "0.3"} if not legacy: - # Current v0.3 artifacts must already carry the authoritative + # Current v0.4 artifacts must already carry the authoritative # control union. Silently synthesizing a missing or malformed # current control would turn an internal consistency failure into # a trusted handoff. Only the frozen v0.2 reader is normalized. return normalized - normalized["verifier_schema_version"] = "0.3" + normalized["verifier_schema_version"] = "0.4" execution = normalized.get("execution") or normalized.get("head_status") execution = execution or "not_run" diff --git a/tests/golden/agent_protocol/claude-code-block-stop.json b/tests/golden/agent_protocol/claude-code-block-stop.json index 8f8b0c9c..ac097a29 100644 --- a/tests/golden/agent_protocol/claude-code-block-stop.json +++ b/tests/golden/agent_protocol/claude-code-block-stop.json @@ -3,7 +3,7 @@ "agent": "claude-code", "tool": { "name": "agents-shipgate", - "version": "0.16.0b4" + "version": "0.16.0b5" }, "subject": { "agent": "claude-code" diff --git a/tests/golden/agent_protocol/codex-block-stop.json b/tests/golden/agent_protocol/codex-block-stop.json index f0249b92..9dfcd09e 100644 --- a/tests/golden/agent_protocol/codex-block-stop.json +++ b/tests/golden/agent_protocol/codex-block-stop.json @@ -3,7 +3,7 @@ "agent": "codex", "tool": { "name": "agents-shipgate", - "version": "0.16.0b4" + "version": "0.16.0b5" }, "subject": { "agent": "codex" diff --git a/tests/golden/agent_protocol/codex-repair-after.json b/tests/golden/agent_protocol/codex-repair-after.json index 853e40ab..5f35ef77 100644 --- a/tests/golden/agent_protocol/codex-repair-after.json +++ b/tests/golden/agent_protocol/codex-repair-after.json @@ -3,7 +3,7 @@ "agent": "codex", "tool": { "name": "agents-shipgate", - "version": "0.16.0b4" + "version": "0.16.0b5" }, "subject": { "agent": "codex" diff --git a/tests/golden/agent_protocol/codex-repair-before.json b/tests/golden/agent_protocol/codex-repair-before.json index 2655ce7e..e23d2766 100644 --- a/tests/golden/agent_protocol/codex-repair-before.json +++ b/tests/golden/agent_protocol/codex-repair-before.json @@ -3,7 +3,7 @@ "agent": "codex", "tool": { "name": "agents-shipgate", - "version": "0.16.0b4" + "version": "0.16.0b5" }, "subject": { "agent": "codex" diff --git a/tests/golden/agent_protocol/cursor-block-stop.json b/tests/golden/agent_protocol/cursor-block-stop.json index 378733e4..19fcd321 100644 --- a/tests/golden/agent_protocol/cursor-block-stop.json +++ b/tests/golden/agent_protocol/cursor-block-stop.json @@ -3,7 +3,7 @@ "agent": "cursor", "tool": { "name": "agents-shipgate", - "version": "0.16.0b4" + "version": "0.16.0b5" }, "subject": { "agent": "cursor" diff --git a/tests/golden/agent_protocol/missing-install.json b/tests/golden/agent_protocol/missing-install.json index 60484543..e0aaa45d 100644 --- a/tests/golden/agent_protocol/missing-install.json +++ b/tests/golden/agent_protocol/missing-install.json @@ -3,7 +3,7 @@ "agent": "codex", "tool": { "name": "agents-shipgate", - "version": "0.16.0b4" + "version": "0.16.0b5" }, "subject": { "agent": "codex" diff --git a/tests/golden/agent_protocol/stale-install.json b/tests/golden/agent_protocol/stale-install.json index aebe4532..999c8b36 100644 --- a/tests/golden/agent_protocol/stale-install.json +++ b/tests/golden/agent_protocol/stale-install.json @@ -3,7 +3,7 @@ "agent": "codex", "tool": { "name": "agents-shipgate", - "version": "0.16.0b4" + "version": "0.16.0b5" }, "subject": { "agent": "codex" diff --git a/tests/golden/codex_boundary_result/agents_requirement_removed.json b/tests/golden/codex_boundary_result/agents_requirement_removed.json index bd0599f5..6e6aacbb 100644 --- a/tests/golden/codex_boundary_result/agents_requirement_removed.json +++ b/tests/golden/codex_boundary_result/agents_requirement_removed.json @@ -3,7 +3,7 @@ "agent": "codex", "tool": { "name": "agents-shipgate", - "version": "0.16.0b4" + "version": "0.16.0b5" }, "subject": { "agent": "codex" diff --git a/tests/golden/codex_boundary_result/docs_only.json b/tests/golden/codex_boundary_result/docs_only.json index bef5426e..96db17ac 100644 --- a/tests/golden/codex_boundary_result/docs_only.json +++ b/tests/golden/codex_boundary_result/docs_only.json @@ -3,7 +3,7 @@ "agent": "codex", "tool": { "name": "agents-shipgate", - "version": "0.16.0b4" + "version": "0.16.0b5" }, "subject": { "agent": "codex" diff --git a/tests/golden/codex_boundary_result/github_action_removed.json b/tests/golden/codex_boundary_result/github_action_removed.json index 1663a3e8..9665d142 100644 --- a/tests/golden/codex_boundary_result/github_action_removed.json +++ b/tests/golden/codex_boundary_result/github_action_removed.json @@ -3,7 +3,7 @@ "agent": "codex", "tool": { "name": "agents-shipgate", - "version": "0.16.0b4" + "version": "0.16.0b5" }, "subject": { "agent": "codex" diff --git a/tests/golden/codex_boundary_result/malformed_toml.json b/tests/golden/codex_boundary_result/malformed_toml.json index e57532fb..df37cc54 100644 --- a/tests/golden/codex_boundary_result/malformed_toml.json +++ b/tests/golden/codex_boundary_result/malformed_toml.json @@ -3,7 +3,7 @@ "agent": "codex", "tool": { "name": "agents-shipgate", - "version": "0.16.0b4" + "version": "0.16.0b5" }, "subject": { "agent": "codex" diff --git a/tests/golden/codex_boundary_result/mcp_auto_approve_write.json b/tests/golden/codex_boundary_result/mcp_auto_approve_write.json index 894aa12d..a88a5b44 100644 --- a/tests/golden/codex_boundary_result/mcp_auto_approve_write.json +++ b/tests/golden/codex_boundary_result/mcp_auto_approve_write.json @@ -3,7 +3,7 @@ "agent": "codex", "tool": { "name": "agents-shipgate", - "version": "0.16.0b4" + "version": "0.16.0b5" }, "subject": { "agent": "codex" diff --git a/tests/golden/codex_boundary_result/network_wildcard.json b/tests/golden/codex_boundary_result/network_wildcard.json index a7df06ab..22342584 100644 --- a/tests/golden/codex_boundary_result/network_wildcard.json +++ b/tests/golden/codex_boundary_result/network_wildcard.json @@ -3,7 +3,7 @@ "agent": "codex", "tool": { "name": "agents-shipgate", - "version": "0.16.0b4" + "version": "0.16.0b5" }, "subject": { "agent": "codex" diff --git a/tests/golden/codex_boundary_result/python_refactor.json b/tests/golden/codex_boundary_result/python_refactor.json index 10b381ce..b459b89e 100644 --- a/tests/golden/codex_boundary_result/python_refactor.json +++ b/tests/golden/codex_boundary_result/python_refactor.json @@ -3,7 +3,7 @@ "agent": "codex", "tool": { "name": "agents-shipgate", - "version": "0.16.0b4" + "version": "0.16.0b5" }, "subject": { "agent": "codex" diff --git a/tests/golden/codex_boundary_result/unknown_permission_key.json b/tests/golden/codex_boundary_result/unknown_permission_key.json index 8a506571..ca2052e7 100644 --- a/tests/golden/codex_boundary_result/unknown_permission_key.json +++ b/tests/golden/codex_boundary_result/unknown_permission_key.json @@ -3,7 +3,7 @@ "agent": "codex", "tool": { "name": "agents-shipgate", - "version": "0.16.0b4" + "version": "0.16.0b5" }, "subject": { "agent": "codex" diff --git a/tests/test_action_surface_diff.py b/tests/test_action_surface_diff.py index b875d47b..b2633ae3 100644 --- a/tests/test_action_surface_diff.py +++ b/tests/test_action_surface_diff.py @@ -484,6 +484,62 @@ def test_action_surface_external_side_effect_alias_matches_external_communicatio ) +def test_custom_action_policy_cannot_launder_heuristic_risk_tag() -> None: + manifest = _manifest( + { + "action_surface": { + "policies": [ + { + "id": "heuristic-financial-block", + "match": {"risk_tags": ["financial_write"]}, + "require": {"approval.required": True}, + "severity": "critical", + "block": True, + } + ] + } + } + ) + tool = Tool( + id="tool:refund_status", + name="refund_status", + source_type="langchain_inventory", + source_id="tools", + extraction_confidence="high", + risk_hints=[ + ToolRiskHint( + tag="financial_action", + source="keyword", + confidence="high", + basis="inferred_keyword", + provenance_kind="keyword_heuristic", + ) + ], + ) + facts = build_action_surface_facts( + manifest, + agent_id="agent:action-test/agent", + tools=[tool], + ) + gaps = [] + + findings = evaluate_action_surface_policies( + manifest, + facts, + ActionSurfaceDiff(), + agent_id="agent:action-test/agent", + tools=[tool], + policy_evidence_gaps=gaps, + ) + + assert not any( + finding.evidence.get("policy_id") == "heuristic-financial-block" + for finding in findings + ) + gap = next(item for item in gaps if "heuristic-financial-block" in item.why) + assert gap.kind == "inferred_policy_applicability" + + def test_enrich_action_surface_diff_populates_structured_source_fields(): """v0.19 reviewer-grade provenance: every change row gains structured ``source_path`` / ``source_start_line`` fields when the @@ -755,6 +811,12 @@ def test_builtin_financial_controls_apply_without_diff_reference(): "safeguards.audit_log", "safeguards.idempotency", ] + assert finding.support is not None + assert finding.support.policy_eligible is True + assert {row.predicate for row in finding.support.predicates} == { + "builtin_control_effect", + "missing_builtin_controls", + } def test_builtin_destructive_controls_apply_to_existing_action(): @@ -784,6 +846,27 @@ def test_builtin_destructive_controls_apply_to_existing_action(): "safeguards.rollback", "confirmation.required", ] + financial = evaluate_action_surface_policies( + manifest, + ActionSurfaceFacts( + actions=[ + _action( + "agent:action-test/agent:mcp:tools:process_payment", + effect="financial_write", + ) + ] + ), + ActionSurfaceDiff(), + agent_id="agent:action-test/agent", + ) + financial_support = next( + item.support + for item in financial + if item.check_id == "SHIP-ACTION-FINANCIAL-WRITE-CONTROL-MISSING" + ) + assert finding.support is not None + assert financial_support is not None + assert finding.support.support_hash != financial_support.support_hash def test_action_surface_diff_reports_modification_taxonomy(): @@ -1184,7 +1267,7 @@ def test_scan_diff_from_prior_report_does_not_launder_financial_keyword_into_blo assert report.action_surface_diff.summary.actions_added == 1 assert report.action_surface_diff.summary.blocking_findings == 0 assert report.release_decision is not None - assert report.release_decision.decision == "review_required" + assert report.release_decision.decision == "insufficient_evidence" assert not any( finding.check_id == "SHIP-ACTION-FINANCIAL-WRITE-CONTROL-MISSING" and finding.blocks_release for finding in report.findings diff --git a/tests/test_agent_handoff.py b/tests/test_agent_handoff.py index 21801a15..8f78303c 100644 --- a/tests/test_agent_handoff.py +++ b/tests/test_agent_handoff.py @@ -32,7 +32,7 @@ def _verifier_payload() -> dict: human_review_required=True, ) return { - "verifier_schema_version": "0.3", + "verifier_schema_version": "0.4", "workspace": "/tmp/repo", "config": "shipgate.yaml", "execution": "succeeded", @@ -138,7 +138,7 @@ def test_agent_handoff_projects_verifier_report_and_verify_run() -> None: }, ) - assert handoff.schema_version == "shipgate.agent_handoff/v3" + assert handoff.schema_version == "shipgate.agent_handoff/v4" assert handoff.gate.decision == "blocked" assert handoff.gate.merge_verdict == "blocked" assert handoff.gate.ci_would_fail is True @@ -154,7 +154,7 @@ def test_agent_handoff_projects_verifier_report_and_verify_run() -> None: def test_agent_handoff_schema_validates_sample_projection() -> None: - schema = json.loads((ROOT / "docs" / "agent-handoff-schema.v3.json").read_text()) + schema = json.loads((ROOT / "docs" / "agent-handoff-schema.v4.json").read_text()) payload = build_agent_handoff(verifier=_verifier_payload()).model_dump(mode="json") Draft202012Validator(schema).validate(payload) @@ -220,7 +220,7 @@ def test_agent_handoff_cli_rerenders_existing_artifacts(tmp_path: Path) -> None: emitted = json.loads(result.output) written = json.loads(out_path.read_text(encoding="utf-8")) assert emitted == written - assert emitted["schema_version"] == "shipgate.agent_handoff/v3" + assert emitted["schema_version"] == "shipgate.agent_handoff/v4" assert emitted["gate"]["decision"] == "blocked" diff --git a/tests/test_agent_instructions_apply.py b/tests/test_agent_instructions_apply.py index 2ad048c1..26ed6bc3 100644 --- a/tests/test_agent_instructions_apply.py +++ b/tests/test_agent_instructions_apply.py @@ -189,13 +189,13 @@ def test_claude_command_current_file_matches_renderer() -> None: def test_local_contract_renderer_has_required_fields() -> None: payload = json.loads(render_local_contract_file()) - assert payload["schema_version"] == "4" - assert payload["contract_version"] == "15" + assert payload["schema_version"] == "5" + assert payload["contract_version"] == "16" assert "verify_local" not in payload["primary_commands"] assert payload["primary_commands"]["verify_pr"].startswith("agents-shipgate verify") assert payload["commands"]["verify_local"].startswith("agents-shipgate verify") assert payload["primary_commands"]["host_audit"].startswith("shipgate audit --host") - assert payload["agent_handoff_schema_version"] == "shipgate.agent_handoff/v3" + assert payload["agent_handoff_schema_version"] == "shipgate.agent_handoff/v4" assert payload["agent_handoff_artifact"] == "agents-shipgate-reports/agent-handoff.json" assert payload["attestation_schema_version"] == "0.4" assert payload["registry_schema_version"] == "0.3" diff --git a/tests/test_agent_instructions_renderers.py b/tests/test_agent_instructions_renderers.py index 01d7548c..bc18d06d 100644 --- a/tests/test_agent_instructions_renderers.py +++ b/tests/test_agent_instructions_renderers.py @@ -45,10 +45,10 @@ REPO_ROOT = Path(__file__).resolve().parent.parent EXPECTED_CLAUDE_CODE_SKILL_RENDER_SHA256 = { ".claude/skills/agents-shipgate/SKILL.md": ( - "98ba22d7518ae4635ed109fd187323da0541281061dd4f259ac7fdb950c7b185" + "02e780f5a1506d948e4c1d77f6ee4c6b4193227a4fd2ced081847d1fb2e5fbd0" ), ".claude/skills/agents-shipgate/ci-recipes/advisory-pr-comment.yml": ( - "e35be58338a6343c712a6a63aff826f5785040f201aa0d7f5f6f257c27d12532" + "44648891d77a12f562913e4402c08bca4be6cff4aa8c69b7950ea97e7d5cc17b" ), ".claude/skills/agents-shipgate/prompts/add-shipgate-to-repo.md": ( "53296f41b7c2bc8538555a4361707de8b990748b7a5d80ae4ce066af83af8fa7" @@ -160,18 +160,18 @@ def test_committed_claude_command_matches_renderer() -> None: def test_local_contract_renderer_exposes_agent_operational_fields() -> None: payload = json.loads(render_local_contract_file()) - assert payload["schema_version"] == "4" + assert payload["schema_version"] == "5" assert payload["agents_shipgate_version"] - assert payload["contract_version"] == "15" + assert payload["contract_version"] == "16" assert payload["minimum_control_contract_version"] == "14" assert payload["primary_commands"]["verify_pr"].startswith("agents-shipgate verify") assert payload["primary_commands"]["host_audit"].startswith("shipgate audit --host") assert "verify_local" not in payload["primary_commands"] assert payload["commands"]["verify_local"].startswith("agents-shipgate verify") - assert payload["verifier_schema_version"] == "0.3" + assert payload["verifier_schema_version"] == "0.4" assert payload["verify_run_schema_version"] == "shipgate.verify_run/v2" - assert payload["agent_handoff_schema_version"] == "shipgate.agent_handoff/v3" - assert payload["agent_handoff_schema_path"] == "docs/agent-handoff-schema.v3.json" + assert payload["agent_handoff_schema_version"] == "shipgate.agent_handoff/v4" + assert payload["agent_handoff_schema_path"] == "docs/agent-handoff-schema.v4.json" assert payload["agent_handoff_artifact"] == "agents-shipgate-reports/agent-handoff.json" assert payload["codex_boundary_result_schema_version"] == "shipgate.codex_boundary_result/v2" assert payload["agent_boundary_result_schema_version"] == ( diff --git a/tests/test_agent_mode.py b/tests/test_agent_mode.py index 0c06868f..de49c716 100644 --- a/tests/test_agent_mode.py +++ b/tests/test_agent_mode.py @@ -220,7 +220,7 @@ def test_verify_json_shortcut_prints_verifier_artifact(tmp_path: Path) -> None: assert result.exit_code == 0, result.output payload = json.loads(result.output) - assert payload["verifier_schema_version"] == "0.3" + assert payload["verifier_schema_version"] == "0.4" assert payload["merge_verdict"] == "insufficient_evidence" assert payload["can_merge_without_human"] is False assert payload["control"]["state"] == "human_review_required" @@ -230,7 +230,7 @@ def test_verify_json_shortcut_prints_verifier_artifact(tmp_path: Path) -> None: handoff_path = repo / "agents-shipgate-reports" / "agent-handoff.json" assert handoff_path.is_file() handoff = json.loads(handoff_path.read_text(encoding="utf-8")) - assert handoff["schema_version"] == "shipgate.agent_handoff/v3" + assert handoff["schema_version"] == "shipgate.agent_handoff/v4" assert handoff["operation"] == "verify_pr" assert not (repo / "agents-shipgate-reports" / "agent-result.json").exists() @@ -253,7 +253,7 @@ def test_verify_preview_writes_agent_handoff(tmp_path: Path) -> None: handoff = json.loads( (repo / "agents-shipgate-reports" / "agent-handoff.json").read_text(encoding="utf-8") ) - assert handoff["schema_version"] == "shipgate.agent_handoff/v3" + assert handoff["schema_version"] == "shipgate.agent_handoff/v4" assert handoff["operation"] == "verify_preview" assert handoff["gate"]["decision"] is None assert handoff["control"]["state"] == "agent_action_required" @@ -269,7 +269,7 @@ def test_verify_format_json_still_prints_full_verifier_artifact( assert result.exit_code == 0, result.output payload = json.loads(result.output) - assert payload["verifier_schema_version"] == "0.3" + assert payload["verifier_schema_version"] == "0.4" assert payload["execution"] == "succeeded" assert payload["head_status"] == "succeeded" assert payload["trigger"]["run_shipgate"] is True @@ -286,7 +286,7 @@ def test_verify_agent_environment_defaults_to_verifier_json( assert result.exit_code == 0, result.output payload = json.loads(result.output) - assert payload["verifier_schema_version"] == "0.3" + assert payload["verifier_schema_version"] == "0.4" assert payload["merge_verdict"] == "insufficient_evidence" diff --git a/tests/test_anthropic_api.py b/tests/test_anthropic_api.py index c21db53d..ede4826e 100644 --- a/tests/test_anthropic_api.py +++ b/tests/test_anthropic_api.py @@ -418,13 +418,23 @@ def test_anthropic_scan_runs_existing_framework_agnostic_checks(tmp_path): # Existing framework-agnostic + generalized SHIP-API-* checks fire on # Anthropic tools without any new check IDs. assert "SHIP-API-FUNCTION-SCHEMA-STRICTNESS" in fingerprints - assert "SHIP-API-PROMPT-TOOL-SCOPE-MISMATCH" in fingerprints + assert "SHIP-API-PROMPT-TOOL-SCOPE-MISMATCH" not in fingerprints + assert any( + gap.kind == "inferred_policy_applicability" + and gap.why.startswith("SHIP-API-PROMPT-TOOL-SCOPE-MISMATCH:") + for gap in report.policy_evidence_gaps + ) assert report.release_decision is not None semantic = report.release_decision.evidence_coverage.semantic_coverage assert semantic.gap_count == 0 assert semantic.review_concern_count == 2 assert semantic.reason_counts == {"unscoped_authority": 2} - assert "SHIP-SCHEMA-MISSING-BOUNDS" in fingerprints + assert "SHIP-SCHEMA-MISSING-BOUNDS" not in fingerprints + assert any( + gap.kind == "inferred_policy_applicability" + and gap.why.startswith("SHIP-SCHEMA-MISSING-BOUNDS:") + for gap in report.policy_evidence_gaps + ) assert "SHIP-MANIFEST-HIGH-RISK-OWNER-MISSING" in fingerprints # Approval is satisfied by anthropic.policy_rules so the check should NOT fire. assert "SHIP-POLICY-APPROVAL-MISSING" not in fingerprints diff --git a/tests/test_baseline_integrity.py b/tests/test_baseline_integrity.py index 434e02bb..6dde8507 100644 --- a/tests/test_baseline_integrity.py +++ b/tests/test_baseline_integrity.py @@ -147,7 +147,7 @@ def test_baseline_from_report_stamps_provenance_on_new_entries(): baseline = baseline_from_report( report, scanner_version="9.9.9", now="2026-01-01T00:00:00Z" ) - assert baseline.schema_version == "0.7" + assert baseline.schema_version == "0.8" assert all(entry.provenance is not None for entry in baseline.findings) p = baseline.findings[0].provenance assert p.scanner_version == "9.9.9" diff --git a/tests/test_baseline_status.py b/tests/test_baseline_status.py index d145cc03..d279ada3 100644 --- a/tests/test_baseline_status.py +++ b/tests/test_baseline_status.py @@ -15,10 +15,12 @@ from agents_shipgate.cli.main import app from agents_shipgate.core.baseline import ( + apply_baseline, baseline_from_report, baseline_status_payload, baseline_status_violations, ) +from agents_shipgate.core.policy_evidence import finding_support, predicate_evidence from agents_shipgate.schemas.baseline import ( BaselineFile, BaselineFinding, @@ -106,13 +108,57 @@ def test_save_metadata_stamped_on_new_entries() -> None: reason="accepted pending Q3 fix", expires=date(2026, 9, 30), ) - assert baseline.schema_version == "0.7" + assert baseline.schema_version == "0.8" provenance = baseline.findings[0].provenance assert provenance.owner == "alice" assert provenance.reason == "accepted pending Q3 fix" assert provenance.expires == date(2026, 9, 30) +def test_baseline_support_hash_prevents_evidence_laundering() -> None: + report = _stub_report(("ORG-POLICY", "tool_a")) + original_support = finding_support( + [ + predicate_evidence( + "effect", + "matched", + observed="write", + confidence="high", + evidence_bases=["protocol_structure"], + policy_eligible=True, + ) + ] + ) + report.findings[0].support = original_support + baseline = baseline_from_report( + report, + scanner_version="9.9.9", + now="2026-06-12T00:00:00Z", + ) + assert baseline.findings[0].support_hash == original_support.support_hash + + report.findings[0].support = finding_support( + [ + predicate_evidence( + "effect", + "matched", + observed="destructive", + confidence="high", + evidence_bases=["protocol_structure"], + policy_eligible=True, + ) + ] + ) + summary = apply_baseline( + report.findings, + baseline, + display_path="baseline.json", + ) + + assert report.findings[0].baseline_status == "new" + assert summary.matched_count == 0 + + def test_resave_without_flags_preserves_owner() -> None: report = _stub_report(("SHIP-X", "tool_a")) first = baseline_from_report( diff --git a/tests/test_capability_domain.py b/tests/test_capability_domain.py index a1699407..4bb79ac1 100644 --- a/tests/test_capability_domain.py +++ b/tests/test_capability_domain.py @@ -389,4 +389,4 @@ def test_building_capability_facts_does_not_change_action_fact_output() -> None: def test_capability_substrate_uses_current_report_schema_version() -> None: - assert ReadinessReport.model_fields["report_schema_version"].default == "0.32" + assert ReadinessReport.model_fields["report_schema_version"].default == "0.33" diff --git a/tests/test_capability_lock.py b/tests/test_capability_lock.py index ba202f49..168d16e6 100644 --- a/tests/test_capability_lock.py +++ b/tests/test_capability_lock.py @@ -476,7 +476,7 @@ def test_mixed_capability_standard_diff_requires_exact_reexport( assert str(exc_info.value) == ( "Mixed capability-standard lock diff is not comparable " - "(base=0.2, head=0.4). Re-export the base lock from its source " + "(base=0.2, head=0.5). Re-export the base lock from its source " "workspace with the current engine using exactly: " f"`{command}`. Then rerun the capability diff." ) diff --git a/tests/test_cli.py b/tests/test_cli.py index 51871123..862ae609 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -627,7 +627,7 @@ def test_cli_explain_json_returns_full_metadata(): assert key in payload -def test_cli_findings_json_filters_by_provenance_kind(tmp_path): +def test_cli_findings_json_reports_no_heuristic_policy_findings(tmp_path): run_scan( config_path=Path("samples/support_refund_agent/shipgate.yaml"), output_dir=tmp_path, @@ -655,31 +655,14 @@ def test_cli_findings_json_filters_by_provenance_kind(tmp_path): "regex_heuristic", ] assert payload["filters"]["include_suppressed"] is False - assert payload["summary"]["matched_findings"] > 0 + assert payload["summary"]["matched_findings"] == 0 assert "suppressed_omitted" in payload["summary"] assert "suppressed_excluded" not in payload["summary"] - assert payload["summary"]["by_provenance_kind"]["keyword_heuristic"] > 0 - assert {finding["provenance_kind"] for finding in payload["findings"]} <= { - "keyword_heuristic", - "regex_heuristic", - } - for finding in payload["findings"]: - assert { - "id", - "fingerprint", - "check_id", - "severity", - "title", - "tool_name", - "confidence", - "provenance_kind", - "agent_action", - "suppressed", - "source", - } <= set(finding) - - -def test_cli_findings_text_outputs_summary(tmp_path): + assert payload["summary"]["by_provenance_kind"]["keyword_heuristic"] == 0 + assert payload["findings"] == [] + + +def test_cli_findings_text_excludes_heuristic_policy_gaps(tmp_path): run_scan( config_path=Path("samples/support_refund_agent/shipgate.yaml"), output_dir=tmp_path, @@ -703,7 +686,7 @@ def test_cli_findings_text_outputs_summary(tmp_path): assert "Scope: active findings only" in result.output assert "Provenance counts:" in result.output assert "keyword_heuristic:" in result.output - assert "SHIP-" in result.output + assert "No findings matched." in result.output def test_cli_findings_invalid_provenance_kind_exits_three(tmp_path): diff --git a/tests/test_conductor.py b/tests/test_conductor.py index a45e69f4..70798671 100644 --- a/tests/test_conductor.py +++ b/tests/test_conductor.py @@ -55,7 +55,7 @@ def test_conductor_static_mcp_call_and_human_checkpoint(tmp_path): ci_mode="advisory", ) - assert report.report_schema_version == "0.32" + assert report.report_schema_version == "0.33" surface = report.frameworks["conductor"] assert surface["workflow_count"] == 1 assert surface["mcp_call_task_count"] == 1 diff --git a/tests/test_evidence_packet.py b/tests/test_evidence_packet.py index 41ae1a78..b43682f9 100644 --- a/tests/test_evidence_packet.py +++ b/tests/test_evidence_packet.py @@ -148,7 +148,7 @@ def test_packet_emits_alongside_report_by_default(tmp_path): out, packet = _scan_with_packet(tmp_path) for name in ("packet.md", "packet.json", "packet.html"): assert (out / name).exists(), name - assert packet.packet_schema_version == "0.10" + assert packet.packet_schema_version == "0.11" def test_packet_v07_keeps_capability_refs_on_release_items(): @@ -630,16 +630,20 @@ def test_verdict_derives_from_release_decision_not_fail_policy(tmp_path): assert "did not execute the agent" in section.static_verdict_disclaimer -def test_capability_intent_diff_lists_observed_tools(tmp_path): +def test_capability_intent_keeps_heuristic_policy_matches_out_of_findings(tmp_path): _, packet = _scan_with_packet(tmp_path) section = packet.capability_intent assert section.declared_purpose assert "stripe.create_refund" in section.observed_tools - # SHIP-SCOPE-PROHIBITED-TOOL-PRESENT fires twice on this fixture. - assert any( + assert not any( item.check_id == "SHIP-SCOPE-PROHIBITED-TOOL-PRESENT" for item in section.divergence_findings ) + assert packet.release_decision.evidence_coverage.policy_gap_count > 0 + assert any( + gap.why.startswith("SHIP-SCOPE-PROHIBITED-TOOL-PRESENT:") + for gap in packet.release_decision.evidence_coverage.evidence_gaps + ) def test_high_risk_surface_includes_high_risk_tools(tmp_path): @@ -935,7 +939,7 @@ def test_load_packet_json_upgrades_v02_hitl_fields(tmp_path): upgraded = load_packet_json(payload) - assert upgraded.packet_schema_version == "0.10" + assert upgraded.packet_schema_version == "0.11" assert upgraded.evidence_matrix.notes assert upgraded.action_surface_diff.status == "not_declared" assert upgraded.action_surface_diff.enabled is False @@ -958,7 +962,7 @@ def test_load_packet_json_upgrades_v01_to_v05(tmp_path): upgraded = load_packet_json(payload) - assert upgraded.packet_schema_version == "0.10" + assert upgraded.packet_schema_version == "0.11" assert upgraded.evidence_matrix.notes assert upgraded.tool_surface_diff.status == "not_declared" assert upgraded.tool_surface_diff.enabled is False @@ -979,7 +983,7 @@ def test_load_packet_json_upgrades_v04_action_surface_section(tmp_path): upgraded = load_packet_json(payload) - assert upgraded.packet_schema_version == "0.10" + assert upgraded.packet_schema_version == "0.11" assert upgraded.action_surface_diff.status == "not_declared" assert upgraded.action_surface_diff.enabled is False assert upgraded.evidence_matrix.notes @@ -993,7 +997,7 @@ def test_load_packet_json_upgrades_v05_evidence_matrix(tmp_path): upgraded = load_packet_json(payload) - assert upgraded.packet_schema_version == "0.10" + assert upgraded.packet_schema_version == "0.11" assert len(upgraded.evidence_matrix.rows) == 13 assert any("older packet schema" in note for note in upgraded.evidence_matrix.notes) @@ -1034,7 +1038,7 @@ def test_legacy_passed_packet_downgrades_to_actionable_insufficient_evidence( upgraded = load_packet_json(payload) - assert upgraded.packet_schema_version == "0.10" + assert upgraded.packet_schema_version == "0.11" assert upgraded.release_decision.decision == "insufficient_evidence" assert upgraded.release_decision.verdict == "INSUFFICIENT EVIDENCE" assert "not a v0.8 safety statement" in upgraded.release_decision.reason @@ -1270,7 +1274,7 @@ def test_evidence_packet_writes_packet_json_when_format_includes_json(tmp_path): # The written packet.json must round-trip. payload = (target / "packet.json").read_text(encoding="utf-8") reloaded = load_packet_json(payload) - assert reloaded.packet_schema_version == "0.10" + assert reloaded.packet_schema_version == "0.11" def test_evidence_packet_pdf_only_exits_zero_when_weasyprint_missing(tmp_path, monkeypatch): @@ -1397,7 +1401,7 @@ def test_evidence_packet_cli_round_trips(tmp_path): ) assert result.exit_code == 0, result.output payload = json.loads(result.output) - assert payload["packet_schema_version"] == "0.10" + assert payload["packet_schema_version"] == "0.11" assert payload["run_id"] == packet.run_id diff --git a/tests/test_fingerprint_compatibility.py b/tests/test_fingerprint_compatibility.py index 1a653040..61d0cc49 100644 --- a/tests/test_fingerprint_compatibility.py +++ b/tests/test_fingerprint_compatibility.py @@ -10,9 +10,6 @@ from agents_shipgate.schemas.report import Finding V015_FINGERPRINTS = { - ("SHIP-SCHEMA-MISSING-BOUNDS", "stripe.create_refund"): "fp_ab60b01cb53cfcbe", - ("SHIP-SCHEMA-BROAD-FREE-TEXT", "zendesk.update_ticket"): "fp_ff2f028953d1c220", - ("SHIP-SCHEMA-BROAD-FREE-TEXT", "gmail.send_customer_email"): "fp_acd63b899d49aa1c", ("SHIP-AUTH-MANIFEST-BROAD-SCOPE", None): "fp_d27325cbdbbf5483", ("SHIP-AUTH-SCOPE-COVERAGE-MISSING", "shopify.cancel_order"): "fp_83852fbd6b440524", ("SHIP-AUTH-SCOPE-COVERAGE-MISSING", "support.search_kb"): "fp_d8e6d1865dae97cc", @@ -36,6 +33,10 @@ def test_v015_unchanged_finding_fingerprints_remain_stable(tmp_path: Path) -> No for finding in report.findings } assert {key: observed.get(key) for key in V015_FINGERPRINTS} == V015_FINGERPRINTS + assert { + gap.why.split(":", 1)[0] + for gap in report.policy_evidence_gaps + } >= {"SHIP-SCHEMA-MISSING-BOUNDS", "SHIP-SCHEMA-BROAD-FREE-TEXT"} assert any(row["name"] == "wildcard_mcp_tools.*" for row in report.tool_catalog) assert all(row["name"] != "wildcard_mcp_tools.*" for row in report.tool_inventory) diff --git a/tests/test_governance_benchmark.py b/tests/test_governance_benchmark.py index ad89dad7..46ae0434 100644 --- a/tests/test_governance_benchmark.py +++ b/tests/test_governance_benchmark.py @@ -227,7 +227,7 @@ def test_governance_benchmark_schemas_validate_catalog_and_result(tmp_path: Path def test_governance_benchmark_preserves_public_schema_boundaries() -> None: - assert ReadinessReport.model_fields["report_schema_version"].default == "0.32" - assert CAPABILITY_LOCK_SCHEMA_VERSION == "0.5" - assert CAPABILITY_LOCK_DIFF_SCHEMA_VERSION == "0.6" + assert ReadinessReport.model_fields["report_schema_version"].default == "0.33" + assert CAPABILITY_LOCK_SCHEMA_VERSION == "0.6" + assert CAPABILITY_LOCK_DIFF_SCHEMA_VERSION == "0.7" assert GOVERNANCE_BENCHMARK_RESULT_SCHEMA_VERSION == "0.2" diff --git a/tests/test_local_contract.py b/tests/test_local_contract.py index ddbce6fc..f101ea7e 100644 --- a/tests/test_local_contract.py +++ b/tests/test_local_contract.py @@ -93,10 +93,10 @@ def test_local_agent_contract_is_minimal_agent_operational_payload() -> None: ] assert payload["verifier_read_order"][0] == "control.state" assert payload["gating_signal"] == GATING_SIGNAL - assert payload["verifier_schema_version"] == "0.3" + assert payload["verifier_schema_version"] == "0.4" assert payload["verify_run_schema_version"] == "shipgate.verify_run/v2" - assert payload["agent_handoff_schema_version"] == "shipgate.agent_handoff/v3" - assert payload["agent_handoff_schema_path"] == "docs/agent-handoff-schema.v3.json" + assert payload["agent_handoff_schema_version"] == "shipgate.agent_handoff/v4" + assert payload["agent_handoff_schema_path"] == "docs/agent-handoff-schema.v4.json" assert payload["agent_handoff_artifact"] == "agents-shipgate-reports/agent-handoff.json" assert payload["codex_boundary_result_schema_version"] == "shipgate.codex_boundary_result/v2" assert payload["agent_boundary_result_schema_version"] == ( diff --git a/tests/test_mcp_server.py b/tests/test_mcp_server.py index ea37acb7..0c59668b 100644 --- a/tests/test_mcp_server.py +++ b/tests/test_mcp_server.py @@ -138,7 +138,7 @@ def test_mcp_capabilities_handler_does_not_write_reports(tmp_path: Path) -> None no_plugins=True, ) - assert payload["capability_lock_schema_version"] == "0.5" + assert payload["capability_lock_schema_version"] == "0.6" assert _snapshot(workspace) == before @@ -205,7 +205,7 @@ def test_mcp_handoff_handler_is_read_only(tmp_path: Path) -> None: payload = shipgate_handoff(verifier_path=str(output_dir / "verifier.json")) - assert payload["schema_version"] == "shipgate.agent_handoff/v3" + assert payload["schema_version"] == "shipgate.agent_handoff/v4" assert payload["gate"]["merge_verdict"] == "mergeable" assert payload["control"]["state"] == "complete" assert _snapshot(tmp_path) == before diff --git a/tests/test_no_heuristics.py b/tests/test_no_heuristics.py index 4ed7334c..201d3487 100644 --- a/tests/test_no_heuristics.py +++ b/tests/test_no_heuristics.py @@ -191,7 +191,7 @@ def test_keep_list_is_explicit_and_non_overlapping() -> None: def test_run_scan_with_no_heuristics_emits_envelope(tmp_path) -> None: """End-to-end: ``run_scan(no_heuristics=True)`` produces a report - whose ``heuristics_filter`` envelope is enabled+populated.""" + whose filter cannot hide heuristic policy-evidence gaps.""" report, _ = run_scan( config_path=SUPPORT_REFUND_FIXTURE, output_dir=tmp_path, @@ -199,14 +199,20 @@ def test_run_scan_with_no_heuristics_emits_envelope(tmp_path) -> None: ci_mode="advisory", no_heuristics=True, ) + control, _ = run_scan( + config_path=SUPPORT_REFUND_FIXTURE, + output_dir=tmp_path / "control", + formats=["json"], + ci_mode="advisory", + no_heuristics=False, + ) assert report.heuristics_filter is not None assert report.heuristics_filter.enabled is True - # The support_refund fixture is known to contain keyword_heuristic - # findings (description-injection and broad-free-text checks). Exact - # count varies as the catalog evolves; assert ≥ 1 so the test - # documents the live-fixture invariant without over-pinning. - assert report.heuristics_filter.filtered_finding_count >= 1 - assert "keyword_heuristic" in report.heuristics_filter.filtered_by_kind + assert report.heuristics_filter.filtered_finding_count == 0 + assert report.policy_evidence_gaps + assert [gap.model_dump(mode="json") for gap in report.policy_evidence_gaps] == [ + gap.model_dump(mode="json") for gap in control.policy_evidence_gaps + ] def test_run_scan_without_flag_is_unaffected(tmp_path) -> None: @@ -301,22 +307,14 @@ def test_no_heuristics_via_cli_smoke(tmp_path) -> None: ) report = json.loads((tmp_path / "report.json").read_text()) assert report["heuristics_filter"]["enabled"] is True - assert report["heuristics_filter"]["filtered_finding_count"] >= 1 + assert report["heuristics_filter"]["filtered_finding_count"] == 0 + assert report["policy_evidence_gaps"] -def test_filter_does_not_overwrite_manifest_suppression_reason_e2e( +def test_filter_cannot_suppress_policy_evidence_gaps_e2e( tmp_path, ) -> None: - """Integration-level pin of the same rule as - ``test_manifest_suppression_reason_preserved_when_also_filterable``: - if a manifest already suppressed a heuristic finding, the user's - reason wins after the filter runs.""" - # support_refund_agent ships heuristic findings without manifest - # suppression; we'd need a custom fixture to test the overlap path - # end-to-end. The unit test above pins the semantic; this test - # documents the absence of regression by confirming the canonical - # reason appears verbatim on at least one filtered finding when - # NO manifest suppression overlaps. + """Heuristic policy gaps are outside Finding suppression entirely.""" report, _ = run_scan( config_path=SUPPORT_REFUND_FIXTURE, output_dir=tmp_path, @@ -324,19 +322,10 @@ def test_filter_does_not_overwrite_manifest_suppression_reason_e2e( ci_mode="advisory", no_heuristics=True, ) - flagged = [ - f - for f in report.findings - if f.suppression_reason == NO_HEURISTICS_SUPPRESSION_REASON - ] - assert flagged, ( - "expected at least one finding with the canonical " - "--no-heuristics suppression reason in the support_refund fixture" - ) - # All flagged findings are heuristic provenance. + assert report.policy_evidence_gaps assert all( - f.provenance_kind in NO_HEURISTICS_EXCLUDED_PROVENANCE_KINDS - for f in flagged + finding.suppression_reason != NO_HEURISTICS_SUPPRESSION_REASON + for finding in report.findings ) diff --git a/tests/test_openai_api.py b/tests/test_openai_api.py index 6116a3d4..46889645 100644 --- a/tests/test_openai_api.py +++ b/tests/test_openai_api.py @@ -148,11 +148,21 @@ def test_openai_api_scan_runs_new_and_existing_checks(tmp_path): check_ids = {finding.check_id for finding in report.findings} assert "SHIP-API-FUNCTION-SCHEMA-STRICTNESS" in check_ids assert "SHIP-API-STRUCTURED-OUTPUT-READINESS" in check_ids - assert "SHIP-API-PROMPT-TOOL-SCOPE-MISMATCH" in check_ids + assert "SHIP-API-PROMPT-TOOL-SCOPE-MISMATCH" not in check_ids + assert any( + gap.kind == "inferred_policy_applicability" + and gap.why.startswith("SHIP-API-PROMPT-TOOL-SCOPE-MISMATCH:") + for gap in report.policy_evidence_gaps + ) assert "SHIP-API-RETRY-WITHOUT-IDEMPOTENCY" in check_ids assert "SHIP-API-TIMEOUT-MISSING" in check_ids assert "SHIP-API-TOOL-OUTPUT-SCHEMA-MISSING" in check_ids - assert "SHIP-SCHEMA-MISSING-BOUNDS" in check_ids + assert "SHIP-SCHEMA-MISSING-BOUNDS" not in check_ids + assert any( + gap.kind == "inferred_policy_applicability" + and gap.why.startswith("SHIP-SCHEMA-MISSING-BOUNDS:") + for gap in report.policy_evidence_gaps + ) assert "SHIP-SIDEFX-IDEMPOTENCY-MISSING" in check_ids trace_finding = next( finding diff --git a/tests/test_p0_policy_evidence_canaries.py b/tests/test_p0_policy_evidence_canaries.py new file mode 100644 index 00000000..bfeae54d --- /dev/null +++ b/tests/test_p0_policy_evidence_canaries.py @@ -0,0 +1,380 @@ +"""P0 exact-outcome canaries for policy-evidence provenance. + +These 48 cases lock the three boundaries that prevent heuristic evidence from +being promoted into an authoritative policy result: claim classification, +predicate support, and release-gate contribution. +""" + +from __future__ import annotations + +import json +from dataclasses import dataclass +from pathlib import Path +from typing import Literal + +import pytest + +from agents_shipgate.ci.release_decision import build_release_decision +from agents_shipgate.cli.scan import run_scan +from agents_shipgate.core.domain import EvidenceBasis, SemanticClaim +from agents_shipgate.core.policy_evidence import ( + conjunction_status, + disjunction_status, + finding_support, + negated_disjunction_status, + policy_evidence_gap, + predicate_evidence, +) +from agents_shipgate.schemas.bindings import AgentBindingGraphAssessment +from agents_shipgate.schemas.common import Confidence, ProvenanceKind +from agents_shipgate.schemas.report import ( + Finding, + FindingSupport, + PolicyMatchStatus, + ReadinessReport, + ReportSummary, + ToolSurfaceSummary, +) + + +@dataclass(frozen=True) +class ClaimCanary: + name: str + basis: EvidenceBasis + confidence: Confidence + expected_provenance: ProvenanceKind + policy_eligible: bool + + +CLAIM_CANARIES = [ + ClaimCanary("reviewed_high", "reviewed_declaration", "high", "static_declaration", True), + ClaimCanary("protocol_high", "protocol_structure", "high", "static_declaration", True), + ClaimCanary("provider_high", "typed_provider_fact", "high", "ast_extraction", True), + ClaimCanary("scope_high", "structural_scope", "high", "static_declaration", True), + ClaimCanary("keyword_high", "inferred_keyword", "high", "keyword_heuristic", False), + ClaimCanary("regex_high", "inferred_regex", "high", "regex_heuristic", False), + ClaimCanary("default_high", "protocol_default", "high", "static_declaration", False), + ClaimCanary("unknown_high", "unknown", "high", "policy_pack", False), + ClaimCanary("reviewed_medium", "reviewed_declaration", "medium", "static_declaration", False), + ClaimCanary("protocol_medium", "protocol_structure", "medium", "static_declaration", False), + ClaimCanary("provider_medium", "typed_provider_fact", "medium", "ast_extraction", False), + ClaimCanary("scope_medium", "structural_scope", "medium", "static_declaration", False), + ClaimCanary("keyword_medium", "inferred_keyword", "medium", "keyword_heuristic", False), + ClaimCanary("regex_medium", "inferred_regex", "medium", "regex_heuristic", False), + ClaimCanary("default_medium", "protocol_default", "medium", "static_declaration", False), + ClaimCanary("unknown_medium", "unknown", "medium", "policy_pack", False), +] + + +@pytest.mark.parametrize("case", CLAIM_CANARIES, ids=lambda case: case.name) +def test_claim_basis_canary(case: ClaimCanary) -> None: + claim = SemanticClaim( + dimension="effect", + value="write", + confidence=case.confidence, + provenance_kind="policy_pack", + basis=case.basis, + source="p0-canary", + ) + + assert claim.provenance_kind == case.expected_provenance + assert claim.policy_eligible is case.policy_eligible + assert claim.claim_id is not None and claim.claim_id.startswith("clm_") + + +@dataclass(frozen=True) +class SupportCanary: + name: str + reducer: Literal["conjunction", "disjunction", "negated_disjunction"] + statuses: tuple[PolicyMatchStatus, ...] + confidences: tuple[Confidence, ...] + eligibility: tuple[bool, ...] + requested_confidence: Confidence + expected_status: PolicyMatchStatus + expected_confidence: Confidence + expected_eligible: bool + + +SUPPORT_CANARIES = [ + SupportCanary("all_authoritative", "conjunction", ("matched",), ("high",), (True,), "high", "matched", "high", True), + SupportCanary("medium_cannot_authorize", "conjunction", ("matched",), ("medium",), (False,), "high", "matched", "medium", False), + SupportCanary("rule_low_caps_evidence", "conjunction", ("matched",), ("high",), (True,), "low", "matched", "low", True), + SupportCanary("heuristic_cannot_authorize", "conjunction", ("matched",), ("high",), (False,), "high", "matched", "high", False), + SupportCanary("indeterminate_conjunction", "conjunction", ("matched", "indeterminate"), ("high", "low"), (True, False), "high", "indeterminate", "low", False), + SupportCanary("negative_conjunction", "conjunction", ("matched", "not_matched"), ("high", "high"), (True, True), "high", "not_matched", "high", False), + SupportCanary("conflicting_conjunction", "conjunction", ("matched", "conflicting"), ("high", "high"), (True, False), "high", "conflicting", "high", False), + SupportCanary("negative_beats_unknown", "conjunction", ("not_matched", "indeterminate"), ("high", "low"), (True, False), "high", "not_matched", "low", False), + SupportCanary("positive_disjunction", "disjunction", ("not_matched", "matched"), ("high", "high"), (True, True), "high", "matched", "high", False), + SupportCanary("positive_beats_conflict", "disjunction", ("conflicting", "matched"), ("low", "high"), (False, True), "high", "matched", "low", False), + SupportCanary("conflict_beats_unknown", "disjunction", ("indeterminate", "conflicting"), ("low", "high"), (False, False), "high", "conflicting", "low", False), + SupportCanary("unknown_disjunction", "disjunction", ("not_matched", "indeterminate"), ("high", "low"), (True, False), "high", "indeterminate", "low", False), + SupportCanary("negative_disjunction", "disjunction", ("not_matched", "not_matched"), ("high", "high"), (True, True), "high", "not_matched", "high", False), + SupportCanary("none_of_positive", "negated_disjunction", ("matched",), ("high",), (True,), "high", "not_matched", "high", False), + SupportCanary("none_of_negative", "negated_disjunction", ("not_matched",), ("high",), (True,), "high", "matched", "high", False), + SupportCanary("none_of_unknown", "negated_disjunction", ("indeterminate",), ("low",), (False,), "high", "indeterminate", "low", False), +] + + +@pytest.mark.parametrize("case", SUPPORT_CANARIES, ids=lambda case: case.name) +def test_policy_support_canary(case: SupportCanary) -> None: + reducers = { + "conjunction": conjunction_status, + "disjunction": disjunction_status, + "negated_disjunction": negated_disjunction_status, + } + predicates = [ + predicate_evidence( + f"predicate_{index}", + status, + confidence=case.confidences[index], + evidence_bases=[ + "protocol_structure" if case.eligibility[index] else "inferred_keyword" + ], + policy_eligible=case.eligibility[index], + ) + for index, status in enumerate(case.statuses) + ] + support = finding_support( + predicates, + requested_confidence=case.requested_confidence, + status=reducers[case.reducer](case.statuses), + ) + + assert support.status == case.expected_status + assert support.confidence == case.expected_confidence + assert support.policy_eligible is case.expected_eligible + assert support.blocking_eligible is case.expected_eligible + + +@dataclass(frozen=True) +class ReleaseCanary: + name: str + support_status: PolicyMatchStatus + support_eligible: bool + severity: Literal["critical", "high", "medium", "low"] + gap_status: PolicyMatchStatus | None + ci_mode: Literal["advisory", "strict"] + fail_on: tuple[Literal["critical", "high", "medium", "low"], ...] + expected_decision: Literal[ + "blocked", "review_required", "insufficient_evidence", "passed" + ] + expected_exit: int + expected_contribution: str + + +RELEASE_CANARIES = [ + ReleaseCanary("eligible_critical_advisory", "matched", True, "critical", None, "advisory", (), "blocked", 0, "severity_block_new"), + ReleaseCanary("eligible_critical_strict", "matched", True, "critical", None, "strict", (), "blocked", 20, "severity_block_new"), + ReleaseCanary("eligible_high_review", "matched", True, "high", None, "advisory", (), "review_required", 0, "review_required"), + ReleaseCanary("eligible_high_fail_on", "matched", True, "high", None, "strict", ("high",), "blocked", 20, "severity_block_new"), + ReleaseCanary("eligible_medium_review", "matched", True, "medium", None, "advisory", (), "review_required", 0, "review_required"), + ReleaseCanary("eligible_low_pass", "matched", True, "low", None, "advisory", (), "passed", 0, "sub_threshold"), + ReleaseCanary("heuristic_critical_ie", "indeterminate", False, "critical", "indeterminate", "advisory", (), "insufficient_evidence", 0, "unsupported_evidence"), + ReleaseCanary("heuristic_critical_strict_ie", "indeterminate", False, "critical", "indeterminate", "strict", (), "insufficient_evidence", 20, "unsupported_evidence"), + ReleaseCanary("heuristic_high_ie", "indeterminate", False, "high", "indeterminate", "advisory", (), "insufficient_evidence", 0, "unsupported_evidence"), + ReleaseCanary("heuristic_medium_ie", "indeterminate", False, "medium", "indeterminate", "advisory", (), "insufficient_evidence", 0, "unsupported_evidence"), + ReleaseCanary("heuristic_low_ie", "indeterminate", False, "low", "indeterminate", "advisory", (), "insufficient_evidence", 0, "unsupported_evidence"), + ReleaseCanary("conflicting_critical_ie", "conflicting", False, "critical", "conflicting", "strict", (), "insufficient_evidence", 20, "unsupported_evidence"), + ReleaseCanary("unknown_high_ie", "indeterminate", False, "high", "indeterminate", "strict", ("high",), "insufficient_evidence", 20, "unsupported_evidence"), + ReleaseCanary("unsupported_without_gap_cannot_block", "matched", False, "critical", None, "strict", (), "passed", 0, "unsupported_evidence"), + ReleaseCanary("unsupported_rule_block_cannot_block", "matched", False, "high", None, "strict", ("high",), "passed", 0, "unsupported_evidence"), + ReleaseCanary("eligible_low_strict_threshold", "matched", True, "low", None, "strict", ("low",), "blocked", 20, "severity_block_new"), +] + + +def _support(case: ReleaseCanary) -> FindingSupport: + return finding_support( + [ + predicate_evidence( + "capability.effect", + case.support_status, + observed="financial_write", + confidence="high", + evidence_bases=[ + "protocol_structure" if case.support_eligible else "inferred_keyword" + ], + policy_eligible=case.support_eligible, + ) + ], + status=case.support_status, + ) + + +@pytest.mark.parametrize("case", RELEASE_CANARIES, ids=lambda case: case.name) +def test_release_contribution_canary(case: ReleaseCanary) -> None: + support = _support(case) + gap = ( + policy_evidence_gap( + status=case.gap_status, + subject="wire_funds", + policy_id="p0-canary", + source_ref="tools.json#/wire_funds", + support=support, + manifest_path="policy_packs[0].rules[0].match", + ) + if case.gap_status is not None + else None + ) + finding = Finding( + id=f"finding-{case.name}", + fingerprint=f"fp-{case.name}", + check_id="SHIP-POLICY-PACK-VIOLATION", + title="P0 policy evidence canary", + severity=case.severity, + category="policy_pack", + recommendation="Provide authoritative evidence.", + provenance_kind="policy_pack", + # Most cases exercise severity routing. One adversarial case sets the + # rule's block bit on unsupported evidence and proves it is ignored. + blocks_release=case.name == "unsupported_rule_block_cannot_block", + support=support, + ) + report = ReadinessReport( + run_id="p0-policy-canary", + project={"name": "p0-policy-canary"}, + agent={"name": "p0-policy-canary"}, + environment={"target": "test"}, + summary=ReportSummary( + status="warnings_detected", + critical_count=int(case.severity == "critical"), + high_count=int(case.severity == "high"), + medium_count=int(case.severity == "medium"), + human_review_recommended=False, + evidence_coverage="static", + ), + tool_surface=ToolSurfaceSummary(total_tools=0, high_risk_tools=0), + binding_surface_facts=AgentBindingGraphAssessment( + root_agent_id="p0-policy-canary", + status="declared", + pass_eligible=True, + ), + findings=[finding], + policy_evidence_gaps=[] if gap is None else [gap], + ) + + decision = build_release_decision( + report=report, + tools=[], + ci_mode=case.ci_mode, + fail_on=list(case.fail_on) or None, + new_findings_only=False, + ) + + assert decision.decision == case.expected_decision + assert decision.fail_policy.exit_code == case.expected_exit + assert decision.contribution_rules[0].rule == case.expected_contribution + + +def test_policy_evidence_canary_catalog_has_exact_planned_shape() -> None: + assert len(CLAIM_CANARIES) == 16 + assert len(SUPPORT_CANARIES) == 16 + assert len(RELEASE_CANARIES) == 16 + names = [ + *(case.name for case in CLAIM_CANARIES), + *(case.name for case in SUPPORT_CANARIES), + *(case.name for case in RELEASE_CANARIES), + ] + assert len(names) == 48 + assert len(names) == len(set(names)) + + +def test_weaker_write_declaration_with_inferred_financial_effect_is_ie( + tmp_path: Path, +) -> None: + """P0: a discarded heuristic escalation must never become a clean pass.""" + + (tmp_path / "tools.json").write_text( + json.dumps( + { + "tools": [ + { + "name": "create_refund", + "description": "Create a refund for a customer payment", + "auth": { + "type": "oauth2", + "mode": "scoped", + "scopes": ["refunds:write"], + }, + "inputSchema": { + "type": "object", + "properties": { + "amount": { + "type": "number", + "minimum": 0, + "maximum": 10000, + } + }, + "required": ["amount"], + }, + } + ] + } + ), + encoding="utf-8", + ) + (tmp_path / "shipgate.yaml").write_text( + """ +version: "0.1" +project: {name: inferred-effect-escalation} +agent: + name: refund-agent + declared_purpose: [process reviewed refunds] +environment: {target: local} +permissions: + scopes: [refunds:write] +tool_sources: + - id: refunds + type: mcp + path: tools.json +agent_bindings: + declarations: + - agent: root + complete: true + tools: [{tool: create_refund, source_id: refunds}] + handoffs: [] + reason: reviewed test binding +action_surface: + actions: + - tool: create_refund + source_id: refunds + effect: write + scopes: [refunds:write] + authority: + mode: scoped + auth_type: oauth2 +""", + encoding="utf-8", + ) + + report, exit_code = run_scan( + config_path=tmp_path / "shipgate.yaml", + output_dir=tmp_path / "reports", + formats=["json"], + ci_mode="strict", + packet_enabled=False, + ) + + assert exit_code == 20 + assert report.release_decision is not None + assert report.release_decision.decision == "insufficient_evidence" + assert report.findings == [] + action = report.action_surface_facts.actions[0] + assert action.semantic_assessment is not None + assert action.semantic_assessment.pass_eligible is True + gaps = [ + gap + for gap in report.policy_evidence_gaps + if "builtin-effect-control-applicability" in gap.why + ] + assert len(gaps) == 1 + assert gaps[0].kind == "mixed_policy_evidence" + assert gaps[0].next_action.kind == "review_policy_evidence" + + +def test_empty_finding_support_is_fail_closed() -> None: + support = finding_support([]) + + assert support.status == "indeterminate" + assert support.policy_eligible is False + assert support.blocking_eligible is False + assert support.predicates[0].evidence_bases == ["unknown"] diff --git a/tests/test_policy_evidence_architecture.py b/tests/test_policy_evidence_architecture.py new file mode 100644 index 00000000..cf57e2bf --- /dev/null +++ b/tests/test_policy_evidence_architecture.py @@ -0,0 +1,40 @@ +"""Static guards against reintroducing parallel evidence inference.""" + +from __future__ import annotations + +import re +from pathlib import Path + +ROOT = Path(__file__).resolve().parents[1] + + +def _source(path: str) -> str: + return (ROOT / path).read_text(encoding="utf-8") + + +def test_semantic_consumers_use_typed_policy_eligibility() -> None: + action_surface = _source("src/agents_shipgate/core/lenses/action_surface.py") + side_effects = _source("src/agents_shipgate/checks/side_effects.py") + mcp_audit = _source("src/agents_shipgate/cli/mcp.py") + + assert "claim.policy_eligible" in action_surface + assert "claim.policy_eligible" in side_effects + assert "claim.policy_eligible" in mcp_audit + assert "claim.provenance_kind not in" not in action_surface + assert "claim.provenance_kind not in" not in mcp_audit + + +def test_policy_rule_metadata_cannot_upgrade_support() -> None: + policy_loader = _source("src/agents_shipgate/inputs/policy_packs.py") + + assert "requested_confidence=rule.confidence" in policy_loader + assert "blocks_release=rule.block and support.blocking_eligible" in policy_loader + assert re.search(r"(? None: + resolver = _source("src/agents_shipgate/core/semantic_assessment.py") + + assert "def _hint_provenance" not in resolver + assert "_validated_hint_basis" in resolver diff --git a/tests/test_policy_packs.py b/tests/test_policy_packs.py index 5d047bdd..c9021610 100644 --- a/tests/test_policy_packs.py +++ b/tests/test_policy_packs.py @@ -87,6 +87,19 @@ def test_manifest_policy_pack_emits_suppressible_overridable_findings(tmp_path): - id: api type: openapi path: openapi.yaml +agent_bindings: + declarations: + - agent: root + complete: true + tools: + - {tool: create_refund, source_id: api} + handoffs: [] + reason: reviewed policy-pack fixture binding +risk_overrides: + tools: + create_refund: + tags: [financial_action] + reason: reviewed financial policy fixture checks: policy_packs: - path: org-pack.yaml @@ -362,7 +375,7 @@ def test_policy_pack_approval_routing_alone_does_not_block_release(tmp_path): ) -def test_policy_pack_legacy_v027_routing_fingerprint_baseline_still_matches( +def test_policy_pack_legacy_v027_baseline_cannot_accept_new_supported_finding( tmp_path, ): _write_openapi(tmp_path) @@ -448,10 +461,12 @@ def test_policy_pack_legacy_v027_routing_fingerprint_baseline_still_matches( if item.check_id == "ORG-LEGACY-ROUTING-FP" ) assert matched.fingerprint == finding.fingerprint - assert matched.baseline_status == "matched" + assert matched.baseline_status == "new" + assert matched.support is not None + assert matched.support.support_hash assert report_with_baseline.baseline is not None - assert report_with_baseline.baseline.matched_count == 1 - assert report_with_baseline.baseline.resolved_count == 0 + assert report_with_baseline.baseline.matched_count == 0 + assert report_with_baseline.baseline.resolved_count == 1 def test_cli_policy_pack_override_and_parameter_predicate(tmp_path): @@ -528,6 +543,67 @@ def test_policy_pack_rule_can_block_release_independent_of_severity(tmp_path): assert any(item.check_id == "ORG-MEDIUM-BLOCKER" for item in report.release_decision.blockers) +def test_heuristic_policy_match_routes_to_evidence_gap_not_finding(tmp_path): + _write_openapi(tmp_path) + (tmp_path / "heuristic-pack.yaml").write_text( + """ +name: Heuristic laundering regression +rules: + - id: ORG-HEURISTIC-FINANCIAL-BLOCK + title: Financial names must not self-create blockers + category: org_policy + severity: critical + confidence: high + block: true + recommendation: Provide reviewed financial-effect evidence. + match: + risk_tags: [financial_action] +""", + encoding="utf-8", + ) + (tmp_path / "shipgate.yaml").write_text( + _manifest_without_policy_pack(reviewed_financial=False) + + """ +checks: + policy_packs: + - path: heuristic-pack.yaml +""", + encoding="utf-8", + ) + + report, exit_code = run_scan( + config_path=tmp_path / "shipgate.yaml", + output_dir=tmp_path / "reports", + formats=["json"], + ci_mode="strict", + no_heuristics=True, + ) + + assert exit_code == 20 + assert not any( + item.check_id == "ORG-HEURISTIC-FINANCIAL-BLOCK" + for item in report.findings + ) + gap = next( + item + for item in report.policy_evidence_gaps + if "ORG-HEURISTIC-FINANCIAL-BLOCK" in item.why + ) + assert gap.kind == "inferred_policy_applicability" + assert gap.next_action.kind == "provide_policy_evidence" + assert report.release_decision is not None + # Other concrete high findings may take the higher-precedence review route, + # but the heuristic policy rule itself never becomes blocked or passed. + assert report.release_decision.decision in {"review_required", "insufficient_evidence"} + assert report.release_decision.evidence_coverage.policy_gap_count == len( + report.policy_evidence_gaps + ) + assert sum( + "ORG-HEURISTIC-FINANCIAL-BLOCK" in item.why + for item in report.policy_evidence_gaps + ) == 1 + + def test_policy_pack_capability_selector_matches_semantic_subject(tmp_path): _write_openapi(tmp_path) (tmp_path / "capability-pack.yaml").write_text( @@ -893,7 +969,14 @@ def _write_openapi(tmp_path: Path) -> None: ) -def _manifest_without_policy_pack() -> str: +def _manifest_without_policy_pack(*, reviewed_financial: bool = True) -> str: + risk_override = """ +risk_overrides: + tools: + create_refund: + tags: [financial_action] + reason: reviewed financial policy fixture +""" if reviewed_financial else "" return """ version: "0.1" project: @@ -916,7 +999,7 @@ def _manifest_without_policy_pack() -> str: - {tool: create_refund, source_id: api} handoffs: [] reason: reviewed policy-pack fixture binding -""" +""" + risk_override # --- v0.2: combinators, numeric predicates, sha256 pin ---------------------- @@ -1103,6 +1186,53 @@ def test_v2_none_of_excludes_matching_subjects(tmp_path): ] +def test_v2_any_of_attributes_the_authoritative_matching_branch(tmp_path): + _write_bounded_openapi(tmp_path, maximum=5000) + (tmp_path / "org-pack-v2.yaml").write_text( + """ +name: Any-of Evidence Attribution +rules: + - id: ORG-AUTHORITATIVE-ANY-OF + title: Attribute the branch that authoritatively established applicability + severity: medium + recommendation: n/a + match: + any_of: + - risk_tags: [financial_action] + - source_types: [openapi] +""", + encoding="utf-8", + ) + (tmp_path / "shipgate.yaml").write_text( + _manifest_without_policy_pack(reviewed_financial=False) + + """ +checks: + policy_packs: + - path: org-pack-v2.yaml +""", + encoding="utf-8", + ) + + report, _ = run_scan( + config_path=tmp_path / "shipgate.yaml", + output_dir=tmp_path / "reports", + formats=["json"], + ci_mode="advisory", + ) + + finding = next( + item + for item in report.findings + if item.check_id == "ORG-AUTHORITATIVE-ANY-OF" + ) + assert finding.evidence["any_of"] == { + "index": 1, + "matched": {"source_types": ["openapi"]}, + } + assert finding.support is not None + assert finding.support.policy_eligible is True + + def test_v1_flat_pack_still_loads_unchanged(tmp_path): """Backward compatibility: a v0.1-shaped pack with only flat fields.""" _write_openapi(tmp_path) diff --git a/tests/test_privacy.py b/tests/test_privacy.py index 29917353..3287e554 100644 --- a/tests/test_privacy.py +++ b/tests/test_privacy.py @@ -2,7 +2,6 @@ import json from pathlib import Path -from agents_shipgate.cli.explain_finding import explain_finding_payload from agents_shipgate.cli.scan import run_scan from agents_shipgate.core.baseline import apply_baseline, baseline_resolved_fingerprints from agents_shipgate.core.findings import ( @@ -340,19 +339,14 @@ def test_scan_redacts_public_outputs_and_reports_privacy_audit(tmp_path, monkeyp assert all(raw_secret not in row.path for row in report.privacy_audit.redacted_paths) report_json = json.loads((reports / "report.json").read_text(encoding="utf-8")) - secret_finding = next( - finding - for finding in report_json["findings"] - if finding["check_id"] == "SHIP-DOC-SECRET-IN-DESCRIPTION" - ) - explanation = explain_finding_payload( - fingerprint=secret_finding["fingerprint"], - report_path=reports / "report.json", + assert any( + gap["kind"] == "inferred_policy_applicability" + and gap["why"].startswith("SHIP-DOC-SECRET-IN-DESCRIPTION:") + for gap in report_json["policy_evidence_gaps"] ) - rendered_explanation = json.dumps(explanation, sort_keys=True) - assert raw_secret not in rendered_explanation - assert github_token not in rendered_explanation - assert agent_secret not in rendered_explanation + # Heuristic-only secret detection is now an evidence gap and deliberately + # has no finding fingerprint that could be baselined or suppressed. + assert report_json["findings"] == [] def test_scan_does_not_revalidate_manifest_after_redaction_collapses_action_tools( diff --git a/tests/test_provenance_kind.py b/tests/test_provenance_kind.py index 7c10ccd4..27057d6d 100644 --- a/tests/test_provenance_kind.py +++ b/tests/test_provenance_kind.py @@ -69,13 +69,10 @@ def test_emitted_findings_never_have_none_provenance(tmp_path, sample): ) -def test_bundled_fixture_distribution(tmp_path): - """Across the bundled samples, both static_declaration and - keyword_heuristic must be represented. Confirms the multi-axis - classification is real, not collapsed to one bucket. (The other - three values are covered by per-category construction tests below; - the bundled samples don't trigger them today.)""" +def test_bundled_fixture_findings_exclude_heuristic_policy_matches(tmp_path): + """Heuristic applicability is represented as evidence gaps, not Findings.""" kinds: set[str] = set() + policy_gap_count = 0 for sample in SAMPLES: out = tmp_path / sample.parent.name out.mkdir() @@ -89,8 +86,11 @@ def test_bundled_fixture_distribution(tmp_path): for finding in report.findings: if finding.provenance_kind is not None: kinds.add(finding.provenance_kind) + policy_gap_count += len(report.policy_evidence_gaps) assert "static_declaration" in kinds - assert "keyword_heuristic" in kinds + assert "keyword_heuristic" not in kinds + assert "regex_heuristic" not in kinds + assert policy_gap_count > 0 def test_each_provenance_value_constructs_cleanly(): diff --git a/tests/test_public_surface_contract.py b/tests/test_public_surface_contract.py index 6aea95bd..ca5b2174 100644 --- a/tests/test_public_surface_contract.py +++ b/tests/test_public_surface_contract.py @@ -479,7 +479,7 @@ def test_well_known_metadata_lists_packet_outputs(): f"current result schema; got {benchmark_result_url!r}." ) assert "verify_run" in schemas and "verify-run-schema.v2.json" in schemas["verify_run"] - assert "agent_handoff" in schemas and "agent-handoff-schema.v3.json" in schemas["agent_handoff"] + assert "agent_handoff" in schemas and "agent-handoff-schema.v4.json" in schemas["agent_handoff"] assert ( "codex_boundary_result" in schemas and "codex-boundary-result-schema.v2.json" in schemas["codex_boundary_result"] diff --git a/tests/test_safety_qualification.py b/tests/test_safety_qualification.py index f833533b..06f3db85 100644 --- a/tests/test_safety_qualification.py +++ b/tests/test_safety_qualification.py @@ -43,7 +43,7 @@ ) DECISIONS = ("passed", "review_required", "insufficient_evidence", "blocked") -VERSION = "0.16.0b4" +VERSION = "0.16.0b5" def _human_label(role: str, reviewer: str, decision: str) -> IndependentHumanLabelV1: @@ -106,7 +106,7 @@ def _test_requirements() -> SafetyQualificationRequirementsV1: minimum_blocked_exact=1, minimum_review_exact=1, minimum_insufficient_evidence_exact=1, - required_report_schema_version="0.32", + required_report_schema_version="0.33", ) @@ -118,8 +118,8 @@ def _write_json(path: Path, value: object) -> None: def _write_wheel(path: Path) -> None: with zipfile.ZipFile(path, "w") as archive: archive.writestr( - "agents_shipgate-0.16.0b4.dist-info/METADATA", - "Metadata-Version: 2.4\nName: agents-shipgate\nVersion: 0.16.0b4\n", + "agents_shipgate-0.16.0b5.dist-info/METADATA", + "Metadata-Version: 2.4\nName: agents-shipgate\nVersion: 0.16.0b5\n", ) @@ -163,7 +163,7 @@ def _fixture( actual_overrides: dict[str, str] | None = None, disagreement_case: str | None = None, ) -> tuple[Path, Path, Path, Path]: - wheel = tmp_path / "agents_shipgate-0.16.0b4-py3-none-any.whl" + wheel = tmp_path / "agents_shipgate-0.16.0b5-py3-none-any.whl" _write_wheel(wheel) policy = tmp_path / "qualification-policy.json" _write_json(policy, {"policy": "beta-exact", "version": 1}) diff --git a/tests/test_safety_qualification_release.py b/tests/test_safety_qualification_release.py index 300368c8..a9c221a1 100644 --- a/tests/test_safety_qualification_release.py +++ b/tests/test_safety_qualification_release.py @@ -26,15 +26,15 @@ verify_release_qualification, ) -VERSION = "0.16.0b4" +VERSION = "0.16.0b5" REPO_ROOT = Path(__file__).resolve().parent.parent def _write_wheel(path: Path) -> None: with zipfile.ZipFile(path, "w") as archive: archive.writestr( - "agents_shipgate-0.16.0b4.dist-info/METADATA", - "Metadata-Version: 2.4\nName: agents-shipgate\nVersion: 0.16.0b4\n", + "agents_shipgate-0.16.0b5.dist-info/METADATA", + "Metadata-Version: 2.4\nName: agents-shipgate\nVersion: 0.16.0b5\n", ) @@ -177,7 +177,7 @@ def _production_result(wheel: Path) -> SafetyQualificationResultV1: def _fixture(tmp_path: Path) -> tuple[Path, Path]: tmp_path.mkdir(parents=True, exist_ok=True) - wheel = tmp_path / "agents_shipgate-0.16.0b4-py3-none-any.whl" + wheel = tmp_path / "agents_shipgate-0.16.0b5-py3-none-any.whl" _write_wheel(wheel) qualification = tmp_path / "safety-qualification.json" qualification.write_text( diff --git a/tests/test_scan.py b/tests/test_scan.py index 5c22df44..7a54a234 100644 --- a/tests/test_scan.py +++ b/tests/test_scan.py @@ -426,7 +426,7 @@ def test_mixed_name_does_not_launder_keyword_effect_into_policy_blocker(tmp_path assert action.semantic_assessment.effect.status == "inferred" assert action.effect == "destructive" assert report.release_decision is not None - assert report.release_decision.decision == "review_required" + assert report.release_decision.decision == "insufficient_evidence" assert any( gap.kind == "inferred_effect_only" for gap in report.release_decision.evidence_coverage.evidence_gaps @@ -805,7 +805,7 @@ def test_baseline_save_and_scan_matches_existing_findings(tmp_path): baseline_path=baseline_path, ) - assert baseline.schema_version == "0.7" + assert baseline.schema_version == "0.8" assert baseline.tool_surface_facts is not None assert baseline.action_surface_facts is not None assert first_report.run_id == second_report.run_id @@ -1104,6 +1104,15 @@ def test_sdk_preview_tool_is_not_treated_as_external_write(tmp_path): if finding.tool_name == "send_email_preview" and finding.severity in {"critical", "high"} ] assert high_preview_findings == [] + assert any( + gap.kind == "inferred_policy_applicability" + and gap.subject == next( + action.tool_id + for action in report.action_surface_facts.actions + if action.tool_name == "send_email_preview" + ) + for gap in report.policy_evidence_gaps + ) def test_manual_risk_override_sets_tags_and_owner(tmp_path): @@ -1284,7 +1293,15 @@ def test_manifest_scope_checks_read_only_purpose_with_write_tool(tmp_path): formats=["json"], ) - assert any(finding.check_id == "SHIP-SCOPE-TOOL-OUTSIDE-PURPOSE" for finding in report.findings) + assert not any( + finding.check_id == "SHIP-SCOPE-TOOL-OUTSIDE-PURPOSE" + for finding in report.findings + ) + assert any( + gap.kind == "inferred_policy_applicability" + and gap.why.startswith("SHIP-SCOPE-TOOL-OUTSIDE-PURPOSE:") + for gap in report.policy_evidence_gaps + ) def test_run_id_and_source_paths_are_reproducible_without_absolute_source_refs(tmp_path): diff --git a/tests/test_schema_boundaries.py b/tests/test_schema_boundaries.py index 6fc37708..7ed8acb8 100644 --- a/tests/test_schema_boundaries.py +++ b/tests/test_schema_boundaries.py @@ -256,7 +256,7 @@ def test_representative_schema_payloads_keep_wire_fields() -> None: tool_surface=ToolSurfaceSummary(total_tools=0, high_risk_tools=0), ) report_payload = report_json_payload(report) - assert report_payload["report_schema_version"] == "0.32" + assert report_payload["report_schema_version"] == "0.33" assert list(report_payload) == [ "schema_version", "report_schema_version", @@ -294,6 +294,7 @@ def test_representative_schema_payloads_keep_wire_fields() -> None: "tool_inventory", "tool_catalog", "source_warnings", + "policy_evidence_gaps", "agent_summary", "policy_audit", "privacy_audit", @@ -337,7 +338,7 @@ def test_representative_schema_payloads_keep_wire_fields() -> None: not_proven=NotProvenSection(headline="not proven"), ) packet_payload = serialize_packet_json(packet) - assert packet_payload["packet_schema_version"] == "0.10" + assert packet_payload["packet_schema_version"] == "0.11" assert "generated_at" not in packet_payload assert "action_surface_diff" in packet_payload assert report_payload["capability_runtime_evidence"]["enabled"] is False @@ -355,7 +356,7 @@ def test_representative_schema_payloads_keep_wire_fields() -> None: ], ) assert baseline.model_dump(mode="json") == { - "schema_version": "0.7", + "schema_version": "0.8", "project": {}, "agent": {}, "created_at": "2026-01-01T00:00:00Z", @@ -370,6 +371,7 @@ def test_representative_schema_payloads_keep_wire_fields() -> None: "severity": "high", "title": "Example", "provenance": None, + "support_hash": None, } ], "tool_surface_facts": None, diff --git a/tests/test_semantic_assessment.py b/tests/test_semantic_assessment.py index 32eaeb63..45e2c44f 100644 --- a/tests/test_semantic_assessment.py +++ b/tests/test_semantic_assessment.py @@ -5,6 +5,7 @@ import pytest from pydantic import ValidationError +from agents_shipgate.core.capability_lattice import mcp_permission_risk_hints from agents_shipgate.core.domain import AuthInfo, Tool, ToolRiskHint from agents_shipgate.core.semantic_assessment import ( assess_tool_semantics, @@ -75,6 +76,33 @@ def test_read_only_hint_cannot_suppress_write_scope() -> None: assert "conflicting_effect_evidence" in {issue.kind for issue in assessment.effect.issues} +def test_structural_scope_risk_hint_preserves_typed_basis() -> None: + tool = _tool( + auth=AuthInfo( + type="oauth2", + scopes=["orders:write"], + source="mcp", + mode="scoped", + explicit=True, + ) + ) + tool.risk_hints.extend(mcp_permission_risk_hints(tool)) + + assessment = assess_tool_semantics(tool) + + scope_hints = [ + claim + for claim in assessment.effect.claims + if claim.source == "risk_hint:auth_scope" + ] + assert scope_hints + assert {claim.basis for claim in scope_hints} == {"structural_scope"} + assert not any( + issue.kind == "invalid_evidence_provenance" + for issue in assessment.effect.issues + ) + + def test_permission_class_aliases_are_unioned_and_conflict_checked() -> None: assessment = assess_tool_semantics( _tool( @@ -133,6 +161,8 @@ def test_manual_positive_risk_refines_structural_effect() -> None: tag="financial_action", source="manual", confidence="high", + basis="reviewed_declaration", + provenance_kind="static_declaration", ) ], auth=AuthInfo(source="openapi", mode="none", explicit=True), @@ -153,6 +183,8 @@ def test_manual_positive_risk_alone_cannot_close_effect_gap() -> None: tag="financial_action", source="manual", confidence="high", + basis="reviewed_declaration", + provenance_kind="static_declaration", ) ], auth=AuthInfo(source="sdk_static", mode="none", explicit=True), @@ -164,6 +196,95 @@ def test_manual_positive_risk_alone_cannot_close_effect_gap() -> None: assert assessment.pass_eligible is False +def test_topical_auth_scope_is_not_authoritative_financial_evidence() -> None: + assessment = assess_tool_semantics( + _tool( + source_type="openapi", + risk_hints=[ + ToolRiskHint( + tag="financial_action", + source="auth_scope", + confidence="high", + basis="inferred_keyword", + provenance_kind="keyword_heuristic", + evidence={"scope": "payments:refund:write"}, + ) + ], + auth=AuthInfo( + type="oauth2", + scopes=["payments:refund:write"], + source="openapi", + mode="scoped", + explicit=True, + ), + ) + ) + + financial = next( + claim for claim in assessment.effect.claims if claim.value == "financial_write" + ) + write = next(claim for claim in assessment.effect.claims if claim.value == "write") + assert financial.basis == "inferred_keyword" + assert financial.policy_eligible is False + assert financial.provenance_kind == "keyword_heuristic" + assert write.basis == "structural_scope" + assert write.policy_eligible is True + assert assessment.conservative_effect == "financial_write" + assert assessment.effect.status == "inferred" + + +def test_heuristic_claim_cannot_conflict_with_reviewed_effect() -> None: + declaration = ActionDeclarationConfig.model_validate( + { + "tool": "process_order", + "effect": "read", + "authority": {"mode": "none"}, + } + ) + assessment = assess_tool_semantics( + _tool( + source_type="langchain_inventory", + risk_hints=[ + ToolRiskHint( + tag="destructive", + source="misleading_keyword", + confidence="high", + basis="inferred_keyword", + provenance_kind="keyword_heuristic", + ) + ], + ), + declaration, + ) + + assert assessment.conservative_effect == "destructive" + assert assessment.effect.status == "declared" + assert "conflicting_effect_evidence" not in { + issue.kind for issue in assessment.effect.issues + } + + +def test_untyped_risk_hint_fails_closed() -> None: + assessment = assess_tool_semantics( + _tool( + source_type="langchain_inventory", + risk_hints=[ + ToolRiskHint( + tag="write", + source="third_party_magic", + confidence="high", + ) + ], + auth=AuthInfo(source="inventory", mode="none", explicit=True), + ) + ) + + assert "invalid_evidence_provenance" in { + issue.kind for issue in assessment.effect.issues + } + assert assessment.pass_eligible is False + + def test_ast_framework_declaration_cannot_replace_reviewed_inventory() -> None: declaration = ActionDeclarationConfig.model_validate( { diff --git a/tests/test_v07_metadata_roundtrip.py b/tests/test_v07_metadata_roundtrip.py index b6ccecdf..8cd97307 100644 --- a/tests/test_v07_metadata_roundtrip.py +++ b/tests/test_v07_metadata_roundtrip.py @@ -240,7 +240,7 @@ def test_package_version_is_current_in_tree_runtime(): """Guard against bumping schemas while leaving package metadata behind.""" import agents_shipgate - assert agents_shipgate.__version__ == "0.16.0b4", ( + assert agents_shipgate.__version__ == "0.16.0b5", ( f"package version is {agents_shipgate.__version__!r}; " - "expected 0.16.0b4 for the current in-tree runtime" + "expected 0.16.0b5 for the current in-tree runtime" ) diff --git a/tests/test_verifier_scenarios.py b/tests/test_verifier_scenarios.py index bfe13bc2..f144fb13 100644 --- a/tests/test_verifier_scenarios.py +++ b/tests/test_verifier_scenarios.py @@ -245,7 +245,7 @@ def test_scenario_committed_base_lock_emits_semantic_capability_diff( ) reports = repo / "agents-shipgate-reports" diff_payload = json.loads((reports / "capability-lock-diff.json").read_text(encoding="utf-8")) - assert diff_payload["capability_lock_diff_schema_version"] == "0.6" + assert diff_payload["capability_lock_diff_schema_version"] == "0.7" assert diff_payload["summary"]["added"] == 1 assert diff_payload["summary"]["changed"] == 0 assert diff_payload["added"][0]["identity"]["tool_name"] == "stripe.create_refund"