Skip to content

feat: attest and version this repo as a single-plugin marketplace#1

Merged
zircote merged 4 commits into
developfrom
feat/attested-marketplace-release
Jul 3, 2026
Merged

feat: attest and version this repo as a single-plugin marketplace#1
zircote merged 4 commits into
developfrom
feat/attested-marketplace-release

Conversation

@zircote

@zircote zircote commented Jul 3, 2026

Copy link
Copy Markdown
Collaborator

Summary

This repository now doubles as its own single-plugin marketplace, versioned independently from the plugin, with a full attested release pipeline:

  • .claude-plugin/marketplace.json (catalog 1.0.0, source: "./") alongside plugin.json (bumped 0.3.0 -> 0.4.0, added homepage/repository).
  • CI (ci.yml): pin-check, actionlint, claude plugin validate . --strict.
  • Release (release.yml): reproducible tarball, SLSA build provenance, a CycloneDX SBOM, six quality-gate verdicts (SAST/CodeQL, SCA/OSV-Scanner, IaC+license/Trivy, Semgrep, secrets/Gitleaks+TruffleHog, manifest structural review) each seam-attested as a signed custom predicate bound to the tarball digest, an OpenVEX disposition, and a cosign-signed marketplace.json catalog. Every attestation and the catalog signature fail-closed re-verify before the tag-gated publish step.
  • SECURITY.md, docs/how-to/verify-release.md, CHANGELOG.md documenting the verification model and commands.
  • MIF Level 1 frontmatter (id/type/created) on every doc touched, alongside the existing diataxis_type/title convention.

