mif-docs: Cut an Attested Release
mif-docs: Cut an Attested Release
Section titled “mif-docs: Cut an Attested Release”1. Overview
Section titled “1. Overview”This runbook is the tactical procedure for cutting an attested v-release of the
mif-docs plugin from the modeled-information-format/mif-docs-plugin repo. A
release is “attested” when .github/workflows/release.yml builds a reproducible
git archive tarball, generates SLSA build provenance, and fail-closed
verifies that provenance with gh attestation verify --signer-workflow
before uploading mif-docs-plugin-<tag>.tar.gz. It covers the full path from a
green main to a marketplace pin update.
It does not cover authoring docs or changing gates — for that, see the
attested-delivery decision record (adr-0003-attested-delivery) and the
validate/author how-to (how-to-validate-and-author). This runbook assumes the
gates are already wired and you simply need to ship.
2. Prerequisites & Access
Section titled “2. Prerequisites & Access”Confirm these before you start; sorting out access mid-cut wastes a release window.
ghCLI authenticated as a user account (a PAT, not a bot) with write access tomodeled-information-format/mif-docs-plugin. The human/PAT identity is load-bearing in section 5 — a bot-created release does not fire therelease: publishedevent that triggers the workflow.- Push access to
main(for the version bump) and permission to create releases. - Local checkout of
mif-docs-pluginon an up-to-datemain. - A workstation with
gh attestation verifyavailable (gh 2.40+).
3. Pre-flight: confirm CI is green and bump the version
Section titled “3. Pre-flight: confirm CI is green and bump the version”-
Confirm CI green on
main. Do not cut from a red tree.Terminal window gh run list --repo modeled-information-format/mif-docs-plugin \--branch main --workflow ci.yml --limit 1Expected: the latest
ci.ymlrun iscompleted/success(pin-check+actionlint,validate,adr-smadrall green). If it is failing, stop and fix CI first — an attested release of a broken tree is still broken. -
Bump the plugin version. Edit
plugin.jsonsoversionis the new release (e.g.0.1.0→0.2.0), commit on a branch, and merge tomainthe normal way. The tag you cut in section 5 must match this version.Terminal window gh release list --repo modeled-information-format/mif-docs-plugin --limit 3Expected: the new version is not already an existing release tag. Re-using a tag will not re-attest a prior artifact — pick the next unused
vX.Y.Z.
4. Dry-run the release workflow
Section titled “4. Dry-run the release workflow”Before cutting anything immutable, prove the pipeline is green end to end.
workflow_dispatch runs release.yml in dry-run mode — it builds, attests,
and verifies but does not upload.
gh workflow run release.yml --repo modeled-information-format/mif-docs-plugin --ref maingh run watch --repo modeled-information-format/mif-docs-plugin \ "$(gh run list --repo modeled-information-format/mif-docs-plugin \ --workflow release.yml --limit 1 --json databaseId -q '.[0].databaseId')"Expected result: the run reaches success with the build → attest → verify
steps all green. If verify fails here, the signer-workflow identity or the
attestation is wrong — fix it now, while nothing has been published. Do not
proceed to the real cut until the dry-run is clean.
5. Cut the release (this triggers the attested upload)
Section titled “5. Cut the release (this triggers the attested upload)”Create the GitHub Release at the target commit as a user/PAT, not a bot. The
release: published event is what triggers release.yml to run for real: gate →
reproducible tarball → SLSA attest → fail-closed verify → upload
mif-docs-plugin-<tag>.tar.gz.
# Replace v0.2.0 with your bumped version; --target pins the exact commit.gh release create v0.2.0 \ --repo modeled-information-format/mif-docs-plugin \ --target main \ --title "mif-docs v0.2.0" \ --notes "Attested release. Artifact verified fail-closed before upload."Expected result: the publish triggers a release.yml run on the release
event. Watch it to completion:
gh run watch --repo modeled-information-format/mif-docs-plugin \ "$(gh run list --repo modeled-information-format/mif-docs-plugin \ --workflow release.yml --event release --limit 1 \ --json databaseId -q '.[0].databaseId')"When it is green, the release has mif-docs-plugin-v0.2.0.tar.gz attached. The
same run also tags mif-docs--v0.2.0 at this commit (read from plugin.json’s
name/version) — that is the tag shape Claude Code’s dependency resolver
looks for when another plugin declares a semver range on mif-docs (e.g.
"version": "^0.2.0"), rather than the bare v0.2.0 tag above.
6. Verify the artifact from a workstation
Section titled “6. Verify the artifact from a workstation”Independently verify the uploaded artifact’s attestation — do not trust the run’s own green check alone.
gh release download v0.2.0 \ --repo modeled-information-format/mif-docs-plugin \ --pattern 'mif-docs-plugin-v0.2.0.tar.gz'
gh attestation verify mif-docs-plugin-v0.2.0.tar.gz \ --repo modeled-information-format/mif-docs-plugin \ --signer-workflow modeled-information-format/mif-docs-plugin/.github/workflows/release.ymlAlso confirm the dependency-resolution alias tag landed:
git ls-remote --tags https://github.com/modeled-information-format/mif-docs-plugin.git \ | grep 'refs/tags/mif-docs--v0.2.0'Expected result for the alias-tag check: a line containing
refs/tags/mif-docs--v0.2.0. Empty output means the tagging step in
release.yml failed or didn’t run — check the run’s logs.
Expected result: gh attestation verify prints a success line confirming the
provenance was issued by the release.yml signer workflow for this repo. A
non-zero exit means the artifact is not trustworthy — go to section 8.
7. Marketplace registration — automatic, verify it actually landed
Section titled “7. Marketplace registration — automatic, verify it actually landed”This step is automated as of the six-app / plugin-catalog-update-hub
rollout — do not open the marketplace PR by hand. release.yml’s
notify-catalog-hub job (needs attest-release, runs only on a real
release event, never on the section-4 dry-run) fires a plugin-released
repository_dispatch at modeled-information-format/.github, naming
claude-code-plugins as the marketplace to re-check. The central
plugin-catalog-update-hub workflow there re-pins mif-docs’s marketplace
entry to the new attested release (tag + commit sha, per the org’s sha-pin
policy) and opens a zero-touch, pre-approved, auto-merge PR — the same PR
shape git log --grep 're-pin mif-docs' in claude-code-plugins shows for
every prior release.
Verify it actually happened rather than assuming the dispatch landed:
gh pr list --repo modeled-information-format/claude-code-plugins \ --search "re-pin mif-docs in:title" --state all --limit 3 \ --json number,title,state,autoMergeRequestExpected result: a chore(catalog): re-pin mif-docs to v0.2.0-shaped PR,
either already MERGED (auto-merge cleared it) or OPEN with
autoMergeRequest non-null in the JSON output and its checks running. If
a check on that PR fails for a reason unrelated to the re-pin itself (e.g.
a pre-existing dependency vulnerability in that repo’s own site), fix it
there — auto-merge will not clear a red required check on its own, and the
PR will sit stuck indefinitely without the failure being obviously
connected to this release. Also note that pushing a fix commit to the
auto-generated branch dismisses the hub’s own pre-approval (standard
branch-protection behavior on new commits) — merge with --admin rather
than waiting for a fresh approval that has to come from a human anyway.
Fallback — only if 20+ minutes pass with no PR appearing at all (the dispatch failed, or the hub run itself errored): trigger it by hand —
gh workflow run plugin-catalog-update-hub.yml \ --repo modeled-information-format/.github --field repo=modeled-information-format/claude-code-plugins— or, as a last resort, edit the marketplace entry for mif-docs directly
(pin both the tag and its commit sha — git rev-parse v0.2.0 gets the
sha) and open the PR by hand. catalog-admission re-verifies the release
attestation fail-closed either way; once merged,
claude plugin install mif-docs@modeled-information-format resolves to the
new version.
8. Detection, symptoms & rollback
Section titled “8. Detection, symptoms & rollback”Symptom: a tag was published but no artifact appeared. The attestation step failed — the verify gate is fail-closed, so a failed attest/verify uploads nothing. The published tag with an empty release is the tell.
gh run list --repo modeled-information-format/mif-docs-plugin \ --workflow release.yml --event release --limit 1gh run view --repo modeled-information-format/mif-docs-plugin \ "$(gh run list --repo modeled-information-format/mif-docs-plugin \ --workflow release.yml --event release --limit 1 \ --json databaseId -q '.[0].databaseId')" --log-failedRead the failed step: a red verify step means the signer-workflow path or the attestation did not match; a red attest step means provenance generation failed.
Rollback. A published release with no verified artifact is not a usable release — retract it rather than leaving a half-cut tag:
gh release delete v0.2.0 --repo modeled-information-format/mif-docs-plugin --cleanup-tagThen fix the root cause, re-run the section 4 dry-run until green, and re-cut from section 5. Do not update the marketplace pin (section 7) until section 6 verification passes from a workstation — an unverified artifact must never be the pinned target.