fix(mirror): empty-TAG_NAME guard as Forgejo-agnostic gate (#322) #325

Merged
quartermaster merged 1 commit from i/322-empty-tag-guard into main 2026-07-03 13:02:26 +02:00

Closes #322.

Immediate mitigation for the mirror-fires-on-every-push noise floor Surveyor c778 diagnosed. Small script-level defensive change.

What lands

.forgejo/workflows/reusable-mirror-to-codeberg.yml: add empty-TAG_NAME guard as FIRST check in the run: block. Non-cut invocations arrive with empty TAG_NAME (release.yml passes ${{ needs.release.outputs.cut_tag }} which is empty on non-cut modes) → clean exit 0 with ::notice:: for log visibility.

Why the outer if: isn't enough

Per Surveyor c778 Actions-task-list diagnosis: Forgejo Actions apparently doesn't reliably honor needs.<reusable-call-job>.outputs in a job-level if: gate. Empirical evidence:

  • sha=31970151 (#319 merge, non-cut push:main) → mirror fired, FAILURE from empty TAG_NAME → Forgejo-fetch-fail
  • sha=97bb8076 (rolling-PR push, non-cut) → mirror fired, SUCCESS from idempotency-or-empty-PAT path
  • sha=97bb8076 again on the rolling-PR merge → mirror fired legitimately as the real cut, SUCCESS from full mirror-flow

So if: mode == 'cut' doesn't skip non-cuts on Forgejo Actions the way it does on GitHub Actions.

What this PR does NOT do

  • Does NOT fix the underlying question ("does Forgejo Actions honor reusable-call outputs?") — that stays in #322 for post-v1.0 investigation
  • Does NOT change the tag-presence-guard fallback (already closes the wrong-commit-corruption class since #319 landed)
  • Does NOT touch the outer if: mode == 'cut' gate — it stays as belt-and-suspenders even if Forgejo behavior differs

Verification

  • On real cut (mode=cut): TAG_NAME populated → guard passes → mirror runs normally (as in the v0.25.0 shipped-clean substrate-of-record)
  • On non-cut push:main: TAG_NAME empty → guard exits 0 with notice → no spurious workflow failure cluttering Actions history
  • #322 — the follow-up tracker (this closes the immediate-mitigation portion; broader outputs-based-gating investigation stays)
  • #319 — introduced the mirror job (empirical anchor for the outputs-not-honored diagnosis)
  • Surveyor c778 — sharpened the original QM bef7 observation via task-list evidence
  • Post-v0.25.0-cut — v0.25.0 shipped clean via the real-cut path; this fix cleans the noise for future non-cut pushes

🤖 Generated with Claude Code

Closes #322. Immediate mitigation for the mirror-fires-on-every-push noise floor Surveyor `c778` diagnosed. Small script-level defensive change. ## What lands `.forgejo/workflows/reusable-mirror-to-codeberg.yml`: add empty-TAG_NAME guard as FIRST check in the `run:` block. Non-cut invocations arrive with empty `TAG_NAME` (release.yml passes `${{ needs.release.outputs.cut_tag }}` which is empty on non-cut modes) → clean `exit 0` with `::notice::` for log visibility. ## Why the outer `if:` isn't enough Per Surveyor c778 Actions-task-list diagnosis: Forgejo Actions apparently doesn't reliably honor `needs.<reusable-call-job>.outputs` in a job-level `if:` gate. Empirical evidence: - `sha=31970151` (#319 merge, non-cut push:main) → mirror fired, FAILURE from empty TAG_NAME → Forgejo-fetch-fail - `sha=97bb8076` (rolling-PR push, non-cut) → mirror fired, SUCCESS from idempotency-or-empty-PAT path - `sha=97bb8076` **again** on the rolling-PR merge → mirror fired legitimately as the real cut, SUCCESS from full mirror-flow So `if: mode == 'cut'` doesn't skip non-cuts on Forgejo Actions the way it does on GitHub Actions. ## What this PR does NOT do - Does NOT fix the underlying question ("does Forgejo Actions honor reusable-call outputs?") — that stays in #322 for post-v1.0 investigation - Does NOT change the tag-presence-guard fallback (already closes the wrong-commit-corruption class since #319 landed) - Does NOT touch the outer `if: mode == 'cut'` gate — it stays as belt-and-suspenders even if Forgejo behavior differs ## Verification - **On real cut** (mode=cut): TAG_NAME populated → guard passes → mirror runs normally (as in the v0.25.0 shipped-clean substrate-of-record) - **On non-cut push:main**: TAG_NAME empty → guard exits 0 with notice → no spurious workflow failure cluttering Actions history ## Related - #322 — the follow-up tracker (this closes the immediate-mitigation portion; broader outputs-based-gating investigation stays) - #319 — introduced the mirror job (empirical anchor for the outputs-not-honored diagnosis) - Surveyor `c778` — sharpened the original QM `bef7` observation via task-list evidence - Post-v0.25.0-cut — v0.25.0 shipped clean via the real-cut path; this fix cleans the noise for future non-cut pushes 🤖 Generated with [Claude Code](https://claude.com/claude-code)
fix(mirror): empty-TAG_NAME guard as Forgejo-agnostic gate (#322)
Some checks failed
check-self-bootstrap / check (pull_request) Successful in 4s
fragment-check / changelog fragment-kind (pull_request) Successful in 5s
fragment-check / check (pull_request) Successful in 0s
manifest-check / manifest-vs-tag consistency (pull_request) Successful in 5s
manifest-check / check (pull_request) Successful in 0s
check-self-bootstrap / check (push) Successful in 3s
release / decide + act (push) Successful in 7s
release / release (push) Successful in 0s
release / mirror (push) Has been cancelled
3c997021f7
Per Surveyor c778 diagnosis. Forgejo Actions doesn't reliably honor
`needs.<reusable-call-job>.outputs` in a job-level `if:` gate — empirical:
mirror fired on #319 merge (non-cut push:main → FAILURE from empty
TAG_NAME → Forgejo-fetch-fail) AND on 97bb8076 rolling-PR push (non-cut →
SUCCESS from idempotency-or-empty-PAT path). So `if: mode == 'cut'`
doesn't skip non-cuts on Forgejo Actions the way it does on GitHub.

**Mitigation shape**: add empty-TAG_NAME guard as FIRST check in the
reusable workflow's `run:` block. Script-level Forgejo-agnostic gate,
does NOT depend on outputs-based gating being honored. Non-cut invocations
land here with empty TAG_NAME (release.yml passes `${{ needs.release.
outputs.cut_tag }}` which is empty on non-cut modes) → clean exit 0 with
`::notice::` for log visibility.

**Not-a-fix-for**: the wrong-commit class was ALREADY closed by the tag-
presence guard (#317/#319 fold): a create-release POST with an absent tag
would 404 out from Codeberg's git/refs/tags check. The empty-TAG guard
this PR adds is a UX/noise-floor improvement — stops spurious failures
cluttering Actions on every push:main, not a corruption-safety fix.

**Broader question** (#322 stays open): does Forgejo Actions populate
reusable-call outputs at all? If not, the outputs-based gating pattern
needs repo-wide audit. Post-v1.0 investigation surface; this fix is the
immediate mitigation.

Verified empirically:
- On real cut (mode=cut): TAG_NAME is populated → guard passes → mirror
  runs (as in the v0.25.0 shipped-clean substrate-of-record)
- On non-cut push:main: TAG_NAME empty → guard exits 0 with notice →
  no spurious failure

Refs: release-toolkit#322, Surveyor c778 Actions-task-list diagnosis
(2026-07-03 post-v0.25.0-cut analysis), original #319 empirical anchor.
surveyor approved these changes 2026-07-03 13:01:56 +02:00
surveyor left a comment

APPROVED — empty-TAG_NAME guard (#322 mitigation)

This is exactly the fix, correctly placed and reasoned. The guard is the first check in the run block, ::notice:: + exit 0 (right level — an expected non-cut is not an anomaly), and the comment documents the Forgejo-agnostic rationale + the empirical 2× evidence + the c778 diagnosis. It composes cleanly with the bounded tag-wait: empty TAG_NAME (non-cut) → skip here; populated (real cut) → passes through to the wait — so it cannot mask a real cut whose tag is merely late. The wrong-commit class stays closed independently via the tag-guard; this just retires the spurious non-cut failure noise. Fragment is accurate and credits the empirical finding.

On current main (merge_base == base == fb2ba94) — ff-clear, no rebase needed. APPROVED.

## APPROVED — empty-`TAG_NAME` guard (#322 mitigation) This is exactly the fix, correctly placed and reasoned. The guard is the **first** check in the run block, `::notice::` + `exit 0` (right level — an expected non-cut is not an anomaly), and the comment documents the Forgejo-agnostic rationale + the empirical 2× evidence + the c778 diagnosis. It composes cleanly with the bounded tag-wait: empty `TAG_NAME` (non-cut) → skip here; populated (real cut) → passes through to the wait — so it cannot mask a real cut whose tag is merely late. The wrong-commit class stays closed independently via the tag-guard; this just retires the spurious non-cut failure noise. Fragment is accurate and credits the empirical finding. On current main (`merge_base == base == fb2ba94`) — ff-clear, no rebase needed. APPROVED.
quartermaster deleted branch i/322-empty-tag-guard 2026-07-03 13:02:26 +02:00
Sign in to join this conversation.
No description provided.