Repository navigation
Description
Relocated from dezren39/darwin-nix#1026 on 2026-10-28 (placement audit): laptop-backups run-record/watchdog — the implementation is the code-side port (backup modules relocated to code via code PR #1740 / nix PR #1043; implementation branch queue/1914-1026-run-record-watchdog). Original body below; issue references inside it are historical (dezren39/nix numbering).
Problem
The two jobs declared in modules/laptop-backups.nix — pm-dashboard-backup (daily 04:30) and repo-archive-backup (daily 05:15) — leave no durable, machine-readable record of their runs. A run's only traces are its journal lines and pm-dashboard-backup's .last-run-epoch, which records the journal window, not run health. #976 verified on 2026-10-27 that neither timer had ever fired, and its acceptance asks for "a freshness check exists so a stopped backup gets named" — there is still no artefact such a check could read, so a backup that stops (timer broken, script failing nightly, laptop job silently exiting 0 per #1021/#1023's class) is discovered at restore time.
Part of design #1024 ("a backup that can prove itself"), phase 3. Parent coverage issue: #976.
Scope
modules/pm-dashboard-backup.sh: on every exit path (complete and failed), write one JSON run record at a fixed path beside.last-run-epoch(e.g.$ROOT/.last-run.json— at the backup root, never inside a dated day dir, so no retention prune can remove it; local prune iterates only20??-??-??dirs). Fields: job name, date argument, start/finish timestamps, result (complete/failed), the failing step(s) when failed, archive sha256 when built, and per-destination (local / R2 / Drive) results.modules/laptop-backups.nix: alaptop-backups-watchdogsystemd user service + timer (runs a few times a day) that checks (a) the dashboard run record — last result iscompleteand its finish time is within the threshold — and (b) both backup timers' last-trigger times against the same threshold. On any violation it exits non-zero with a line naming the job and the age/result. The threshold is a module option (services.laptop-backups.watchdog.maxAgeHoursor similar), default a little over one daily period (26h). The watchdog changes nothing and attempts no repair.- The repo-archive job is covered via its timer's last-trigger time only: its script (
~/tools/repo-archive-backup.sh) and restic credentials live outside this repo, so no run record is required of it in this issue — that limitation is stated in the module comment.
Acceptance criteria
- A complete pm-dashboard-backup run leaves
.last-run.json(or the chosen fixed path) withresult: complete, the archive sha256, and all three destination results. - A failed run (e.g. stubbed rclone upload failure in the harness) leaves a record with
result: failedand the failing step named — the record is written on the failure path too. - With a stale or failed dashboard record, the watchdog exits non-zero naming
pm-dashboard-backupand the age; with a fresh complete record and recently-fired timers, it exits 0. - With a backup timer whose last trigger exceeds the threshold, the watchdog exits non-zero naming that timer/job.
- Test coverage in the repo's established style: a watchdog test suite (stubbed record files + timestamps) wired as a flake check, plus a pm-dashboard-backup suite scenario asserting the record's contents on success and on failure.
- Existing behaviour unchanged: schedule, retention, destinations, cursor semantics; the watchdog is additive and read-only.
🤖 Agent note · state: filed
- Agent: repo-design-architect job
- Session / ID: repo-design-architect cron run 2026-10-28 08:33 CDT (split rotation: opencode / openchamber / nix; nix's first pass)
- Found via: design pass for fix(id-web): preserve HTML blocks on save via code_block provenance attr (#982) #1024 — fix(cold-store-reaper): curl fallback for S3 downloads urllib cannot deliver #976's freshness-check acceptance has no artefact to read; neither job writes a run record
- Task: Phase 3 of design fix(id-web): preserve HTML blocks on save via code_block provenance attr (#982) #1024 (sub-issue of it); enqueued to repo-health-nix-pilot as a triage packet
- Expectation: effort/m; independent of phase 2 (the expected-set issue) — either may land first
Activity
developing-today commentedon Oct 28, 2026
CODE-769
dezren39 commentedon Oct 28, 2026
History carried over from dezren39/darwin-nix#1026 (verbatim, oldest first):
@dezren39 · 2026-10-28T14:50:55Z
🤖 Agent note · triage confirmed
2aebfa908c8ecde88b5f17bec9446156693aedb2— neither job writes a run record, no watchdog exists, exactly as filed.modules/laptop-backups.nixverified directly on current main this run: exactly two services+timers (pm-dashboard-backup04:30,repo-archive-backup05:15), no watchdog unit/timer, noOnFailure, no threshold option.modules/pm-dashboard-backup.sh(268 lines): the only durable outputs are the archive, its manifest, and.last-run-epoch(MARKERat :33, written at :266 as the journal-window cursor only after all legs succeed) — no.last-run.jsonor equivalent anywhere in the script.on_exit(:47-52) sees every exit path; extend it rather than adding a second trap (bash traps replace, not stack — the opencode: pool effort remap — wired rungs, capability order, meta refresh #1010 lesson documented at :40-45). (2)$ROOT/.last-run.jsonis prune-safe, verified: local prune iterates only$ROOT/20??-??-??(:227-232) andprune_remotepurges only dated dirs matching^20[0-9]{2}-(:240-250). (3) Test style exists to extend:pm-dashboard-backup.test.sh/-prune/-datesuites are wired as flake checks inflake.nix. (4) fix(cold-store-reaper): curl fallback for S3 downloads urllib cannot deliver #976's 2026-10-28 update says both timers have now fired for the first time withResult=success, so the watchdog's timer check has realLastTriggerdata to read.repo-health:v1:fix:dezren39/nix:issue:1026, kind small_fix) — run record on every exit path, watchdog service+timer withservices.laptop-backups.watchdog.maxAgeHours(default 26), watchdog suite + record scenario wired as flake checks. Sequencing: PR OpenClaw: connect chat channels (Slack, Telegram, WhatsApp) #1029 (opencode: pool pass 2 — contributor seats, variant remaps, speed pool #1023), PR id(web): adjacent bullet lists with different markers accumulate '<!-- end list -->' comments and html fences on every save — non-fixpoint, grows per save #1032 (fix(id-web): degrade unsupported table-cell content instead of panicking on save (#1017) #1021), and the feat(home): install OpenClaw gateway via nix-openclaw #1025 fix (task 1895) all touchpm-dashboard-backup.shand land first — semantically phase 3 is independent of phase 2 per this issue, the sequencing is only to avoid same-file conflicts.@dezren39 · 2026-10-28T14:52:16Z
🤖 Agent note · parked on sequencing
modules/pm-dashboard-backup.sh; this fix starts from a main that includes them.$ROOT/.last-run.jsonvia the existing EXIT trap,laptop-backups-watchdogservice+timer withmaxAgeHoursdefault 26, suites wired as flake checks, validation on the laptop.2aebfa9— see the triage verdict comment above.@dezren39 · 2026-10-28T16:40:44Z
🤖 Agent note · state: parked (CI pending)
queue/1914-1026-run-record-watchdog:.last-run.jsonrun record via the existing EXIT trap (suppressed until DATE validation passes, per Fix /today 500: tolerate string-shaped progressedSinceLastRun #989; SKIP leaves no record), thelaptop-backups-watchdogscript + service/timer withservices.laptop-backups.watchdog.maxAgeHours(default 26), and record + watchdog test suites wired as flake checks. All pre-existing pm suites pass and all 7 related flake checks pass on the laptop; module evaluated via a scratch home-manager config.nix buildqueued at park time)@dezren39 · 2026-10-28T17:37:15Z
🤖 Agent note · blocked — target moved out of this repo
packet_defect: the packet's target files no longer exist in this repo)nix build) is green and the sequencing gate cleared (PR 🎨 Palette: Enhance accessibility and keyboard navigation in image browser dialog #1039 merged 2026-10-28T16:25:17Z) — but PR id(web): prosemirror_to_markdown panics on misplaced nodes — json_to_ast never validates block/inline placement #1043 then merged (2026-10-28T17:12:06Z) removing the laptop modules from this repo under Drew's 2026-10-28 rule (dezren39/nix is the Darwin flake; all laptop services are made by the developing-today/code flake). The modules were relocated tonixos/home/in developing-today/code by amd: laptop services live in this flake — relocate modules from dezren39/nix, add repo-keeper + autocommit (code#1723) #1740 (merged 2026-10-28T17:11:59Z).modules/laptop-backups.nixandmodules/pm-dashboard-backup.shreturn 404 on current nixmain(d443337); PR fix(id-web): carry tight on task_list so loose task items stop fusing on save (#1036) #1041 is nowCONFLICTING/DIRTY. Merging it would resurrect the deleted modules, so it is being closed unmerged; the implementation is preserved on branchqueue/1914-1026-run-record-watchdog@ 55c636f (all suites + 7 flake checks passed on the laptop, CI green, before the move).nixos/home/(pm-dashboard-backup.sh,laptop-backups.nix, plus the watchdog script and record/watchdog suites). Verified on codemainthis shift: the relocated files are present, but there is no.last-run.jsonrecord and nolaptop-backups-watchdog— the phase-3 work has not landed there. Per the cross-repo rule this origin issue tracks the set; no new per-repo issue was filed.64 remaining items
dezren39 commentedon Oct 31, 2026
🤖 Agent note · picked up
Merge pull request #2989 from developing-today/queue/3057-1841-laptop…
dezren39 commentedon Oct 31, 2026
🤖 Agent note · done