Skip to content

Latest commit

 

History

History
101 lines (70 loc) · 4.12 KB

File metadata and controls

101 lines (70 loc) · 4.12 KB

Release Radar — workflow

This document explains the workflow: what each phase does, what's deterministic vs. synthesized, and where the kill conditions are. The machine-readable version is release-radar.workflow.js; the slash-command entry point is commands/release-radar.md.

The shape

git range ──▶ [collect] ──▶ [categorize] ──▶ [synthesize] ──▶ [render] ──▶ artifacts
                 │               │                  │              │
              scripts/        scripts/          Claude turn    scripts/
           collect-commits  categorize.js     (grounded)    update-changelog.js

Three of the four working steps are plain Node scripts with no dependencies and unit tests. The LLM only does step 3, synthesis, and it's handed the commit facts as its only grounding.

Phases

The workflow runner executes these in order. Each is a named, resumable phase.

1. collect-commits (deterministic)

Resolves the range (<last-tag>..HEAD by default) and parses git log into structured commits: { hash, shortHash, subject, body, type, scope, breaking, isMerge }. Merge commits are excluded. Implemented in scripts/commit-range.js (aliased as scripts/collect-commits.js).

Fails the run if the range has zero commits — there is nothing to release.

2. categorize (deterministic)

Classifies each commit into a Keep-a-Changelog section using its conventional-commit prefix, with a keyword heuristic fallback when there's no prefix. Drops noise (version bumps, "release" commits, WIP). Internal churn (chore/ci/test/style) is dropped from user-facing notes. Implemented in scripts/classify-commits.js (aliased as scripts/categorize.js).

Returns { sections, breaking, dropped }.

3. kill-condition (guard)

If every commit was internal — sections has no user-facing groups — the workflow stops and says so. It does not generate an empty or fabricated release. This is the single most important behavior: a release flow that invents content is worse than no release flow.

4. synthesize-highlights (LLM)

A single Claude turn writes a 1-3 sentence highlights summary for the GitHub release. The prompt (see highlightsPrompt in the workflow) hands it only the categorized commits and instructs it to reply NO_USER_FACING_CHANGES rather than invent anything. Banned marketing words are listed in the prompt.

5. synthesize-post (LLM)

A second Claude turn writes the short announcement, held to references/announcement-voice.md.

6. render (deterministic)

scripts/update-changelog.js turns the categorized data + the two synthesized texts into:

  • a Keep-a-Changelog section (renderChangelog)
  • a draft GitHub release body (renderRelease — highlights + the list)
  • a short announcement (renderSocial — the post text + real counts)

Counts in the announcement come from the data, never from the model.

7. write-changelog (side effect)

Prepends the rendered section to CHANGELOG.md, creating the file with a proper Keep-a-Changelog header if it doesn't exist.

8. draft-github-release (side effect, dry-run by default)

Prints the gh release create v<version> --draft command with the release body on stdin. It does not publish unless you run it yourself — drafts are reviewed before they go out.

Why split deterministic from synthesized

Range resolution, log parsing, and classification are mechanical and must be reproducible — the same commits always produce the same buckets. Putting them in tested scripts means the model can't get them subtly wrong, and it means the scripts are useful on their own (see "Run it without Claude" in the README).

The model is good at exactly one thing here: turning a bucketed list into a readable sentence. That's all it's asked to do, and it's fenced in by the grounding so it can't drift into invention.

Grounding contract

The synthesis prompts contain the literal commit subjects. The instruction is: use only these; if the list is empty, say so. This is the same evidence-before- assertion rule the scripts enforce mechanically, applied to the one step a human-readable model touches.