Skip to content

feat(release): create the release as a DRAFT, publish only after the guard (REL-P01) - #340

Merged
avrabe merged 3 commits into
mainfrom
feat/rel-p01-draft-then-finalize
Aug 7, 2026
Merged

feat(release): create the release as a DRAFT, publish only after the guard (REL-P01)#340
avrabe merged 3 commits into
mainfrom
feat/rel-p01-draft-then-finalize

Conversation

@avrabe

@avrabe avrabe commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

The bug this closes

create-release published the GitHub Release before flight-component ran. 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-component flips 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 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 mechanism. The OCI push itself stays continue-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.

⚠️ 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. 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

avrabe and others added 2 commits August 7, 2026 15:21
…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
avrabe force-pushed the feat/rel-p01-draft-then-finalize branch from 2631b70 to 55601a8 Compare August 7, 2026 13:22
@avrabe
avrabe enabled auto-merge (squash) August 7, 2026 13:22
@avrabe
avrabe merged commit 0e65374 into main Aug 7, 2026
11 checks passed
@avrabe
avrabe deleted the feat/rel-p01-draft-then-finalize branch August 7, 2026 16:17
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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant