The ui-* archives are byte-identical copies of the canonical ones, published
after verify so they inherit the hash-bound VirusTotal verdicts. They were
absent from checksums.txt, and publish-legacy-aliases.sh documented that as
intentional: "checksums.txt covers the canonical names current installers
request."
That reasoning has a hole. The aliases exist only for 0.9.x updaters (#1538),
and those verify the NAME they asked for. So the alias fixed the 404 and moved
the failure one step later - the updater downloads the archive, cannot find its
name in checksums.txt, and refuses:
warning: codebase-memory-mcp-ui-darwin-arm64.tar.gz not found in checksums.txt
error: refusing to install an unverified download
Reported by AmooAti in #1134. Confirmed on the live v0.10.4 release: eight ui-*
archives published, zero of them listed. Every pre-0.10 user who answered the
old variant chooser with "ui" is hard-blocked from updating by any path.
The same digest is now emitted under the legacy name before the attestation
step, so the attested artifact covers both names. No new bytes and no new scan
surface: an alias is a copy, so its sha256 is by construction the one already
computed.
The rule lives in scripts/ci/append-legacy-alias-checksums.sh rather than inline
in the workflow, because the venue-parity contract requires it: a venue may
provision, plumb artifacts, or call a canonical leg script, and text
transformation is none of those. Keeping it beside publish-legacy-aliases.sh
also puts the two halves of the alias rule in one place, which matters because
they must stay in step - .tar.gz and .zip only, never an already-ui-* name. It
fails closed when it matches nothing, since a name with no asset is as broken as
an asset with no name.
Validated against the real v0.10.4 checksums file: the generated set is exactly
the eight ui-* assets that release published - no phantom names, none missing -
and the empty case exits non-zero.
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
Two independent CI/release-gate correctness fixes. Both were found while sweeping open reports; both had already cost real people real time.
1. checksums.txt does not cover the legacy ui-* aliases (#1134)
The ui-* archives are byte-identical copies published after verify, so they inherit the hash-bound VirusTotal verdicts. They were absent from checksums.txt, and publish-legacy-aliases.sh documented that as deliberate — "checksums.txt covers the canonical names current installers request."
That reasoning has a hole: the aliases exist only for 0.9.x updaters (#1538), and those verify the name they asked for. We fixed the 404 and moved the failure to verification:
Confirmed on the live v0.10.4 release: 8 ui-* archives published, 0 listed in checksums.txt. Every pre-0.10 user who answered the old chooser with "ui" is hard-blocked from updating by any path.
The fix emits the same digest under the legacy name before the attestation step, so the attested artifact covers both names. No new bytes and no new scan surface — an alias is a copy, so its sha256 is by construction the one already computed.
Validation: run against the real v0.10.4 checksums.txt, the generated set is exactly the eight ui-* assets that release published — no phantom names, none missing. The rule mirrors publish-legacy-aliases.sh (.tar.gz/.zip, never an already-ui-* name) and fails closed if it matches nothing, because a name with no asset is as broken as an asset with no name.
2. The licence gate's verdict depended on a live HTTP fetch
audit-license-provenance.py curled apache.org whenever the gate ran and byte-compared, with capture_output and no error check. A failed fetch produced an empty string → unequal → DIFFERS, indistinguishable from a real licence change.
This is the O9 pattern: a gating verdict must be a pure function of the tree, not of whether a web server answered. It reddened security / license-gate on PR #1337 — a branch touching cli.h, hook_augment.c, test_cli.c and no licence at all — leaving that contributor blocked for over two weeks on a signal unrelated to their change.
Verified before pinning: our vendored copy is byte-identical to the upstream canonical text (11358 bytes both sides, diff clean). The Apache-2.0 text is immutable and versioned, so a digest expresses "this is that text" honestly. A mismatch now means our vendored copy changed — exactly, and only, what this audit exists to detect.
The audit passes locally with no network access on the nomic entry.
Risk review
Every consumer of checksums.txt was checked before touching it:
Fixes #1134. Unblocks #1337.