v0.282.0: after_setup (the app's own sign-up switch), close sign-up now (decision 49), probes read lists + done_status (R-715)
gates / gates (push) Successful in 27s
gates / gates (push) Successful in 27s
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:
@@ -134,6 +134,9 @@ func (m *Manager) OpenSignupWindow(name string) (string, error) {
|
||||
if err := m.removeSignupBlockFile(name); err != nil {
|
||||
return "", err
|
||||
}
|
||||
if st.Meta.AfterSetup != nil && len(st.Meta.AfterSetup.Env) > 0 { // v0.282.0: the app's own switch opens too (a restart)
|
||||
m.goNativeLock(name, false, "the household's window")
|
||||
}
|
||||
m.logger.Printf("[INFO] [stacks] %s: the household opened sign-up until %s — the loop closes it again", name, until)
|
||||
return until, nil
|
||||
}
|
||||
@@ -178,9 +181,27 @@ func (m *Manager) reconcileSignupBlocks() {
|
||||
if err := m.writeSignupBlock(n, w.hosts, w.fragment); err != nil {
|
||||
m.logger.Printf("[ERROR] [stacks] %s: the sign-up block could not be written: %v — sign-up is OPEN until it is", n, err)
|
||||
}
|
||||
// v0.282.0: the app's own switch back on (after the household's window), or a retry of a failed attempt —
|
||||
// at most every nativeLockRetry, so a switch that cannot be set does not restart the app every tick.
|
||||
if st, ok := m.GetStack(n); ok && st.Meta.AfterSetup != nil && st.AppConfig != nil && st.AppConfig.SetupGate != nil &&
|
||||
st.AppConfig.SetupGate.NativeLock != NativeLockApplied {
|
||||
last := st.AppConfig.AfterSetup
|
||||
due := last == nil || last.OK
|
||||
if !due {
|
||||
if t, err := time.Parse(time.RFC3339, last.At); err != nil || m.now().Sub(t) > nativeLockRetry {
|
||||
due = true
|
||||
}
|
||||
}
|
||||
if due {
|
||||
m.goNativeLock(n, true, "the loop (window ended or retry)")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// nativeLockRetry: how often the loop retries an app's own switch that could not be set.
|
||||
var nativeLockRetry = 30 * time.Minute
|
||||
|
||||
// SetupGateProbe asks the app's own "setup done" status once (the household's button asks it first, Part A of the
|
||||
// 2026-09-29 afternoon brief). has=false: the template declares no probe.
|
||||
func (m *Manager) SetupGateProbe(name string) (has bool, done bool, got string, err error) {
|
||||
@@ -192,17 +213,20 @@ func (m *Manager) SetupGateProbe(name string) (has bool, done bool, got string,
|
||||
if p == nil || p.URL == "" {
|
||||
return false, false, "", nil
|
||||
}
|
||||
body, err := setupGateProbeGet(p.URL)
|
||||
if err != nil {
|
||||
return true, false, "", err
|
||||
}
|
||||
done, got = probeSaysDone(body, p.Field, p.Done)
|
||||
return true, done, got, nil
|
||||
done, got, err = probeOnce(p)
|
||||
return true, done, got, err
|
||||
}
|
||||
|
||||
// SetSetupGateProbeGetForTest swaps the probe's HTTP read (a test seam for other packages); returns the restore.
|
||||
// f's error means "unreadable"; a nil error is a 200 with that body.
|
||||
func SetSetupGateProbeGetForTest(f func(url string) ([]byte, error)) func() {
|
||||
old := setupGateProbeGet
|
||||
setupGateProbeGet = f
|
||||
return func() { setupGateProbeGet = old }
|
||||
old := setupGateProbeFetch
|
||||
setupGateProbeFetch = func(url string) (int, []byte, error) {
|
||||
b, err := f(url)
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
return 200, b, nil
|
||||
}
|
||||
return func() { setupGateProbeFetch = old }
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user