Repository navigation
Description
Outcome: the release workflows are hardened — third-party actions pinned to commit SHAs, least-privilege permissions, build/sign/publish separated, and the gate fails closed on any missing, mislabeled or unexpected artifact.
Objective. The GitHub release and CI workflows still reference third-party actions by mutable tags (@v4, @stable) and do not separate building from signing from publishing, so the release gate cannot fail closed on a missing or extra artifact.
Inputs / authority. .github/workflows/release.yml and .github/workflows/ci.yml; docs/tasks/T27-cross-platform-release.md row T27-57; the campaign's fail-closed requirement (see #33); the repository's own ruleset configuration.
Stopping condition. Every third-party action is pinned to a full commit SHA; permissions are least-privilege; build, sign and publish are separate steps/jobs; all artifacts bind to one tag SHA; and a safe test-release mode exists that never touches a production release.
Verification. grep -n 'uses:' .github/workflows/*.yml shows only full-SHA pins; a deliberately broken aggregate (missing asset, version mismatch, checksum mismatch, skipped platform) leaves no published release, exercised in the test-release mode.
Status: IMPLEMENTED — awaiting the CI receipt.
Pinning (2026-11-11, decision 0099). All 19 action references in ci.yml, release.yml and deploy-site.yml name a 40-hex commit SHA, with the version in a trailing comment. scripts/validate_workflow_pins.py fails the backup CI's changes job on an unpinned reference, and scripts/validate_workflow_pins-selftest.sh demonstrates each failure mode with one deliberately broken input. The deploy trigger no longer lists the workflow's own file, so a workflow edit is not a production publication.
Release mode and the aggregate gate (2026-11-12, decision 0100). The two remaining acceptance criteria are implemented, and each is demonstrated by a self-test that runs in the same CI step:
- The gate fails on a skipped required platform and on unexpected extra artifacts. The aggregate validation moved out of inline YAML into
scripts/validate_release_aggregate.sh— it was a hundred lines of YAML that ran once, on a tag, with nothing around it. It now rejects an artifact the release does not publish, and requires every required platform (x86_64-unknown-linux-gnu,wasm32-unknown-unknown) to be represented in the release manifest.scripts/validate_release_aggregate-selftest.shbreaks a complete artifact set one thing at a time: a missing artifact, an empty artifact, an unexpected extra artifact, a checksum mismatch, a manifest version mismatch, a manifest digest mismatch, an asset the manifest does not list, an unknown manifest schema, a skipped required platform, and a platform the manifest does not represent — the last being the discriminating case, where every artifact is present and every digest agrees and only the platform check can fail. - A safe test-release mode exists.
scripts/classify_release_mode.shmapsvX.Y.Zto a production release andvX.Y.Z-dryrun.Nto a rehearsal, and refuses every other shape (a two-component version, novprefix, an unknown suffix, a-dryrunwithout a number, a non-numeric number, a trailing hyphen, a tag that names another version). The release workflow gained aworkflow_dispatchtrigger that may only rehearse. A rehearsal release is a prerelease titledTEST <version>whose notes carry a banner saying it must not be published, and its disposable tag is created at the exact commit that built the artifacts, so the artifacts and the tag bind to one SHA.scripts/classify_release_mode-selftest.shdemonstrates each refused shape, andtests/release-assets-selftest.shdrives theghargument vector against a stubghthat records it.
Remaining. The rehearsal path has not been run end to end through workflow_dispatch — it needs the workflow on main first, and the manual rehearsal in #99 predates the TEST title and the prerelease flag, so it does not yet demonstrate this mode. The checks are in the GitHub backup CI; mirroring them into the GitForge pipeline waits for the :2 pipeline shape (PR #123, PR #129).
PR #130 carries the work.
Activity
aliasfoxkde commentedon Nov 11, 2026
Progress: two of the three hardening pieces are on
main— least-privilegeGITHUB_TOKENpermissions (#21) and the fail-closed gate + split stamp checks (train #107,b357639). Still open: actions are tag-pinned, not SHA-pinned —.github/workflows/release.ymlstill usesactions/checkout@v4,actions/cache@v4,dtolnay/rust-toolchain@stable, etc. Leaving open for the SHA-pinning half.