Release Publication Verifier (post-tag)
You verify that a release actually published — turning a tag into concrete,
auditable proof across every release surface — so release-completion issues are
closed with evidence, not assumption. This runs after flow-release cuts and
pushes the tag (the release gate) and before the post-release close-outs.
This is native release-engineering verification (Rust crates, .deb/.rpm,
container images, GHCR, release-asset checksums, installer URLs) — not plugin
packaging. For plugin archives use package-all-plugins; for the release
sequence itself use flow-release.
When to use
- After a tag is pushed and the release workflows (
gitea-release.yml,npm-publish.yml,github-mirror.yml, container build) have run. - Before closing release-completion issues — paste the emitted evidence summary into the close comment.
- As the verification step
flow-releasepoints to after itsreleasegate.
Inputs
- tag (required) — e.g.
v2026.6.2. The verification context. - --repo
<owner/name>— primary tracker repo (default:remotes.issue_trackerresolved from.aiwg/aiwg.config). - --mirror
<owner/name>— public mirror (default:remotes.secondary[]withpurpose: public-mirror, orproject.mirror_urlfrom.aiwg/release.config). - --config
<path>— release config (default.aiwg/release.config). - --json — emit the evidence as JSON instead of the markdown summary.
Repo-local configuration
Read the optional publication_verify block from .aiwg/release.config. It
declares what "published" means for THIS project — required asset globs, the
checksum manifest, the GHCR image matrix, the installer URL, and platform smoke
jobs. When the block is absent, run the universal checks (release exists +
assets present + checksums) and record everything else as MISSING (not
configured) rather than failing.
# .aiwg/release.config (optional block)
publication_verify:
release_assets: # globs that MUST be attached to the release
- "aiwg-{version}.tgz"
- "*.deb"
- "*.rpm"
- "install.sh"
checksums:
manifest: "SHA256SUMS" # manifest asset name
must_cover: ["*.tgz", "*.deb", "*.rpm", "install.sh"] # globs the manifest must list
ghcr_images: # container images that MUST exist + run
- image: "ghcr.io/{owner}/aiwg"
tags: ["{version}", "latest"]
smoke: "--version" # documented command that must succeed
- image: "ghcr.io/{owner}/aiwg-agent-client"
tags: ["{version}"]
smoke: "--version"
installer:
url: "https://github.com/{mirror}/releases/download/{tag}/install.sh"
dry_run: "sh install.sh --dry-run" # must succeed against the real release URL
platform_jobs: # platform-specific evidence (e.g. host-direct smoke)
- name: "mutsu-apple-silicon-smoke"
evidence: "ci-run or log path proving the platform job ran green"
{version} is the tag without the v; {tag} is the full tag; {owner} /
{mirror} come from the resolved repos.
Verification surfaces
Run each surface that applies. Use mcp__git-gitea__* for Gitea, gh for
GitHub, curl/oras/docker/skopeo for GHCR, sha256sum -c for checksums.
Never print tokens — load from env/file per the token-security rule.
- Gitea release exists + assets — the release for
tagexists and everyrelease_assetsglob matches at least one attached asset. - GitHub mirror release + assets — same against the mirror (skip with a
recorded MISSING if no mirror is configured, or if the tag is a pre-release
that intentionally skips the mirror per
flow-releasepolicy). - SHA256SUMS present + covers — the
checksums.manifestasset exists and lists an entry for everymust_coverglob. Download the manifest and the covered assets;sha256sum -cmust pass for each downloaded file. - Native package checksums — for each
.deb/.rpm/installer asset, verify its recorded checksum matches the downloaded file (FAILED on mismatch). - GHCR images exist + smoke — for each
ghcr_imagesentry and tag, confirm the image manifest exists (docker manifest inspect/skopeo inspect/oras manifest fetch), then run the documentedsmokecommand and assert exit 0. - Installer dry-run from the real URL — fetch
installer.urlfor the tag and runinstaller.dry_run; assert exit 0. This proves the published installer is reachable and parses, not just that a file exists. - Platform jobs — for each
platform_jobsentry, confirm the referenced CI run / log evidence shows the platform-specific job ran green (e.g. mutsu Apple-Silicon host-direct smoke).
Missing vs failed (record them distinctly)
Every check resolves to exactly one verdict:
| Verdict | Meaning | Example |
|---|---|---|
| PASS | Proof found and correct | aiwg-2026.6.2.tgz attached; checksum matches |
| FAIL | Proof found but wrong | SHA256SUMS lists the tarball but the hash mismatches |
| MISSING | Proof not found | No .deb asset attached; or GHCR image tag absent |
| N/A (not configured) | The project didn't declare this surface | No ghcr_images block → skip, don't fail |
FAIL and MISSING are different problems: FAIL means the release is corrupt
and must be re-cut; MISSING means a surface didn't publish (workflow didn't run,
asset not uploaded) — investigate the pipeline. Never collapse the two into a
single "not green" bucket; the close comment must show which is which.
Output schema
Default output is an issue-comment-ready markdown block. With --json, emit
the same data as { tag, repo, mirror, checks: [{ surface, target, verdict, detail }], summary: { pass, fail, missing, na }, publishable: <bool> }.
publishable is true only when there are zero FAIL and zero unexpected
MISSING (configured-but-absent) checks.
Markdown template to paste into the release-completion issue:
## Release publication verification — {tag}
| Surface | Target | Verdict | Detail |
|---------|--------|---------|--------|
| Gitea release | {repo} | PASS | release present, 4/4 asset globs matched |
| GitHub mirror | {mirror} | PASS | release present, 4/4 asset globs matched |
| SHA256SUMS | SHA256SUMS | PASS | covers tgz/deb/rpm/install.sh; `sha256sum -c` OK |
| Native pkg checksums | *.deb, *.rpm | PASS | hashes match |
| GHCR images | aiwg:{version},latest | PASS | manifests exist; `--version` exits 0 |
| Installer dry-run | install.sh@{tag} | PASS | `--dry-run` exits 0 from release URL |
| Platform job | mutsu-apple-silicon-smoke | MISSING | no CI evidence found — investigate |
**Summary:** 6 PASS · 0 FAIL · 1 MISSING · 0 N/A → **not fully published**
(missing: mutsu Apple-Silicon smoke evidence).
Acceptance criteria
- Accepts a
tagand repo target (and optional mirror/config). - Verifies Gitea + GitHub release assets, the checksum manifest, native package checksums, GHCR image existence + smoke, and installer dry-run when configured.
- Records MISSING distinctly from FAIL (and N/A for not-configured surfaces).
- Emits a concise issue-comment-ready evidence summary (and
--json). - Discoverable via
aiwg discover "post tag release asset verifier GHCR packages installer".
Related
flow-release— the release sequence; points here after itsreleasegate, beforepost-releaseclose-outs.- Schema:
agentic/code/frameworks/sdlc-complete/schemas/flows/release-config.yaml(publication_verifyblock). - Rules:
token-security,ci-green-before-done,delivery-policy,citation-policy(evidence must be real, never fabricated). - Config:
.aiwg/release.configper project.