controller v0.269.0: whole restore from the second drive; crash loops stopped; exact image digests; steps judged by their own .felhom.yml (decisions 26-28, R-661 R-666 R-667 R-668 R-664 R-665 R-662, 09 6.4 part 6)
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:
2026-09-24 12:18:39 +02:00
parent 7c3b3a9694
commit 3c6b49b31c
141 changed files with 3401 additions and 237 deletions
+28
View File
@@ -2,6 +2,7 @@ package stacks
import (
"strings"
"time"
"gitea.dooplex.hu/admin/felhom-controller/internal/util"
)
@@ -79,11 +80,38 @@ func CatalogOrder(s Stack) UpdateOrder {
}
}
if differing == 0 {
// v0.269.0 (`09` §6.4 part 6): the same TAG can be a different image. Behind only when the catalog
// holds a TESTED digest for the service, the installed digest is known and differs, and the test
// is NEWER than this install — never from a registry query, never on an unknown.
if digestBehind(s) {
return UpdateOrderBehind
}
return UpdateOrderCurrent
}
return UpdateOrderAhead
}
// digestBehind: see CatalogOrder. Pinned by TestDigest_FloatingTagBehindOnlyForANewerTestedDigest.
func digestBehind(s Stack) bool {
if len(s.CatalogDigests) == 0 || s.CatalogTestedAt.IsZero() {
return false
}
for svc, want := range s.CatalogDigests {
got, ok := s.AppConfig.InstalledImages[svc]
if !ok || got.Digest == "" || !digestRe.MatchString(want) || got.Digest == want {
continue
}
at, err := time.Parse(time.RFC3339, got.At)
if err != nil {
continue // when the install happened is unknown → never "behind" on it
}
if s.CatalogTestedAt.After(at) {
return true
}
}
return false
}
// CompareImageRefs orders two image references the way a human reads them: -1 when a is older than
// b, 0 when they are the same version, 1 when a is newer. The second return is the whole point —
// FALSE means "these two cannot be ordered", and every caller must treat that as "do not know"