Tower · Proposal · Release record lifecycle

Start the next release record when the last one ships

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.

Proposal October 13, 2026Status proposed and R1 approved October 13, 2026; R1 to R4 shipped in Tower v0.67.0 to v0.70.0, October 13 and 14, 2026Grounded in Tower v0.66.0 and Glidepath main, October 13, 2026Docs Tower docs
Built · R1 to R4 shipped in Tower v0.67 to v0.70

The record shows up after every decision it could have informed has already been made.

Where it stands

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.

PieceWhat it does todayState
Record sub-tabList and detail views: what changed, what was built, what happened, then human context and a verification strip.Shipped
Export and compareA self-contained HTML document that prints to PDF, and a diff of two records of the same app.Shipped
Saved to gitWhen 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 contextSummary, risk, notes, tags and approvals stamped with the signed-in identity. Every edit is a PR.Shipped
Build evidenceArchived pipeline runs, tests, scans, provenance, SBOM and the guardrail ledger, kept in the record so they outlive live data.Shipped
Confidence scoreStarts at 50, with points for guardrails, provenance, approvals and "no incidents".Shipped
Outcome trackingStatus 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 watchChecks at +24h and +7d, blocked while the prod cluster's Prometheus is scaled to zero.Not started
Approval gateBlock promotion without approvals, for apps that opt in.Parked

How teams run releases now

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.

Linear Releases

Releases are continuous or scheduled, and CI attaches issues as commits land. Make the release first and attach work at merge time.

Jira and Keep a Changelog

"Unreleased" is a state teams already use. Tower can show it with no extra input.

Octopus, Kargo, GitLab

The thing that moves between environments has one identity. Each image is a candidate for a release.

Runway

A release has a kickoff, one named pilot and a short checklist, mostly evidence the system already has.

DORA and ServiceNow

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.

LaunchDarkly

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.

Most of a release happens before Tower makes a record

When the release record existsA release timeline from the last Flight release to the bake window. Today Tower's record appears only after Healthy. Glidepath's tracking ConfigMap starts at the release PR, lives on the dev cluster and is swept after 14 days. The proposed record runs the whole way through Draft, Candidate, Ready, Rolling out and Released.Last releaseprod @ previousPRs mergemain · 1..nImage builtsignedGround soakdev · testRelease PRproposedGates, mergeguardrailsCanaryprogressingHealthyhealthyBake+24h · +7dTOWER RECORD · TODAYno record · scope, notes and sign-off have nowhere to livesealed in gitGLIDEPATH TRACKING CONFIGMAPproposed → healthy · dev cluster · swept at 14 daysPROPOSED RECORDDraftCandidateReadyRolling outReleased · bakeOpens when the previous release ships. Seals into git at Healthy. Ends when the next one supersedes it.
Figure 1One release, from the last Flight release to the bake window. Columns are events, not equal time. The dashed bar is where no Tower record exists today.

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.

One branch, one next release

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.

Branches, merges and the draftMain carries the previous release, BIRCH. Two developers' feature branches merge into main, and each merge builds a candidate image: quiet-heron, then amber-finch. A third branch is still an open PR, so it is incoming, not scope. The draft, CEDAR, spans everything on main after BIRCH.mainfeature/bag-reprint · dev 1feature/read-pref · dev 2feature/tz-cutoff · PR #221 opennot merged · incoming, not scopeBIRCH · on prod1.8.2-6f1c0aaquiet-heron1.8.2-4c1e9a0amber-finch1.9.0-a41f9c2 · promote →NEXT RELEASE · CEDAR · DRAFTOpens when BIRCH ships. Scope is main after BIRCH. Promoting amber-finch ships both features.
Figure 2Each dot on main is a merge, and each merge builds an image with its own slug. quiet-heron still says 1.8.2 because nobody had bumped the version yet.

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.

Five phases on one record

Release record lifecycleMain phases Draft, Candidate, Ready, Rolling out and Released. Detours: Rework from Ready back to Candidate, and Aborted from Rolling out with a retry. Endings: Rolled back from Rolling out, and Superseded from Released when the next release is healthy.DETOURSENDINGSDraftopens at last shipCandidatesoaking on GroundReadyrelease PR, sign-offRolling outmerged · canaryReleasedhealthy, bakingBUILDPROMOTEMERGEHEALTHYReworkcandidate rejectedNO-GOREBUILDAbortedcanary paused or failedABORTRETRYRolled backfrom rollout or releasedROLLBACKSupersedednext release healthyNEXT LIVEReady is the new step: the release PR is open, gates run, and sign-off happens here.
Figure 3Nine states and ten transitions. Ready and the merge out of it are highlighted, because that is the new decision point.

Glidepath's states, folded in

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.

One identity across phases

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.

Where state lives

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.

What it looks like in Tower

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.

Ten changes, in priority order

01 · "Next release" live draft

R1

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.

02 · Candidates inside a release

R1

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.

03 · Sign-off before the Flight merge

R2

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.

04 · Readiness checklist, go or no-go

R2

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.

05 · Release notes with two audiences

R2

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.

06 · A named release owner

R2

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.

07 · Outcome loop and a two-part score

R3

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.

08 · Risk signals filled in automatically

R3

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.

09 · Scheduled releases, optional

R4

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.

10 · Fleet calendar and notifications

R4

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.

Schema v2 adds to v1

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.

Four stages, cheapest and most valuable first

R1 · Show the release before it ships

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.

R2 · Sign off before merging

Sign-off from PR reviews, an owner, a notes editor and draft file, and a seal that copies the draft in as schema v2.

R3 · Record the outcome

Rollbacks written back, Readiness and Outcome scores, bake-window SLO checks, and GitHub Releases with a Changelog view.

R4 · For teams that plan

Scheduled releases, Jira keys, a fleet calendar and messages at Ready and Released. The sign-off gate stays parked.

What not to build, and what's decided

Not buildingWhy
An issue trackerLink to GitHub Issues, Jira or Linear, and read keys from PR text. The backlog doesn't live in Tower.
A change advisory boardOne peer sign-off, backed by evidence.
A record per Ground deployAn image is a candidate. A release is what reaches Flight.
A PR for every draft editDrafts 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.

Decided October 13, 2026

Sources