carlos / platform Public

console: bake status line, production toggle, fast-track control and the stable-promote prompt

Task 8 of console-bake-controls (spec §7 UI): renders .Detail.Production
as a status line with owner-only turn on/off controls and the persistent
prod-wiring nudge; adds the owner-only fast-track control gated on a
non-zero BakedUntil per held channel row, plus its permanent
"fast-tracked" badge; adds the enable_bake checkbox to the stable-promote
form while the flag is off. app.html's channels table gets the same
fast-tracked note for consistency.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VioXzogWf1Xi4Rswz2B6EK
Paul Campbell 72b0abe359e6253d7482f74a583ff15e6222e46d parent 81a62f9
3 files changed, +152 −2
  • internal/console/ladder_test.go +96 −0
  • internal/console/templates/app.html +1 −1
  • internal/console/templates/ladder.html +55 −1
diff --git a/internal/console/ladder_test.go b/internal/console/ladder_test.go
index 2194c2768..c0b90edf3 100644
--- a/internal/console/ladder_test.go
+++ b/internal/console/ladder_test.go
@@ -536,3 +536,99 @@ func TestLadderPromotesOntoAnExpiredCanary(t *testing.T) {
t.Fatalf("expired canary still holds %s — expiry has become a gate", p.Version)
}
}
+
+// The bake status line and its controls (spec §7): off-by-default says so;
+// an owner gets the turn-on/turn-off controls; a held channel row offers
+// the owner fast-track; the stable promote form carries the enable_bake
+// prompt while the flag is off.
+func TestLadderShowsBakeStateAndControls(t *testing.T) {
+ fx := newConsoleFixture(t)
+ shipVersion(t, fx, "kass", "v3")
+ seedChannel(t, fx, "kass", "beta", "v3")
+ c := signInPaul(t, fx)
+
+ _, _, body := get(t, c, fx.u("/apps/kass"))
+ if !strings.Contains(body, "Supply-chain bake") {
+ t.Fatalf("no bake status line:\n%s", body)
+ }
+ if !strings.Contains(body, `action="/a/bab/apps/kass/production"`) {
+ t.Fatal("owner is offered no production toggle")
+ }
+ // Off by default, and the stable promote form asks the question.
+ if !strings.Contains(body, `name="enable_bake"`) {
+ t.Fatal("the stable promote form does not carry the enable_bake prompt")
+ }
+
+ // Flip on; the held beta head now shows its window and the fast-track
+ // control appears on it.
+ if err := release.SetProduction(context.Background(), fx.rel, fx.priv,
+ release.OpsAccount, "kass", true, "test"); err != nil {
+ t.Fatal(err)
+ }
+ _, _, body = get(t, c, fx.u("/apps/kass"))
+ if !strings.Contains(body, `action="/a/bab/apps/kass/fasttrack"`) {
+ t.Fatalf("no fast-track control on a held head:\n%s", rungBlock(t, body, "beta"))
+ }
+ if strings.Contains(body, `name="enable_bake"`) {
+ t.Fatal("the enable_bake prompt survived the flag turning on")
+ }
+
+ // Waive it; the badge is permanent and the control is gone.
+ if _, err := release.FastTrack(context.Background(), fx.rel, fx.priv,
+ release.OpsAccount, "kass", "beta", "test"); err != nil {
+ t.Fatal(err)
+ }
+ _, _, body = get(t, c, fx.u("/apps/kass"))
+ b := rungBlock(t, body, "beta")
+ if !strings.Contains(b, "fast-tracked") {
+ t.Errorf("no permanent fast-tracked badge:\n%s", b)
+ }
+ if strings.Contains(b, `action="/a/bab/apps/kass/fasttrack"`) {
+ t.Error("a waived head still offers the fast-track control")
+ }
+}
+
+// A member sees state, never the owner-only controls (off / fast-track) —
+// but IS offered turn-on, the safe direction.
+func TestLadderBakeControlsMemberView(t *testing.T) {
+ fx := newConsoleFixture(t)
+ if err := fx.st.AddMember(OpsAccountID, "sam@keymail.dev", RoleMember, time.Now()); err != nil {
+ t.Fatal(err)
+ }
+ fx.setUser("sam@keymail.dev")
+ c := newBrowser(t)
+ signUp(t, fx, c, "code-sam-baketpl")
+ _, _, body := get(t, c, fx.u("/apps/kass"))
+ if !strings.Contains(body, "Supply-chain bake") {
+ t.Fatal("member sees no bake status")
+ }
+ if !strings.Contains(body, `name="production" value="on"`) {
+ t.Error("member is not offered the turn-on control")
+ }
+ if strings.Contains(body, `name="production" value="off"`) {
+ t.Error("member is offered the owner-only turn-off control")
+ }
+ if strings.Contains(body, `/fasttrack"`) {
+ t.Error("member is offered the owner-only fast-track control")
+ }
+}
+
+// The prod-wiring nudge: a stable route with bake off gets the persistent
+// recommendation line.
+func TestLadderNudgesProdWiredBakeOff(t *testing.T) {
+ fx := newConsoleFixture(t)
+ c := signInPaul(t, fx)
+ // The fixture's kass.carlosapp.dev is on stable and the flag is off (the
+ // default): that IS the nudge condition.
+ _, _, body := get(t, c, fx.u("/apps/kass"))
+ if !strings.Contains(body, "adopts releases immediately") {
+ t.Fatalf("no prod-wiring nudge:\n%s", body)
+ }
+ if err := release.SetProduction(context.Background(), fx.rel, fx.priv,
+ release.OpsAccount, "kass", true, "test"); err != nil {
+ t.Fatal(err)
+ }
+ if _, _, body := get(t, c, fx.u("/apps/kass")); strings.Contains(body, "adopts releases immediately") {
+ t.Fatal("the nudge survived the flag turning on")
+ }
+}
diff --git a/internal/console/templates/app.html b/internal/console/templates/app.html
index d25b31297..8684587a9 100644
--- a/internal/console/templates/app.html
+++ b/internal/console/templates/app.html
@@ -95,7 +95,7 @@
{{else}}
<td><code>{{.Version}}</code></td>
<td>{{fmtTime .PromotedAt}}</td>
- <td>{{if .BakedUntil.IsZero}}live{{else}}bakes until ~{{fmtTime .BakedUntil}}{{end}}</td>
+ <td>{{if .BakedUntil.IsZero}}live{{else}}bakes until ~{{fmtTime .BakedUntil}}{{end}}{{if .FastTrack}} · fast-tracked{{end}}</td>
{{end}}
<td>{{if and $.CanPromote .HasHistory (not .Err)}}
<form method="post" action="{{$.Base}}/apps/{{$.Detail.App}}/rollback">
diff --git a/internal/console/templates/ladder.html b/internal/console/templates/ladder.html
index 72448ae20..f4290fe0c 100644
--- a/internal/console/templates/ladder.html
+++ b/internal/console/templates/ladder.html
@@ -3,6 +3,47 @@
<p class="small">A channel is maturity, not a place: the same build climbs
canary → edge → beta → stable, and a promotion moves a signed pointer without
touching what any host is wired to.</p>
+{{with .Detail.Production}}
+<div class="bake">
+ {{if .Err}}
+ <p class="error">Production flag unreadable: {{.Err}} — hosts treat this app as in production and keep baking.</p>
+ {{else if .On}}
+ <p class="small">Supply-chain bake: <strong>on</strong> — beta holds 24h and stable 72h on each host before it adopts.
+ {{if and $.CanPromote $.IsOwner}}
+ <form method="post" action="{{$.Base}}/apps/{{$.Detail.App}}/production" class="inline">
+ <input type="hidden" name="csrf" value="{{$.CSRF}}">
+ <input type="hidden" name="production" value="off">
+ <button type="submit" class="linkish">Turn off</button>
+ </form>
+ <span class="small">— confirms with your passkey; each host honors it after the stable window (~72h) on its own clock</span>
+ {{end}}
+ </p>
+ {{else if .TurningOff}}
+ <p class="small">Supply-chain bake: <strong>turning off</strong> — hosts honor it from ~{{fmtTime .EffectiveAt}}, each on its own clock.
+ {{if $.CanPromote}}
+ <form method="post" action="{{$.Base}}/apps/{{$.Detail.App}}/production" class="inline">
+ <input type="hidden" name="csrf" value="{{$.CSRF}}">
+ <input type="hidden" name="production" value="on">
+ <button type="submit" class="linkish">Turn back on</button>
+ </form>
+ {{end}}
+ </p>
+ {{else}}
+ <p class="small">Supply-chain bake: <strong>off</strong> (default) — hosts adopt releases immediately.
+ {{if $.CanPromote}}
+ <form method="post" action="{{$.Base}}/apps/{{$.Detail.App}}/production" class="inline">
+ <input type="hidden" name="csrf" value="{{$.CSRF}}">
+ <input type="hidden" name="production" value="on">
+ <button type="submit" class="linkish">Turn on</button>
+ </form>
+ {{end}}
+ </p>
+ {{end}}
+ {{if .Nudge}}
+ <p class="small nudge">{{icon "triangle-alert"}} This app looks production-wired but adopts releases immediately — consider enabling the supply-chain bake.</p>
+ {{end}}
+</div>
+{{end}}
<ol class="ladder">
{{range .Detail.Ladder}}
<li class="rung" id="rung-{{.Rung}}">
@@ -16,7 +57,16 @@ touching what any host is wired to.</p>
<p><code>{{.Channel}}</code> holds <code>{{.Version}}</code>
<span class="small">promoted {{fmtTime .PromotedAt}}
{{if .Actor}}by {{.Actor}}{{else}}— by whom is not recorded, on a pointer written before the trail existed{{end}}
- {{if .Hotfix}}· ladder bypassed (hotfix){{end}}</span></p>
+ {{if .Hotfix}}· ladder bypassed (hotfix){{end}}
+ {{if .FastTrack}}· fast-tracked past the bake window{{end}}</span></p>
+ {{if and (not .BakedUntil.IsZero) $.CanPromote $.IsOwner}}
+ <form method="post" action="{{$.Base}}/apps/{{$.Detail.App}}/fasttrack" class="inline">
+ <input type="hidden" name="csrf" value="{{$.CSRF}}">
+ <input type="hidden" name="channel" value="{{.Channel}}">
+ <button type="submit" class="linkish">Fast-track {{.Version}}</button>
+ </form>
+ <span class="small">— in its bake window until ~{{fmtTime .BakedUntil}}; confirms with your passkey</span>
+ {{end}}
{{end}}
{{if .Routes}}
<ul class="routes">
@@ -46,6 +96,10 @@ touching what any host is wired to.</p>
<input type="hidden" name="csrf" value="{{$.CSRF}}">
<input type="hidden" name="channel" value="{{.Up.To}}">
<input type="hidden" name="version" value="{{.Up.Version}}">
+ {{if and (eq .Up.To "stable") (not $.Detail.Production.On)}}
+ <label class="small"><input type="checkbox" name="enable_bake" value="1" checked>
+ This app is in production — enable the supply-chain bake (recommended)</label>
+ {{end}}
<button type="submit" class="linkish">Promote {{.Up.Version}} to {{.Up.To}}</button>
</form>
{{if .Up.StepUp}}<span class="small">— confirms with your passkey</span>{{end}}