carlos / platform Public

restarts watch and memory drop-in: the last three review findings (#284)

Summary

Astra's second pass on slice A: all four P1s fixed, three P2s left.
- A reload owed to systemd is a marker file beside the drop-in, written
  before the file changes and removed after the reload, instead of a
  rollback. Every way of stopping in between leaves the marker, so the
  next run reloads even though the bytes already match.
- The saved restart watch is validated on load (version, times, counter
  ranges); anything else is an empty watch. A decodable file with no
  baseline no longer turns a lifetime counter into recent restarts.
- A clock stepped backwards rebaselines the watch instead of keeping
  future-dated restarts in the window.
Census overlay regenerated.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
claude agent, operated by paul@keymail.dev db57e5caec44e0d61cbc9fb56141345092e753f9 parent bf7074d
6 files changed, +154 −52
  • internal/host/restartwatch.go +35 −6
  • internal/host/restartwatch_test.go +59 −0
  • internal/ops/litestream_memory.go +22 −17
  • internal/ops/ops_test.go +31 −22
  • internal/provision/plan14_root_reads_test.go +1 −1
  • internal/provision/testdata/plan14-root-reads-ls284.tsv +6 −6

CI cancelled — superseded by a newer push — run details

CI log
=== gate ===
cd launchctx && go vet ./... && go test ./... -count=1
ok  	amadan.net/carlos/platform.git/launchctx	0.011s
?   	amadan.net/carlos/platform.git/launchctx/controlname	[no test files]
?   	amadan.net/carlos/platform.git/launchctx/wireid	[no test files]
go vet ./...
gofmt -l .: clean
go test ./... -count=1 -timeout 30m
ok  	github.com/carlosframework/platform	332.941s
?   	github.com/carlosframework/platform/cmd/changelog	[no test files]
ok  	github.com/carlosframework/platform/cmd/console	3.026s
ok  	github.com/carlosframework/platform/cmd/moveprobe	93.193s
ok  	github.com/carlosframework/platform/cmd/zddprobe	0.377s
ok  	github.com/carlosframework/platform/internal/activator	226.409s
ok  	github.com/carlosframework/platform/internal/agentsock	0.018s
ok  	github.com/carlosframework/platform/internal/alerting	0.003s
ok  	github.com/carlosframework/platform/internal/analytics	0.019s
ok  	github.com/carlosframework/platform/internal/appconfig	2.269s
ok  	github.com/carlosframework/platform/internal/apperr	0.070s
ok  	github.com/carlosframework/platform/internal/applog	0.129s
ok  	github.com/carlosframework/platform/internal/awsinv	0.005s
ok  	github.com/carlosframework/platform/internal/boxchoice	0.004s
ok  	github.com/carlosframework/platform/internal/boxid	0.842s
?   	github.com/carlosframework/platform/internal/boxid/boxidtest	[no test files]
?   	github.com/carlosframework/platform/internal/boxid/internal/signtest	[no test files]
ok  	github.com/carlosframework/platform/internal/boxvolumes	0.008s
ok  	github.com/carlosframework/platform/internal/caps	0.011s
ok  	github.com/carlosframework/platform/internal/certwarm	0.002s
ok  	github.com/carlosframework/platform/internal/cgroup	0.693s
ok  	github.com/carlosframework/platform/internal/changelog	0.308s
ok  	github.com/carlosframework/platform/internal/clamd	0.054s
?   	github.com/carlosframework/platform/internal/cliflag	[no test files]

amadan: job cancelled — a newer push superseded it
diff --git a/internal/host/restartwatch.go b/internal/host/restartwatch.go
index 01160b67d..028cfbc06 100644
--- a/internal/host/restartwatch.go
+++ b/internal/host/restartwatch.go
@@ -44,11 +44,16 @@ type unitRestartState struct {
// the agent's own restarts are counted: carlos-edge.service is one of the
// units, and a watch held only in memory would die with each of them.
type restartWatch struct {
+ // V is the saved shape's version; loadRestartWatch takes no other.
+ V int `json:"v"`
// At is the last tick that got an answer from systemd.
At time.Time `json:"at"`
Units map[string]*unitRestartHistory `json:"units"`
}
+// restartWatchVersion is the only saved shape this build reads.
+const restartWatchVersion = 1
+
type unitRestartHistory struct {
// Last is the counter at the last tick.
Last int64 `json:"last"`
@@ -79,14 +84,17 @@ type restartBump struct {
//
// A gap longer than the window between two answers leaves nothing to count:
// the watch knows how far each counter moved and not when, so it starts
-// again from the present rather than date old restarts to this tick.
+// again from the present rather than date old restarts to this tick. A clock
+// that has gone backwards is the same: what was saved is dated in a future
+// the watch can no longer measure against.
func (w *restartWatch) tick(now time.Time, units map[string]unitRestartState) map[string]metrics.UnitRestarts {
if units == nil {
return nil
}
- if w.Units == nil || (!w.At.IsZero() && now.Sub(w.At) > restartWindow) {
+ if w.Units == nil || now.Before(w.At) || (!w.At.IsZero() && now.Sub(w.At) > restartWindow) {
w.Units = map[string]*unitRestartHistory{}
}
+ w.V = restartWatchVersion
for name := range w.Units {
if _, ok := units[name]; !ok {
delete(w.Units, name) // unloaded: nothing left to watch
@@ -133,18 +141,39 @@ func (w *restartWatch) tick(now time.Time, units map[string]unitRestartState) ma
// alert state it feeds.
func restartWatchPath() string { return filepath.Join(dataDir(), "monitor", "restarts.json") }
-// loadRestartWatch reads a saved watch. Anything unreadable is an empty
-// watch, which is always a safe place to start from: it can delay an alert by
-// a few ticks and can never invent one.
+// loadRestartWatch reads a saved watch. Anything unreadable, of another
+// version, or not exactly the shape tick leaves behind is an empty watch,
+// which is always a safe place to start from: it can delay an alert by a few
+// ticks and can never invent one. The file sits in the data directory, so it
+// is checked as input, not trusted as this program's own output: a unit
+// history with no baseline would read a healthy unit's lifetime counter as
+// that many restarts just now.
func loadRestartWatch(path string) restartWatch {
var w restartWatch
b, err := os.ReadFile(path)
- if err != nil || json.Unmarshal(b, &w) != nil {
+ if err != nil || json.Unmarshal(b, &w) != nil || !w.wellFormed() {
return restartWatch{}
}
return w
}
+func (w restartWatch) wellFormed() bool {
+ if w.V != restartWatchVersion || w.At.IsZero() {
+ return false
+ }
+ for _, h := range w.Units {
+ if h == nil || h.Last < 0 || h.QuietSince.IsZero() || h.QuietSince.After(w.At) {
+ return false
+ }
+ for _, b := range h.Bumps {
+ if b.N <= 0 || b.At.IsZero() || b.At.After(w.At) {
+ return false
+ }
+ }
+ }
+ return true
+}
+
// saveRestartWatch writes the watch by temp file and rename, like
// persistAlertState. Best effort: a failed save costs history across the
// next agent restart, nothing more.
diff --git a/internal/host/restartwatch_test.go b/internal/host/restartwatch_test.go
index 9efda2ccd..abdbe2979 100644
--- a/internal/host/restartwatch_test.go
+++ b/internal/host/restartwatch_test.go
@@ -1,6 +1,7 @@
package host
import (
+ "os"
"path/filepath"
"testing"
"time"
@@ -152,6 +153,64 @@ func TestRestartWatchSavedAcrossAnAgentRestart(t *testing.T) {
}
}
+// The saved watch is a file in the data directory, so it is read as input. A
+// history that decodes but is not what tick writes must not become a
+// baseline: a unit with no recorded counter would turn a healthy unit's
+// lifetime 900 into 900 restarts at startup.
+func TestRestartWatchDiscardsASavedWatchItDidNotWrite(t *testing.T) {
+ t0 := time.Date(2026, 10, 13, 12, 0, 0, 0, time.UTC)
+ at := t0.Format(time.RFC3339)
+ for name, body := range map[string]string{
+ "not json": `{`,
+ "no version": `{"at":"` + at + `","units":{"carlos-litestream.service":{"last":5,"quiet_since":"` + at + `"}}}`,
+ "another version": `{"v":2,"at":"` + at + `","units":{}}`,
+ "no time": `{"v":1,"units":{"carlos-litestream.service":{"last":5,"quiet_since":"` + at + `"}}}`,
+ "empty unit history": `{"v":1,"at":"` + at + `","units":{"carlos-litestream.service":{}}}`,
+ "null unit history": `{"v":1,"at":"` + at + `","units":{"carlos-litestream.service":null}}`,
+ "negative counter": `{"v":1,"at":"` + at + `","units":{"carlos-litestream.service":{"last":-3,"quiet_since":"` + at + `"}}}`,
+ "negative rise": `{"v":1,"at":"` + at + `","units":{"carlos-litestream.service":{"last":5,"quiet_since":"` + at + `","bumps":[{"at":"` + at + `","n":-900}]}}}`,
+ "rise from the future": `{"v":1,"at":"` + at + `","units":{"carlos-litestream.service":{"last":5,"quiet_since":"` + at + `","bumps":[{"at":"2030-01-01T00:00:00Z","n":4}]}}}`,
+ } {
+ path := filepath.Join(t.TempDir(), "restarts.json")
+ if err := os.WriteFile(path, []byte(body), 0o644); err != nil {
+ t.Fatal(err)
+ }
+ w := loadRestartWatch(path)
+ if w.Units != nil || !w.At.IsZero() {
+ t.Errorf("%s: loaded as %+v, want an empty watch", name, w)
+ continue
+ }
+ if got := w.tick(t0.Add(time.Minute), oneUnit(900)); got[lsUnit].Count != 0 || got[lsUnit].Settled {
+ t.Errorf("%s: a healthy unit at 900 read as %+v, want a fresh baseline", name, got[lsUnit])
+ }
+ }
+}
+
+// A clock stepped backwards leaves the saved rises dated in the future. They
+// are dropped with the rest of the history, not kept "inside the window"
+// until the clock catches up with them.
+func TestRestartWatchRebaselinesWhenTheClockGoesBackwards(t *testing.T) {
+ path := filepath.Join(t.TempDir(), "restarts.json")
+ noon := time.Date(2026, 10, 13, 12, 0, 0, 0, time.UTC)
+ var w restartWatch
+ w.tick(noon, oneUnit(10))
+ if got := w.tick(noon.Add(time.Minute), oneUnit(14)); got[lsUnit].Count != 4 {
+ t.Fatalf("before the step: %+v, want 4", got[lsUnit])
+ }
+ if err := saveRestartWatch(path, w); err != nil {
+ t.Fatal(err)
+ }
+ // The clock is corrected back an hour and the agent restarts.
+ w = loadRestartWatch(path)
+ got := w.tick(noon.Add(-time.Hour), oneUnit(14))
+ if got[lsUnit].Count != 0 || got[lsUnit].Settled {
+ t.Fatalf("after the step: %+v, want no dated restarts and not settled", got[lsUnit])
+ }
+ if got = w.tick(noon.Add(-time.Hour).Add(restartWindow), oneUnit(14)); got[lsUnit].Count != 0 || !got[lsUnit].Settled {
+ t.Fatalf("a window later: %+v, want settled at zero", got[lsUnit])
+ }
+}
+
func TestParseUnitRestarts(t *testing.T) {
// The shape systemd 257 prints: properties in its own order, a blank line
// between units.
diff --git a/internal/ops/litestream_memory.go b/internal/ops/litestream_memory.go
index c503cfa2a..55f359c0d 100644
--- a/internal/ops/litestream_memory.go
+++ b/internal/ops/litestream_memory.go
@@ -16,7 +16,6 @@ package ops
// this verb keeps the unit's own default.
import (
- "errors"
"fmt"
"os"
"path/filepath"
@@ -85,33 +84,39 @@ func LitestreamMemoryDropIn(n int) string {
// reload for one, and a half-written drop-in falls back to the unit's smaller
// default under a replicator that is already using more.
//
-// A reload that fails puts the previous file back. The file on disk then
-// still differs from what this verb wants, so the next run tries again;
-// leaving the new file in place would have every later run find nothing to
-// change and never reload.
+// A reload is owed from the moment the file is about to change until systemd
+// has taken it, and that debt is a marker file beside the drop-in, not a
+// comparison of bytes: once the new file is on disk a later run finds nothing
+// to rewrite, and would never reload a cap an earlier run failed to load.
+// The marker is written first and removed last, so every way of stopping in
+// between leaves it for the next run. systemd ignores it (no .conf suffix).
func writeMemoryDropIn(path string, n int, reload func() error) (changed bool, err error) {
want := LitestreamMemoryDropIn(n)
+ pending := path + ".reload-pending"
prior, readErr := os.ReadFile(path)
- switch {
- case readErr == nil && string(prior) == want:
- return false, nil
- case readErr != nil && !os.IsNotExist(readErr):
+ if readErr != nil && !os.IsNotExist(readErr) {
return false, readErr
}
+ current := readErr == nil && string(prior) == want
+ if _, err := os.Lstat(pending); current && os.IsNotExist(err) {
+ return false, nil
+ }
if err := os.MkdirAll(filepath.Dir(path), 0o755); err != nil {
return false, err
}
- if err := safefile.WriteFile(path, []byte(want), 0o644); err != nil {
+ if err := safefile.WriteFile(pending, nil, 0o644); err != nil {
return false, err
}
- if err := reload(); err != nil {
- var undo error
- if readErr == nil {
- undo = safefile.WriteFile(path, prior, 0o644)
- } else {
- undo = os.Remove(path)
+ if !current {
+ if err := safefile.WriteFile(path, []byte(want), 0o644); err != nil {
+ return false, err
}
- return false, errors.Join(fmt.Errorf("systemd did not reload: %w", err), undo)
}
+ if err := reload(); err != nil {
+ return false, fmt.Errorf("systemd did not reload: %w", err)
+ }
+ // systemd has the cap. A marker that will not go only costs the next run
+ // one reload it did not need.
+ _ = os.Remove(pending)
return true, nil
}
diff --git a/internal/ops/ops_test.go b/internal/ops/ops_test.go
index a3a414636..7e9d89a79 100644
--- a/internal/ops/ops_test.go
+++ b/internal/ops/ops_test.go
@@ -941,7 +941,7 @@ func TestWriteMemoryDropInReloadsOnlyOnChange(t *testing.T) {
t.Errorf("drop-in mode = %v (%v), want 0644: systemd reads it as root, operators as anyone", fi.Mode().Perm(), err)
}
if ents, _ := os.ReadDir(filepath.Dir(path)); len(ents) != 1 {
- t.Errorf("the drop-in directory holds %d entries, want only the drop-in (no staging left behind)", len(ents))
+ t.Errorf("the drop-in directory holds %d entries, want only the drop-in (no staging or marker left behind)", len(ents))
}
// The same count changes no byte, so it reloads nothing.
@@ -955,37 +955,46 @@ func TestWriteMemoryDropInReloadsOnlyOnChange(t *testing.T) {
}
// A reload that fails must not strand the new cap on disk unloaded: every
-// later run would find the file already right and never reload it. The
-// previous file comes back, so the next run tries again and, when the reload
-// works, reports the change.
+// later run would find the file already right and never reload it. The debt
+// is a marker beside the drop-in, so the next run reloads whatever the bytes
+// say, and reports the change once systemd has it.
func TestWriteMemoryDropInRetriesAfterAFailedReload(t *testing.T) {
path := filepath.Join(t.TempDir(), "d", "50-memory.conf")
- broken := func() error { return errors.New("no systemd here") }
- working := func() error { return nil }
+ pending := path + ".reload-pending"
+ reloads := 0
+ broken := func() error { reloads++; return errors.New("no systemd here") }
+ working := func() error { reloads++; return nil }
+ want := "MemoryMax=" + strconv.FormatInt(LitestreamMemoryMax(200)>>20, 10) + "M\n"
- // No file before: a failed reload leaves none.
if changed, err := writeMemoryDropIn(path, 200, broken); changed || err == nil {
t.Fatalf("changed=%v err=%v, want false and the reload error", changed, err)
}
- if _, err := os.Stat(path); !os.IsNotExist(err) {
- t.Fatalf("a failed first reload left the drop-in in place (stat err %v)", err)
+ if body, _ := os.ReadFile(path); !strings.Contains(string(body), want) {
+ t.Fatalf("the new cap is not on disk after a failed reload:\n%s", body)
}
- if changed, err := writeMemoryDropIn(path, 200, working); !changed || err != nil {
- t.Fatalf("retry: changed=%v err=%v, want true, nil", changed, err)
+ if _, err := os.Stat(pending); err != nil {
+ t.Fatalf("a failed reload left no marker: %v", err)
}
-
- // A file before: a failed reload puts its bytes back.
- before, _ := os.ReadFile(path)
- if changed, err := writeMemoryDropIn(path, 400, broken); changed || err == nil {
- t.Fatalf("changed=%v err=%v, want false and the reload error", changed, err)
+ // The bytes are already right, and it still reloads, twice if it has to.
+ if changed, err := writeMemoryDropIn(path, 200, broken); changed || err == nil || reloads != 2 {
+ t.Fatalf("second failure: changed=%v err=%v reloads=%d, want false, an error, 2", changed, err, reloads)
+ }
+ if changed, err := writeMemoryDropIn(path, 200, working); !changed || err != nil || reloads != 3 {
+ t.Fatalf("retry: changed=%v err=%v reloads=%d, want true, nil, 3", changed, err, reloads)
}
- if after, _ := os.ReadFile(path); string(after) != string(before) {
- t.Fatalf("a failed reload left the new cap on disk:\n%s", after)
+ if _, err := os.Stat(pending); !os.IsNotExist(err) {
+ t.Fatalf("the marker outlived a successful reload (stat err %v)", err)
}
- if changed, err := writeMemoryDropIn(path, 400, working); !changed || err != nil {
- t.Fatalf("retry over a previous file: changed=%v err=%v, want true, nil", changed, err)
+ if changed, err := writeMemoryDropIn(path, 200, working); changed || err != nil || reloads != 3 {
+ t.Fatalf("settled: changed=%v err=%v reloads=%d, want false, nil, 3", changed, err, reloads)
+ }
+
+ // A marker with nothing else wrong (a run stopped after systemd reloaded
+ // but before the marker went) costs one more reload and no more.
+ if err := os.WriteFile(pending, nil, 0o644); err != nil {
+ t.Fatal(err)
}
- if after, _ := os.ReadFile(path); !strings.Contains(string(after), "MemoryMax="+strconv.FormatInt(LitestreamMemoryMax(400)>>20, 10)+"M\n") {
- t.Fatalf("the retried cap is not the one for 400 databases:\n%s", after)
+ if changed, err := writeMemoryDropIn(path, 200, working); !changed || err != nil || reloads != 4 {
+ t.Fatalf("stray marker: changed=%v err=%v reloads=%d, want true, nil, 4", changed, err, reloads)
}
}
diff --git a/internal/provision/plan14_root_reads_test.go b/internal/provision/plan14_root_reads_test.go
index 41ccd4f28..d3c0f085a 100644
--- a/internal/provision/plan14_root_reads_test.go
+++ b/internal/provision/plan14_root_reads_test.go
@@ -667,7 +667,7 @@ func TestPlan14RootReadCensus(t *testing.T) {
// Census ruling ls284 (#284): the litestream memory drop-in, the unit's
// restart backoff and the agent's restart-counter probe and its saved state add seven frozen
// declarations.
- seal = "d17b4d9b8522d2a671b4e0ecdb010a6eeb2dc4c32d2e79a70266cc66845b56ca"
+ seal = "4501bab20e84943df805b7727eba4aa64e21da84d56e80da5847187ddc6561c8"
errs := append(compareRootReads(got, inv), checkRootReadLegacy(inv, seal)...)
errs = append(errs, checkRootReadDeclarations(got, inv)...)
if len(errs) > 0 {
diff --git a/internal/provision/testdata/plan14-root-reads-ls284.tsv b/internal/provision/testdata/plan14-root-reads-ls284.tsv
index 9bcbdfcf1..06ecb0245 100644
--- a/internal/provision/testdata/plan14-root-reads-ls284.tsv
+++ b/internal/provision/testdata/plan14-root-reads-ls284.tsv
@@ -2,21 +2,21 @@
# The ruling is requested from the census owner on amadan #280; these rows state the branch's own account. Earlier overlays are immutable.
@legacy f942faf6b1bda4b171de3930c7a9f5627d4db32a2f802c5ecb10ef26410e0af9 internal/console/templates/metrics.html external legacy accepted-legacy accepted-legacy;rev3=census-ruling-ls284;basis=The alerts table shows a rule's detail beside its name. Same alert state objects, read by the same console sweep; no new source or authority. file:external@0 8732b0e3a4e60029b6c13109be12b15f0a48f51f0fb3c7578e6a52fc26e93140
internal/console/templates/metrics.html external legacy accepted-legacy accepted-legacy;rev3=census-ruling-ls284;basis=The alerts table shows a rule's detail beside its name. Same alert state objects, read by the same console sweep; no new source or authority. file:external@0 8732b0e3a4e60029b6c13109be12b15f0a48f51f0fb3c7578e6a52fc26e93140
-@legacy 94e5b066d93dbc5769523cae394e58c44b75643afdd2af60a76b91a35a506a99 internal/host/restartwatch.go loadRestartWatch legacy accepted-legacy accepted-legacy;rev3=census-ruling-ls284;basis=The agent, as carlos, reads back its own restart-watch state from the data directory's monitor folder, beside the alert state it already keeps there. Feeds an alert only; never read by root and no root decision. file:ReadFile@81 96d67295e3e0842253c19e7df5dcefd15485b38cb48b4e249cc45a6ee193d976
-internal/host/restartwatch.go loadRestartWatch legacy accepted-legacy accepted-legacy;rev3=census-ruling-ls284;basis=The agent, as carlos, reads back its own restart-watch state from the data directory's monitor folder, beside the alert state it already keeps there. Feeds an alert only; never read by root and no root decision. file:ReadFile@81 96d67295e3e0842253c19e7df5dcefd15485b38cb48b4e249cc45a6ee193d976
+@legacy 4d3f9d6b930b32c1fd002d7a831aaf4c7317afd20972ba356031c135ffef8a1b internal/host/restartwatch.go loadRestartWatch legacy accepted-legacy accepted-legacy;rev3=census-ruling-ls284;basis=The agent, as carlos, reads back its own restart-watch state from the data directory's monitor folder, beside the alert state it already keeps there. Feeds an alert only; never read by root and no root decision. file:ReadFile@81 1507415bd6d74eb96c6e23de214e9da98cfa6be25a4888d3ecf95b56c45d508c
+internal/host/restartwatch.go loadRestartWatch legacy accepted-legacy accepted-legacy;rev3=census-ruling-ls284;basis=The agent, as carlos, reads back its own restart-watch state from the data directory's monitor folder, beside the alert state it already keeps there. Feeds an alert only; never read by root and no root decision. file:ReadFile@81 1507415bd6d74eb96c6e23de214e9da98cfa6be25a4888d3ecf95b56c45d508c
@legacy bc86270eeccc35765f5ecd7771202ed99605065e5e703c23bc14934aa79ddb3d internal/host/restartwatch.go platformUnitRestarts legacy accepted-legacy accepted-legacy;rev3=census-ruling-ls284;basis=The agent, as carlos, asks systemd for the restart counters of loaded carlos-*.service units; a fixed command with no registry or tenant input, the same class as internal/host/probes.go adoptTimerActive under J4 root-input. Feeds an alert only; no root decision. file:CommandContext@166 fde6a651c0a8a66c324124a2bd3085959c9b575cae08e2d65d2eb666761b0dcc
internal/host/restartwatch.go platformUnitRestarts legacy accepted-legacy accepted-legacy;rev3=census-ruling-ls284;basis=The agent, as carlos, asks systemd for the restart counters of loaded carlos-*.service units; a fixed command with no registry or tenant input, the same class as internal/host/probes.go adoptTimerActive under J4 root-input. Feeds an alert only; no root decision. file:CommandContext@166 fde6a651c0a8a66c324124a2bd3085959c9b575cae08e2d65d2eb666761b0dcc
@legacy e39e5045ef43883a78c327f56fbd90e501ff3c012c8ddf512b0f7585b75623c1 internal/host/units/carlos-litestream.service external legacy accepted-legacy accepted-legacy;rev3=census-ruling-ls284;basis=Memory cap default 512M with GOMEMLIMIT, and restart backoff (RestartSteps, RestartMaxDelaySec). User, paths, command and sandboxing unchanged; no new source or authority. file:external@0 2fc82f963202103a16a89aaecdcaeb89c6b9fee8e9d0e09cb76389a2326c1714
internal/host/units/carlos-litestream.service external legacy accepted-legacy accepted-legacy;rev3=census-ruling-ls284;basis=Memory cap default 512M with GOMEMLIMIT, and restart backoff (RestartSteps, RestartMaxDelaySec). User, paths, command and sandboxing unchanged; no new source or authority. file:external@0 2fc82f963202103a16a89aaecdcaeb89c6b9fee8e9d0e09cb76389a2326c1714
-@legacy 63b9984a01df41c70dfd5364238390d2a78d6b814b46f03f5f7c25f799d386e7 internal/ops/litestream_memory.go writeMemoryDropIn legacy accepted-legacy accepted-legacy;rev3=census-ruling-ls284;basis=Root reads the root-owned drop-in this verb itself wrote, to decide whether to rewrite it; the same compare-before-write as writeIfChanged under J4 root-input. The path is fixed or an operator-supplied flag. file:ReadFile@146 9ba23f0626a3f649027d809610c6fa0e02584f9e501517de69b9d57767e06d90
-internal/ops/litestream_memory.go writeMemoryDropIn legacy accepted-legacy accepted-legacy;rev3=census-ruling-ls284;basis=Root reads the root-owned drop-in this verb itself wrote, to decide whether to rewrite it; the same compare-before-write as writeIfChanged under J4 root-input. The path is fixed or an operator-supplied flag. file:ReadFile@146 9ba23f0626a3f649027d809610c6fa0e02584f9e501517de69b9d57767e06d90
+@legacy db8b810137dce7ad80730610211e0a8a2262242dacedf00621e2cca9b3818dcf internal/ops/litestream_memory.go writeMemoryDropIn legacy accepted-legacy accepted-legacy;rev3=census-ruling-ls284;basis=Root reads the root-owned drop-in this verb itself wrote, and lstats its own reload-pending marker beside it, to decide whether to rewrite and reload; the same compare-before-write as writeIfChanged under J4 root-input. The path is fixed or an operator-supplied flag. file:Lstat@342,file:ReadFile@183 32b3e20e28898170b36664fe67715582425888f7e90aeaa9c2afb561799b28fe
+internal/ops/litestream_memory.go writeMemoryDropIn legacy accepted-legacy accepted-legacy;rev3=census-ruling-ls284;basis=Root reads the root-owned drop-in this verb itself wrote, and lstats its own reload-pending marker beside it, to decide whether to rewrite and reload; the same compare-before-write as writeIfChanged under J4 root-input. The path is fixed or an operator-supplied flag. file:Lstat@342,file:ReadFile@183 32b3e20e28898170b36664fe67715582425888f7e90aeaa9c2afb561799b28fe
@legacy dc2175dd0445552045146497dd473327ec0ddca2de728f6b8a2c5c755b82420d internal/ops/ops.go runLitestream legacy accepted-legacy accepted-legacy;rev3=census-ruling-ls284;basis=Same registry open and prior-config read. Adds one root-owned write, the memory drop-in beside the unit, sized from the database count this run computed, and a daemon-reload; no new input is trusted. data-literal@214,file:Open@1383,file:ReadFile@2357 380a01961cc02f2a18673bc4ad0a0bc1112905d4726f8ab9e7b370da36686306
internal/ops/ops.go runLitestream legacy accepted-legacy accepted-legacy;rev3=census-ruling-ls284;basis=Same registry open and prior-config read. Adds one root-owned write, the memory drop-in beside the unit, sized from the database count this run computed, and a daemon-reload; no new input is trusted. data-literal@214,file:Open@1383,file:ReadFile@2357 380a01961cc02f2a18673bc4ad0a0bc1112905d4726f8ab9e7b370da36686306
@legacy cb8030cd13f53803aca45d166742f194f1cb00e1d881263a776b79a334a37189 internal/ops/ops.go systemdDaemonReload legacy accepted-legacy accepted-legacy;rev3=census-ruling-ls284;basis=Root runs a fixed systemctl daemon-reload with no caller input, the same class as internal/host/bootstrap.go systemctl under J4 root-input. file:Command@48 3edadda9e71b806a78d14160f2da0b6091212c4ee55933a327856be811290a74
internal/ops/ops.go systemdDaemonReload legacy accepted-legacy accepted-legacy;rev3=census-ruling-ls284;basis=Root runs a fixed systemctl daemon-reload with no caller input, the same class as internal/host/bootstrap.go systemctl under J4 root-input. file:Command@48 3edadda9e71b806a78d14160f2da0b6091212c4ee55933a327856be811290a74
@coverage internal/console 85d4f9521a2acc08aa1276f414be41cea32aa4275f761546e77e951d0d58b558
@coverage internal/console/templates e622e1e901447dc067aba32e4e520eef4279ffcdd807695f012fd38f646d48b4
-@coverage internal/host 53769084266f3ae76e999ed6f7f01f3f362618535af4bf283c607e41aa51a3be
+@coverage internal/host 563e0efbe9bd9de3e4d6844cb6d650d5cd221681fed5594ecd502d45cef68826
@coverage internal/host/units e06e4a1a0d9d45f8e7494f0419834bf23e14fb2cd4861b94ef905bfaf795a7d1
@coverage internal/metrics 1adc51fc485ba895970c22277b7bff997c24ebededbd19f48a10e75cf531323a
-@coverage internal/ops 1c36eec5ac001ce556030e557579be93dd082d57d47c5c002ee93bdc44cf12f8
+@coverage internal/ops 31e815c0a685f7ef7013c9295a1f570faff3b6846901c215617832b436ffffc1