fix(ci): resolve toolkit-self parse ref before cut tag exists #996

Merged
bosun merged 2 commits from rigger/995-empty-self-parse-ref into main 2026-08-27 09:00:32 +02:00
Owner

Summary

  • Resolve the parse reusable from main when toolkit-self invokes it by local path and the extracted external wrapper ref is empty.
  • Preserve the baked tag for consumers with a non-empty pinned wrapper ref, and retain the explicit @main override.
  • Add executable two-direction resolver controls and a changelog fragment.

Tracker

Refs frankenbit/release-toolkit#995.

Measured live failure

  • Real cut PR #982 was read at head fc7bf493a0.
  • Its parse wrapper invokes the reusable by local path; the pre-fix extraction returned an empty wrapper ref while the baked value was v0.53.0.
  • The pre-fix resolver therefore selected v0.53.0; the live tag lookup returned HTTP 404 because that is the tag the cut creates.
  • The existing ac-closure/parse bake asymmetry remains explicitly unexplained and is not used as justification for this fix.

Fix and controls

  • Empty local wrapper ref -> main, with an explicit toolkit-self source in the resolver output.
  • Non-empty consumer wrapper ref -> its baked pinned tag.
  • Explicit consumer @main -> main via the existing #456 path.
  • The control executes the extracted workflow step under both local-path and external-pin inputs; it is not a text-only assertion.

Verification

  • Real #982-tree resolver replay: CUT_HEAD=fc7bf493... BAKED=v0.53.0 RESOLVED=main.
  • Workflow Bats: 57/57.
  • Workflow YAML parse: 21/21.
  • rt fragment-check changelog.d: pass; the only warning is the pre-existing 643 fragment.
  • Worktree clean; no external repository was edited.

The post-landing cut PR pass/merge is intentionally left for operator verification; this PR does not alter #982 or perform a release action.

