| index d249e3c39..8d8a9eb69 100644 |
| --- a/internal/console/server.go |
| +++ b/internal/console/server.go |
| @@ -446,10 +446,11 @@ type page struct { |
| Flash string // one-shot success notice (cookie-carried across a redirect) |
| FlashErr string // one-shot error notice |
| |
| - Apps []appRow // account home |
| - Detail *AppView // /a/{sqid}/apps/{app} |
| - Versions []VersionView // app page: shipped releases, newest first |
| - Tags []release.Tag // releases tab: every tag this app has cut, changelog and all |
| + Apps []appRow // account home |
| + Detail *AppView // /a/{sqid}/apps/{app} |
| + Versions []VersionView // app page: shipped releases, newest first |
| + Tags []release.Tag // releases tab: every tag this app has cut, changelog and all |
| + RetentionKeep int // releases tab: >0 when an ambient retention policy is set (spec 2026-08-10-releases-retention-design.md §7) |
| // ReleaseSummary is the Releases tab's lede strip: what is live and |
| // what is next, folded from Detail.Channels once the tab has them. |
| ReleaseSummary ReleaseSummary |
| @@ -1564,6 +1565,13 @@ func (s *Server) handleAppReleases(w http.ResponseWriter, r *http.Request) { |
| p.Tags = tags |
| } |
| p.Versions = versions |
| + // Read-only in v1: the policy is set via `carlos releases retention`; |
| + // this line just keeps the page honest about why old versions vanish. |
| + if ret, err := release.GetRetention(r.Context(), s.cfg.Releases, acct.Sqid, p.AppName); err != nil { |
| + log.Printf("console: retention %s: %v", p.AppName, err) |
| + } else if ret != nil { |
| + p.RetentionKeep = ret.Keep |
| + } |
| // The "how do I give this a URL" copy — not shared chrome (it's computed |
| // per app page, not carried by appPage), but the same fill the Instances |
| // tab's empty state shows: attachHint below. |