When a repository publishes a release, PullPulse posts one card to the channels that asked for it and edits that card as the release deploys.
Release news is the one surface off until asked for. A channel that watches a repository's pull
requests has not asked to hear about its tags, so releases is false by default — turn it on per
route in .pullpulse.yml, or tick Announce releases on the watch form. See
the configuration reference.
The release card
The card posts on GitHub's release.published with draft: false — the one event that fires for
stable releases and pre-releases alike. A later edited updates the same message; unpublished
and deleted fold it.
┌ card ────────────────────────────────────────────┐
│ ⬆ v2.14.0 · acme/billing
│ ● Tagged · ● Notes · ◔ Deploy
│ 🚢 12 pull requests from 5 people · since v2.13.2 · 6 days
│ _Idempotent webhooks, proration fix, signing-key rotation._
│ [Release notes] [Diff v2.13.2…v2.14.0] [Draft announcement]
└──────────────────────────────────────────────────┘
⬆ Deploying to production · by @dana · 4:02 PM
tagged by @dana · a3f2c1d · pre-release · 🔒 immutable · 3 Features · 7 Bug fixes
▸ What's in it · 12 pull requests · 5 people
🔍 Explain 🔕 Mute releases
The counts lead because they are the only part a reader can act on without opening anything: how
big the release is, and what it is a release from. A release whose notes named nothing says
🚢 no pull requests we could resolve rather than 0 pull requests, which would read as a claim
that nothing shipped. One line of the body is quoted under it; the body itself, up to 125 000
characters of Markdown, is never rendered whole.
The three-stop rail
Same glyph family as the pull request card's four-stop rail, three stops instead of four.
| Stop | reads |
|---|---|
| Tagged | always ● — the card exists because a release was published |
| Notes | ● when the generated notes named something, ◌ when they named nothing |
| Deploy | ◌ nothing reported · ○ queued or pending · ◔ in progress · ● succeeded · ⊗ failed |
Badges and the meta line
pre-release and 🔒 immutable sit on the meta line, not in the title: they are facts about the
release rather than states of it — the rail says how it is doing — and a badge in the title would
push the tag off the end. The line reads tagged by @dana · a3f2c1d, then the badges, then the
count per category the notes used.
What's in it, and where it comes from
One collapsible container, collapsed by default: a channel that opted into release news did not opt
into twelve lines of it every time, so the subtitle carries the counts and the click carries the
list. The grouping is GitHub's own. PullPulse asks POST /releases/generate-notes with
previous_tag_name; GitHub resolves the pull requests, applies the repository's
.github/release.yml categories, and persists nothing. The returned Markdown is parsed for the
headings and the pull request numbers, and each number is joined to PullPulse's own
pull_requests rows for the Slack mention and the stored brief — which is why the container names
people rather than logins. A category with no heading is listed as Changes. When generate-notes
fails, the fallback walks compare base…head and commits/{sha}/pulls, capped at 100 commits.
Draft announcement is the AI action: a customer-facing paragraph written from the briefs of the included pull requests.
The deploy stop
Deployments come from deployment_status, tied to the release by deployment.sha matching the
tag's commit or deployment.ref equalling the tag name — GitHub keeps no explicit link between the
two. The ledger line above the meta line states the latest thing only: Deploying to production,
Deployed to production, Deploy failed, each linked to its run and stamped in the reader's own
clock. Since a job can use an environment with deployment: false, a repository that reports no
deployment events falls back to workflow_run on the tag, and the stop reads ◌ when neither
exists. A release with no deployment gets no ledger line rather than a line saying nothing
happened.
Shipped today
Above three releases from one repository in one UTC day the cards stop, and one Shipped today message per repository per day is edited in place instead — a repository that ships continuously would otherwise hand a channel a card an hour. It is a header, a strip, and a table with one row per release: Release, Rail, What, PRs, By, At, newest first and never ranked by anything else, because the reader's question is "what has shipped since I last looked".
Back on the pull request
When a release ships a merged pull request, that pull request's fold gains · shipped in v2.14.0,
linked to the release; its Work Object pane gains a Release field; and the digest's merged
bucket says the same.
Weekly notes
Everything the cards read is stored, which is what makes "what shipped this week" answerable
without asking GitHub again: the week's releases with their items, summarised from the stored
briefs and grouped by release, alongside who worked on what. It is a per-channel permission in
.pullpulse.yml like pulseReport, not a schedule — see
the configuration reference.
What GitHub has to allow
Releases need three webhook subscriptions and two read-only permissions that older installations do
not have: the events release, deployment_status and workflow_run, and the permissions
deployments and actions. The permissions are what GitHub gates the events' delivery on, and it
does not warn — without them the events are silently not sent, so the symptom is a deploy stop that
never moves rather than an error anywhere. Adding one re-prompts every installation's org admin, and
until an org accepts, its cards render tagged and noted with the deploy stop pending forever.
Operators: see operations.