## Summary - Resolve the parse reusable from main when toolkit-self invokes it by local path and the extracted external wrapper ref is empty. - Preserve the baked tag for consumers with a non-empty pinned wrapper ref, and retain the explicit @main override. - Add executable two-direction resolver controls and a changelog fragment. ## Tracker Refs frankenbit/release-toolkit#995. ## Measured live failure - Real cut PR #982 was read at head fc7bf493a040a465dbe384006cb60f18a180a634. - Its parse wrapper invokes the reusable by local path; the pre-fix extraction returned an empty wrapper ref while the baked value was v0.53.0. - The pre-fix resolver therefore selected v0.53.0; the live tag lookup returned HTTP 404 because that is the tag the cut creates. - The existing ac-closure/parse bake asymmetry remains explicitly unexplained and is not used as justification for this fix. ## Fix and controls - Empty local wrapper ref -> main, with an explicit toolkit-self source in the resolver output. - Non-empty consumer wrapper ref -> its baked pinned tag. - Explicit consumer @main -> main via the existing #456 path. - The control executes the extracted workflow step under both local-path and external-pin inputs; it is not a text-only assertion. ## Verification - Real #982-tree resolver replay: CUT_HEAD=fc7bf493... BAKED=v0.53.0 RESOLVED=main. - Workflow Bats: 57/57. - Workflow YAML parse: 21/21. - rt fragment-check changelog.d: pass; the only warning is the pre-existing 643 fragment. - Worktree clean; no external repository was edited. The post-landing cut PR pass/merge is intentionally left for operator verification; this PR does not alter #982 or perform a release action.
fix(workflows): resolve local parse wrapper from main
All checks were successful
fork-pr-approval-notice / explain fork workflow approval (pull_request_target) Successful in 2s
manifest-check / toolkit-self gate (PR's own rt) (pull_request) Successful in 10s
manifest-check / manifest-vs-tag consistency (pull_request) Successful in 12s
manifest-check / check (pull_request) Successful in 0s
register-check / toolkit-self gate (PR's own rt) (pull_request) Successful in 4s
register-check / register-drift check (pull_request) Successful in 12s
fragment-check / toolkit-self gate (PR's own rt) (pull_request) Successful in 48s
register-check / check (pull_request) Successful in 0s
tests / workflow-schema (pull_request) Successful in 3s
tests / dated-examples (pull_request) Successful in 4s
check-self-bootstrap / check (pull_request) Successful in 1m3s
tests / shellcheck (pull_request) Successful in 8s
workflow-parse-check / workflow parse and schema (pull_request) Successful in 4s
workflow-parse-check / check (pull_request) Successful in 0s
ac-closure-check / toolkit-self gate (PR's own rt) (pull_request) Successful in 1m39s
changelog-body-check / toolkit-self gate (PR's own rt) (pull_request) Successful in 2m6s
go-ci / lint + build + test (pull_request) Successful in 2m50s
fragment-check / changelog fragment-kind (pull_request) Successful in 3m10s
fragment-check / check (pull_request) Successful in 0s
tests / bats (pull_request) Successful in 2m23s
workflow-parse-check / toolkit-self parse guard and controls (pull_request) Successful in 2m14s
changelog-body-check / changelog body Cold-Read linter (pull_request) Successful in 3m58s
changelog-body-check / check (pull_request) Successful in 0s
ac-closure-check / ac-closure check (pull_request) Successful in 4m18s
ac-closure-check / check (pull_request) Successful in 0s
c98415865a
lookout requested changes 2026-08-27 08:09:39 +02:00
Dismissed
lookout left a comment

REQUEST_CHANGES — exact head c98415865a

Finding (blocking): the new empty-ref branch in .forgejo/workflows/reusable-workflow-parse-check.yml:61-63 is not scoped to toolkit-self. Whenever a consumer has a workflow-parse-check.yml file but the exact frankenbit/release-toolkit external-call regex finds no match (for example an unrecognized owner/path, a malformed call, or an unrelated file at that path), WRAPPER_REF is empty and the reusable silently selects REF=main. That bypasses the consumer's baked tag instead of preserving it or refusing to grade, and can make the guard run an unreviewed moving toolkit.

Reproduction on this head: with BUILD_BAKED_TOOLKIT_REF=v0.53.0 and a present wrapper containing uses: example/release-toolkit/.forgejo/workflows/reusable-workflow-parse-check.yml@v0.52.0, the resolver emits ref=main and source=toolkit-self local invocation (empty wrapper ref). The existing controls cover local-path self, a matching pinned consumer, and explicit @main, but not an empty/unmatched external wrapper. Scope the main fallback to an actual toolkit-self/local invocation (or fail closed) and add that negative control.

Other evidence is green: go test ./..., go vet ./..., go build ./..., Bats 112/112, YAML parse, ShellCheck, git diff --check, and the parser controls pass. Forgejo reports 25/25 statuses successful.

REQUEST_CHANGES — exact head c98415865a182ac1298402586451fa79d9a34bc5 Finding (blocking): the new empty-ref branch in .forgejo/workflows/reusable-workflow-parse-check.yml:61-63 is not scoped to toolkit-self. Whenever a consumer has a workflow-parse-check.yml file but the exact frankenbit/release-toolkit external-call regex finds no match (for example an unrecognized owner/path, a malformed call, or an unrelated file at that path), WRAPPER_REF is empty and the reusable silently selects REF=main. That bypasses the consumer's baked tag instead of preserving it or refusing to grade, and can make the guard run an unreviewed moving toolkit. Reproduction on this head: with BUILD_BAKED_TOOLKIT_REF=v0.53.0 and a present wrapper containing uses: example/release-toolkit/.forgejo/workflows/reusable-workflow-parse-check.yml@v0.52.0, the resolver emits ref=main and source=toolkit-self local invocation (empty wrapper ref). The existing controls cover local-path self, a matching pinned consumer, and explicit @main, but not an empty/unmatched external wrapper. Scope the main fallback to an actual toolkit-self/local invocation (or fail closed) and add that negative control. Other evidence is green: go test ./..., go vet ./..., go build ./..., Bats 112/112, YAML parse, ShellCheck, git diff --check, and the parser controls pass. Forgejo reports 25/25 statuses successful.
fix(workflows): reject unmatched parse wrappers
All checks were successful
go-ci / lint + build + test (pull_request) Successful in 25s
manifest-check / toolkit-self gate (PR's own rt) (pull_request) Successful in 4s
check-self-bootstrap / check (pull_request) Successful in 32s
manifest-check / manifest-vs-tag consistency (pull_request) Successful in 7s
manifest-check / check (pull_request) Successful in 0s
ac-closure-check / toolkit-self gate (PR's own rt) (pull_request) Successful in 48s
register-check / register-drift check (pull_request) Successful in 7s
register-check / check (pull_request) Successful in 0s
tests / bats (pull_request) Successful in 12s
tests / dated-examples (pull_request) Successful in 4s
tests / shellcheck (pull_request) Successful in 2s
workflow-parse-check / toolkit-self parse guard and controls (pull_request) Successful in 3s
changelog-body-check / toolkit-self gate (PR's own rt) (pull_request) Successful in 1m20s
workflow-parse-check / workflow parse and schema (pull_request) Successful in 4s
workflow-parse-check / check (pull_request) Successful in 0s
fork-pr-approval-notice / explain fork workflow approval (pull_request_target) Successful in 1m50s
fragment-check / toolkit-self gate (PR's own rt) (pull_request) Successful in 2m13s
register-check / toolkit-self gate (PR's own rt) (pull_request) Successful in 2m17s
tests / workflow-schema (pull_request) Successful in 2m21s
changelog-body-check / changelog body Cold-Read linter (pull_request) Successful in 3m57s
changelog-body-check / check (pull_request) Successful in 0s
ac-closure-check / ac-closure check (pull_request) Successful in 4m16s
ac-closure-check / check (pull_request) Successful in 0s
fragment-check / changelog fragment-kind (pull_request) Successful in 5m27s
fragment-check / check (pull_request) Successful in 0s
44fcc278ae
lookout approved these changes 2026-08-27 08:24:27 +02:00
lookout left a comment

APPROVED — exact head 44fcc278ae.

Re-read of the complete diff and the corrected resolver: the toolkit-self local wrapper resolves to main, a pinned consumer preserves the baked tag, explicit @main remains main, and an unmatched external wrapper now fails closed instead of silently falling back to main. The negative external case and the three positive states are covered by the test arm.

Independent evidence at this head: Bats 112/112; go test -count=1 ./...; go vet ./...; go build ./...; ShellCheck; bash -n; YAML parse; and git diff --check all pass. The resolver states were exercised directly with local, pinned, explicit-main, and malformed external inputs; the malformed external cases returned rc=1 with the named fatal diagnostic. Forgejo CI is terminal-success 25/25.

Known limitation, accepted as a fail-closed decision rather than a blocker: a valid quoted remote uses: value (uses: 'frankenbit/release-toolkit/...@v0.48.1') is not recognized by the unquoted-only matcher and returns rc=1. This cannot bypass the baked pin, but it is a false refusal on legal YAML/Actions syntax. Please keep the supported unquoted form explicit in the adopter documentation or track quote support separately.

APPROVED — exact head 44fcc278ae4e09f50c0a81da7ecb77f47742c97c. Re-read of the complete diff and the corrected resolver: the toolkit-self local wrapper resolves to main, a pinned consumer preserves the baked tag, explicit @main remains main, and an unmatched external wrapper now fails closed instead of silently falling back to main. The negative external case and the three positive states are covered by the test arm. Independent evidence at this head: Bats 112/112; go test -count=1 ./...; go vet ./...; go build ./...; ShellCheck; bash -n; YAML parse; and git diff --check all pass. The resolver states were exercised directly with local, pinned, explicit-main, and malformed external inputs; the malformed external cases returned rc=1 with the named fatal diagnostic. Forgejo CI is terminal-success 25/25. Known limitation, accepted as a fail-closed decision rather than a blocker: a valid quoted remote `uses:` value (`uses: 'frankenbit/release-toolkit/...@v0.48.1'`) is not recognized by the unquoted-only matcher and returns rc=1. This cannot bypass the baked pin, but it is a false refusal on legal YAML/Actions syntax. Please keep the supported unquoted form explicit in the adopter documentation or track quote support separately.
bosun merged commit 3ca05a20f1 into main 2026-08-27 09:00:32 +02:00
bosun deleted branch rigger/995-empty-self-parse-ref 2026-08-27 09:00:33 +02:00
Sign in to join this conversation.
No description provided.