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
12 changes: 6 additions & 6 deletions .well-known/agents-shipgate.json
Original file line number Diff line number Diff line change
Expand Up @@ -309,9 +309,9 @@
"attestation_schema_version": "0.5",
"registry_schema_version": "0.4",
"org_evidence_bundle_schema_version": "shipgate.org_evidence_bundle/v2",
"host_grants_inventory_schema_version": "0.6",
"host_grants_baseline_schema_version": "0.6",
"host_grants_drift_schema_version": "0.6",
"host_grants_inventory_schema_version": "0.7",
"host_grants_baseline_schema_version": "0.7",
"host_grants_drift_schema_version": "0.7",
"trigger_catalog_schema_version": "0.4",
"capability_standard_version": "0.5",
"governance_benchmark_catalog_schema_version": "0.2",
Expand Down Expand Up @@ -525,9 +525,9 @@
"org_governance": "https://raw.githubusercontent.com/ThreeMoonsLab/agents-shipgate/main/docs/org-governance-schema.v0.1.json",
"org_evidence_bundle": "https://raw.githubusercontent.com/ThreeMoonsLab/agents-shipgate/main/docs/org-evidence-bundle-schema.v2.json",
"registry": "https://raw.githubusercontent.com/ThreeMoonsLab/agents-shipgate/main/docs/registry-schema.v0.4.json",
"host_grants_inventory": "https://raw.githubusercontent.com/ThreeMoonsLab/agents-shipgate/main/docs/host-grants-inventory-schema.v0.6.json",
"host_grants_baseline": "https://raw.githubusercontent.com/ThreeMoonsLab/agents-shipgate/main/docs/host-grants-baseline-schema.v0.6.json",
"host_grants_drift": "https://raw.githubusercontent.com/ThreeMoonsLab/agents-shipgate/main/docs/host-grants-drift-schema.v0.6.json",
"host_grants_inventory": "https://raw.githubusercontent.com/ThreeMoonsLab/agents-shipgate/main/docs/host-grants-inventory-schema.v0.7.json",
"host_grants_baseline": "https://raw.githubusercontent.com/ThreeMoonsLab/agents-shipgate/main/docs/host-grants-baseline-schema.v0.7.json",
"host_grants_drift": "https://raw.githubusercontent.com/ThreeMoonsLab/agents-shipgate/main/docs/host-grants-drift-schema.v0.7.json",
"scenario": "https://raw.githubusercontent.com/ThreeMoonsLab/agents-shipgate/main/docs/scenario-schema.v0.1.json",
"checks_catalog": "https://raw.githubusercontent.com/ThreeMoonsLab/agents-shipgate/main/docs/checks.json",
"determinism_boundary": "https://raw.githubusercontent.com/ThreeMoonsLab/agents-shipgate/main/docs/determinism-boundary.json",
Expand Down
6 changes: 3 additions & 3 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -832,9 +832,9 @@ For the short, current statement of "which fields to read", see [`docs/agent-con
| Verifier schema (current) | [`docs/verifier-schema.v0.21.json`](docs/verifier-schema.v0.21.json) | `0.21` |
| Agent handoff schema (current) | [`docs/agent-handoff-schema.v9.json`](docs/agent-handoff-schema.v9.json) | `shipgate.agent_handoff/v9` |
| Preflight schema (current) | [`docs/preflight-schema.v0.5.json`](docs/preflight-schema.v0.5.json) | `0.5` |
| Host-grants inventory schema | [`docs/host-grants-inventory-schema.v0.6.json`](docs/host-grants-inventory-schema.v0.6.json) | `0.6` |
| Host-grants baseline schema | [`docs/host-grants-baseline-schema.v0.6.json`](docs/host-grants-baseline-schema.v0.6.json) | `0.6` |
| Host-grants drift schema | [`docs/host-grants-drift-schema.v0.6.json`](docs/host-grants-drift-schema.v0.6.json) | `0.6` |
| Host-grants inventory schema | [`docs/host-grants-inventory-schema.v0.7.json`](docs/host-grants-inventory-schema.v0.7.json) | `0.7` |
| Host-grants baseline schema | [`docs/host-grants-baseline-schema.v0.7.json`](docs/host-grants-baseline-schema.v0.7.json) | `0.7` |
| Host-grants drift schema | [`docs/host-grants-drift-schema.v0.7.json`](docs/host-grants-drift-schema.v0.7.json) | `0.7` |
| Capability standard | [`docs/capability-standard.md`](docs/capability-standard.md) | `0.5` |
| Capability lock schema | [`docs/capability-lock-schema.v0.8.json`](docs/capability-lock-schema.v0.8.json) | `0.8` |
| Capability lock diff schema | [`docs/capability-lock-diff-schema.v0.9.json`](docs/capability-lock-diff-schema.v0.9.json) | `0.9` |
Expand Down
10 changes: 9 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,16 @@
- **The problem.** A pull request that added a Cursor plugin's `mcp.json`, removed a `beforeShellExecution` guard from `.cursor/hooks.json`, gave a dotfiles package's `claude/.claude/settings.json` `Bash(*)`, or moved a marketplace plugin's pinned `sha` printed `No static host-grant changes detected`, as a docs-only change does. Re-running a 23-PR public corpus after #812 found 11 of 23 pull requests were such coverage gaps: 0 of the 9 comparable zero-row results named the changed relevant file, and 4 of them named a file the pull request did not touch while omitting the one it did.
- **What is named.** `diff`, `verify` and the manifest-free PR comment list, under `What this run established`, each path in the comparison's own changed-file set that a bounded, documented candidate rule recognises and no reader of this entry read: `mcp.json` in a plugin directory, a plugin manifest's `mcpServers`, a Codex, Cursor or Copilot manifest's `hooks` and the hook files it names, a manifest or marketplace that does not parse, `.cursor/hooks.json`, host settings below the repository root, and an external marketplace plugin source — `plugins/demo/mcp.json (cursor): added, not read by this entry: MCP configuration in a plugin directory; no row, and loading is not established`. An external source names what it now points at, redacted, and is never fetched. The block's first line says the list includes them. Ordinary documentation, an unrelated `*.json` and an unchanged candidate name nothing.
- **What it is not.** Never a row, a widening, a `check` violation or a claim that a host loads the file. Nothing is fetched or run, only plugin manifests and marketplaces are read, and at most 32 candidates are examined; the rest, and any whose rule needed a file that was not read or did not parse, are counted as not examined, on a line that names both causes. The rules are listed in `docs/host-boundary-support.md` under *Changed inputs named but not read*.
- **JSON.** A `changed_not_read` coverage item with its `candidate` rule, ranked right after the blocking limits and inside the existing cap; `read_sources_only` is `false` while one is named, and `unread_candidates` / `unread_candidates_not_examined` say whether the change set was examined. Verifier `0.20` → `0.21`, capability diff `0.3` → `0.4`, runtime contract 40 → 41; host-grants stays `0.6`, and `minimum_control_contract_version` stays `21`. A `0.20` verifier reads with the search not recorded.
- **JSON.** A `changed_not_read` coverage item with its `candidate` rule, ranked right after the blocking limits and inside the existing cap; `read_sources_only` is `false` while one is named, and `unread_candidates` / `unread_candidates_not_examined` say whether the change set was examined. Verifier `0.20` → `0.21`, capability diff `0.3` → `0.4`, runtime contract 40 → 41; host-grants does not move for it (#819, below, moves it to `0.7` in the same contract), and `minimum_control_contract_version` stays `21`. A `0.20` verifier reads with the search not recorded.
- **One route moves, on `verify` and `verify --preview` alike.** A manifest-free `verify` whose only host-relevant change is such an input, or a changed candidate it counts as not examined, now publishes the host comparison (advisory, exit `0`) instead of the setup route, which said nothing about the change. `verify --preview` moves the same way: its next action is now `discover` (`audit --host`) with the comparison published, where it was `initialize` (`init --write`) with none. That includes an agent-related workspace, as it already did when the change edited a host file this entry reads. The pilot ledger's source-tree column was re-measured for contract 41. Rows, digests, baselines, `audit --host`, `check` and the benchmark replays are unchanged.
- A hook row now names what changed in the hook, and an MCP row names a change to the server's launch arguments. Before, `diff`, `verify`, the manifest-free PR comment and `check` printed `PostToolUse → PostToolUse` whether the edit was to the hook's matcher, its command or its timeout, and an MCP server whose version pin moved from `example-mcp-server@1.2.3` to `@latest` read `docs: no difference in the command name npx, env key names or header key names; the change is in a detail this output does not show, such as the command's path or arguments`: the grants carried none of it, and only `config_sha256` saw the edit. On five of 23 public pull requests measured on 2026-09-15, the hook rows showed only event names. (#819, slice 2 of #795; direction is #820, an unpinned-launch note #825)
- **The rows a reviewer reads:** `PostToolUse: matcher Edit → Edit|Write|Bash`, `PostToolUse: command changed (lint.sh sha256:d075f5f4772e → curl sha256:a510416cbecc)`, `PostToolUse: timeout 10 → 600` and `docs: package example-mcp-server@1.2.3 → example-mcp-server@latest`, in `diff`, `verify` text, the PR comment and `check` text, and in `review.changes[].change` in `diff --json` and `verifier.json`; any other launch argument edit reads `launch arguments changed (sha256:… → sha256:…)`, a digest printed as its first twelve hex digits. A timeout written as text prints quoted, so it never reads as a number or a boolean: `timeout 5 → "5"`, `timeout true → "true"`. With several handlers under one event the entry names which one (`handler 2 timeout 5 → 50`), and an added or removed handler is listed as such. The same published handlers in another order read `the published handlers in a different order; a detail this output does not show may also differ, such as …`, never that they are the same handlers. An added or removed hook names its handlers, `SessionEnd (command cleanup.sh sha256:18d2c7ec39bc)`, and an added MCP server its package. When none of the published fields differ, the entry says the change is in a detail it does not show — another hook setting such as `async`, or a redacted or shortened matcher or timeout; for an MCP server, the command's path or another setting such as `cwd` — instead of repeating the same values. The row's direction, severity, `why` and loading basis are unchanged: plugin-selected (#714) and Codex hooks read as before, and no entry claims a direction (#820), runtime loading or what a command does.
- **No command or argument text is published, on any surface.** A hook command is published as its executable's name — the last path segment of its first word, only when that is a plain token (`[A-Za-z0-9._+-]`, at most 80 characters) that no redaction rule rewrites, not a shell reserved word such as `if` and not part of a URL (a first word holding `://`), otherwise `<not-shown>` — and the SHA-256 of the whole command as `config_sha256`'s input holds it. An MCP server's arguments are published as at most one package specification of a strict shape (npm `name@version` or `@scope/name@version` with a version of two or three numeric parts, a `^`/`~` range on one or a common dist-tag; PyPI `name==version` with a version of two or more parts; an OCI image reference with a path and a tag or `sha256` digest) that no redaction rule rewrites and that follows no flag but a package runner's own (`-y`, `--from`, `--rm` …), and the SHA-256 of every argument with the package replaced by a marker and its position digested beside them. The matcher passes the #802 published-label redaction and is cut at 120 characters, and a matcher longer than 1,024 characters as `config_sha256`'s input holds it is `<not-shown>`, never redacted or cut; a timeout is the number or boolean as declared, an over-80-digit integer's cut digits, a plain-token string, or `<not-shown>` for anything else, a non-finite float among them. Earlier drafts published redacted command words, and each of four review cycles found a credential the redaction rules missed inside free-form shell text; publishing none of it closes the class.
- **Host-grants `0.7`:** a hook grant adds `handlers[]` — each handler's group `matcher`, its `command` `{executable, sha256}` and its `timeout` — and `omitted_handlers` (at most sixteen handlers are listed); an MCP server grant adds `package` and `args_sha256`, both `null` when no `args` is declared. A declaration outside the documented shape (a list of matcher groups whose `hooks` are objects, each `command` a string where one is declared) publishes `handlers: null`, and its row says the matcher, command and timeout are not shown because `the declaration is not a list of matcher groups whose hooks are objects and whose commands are strings`; when only one side is outside it, the row names that side (`base` or `head`) and lists the other side's handlers. Runtime contract v41.
- **Saved baselines hold none of it:** `audit --host --save-baseline` writes each hook and MCP grant without `handlers`, `package` or `args_sha256`, in either scope, so nothing read from `~/.claude/settings.json`, `~/.cursor/mcp.json`, managed settings or a git-ignored `.claude/settings.local.json` reaches the committed baseline. A saved `0.7` baseline's grants are the ones a `0.6` baseline holds; no comparison, row or digest read them, and `inventory_sha256` is unchanged.
- **The PR comment keeps every line 1.1.0 kept:** its 6,000-character bound cuts at the first line that does not fit, so long entries could hide every row after them, the coverage block, the change count, the review question, the reproduction and the advisory. The lines 1.1.0 printed now get their room first, the coverage block included, and the entries only what is left: an entry is printed whole when the whole comment fits; otherwise every longer entry is cut to the widest length of at least 60 characters at which it does, ending in `…`; and where not even that fits, entries are printed in their shortest form, longest first: a field-level difference cut after its name (`PreToolUse: …`), an added or removed grant as its row (`(absent) → PreToolUse`). No entry in that form is longer than the one 1.1.0 printed, and a permission rule's entry is never shortened. One line after the rows, not one per entry, says entries were shortened and that `verifier.json` holds each whole. On a pull request that moves two hook scripts under 14 events, 1.1.0's comment held every row, the coverage block, the review question, the reproduction and the advisory, and so does this one, within the same 6,000 characters. `verifier.json` and the other routes keep every entry whole. A comment with no readiness report now points to `verifier.json` when it omits detail, not to a `report.md` that route does not write.
- **Unchanged:** grant equality and every inventory digest leave the new members out, so a change is a row exactly when it was one before, through `config_sha256`; a value the digest's own input redacts (after `--token`, `--api-key` or `--password`, a `--password=…` value, an `X-Api-Key:` header value, a URL's path) moves no published digest, so a change confined to it is no row, as on 1.1.0; every row value, the row count, `check`'s boundary result and the control envelope's `capability_rows` publish what they did; verifier `0.21` and capability diff `0.4` do not move for it; the host-config and cold-start benchmark replays reproduce their run-of-record scores. The digest's credential-assignment rule gained a lookahead that removes its quadratic time on a long run of name characters and matches exactly what it matched, so every `config_sha256` is unchanged. Re-running `diff --json` on the 80 vendored benchmark cases with the prepared `1.1.0` commit and this tree on 2026-09-23 gave byte-identical rows on all 80; 23 entries on 22 cases changed, and each of the 7 changed hook or MCP entries (six repositories; one is vendored in both benchmarks) that read `PreToolUse → PreToolUse` or `no difference in the command name …` now names the field that changed, such as `mcp-outline: package mcp-outline==1.10.0 → mcp-outline==1.10.1` or `PreToolUse: handler 2 timeout 30 → 120`.
- **Compatibility:** a `0.6` baseline stays comparable with no new row or reason, and `audit --host --save-baseline` may now replace it; an older one is still refused, as before. Validators pinned to the `0.6` schemas reject a `0.7` inventory, baseline or drift payload; the `0.6` files stay published. See the [migration note](STABILITY.md#hook-mcp-detail-fields-819).

- A plugin directory that cannot be compared no longer hides the host changes outside it. (#808)
- **The problem.** A pull request that broke `plugins/demo/.claude-plugin/plugin.json` and also dropped a `deny` rule from `.claude/settings.json` printed `Cannot compare against main: head_inventory_incomplete` and no row on `diff`, `verify` and the manifest-free PR comment, where the published `1.0.0` showed the removed denial. The plugin-reference limit #714 introduced refused the whole comparison, including files that plugin cannot reach.
Expand Down
Loading
Loading