From 3092d9739287786c01ac6370fa13ac45a3a27d43 Mon Sep 17 00:00:00 2001 From: pengfei-threemoonslab Date: Fri, 15 May 2026 22:29:25 -0700 Subject: [PATCH] Tighten public contract surfaces --- .github/ISSUE_TEMPLATE/bug_report.yml | 2 +- .well-known/agents-shipgate.json | 13 +- README.md | 17 +- docs/INDEX.md | 1 + docs/agent-contract-current.md | 3 +- docs/ai-search-summary.md | 17 +- docs/distribution.md | 2 +- docs/faq.md | 2 +- docs/integrations.md | 8 +- docs/public-contract.json | 29 ++ docs/quickstart.md | 2 +- docs/target-repo-agent-snippets.md | 2 +- docs/upstream-integrations.md | 2 +- docs/zero-install.md | 4 +- examples/circleci/01-advisory.yml | 2 +- examples/circleci/02-strict-with-baseline.yml | 2 +- .../circleci/03-sarif-artifact-retention.yml | 2 +- .../circleci/04-multi-config-workspace.yml | 2 +- .../circleci/05-on-tool-source-changes.yml | 2 +- .../github-actions/01-advisory-pr-comment.yml | 4 +- .../github-actions/02-strict-on-critical.yml | 4 +- .../03-strict-with-baseline.yml | 4 +- .../04-multi-config-workspace.yml | 2 +- .../05-sarif-to-code-scanning.yml | 4 +- .../06-on-tool-source-changes.yml | 4 +- examples/github-actions/README.md | 6 +- examples/gitlab-ci/01-advisory.yml | 2 +- .../gitlab-ci/02-strict-with-baseline.yml | 2 +- examples/gitlab-ci/03-sarif-or-artifact.yml | 2 +- .../gitlab-ci/04-multi-config-workspace.yml | 2 +- .../gitlab-ci/05-on-tool-source-changes.yml | 2 +- .../golden-prs/golden-pr-from-coding-agent.md | 2 +- examples/pre-commit/README.md | 4 +- llms-full.txt | 3 +- llms.txt | 16 +- prompts/decide-shipgate-relevance.md | 2 +- prompts/stabilize-strict-mode.md | 2 +- pyproject.toml | 5 +- .../ci-recipes/advisory-pr-comment.yml | 4 +- .../prompts/decide-shipgate-relevance.md | 2 +- .../prompts/stabilize-strict-mode.md | 2 +- tests/test_public_surface_contract.py | 347 ++++++++++++++---- 42 files changed, 414 insertions(+), 127 deletions(-) create mode 100644 docs/public-contract.json diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml index 0f77584..e4ab58f 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yml +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -11,7 +11,7 @@ body: id: version attributes: label: Agents Shipgate version - placeholder: "v0.10.0" + placeholder: "v0.8.0" validations: required: true - type: dropdown diff --git a/.well-known/agents-shipgate.json b/.well-known/agents-shipgate.json index d4bbc66..03cb4e0 100644 --- a/.well-known/agents-shipgate.json +++ b/.well-known/agents-shipgate.json @@ -4,6 +4,13 @@ "display_name": "Agents Shipgate", "tagline": "Static release-readiness gate for AI agent tool surfaces", "version": "0.10.0", + "version_context": "current main / in-tree CLI contract; may be ahead of the published release", + "published_release": { + "version": "0.8.0", + "tag": "v0.8.0", + "github_release_url": "https://github.com/ThreeMoonsLab/agents-shipgate/releases/tag/v0.8.0", + "pypi_url": "https://pypi.org/project/agents-shipgate/0.8.0/" + }, "license": "Apache-2.0", "publisher": { "name": "Three Moons Lab", @@ -11,7 +18,7 @@ }, "package": { "pypi": "agents-shipgate", - "github_action": "ThreeMoonsLab/agents-shipgate@v0.10.0", + "github_action": "ThreeMoonsLab/agents-shipgate@v0.8.0", "github_repo": "ThreeMoonsLab/agents-shipgate" }, "install": { @@ -25,10 +32,11 @@ "self_check": "agents-shipgate self-check --json", "contract": "agents-shipgate contract --json", "contract_version": "1", - "inputs": ["mcp", "openapi", "openai_agents_sdk", "anthropic_messages_api", "google_adk", "langchain", "crewai", "openai_api", "codex_plugin"], + "inputs": ["mcp", "openapi", "openai_agents_sdk", "anthropic_messages_api", "google_adk", "langchain", "crewai", "n8n", "openai_api", "codex_plugin"], "outputs": ["markdown", "json", "sarif", "packet_md", "packet_json", "packet_html"], "gating_signal": "release_decision.decision", "trust_model": "static_by_default", + "schemas_context": "current main / in-tree schemas; may be ahead of the published release", "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.16.json", @@ -37,6 +45,7 @@ }, "agent_instructions": "https://raw.githubusercontent.com/ThreeMoonsLab/agents-shipgate/main/AGENTS.md", "agent_contract": "https://raw.githubusercontent.com/ThreeMoonsLab/agents-shipgate/main/docs/agent-contract-current.md", + "public_contract": "https://raw.githubusercontent.com/ThreeMoonsLab/agents-shipgate/main/docs/public-contract.json", "stability_contract": "https://raw.githubusercontent.com/ThreeMoonsLab/agents-shipgate/main/STABILITY.md", "triggers_url": "https://raw.githubusercontent.com/ThreeMoonsLab/agents-shipgate/main/docs/triggers.json", "errors_url": "https://raw.githubusercontent.com/ThreeMoonsLab/agents-shipgate/main/docs/errors.json", diff --git a/README.md b/README.md index ebbd277..da489cc 100644 --- a/README.md +++ b/README.md @@ -19,9 +19,9 @@ Agents Shipgate is an open-source CLI and GitHub Action that scans MCP, OpenAPI, OpenAI Agents SDK, Anthropic Messages API, Google ADK, -LangChain/LangGraph, CrewAI, n8n, and OpenAI API artifacts, then writes a -deterministic **Tool-Use Readiness Report** before your agent gets -production-like permissions. +LangChain/LangGraph, CrewAI, n8n, OpenAI API artifacts, and Codex plugin +metadata, then writes a deterministic **Tool-Use Readiness Report** before your +agent gets production-like permissions. **Website:** [threemoonslab.com](https://threemoonslab.com/) — [quickstart](https://threemoonslab.com/quickstart/), @@ -163,7 +163,7 @@ minimal manifests, see [`docs/minimal-real-configs.md`](docs/minimal-real-config ## Use in CI ```yaml -- uses: ThreeMoonsLab/agents-shipgate@v0.10.0 +- uses: ThreeMoonsLab/agents-shipgate@v0.8.0 with: config: shipgate.yaml ci_mode: advisory @@ -190,7 +190,7 @@ Set `pr_comment: "true"` to post a compact PR summary: ## What it produces -- **Tool-Use Readiness Report** — `agents-shipgate-reports/report.{md,json,sarif}`. Markdown for human release review, JSON for tools and coding agents (current schema [v0.16](docs/report-schema.v0.16.json); gating signal is `release_decision.decision`; v0.16 adds first-class Action Surface Diff fields on top of v0.15's per-finding `provenance_kind`), SARIF for GitHub code-scanning workflows. +- **Tool-Use Readiness Report** — `agents-shipgate-reports/report.{md,json,sarif}`. Markdown for human release review, JSON for tools and coding agents (current main schema [v0.16](docs/report-schema.v0.16.json); gating signal is `release_decision.decision`; v0.16 adds first-class Action Surface Diff fields on top of v0.15's per-finding `provenance_kind`), SARIF for GitHub code-scanning workflows. - **Release Evidence Packet** — `agents-shipgate-reports/packet.{md,json,html}` (and `packet.pdf` with the `[pdf]` extras). Reviewer-shaped synthesis with fixed sections, including tool-surface and action-surface diffs when available. Governed by [packet schema v0.5](docs/packet-schema.v0.5.json) — see [STABILITY.md §Release Evidence Packet](STABILITY.md#release-evidence-packet-v05). ## Exit codes @@ -213,6 +213,7 @@ Agents Shipgate is designed to be agent-friendly. If you're a coding agent (Clau - **[`llms.txt`](llms.txt)** — short index of every machine-readable surface, one fetch. - **[`llms-full.txt`](llms-full.txt)** — long-form concatenation of `AGENTS.md` + recipes + checks + concepts + autofix policy, in one document. Built by `scripts/build-llms-full.py`. - **[`.well-known/agents-shipgate.json`](.well-known/agents-shipgate.json)** — discovery metadata (tagline, install commands, schema URLs, gating signal, exit codes, trigger-catalog URL). +- **[`docs/public-contract.json`](docs/public-contract.json)** — committed public constants: latest published release, canonical tagline, install commands, GitHub Action pin, trigger URL, and current-main schema URLs. - **[`docs/triggers.json`](docs/triggers.json)** — machine-readable mirror of the AGENTS.md trigger table. Apply the rules to a PR diff to decide whether to propose `agents-shipgate detect`. Schema is stable for `0.x`. - **[`tools/shipgate-detect.py`](tools/shipgate-detect.py)** — zero-install, stdlib-only detector. `curl … | python3 - --workspace . --json` returns the same structural verdict as `agents-shipgate detect --json`. Pinned to the canonical CLI by [`tests/test_zero_install_detector.py`](tests/test_zero_install_detector.py). See [`docs/zero-install.md`](docs/zero-install.md). - **`agents-shipgate contract --json`** — verify the installed CLI's local contract before relying on hard-coded schema or gating assumptions. @@ -226,7 +227,7 @@ Agents Shipgate is designed to be agent-friendly. If you're a coding agent (Clau - **[`prompts/`](prompts/)** — reusable prompts for common workflows - **[`skills/agents-shipgate/`](skills/agents-shipgate/)** + **[`.claude/commands/shipgate.md`](.claude/commands/shipgate.md)** — self-contained Claude Code skill (bundled prompts and CI recipe) and `/shipgate` slash command. See [`docs/agents/use-with-claude-code.md`](docs/agents/use-with-claude-code.md) to install in your own project. - **[`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.16.json`](docs/report-schema.v0.16.json)** — JSON Schemas for live editor validation (current; emitted reports carry `report_schema_version: "0.16"`). v0.16 adds `action_surface_facts` and `action_surface_diff`; v0.15 added the per-finding `provenance_kind` enum. Read `release_decision.decision` for release gating in new consumers; read `agent_summary.first_recommended_action` for a deterministic next step. +- **[`docs/manifest-v0.1.json`](docs/manifest-v0.1.json)** + **[`docs/report-schema.v0.16.json`](docs/report-schema.v0.16.json)** — JSON Schemas for current main / in-tree live editor validation (emitted reports carry `report_schema_version: "0.16"`). v0.16 adds `action_surface_facts` and `action_surface_diff`; v0.15 added the per-finding `provenance_kind` enum. Read `release_decision.decision` for release gating in new consumers; read `agent_summary.first_recommended_action` for a deterministic next step. - **[`docs/checks.json`](docs/checks.json)** — machine-readable check catalog Every command has a `--json` form. Errors emit a structured `next_action` line on stderr when `AGENTS_SHIPGATE_AGENT_MODE=1`. @@ -446,12 +447,12 @@ jobs: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd with: fetch-depth: 0 - - uses: ThreeMoonsLab/agents-shipgate@v0.10.0 + - uses: ThreeMoonsLab/agents-shipgate@v0.8.0 with: ci_mode: advisory diff_base: target pr_comment: 'true' - shipgate_version: '0.10.0' + shipgate_version: '0.8.0' ``` Switch to `ci_mode: strict` only after your team has reviewed the advisory output. See [`examples/github-actions/`](examples/github-actions/) for strict / baseline / SARIF / multi-config / changed-paths recipes. diff --git a/docs/INDEX.md b/docs/INDEX.md index 3906eb3..fbca956 100644 --- a/docs/INDEX.md +++ b/docs/INDEX.md @@ -20,6 +20,7 @@ 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) +- [`public-contract.json`](public-contract.json) — committed public-surface constants for published release, tagline, install commands, trigger URL, and current-main schema URLs - [`manifest-v0.1.json`](manifest-v0.1.json) — JSON Schema for `shipgate.yaml` - [`report-schema.v0.16.json`](report-schema.v0.16.json) — JSON Schema for `report.json` (current; emitted reports carry `report_schema_version: "0.16"`, which adds first-class Action Surface Diff fields) - [`agent-action-guide.md`](agent-action-guide.md) — per-category recipe for what to do with a finding (canonical fix per check category, last-resort suppression rules) diff --git a/docs/agent-contract-current.md b/docs/agent-contract-current.md index 14605ed..d3682a5 100644 --- a/docs/agent-contract-current.md +++ b/docs/agent-contract-current.md @@ -10,7 +10,8 @@ Verify the installed CLI contract locally before relying on hard-coded docs: agents-shipgate contract --json ``` -- Latest release: `v0.10.0` (see [pyproject.toml](../pyproject.toml) for the in-tree version) +- Latest published release: `v0.8.0` +- In-tree CLI version on current main: `v0.10.0` (see [pyproject.toml](../pyproject.toml)) - Runtime contract: `1` - Current report schema: `0.16` — [`docs/report-schema.v0.16.json`](report-schema.v0.16.json) - Current packet schema: `0.5` — [`docs/packet-schema.v0.5.json`](packet-schema.v0.5.json) diff --git a/docs/ai-search-summary.md b/docs/ai-search-summary.md index 815af8d..632c2e7 100644 --- a/docs/ai-search-summary.md +++ b/docs/ai-search-summary.md @@ -11,6 +11,10 @@ It is a static release-readiness gate for AI agent tool surfaces. It reads a `shipgate.yaml` manifest plus declared local tool sources, then writes deterministic Tool-Use Readiness Reports as Markdown, JSON, and SARIF. +The latest published release is `v0.8.0`. The current main branch may document +newer in-tree CLI and schema contracts before they are published; verify a local +install with `agents-shipgate contract --json`. + Use Agents Shipgate before an AI agent receives staging, production-like, or production permissions to tools that can refund, email, cancel, deploy, modify records, read sensitive data, or change infrastructure. @@ -42,8 +46,10 @@ Agents Shipgate supports these static tool-source inputs: - Google ADK Python and YAML config. - LangChain and LangGraph Python entrypoints, using static AST extraction. - CrewAI Python entrypoints, using static AST extraction. +- n8n workflow JSON and source-control stubs. - OpenAI API artifacts, including prompts, function schemas, response formats, tests, and traces. +- Codex plugin package and marketplace metadata. ## What it is not @@ -81,8 +87,17 @@ shipgate, and Agents-Shipgate. - Repository: - Package: - GitHub Action: +- Public contract constants: [`public-contract.json`](public-contract.json) - Agent instructions: [`../AGENTS.md`](../AGENTS.md) - Machine-readable summary: [`../llms.txt`](../llms.txt) +- Long-form agent reference: [`../llms-full.txt`](../llms-full.txt) - Discovery metadata: [`../.well-known/agents-shipgate.json`](../.well-known/agents-shipgate.json) -- Report schema (current): [`report-schema.v0.14.json`](report-schema.v0.14.json) (v0.13 frozen at [`report-schema.v0.13.json`](report-schema.v0.13.json)) +- Trigger catalog: [`triggers.json`](triggers.json) +- Current main report schema: [`report-schema.v0.16.json`](report-schema.v0.16.json) +- Current main packet schema: [`packet-schema.v0.5.json`](packet-schema.v0.5.json) - Check catalog: [`checks.json`](checks.json) + +For release gating, coding agents should read +`agents-shipgate-reports/report.json` and use +`release_decision.decision` as the gate signal. Do not scrape Markdown reports +or gate on legacy `summary.status`. diff --git a/docs/distribution.md b/docs/distribution.md index 1fdd686..b767acf 100644 --- a/docs/distribution.md +++ b/docs/distribution.md @@ -5,7 +5,7 @@ These items require release infrastructure, registry credentials, domains, or Gi ## Package Channels - `agents-shipgate` is published on PyPI. -- Pinned GitHub Action release tags are published, including `v0.10.0`. +- Pinned GitHub Action release tags are published, including `v0.8.0`. - GitHub Releases attach the wheel, sdist, SBOM, and Sigstore bundles. - Evaluate a container image later only if it has an exercised build-and-test path. - Evaluate Homebrew once CLI usage warrants it. diff --git a/docs/faq.md b/docs/faq.md index 1cbcc76..e4b7b80 100644 --- a/docs/faq.md +++ b/docs/faq.md @@ -121,7 +121,7 @@ Skip emission with `--no-packet`; re-render later with ## Is it production-ready? -v0.10.0 is the latest released version. The manifest schema is stable +v0.8.0 is the latest released version. The manifest schema is stable across the 0.x series; see [`STABILITY.md`](../STABILITY.md). Used by early design partners. Public preview. diff --git a/docs/integrations.md b/docs/integrations.md index 3cbad75..060a0fa 100644 --- a/docs/integrations.md +++ b/docs/integrations.md @@ -21,7 +21,7 @@ jobs: steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd - id: agents-shipgate - uses: ThreeMoonsLab/agents-shipgate@v0.10.0 + uses: ThreeMoonsLab/agents-shipgate@v0.8.0 with: config: shipgate.yaml ci_mode: advisory @@ -113,7 +113,7 @@ agents-shipgate: stage: test image: python:3.12 script: - - python -m pip install "agents-shipgate==0.10.0" + - python -m pip install "agents-shipgate==0.8.0" - agents-shipgate scan --config shipgate.yaml --ci-mode advisory --format markdown,json,sarif artifacts: when: always @@ -145,7 +145,7 @@ jobs: - image: cimg/python:3.12 steps: - checkout - - run: python -m pip install "agents-shipgate==0.10.0" + - run: python -m pip install "agents-shipgate==0.8.0" - run: agents-shipgate scan --config shipgate.yaml --ci-mode advisory --format markdown,json,sarif - store_artifacts: path: agents-shipgate-reports @@ -174,7 +174,7 @@ Run Agents Shipgate locally on every commit that touches a tool-surface artifact # .pre-commit-config.yaml repos: - repo: https://github.com/ThreeMoonsLab/agents-shipgate - rev: v0.10.0 + rev: v0.8.0 hooks: - id: agents-shipgate ``` diff --git a/docs/public-contract.json b/docs/public-contract.json new file mode 100644 index 0000000..ae3d856 --- /dev/null +++ b/docs/public-contract.json @@ -0,0 +1,29 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "schema_version": "0.1", + "name": "agents-shipgate-public-contract", + "description": "Committed public-surface constants used to keep agent-facing discovery metadata, docs, and examples aligned without network calls in CI.", + "published_release": { + "version": "0.8.0", + "tag": "v0.8.0", + "github_release_url": "https://github.com/ThreeMoonsLab/agents-shipgate/releases/tag/v0.8.0", + "pypi_url": "https://pypi.org/project/agents-shipgate/0.8.0/" + }, + "canonical_tagline": "Static release-readiness gate for AI agent tool surfaces", + "install": { + "pipx": "pipx install agents-shipgate", + "pip": "python -m pip install agents-shipgate", + "uv": "uv tool install agents-shipgate", + "uvx_fixture": "uvx agents-shipgate fixture run support_refund_agent" + }, + "github_action": "ThreeMoonsLab/agents-shipgate@v0.8.0", + "trigger_catalog_url": "https://raw.githubusercontent.com/ThreeMoonsLab/agents-shipgate/main/docs/triggers.json", + "gating_signal": "release_decision.decision", + "main_branch_contract": { + "context": "current main / in-tree CLI contract; may be ahead of the published release", + "manifest_schema_url": "https://raw.githubusercontent.com/ThreeMoonsLab/agents-shipgate/main/docs/manifest-v0.1.json", + "report_schema_url": "https://raw.githubusercontent.com/ThreeMoonsLab/agents-shipgate/main/docs/report-schema.v0.16.json", + "packet_schema_url": "https://raw.githubusercontent.com/ThreeMoonsLab/agents-shipgate/main/docs/packet-schema.v0.5.json", + "checks_catalog_url": "https://raw.githubusercontent.com/ThreeMoonsLab/agents-shipgate/main/docs/checks.json" + } +} diff --git a/docs/quickstart.md b/docs/quickstart.md index 137616e..f6e9131 100644 --- a/docs/quickstart.md +++ b/docs/quickstart.md @@ -167,7 +167,7 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - - uses: ThreeMoonsLab/agents-shipgate@v0.10.0 + - uses: ThreeMoonsLab/agents-shipgate@v0.8.0 with: config: shipgate.yaml ci_mode: advisory diff --git a/docs/target-repo-agent-snippets.md b/docs/target-repo-agent-snippets.md index f698476..c96dd6a 100644 --- a/docs/target-repo-agent-snippets.md +++ b/docs/target-repo-agent-snippets.md @@ -204,7 +204,7 @@ jobs: - uses: actions/checkout@v4 with: fetch-depth: 0 - - uses: ThreeMoonsLab/agents-shipgate@v0.10.0 + - uses: ThreeMoonsLab/agents-shipgate@v0.8.0 with: config: shipgate.yaml ci_mode: advisory diff --git a/docs/upstream-integrations.md b/docs/upstream-integrations.md index df89a8e..705e0eb 100644 --- a/docs/upstream-integrations.md +++ b/docs/upstream-integrations.md @@ -311,7 +311,7 @@ jobs: - uses: actions/checkout@v4 with: fetch-depth: 0 - - uses: ThreeMoonsLab/agents-shipgate@v0.10.0 + - uses: ThreeMoonsLab/agents-shipgate@v0.8.0 with: ci_mode: advisory diff_base: target diff --git a/docs/zero-install.md b/docs/zero-install.md index 3aa7f91..4b5da93 100644 --- a/docs/zero-install.md +++ b/docs/zero-install.md @@ -76,12 +76,12 @@ jobs: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd with: fetch-depth: 0 - - uses: ThreeMoonsLab/agents-shipgate@v0.10.0 + - uses: ThreeMoonsLab/agents-shipgate@v0.8.0 with: ci_mode: advisory diff_base: target pr_comment: 'true' - shipgate_version: '0.10.0' + shipgate_version: '0.8.0' ``` The full template lives at [`examples/github-actions/01-advisory-pr-comment.yml`](https://github.com/ThreeMoonsLab/agents-shipgate/blob/main/examples/github-actions/01-advisory-pr-comment.yml). diff --git a/examples/circleci/01-advisory.yml b/examples/circleci/01-advisory.yml index 54dabeb..8b340bc 100644 --- a/examples/circleci/01-advisory.yml +++ b/examples/circleci/01-advisory.yml @@ -6,7 +6,7 @@ jobs: - image: cimg/python:3.12 steps: - checkout - - run: python -m pip install "agents-shipgate==0.10.0" + - run: python -m pip install "agents-shipgate==0.8.0" - run: agents-shipgate scan --config shipgate.yaml --ci-mode advisory --format markdown,json,sarif - store_artifacts: path: agents-shipgate-reports diff --git a/examples/circleci/02-strict-with-baseline.yml b/examples/circleci/02-strict-with-baseline.yml index 3cfae64..7f50418 100644 --- a/examples/circleci/02-strict-with-baseline.yml +++ b/examples/circleci/02-strict-with-baseline.yml @@ -6,7 +6,7 @@ jobs: - image: cimg/python:3.12 steps: - checkout - - run: python -m pip install "agents-shipgate==0.10.0" + - run: python -m pip install "agents-shipgate==0.8.0" - run: name: Agents Shipgate strict scan command: > diff --git a/examples/circleci/03-sarif-artifact-retention.yml b/examples/circleci/03-sarif-artifact-retention.yml index fb13b1c..2b80364 100644 --- a/examples/circleci/03-sarif-artifact-retention.yml +++ b/examples/circleci/03-sarif-artifact-retention.yml @@ -6,7 +6,7 @@ jobs: - image: cimg/python:3.12 steps: - checkout - - run: python -m pip install "agents-shipgate==0.10.0" + - run: python -m pip install "agents-shipgate==0.8.0" - run: agents-shipgate scan --config shipgate.yaml --ci-mode advisory --format markdown,json,sarif - store_artifacts: path: agents-shipgate-reports/report.sarif diff --git a/examples/circleci/04-multi-config-workspace.yml b/examples/circleci/04-multi-config-workspace.yml index a3b55f2..07744ae 100644 --- a/examples/circleci/04-multi-config-workspace.yml +++ b/examples/circleci/04-multi-config-workspace.yml @@ -6,7 +6,7 @@ jobs: - image: cimg/python:3.12 steps: - checkout - - run: python -m pip install "agents-shipgate==0.10.0" + - run: python -m pip install "agents-shipgate==0.8.0" - run: name: Agents Shipgate workspace scan command: > diff --git a/examples/circleci/05-on-tool-source-changes.yml b/examples/circleci/05-on-tool-source-changes.yml index fc96a91..d147a9a 100644 --- a/examples/circleci/05-on-tool-source-changes.yml +++ b/examples/circleci/05-on-tool-source-changes.yml @@ -6,7 +6,7 @@ jobs: - image: cimg/python:3.12 steps: - checkout - - run: python -m pip install "agents-shipgate==0.10.0" + - run: python -m pip install "agents-shipgate==0.8.0" - run: name: Run only when tool sources changed command: | diff --git a/examples/github-actions/01-advisory-pr-comment.yml b/examples/github-actions/01-advisory-pr-comment.yml index a267c5a..f0ec462 100644 --- a/examples/github-actions/01-advisory-pr-comment.yml +++ b/examples/github-actions/01-advisory-pr-comment.yml @@ -18,9 +18,9 @@ jobs: - uses: actions/checkout@v4 with: fetch-depth: 0 - - uses: ThreeMoonsLab/agents-shipgate@v0.10.0 + - uses: ThreeMoonsLab/agents-shipgate@v0.8.0 with: ci_mode: advisory diff_base: target pr_comment: 'true' - shipgate_version: '0.10.0' + shipgate_version: '0.8.0' diff --git a/examples/github-actions/02-strict-on-critical.yml b/examples/github-actions/02-strict-on-critical.yml index 98444d6..b675a4e 100644 --- a/examples/github-actions/02-strict-on-critical.yml +++ b/examples/github-actions/02-strict-on-critical.yml @@ -16,9 +16,9 @@ jobs: timeout-minutes: 10 steps: - uses: actions/checkout@v4 - - uses: ThreeMoonsLab/agents-shipgate@v0.10.0 + - uses: ThreeMoonsLab/agents-shipgate@v0.8.0 with: ci_mode: strict fail_on: critical pr_comment: 'true' - shipgate_version: '0.10.0' + shipgate_version: '0.8.0' diff --git a/examples/github-actions/03-strict-with-baseline.yml b/examples/github-actions/03-strict-with-baseline.yml index 5dfb6cd..e07ef27 100644 --- a/examples/github-actions/03-strict-with-baseline.yml +++ b/examples/github-actions/03-strict-with-baseline.yml @@ -17,10 +17,10 @@ jobs: timeout-minutes: 10 steps: - uses: actions/checkout@v4 - - uses: ThreeMoonsLab/agents-shipgate@v0.10.0 + - uses: ThreeMoonsLab/agents-shipgate@v0.8.0 with: ci_mode: strict fail_on: critical,high baseline: .agents-shipgate/baseline.json pr_comment: 'true' - shipgate_version: '0.10.0' + shipgate_version: '0.8.0' diff --git a/examples/github-actions/04-multi-config-workspace.yml b/examples/github-actions/04-multi-config-workspace.yml index be14c07..5c7befa 100644 --- a/examples/github-actions/04-multi-config-workspace.yml +++ b/examples/github-actions/04-multi-config-workspace.yml @@ -21,7 +21,7 @@ jobs: with: python-version: '3.12' cache: pip - - run: pip install --quiet agents-shipgate==0.10.0 + - run: pip install --quiet agents-shipgate==0.8.0 - run: | agents-shipgate scan \ --workspace . \ diff --git a/examples/github-actions/05-sarif-to-code-scanning.yml b/examples/github-actions/05-sarif-to-code-scanning.yml index 42870a3..e4636ea 100644 --- a/examples/github-actions/05-sarif-to-code-scanning.yml +++ b/examples/github-actions/05-sarif-to-code-scanning.yml @@ -20,12 +20,12 @@ jobs: steps: - uses: actions/checkout@v4 - id: shipgate - uses: ThreeMoonsLab/agents-shipgate@v0.10.0 + uses: ThreeMoonsLab/agents-shipgate@v0.8.0 with: ci_mode: advisory formats: markdown,json,sarif pr_comment: 'true' - shipgate_version: '0.10.0' + shipgate_version: '0.8.0' - if: always() uses: github/codeql-action/upload-sarif@v3 with: diff --git a/examples/github-actions/06-on-tool-source-changes.yml b/examples/github-actions/06-on-tool-source-changes.yml index 6ab2918..f5e958b 100644 --- a/examples/github-actions/06-on-tool-source-changes.yml +++ b/examples/github-actions/06-on-tool-source-changes.yml @@ -30,8 +30,8 @@ jobs: timeout-minutes: 10 steps: - uses: actions/checkout@v4 - - uses: ThreeMoonsLab/agents-shipgate@v0.10.0 + - uses: ThreeMoonsLab/agents-shipgate@v0.8.0 with: ci_mode: advisory pr_comment: 'true' - shipgate_version: '0.10.0' + shipgate_version: '0.8.0' diff --git a/examples/github-actions/README.md b/examples/github-actions/README.md index 74914b4..71c2331 100644 --- a/examples/github-actions/README.md +++ b/examples/github-actions/README.md @@ -29,9 +29,9 @@ Configure per-job, never repo-wide. For reproducible CI, pin both the action and the underlying CLI: ```yaml -- uses: ThreeMoonsLab/agents-shipgate@v0.10.0 +- uses: ThreeMoonsLab/agents-shipgate@v0.8.0 with: - shipgate_version: "0.10.0" + shipgate_version: "0.8.0" ``` When `shipgate_version` is empty the action installs the CLI from the action source — convenient on `@main`, less reproducible. @@ -49,7 +49,7 @@ When `shipgate_version` is empty the action installs the CLI from the action sou ```yaml - id: shipgate - uses: ThreeMoonsLab/agents-shipgate@v0.10.0 + uses: ThreeMoonsLab/agents-shipgate@v0.8.0 - if: steps.shipgate.outputs.decision == 'blocked' run: echo "Release blocked by Agents Shipgate" diff --git a/examples/gitlab-ci/01-advisory.yml b/examples/gitlab-ci/01-advisory.yml index d671f6a..6d6622d 100644 --- a/examples/gitlab-ci/01-advisory.yml +++ b/examples/gitlab-ci/01-advisory.yml @@ -5,7 +5,7 @@ agents_shipgate: stage: test image: python:3.12 script: - - python -m pip install "agents-shipgate==0.10.0" + - python -m pip install "agents-shipgate==0.8.0" - agents-shipgate scan --config shipgate.yaml --ci-mode advisory --format markdown,json,sarif artifacts: when: always diff --git a/examples/gitlab-ci/02-strict-with-baseline.yml b/examples/gitlab-ci/02-strict-with-baseline.yml index 5a8feb7..d94b2a6 100644 --- a/examples/gitlab-ci/02-strict-with-baseline.yml +++ b/examples/gitlab-ci/02-strict-with-baseline.yml @@ -5,7 +5,7 @@ agents_shipgate: stage: test image: python:3.12 script: - - python -m pip install "agents-shipgate==0.10.0" + - python -m pip install "agents-shipgate==0.8.0" - > agents-shipgate scan --config shipgate.yaml diff --git a/examples/gitlab-ci/03-sarif-or-artifact.yml b/examples/gitlab-ci/03-sarif-or-artifact.yml index 4ee64fa..7711f4f 100644 --- a/examples/gitlab-ci/03-sarif-or-artifact.yml +++ b/examples/gitlab-ci/03-sarif-or-artifact.yml @@ -5,7 +5,7 @@ agents_shipgate: stage: test image: python:3.12 script: - - python -m pip install "agents-shipgate==0.10.0" + - python -m pip install "agents-shipgate==0.8.0" - agents-shipgate scan --config shipgate.yaml --ci-mode advisory --format markdown,json,sarif artifacts: when: always diff --git a/examples/gitlab-ci/04-multi-config-workspace.yml b/examples/gitlab-ci/04-multi-config-workspace.yml index 4ca6889..4bf9219 100644 --- a/examples/gitlab-ci/04-multi-config-workspace.yml +++ b/examples/gitlab-ci/04-multi-config-workspace.yml @@ -5,7 +5,7 @@ agents_shipgate: stage: test image: python:3.12 script: - - python -m pip install "agents-shipgate==0.10.0" + - python -m pip install "agents-shipgate==0.8.0" - > agents-shipgate scan --workspace . diff --git a/examples/gitlab-ci/05-on-tool-source-changes.yml b/examples/gitlab-ci/05-on-tool-source-changes.yml index e95d5f7..cc8d229 100644 --- a/examples/gitlab-ci/05-on-tool-source-changes.yml +++ b/examples/gitlab-ci/05-on-tool-source-changes.yml @@ -19,7 +19,7 @@ agents_shipgate: - "**/SKILL.md" - "**/*.py" script: - - python -m pip install "agents-shipgate==0.10.0" + - python -m pip install "agents-shipgate==0.8.0" - agents-shipgate scan --config shipgate.yaml --ci-mode advisory --format markdown,json,sarif artifacts: when: always diff --git a/examples/golden-prs/golden-pr-from-coding-agent.md b/examples/golden-prs/golden-pr-from-coding-agent.md index e7ebb03..aba8dd0 100644 --- a/examples/golden-prs/golden-pr-from-coding-agent.md +++ b/examples/golden-prs/golden-pr-from-coding-agent.md @@ -62,7 +62,7 @@ is at `agents-shipgate-reports/report.json`; the top-level - Release Evidence Packet: `agents-shipgate-reports/packet.{md,json,html}` **CI**: `.github/workflows/agents-shipgate.yml` already wires -`agents-shipgate@v0.10.0` in advisory mode; this PR will get a +`agents-shipgate@v0.8.0` in advisory mode; this PR will get a sticky-marker comment from the Action on every push.
diff --git a/examples/pre-commit/README.md b/examples/pre-commit/README.md index 4b0f261..54f69d8 100644 --- a/examples/pre-commit/README.md +++ b/examples/pre-commit/README.md @@ -13,7 +13,7 @@ In your repo's `.pre-commit-config.yaml`: ```yaml repos: - repo: https://github.com/ThreeMoonsLab/agents-shipgate - rev: v0.10.0 + rev: v0.8.0 hooks: - id: agents-shipgate ``` @@ -92,7 +92,7 @@ Use the `agents-shipgate-strict` hook ID for the strict variant, or override the ```yaml repos: - repo: https://github.com/ThreeMoonsLab/agents-shipgate - rev: v0.10.0 + rev: v0.8.0 hooks: - id: agents-shipgate entry: agents-shipgate scan -c shipgate.yaml --ci-mode strict --fail-on critical diff --git a/llms-full.txt b/llms-full.txt index 4180ec6..900691d 100644 --- a/llms-full.txt +++ b/llms-full.txt @@ -813,7 +813,8 @@ Verify the installed CLI contract locally before relying on hard-coded docs: agents-shipgate contract --json ``` -- Latest release: `v0.10.0` (see [pyproject.toml](../pyproject.toml) for the in-tree version) +- Latest published release: `v0.8.0` +- In-tree CLI version on current main: `v0.10.0` (see [pyproject.toml](../pyproject.toml)) - Runtime contract: `1` - Current report schema: `0.16` — [`docs/report-schema.v0.16.json`](report-schema.v0.16.json) - Current packet schema: `0.5` — [`docs/packet-schema.v0.5.json`](packet-schema.v0.5.json) diff --git a/llms.txt b/llms.txt index 8bfe79a..421bd8f 100644 --- a/llms.txt +++ b/llms.txt @@ -12,7 +12,8 @@ - Publisher: Three Moons Lab - Publisher URL: https://threemoonslab.com/ - License: Apache-2.0 -- Latest public release: v0.10.0 +- Latest public release: v0.8.0 +- In-tree CLI version on current main: v0.10.0 - Canonical repository: https://github.com/ThreeMoonsLab/agents-shipgate - Do not use: Agent Shipcheck, Agent Shipgate, agents shipgate, Agents-Shipgate @@ -49,15 +50,17 @@ - Google ADK Python and YAML config. - LangChain and LangGraph Python entrypoints, using static AST extraction. - CrewAI Python entrypoints, using static AST extraction. +- n8n workflow JSON and source-control stubs. - OpenAI API artifacts: prompts, function schemas, response formats, tests, and traces. +- Codex plugin package and marketplace metadata. ## Outputs - 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.16.json +- JSON report schema (current main / in-tree): https://raw.githubusercontent.com/ThreeMoonsLab/agents-shipgate/main/docs/report-schema.v0.16.json - 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.5.json +- Packet schema (current main / in-tree): https://raw.githubusercontent.com/ThreeMoonsLab/agents-shipgate/main/docs/packet-schema.v0.5.json - SARIF report: `agents-shipgate-reports/report.sarif`. - Check catalog: https://raw.githubusercontent.com/ThreeMoonsLab/agents-shipgate/main/docs/checks.json - Release gating signal: `release_decision.decision` ∈ `{"blocked", "review_required", "insufficient_evidence", "passed"}` in `report.json` (baseline-aware; v0.8+; `insufficient_evidence` added v0.14; consumers should treat unknown future values as `review_required`). @@ -71,7 +74,7 @@ - Run a zero-config fixture: `agents-shipgate fixture run support_refund_agent`. - Initialize a repo manifest: `agents-shipgate init --workspace . --write`. - Scan a repo: `agents-shipgate scan -c shipgate.yaml`. -- GitHub Action: `ThreeMoonsLab/agents-shipgate@v0.10.0`. +- GitHub Action: `ThreeMoonsLab/agents-shipgate@v0.8.0`. ## Source of truth @@ -88,14 +91,15 @@ - STABILITY.md: https://github.com/ThreeMoonsLab/agents-shipgate/blob/main/STABILITY.md - Discovery metadata: https://raw.githubusercontent.com/ThreeMoonsLab/agents-shipgate/main/.well-known/agents-shipgate.json - Website discovery metadata: https://threemoonslab.com/.well-known/agents-shipgate.json +- Public contract constants: https://raw.githubusercontent.com/ThreeMoonsLab/agents-shipgate/main/docs/public-contract.json - Trigger catalog (machine-readable mirror of the AGENTS.md trigger table): https://raw.githubusercontent.com/ThreeMoonsLab/agents-shipgate/main/docs/triggers.json - Error-kind catalog (machine-readable enumeration of agent-mode error kinds with exit codes and recovery hints): https://raw.githubusercontent.com/ThreeMoonsLab/agents-shipgate/main/docs/errors.json - Long-form agent reference (AGENTS.md + recipes + checks + concepts + autofix policy concatenated): https://raw.githubusercontent.com/ThreeMoonsLab/agents-shipgate/main/llms-full.txt - 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.16.json -- Packet schema (current): https://raw.githubusercontent.com/ThreeMoonsLab/agents-shipgate/main/docs/packet-schema.v0.5.json +- Report schema (current main / in-tree): https://raw.githubusercontent.com/ThreeMoonsLab/agents-shipgate/main/docs/report-schema.v0.16.json +- Packet schema (current main / in-tree): https://raw.githubusercontent.com/ThreeMoonsLab/agents-shipgate/main/docs/packet-schema.v0.5.json - Current agent contract: https://raw.githubusercontent.com/ThreeMoonsLab/agents-shipgate/main/docs/agent-contract-current.md ## Category vocabulary diff --git a/prompts/decide-shipgate-relevance.md b/prompts/decide-shipgate-relevance.md index 6c1cab7..fa51abc 100644 --- a/prompts/decide-shipgate-relevance.md +++ b/prompts/decide-shipgate-relevance.md @@ -78,7 +78,7 @@ the rules to the changed file list. - If `run_shipgate: true` and Shipgate is **not** installed: install it (`pipx install agents-shipgate`) and run `detect`. If the user prefers a zero-install first step, point them at the GitHub Action - (`ThreeMoonsLab/agents-shipgate@v0.10.0`) instead. + (`ThreeMoonsLab/agents-shipgate@v0.8.0`) instead. - If `run_shipgate: false` and `dry_run_recommended: true`: propose a non-mutating scan only — never propose `init --write` based on a dry-run match alone. Phrase it as "X may have shifted the tool diff --git a/prompts/stabilize-strict-mode.md b/prompts/stabilize-strict-mode.md index 990bba6..9852d5e 100644 --- a/prompts/stabilize-strict-mode.md +++ b/prompts/stabilize-strict-mode.md @@ -37,7 +37,7 @@ The user has Agents Shipgate running in **advisory** mode and wants to graduate 5. **Update the CI workflow.** Replace the existing advisory step with strict + baseline. Use [`examples/github-actions/03-strict-with-baseline.yml`](https://github.com/ThreeMoonsLab/agents-shipgate/blob/main/examples/github-actions/03-strict-with-baseline.yml) as the template: ```yaml - - uses: ThreeMoonsLab/agents-shipgate@v0.10.0 + - uses: ThreeMoonsLab/agents-shipgate@v0.8.0 with: ci_mode: strict fail_on: critical diff --git a/pyproject.toml b/pyproject.toml index 943b5ac..3c34c6c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -5,7 +5,7 @@ build-backend = "hatchling.build" [project] name = "agents-shipgate" version = "0.10.0" -description = "Static release-readiness gate for AI agent tool surfaces. CLI + GitHub Action. Scans MCP, OpenAPI, OpenAI Agents SDK, Anthropic, Google ADK, LangChain, CrewAI, n8n." +description = "Static release-readiness gate for AI agent tool surfaces. CLI + GitHub Action. Scans MCP, OpenAPI, OpenAI Agents SDK, Anthropic, Google ADK, LangChain, CrewAI, n8n, Codex plugins." readme = "README.md" requires-python = ">=3.12" license = "Apache-2.0" @@ -28,6 +28,8 @@ keywords = [ "github-action", "sarif", "agent-tools", + "n8n", + "codex-plugin", ] classifiers = [ "Development Status :: 4 - Beta", @@ -63,6 +65,7 @@ Changelog = "https://github.com/ThreeMoonsLab/agents-shipgate/blob/main/CHANGELO "LLMs Full" = "https://raw.githubusercontent.com/ThreeMoonsLab/agents-shipgate/main/llms-full.txt" "Trigger Catalog" = "https://raw.githubusercontent.com/ThreeMoonsLab/agents-shipgate/main/docs/triggers.json" "Well-Known" = "https://raw.githubusercontent.com/ThreeMoonsLab/agents-shipgate/main/.well-known/agents-shipgate.json" +"Public Contract" = "https://raw.githubusercontent.com/ThreeMoonsLab/agents-shipgate/main/docs/public-contract.json" [project.optional-dependencies] pdf = [ diff --git a/skills/agents-shipgate/ci-recipes/advisory-pr-comment.yml b/skills/agents-shipgate/ci-recipes/advisory-pr-comment.yml index a267c5a..f0ec462 100644 --- a/skills/agents-shipgate/ci-recipes/advisory-pr-comment.yml +++ b/skills/agents-shipgate/ci-recipes/advisory-pr-comment.yml @@ -18,9 +18,9 @@ jobs: - uses: actions/checkout@v4 with: fetch-depth: 0 - - uses: ThreeMoonsLab/agents-shipgate@v0.10.0 + - uses: ThreeMoonsLab/agents-shipgate@v0.8.0 with: ci_mode: advisory diff_base: target pr_comment: 'true' - shipgate_version: '0.10.0' + shipgate_version: '0.8.0' diff --git a/skills/agents-shipgate/prompts/decide-shipgate-relevance.md b/skills/agents-shipgate/prompts/decide-shipgate-relevance.md index 6c1cab7..fa51abc 100644 --- a/skills/agents-shipgate/prompts/decide-shipgate-relevance.md +++ b/skills/agents-shipgate/prompts/decide-shipgate-relevance.md @@ -78,7 +78,7 @@ the rules to the changed file list. - If `run_shipgate: true` and Shipgate is **not** installed: install it (`pipx install agents-shipgate`) and run `detect`. If the user prefers a zero-install first step, point them at the GitHub Action - (`ThreeMoonsLab/agents-shipgate@v0.10.0`) instead. + (`ThreeMoonsLab/agents-shipgate@v0.8.0`) instead. - If `run_shipgate: false` and `dry_run_recommended: true`: propose a non-mutating scan only — never propose `init --write` based on a dry-run match alone. Phrase it as "X may have shifted the tool diff --git a/skills/agents-shipgate/prompts/stabilize-strict-mode.md b/skills/agents-shipgate/prompts/stabilize-strict-mode.md index 990bba6..9852d5e 100644 --- a/skills/agents-shipgate/prompts/stabilize-strict-mode.md +++ b/skills/agents-shipgate/prompts/stabilize-strict-mode.md @@ -37,7 +37,7 @@ The user has Agents Shipgate running in **advisory** mode and wants to graduate 5. **Update the CI workflow.** Replace the existing advisory step with strict + baseline. Use [`examples/github-actions/03-strict-with-baseline.yml`](https://github.com/ThreeMoonsLab/agents-shipgate/blob/main/examples/github-actions/03-strict-with-baseline.yml) as the template: ```yaml - - uses: ThreeMoonsLab/agents-shipgate@v0.10.0 + - uses: ThreeMoonsLab/agents-shipgate@v0.8.0 with: ci_mode: strict fail_on: critical diff --git a/tests/test_public_surface_contract.py b/tests/test_public_surface_contract.py index 555bb08..f0c840d 100644 --- a/tests/test_public_surface_contract.py +++ b/tests/test_public_surface_contract.py @@ -76,14 +76,46 @@ SHIPGATE_VERSION_INPUT_PATTERN = re.compile( r"shipgate_version:\s*['\"](\d+\.\d+\.\d+)['\"]" ) -# Surfaces that name the *latest released* version inline (not as an -# Action / pip / shipgate_version pin) and must move with the package -# version on every bump. Each entry is a (path, regex) pair where the -# regex's first capture group is the version literal to compare against -# pyproject.toml. The regexes are anchored to surrounding phrasing so -# historical version mentions in the same file (e.g. ROADMAP.md's -# release-history list, faq.md's older v0.x narrative) are not matched. -VERSION_LITERAL_TARGETS = ( +PUBLIC_CONTRACT_PATH = "docs/public-contract.json" +TAGLINE = "Static release-readiness gate for AI agent tool surfaces" +PUBLIC_CONTRACT_URL = ( + "https://raw.githubusercontent.com/ThreeMoonsLab/agents-shipgate/main/" + "docs/public-contract.json" +) +TRIGGER_CATALOG_URL = ( + "https://raw.githubusercontent.com/ThreeMoonsLab/agents-shipgate/main/" + "docs/triggers.json" +) +MANIFEST_SCHEMA_URL = ( + "https://raw.githubusercontent.com/ThreeMoonsLab/agents-shipgate/main/" + "docs/manifest-v0.1.json" +) +REPORT_SCHEMA_URL = ( + "https://raw.githubusercontent.com/ThreeMoonsLab/agents-shipgate/main/" + f"docs/{CURRENT_REPORT_SCHEMA}" +) +PACKET_SCHEMA_URL = ( + "https://raw.githubusercontent.com/ThreeMoonsLab/agents-shipgate/main/" + f"docs/{CURRENT_PACKET_SCHEMA}" +) +INSTALL_COMMANDS = { + "pipx": "pipx install agents-shipgate", + "pip": "python -m pip install agents-shipgate", + "uv": "uv tool install agents-shipgate", +} +IN_TREE_VERSION_CONTEXT = re.compile( + r"\b(?:in-tree|current main|main branch|local CLI contract|" + r"unreleased|pyproject\.toml|__version__)\b", + re.IGNORECASE, +) + +# Surfaces that name the *latest published* version inline (not as an +# Action / pip / shipgate_version pin) and must move with +# docs/public-contract.json on every release. Each entry is a (path, regex) +# pair where the regex's first capture group is the version literal to compare +# against the committed public contract. The regexes are anchored to surrounding +# phrasing so historical version mentions in the same file are not matched. +PUBLISHED_VERSION_LITERAL_TARGETS = ( ( ".github/ISSUE_TEMPLATE/bug_report.yml", re.compile(r"placeholder:\s*\"v(\d+\.\d+\.\d+)\""), @@ -98,6 +130,12 @@ "docs/faq.md", re.compile(r"v(\d+\.\d+\.\d+) is the latest released version"), ), +) + +# Surfaces that intentionally name the in-tree version instead of the published +# release. These must move with pyproject.toml and must carry clear "current +# main / in-tree / local CLI contract" wording. +IN_TREE_VERSION_LITERAL_TARGETS = ( ( "ROADMAP.md", re.compile(r"preparing the\s+`v(\d+\.\d+\.\d+)`\s+release"), @@ -126,6 +164,7 @@ "docs/faq.md", "examples/github-actions/README.md", "docs/agent-contract-current.md", + "docs/ai-search-summary.md", ) # Strict superset of PUBLIC_SURFACES that adds files which carry @@ -138,6 +177,8 @@ *PUBLIC_SURFACES, "docs/integrations.md", "docs/quickstart.md", + "docs/zero-install.md", + "docs/upstream-integrations.md", "docs/target-repo-agent-snippets.md", "examples/github-actions/01-advisory-pr-comment.yml", "examples/github-actions/02-strict-on-critical.yml", @@ -155,19 +196,40 @@ "examples/gitlab-ci/03-sarif-or-artifact.yml", "examples/gitlab-ci/04-multi-config-workspace.yml", "examples/gitlab-ci/05-on-tool-source-changes.yml", + "examples/pre-commit/README.md", + "examples/golden-prs/golden-pr-from-coding-agent.md", "prompts/stabilize-strict-mode.md", + "prompts/decide-shipgate-relevance.md", "skills/agents-shipgate/prompts/stabilize-strict-mode.md", + "skills/agents-shipgate/prompts/decide-shipgate-relevance.md", "skills/agents-shipgate/ci-recipes/advisory-pr-comment.yml", ) def _load_pyproject_version() -> str: """Read `[project].version` from pyproject.toml — single source of - truth for the package version that every public surface must echo.""" + truth for the in-tree CLI version on current main.""" with (REPO_ROOT / "pyproject.toml").open("rb") as f: return tomllib.load(f)["project"]["version"] +def _load_pyproject() -> dict: + with (REPO_ROOT / "pyproject.toml").open("rb") as f: + return tomllib.load(f) + + +def _load_public_contract() -> dict: + return json.loads(_read(PUBLIC_CONTRACT_PATH)) + + +def _published_release_version() -> str: + return _load_public_contract()["published_release"]["version"] + + +def _published_release_tag() -> str: + return _load_public_contract()["published_release"]["tag"] + + def _read(relpath: str) -> str: return (REPO_ROOT / relpath).read_text(encoding="utf-8") @@ -249,9 +311,18 @@ def test_well_known_metadata_lists_packet_outputs(): assert data.get("contract") == "agents-shipgate contract --json" assert data.get("contract_version") == contract["contract_version"] assert data.get("version") == contract["cli_version"] + assert "in-tree" in data.get("version_context", ""), ( + ".well-known version is the current-main CLI version, so the JSON " + "must label it as in-tree/current-main rather than a published " + "release." + ) + assert data.get("published_release", {}).get("version") == ( + _published_release_version() + ) + assert data.get("published_release", {}).get("tag") == _published_release_tag() package = data.get("package", {}) assert package.get("github_action") == ( - f"ThreeMoonsLab/agents-shipgate@v{contract['cli_version']}" + f"ThreeMoonsLab/agents-shipgate@{_published_release_tag()}" ) outputs = data.get("outputs", []) for expected in ("packet_md", "packet_json", "packet_html"): @@ -280,6 +351,10 @@ def test_well_known_metadata_lists_packet_outputs(): f".well-known schemas.packet must point to {CURRENT_PACKET_SCHEMA}; " f"got {packet_url!r}." ) + assert "current main" in data.get("schemas_context", ""), ( + ".well-known schemas point at main-branch schema files, so the JSON " + "must say they are current-main / in-tree contracts." + ) def test_agent_contract_current_doc_is_canonical(): @@ -297,9 +372,16 @@ def test_agent_contract_current_doc_is_canonical(): f"contract version `{CONTRACT_VERSION}`." ) assert __version__ == contract["cli_version"] - assert f"Latest release: `v{contract['cli_version']}`" in text, ( + assert f"Latest published release: `{_published_release_tag()}`" in text, ( + "docs/agent-contract-current.md must distinguish the published " + "release from the in-tree CLI contract." + ) + assert ( + f"In-tree CLI version on current main: `v{contract['cli_version']}`" + in text + ), ( "docs/agent-contract-current.md must agree with the runtime " - "contract's cli_version." + "contract's cli_version while labeling it as current-main/in-tree." ) assert CURRENT_REPORT_SCHEMA in text, ( "docs/agent-contract-current.md must reference the current " @@ -356,8 +438,11 @@ def test_constants_match_contract_doc(): packet_match = re.search( r"Current packet schema:\s*`(\d+\.\d+)`", text ) - release_match = re.search( - r"Latest release:\s*`v(\d+\.\d+\.\d+)`", text + published_match = re.search( + r"Latest published release:\s*`v(\d+\.\d+\.\d+)`", text + ) + in_tree_match = re.search( + r"In-tree CLI version on current main:\s*`v(\d+\.\d+\.\d+)`", text ) assert report_match, ( "docs/agent-contract-current.md must declare 'Current report " @@ -367,9 +452,13 @@ def test_constants_match_contract_doc(): "docs/agent-contract-current.md must declare 'Current packet " "schema: `X.Y`'." ) - assert release_match, ( - "docs/agent-contract-current.md must declare 'Latest release: " - "`vX.Y.Z`'." + assert published_match, ( + "docs/agent-contract-current.md must declare 'Latest published " + "release: `vX.Y.Z`'." + ) + assert in_tree_match, ( + "docs/agent-contract-current.md must declare 'In-tree CLI version " + "on current main: `vX.Y.Z`'." ) assert report_match.group(1) == CURRENT_REPORT_SCHEMA_VERSION, ( f"contract doc says report schema is " @@ -381,18 +470,22 @@ def test_constants_match_contract_doc(): f"{packet_match.group(1)!r}; test constant says " f"{CURRENT_PACKET_SCHEMA_VERSION!r}. Update both together." ) - assert release_match.group(1) == _load_pyproject_version(), ( - f"contract doc says latest release is " - f"v{release_match.group(1)}; pyproject.toml says " + assert published_match.group(1) == _published_release_version(), ( + f"contract doc says latest published release is " + f"v{published_match.group(1)}; public contract says " + f"{_published_release_tag()}. Update both together." + ) + assert in_tree_match.group(1) == _load_pyproject_version(), ( + f"contract doc says in-tree CLI version is " + f"v{in_tree_match.group(1)}; pyproject.toml says " f"v{_load_pyproject_version()}. Update both together." ) -def test_pyproject_version_propagates_to_metadata_surfaces(): - """pyproject.toml [project].version is the single source of truth - for the package version. Every public metadata surface must echo it - exactly. Catches a stale .well-known, llms.txt, contract doc, or - src/__init__ when the package version bumps.""" +def test_in_tree_version_propagates_to_runtime_contract_surfaces(): + """pyproject.toml [project].version is the source of truth for the + current-main / in-tree CLI version. Public surfaces may mention it, + but only with explicit in-tree/current-main wording.""" expected = _load_pyproject_version() # src/agents_shipgate/__init__.__version__ @@ -404,13 +497,43 @@ def test_pyproject_version_propagates_to_metadata_surfaces(): f"{expected!r}. Update src/agents_shipgate/__init__.py." ) - # .well-known/agents-shipgate.json well_known = json.loads(_read(".well-known/agents-shipgate.json")) assert well_known["version"] == expected, ( f".well-known/agents-shipgate.json `version` is " f"{well_known['version']!r}; pyproject.toml says " f"{expected!r}." ) + assert IN_TREE_VERSION_CONTEXT.search(well_known["version_context"]) + + llms_text = _read("llms.txt") + llms_in_tree = re.search( + r"In-tree CLI version on current main:\s*v(\d+\.\d+\.\d+)", + llms_text, + ) + assert llms_in_tree and llms_in_tree.group(1) == expected, ( + f"llms.txt must name the in-tree CLI version as v{expected} " + "with current-main wording." + ) + + contract_text = _read("docs/agent-contract-current.md") + contract_in_tree = re.search( + r"In-tree CLI version on current main:\s*`v(\d+\.\d+\.\d+)`", + contract_text, + ) + assert contract_in_tree and contract_in_tree.group(1) == expected, ( + f"docs/agent-contract-current.md in-tree CLI version must be " + f"`v{expected}`." + ) + + +def test_published_release_version_propagates_to_public_metadata_surfaces(): + """docs/public-contract.json is the source of truth for the latest + published release and public Action / install pins. This deliberately + differs from the current-main in-tree CLI version while main is ahead.""" + expected = _published_release_version() + + well_known = json.loads(_read(".well-known/agents-shipgate.json")) + assert well_known["published_release"]["version"] == expected action_pin = well_known["package"]["github_action"] action_match = ACTION_PIN_PATTERN.search(action_pin) assert action_match, ( @@ -419,11 +542,9 @@ def test_pyproject_version_propagates_to_metadata_surfaces(): ) assert action_match.group(1) == expected, ( f".well-known package.github_action pins " - f"v{action_match.group(1)}; pyproject.toml says v{expected}." + f"v{action_match.group(1)}; public contract says v{expected}." ) - # llms.txt — both the "Latest public release" line and the - # GitHub Action line must echo the package version. llms_text = _read("llms.txt") llms_release = re.search( r"Latest public release:\s*v(\d+\.\d+\.\d+)", llms_text @@ -433,7 +554,7 @@ def test_pyproject_version_propagates_to_metadata_surfaces(): ) assert llms_release.group(1) == expected, ( f"llms.txt 'Latest public release' is " - f"v{llms_release.group(1)}; pyproject.toml says v{expected}." + f"v{llms_release.group(1)}; public contract says v{expected}." ) llms_action = ACTION_PIN_PATTERN.search(llms_text) assert llms_action, ( @@ -442,20 +563,104 @@ def test_pyproject_version_propagates_to_metadata_surfaces(): ) assert llms_action.group(1) == expected, ( f"llms.txt Action pin is v{llms_action.group(1)}; " - f"pyproject.toml says v{expected}." + f"public contract says v{expected}." ) - # docs/agent-contract-current.md contract_text = _read("docs/agent-contract-current.md") contract_release = re.search( - r"Latest release:\s*`v(\d+\.\d+\.\d+)`", contract_text + r"Latest published release:\s*`v(\d+\.\d+\.\d+)`", + contract_text, ) assert contract_release and contract_release.group(1) == expected, ( - f"docs/agent-contract-current.md 'Latest release' must be " + f"docs/agent-contract-current.md 'Latest published release' must be " f"`v{expected}`." ) +def test_public_contract_constants_are_current(): + """docs/public-contract.json is the committed no-network source of + truth for public release metadata and agent-discovery constants.""" + data = _load_public_contract() + assert data["schema_version"] == "0.1" + assert data["published_release"]["tag"] == f"v{data['published_release']['version']}" + assert data["canonical_tagline"] == TAGLINE + for key, command in INSTALL_COMMANDS.items(): + assert data["install"][key] == command + assert data["install"]["uvx_fixture"] == ( + "uvx agents-shipgate fixture run support_refund_agent" + ) + assert data["github_action"] == ( + f"ThreeMoonsLab/agents-shipgate@{data['published_release']['tag']}" + ) + assert data["trigger_catalog_url"] == TRIGGER_CATALOG_URL + assert data["gating_signal"] == GATING_SIGNAL + main_contract = data["main_branch_contract"] + assert "current main" in main_contract["context"] + assert main_contract["manifest_schema_url"] == MANIFEST_SCHEMA_URL + assert main_contract["report_schema_url"] == REPORT_SCHEMA_URL + assert main_contract["packet_schema_url"] == PACKET_SCHEMA_URL + + +@pytest.mark.parametrize("relpath", PUBLIC_SURFACES) +def test_public_surfaces_contextualize_in_tree_version(relpath): + """If a public surface mentions the current in-tree CLI version, it + must label it as current-main / in-tree / local contract. This prevents + unreleased main from masquerading as the latest public release.""" + version = re.escape(_load_pyproject_version()) + text = _read(relpath) + for match in re.finditer(rf"(?'` Action input in workflow - examples must match the package version too. Catches a stale + examples must match the published release version too. Catches a stale matrix where the Action pin is updated but the CLI install version inside it is left behind.""" - expected = _load_pyproject_version() + expected = _published_release_version() for line_number, line, found in _file_lines_with_pin( relpath, SHIPGATE_VERSION_INPUT_PATTERN ): assert found == expected, ( f"{relpath}:{line_number} sets shipgate_version: " - f"'{found}'; pyproject.toml says {expected}.\n line: " + f"'{found}'; public contract says {expected}.\n line: " f"{line.strip()!r}" ) -@pytest.mark.parametrize("relpath,pattern", VERSION_LITERAL_TARGETS) -def test_version_literals_match_pyproject_version(relpath, pattern): +@pytest.mark.parametrize("relpath,pattern", PUBLISHED_VERSION_LITERAL_TARGETS) +def test_published_version_literals_match_public_contract(relpath, pattern): """Plain release-version literals on these public surfaces (the bug-report placeholder, distribution.md's release-tag list, - faq.md's 'latest released version' line, ROADMAP.md's lead - paragraph) must move with pyproject.toml on every bump. The - Action / pip / shipgate_version pin tests don't catch these - because the literals aren't pins.""" - expected = _load_pyproject_version() + faq.md's 'latest released version' line) must move with + docs/public-contract.json on every published release. The Action / + pip / shipgate_version pin tests don't catch these because the + literals aren't pins.""" + expected = _published_release_version() text = _read(relpath) match = pattern.search(text) assert match, ( f"{relpath} no longer contains the expected version-literal " f"phrase ({pattern.pattern!r}). Either the surface was rewritten " - "(update VERSION_LITERAL_TARGETS to match the new phrasing) or " - "the literal was dropped entirely." + "(update PUBLISHED_VERSION_LITERAL_TARGETS to match the new " + "phrasing) or the literal was dropped entirely." ) assert match.group(1) == expected, ( f"{relpath} names release version v{match.group(1)} in " - f"public copy; pyproject.toml says v{expected}. Bump the " - "literal in this file or align pyproject.toml.\n match: " + f"public copy; public contract says v{expected}. Bump the " + "literal in this file or update docs/public-contract.json.\n" + " match: " f"{match.group(0)!r}" ) +@pytest.mark.parametrize("relpath,pattern", IN_TREE_VERSION_LITERAL_TARGETS) +def test_in_tree_version_literals_match_pyproject_version(relpath, pattern): + """Plain literals that intentionally reference the upcoming in-tree + version must move with pyproject.toml and carry release-prep/current-main + context rather than claiming to be the latest published release.""" + expected = _load_pyproject_version() + text = _read(relpath) + match = pattern.search(text) + assert match, ( + f"{relpath} no longer contains the expected in-tree version " + f"phrase ({pattern.pattern!r})." + ) + assert match.group(1) == expected, ( + f"{relpath} names in-tree version v{match.group(1)}; " + f"pyproject.toml says v{expected}." + ) + + @pytest.mark.parametrize("relpath", PUBLIC_SURFACES) def test_public_surface_mentions_current_packet_schema_when_it_mentions_any( relpath,