v0.283.0: apps go off-site by themselves (decision 50) with a size warning; a Stop holds during a backup (R-721); page slips (R-724/R-725)
gates / gates (push) Successful in 25s
gates / gates (push) Successful in 25s
Red-proofs RP31-RP38. MinAgent 0.131.0 (unchanged). Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
This commit is contained in:
@@ -156,3 +156,16 @@ func isObservedUp(s Stack) bool {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// WantsStopped (R-721, v0.283.0) reports whether the household's own recorded intent for the stack is
|
||||
// „stopped". The machines that stop an app for their own purposes and start it again afterwards — the
|
||||
// whole-guest backup's quiesce, the nightly volume dump — ask this before their restart, so a Stop the
|
||||
// household pressed WHILE the app was down for them holds. Measured 2026-09-29 on a fresh box: Stop at
|
||||
// 19:37:16 recorded `desired state … stopped`, and the backup's resume started the app the same second.
|
||||
func (m *Manager) WantsStopped(name string) bool {
|
||||
st, ok := m.GetStack(name)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
return DesiredStateOf(*st) == DesiredStateStopped
|
||||
}
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
package stacks
|
||||
|
||||
import "testing"
|
||||
|
||||
// R-721 (v0.283.0): the nightly update leg never presses an app the household stopped — an update brings the
|
||||
// app up with its new images, which would undo the Stop. Measured from source 2026-09-30: legCandidate had no
|
||||
// desired-state check at all.
|
||||
// COMPANION RED-PROOF: remove the DesiredStateStopped check in legCandidate → the step is pressed.
|
||||
func TestR721_UpdateLegLeavesAStoppedAppAlone(t *testing.T) {
|
||||
m, dir, _, ups, _ := legManager(t)
|
||||
writeLadder(t, m, ladderLine(ladderA, ladderB), ladderLine(ladderB, ladderC))
|
||||
legOpts(m, nil)
|
||||
if err := m.SetDesiredState("nextcloud", DesiredStateStopped); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
s := mustLeg(t, m)
|
||||
if len(*ups) != 0 || pinOf(t, dir) != ladderA {
|
||||
t.Fatalf("the leg updated an app the household stopped (ups=%v)", *ups)
|
||||
}
|
||||
if st := legStepFor(s, "nextcloud"); st.Reason != LegSkipStoppedByHousehold {
|
||||
t.Fatalf("skip reason %+v, want %q", st, LegSkipStoppedByHousehold)
|
||||
}
|
||||
}
|
||||
@@ -50,7 +50,9 @@ const (
|
||||
LegSkipWindowEnd = "window_end"
|
||||
LegSkipSwitchedOff = "switched_off"
|
||||
LegSkipCancelled = "cancelled"
|
||||
legCurrent = "current" // not a skip: nothing to do
|
||||
// LegSkipStoppedByHousehold (R-721, v0.283.0): the household stopped the app; an update would start it.
|
||||
LegSkipStoppedByHousehold = "stopped_by_household"
|
||||
legCurrent = "current" // not a skip: nothing to do
|
||||
|
||||
LegOutcomeDone = "done"
|
||||
LegOutcomeUndone = "undone"
|
||||
@@ -395,6 +397,9 @@ func (m *Manager) legCandidate(ctx context.Context, name string, o *UpdateLegOpt
|
||||
if st.HoldReason != "" || st.updateHeld {
|
||||
return LadderEntry{}, LegSkipHeld
|
||||
}
|
||||
if DesiredStateOf(*st) == DesiredStateStopped {
|
||||
return LadderEntry{}, LegSkipStoppedByHousehold
|
||||
}
|
||||
if g := m.guards(); g != nil {
|
||||
if held, _ := g.HoldFor(name); held {
|
||||
return LadderEntry{}, LegSkipHeld
|
||||
|
||||
Reference in New Issue
Block a user