v0.2.0 — runs-on parameterization + fragment newline normalization #3

Closed
opened 2026-06-24 15:58:47 +02:00 by quartermaster · 1 comment

Scope

Two fixes surfaced post-v0.1.0 cut, both blocking real-consumer adoption. Per operator + Bosun (α) ratification: cut v0.1.1 properly via toolkit's own machinery rather than force-moving the published v0.1.0 tag.

Version-label settle: original framing was v0.1.1 (patch); operator α-settled to v0.2.0 (minor) per SemVer-precedent discussion — adding a required runs_on input is structurally a breaking change for v0.1.0 consumers, even though "patch avoids removing or renaming" doesn't explicitly cover required-input additions. AC text below preserves the original v0.1.1 phrasing for substrate-of-record-integrity; ticks annotate the actual shipping version where relevant.

Fix 1: runs-on parameterization

The 4 reusable workflows (_release-prep.yml / _release-draft.yml / _release-publish.yml / _manifest-check.yml) hard-code runs-on: docker. The alcatraz Forgejo runner config has no docker label — available labels are dotnet / godot / python / go / playwright / squadron-test / docker-build / squadron-deploy. The reusables would never schedule.

Surfaced when starting cellblock greenfield adoption (cellblock#162). My oversight at authoring-time; should have grepped the runner config before specifying.

Change: add runs_on as a workflow_call input (required: true, no default) to each reusable. Consumers must specify a label that exists in their Forgejo's runner config. Workflows reference it via runs-on: ${{ inputs.runs_on }}.

This IS a breaking change for any consumer pinning to @v0.1.0 — but no consumers exist yet so the practical blast radius is zero. Documented in CHANGELOG as a ### Fixed entry; semantic note in docs/integration.md that consumers MUST specify runs_on.

Fix 2: Tool-side fragment newline normalization

Per operator's hard-wrap framing during v0.1.0 cut: hard-wrapped fragment bullets render with literal line breaks through some pipelines (GFM-soft-break-as-<br> renderers, RSS/email/notifications, plain-text copy-paste). v0.1.0 ships with the source-side convention documented in docs/conventions.md; v0.1.1 adds defensive tool-side normalization.

Change: scripts/lib/fragments.sh::categorize_fragments collapses single-newline-within-paragraph runs to spaces while preserving:

  • Blank-line paragraph separators (preserved as-is)
  • Indented sublists (preserved indentation; lines starting with keep their newline)
  • Fenced code blocks (don't normalize anything inside ``` fences)

Consumers who hard-wrap by accident get the right output anyway; the source-side convention becomes defensive rather than just advisory.

Self-cut via toolkit's own reusables (deferred from v0.1.0 §ACs)

v0.1.0 was cut via local script invocation (bootstrap chicken-and-egg). v0.1.1 will add the toolkit's own .forgejo/workflows/release.yml + release-draft.yml + manifest-check.yml referencing toolkit @v0.1.1 (anticipating the cut). v0.1.1 itself uses local-script-cut one more time; v0.1.2+ fully dogfoods.

Note: requires the runs-on fix from Fix 1 to land FIRST in the cut content (i.e., v0.1.1's released reusables have the parameterization).

Acceptance criteria

  • 4 reusables: add runs_on workflow_call input + reference ${{ inputs.runs_on }} in jobs — verified all 4 of _release-prep.yml + _release-draft.yml + _release-publish.yml + _manifest-check.yml reference ${{ inputs.runs_on }} (PR #5, commit 64adc7b)
  • Workflow schema tests (tests/workflows.bats) updated to verify runs_on is a required input — verified 10 runs_on mentions in workflows.bats including the schema-regression test (PR #5)
  • fragments.sh::categorize_fragments collapses single-newlines-in-paragraph to spaces — verified _normalize_paragraph_continuations awk state machine + categorize_fragments pipes each fragment through it (PR #5, commit f490312)
  • New unit tests in tests/fragments.bats for the normalization (preserve paragraphs / preserve indented sublists / preserve fenced code blocks) — verified 7 @test "normalize: ..." cases (PR #5)
  • changelog.d/<id>.fixed.md fragments documenting both fixes — both shipped + consumed at cut; content now in CHANGELOG.md v0.2.0 ### Fixed section (note: 3.fixed-2.md had to be manually folded into prep PR due to parser bug filed as release-toolkit#9)
  • docs/integration.md updated with the runs_on parameter note + the convention move from "advisory" to "defensive" — verified "About the runs_on parameter" subsection + "Defensive from v0.2.0" framing in conventions.md (PR #5)
  • Toolkit's own .forgejo/workflows/release.yml + release-draft.yml + manifest-check.yml added (consumer-side; references toolkit @v0.1.1 — shipped as @v0.2.0 per α settle) — verified all 3 consumer-shape workflows on main (PR #6 merged)
  • v0.1.1 cut via local script invocation — shipped as v0.2.0 per α settle; cut via scripts/release-prep.sh --bump minor (PR #8 release-prep merged at a521998)
  • v0.1.1 published — shipped as v0.2.0; verified release id 110: draft=false, tag=v0.2.0, published_at=2026-06-24T16:55:53+02:00; tag canonically present in git tag -l 'v*'

Cross-tracker impact

Unblocks:

  • cellblock#162 consumer-workflows follow-up PR
  • (Partial) cellblock#157 — closes when cellblock's manifest-check.yml workflow lands referencing @v0.2.0

Doesn't fully unblock tmux-tell#617 (5 remaining gap items deferred to v0.3 — see release-toolkit#4).

v0.2.x scope surfaced during the cut + filed for v0.2.1:

  • release-toolkit#9 (fragments parser silent-drop on hyphenated id) — caught during the v0.2.0 cut; manually folded
  • release-toolkit#10 (install-deps step assumes sudo) — caught during PR #6 CI failure; alcatraz-infra commit 7715b14 baked yq into ci-go as immediate unblock

— QM, 2026-06-24, per operator α + Bosun 8cb5 ratification. All ACs canonically probed against main 2026-06-24 18:08 + ticked; release-toolkit v0.2.0 shipped + published.

## Scope Two fixes surfaced post-v0.1.0 cut, both blocking real-consumer adoption. Per operator + Bosun (α) ratification: cut v0.1.1 properly via toolkit's own machinery rather than force-moving the published v0.1.0 tag. **Version-label settle**: original framing was v0.1.1 (patch); operator α-settled to **v0.2.0 (minor)** per SemVer-precedent discussion — adding a required `runs_on` input is structurally a breaking change for v0.1.0 consumers, even though "patch avoids removing or renaming" doesn't explicitly cover required-input additions. AC text below preserves the original v0.1.1 phrasing for substrate-of-record-integrity; ticks annotate the actual shipping version where relevant. ## Fix 1: `runs-on` parameterization The 4 reusable workflows (`_release-prep.yml` / `_release-draft.yml` / `_release-publish.yml` / `_manifest-check.yml`) hard-code `runs-on: docker`. The alcatraz Forgejo runner config has no `docker` label — available labels are `dotnet / godot / python / go / playwright / squadron-test / docker-build / squadron-deploy`. The reusables would never schedule. Surfaced when starting cellblock greenfield adoption (cellblock#162). My oversight at authoring-time; should have grepped the runner config before specifying. **Change**: add `runs_on` as a `workflow_call` input (`required: true`, no default) to each reusable. Consumers must specify a label that exists in their Forgejo's runner config. Workflows reference it via `runs-on: ${{ inputs.runs_on }}`. This IS a breaking change for any consumer pinning to `@v0.1.0` — but no consumers exist yet so the practical blast radius is zero. Documented in CHANGELOG as a `### Fixed` entry; semantic note in `docs/integration.md` that consumers MUST specify `runs_on`. ## Fix 2: Tool-side fragment newline normalization Per operator's hard-wrap framing during v0.1.0 cut: hard-wrapped fragment bullets render with literal line breaks through some pipelines (GFM-soft-break-as-`<br>` renderers, RSS/email/notifications, plain-text copy-paste). v0.1.0 ships with the source-side convention documented in `docs/conventions.md`; v0.1.1 adds defensive tool-side normalization. **Change**: `scripts/lib/fragments.sh::categorize_fragments` collapses single-newline-within-paragraph runs to spaces while preserving: - Blank-line paragraph separators (preserved as-is) - Indented sublists (preserved indentation; lines starting with ` ` keep their newline) - Fenced code blocks (don't normalize anything inside ``` fences) Consumers who hard-wrap by accident get the right output anyway; the source-side convention becomes defensive rather than just advisory. ## Self-cut via toolkit's own reusables (deferred from v0.1.0 §ACs) v0.1.0 was cut via local script invocation (bootstrap chicken-and-egg). v0.1.1 will add the toolkit's own `.forgejo/workflows/release.yml` + `release-draft.yml` + `manifest-check.yml` referencing toolkit `@v0.1.1` (anticipating the cut). v0.1.1 itself uses local-script-cut one more time; v0.1.2+ fully dogfoods. Note: requires the runs-on fix from Fix 1 to land FIRST in the cut content (i.e., v0.1.1's released reusables have the parameterization). ## Acceptance criteria - [x] 4 reusables: add `runs_on` workflow_call input + reference `${{ inputs.runs_on }}` in jobs — verified all 4 of `_release-prep.yml` + `_release-draft.yml` + `_release-publish.yml` + `_manifest-check.yml` reference `${{ inputs.runs_on }}` (PR #5, commit 64adc7b) - [x] Workflow schema tests (`tests/workflows.bats`) updated to verify `runs_on` is a required input — verified 10 `runs_on` mentions in workflows.bats including the schema-regression test (PR #5) - [x] `fragments.sh::categorize_fragments` collapses single-newlines-in-paragraph to spaces — verified `_normalize_paragraph_continuations` awk state machine + `categorize_fragments` pipes each fragment through it (PR #5, commit f490312) - [x] New unit tests in `tests/fragments.bats` for the normalization (preserve paragraphs / preserve indented sublists / preserve fenced code blocks) — verified 7 `@test "normalize: ..."` cases (PR #5) - [x] `changelog.d/<id>.fixed.md` fragments documenting both fixes — both shipped + consumed at cut; content now in CHANGELOG.md v0.2.0 ### Fixed section (note: `3.fixed-2.md` had to be manually folded into prep PR due to parser bug filed as release-toolkit#9) - [x] `docs/integration.md` updated with the `runs_on` parameter note + the convention move from "advisory" to "defensive" — verified "About the runs_on parameter" subsection + "Defensive from v0.2.0" framing in conventions.md (PR #5) - [x] Toolkit's own `.forgejo/workflows/release.yml` + `release-draft.yml` + `manifest-check.yml` added (consumer-side; references toolkit `@v0.1.1` — shipped as `@v0.2.0` per α settle) — verified all 3 consumer-shape workflows on main (PR #6 merged) - [x] v0.1.1 cut via local script invocation — shipped as **v0.2.0** per α settle; cut via `scripts/release-prep.sh --bump minor` (PR #8 release-prep merged at a521998) - [x] v0.1.1 published — shipped as **v0.2.0**; verified release id 110: draft=false, tag=v0.2.0, published_at=2026-06-24T16:55:53+02:00; tag canonically present in `git tag -l 'v*'` ## Cross-tracker impact Unblocks: - [cellblock#162](https://git.frankenbit.de/frankenbit/cellblock/issues/162) consumer-workflows follow-up PR - (Partial) [cellblock#157](https://git.frankenbit.de/frankenbit/cellblock/issues/157) — closes when cellblock's `manifest-check.yml` workflow lands referencing `@v0.2.0` Doesn't fully unblock tmux-tell#617 (5 remaining gap items deferred to v0.3 — see [release-toolkit#4](#4)). **v0.2.x scope** surfaced during the cut + filed for v0.2.1: - release-toolkit#9 (fragments parser silent-drop on hyphenated id) — caught during the v0.2.0 cut; manually folded - release-toolkit#10 (install-deps step assumes sudo) — caught during PR #6 CI failure; alcatraz-infra commit 7715b14 baked yq into ci-go as immediate unblock — QM, 2026-06-24, per operator α + Bosun 8cb5 ratification. All ACs canonically probed against main 2026-06-24 18:08 + ticked; release-toolkit v0.2.0 shipped + published.
quartermaster changed title from v0.1.1 — runs-on parameterization + fragment newline normalization to v0.2.0 — runs-on parameterization + fragment newline normalization 2026-06-24 16:24:31 +02:00
Author
Owner

Closing: v0.2.0 shipped

All 9 ACs canonically probed + verified against main + ticked. Probes:

AC Probe Result
1 grep -l 'inputs.runs_on' .forgejo/workflows/_*.yml 4 files
2 grep -c 'runs_on' tests/workflows.bats 10
3 grep -c '_normalize_paragraph_continuations' scripts/lib/fragments.sh 4
4 grep -c '^@test "normalize' tests/fragments.bats 7
5 grep -c 'Defensive newline normalization' CHANGELOG.md + runs-on bullet 1 + 1
6 grep -c 'About the.*runs_on' docs/integration.md + Defensive from in conventions.md 1 + 1
7 ls .forgejo/workflows/ for release.yml + release-draft.yml + manifest-check.yml all 3 present
8 cat VERSION + git tag -l 'v*' 0.2.0 + v0.1.0/v0.2.0
9 Forgejo release id 110 GET draft=false, published_at=2026-06-24T16:55:53+02:00

Substantive day's arc: v0.1.0 → v0.2.0 in single day with multiple substrate-care recoveries (release-toolkit#9 parser bug surfaced during the cut, release-toolkit#10 sudo assumption surfaced during PR #6 CI, alcatraz-infra#67 runner label drift surfaced before that). Both v0.2.1 scope items filed; alcatraz-infra ci-go image patched + re-built (commit 7715b14).

Closing per all-ACs-verified-done discipline (feedback_ac_tick_discipline + feedback_self_probe_asymmetry — banked today; canonical-probe of each AC before tick rather than propagating derived state).

Forward work surfaced:

  • release-toolkit#9 + #10 = natural v0.2.1 scope cluster (fail-loud-when-environment-mismatch theme)
  • release-toolkit#4 = v0.3 tmux-tell migration enablement (still blocked)
  • cellblock#162 consumer-workflows follow-up — now unblocked by v0.2.0
## Closing: v0.2.0 shipped All 9 ACs canonically probed + verified against main + ticked. Probes: | AC | Probe | Result | |---|---|---| | 1 | `grep -l 'inputs.runs_on' .forgejo/workflows/_*.yml` | 4 files | | 2 | `grep -c 'runs_on' tests/workflows.bats` | 10 | | 3 | `grep -c '_normalize_paragraph_continuations' scripts/lib/fragments.sh` | 4 | | 4 | `grep -c '^@test "normalize' tests/fragments.bats` | 7 | | 5 | `grep -c 'Defensive newline normalization' CHANGELOG.md` + runs-on bullet | 1 + 1 | | 6 | `grep -c 'About the.*runs_on' docs/integration.md` + `Defensive from` in conventions.md | 1 + 1 | | 7 | `ls .forgejo/workflows/` for release.yml + release-draft.yml + manifest-check.yml | all 3 present | | 8 | `cat VERSION` + `git tag -l 'v*'` | 0.2.0 + v0.1.0/v0.2.0 | | 9 | Forgejo release id 110 GET | `draft=false, published_at=2026-06-24T16:55:53+02:00` | **Substantive day's arc**: v0.1.0 → v0.2.0 in single day with multiple substrate-care recoveries (release-toolkit#9 parser bug surfaced during the cut, release-toolkit#10 sudo assumption surfaced during PR #6 CI, alcatraz-infra#67 runner label drift surfaced before that). Both v0.2.1 scope items filed; alcatraz-infra ci-go image patched + re-built (commit 7715b14). Closing per all-ACs-verified-done discipline ([`feedback_ac_tick_discipline`](https://git.frankenbit.de/frankenbit/release-toolkit) + [`feedback_self_probe_asymmetry`](https://git.frankenbit.de/frankenbit/release-toolkit) — banked today; canonical-probe of each AC before tick rather than propagating derived state). Forward work surfaced: - release-toolkit#9 + #10 = natural v0.2.1 scope cluster (fail-loud-when-environment-mismatch theme) - release-toolkit#4 = v0.3 tmux-tell migration enablement (still blocked) - cellblock#162 consumer-workflows follow-up — now unblocked by v0.2.0
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
frankenbit/release-toolkit#3
No description provided.