Make stable release promotion copy and verify asset bytes #1575

Closed
opened 2026-07-28 02:39:21 +00:00 by coilyco-ops · 3 comments
Member

Outcome

Ward's stage-two release promotion must copy the staged binary bytes into the stable Forgejo release, verify those bytes before publication, and give the GitHub mirror only the already-verified artifact set.

Parent goal: #1574

Current evidence

  • The stable v0.823.0 release reports SHA256SUMS at 268 bytes and platform assets at roughly 279 to 286 bytes.
  • The same commit's draft release contains platform binaries at roughly 13.2 to 14.9 MB and a 512-byte checksum file.
  • .forgejo/workflows/release.yml fetches each staged asset from /releases/{id}/assets/{asset_id} with Accept: application/octet-stream, then uploads that response to the stable release without validating its shape or checksum.
  • This Forgejo deployment returns an asset metadata document from that route despite the requested accept header. The promotion job therefore uploads metadata JSON as if it were the binary.
  • scripts/forgejo-release-asset.sh already follows Forgejo metadata hops to the raw byte body. Homebrew and Scoop use the equivalent resolved attachment path and currently avoid the broken stable layer.
  • #1493 fixed the package-manager side of this Forgejo behavior, but it did not change stage-two stable asset promotion.

Affected release: https://forgejo.coilysiren.me/coilyco-flight-deck/ward/releases/tag/v0.823.0

Required behavior

  • Engineering must route every staged asset fetch through scripts/forgejo-release-asset.sh or one shared equivalent that follows metadata hops and emits the raw body.
  • The promotion job must verify all staged platform binaries against SHA256SUMS before any stable asset replacement or upload begins.
  • The promotion job must reject metadata-shaped payloads, missing assets, duplicate names, checksum mismatches, and implausibly small platform binaries.
  • The promotion job must read every uploaded stable asset back through the same byte resolver and verify names, sizes, and checksums before it marks the Forgejo release public.
  • The GitHub mirror job must consume only that verified dist set. It must not rebuild or re-fetch a different payload.
  • Repository tests must model a Forgejo API route that returns metadata despite Accept: application/octet-stream, then prove that promotion reaches and verifies the raw attachment bytes.
  • Release documentation must describe the verification boundary. docs/FEATURES.md does not need a bugfix-only update.

Acceptance criteria

  • A fixture returns metadata JSON from the asset API route and raw bytes from its browser_download_url, and the promotion path stores the raw bytes.
  • A fixture that substitutes metadata JSON, truncates a binary, omits an asset, duplicates a name, or changes a checksum fails before stable publication.
  • A fixture proves that every stable asset is read back and reverified before the release becomes public.
  • A fixture proves that the GitHub mirror upload uses the same verified files.
  • Ward's repository-declared vet, lint, test, and pre-commit validation passes.
  • The next normal Forgejo release contains full-size stable platform binaries with checksums that match SHA256SUMS.
  • The next normal GitHub mirror release contains byte-identical assets when mirror publication is enabled. If mirror credentials are not configured, the release evidence must say so explicitly and defer only the mirror-specific observation to #646.

Safety

  • Engineering must prove the fix with repository fixtures before one grounded push.
  • Engineering and QA may inspect release and Actions state through read-only surfaces. An operator must perform any live mutation beyond the normal push-triggered release flow.
  • The team must not use repeated release pushes as an experiment loop.

Codex, via advisor surface

## Outcome Ward's stage-two release promotion must copy the staged binary bytes into the stable Forgejo release, verify those bytes before publication, and give the GitHub mirror only the already-verified artifact set. Parent goal: https://forgejo.coilysiren.me/coilyco-flight-deck/ward/issues/1574 ## Current evidence * The stable v0.823.0 release reports `SHA256SUMS` at 268 bytes and platform assets at roughly 279 to 286 bytes. * The same commit's draft release contains platform binaries at roughly 13.2 to 14.9 MB and a 512-byte checksum file. * `.forgejo/workflows/release.yml` fetches each staged asset from `/releases/{id}/assets/{asset_id}` with `Accept: application/octet-stream`, then uploads that response to the stable release without validating its shape or checksum. * This Forgejo deployment returns an asset metadata document from that route despite the requested accept header. The promotion job therefore uploads metadata JSON as if it were the binary. * `scripts/forgejo-release-asset.sh` already follows Forgejo metadata hops to the raw byte body. Homebrew and Scoop use the equivalent resolved attachment path and currently avoid the broken stable layer. * https://forgejo.coilysiren.me/coilyco-flight-deck/ward/issues/1493 fixed the package-manager side of this Forgejo behavior, but it did not change stage-two stable asset promotion. Affected release: https://forgejo.coilysiren.me/coilyco-flight-deck/ward/releases/tag/v0.823.0 ## Required behavior * Engineering must route every staged asset fetch through `scripts/forgejo-release-asset.sh` or one shared equivalent that follows metadata hops and emits the raw body. * The promotion job must verify all staged platform binaries against `SHA256SUMS` before any stable asset replacement or upload begins. * The promotion job must reject metadata-shaped payloads, missing assets, duplicate names, checksum mismatches, and implausibly small platform binaries. * The promotion job must read every uploaded stable asset back through the same byte resolver and verify names, sizes, and checksums before it marks the Forgejo release public. * The GitHub mirror job must consume only that verified `dist` set. It must not rebuild or re-fetch a different payload. * Repository tests must model a Forgejo API route that returns metadata despite `Accept: application/octet-stream`, then prove that promotion reaches and verifies the raw attachment bytes. * Release documentation must describe the verification boundary. `docs/FEATURES.md` does not need a bugfix-only update. ## Acceptance criteria * A fixture returns metadata JSON from the asset API route and raw bytes from its `browser_download_url`, and the promotion path stores the raw bytes. * A fixture that substitutes metadata JSON, truncates a binary, omits an asset, duplicates a name, or changes a checksum fails before stable publication. * A fixture proves that every stable asset is read back and reverified before the release becomes public. * A fixture proves that the GitHub mirror upload uses the same verified files. * Ward's repository-declared vet, lint, test, and pre-commit validation passes. * The next normal Forgejo release contains full-size stable platform binaries with checksums that match `SHA256SUMS`. * The next normal GitHub mirror release contains byte-identical assets when mirror publication is enabled. If mirror credentials are not configured, the release evidence must say so explicitly and defer only the mirror-specific observation to https://forgejo.coilysiren.me/coilyco-flight-deck/ward/issues/646. ## Safety * Engineering must prove the fix with repository fixtures before one grounded push. * Engineering and QA may inspect release and Actions state through read-only surfaces. An operator must perform any live mutation beyond the normal push-triggered release flow. * The team must not use repeated release pushes as an experiment loop. <!-- ward-agent-signature --> Codex, via advisor surface
Author
Member

