feat(quality): mechanized prose-quality readability check + fragment-authoring convention doc (v1.0.0 must-fix) #421

Closed
opened 2026-07-05 23:59:04 +02:00 by bosun · 3 comments
Owner

Closed by PR#431 (Part 1) + PR#430 (Part 2) both merged. Two-artifacts-one-discipline convergence: mechanized readability check ships with sentence ≤30 / paragraph ≤100 / paren-nesting ≤2 defaults, 5 consumer-input overrides; convention doc names the density rules + AFTER example annotated with concrete em-dash split-point per Surveyor 3656 catch. Doc + check agree on the shared example: check flags the AFTER's 41w opener, doc names why the bar is deliberately tighter than v0.27.0 shipped. Both directions of reverse-convergence firing.

**Closed by PR#431 (Part 1) + PR#430 (Part 2) both merged.** Two-artifacts-one-discipline convergence: mechanized readability check ships with sentence ≤30 / paragraph ≤100 / paren-nesting ≤2 defaults, 5 consumer-input overrides; convention doc names the density rules + AFTER example annotated with concrete em-dash split-point per Surveyor 3656 catch. Doc + check agree on the shared example: check flags the AFTER's 41w opener, doc names why the bar is deliberately tighter than v0.27.0 shipped. Both directions of reverse-convergence firing.
Author
Owner

Preserved v0.27.0 CHANGELOG section — "before" example for convention doc

Operator directive 2026-07-06: preserve the pre-cold-read prose as pedagogy scaffold for #421 Part 2 convention doc. v0.27.0 will ship with Lookout-cold-read-informed rewrites (Herald applies pre-cut); this section captures the original dense form as the concrete "before" example paired against the shipped "after."

Sourced from release-prep/rolling head e8a078f, prior to Herald applying Lookout finding 68b9 rewrites.


## [v0.27.0] - 2026-07-05

### Added

