Today a Release Record is a receipt: Tower writes it after an image is already healthy in a Flight environment. But most release work happens before that point. Someone gathers scope, writes notes, picks a candidate and makes the go or no-go call. The record should exist for all of it, and still be a sealed, git-backed receipt at the end.
Release management is the part of Tower I trust most. The October 11 field report rated it Strong. So this isn't a rescue. It's about timing.
| Piece | What it does today | State |
|---|---|---|
| Record sub-tab | List and detail views: what changed, what was built, what happened, then human context and a verification strip. | Shipped |
| Export and compare | A self-contained HTML document that prints to PDF, and a diff of two records of the same app. | Shipped |
| Saved to git | When a release goes Healthy in a Flight environment, a poller opens a PR adding releases/<app>@<tag>.yaml to the app's gitops repo. Cloud apps write theirs from the merged pin PR. | Shipped |
| Human context | Summary, risk, notes, tags and approvals stamped with the signed-in identity. Every edit is a PR. | Shipped |
| Build evidence | Archived pipeline runs, tests, scans, provenance, SBOM and the guardrail ledger, kept in the record so they outlive live data. | Shipped |
| Confidence score | Starts at 50, with points for guardrails, provenance, approvals and "no incidents". | Shipped |
| Outcome tracking | Status can only be healthy, degraded or superseded, and incidents can't be recorded, so every release gets the "no incidents" points. Glidepath knows about rollbacks; the record doesn't. | Gap |
| Post-release SLO watch | Checks at +24h and +7d, blocked while the prod cluster's Prometheus is scaled to zero. | Not started |
| Approval gate | Block promotion without approvals, for apps that opt in. | Parked |
One idea runs through every tool I looked at: a release is an object with a lifecycle, and a deploy is one event in it. The tools that track releases well create the object early and attach work to it as it happens. The record is a byproduct.
Releases are continuous or scheduled, and CI attaches issues as commits land. Make the release first and attach work at merge time.
"Unreleased" is a state teams already use. Tower can show it with no extra input.
The thing that moves between environments has one identity. Each image is a candidate for a release.
A release has a kickoff, one named pilot and a short checklist, mostly evidence the system already has.
External approvals slow delivery and don't lower the change failure rate. Use a light peer sign-off backed by evidence, not a change board.
Deploy and release are separate. The record isn't finished at Healthy: rollbacks and degradation have to flow back in, or the score can't be trusted.
Approvals currently come after the fact. An approval added once a release is already healthy in production is really an acknowledgement. Moving sign-off onto the release PR, while gates run, makes it a real decision.
Notes get written as work merges. If the draft builds up from merged PRs, the notes are mostly written by release day.
Picking a candidate needs context. A draft can show every image built since the last release, how long each has run on Ground, and its gate results, side by side.
The draft needs no storage. Everything it shows comes from data Tower already fetches. A file is needed only for what people type, and only once they type it.
Not everything gets a record. The unit is still a release to Flight. It just starts earlier. Every image is a candidate inside a release, not a release of its own.
Each Skyport app builds Flight releases from main, with no release branches. Every merge builds an image tagged <version>-<sha> and deploys it to Ground, so every merge produces a candidate. A release is the candidate you choose to promote, plus everything merged before it. Work attaches to the draft at three levels: planned issues on a milestone (optional), incoming open PRs, and scope, the PRs merged since the last release.
Two developers, two features. Both feed the same release. Because main is a single line, you can't ship the later feature without the earlier one. To hold one back, keep its PR open or merge it behind a feature flag, and the record notes the flag.
A nickname, and a version that settles later. Each release gets a name when its draft opens, handed out alphabetically per app the way hurricanes are named: ASTER, BIRCH, CEDAR. It comes from a different word family than image slugs like amber-finch, so people say "CEDAR, shipped as amber-finch". The version is a label, not an identity. The Skyport apps' sealed records show both habits: gate-api bumped its version every release, while baggage-api and boarding-api each shipped the same version twice. So the draft suggests a version from the merged PRs, readiness flags one that hasn't moved, and the seal records the real one.
A side finding: image slugs come from only four hex characters and a noun list with duplicates, about 1,600 combinations, so slugs will repeat across an app's history. Sequential release names never do.
Ready is Glidepath's proposed. Rolling out covers merged, progressing and sync-failed. Aborted covers aborted and degraded. Released, Superseded and Rolled back keep their names.
A record keyed on app@imageTag can't exist before the image does, so it becomes baggage-api@CEDAR. Existing records keep their file names and get nicknames in date order.
Draft and Candidate are computed. Typed fields go in a draft file once someone writes something. At Healthy the poller seals the record as it does today, and the outcome is added once, when the bake window ends.
Sealed records keep their list and detail views. Above them goes one live card for the release in progress. This sketch uses illustrative values, not real data.
A card at the top of the Record list from the moment the previous release ships: nickname, suggested version, PRs merged since then sorted by the existing classifyPr, incoming open PRs, linked GitHub issues, candidate images and a notes preview.
Mostly frontend, built from data Tower already fetches. No PRs and no files. Linked issues probably need GitHub's closing-issue references on each PR.
Every image built since the last release, side by side: slug, Ground environments, time on Ground, gate results, provenance, and SBOM diff against the last release. Promote acts on a candidate.
Reuses Tekton Results pipeline runs, deploy history and provenance. Kargo calls this Freight.
In Ready, an approval is a review on the gitops release PR from someone other than the author. Approvals after release are still allowed but labelled acknowledged, and only pre-merge ones earn score.
DORA's peer-review finding. Decided October 13, 2026: sign-off is recorded, never blocking, for now.
At most eight items, most filled in by the system: guardrails green, provenance verified, minimum Ground soak, rollback target eligible, no open release-blocker issues. A person confirms notes, data risk and sign-off.
Runway's checklists. Rollback eligibility already exists in Glidepath.
Keep a Changelog categories, an internal changelog and user-facing notes with Known issues. Sealing writes a GitHub Release and feeds a per-app Changelog view.
Replaces the blank Summary field with a pre-filled draft.
An owner on the draft, defaulting to whoever promotes, with an optional rotation in cicd.yaml. The owner makes the call; others add evidence.
Runway's release pilot. The existing authoredBy field covers part of it.
Today's confidence score becomes Readiness, minus the constant "+5, no incidents". A new Outcome score: clean, degraded, aborted then recovered, or rolled back, plus SLO checks at +1h, +24h and +7d.
Glidepath already emits rolled-back and superseded. SLO checks need the prod cluster's Prometheus running again.
Batch size, migration files, Flight config changes, dependency changes from the SBOM diff and a changed rollout strategy suggest low, medium or high, which a person confirms.
The existing risk fields. Batch size is the factor DORA links to change failure rate.
A target date and planned scope from a GitHub milestone, then Jira keys. Scope can freeze, and open items move to the next release when this one seals.
Linear's scheduled pipelines and Jira fix versions. Continuous mode stays the default.
An Ops Wall panel of each app's draft, ready and baking releases, messages at Ready and Released, and a signed read-only link for stakeholders.
The Phase 3 distribution items, now with something worth sending before release.
Every v1 field stays. A v1 file reads as a release with one candidate and no draft history.
# gitops-<app>/releases/<app>@<release>.yaml (sealed at Healthy, as today) schemaVersion: 2 id: baggage-api@CEDAR # assigned when the draft opens, never changes version: { suggested: 1.9.0, shipped: 1.9.0 } phase: released # draft | candidate | ready | rolling-out | released | superseded | rolled-back base: { branch: main, commit: 6f1c0aa } # scope = merged on main after this scope: { prs: [212, 215, 217, …], issues: ["#198", "#203"] } candidates: - { tag: 1.9.0-a41f9c2, slug: amber-finch, groundSoak: 26h, chosen: true } - { tag: 1.8.2-4c1e9a0, slug: quiet-heron, chosen: false } readiness: { items: [{ id: guardrails, ok: true, auto: true }, …], score: 92 } signoffs: [{ role: qa, via: "gitops PR review", before: merge }] notes: { internal: …, public: …, knownIssues: […] } risk: { suggested: medium, confirmed: low, rollbackTo: v1.8.2 } outcome: { state: clean, slo24h: met, slo7d: met } # + every v1 field: deployments, promotionChain, guardrails, pipelineRuns, testResults, humanContext …
The draft file lives at glidepath/releases/next.yaml in the source repo, created only when someone edits the draft. Pushes that touch only glidepath/ already skip builds. One thing to check first: the lower-environments ApplicationSet must not treat releases/ as an environment folder.
The draft card with its nickname, scope, incoming PRs, suggested version, candidates and soak time, a display-only checklist and linked issues. Approved to ship alone, with no new storage.
Sign-off from PR reviews, an owner, a notes editor and draft file, and a seal that copies the draft in as schema v2.
Rollbacks written back, Readiness and Outcome scores, bake-window SLO checks, and GitHub Releases with a Changelog view.
Scheduled releases, Jira keys, a fleet calendar and messages at Ready and Released. The sign-off gate stays parked.
| Not building | Why |
|---|---|
| An issue tracker | Link to GitHub Issues, Jira or Linear, and read keys from PR text. The backlog doesn't live in Tower. |
| A change advisory board | One peer sign-off, backed by evidence. |
| A record per Ground deploy | An image is a candidate. A release is what reaches Flight. |
| A PR for every draft edit | Drafts are computed or committed straight to a build-exempt folder. PRs are for sealing and outcome only, which avoids the gate trouble record branches caused in glidepath#83. |