The compass release-notes pipeline
Table of Contents
Summary
compass release-notes (projects/ores.compass/src/compass_release_notes.py)
closes a sprint through five independent, idempotent verbs —
create, charts, export, commit, draft — run in that order
from a task branch off main. Splitting the pipeline this way lets
each step be re-run in isolation to fix a mistake (wrong
categorisation, a formatting bug, the wrong codename) without
redoing the others or losing already-published work. The design's
one real subtlety is that the generated Markdown embeds chart images
via raw.githubusercontent.com/.../main/... URLs rather than
uploading them anywhere — so those images only render once
release_notes.org=/.md= and the four chart PNGs are actually
merged to main, which is why commit and draft are separate
verbs with a PR in between.
Detail
Why five verbs, not one
An single "generate and publish release notes" command sounds appealing but fails the moment anything about the sprint needs a second look — and something always does: a story miscategorised into "Other", a codegen bug like org's subscript export mangling prose identifiers, a wrong release codename. Five narrow verbs mean any one of those can be fixed by re-running just that verb, without re-doing PR collection or losing a tag/release that already exists:
create --sprint N [--since-tag vX.Y.Z]— wrapscollect_release_pr_data.py(enumerates merged PRs viaghsince the previousv*tag) andgenerate_release_notes.py(parsessprint.organd everystory.org, groups stories into sections viaTAG_TO_SECTION, derives Highlights from the retrospective, rendersdoc_release_notes.org.mustachevia pystache). Leaves two(( ... ))summary-blurb placeholders for a human/LLM to fill — synthesising sprint narrative from mission/retrospective/PR list is deliberately not automated.charts --sprint N— delegates tocompass sprint charts(cmd_sprint_chartsincompass.py) so the org file's[[proj:...]]chart image links resolve to real PNGs.export --sprint N— batch-runsores-build-release-notes.elunder Emacs to turnrelease_notes.orginto GitHub-flavoured Markdown. House export settings: no table of contents, no section numbers, and — critically —org-export-with-sub-superscriptsdisabled (org's default treats anyword_wordoutside verbatim markup as a subscript; without this, prose identifiers likemarket_fixingexport asmarket<sub>fixing</sub>). Same convention asores-build-site.elandores-build-help.el.commit --sprint N— stages, commits, and pushesrelease_notes.org=/.md= and the four chart PNGs on the current branch. No-ops cleanly if nothing changed.draft --sprint N --codename <Name>— tagsmain(signed,git tag -s) and opens a draft GitHub release viagh release create/edit --notes-file. Always draft: a human reviews, attaches extras, and publishes it live.
Image resolution: raw.githubusercontent.com, not an upload API
There is no GitHub API call that "uploads an image into a release
body." Instead, the proj: link exporter in
ores-build-release-notes.el rewrites every [[proj:path]] link:
images become
https://raw.githubusercontent.com/OreStudio/OreStudio/main/<path>,
everything else becomes a
https://github.com/OreStudio/OreStudio/blob/main/<path> URL. Both
only resolve once <path> exists on main — which is exactly why
commit (push the files on a branch) and draft (tag and open the
release) are separate verbs with a PR-merge step in between, not one
atomic action. Running draft before the commit's PR has merged
still works — the release is created/updated correctly — but its
images 404 (or worse, silently show whatever stale version of those
files happens to already be on main) until the merge lands. No
special re-run is needed afterwards: the URLs are stable, so once
the files land on main the same release body starts rendering
correctly.
Tag/release idempotency
The tag-creation and up-to-date-main checks in draft only fire
when a new tag actually needs to be created — the operation that
can tag the wrong commit if run from a stale branch. Reusing a tag
that already exists (locally, on origin, or both) skips that check
entirely, because the only remaining work (gh release create/edit)
doesn't care what is checked out locally. A tag that exists on
origin but not locally is fetched, never recreated, so a same-name
tag can never silently diverge onto a different commit. This makes
the whole verb safe to re-run repeatedly — including editing a
release's title after the fact (verified live: redrafting
v0.0.21 with the correct --codename updated the title in place
without creating a new release or losing the tag/PR/notes work
already done).
Release title convention
House convention (see gh release list): <tag>, "<Codename>" —
e.g. v0.0.20, "Capopolo". The codename is invented per release (no
derivable default), so --codename is a required argument to
draft, not optional.
See also
- Agile Add Release Notes — the skill that drives this pipeline.
- How do I generate release notes? — the step-by-step recipe.
- Sprint closure — the lifecycle phase this pipeline serves.
- Release (glossary).