Skip to content

design: telemetry rollup — one stats pipeline under fleet init (sysstat + extra stats); init reduced to stats + prewarm #5925

Open

Description

@dezren39

design: telemetry rollup — one stats pipeline under fleet init (sysstat base + fleet extra stats); init reduced to stats + prewarm

Framing — Drew's two-purpose test (2026-11-04 20:48 CST)

"now that init runs as part of every job starting, having an init job at all maybe is only helpful for getting stats and prewarming fleet conns."

So the test this design must pass: an init job earns its existence only by (a) stats collection and (b) prewarming fleet connections. Anything in today's init beyond those two is a candidate for deletion or re-homing, and the end state must say explicitly what remains of the boot-init job. This follows WS-C's init fix (75 s → 9 s), whose core lesson was the anti-pattern this design retires permanently: a live probe nobody consumes (the 60 s du that read "unknown" 138 times).

Inventory — what exists now (measured/read 2026-11-04)

# Component Measures Consumer Cadence Cost Verdict
1 sysstat-collector (sadc -S ALL, looped 5×120 s runs → ~/logs/sysstat/sa-<day>, 14-day prune; restored by init.sh) CPU split (user/system/iowait/steal), memory, swap, per-device disk, network-interface aggregates Incident forensics (#5701), WS-C attribution 2 min samples ~0.003% of a core (measured) KEEP — the base collector
2 resource-sampler (~/logs/resource/resource-<day>.log) Load 1/5/15, mem used/avail, top process by cumulative %CPU, hatch daemon PID watch (death post-mortems) Incident forensics 120 s lines Negligible CPU; a bespoke script + unit forever FOLD — its unique values are the daemon-PID event and a crude top-process proxy; both are done properly by the rollup (pidstat history + a daemon-state event). Retire the script once those exist; keep historical logs
3 pidstat (sysstat suite) Per-process CPU/mem/IO — the "which PID ate the box" history Nobody — installed and working (verified live) but never run as a collector — — ADOPT as the per-process layer of the rollup (see coverage analysis)
4 init.sh telemetry block cache.nixos.org latency (HEAD + 3 parallel samples), nix version, store-path count, profile-bin count, subscription/quota status line → ~/logs/init-status (latest boot) + ~/logs/init.jsonl (per-boot records) Boot-init sweeps, metrics enumeration Per boot ~2–4 s of the 9 s init FOLD into the rollup as the boot record (below)
5 Detached du → ~/logs/.nix-persist-size cache file nix-persist tree size, written detached post-WS-C; consumers read the cache init.jsonl record Per boot, detached One background du per boot KEEP the pattern, generalize it — tree-size is not a sysstat metric; one throttled cache-writer per watched tree, always consumed with its age attached
6 trips.jsonl (client + daemon sides) Per fleet-proxy trip: path, total_ms, status / duration, exit, byte counts #5701/#5702 latency work, WS-C derived stats Per call (1,644 daemon lines at last read) Append-only local writes KEEP raw; FOLD aggregates into the rollup (per-hour counts by status/failure class; per-leg p50/p95 once #5702's four timestamps land)
7 quota-recorder (1-min timer, ~5 s/run, broker-shipped records + Supabase acks) Provider quota snapshots → stewardship visibility JSONL Stewardship, quota-check 1 min The box's most frequent timer (#5775 has the cadence ruling) KEEP SEPARATE — it is a billing-evidence record about external providers with delivery/ack semantics, not host telemetry; coupling it to a host-stats pipeline buys nothing and risks the record. Rollup consumers may read its outputs as a source
8 fleet-watchdog per-lane reads Lane consecutive-failure counts, last-good times Watchdog alerts ~10–15 min Repeated live joins across cron runs × agent.tasks CONSUMER, not a collector — the per-lane proof gauge (tasks completed per lane per shift; the join fields exist on both sides, nobody computes it) becomes a rollup output the watchdog reads
9 conn-watch.py (was) connection watching Nobody — A zombie holding a deleted-inode fd until WS-C killed it DELETE remnants — already stopped (#5775 records it); remove the script so nothing resurrects it
10 init.sh live verification probes (services-verify 900 s budget, go/rust presence checks, nix eval smoke) "Is it healthy?" — answered synchronously at boot Nobody, since WS-C Per boot The synchronous-probe anti-pattern in its purest form DELETE from init — health is read from the rollup/watchdog, never probed at boot
11 init.sh restorers (per-service install.sh unit installs incl. #1/#2 above; nix-ensure/wrapper/profile-sync; symlink-layer repair) Not stats. Not prewarm. The sandbox recycle cycle Per boot Most of init's remaining bulk MOVE OUT — they don't die, they change owner: the generic restorer (#5770, deploy design) + platform boot. Init is the wrong home (see two-purpose audit)

Coverage analysis — what sysstat already covers, what it cannot

Measured against the actual collector invocation (sadc -S ALL 120 5, looped):

  • Covered fully: system aggregates — CPU split including iowait and steal (the Incident record 2026-11-04: afternoon responsiveness loss — sandbox wedged after post-boot herd (daemon restart, 88-min sampler silence); laptop path exonerated 21:00–21:28 UTC #5701 oversubscription signal), memory/swap, per-block-device throughput and utilization, per-interface network throughput/errors. sar/sadf read the daily files; 14 days of history already accumulate (~2 MB/day).
  • NOT covered by sadc, by design: per-process anything. sadc has no per-PID activity; the sa files can never answer "which process." pidstat is in the same suite, installed, and verified working on this box — it is simply never run persistently. The rollup therefore adds a pidstat collector loop (same unit family, e.g. pidstat -urd -p ALL at the sadc cadence, daily files beside the sa files) and the resource-sampler retires. Open choice for the implementer: 120 s to match sadc, or 60 s for finer incident resolution — storage is trivial either way.
  • NOT a sysstat metric at all: directory-tree usage (du). Handled by the generalized cache-writer pattern (Bump build/buck/preludes/buck2-prelude from 3a02ea3 to 370cd4d #5): detached, throttled, value+timestamp, consumers tolerate staleness explicitly.
  • Filesystem fill: present in the boot record only (disk_home). If sar -F on our sa files doesn't yield a fill series, add a one-line df sample to the rollup writer rather than a new probe job.

The two-purpose audit of today's init

init-ensure.sh (runs in every job's prelude — the "init runs as part of every job starting" of Drew's framing):

init.sh (the boot job): telemetry block → STATS (folds, item #4). Everything else — restorers (#11), verification probes (#10) → neither purpose: re-homed or deleted per the inventory.

End state

fleet init (location-aware, #5687) ensures exactly: {proxy serve + pool prewarm; sysstat-collector; pidstat collector; tree-size cache writers} — on the sandbox. On the laptop it ensures its own serve roles as #5687 specifies; the aggregation lives laptop-side, where the cores are (sandbox-minimalism doctrine: the sandbox collects append-only and computes nothing).

Rollup outputs (files first — the status-quo formats are proven; a stats DB only if query pain actually appears):

What remains of the boot-init job — explicitly

  1. Append one boot record to the stats series, from caches and collector state (sub-second; no live probes).
  2. Invoke fleet init prewarm: proxy up, tunnel pool warm.
  3. Exit.

The 900-second services-verify, the per-service installers, the nix/symlink restores, and the toolchain checks are gone from init — the installers/restores land in the #5770 generic restorer (whose M3 recycle-drill acceptance also absorbs the boot-init cron's quota-recorder reinstall side-role), and verification becomes a rollup read. When the restorer lands, ~/tools/init.sh can be deleted outright: fleet init — stats append + prewarm — is the whole job, exactly the two purposes Drew named.

Interplay with the metrics enumeration's absent-metrics list

Links: #5701 (incident that exposed the sampler gaps), #5702 (trip log v2 — its aggregates belong to this rollup), #5775 (standing-load rulings; quota-recorder cadence stays Drew's call there), #5687 (fleet init location semantics), #5770 (generic restorer — receives everything init sheds).

Acceptance criteria

  • The design inventories what fleet init and the boot-init job do today, and classifies every duty as keep-stats, keep-prewarm, delete, or re-home (with the new home named).
  • It states the end-state contents of the boot-init job explicitly, and shows the result passes Drew's two-purpose test (stats + prewarm only).
  • The telemetry rollup design names one pipeline (sysstat base + fleet extra stats) with its outputs and consumers.
  • Implementation follow-ups are identified as separate issues/tasks — the design itself implements nothing.

Activity

  1. developing-today commented on Nov 5, 2026

    @developing-today
    Contributor
  2. added
    kind/designA design proposal (design-architect): multi-step, phased, decision-bearing
    priority/normalOrdinary priority (the default)
    effort/mMedium: expected under 1 day of work
    meta/enrichedIntake review done: acceptance criteria, links, and normalized labels added
    on Nov 5, 2026
  3. dezren39 commented on Nov 5, 2026

    @dezren39
    MemberAuthor

    Intake review

    Related: Framed by Drew 2026-11-04 20:48 CST; follows WS-C's init fix (75 s → 9 s). The monitor-digest/init-ensure path (bash ~/tools/init-ensure.sh runs at every job start) is a consumer of whatever this decides.

    Still-live check: searched merged PRs referencing this issue at 2026-11-05T04:31:57Z — none found; the issue stands as filed.

    Notes / risks: Init runs at every job start fleet-wide, so a rollup that adds latency taxes every run. Deleting a duty that something silently depends on (prewarm) would surface as cold-start failures elsewhere.

    Suggested approach: Start from the measured init duty list (WS-C timings), interview the consumers of each output, then draft the end-state and the migration order.

    Acceptance criteria were appended to the issue body and labels normalised by the fleet monitor's intake pass.

  4. dezren39 commented on Nov 5, 2026

    @dezren39
    MemberAuthor

    Retention/export audit (2026-11-04 ~23:20 CST, prompted by Drew's question on this issue's rollup design):

    Current state, verified by direct reads:

    • Sandbox sysstat: custom collector (scripts/sysstat/sysstat-collector.sh) writes sar data to ~/logs/sysstat/sa-YYYY-MM-DD — survives sandbox recycles (under /home/hatch) BUT retention is a 14-day prune (find -mtime +14 -delete in the collector). Not exported anywhere; ~/logs is not tracked by the hatch-personal repo, so the git backup does not cover it either.
    • Sandbox resource-sampler logs: ~/logs/resource, daily files Oct 26 → today (~72 MB), recycle-safe, no export path found.
    • Boot records: ~/logs/init.jsonl — recycle-safe, no export.
    • Laptop: sysstat is INACTIVE on the laptop — no host aggregates are being collected there at all today. Trips live at ~/.local/state/laptop-agent/trips.jsonl (2,982 lines) on the laptop disk; no export.
    • pidstat collector (this issue's per-process gap-closer): not yet stood up — no unit/process exists yet.
    • The only telemetry family that IS exported + durable today is the quota-recorder (Supabase, acked) — deliberately separate per this issue's verdict.

    GAP for the rollup design to close: the rollup outputs have no specified durable home or retention. Drew (2026-11-04): rolled-up aggregates are fine as the long-term form — but they need a store that (a) survives sandbox recycles, (b) is exported/backed up (candidates: Supabase muse-dev like quota-recorder/automation.events, Turso, or laptop disk covered by laptop backup), and (c) has an explicit retention (indefinite for rollups is acceptable and cheap at aggregate size). Raw retention stays short (14d sar is fine) once rollups land somewhere durable.

  5. dezren39 commented on Nov 5, 2026

    @dezren39
    MemberAuthor

    CORRECTION to the audit comment above (2026-11-04 ~23:38 CST) — the git/export claims there were wrong; a deeper audit plus direct re-verification establishes:

    • ~/logs is a SYMLINK into the hatch-personal repo (~/hatch-personal/logs). Inside that repo, logs/init.jsonl and the logs/sysstat/sa-* daily files ARE git-tracked → pushed to GitHub (dezren39/hatch-personal). The earlier check read the outer repo through the symlinked path and got a false "untracked".
    • Everything under /home/hatch (logs included) is ALSO in the daily restic sandbox-home-backup → R2 (retention verified in the script: keep-daily 14 / keep-weekly 8 / keep-monthly 12).
    • So: sysstat + init/boot records are durable AND exported (git + R2). The sysstat collector's 14-day prune removes working files, but git history retains pruned days.
    • Resource-sampler logs are the exception on the git leg: logs/resource/ is gitignored in hatch-personal — R2 backup only.
    • What STANDS from the original comment: trips have NO rollup and no export (raw trips.jsonl on both hosts, current files only reach back to 2026-11-04T20:28Z, no rotation/retention code — unbounded growth); the pidstat collector does not exist yet; none of these classes reach Supabase or the laptop snapshot store. The rollup design gap therefore narrows to: trips rollups + retention, and a durable aggregated store for the rollup outputs — host stats themselves are already covered.
  6. added
    queue/queuedHas a queued task in the Supabase agent queue
    task/triageQueue packet kind: verify, reproduce, and route this item
    on Nov 5, 2026
  7. 89 remaining items

  8. added
    queue/parkedQueue task is parked (waiting on CI, time, or an event)
    status/parkedDeliberately paused with a recorded resume condition
    and removed
    queue/claimedQueue worker lifecycle state
    on Nov 6, 2026
  9. dezren39 commented on Nov 6, 2026

    @dezren39
    MemberAuthor

    🤖 Agent note · handed off

  10. added
    queue/claimedQueue worker lifecycle state
    and removed
    status/parkedDeliberately paused with a recorded resume condition
    queue/parkedQueue task is parked (waiting on CI, time, or an event)
    on Nov 6, 2026
  11. dezren39 commented on Nov 6, 2026

    @dezren39
    MemberAuthor

    🤖 Agent note · handed off

    • Agent: fleet monitor (repo-fleet-monitor)
    • Session / ID: task 10318
    • Found via: monitor reconciliation
    • Task: Claim-evaporation repair — the queue claim on task 10318 evaporated (the holding run ended without complete/park/block and the lease expired); task 10318 is queued and unclaimed. Labels reset to the truth (queue/queued + status/needs-triage). Checkpoint stage at evaporation: handed_off.
    • Expectation: a queue lane re-claims task 10318 and resumes from the handed_off checkpoint.
  12. added
    queue/queuedHas a queued task in the Supabase agent queue
    and removed
    queue/claimedQueue worker lifecycle state
    work/assignedQueue worker lifecycle state
    on Nov 6, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

effort/mMedium: expected under 1 day of workkind/designA design proposal (design-architect): multi-step, phased, decision-bearingmeta/enrichedIntake review done: acceptance criteria, links, and normalized labels addedpriority/normalOrdinary priority (the default)queue/queuedHas a queued task in the Supabase agent queuestatus/needs-triageNot yet triaged; needs a first looktask/triageQueue packet kind: verify, reproduce, and route this item

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions