carlos / platform Public

host: terminal() is nil-safe like matches(); cover applied.json

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015QCUCpfxbCyEAVQ2m51dH5
Paul Campbell pushed by paul@keymail.dev b11192f34305d18caa47c9da4fc4feb541f24972 parent 1e2f33a
2 files changed, +21 −0
  • internal/host/systemstate.go +3 −0
  • internal/host/systemstate_test.go +18 −0
diff --git a/internal/host/systemstate.go b/internal/host/systemstate.go
index 5928e9504..560c28e3f 100644
--- a/internal/host/systemstate.go
+++ b/internal/host/systemstate.go
@@ -67,6 +67,9 @@ func (r *systemResult) matches(channel, version string, promotedAt time.Time) bo
// terminal: outcomes the leg must not retry for the same pointer.
func (r *systemResult) terminal() bool {
+ if r == nil {
+ return false
+ }
switch r.Outcome {
case systemOutcomeApplied, systemOutcomeRolledBack, systemOutcomeRollbackFailed, systemOutcomeRefused:
return true
diff --git a/internal/host/systemstate_test.go b/internal/host/systemstate_test.go
index 69f0b68ca..b7ea13993 100644
--- a/internal/host/systemstate_test.go
+++ b/internal/host/systemstate_test.go
@@ -69,6 +69,16 @@ func TestSystemStateReadersRoundTrip(t *testing.T) {
if err != nil || len(seen) != 0 {
t.Fatalf("missing seen: %v %v", seen, err)
}
+ if a, err := readSystemApplied(opt); err != nil || a != nil {
+ t.Fatalf("missing applied: %v %v", a, err)
+ }
+ if j, err := readSystemJournal(opt); err != nil || j != nil {
+ t.Fatalf("missing journal: %v %v", j, err)
+ }
+ var nilResult *systemResult
+ if nilResult.matches("stable", "v1", time.Time{}) || nilResult.terminal() {
+ t.Fatal("nil *systemResult must answer matches/terminal as false, not panic")
+ }
at := time.Date(2026, 9, 6, 9, 0, 0, 0, time.UTC)
res := systemResult{Channel: "stable", Version: "v1", PromotedAt: at, Outcome: systemOutcomeRolledBack, Reason: "gate", At: at}
if err := writeJSONDurable(filepath.Join(root, "result.json"), res, 0o644); err != nil {
@@ -105,4 +115,12 @@ func TestSystemStateReadersRoundTrip(t *testing.T) {
if err != nil || j2.Phase != systemPhaseInstalled || j2.UnitsAdded[0] != "a.path" {
t.Fatalf("journal = %+v err=%v", j2, err)
}
+ applied := systemApplied{Version: "v1", Channel: "stable", At: at}
+ if err := writeJSONDurable(filepath.Join(root, "applied.json"), applied, 0o644); err != nil {
+ t.Fatal(err)
+ }
+ a2, err := readSystemApplied(opt)
+ if err != nil || a2.Version != "v1" || a2.Channel != "stable" || !a2.At.Equal(at) {
+ t.Fatalf("applied = %+v err=%v", a2, err)
+ }
}