Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions .well-known/agents-shipgate.json
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,7 @@
"agent_boundary_result_schema_path": "docs/agent-boundary-result-schema.v1.json",
"report_schema_version": "0.34",
"packet_schema_version": "0.12",
"verifier_schema_version": "0.6",
"verifier_schema_version": "0.7",
"verify_run_schema_version": "shipgate.verify_run/v3",
"verification_plan_schema_version": "shipgate.verification_plan/v1",
"verification_unit_result_schema_version": "shipgate.verification_unit_result/v1",
Expand Down Expand Up @@ -279,7 +279,7 @@
"host_grants_inventory_schema_version": "0.2",
"host_grants_baseline_schema_version": "0.2",
"host_grants_drift_schema_version": "0.2",
"trigger_catalog_schema_version": "0.2",
"trigger_catalog_schema_version": "0.3",
"capability_standard_version": "0.5",
"governance_benchmark_catalog_schema_version": "0.2",
"governance_benchmark_result_schema_version": "0.2",
Expand Down Expand Up @@ -441,7 +441,7 @@
"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.6.json",
"verifier": "https://raw.githubusercontent.com/ThreeMoonsLab/agents-shipgate/main/docs/verifier-schema.v0.7.json",
"verify_run": "https://raw.githubusercontent.com/ThreeMoonsLab/agents-shipgate/main/docs/verify-run-schema.v3.json",
"verification_plan": "https://raw.githubusercontent.com/ThreeMoonsLab/agents-shipgate/main/docs/verification-plan-schema.v1.json",
"verification_unit_result": "https://raw.githubusercontent.com/ThreeMoonsLab/agents-shipgate/main/docs/verification-unit-result-schema.v1.json",
Expand Down
2 changes: 1 addition & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
# Agents Shipgate · Agent Instructions

