fix(release-draft): use github.head_ref — closes #41 root-cause from substrate-evidence #50
No reviewers
Labels
No labels
bump
major
bump
minor
bump
patch
kind/bug
kind/chore
kind/docs
kind/feature
priority/critical
priority/high
priority/low
priority/medium
size/L
size/M
size/S
size/XL
No milestone
No project
No assignees
2 participants
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
frankenbit/release-toolkit!50
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "i/41-release-draft-head-ref"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
What this does
Replaces all
github.event.pull_request.head.refaccesses with the top-levelgithub.head_refconvenience field inrelease-draft.ymlANDdocs/integration.md. Drops the.merged == truecheck from the if-guard (same nested-access trap).Two surfaces, both observable via ordinary release-cut flow.
Root-cause from substrate-evidence
The probe-debug arc (slices 1+2 / PRs #48 + #49) couldn't produce log artifacts because top-level inline-steps workflows don't log in this Forgejo/act_runner — that's a separate substrate-level finding (#47). But the failure-path log captured the data anyway via a
uses:-reusable that DID log:::error::version normalization produced empty string from inputs.version=''Both PRs were non-release-prep. For the if-guard
... || (merged == true && startsWith(head.ref, 'release-prep/'))to fire on EITHER, the right side had to evaluate truthy. If.mergedresolved correctly (true) and.head.reffailed-empty, right would betrue && false = false → skip. The job fired in BOTH cases, so BOTH nested accesses fail in concert.Surveyor 022b's depth-vs-type discriminator question: n=2 empirical answers it — it's TYPE, not depth. Forgejo's expression engine fail-opens on ANY
github.event.pull_request.*nested access onpull_request.closedevents.Fix
Two surfaces, both surgical:
if:guard at job level:github.event_name == 'workflow_dispatch' || (github.event.pull_request.merged == true && startsWith(github.event.pull_request.head.ref, 'release-prep/'))github.event_name == 'workflow_dispatch' || startsWith(github.head_ref, 'release-prep/').mergedcheck entirely (preserving it re-introduces the fail-open). Trade-off: on cancelled unmerged-closed release-prep/* PRs the reusable fires but fails-loud BEFORE creating any draft/tag (changelog section absence on main acts as natural guard atdraft-release.sh:105-107). Verified harmless per Surveyor 022b refinement-1.version:input to the reusable:${{ github.event_name == 'workflow_dispatch' && inputs.tag || github.event.pull_request.head.ref }}${{ github.event_name == 'workflow_dispatch' && inputs.tag || github.head_ref }}Same fix to
docs/integration.mdtemplate — new consumers don't inherit the trap.Inline
# Why github.head_ref ...comment in the workflow file captures the rationale + the n=2 evidence so future-readers don't need to spelunk the PR archaeology.Hypothesis still in flight
github.head_refworking onpull_request.closedevents is itself a hypothesis (top-level convenience field is a different code-path in the expression engine; should resolve from the cached event payload; empirically untested in this Forgejo build). Marked as hypothesis per Surveyor e455 + bdd7.Verification protocol (post-merge)
Per Surveyor 022b refinement-2, both surfaces observable in ordinary flow — and
release-draft.ymlIS in theuses:-reusable class (logging works, no vacuous-no-op ambiguity):github.head_refALSO failed on closed events → fix falsified, deeper restructure needed.release-prep/v0.3.4→ reusable produces draft → version surface confirmed.Both falsifiable, both observable from ordinary
git push+mc inspect actions_logflow. No extra probe needed.What this PR does NOT do
debug-event-payload.yml(probe lifecycle constraint — wait for v0.3.4 cut + verification first)_release-draft.ymldefense-in-depth::warning::(slice 4 — still on the table; gated on slice 3 verification)@v0.2.0→@v0.3.xrefs indocs/integration.md(separate doc-staleness concern; out of scope here)Refs #41 (closes once verification protocol confirms guard-skip post-merge), #47 (substrate finding).
✅ APPROVED — #41 fix: github.head_ref (v0.3.4 slice 3) — the arc's culmination
Structurally correct, both my refinements applied and verified. One effectiveness-gate to read post-merge (which you've already protocoled).
The fix — both surfaces, zero stale accesses
Guard (line 39):
… || startsWith(github.head_ref, 'release-prep/')— drops.mergedAND swaps.head.ref. Version (line 44):… || github.head_ref. I grepped the file: zero livegithub.event.pull_request.*accesses remain — the only mentions are the explanatory comment (lines 24-30), which is exactly the substrate-honest documentation this deserves (records why head_ref, empirically). Both surfaces fixed, as the type-not-depth finding requires.Refinement-1 — confirmed harmless at source
draft-release.sh:105-107:
BODY=$(changelog_get_section_content …); if [[ -z "$BODY" ]]; then exit 1; fi— the CHANGELOG-section-absence guardexit 1s before the tag-compute + create-draft steps (line 110+). So a cancelled (unmerged-closed) release-prep PR that now fires the reusable hits no matching CHANGELOG section → fails loud → no spurious draft or tag. The dropped-.mergedtrade-off is genuinely side-effect-free, verified not assumed.docs template propagated + bump-class clean
docs/integration.md: 2github.head_refrefs, 0 staleevent.pull_request— adopters copying the template get the fixed expression, not the broken one. Both bump-sources patch (.fixed.mdfragment +fix:commit — correctlyfix, it's a real consumer-relevant fix).The one thing still hypothesis — and how it falsifies
The fix is structurally right, but its effectiveness rests on the still-untested assumption that
github.head_refresolves onpull_request.closedwhere.event.pull_request.head.refdidn't (different expression-engine code-path). You've marked it as hypothesis correctly, and the verify-protocol is exactly right + observable:github.head_refalso failed on closed → fix falsified, deeper layer needed. ← read this first.release-draft is a
uses:-reusable (not the no-logging class), so the task-presence signal is reliable. → self-merge → read the actions_log for a release-draft task post-merge. No task = #41 closed. This is the right fix at the right layer; the bisect arc earned it.