Confirmed blast radius and acceptance refinement

The read-only release audit reproduced the defect directly:

  • The staged draft asset API route returned HTTP 200, application/json, and a 279-byte attachment document despite Accept: application/octet-stream.
  • The current promotion loop writes that response directly to dist and uploads it unchanged. The stable ward-linux-amd64 asset is the same 279 bytes.
  • The workflow's cat dist/SHA256SUMS prints a 268-byte JSON document, so the job remains green without verifying a checksum file.
  • The newest 50 public release records contain 38 releases with 13 tiny assets each, beginning at v0.781.0. Earlier recent records expose no assets.
  • Homebrew and Scoop v0.823.0 currently resolve the stable JSON stubs onward to full draft attachments. The install channels work at the moment, but they depend on disposable draft retention rather than stable release bytes.
  • The green release run is https://forgejo.coilysiren.me/coilyco-flight-deck/ward/actions/runs/2461.

The implementation must therefore add these proofs:

  • Homebrew and Scoop manifests must resolve to stable-release attachment bytes after the fix, not to draft-* release attachments.
  • Tests must bind helper usage and checksum verification specifically to the promote-draft-assets path. A whole-workflow string search is insufficient because later Scoop steps already use scripts/forgejo-release-asset.sh.
  • The implementation should prefer a fixture-tested promotion script or an equivalently isolated unit whose failure cases can run without probing live releases.

Codex, via advisor surface

## Confirmed blast radius and acceptance refinement The read-only release audit reproduced the defect directly: * The staged draft asset API route returned HTTP 200, `application/json`, and a 279-byte attachment document despite `Accept: application/octet-stream`. * The current promotion loop writes that response directly to `dist` and uploads it unchanged. The stable `ward-linux-amd64` asset is the same 279 bytes. * The workflow's `cat dist/SHA256SUMS` prints a 268-byte JSON document, so the job remains green without verifying a checksum file. * The newest 50 public release records contain 38 releases with 13 tiny assets each, beginning at v0.781.0. Earlier recent records expose no assets. * Homebrew and Scoop v0.823.0 currently resolve the stable JSON stubs onward to full draft attachments. The install channels work at the moment, but they depend on disposable draft retention rather than stable release bytes. * The green release run is https://forgejo.coilysiren.me/coilyco-flight-deck/ward/actions/runs/2461. The implementation must therefore add these proofs: * Homebrew and Scoop manifests must resolve to stable-release attachment bytes after the fix, not to `draft-*` release attachments. * Tests must bind helper usage and checksum verification specifically to the `promote-draft-assets` path. A whole-workflow string search is insufficient because later Scoop steps already use `scripts/forgejo-release-asset.sh`. * The implementation should prefer a fixture-tested promotion script or an equivalently isolated unit whose failure cases can run without probing live releases. <!-- ward-agent-signature --> Codex, via advisor surface
Author
Member

WARD-WORKFLOW: #1580

details

workflow: pull-request; review summary: skipped (temporary ward default pending brokered QA)
felt: the release boundary is now isolated and testable.
confidence: high
surprises: full make test still fails in unrelated cmd/ward tests.
follow-ups: none

Codex, via advisor surface

WARD-WORKFLOW: https://forgejo.coilysiren.me/coilyco-flight-deck/ward/pulls/1580 <details><summary>details</summary> workflow: pull-request; review summary: skipped (temporary ward default pending brokered QA) felt: the release boundary is now isolated and testable. confidence: high surprises: full `make test` still fails in unrelated `cmd/ward` tests. follow-ups: none <!-- ward-agent-signature --> Codex, via advisor surface </details>
Author
Member

WARD-WORKFLOW: reservation-released

release details

Run finished with WARD-WORKFLOW: https://forgejo.coilysiren.me/coilyco-flight-deck/ward/pulls/1580.

ward container reap released container engineer-codex-ward-1575 (--harness codex): the terminal outcome supersedes the reservation, so a later redispatch no longer needs --override-reservation.

Outcome summary: #1580

— Codex, via ward agent

<!-- ward-agent-reservation-released --> WARD-WORKFLOW: reservation-released <details><summary>release details</summary> Run finished with `WARD-WORKFLOW: https://forgejo.coilysiren.me/coilyco-flight-deck/ward/pulls/1580`. `ward container reap` released container `engineer-codex-ward-1575` (`--harness codex`): the terminal outcome supersedes the reservation, so a later redispatch no longer needs `--override-reservation`. **Outcome summary:** https://forgejo.coilysiren.me/coilyco-flight-deck/ward/pulls/1580 </details> <!-- ward-agent-signature --> — Codex, via `ward agent`
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
coilyco-flight-deck/ward#1575
No description provided.