Authoritative instructions for AI coding agents (Claude Code, Codex, Cursor, Aider, Cline, Windsurf, Devin, or any other harness — see [`docs/agents/any-coding-agent.md`](docs/agents/any-coding-agent.md)) working **with** this repository or a project that uses Agents Shipgate.
Expand Down Expand Up @@ -315,7 +315,7 @@
agents-shipgate trigger --list-rules --json
```

The command emits a stable JSON verdict: `should_run` (alias of `run_shipgate`), `force_run`, `dry_run_recommended`, `skip_reason`, `matched_rules`, `changed_files`, and `diff_tokens`. The developer entry point `python -m agents_shipgate.triggers shipgate.yaml prompts/refund.md` is preserved.
The command emits a stable JSON verdict: `should_run` (alias of `run_shipgate`), `force_run`, `dry_run_recommended`, `skip_reason`, `matched_rules`, `changed_files`, `diff_tokens`, plus `input_status` and `evaluation_status` (catalog schema `0.3`). When the diff could not be read in full, a *skip* verdict is withheld: `evaluation_status` is `not_evaluated` and `should_run`/`run_shipgate`/`skip`/`skip_reason` are `null`, so an unread diff is never reported as `no_match`. A *run* verdict is still published, because rule matching is monotone — evidence that already matched cannot be un-matched by the bytes that are missing — and it arrives as `evaluation_status: evaluated` with `should_run: true`. That evidence may be a rule matched on the change set or `force_run` from a manifest that is present regardless of the diff, so read `matched_rules` before attributing it. Branch on `evaluation_status`, not on `should_run` alone. The developer entry point `python -m agents_shipgate.triggers shipgate.yaml prompts/refund.md` is preserved.

**Stop conditions.** Stop and do not run `init` only when **all** of these hold:

Expand Down
38 changes: 38 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,44 @@

## Unreleased

- **An unreadable PR diff is no longer reported as "nothing here is
agent-related."** `verify --preview` collapsed every diff-acquisition failure
into one message, then evaluated the trigger catalog against the empty inputs
that failure left behind — publishing `skip_reason: "no_match"` with the
rationale *"nothing in this PR signals a tool-surface change"* about a PR it
had never read. The top-level control result stayed fail-closed
(`merge_verdict: "unknown"`), but the explanation invited exactly the wrong
conclusion, and on an unconfigured workspace the failure was not reported at
all: both diff-failure branches were gated on a manifest being present, so a
shallow or blobless clone of an un-adopted repository — the normal shape of
first contact — fell through to *"Shipgate is not configured in this
workspace"* with the Git error visible nowhere but `base_notes`. Three
things changed. Diff acquisition is now classified rather than flattened:
`not_attempted`, `refs_missing`, `merge_base_missing`,
`unrelated_histories`, `objects_missing`, `metadata_limit_exceeded`,
`body_limit_exceeded`, `git_timeout`, and `git_failed` are read off Git's own
diagnostic — including the two causes Git reports identically as "no merge
base", a shallow checkout that deepening repairs versus two roots that no
fetch can ever join — and travel on the new
`verifier.json` `diff_status` block together with a bounded, path-redacted
excerpt and the precise repair — deepen history, hydrate partial-clone
objects (verification sets `GIT_NO_LAZY_FETCH=1`, so Git will not fetch them
implicitly), or take it to a human when fetching cannot help. Metadata and
body are collected separately, so a diff whose body cannot be read no longer
discards the changed paths that were read successfully; a blobless clone
answers `--name-status` in full, and those paths are exactly what says a PR
touches an agent surface. And the trigger evaluator gained the state it was
missing: `input_status` and `evaluation_status`, with `should_run`,
`run_shipgate`, `skip`, and `skip_reason` all `null` when the inputs were not
fully read. The asymmetry is deliberate — rule matching is monotone in the
evidence, so a *run* verdict reached from partial evidence stays sound and is
still published, while any *skip* verdict is withheld. Trigger catalog schema
`0.2 → 0.3` (nullable verdict fields, the two new fields, and the new
`next_action.kind: "input_required"`); verifier schema `0.6 → 0.7`
(`diff_status`; v0.6 remains a frozen reference and is still readable).
`contract_version`, `report_schema_version`, and every other schema counter
are unchanged.

- **Google ADK repositories that share one tool between agents can be scanned
again.** Binding the same `FunctionTool` to a coordinator and its sub-agents
is the canonical ADK multi-agent shape — it is what `google/adk-samples`
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -639,7 +639,7 @@ artifacts — in read order:

- **`agents-shipgate-reports/verification-receipt.json`** — the **first artifact a coding agent validates**: a terminal content-addressed closure over the exact request (including `verification-input.diff`), worker result, decision, and artifact set. It is written last; use `agents-shipgate verification reproduce` to validate every referenced hash.
- **`agents-shipgate-reports/agent-handoff.json`** — the compact `shipgate.agent_handoff/v6` object. Lead with `control.state`, then `gate.merge_verdict`; it projects the same request, decision, and authorization evaluation and does not introduce a second verdict.
- **`agents-shipgate-reports/verifier.json`** — the **authoritative PR/control evidence substrate** (`verifier_schema_version: "0.6"`). A coding agent switches on `control.state`, then reads `authorization`, `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. Only an accepted signed authorization evaluation may expose an exact reviewed command; the release verdict remains unchanged. 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/verifier.json`** — the **authoritative PR/control evidence substrate** (`verifier_schema_version: "0.7"`). A coding agent switches on `control.state`, then reads `authorization`, `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. Only an accepted signed authorization evaluation may expose an exact reviewed command; the release verdict remains unchanged. 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 `shipgate.verify_run/v3` projection embedding the exact verification plan, executor, unit-result IDs, decision ID, outcome, and artifact paths. Its deprecated `run_id` is an exact alias of `request_id`.
- **`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.
Expand Down
78 changes: 74 additions & 4 deletions STABILITY.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,42 @@ for reproducible CI.

---

<a id="migration-note-unreleased-diff-status"></a>

## Migration Note: unreleased — diff input health

Verifier schema `0.6 → 0.7` and trigger catalog `0.2 → 0.3`. `contract_version`
stays at `19`; no CLI surface changed.

`verifier.json` gains a top-level `diff_status` block that reports whether the
compared change set was actually read: `completeness` (`complete` / `partial` /
`unavailable`), a `reason` token (`not_attempted`, `refs_missing`,
`merge_base_missing`, `unrelated_histories`, `objects_missing`,
`metadata_limit_exceeded`, `body_limit_exceeded`, `git_timeout`,
`git_failed`), a bounded path-redacted `detail`, the
`remediation`, and `fetch_repairable`. Verifier v0.6 remains a frozen reference
and its artifacts still parse.

The trigger evaluator gains `input_status` and `evaluation_status`, and
`should_run`, `run_shipgate`, `skip`, and `skip_reason` become nullable.
**Consumers that switch on `should_run` must handle `null`**: it means the diff
was not read in full, so no verdict exists. Treating `null` as falsy is safe —
it routes to "do not claim this PR is irrelevant" — but reporting it as "skip"
is not. `next_action.kind` gains `"input_required"`; treat unrecognized kinds as
"no command is authorized".

Before this change, a shallow clone with no reachable merge base and a partial
clone with unfetched blobs both surfaced as one message, and the trigger then
evaluated the empty inputs those failures left behind and reported
`skip_reason: "no_match"` — "nothing in this PR signals a tool-surface change" —
about a PR the verifier never read. On a workspace without `shipgate.yaml` the
failure was not surfaced at all: preview routed to "Shipgate is not configured
in this workspace". Both are fixed, and a diff whose body cannot be read now
keeps the changed paths that were collected successfully instead of discarding
them.

---

<a id="migration-note-0-16-0b7"></a>

## Migration Note: 0.16.0b7
Expand Down Expand Up @@ -595,6 +631,9 @@ Stable JSON fields:
control contract vocabulary.
- `verifier_schema_version` — schema version for
`agents-shipgate-reports/verifier.json`.
- `trigger_catalog_schema_version` — schema version of the published trigger
catalog (`docs/triggers.json`) and, with it, of the run/skip verdict the
evaluator emits.
- `verify_run_schema_version` — schema version for
`agents-shipgate-reports/verify-run.json`.
- `human_authorization_request_schema_version`,
Expand Down Expand Up @@ -1300,17 +1339,38 @@ release decision. That action may be `detect`/`initialize` for
relevant unconfigured repos, or `verify` for configured repos. Use it as the
first touch on a repo or PR before committing to a full scan.

`verifier.json` is governed by [`docs/verifier-schema.v0.6.json`](docs/verifier-schema.v0.6.json).
Verifier v0.1 through v0.5 remain frozen references. It remains an orchestration artifact: `release_decision.decision` in
`verifier.json` is governed by [`docs/verifier-schema.v0.7.json`](docs/verifier-schema.v0.7.json).
Verifier v0.1 through v0.6 remain frozen references. It remains an orchestration artifact: `release_decision.decision` in
`report.json` is still the only release gate. Release and merge fields remain
mirrors or deterministic projections of report data; the v0.6 authorization
evaluation is an operational overlay that cannot change them. Stable additive
evaluation and the v0.7 `diff_status` block are operational overlays that
cannot change them. Stable additive
fields a consumer may read:

- `control` — the schema-enforced `complete | agent_action_required |
human_review_required` operational projection. The same serialized object is
emitted by verifier, handoff, and verify-run.
- `execution` — `"not_run" | "succeeded" | "skipped" | "failed"`.
- `diff_status` (v0.7+) — how completely the compared change set was read, and
why not when it was not. `completeness` is `"complete" | "partial" |
"unavailable"`; `reason` is `null` exactly when `completeness` is
`"complete"`, and otherwise one of `not_attempted`, `refs_missing`,
`merge_base_missing`, `unrelated_histories`, `objects_missing`,
`metadata_limit_exceeded`, `body_limit_exceeded`, `git_timeout`,
`git_failed`. `merge_base_missing` and `unrelated_histories` are
deliberately distinct: the first is a shallow checkout that truncated a
merge base which does exist, and deepening restores it; the second is two
roots with no common ancestor, which no fetch can create — `fetch_repairable`
is the field to branch on. `detail` is a bounded, path-redacted excerpt of
Git's own diagnostic; `remediation` names the repair; `fetch_repairable`
says whether making refs or objects available locally can fix it.
**`"complete"` is the only value that licenses reading a negative `trigger`
result.** Anything else means the evidence the verdict would rest on was
missing — it is never evidence that a PR is unrelated to agent capabilities.
`null` means the artifact predates v0.7 and carries no input-health
evidence, which a consumer must treat as unknown, never as complete. New
`reason` values may be added additively; treat an unrecognized reason as
"the diff was not read in full".
- `static_analysis_only`, `runtime_behavior_verified`, and
`static_verdict_disclaimer` — locked to `true`, `false`, and the canonical
static-only disclaimer. When an embedded release decision is present, the
Expand Down Expand Up @@ -1357,7 +1417,17 @@ fields a consumer may read:
context, not as the controller's primary verdict.
- `mode` — `"advisory"` / `"strict"` / `"skipped"` / `"preview"`.

`verifier.json` also carries `trigger` (the run/skip evaluation), `base_status`,
`verifier.json` also carries `trigger` — the run/skip evaluation, catalog
schema `0.3`. Read `trigger.evaluation_status` before `trigger.should_run`:
when it is `"not_evaluated"`, `should_run`, `run_shipgate`, `skip`, and
`skip_reason` are all `null` because the diff was not read in full (see
`diff_status`), and `next_action.kind` is `"input_required"`. `skip_reason` is
one of `stop_conditions`, `skip_rule`, `dry_run_only`, `no_match` — and
`no_match` is never emitted for inputs that were not fully read. A `run`
verdict *is* still published from partial evidence: rule matching is monotone,
so more evidence can only add matches. `matched_rules` says what carried it —
a `force_run` match rests on the manifest being present, not on anything the
diff showed. It also carries `base_status`,
`head_status`, `base_ref`, `head_ref`, `changed_files`, `base_notes`, the full
embedded `release_decision`, and an `artifacts` map
(`{verifier_json, pr_comment, report_json, report_markdown, report_sarif,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,8 @@
"e45f9d385f0e7744a5731694f337952682e1849e97be2d0a488ca3cff9db5792",
"98ba22d7518ae4635ed109fd187323da0541281061dd4f259ac7fdb950c7b185",
"02e780f5a1506d948e4c1d77f6ee4c6b4193227a4fd2ced081847d1fb2e5fbd0",
"bc5cd31a5c4d4f6a1ebf6a04db3f80480e7cc5f9ab2b7a6f7e3f62e8ddfc3937"
"bc5cd31a5c4d4f6a1ebf6a04db3f80480e7cc5f9ab2b7a6f7e3f62e8ddfc3937",
"58ea3b6bba89078ec54d6b5493ffebf9250d9619fbacef5090285b009e58cdcd"
],
"prompts/add-shipgate-to-repo.md": [
"ea3c37cfbbd42c40d164abfe21d468a3a5550d5384125f94a53c947dea6b4b2a",
Expand Down Expand Up @@ -83,7 +84,8 @@
"8d1540095101cd7ff3aec4ba998ced5c135cdbdb71637ad0c4e5d42fc6ec9ab7",
"a8ee5f93cab1017c623075c39c1c5bdc639855c37e588e1c9190ab963bb50446",
"8f408aed05cb85e06c9f8bb13ee189131eeccfa66fa2c1119e802c43ae97f19c",
"686ab73c76936dee6290716d197c97bf77893534654157dc01274e7aeb32fde7"
"686ab73c76936dee6290716d197c97bf77893534654157dc01274e7aeb32fde7",
"370a81cf1c35212584702ca89c5476f3cd6c19aaaf8b4bb9f57c18476f0d13ef"
]
},
"bootstrap_legacy_sha256": {
Expand Down
Loading