feat(release): create the release as a DRAFT, publish only after the guard (REL-P01) - #340
Merged
Merged
Conversation
…guard (REL-P01) `create-release` published the GitHub Release BEFORE `flight-component` ran, so a failure in that later job left a public, permanently-incomplete release. That is not hypothetical: on falcon-v1.131.0 the component build failed on a missing rustc target and the release was already out with 12 assets and no wasm components — unrepairable without moving a tag or cutting a new version (v1.131.1). The #140 wasm-asset guard could not help, because it lives INSIDE the later job, downstream of the publish. The gate was downstream of the damage. Now: the release is created with `--draft`, and `flight-component` flips it to published as its FINAL step — after the bundle is built, signed and attached, after the #140 guard passes, and after the OCI push. A draft is invisible to consumers, so the failure mode becomes "no release" rather than "half a release". Deliberately NOT `if: always()` and NOT continue-on-error: if anything earlier in the job fails, the job stops before the flip and the release stays a draft — that is the entire mechanism. The OCI push itself REMAINS continue-on-error, so a registry hiccup still publishes (it must never fail a release) while a missing component does not. The finalize step re-asserts the guard's property against the LIVE release rather than trusting that an earlier step in the same job checked it, and re-validates the tag shape before it reaches any shell command. VERIFICATION STATUS — NOT YET MET. REL-P01's criteria require DELIBERATE FAILURE INJECTION (break the component build on a scratch tag; confirm no published release survives), because reasoning about a workflow is exactly how the v1.131.0 ordering bug went unnoticed in the first place. That injection creates a visible tag and draft release, so it is proposed rather than done unilaterally. The happy path (draft -> published) will be exercised by the next real release; the failure path is what still needs injecting. The requirement stays `proposed`. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HvusAXYbHLyv3uTzfBcMbG
…mented Per the two-commit rule an implementation PR sets at most `implemented`; the promotion to `verified` is a separate code-free verify PR after the release exercises it. Precedent: OCI-P01 landed at `implemented` in 4d454f2 and was promoted in bb5b810. Caught before tagging: all three v1.133 artifacts were still `proposed` on their own implementation branches, which would have put three unimplemented- looking artifacts in the release scope at tag time. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HvusAXYbHLyv3uTzfBcMbG
avrabe
force-pushed
the
feat/rel-p01-draft-then-finalize
branch
from
August 7, 2026 13:22
2631b70 to
55601a8
Compare
avrabe
enabled auto-merge (squash)
August 7, 2026 13:22
avrabe
added a commit
that referenced
this pull request
Aug 7, 2026
…erifiers (#346) All three v1.133 requirements sat at `implemented` with NO verification artifact pointing at them. The release-execution traceability gate blocks a tag in that state, and it should: `implemented` with no verifier is a claim with no evidence. This was my own gap. I bumped the three to `implemented` citing OCI-P01 as precedent — but OCI-P01 landed at `implemented` WITH a verifier (FV-FALCON-OCI-001) attached. I replicated the status and not the evidence. Adds one verifier each, with CI-runnable assertions rather than prose: FV-FALCON-REL-001 -> SWREQ-FALCON-REL-P01 both `gh release create` paths pass --draft; one terminal publish step; the malformed-tag guard exists. FV-FALCON-OCI-002 -> SWREQ-FALCON-OCI-P02 8/8 components are no_std AND export the shared bounded-arena runtime. Asserted as EIGHT, not "at least one": v1.129 shipped a partially converted cascade, and one std stage re-introduces WASI and memory.grow into the fused image. FV-FALCON-OCI-003 -> SWREQ-FALCON-OCI-P04 the package declares pulseengine:falcon-cascade AND there are ZERO remaining `falcon:cascade` references. The negative assertion carries the weight — a PARTIAL rename is worse than none. These are real verifiers, not decoration — each was observed to FAIL before its implementation merged and PASS after: OCI-003 PASS once #337 landed OCI-002 1/8 -> 8/8 the moment #339 landed REL-001 0/2 -> 2/2 the moment #340 landed A step that cannot fail is not evidence; these can, and did. Also caught by running the gate rather than trusting the draft: an `gh release view falcon-v1.133.0 ... # bench-only` step really EXECUTED and failed rc=1. The gate detects bench-only by COMMAND SHAPE (BENCH_PATTERNS) — rivet strips shell comments at the YAML->JSON boundary, so the marker is inert. Removed, with the reasoning recorded in the artifact: a step whose result depends on WHEN it runs is not evidence. All three stay `implemented`, not `verified`. The binding evidence — published blobs, and an actual draft->published transition — only exists once falcon-v1.133.0 has run. Promotion is the post-tag verify PR, per the two-commit rule. Verified locally: gate scoped to these artifacts: 7/7 steps PASS, exit 0, 5 bench-only skipped rivet validate (v0.19.0 — the version the GATE pins, not local 0.28): PASS Claude-Session: https://claude.ai/code/session_01HvusAXYbHLyv3uTzfBcMbG Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
avrabe
added a commit
that referenced
this pull request
Aug 8, 2026
…350) The sweep has grown into its own ceiling. Measured across one day, same 156-artifact sweep: #340 ~63m success #344 1h30m21s FAILURE — killed at timeout-minutes: 90 #344 (earlier) cancelled #342 failed twice the same way #343 success twice Variance now exceeds headroom, so a REQUIRED check is decided by runner load rather than by whether anything is wrong. And a timeout surfaces as `fail`, indistinguishable from a real failure unless you read the duration — which already cost a diagnosis cycle. Worst of all, it was blocking the two PRs meant to improve this gate (#342 fail-open fix, #343 sweep speedup). Raising the ceiling does not weaken the gate: it is being KILLED, not failing. Landing it here because this PR already owns this file and is itself blocked by the timeout it fixes. THIS IS A STOPGAP and the third reactive bump (60 -> 90 -> 150). The trend is the real problem: 45m in July, >=90m now. #350 tracks it, and names the next measurement — the compile-vs-test split ON the runner. The sweep spans 64 crates with 29 `--release` steps, so compilation is the likely dominant cost; whole-crate -> named conversion (#343/#262) is right for traceability but, measured, will not close a 30-minute gap on its own (the non-falcon-core offenders run in 0-5s). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HvusAXYbHLyv3uTzfBcMbG
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The bug this closes
create-releasepublished the GitHub Release beforeflight-componentran. On falcon-v1.131.0 that job failed (missing rustc target) and the release was already public — 12 assets, permanently no wasm components. Unrepairable without moving a tag or cutting a new version (v1.131.1).The #140 wasm-asset guard couldn't help: it lives inside the later job, downstream of the publish. The gate was downstream of the damage.
The fix
Create with
--draft;flight-componentflips it to published as its final step — after the bundle is built, signed and attached, after the guard passes, and after the OCI push. A draft is invisible to consumers, so the failure mode becomes "no release" instead of "half a release".Deliberately not
if: always()and notcontinue-on-error: if anything earlier in the job fails, the job stops before the flip and the release stays a draft — that is the mechanism. The OCI push itself stayscontinue-on-error, so a registry hiccup still publishes (it must never fail a release) while a missing component does not.The finalize step also re-asserts the guard's property against the live release rather than trusting an earlier step in the same job, and re-validates the tag shape before it reaches any shell command.
REL-P01's criteria require deliberate failure injection: break the component build on a scratch tag, confirm no published release survives. Reasoning about a workflow is exactly how the v1.131.0 ordering bug went unnoticed in the first place, so I'm not claiming this verified from the diff.
That injection creates a visible tag and draft release in the repo, so it's proposed rather than done unilaterally. The happy path (draft → published) will be exercised by the next real release; the failure path still needs injecting.
The requirement stays
proposed. Merging this is safe — worst case the behaviour is unchanged-but-drafted — but it isn't verified until injected.Refs:
SWREQ-FALCON-REL-P01, #333, v1.131.0/v1.131.1.🤖 Generated with Claude Code