Forgejo API layer resilience (`scripts/lib/forgejo-api.sh`): connection + total timeouts on every curl invocation (5s connect, 30s total by default; env-tunable via `FORGEJO_API_CONNECT_TIMEOUT_S` + `FORGEJO_API_TIMEOUT_S`), `forgejo_api_call_with_retry` wrapper with exponential backoff on 429 / 5xx / network errors (honors Retry-After on 429 when numeric; capped at `FORGEJO_API_RETRY_CAP_S` seconds so a hostile header can't wedge the workflow; 429 retries on any method, but 5xx / network-error retries are gated to idempotent methods — a POST / PATCH is not retried by default since the server may have partially applied it (RFC 9110 §9.2.2), with `FORGEJO_API_RETRY_UNSAFE=1` to opt a known-safe non-idempotent call back in), and `forgejo_api_paginate` walking `?page=N&limit=M` to completion for list endpoints. Retrofitted `forgejo_list_tags` + `forgejo_find_pr_by_head` to paginate — the pre-resilience behavior silently dropped items past page 1 (~50 items) on active repos ([#334](https://git.frankenbit.de/frankenbit/release-toolkit/issues/334); v1.0.0 must-fix per operator 2026-07-05).

Layered `publish_mode` resolution matching the #370 `default_branch` pattern at a different knob (v1.0.0 must-fix per SemVer stability contract). Adopters can now flip publish mode per-project without editing the consumer workflow: set `vars.RT_PUBLISH_MODE=immediate` in Forgejo Settings → Actions → Variables, or add `publish_mode: immediate` to `release-toolkit.yml`. Precedence (first non-empty wins): (1) `RT_PUBLISH_MODE` env / `vars.RT_PUBLISH_MODE` → (2) config `publish_mode:` field → (3) workflow_call `with: publish_mode` input (existing behavior) → (4) default `draft` (#332). Invalid values fail loud with the layered resolution chain enumerated for debugging ([#408](https://git.frankenbit.de/frankenbit/release-toolkit/issues/408)).

`docs/integration.md` — new **Per-project configuration overrides** section giving adopters a single at-a-glance answer to "how do I override X for my project without editing the workflow?" A table maps every config knob to where it can be set (`release-toolkit.yml` field / Forgejo repo variable / workflow input) and links to its authoritative precedence: `default_branch` and `publish_mode` expose the full per-project override surface (including a `vars.RT_*` repo variable you flip in Settings with no commit), while the structural knobs (`release_type`, `version_files`, `changelog`, `tag_format`) are version-controlled config-file values. Three worked example blocks show the common override workflows — set-once in config, flip via repo variable, and override for a single cut via a dispatch input. Link-first to the per-knob `default_branch` (#370) and `publish_mode` (#408) resolution sections rather than restating their precedence tables ([#409](https://git.frankenbit.de/frankenbit/release-toolkit/issues/409)).

### Changed

`README.md` + `docs/integration.md` — the Codeberg tested-state now reflects empirical proof. The [tic-tac-toe companion demo](https://codeberg.org/frankenbit/tic-tac-toe) runs release-toolkit end-to-end on Codeberg's shared runner (fresh-repo bootstrap through cut to draft release), so the Positioning + runtime-compatibility notes flip Codeberg from "runs wherever a runner is available" (assumed) to **demonstrated end-to-end** (proven), with a companion-demo cross-link and a brief "Running on Codeberg" adopter note (repo-unit enablement + no-token graceful degradation). Closes the #382 demo cross-link AC ([#412](https://git.frankenbit.de/frankenbit/release-toolkit/issues/412)).

### Fixed

`.forgejo/workflows/reusable-*.yml` — the `Consumer wiring` docstring examples now use the `@vX.Y.Z` placeholder consistently instead of concrete pins that had drifted stale (`@v0.2.0`, `@v0.4.0-rc.1`, an unresolvable `@v1.0.0`, plus `@v0.20.0` / `@v0.21.0`). An adopter copy-pasting a wiring example now sees an explicit substitute-your-version placeholder rather than a stale or unresolvable ref. Matches the convention `reusable-mirror-to-codeberg.yml` already used; concrete copy-paste-ready refs stay in the prose docs (README / integration.md / examples/), kept fresh by the post-cut doc-ref hook ([#407](https://git.frankenbit.de/frankenbit/release-toolkit/issues/407)).

**Push-during-cut cancellation recovery** (v1.0.0 must-fix; empirical anchor: #382 tic-tac-toe demo repo v0.1.1 → v0.2.0 cycle 2026-07-05). Two-part fix for the class where a push arriving between "rolling-PR merged" and "cut workflow picked up by runner" cancels the cut → the `## [vX.Y.Z]` CHANGELOG section is orphaned (release never tagged) → next workflow walks commits from the stale manifest anchor + emits a duplicate entry in the following rolling PR:

- **Prevention** (adopter-side, required): consumer wiring template in `docs/integration.md` now includes a workflow-level `concurrency:` block (`group: release-cut-${{ github.ref }}`, `cancel-in-progress: false`) that queues subsequent pushes behind the in-flight cut.
- **Detection** (toolkit-side, belt-and-suspenders): `release-decide.sh` fails loud when `CHANGELOG.md`'s top-most released section is ahead of `manifest.last_released_version` — the orphan signature. The error lists two recovery options (complete the pending release + update manifest, OR revert the CHANGELOG section back under `[Unreleased]`) so an adopter chooses which path fits their intent. Auto-heal is out of scope for v1.0.0 — either recovery is a scope decision the adopter has to make.

Closes [#417](https://git.frankenbit.de/frankenbit/release-toolkit/issues/417).

Appended context — Lookout cold-read findings (bus id 68b9) identified 6 concrete rewrite targets on this text (lines 27 / 29 / 31 / 35 / 39 / 41-44 in the source), plus 8 patterns for the convention doc. Herald authors Part 2 with this before-form + shipped v0.27.0 after-form as the paired example.

## Preserved v0.27.0 CHANGELOG section — "before" example for convention doc **Operator directive 2026-07-06**: preserve the pre-cold-read prose as pedagogy scaffold for #421 Part 2 convention doc. v0.27.0 will ship with Lookout-cold-read-informed rewrites (Herald applies pre-cut); this section captures the original dense form as the concrete "before" example paired against the shipped "after." Sourced from `release-prep/rolling` head e8a078f, prior to Herald applying Lookout finding 68b9 rewrites. --- ```markdown ## [v0.27.0] - 2026-07-05 ### Added Forgejo API layer resilience (`scripts/lib/forgejo-api.sh`): connection + total timeouts on every curl invocation (5s connect, 30s total by default; env-tunable via `FORGEJO_API_CONNECT_TIMEOUT_S` + `FORGEJO_API_TIMEOUT_S`), `forgejo_api_call_with_retry` wrapper with exponential backoff on 429 / 5xx / network errors (honors Retry-After on 429 when numeric; capped at `FORGEJO_API_RETRY_CAP_S` seconds so a hostile header can't wedge the workflow; 429 retries on any method, but 5xx / network-error retries are gated to idempotent methods — a POST / PATCH is not retried by default since the server may have partially applied it (RFC 9110 §9.2.2), with `FORGEJO_API_RETRY_UNSAFE=1` to opt a known-safe non-idempotent call back in), and `forgejo_api_paginate` walking `?page=N&limit=M` to completion for list endpoints. Retrofitted `forgejo_list_tags` + `forgejo_find_pr_by_head` to paginate — the pre-resilience behavior silently dropped items past page 1 (~50 items) on active repos ([#334](https://git.frankenbit.de/frankenbit/release-toolkit/issues/334); v1.0.0 must-fix per operator 2026-07-05). Layered `publish_mode` resolution matching the #370 `default_branch` pattern at a different knob (v1.0.0 must-fix per SemVer stability contract). Adopters can now flip publish mode per-project without editing the consumer workflow: set `vars.RT_PUBLISH_MODE=immediate` in Forgejo Settings → Actions → Variables, or add `publish_mode: immediate` to `release-toolkit.yml`. Precedence (first non-empty wins): (1) `RT_PUBLISH_MODE` env / `vars.RT_PUBLISH_MODE` → (2) config `publish_mode:` field → (3) workflow_call `with: publish_mode` input (existing behavior) → (4) default `draft` (#332). Invalid values fail loud with the layered resolution chain enumerated for debugging ([#408](https://git.frankenbit.de/frankenbit/release-toolkit/issues/408)). `docs/integration.md` — new **Per-project configuration overrides** section giving adopters a single at-a-glance answer to "how do I override X for my project without editing the workflow?" A table maps every config knob to where it can be set (`release-toolkit.yml` field / Forgejo repo variable / workflow input) and links to its authoritative precedence: `default_branch` and `publish_mode` expose the full per-project override surface (including a `vars.RT_*` repo variable you flip in Settings with no commit), while the structural knobs (`release_type`, `version_files`, `changelog`, `tag_format`) are version-controlled config-file values. Three worked example blocks show the common override workflows — set-once in config, flip via repo variable, and override for a single cut via a dispatch input. Link-first to the per-knob `default_branch` (#370) and `publish_mode` (#408) resolution sections rather than restating their precedence tables ([#409](https://git.frankenbit.de/frankenbit/release-toolkit/issues/409)). ### Changed `README.md` + `docs/integration.md` — the Codeberg tested-state now reflects empirical proof. The [tic-tac-toe companion demo](https://codeberg.org/frankenbit/tic-tac-toe) runs release-toolkit end-to-end on Codeberg's shared runner (fresh-repo bootstrap through cut to draft release), so the Positioning + runtime-compatibility notes flip Codeberg from "runs wherever a runner is available" (assumed) to **demonstrated end-to-end** (proven), with a companion-demo cross-link and a brief "Running on Codeberg" adopter note (repo-unit enablement + no-token graceful degradation). Closes the #382 demo cross-link AC ([#412](https://git.frankenbit.de/frankenbit/release-toolkit/issues/412)). ### Fixed `.forgejo/workflows/reusable-*.yml` — the `Consumer wiring` docstring examples now use the `@vX.Y.Z` placeholder consistently instead of concrete pins that had drifted stale (`@v0.2.0`, `@v0.4.0-rc.1`, an unresolvable `@v1.0.0`, plus `@v0.20.0` / `@v0.21.0`). An adopter copy-pasting a wiring example now sees an explicit substitute-your-version placeholder rather than a stale or unresolvable ref. Matches the convention `reusable-mirror-to-codeberg.yml` already used; concrete copy-paste-ready refs stay in the prose docs (README / integration.md / examples/), kept fresh by the post-cut doc-ref hook ([#407](https://git.frankenbit.de/frankenbit/release-toolkit/issues/407)). **Push-during-cut cancellation recovery** (v1.0.0 must-fix; empirical anchor: #382 tic-tac-toe demo repo v0.1.1 → v0.2.0 cycle 2026-07-05). Two-part fix for the class where a push arriving between "rolling-PR merged" and "cut workflow picked up by runner" cancels the cut → the `## [vX.Y.Z]` CHANGELOG section is orphaned (release never tagged) → next workflow walks commits from the stale manifest anchor + emits a duplicate entry in the following rolling PR: - **Prevention** (adopter-side, required): consumer wiring template in `docs/integration.md` now includes a workflow-level `concurrency:` block (`group: release-cut-${{ github.ref }}`, `cancel-in-progress: false`) that queues subsequent pushes behind the in-flight cut. - **Detection** (toolkit-side, belt-and-suspenders): `release-decide.sh` fails loud when `CHANGELOG.md`'s top-most released section is ahead of `manifest.last_released_version` — the orphan signature. The error lists two recovery options (complete the pending release + update manifest, OR revert the CHANGELOG section back under `[Unreleased]`) so an adopter chooses which path fits their intent. Auto-heal is out of scope for v1.0.0 — either recovery is a scope decision the adopter has to make. Closes [#417](https://git.frankenbit.de/frankenbit/release-toolkit/issues/417). ``` --- Appended context — Lookout cold-read findings (bus id 68b9) identified 6 concrete rewrite targets on this text (lines 27 / 29 / 31 / 35 / 39 / 41-44 in the source), plus 8 patterns for the convention doc. Herald authors Part 2 with this before-form + shipped v0.27.0 after-form as the paired example.
herald self-assigned this 2026-07-06 11:01:16 +02:00

Post-close cross-ref for register-discipline family (2026-07-06): release-toolkit#435 opened as a follow-up to #403/PR#434 for adopter-overridable REGISTER_PATTERNS. Surveyor 5e14 named the family-cluster shape (adopter-facing vs maintainer-internal register mechanism) with #421 as one of the n=3 anchors — the readability doc showcased a check-failing exemplar (#431 PR#433 arc). Preserved here for post-sprint discoverability.

**Post-close cross-ref for register-discipline family (2026-07-06)**: release-toolkit#435 opened as a follow-up to #403/PR#434 for adopter-overridable REGISTER_PATTERNS. Surveyor 5e14 named the family-cluster shape (adopter-facing vs maintainer-internal register mechanism) with #421 as one of the n=3 anchors — the readability doc showcased a check-failing exemplar (#431 PR#433 arc). Preserved here for post-sprint discoverability.
Author
Owner

Cold-read checklist for adopter-facing prose (via Lookout)

Posted on bus 2026-07-06 (msg 0449) as generalized-checklist output distilled beyond the 6 PR#406 rewrite targets that seeded the #421 arc. Attaching here as durable reference for the register-discipline family cluster (this tracker + #431/#403/#435).


  • Declare the reader frame first: who is reading, what version/state are they on, what decision are they trying to make?
  • After a 30-second skim, write the inferred headline in one sentence. If you cannot, the prose lacks a clear top-line.
  • Lead every entry with reader outcome, not implementation mechanism, issue lineage, or internal analogy.
  • Mark required action explicitly: Action required, Recommended, or No action required. Do not make the reader infer safety/upgrade duties from a narrative.
  • Split outcome from detail: first sentence says what changed and why it matters; second sentence gives mechanism/limits.
  • Treat jargon as a tax. If a first-time reader would not know the term, either define it in place or replace it with plain language.
  • Flag internal-room language: reviewer names, ceremony words, dated incident archaeology, "must-fix per X", "empirical anchor", "AC", and local metaphors. Move that to PRs/ADRs unless it changes adopter behavior.
  • Prefer one idea per sentence. Long parentheticals and chained clauses are usually where the adopter loses the thread.
  • Quote concrete offender text in the review. Vague "too dense" feedback is less useful than "line N starts with mechanism and buries the user outcome."
  • Ask the six reader questions: breaking changes? required upgrade action? desirable new capability? relevant bug fixes? removals/deprecations? migration path? Note where each answer appears: top/mid/bottom/not found.
  • Estimate signal vs noise by reader-actionability, not word count. Some long prose is fine if every sentence helps the adopter decide.
  • Documentation-change entries should say what question the docs now answer, not describe the document's table of contents.
  • Operational bug-fix entries should lead with the failure mode in user terms, then prevention/detection/recovery.
  • Keep proof/history as links at the end. The changelog should not require the reader to understand the investigation chain.

Short version: a cold-read catches where authors wrote from memory of the work instead of from the adopter's decision point.


Reference for future adopter-facing surface work + Round-5 external cold-read template scaffold. Not tracker-scoped work — durable material attached to the family-cluster surface where it naturally sits.

## Cold-read checklist for adopter-facing prose (via Lookout) Posted on bus 2026-07-06 (msg 0449) as generalized-checklist output distilled beyond the 6 PR#406 rewrite targets that seeded the #421 arc. Attaching here as durable reference for the register-discipline family cluster (this tracker + #431/#403/#435). --- - Declare the reader frame first: who is reading, what version/state are they on, what decision are they trying to make? - After a 30-second skim, write the inferred headline in one sentence. If you cannot, the prose lacks a clear top-line. - Lead every entry with reader outcome, not implementation mechanism, issue lineage, or internal analogy. - Mark required action explicitly: `Action required`, `Recommended`, or `No action required`. Do not make the reader infer safety/upgrade duties from a narrative. - Split outcome from detail: first sentence says what changed and why it matters; second sentence gives mechanism/limits. - Treat jargon as a tax. If a first-time reader would not know the term, either define it in place or replace it with plain language. - Flag internal-room language: reviewer names, ceremony words, dated incident archaeology, "must-fix per X", "empirical anchor", "AC", and local metaphors. Move that to PRs/ADRs unless it changes adopter behavior. - Prefer one idea per sentence. Long parentheticals and chained clauses are usually where the adopter loses the thread. - Quote concrete offender text in the review. Vague "too dense" feedback is less useful than "line N starts with mechanism and buries the user outcome." - Ask the six reader questions: breaking changes? required upgrade action? desirable new capability? relevant bug fixes? removals/deprecations? migration path? Note where each answer appears: top/mid/bottom/not found. - Estimate signal vs noise by reader-actionability, not word count. Some long prose is fine if every sentence helps the adopter decide. - Documentation-change entries should say what question the docs now answer, not describe the document's table of contents. - Operational bug-fix entries should lead with the failure mode in user terms, then prevention/detection/recovery. - Keep proof/history as links at the end. The changelog should not require the reader to understand the investigation chain. **Short version**: a cold-read catches where authors wrote from memory of the work instead of from the adopter's decision point. --- Reference for future adopter-facing surface work + Round-5 external cold-read template scaffold. Not tracker-scoped work — durable material attached to the family-cluster surface where it naturally sits.
Sign in to join this conversation.
No milestone
No project
No assignees
2 participants
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#421
No description provided.