Scope decisions

  • No GitHub App token minting. Release publish uses the default GITHUB_TOKEN, scoped per job. The full-tier attestation/signing machinery (SBOM, cosign, VEX, per-gate attestations) doesn't require a separate release identity; that was a distinct concern in the reference architecture this was adapted from.
  • No separate central signer. Unlike a multi-repo org, this repo has no shared reusable-workflow signer. Every attestation and the catalog signature are produced by this same release.yml running in this same repo, so verification pins --signer-workflow to that one workflow rather than a distinct central identity.
  • ShellCheck not wired. This repo has no .sh scripts to scan (its only executable logic is .github/workflows/*.yml run: blocks). A gate with nothing to scan would produce a permanently-empty attestation that asserts nothing.

Review

Two independent review passes (delegated to an out-of-session reviewer, not self-reviewed), both before commit:

  1. First pass (minimal-tier commit): found a release.yml publish-gating bug (workflow_dispatch against an existing tag could force-publish outside the intended tag-push-only flow) and two hand-rolled CI steps reimplementing maintained actions (zgosalvez/github-actions-ensure-sha-pinned-actions, reviewdog/action-actionlint) — all fixed.
  2. Second pass (full-tier expansion, ~850 new lines across 18 jobs): found and fixed 6 issues, most severe being all 9 gh attestation verify calls in the verify job omitting --signer-workflow (so --repo alone only proved "signed by some workflow in this repo," not specifically release.yml), and a Gitleaks fallback that masked a crashed scan as a clean "0 findings" pass instead of failing closed. Full list in the commit message for 0d47ff3.

Copilot's automated review (first pass) also caught 2 stale hard-coded-version issues in docs, both fixed and threads resolved.

Test Plan

  • claude plugin validate . --strict passes on both manifests.
  • actionlint clean on both workflow files.
  • pin-check verified against real and deliberately-broken samples.
  • All new action SHA pins independently verified against live GitHub API (not copied from training data); one reference-repo pin (osv-scanner-action) was caught pointing at a stale, no-longer-current commit and corrected to the live tag's actual SHA.
  • MIF L1 validation (mif-validate --level 1) passes on every touched doc.
  • git archive tarball-build step dry-run locally.
  • The full attest -> verify -> publish chain needs real GitHub Actions OIDC context; will exercise via workflow_dispatch once this PR is up, and for real on the first tag (v0.4.0, planned immediately after merge).

zircote added 2 commits July 3, 2026 10:00
Add .claude-plugin/marketplace.json so this repo doubles as its own
marketplace catalog, versioned independently from plugin.json. Add a
release pipeline (SLSA build provenance, fail-closed gh attestation
verify, tag-gated GitHub Release) and CI (pin-check, actionlint, claude
plugin validate) scaled to a single solo-maintained skill bundle rather
than the multi-repo container-image pipeline this was modeled on.

Also fixes install docs (README, docs/how-to/install.md), which referenced
a nonexistent plugins.marketplaces config key.

AI-Tool: claude_code
AI-Tool-Version: 2.1.199
AI-Tool-Entrypoint: cli
AI-Tool-Location: local
AI-Model: claude-sonnet-5
AI-Effort: high
AI-Teammate-Mode: in-process
AI-Touched-Lines-Pct: 0.9258
AI-Generated-At: 2026-07-03T14:00:27.806641+00:00
AI-Cohort: ai_assisted
AI-Session-Id: claude-code:c1d77b9a-f74e-4cd6-90e4-ec73cf6aa592
AI-Authoring-Mode: cli-agent
Vcs-Repository-Name: zircote/cdc-error-handling
- Replace hand-rolled pin-check and actionlint-install steps with the
  maintained actions they reimplemented
  (zgosalvez/github-actions-ensure-sha-pinned-actions,
  reviewdog/action-actionlint) per the Don't Reinvent the Wheel rule.
  This also resolves a latent false-negative in the hand-rolled pin-check
  regex for digest-less docker:// references.
- Fix release.yml publish gating: previously only checked ref shape, not
  the triggering event, so a workflow_dispatch run manually pointed at an
  existing tag would still publish despite the header comment's claim
  that dispatch never publishes. Now requires the push event too.
- Remove build job's unused artifact-name output; no downstream job
  consumed it.

AI-Tool: claude_code
AI-Tool-Version: 2.1.199
AI-Tool-Entrypoint: cli
AI-Tool-Location: local
AI-Model: claude-sonnet-5
AI-Effort: high
AI-Teammate-Mode: in-process
AI-Touched-Lines-Pct: 0.2
AI-Generated-At: 2026-07-03T14:09:03.424292+00:00
AI-Cohort: ai_assisted
AI-Session-Id: claude-code:c1d77b9a-f74e-4cd6-90e4-ec73cf6aa592
AI-Authoring-Mode: cli-agent
Vcs-Repository-Name: zircote/cdc-error-handling
@zircote zircote marked this pull request as ready for review July 3, 2026 14:18
Copilot AI review requested due to automatic review settings July 3, 2026 14:18

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR turns the repository into a self-contained single-plugin marketplace and adds an attested, tag-driven release process plus CI validation to support supply-chain verification for published tarballs.

Changes:

  • Added .claude-plugin/marketplace.json and updated the plugin manifest version/metadata.
  • Introduced CI and a release workflow that builds a reproducible tarball, generates SLSA provenance, verifies it fail-closed, and publishes a GitHub Release on tag pushes.
  • Updated docs (README, install + verification how-tos, SECURITY.md) and added a CHANGELOG.md to document installation and verification workflows.

Reviewed changes

Copilot reviewed 9 out of 9 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
SECURITY.md Adds a security policy plus a concrete release verification model and commands.
README.md Updates installation/validation guidance and documents marketplace + release verification workflow.
docs/how-to/verify-release.md New walkthrough for verifying release checksums and SLSA provenance with gh.
docs/how-to/install.md Updates installation instructions to use marketplace add + current settings syntax.
CHANGELOG.md Introduces Keep a Changelog history and documents the new CI/release posture.
.github/workflows/release.yml Adds a tag-gated release pipeline with provenance attestation + fail-closed verification.
.github/workflows/ci.yml Adds CI for SHA pinning, actionlint, and strict plugin/marketplace validation.
.claude-plugin/plugin.json Bumps plugin version to 0.4.0 and adds homepage/repository metadata.
.claude-plugin/marketplace.json New single-plugin marketplace catalog pointing to the repo root (./).

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread CHANGELOG.md Outdated
Comment thread docs/how-to/verify-release.md Outdated
zircote added 2 commits July 3, 2026 10:24
- CHANGELOG.md: the 0.4.0 entry said CI runs `claude plugin validate .`,
  but the workflow actually runs `claude plugin validate . --strict`.
- docs/how-to/verify-release.md: commands were hard-coded to v0.4.0 and
  its exact filenames, which would go stale after the next release and
  invite copy/paste failures. Switched to a substitutable TAG variable
  and glob patterns, matching SECURITY.md's existing convention.
- SECURITY.md: fixed the same hard-coded-checksum-filename issue in the
  adjacent verification section for consistency.

AI-Tool: claude_code
AI-Tool-Version: 2.1.199
AI-Tool-Entrypoint: cli
AI-Tool-Location: local
AI-Model: claude-sonnet-5
AI-Effort: high
AI-Teammate-Mode: in-process
AI-Touched-Lines-Pct: 0.619
AI-Generated-At: 2026-07-03T14:24:10.303330+00:00
AI-Cohort: ai_assisted
AI-Session-Id: claude-code:c1d77b9a-f74e-4cd6-90e4-ec73cf6aa592
AI-Authoring-Mode: cli-agent
Vcs-Repository-Name: zircote/cdc-error-handling
Per your explicit choice ("Full: mirror the reference repo exactly") for
attestation depth, on default GITHUB_TOKEN for release auth (no GitHub
App token minting):

- Six quality-gate jobs (SAST via CodeQL language:actions, SCA via
  OSV-Scanner, IaC/license via Trivy, Semgrep, secrets via Gitleaks +
  TruffleHog, manifest structural review), each seam-attested as a signed
  custom predicate (https://zircote.github.io/attestations/<gate>/v1)
  bound to the release tarball's digest.
- CycloneDX SBOM (anchore/sbom-action + actions/attest-sbom).
- Cosign keyless signature over .claude-plugin/marketplace.json, verified
  in-run before publish.
- OpenVEX disposition attestation (.vex/openvex.json).
- ShellCheck deliberately not wired: this repo has no .sh scripts to scan.

Also adds MIF Level 1 frontmatter (id/type/created) to every doc touched
in this branch (README, install.md, verify-release.md, SECURITY.md,
CHANGELOG.md), alongside the existing diataxis_type/title convention,
per your explicit choice to satisfy the globally-installed mif-docs
plugin's fail-closed guard rather than ignore it.

This body of work went through an independent review pass before commit
(delegated, not self-reviewed) that found and fixed:
- The verify job's 9 gh attestation verify calls omitted
  --signer-workflow, so --repo alone only proved "signed by some workflow
  in this repo," not specifically release.yml. Fixed, and updated every
  consumer-facing verify command in SECURITY.md/verify-release.md to
  match.
- Gitleaks' fallback line masked a crashed/failed scan as a clean "0
  findings" SARIF instead of failing the job. Now fails closed like every
  other gate.
- gate-sca's SARIF upload was missing an explicit category (every sibling
  gate has one).
- The header comment named job names (attest-sbom, attest-vex) that don't
  exist; corrected to the real names (sbom, vex).
- Hardened the verify job's artifact resolution to use the build job's
  recorded subject-name instead of a fragile `ls dist` glob.
- Dropped unused contents:read from the six attest-* jobs, which never
  check out the repo.
- Left the cosign catalog-signer regex intentionally unanchored on @ref
  (documented why: it also self-verifies on workflow_dispatch dry-runs
  from a branch, and anchoring to refs/tags/ would break that).

AI-Tool: claude_code
AI-Tool-Version: 2.1.199
AI-Tool-Entrypoint: cli
AI-Tool-Location: local
AI-Model: claude-sonnet-5
AI-Effort: high
AI-Teammate-Mode: in-process
AI-Touched-Lines-Pct: 0.9512
AI-Generated-At: 2026-07-03T14:59:35.453747+00:00
AI-Cohort: ai_only
AI-Session-Id: claude-code:c1d77b9a-f74e-4cd6-90e4-ec73cf6aa592
AI-Authoring-Mode: cli-agent
Vcs-Repository-Name: zircote/cdc-error-handling
@zircote zircote merged commit ecb8d55 into develop Jul 3, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants