{"slug":"nas-setup","total":33,"limit":100,"offset":0,"since":null,"commits":[{"hash":"808d749","date":"2026-09-14 01:11:26 -0700","author":"Steve Abrams","subject":"TK-11648: negative test drives the REAL backup script via a plist-safe seam","body":"The guard-copy test proved the guard LOGIC but stayed green if the shipped\nscript's guard was reverted. Add a BACKUP_TEST_MODE seam (SRC/DEST/HENRY/DATA\nenv overrides, mount-guard relaxed, heartbeat output redirected so it can't\npoison the live fleet-health latest.json) and an end-to-end negative test that\nruns backup-repos-to-henry.sh itself against a hollow-.git fixture and asserts\nit writes NO wrong-repo bundle + reports WARN. Verified the assertion goes RED\nwhen the guard is neutralized (TK-11431 amdt 3).\n\nCo-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>\nClaude-Session: https://claude.ai/code/session_01SnGVNUxnQfxpU1vfZmZFv3"},{"hash":"9603008","date":"2026-09-14 00:00:01 -0700","author":"Steve Abrams","subject":"TK-11648: identity guard so backup never bundles the WRONG repo (false-green)","body":"backup-repos-to-henry.sh guarded only that .git EXISTS and that the bundle\npasses 'git bundle verify' — which proves a VALID history, not THIS repo's.\nA present-but-hollow child .git makes 'git bundle create --all' walk UP to\nthe ~/Projects meta-repo and bundle THAT under the child's name: a valid\nbundle of the wrong repo, so the ONLY off-machine copy (no-remotes rule) is\nsilently worthless while the run reads PASS. Confirmed: animals.bundle was a\n1.6G copy of the meta-repo (HEAD resolved as a meta-repo commit).\n\nFIX: assert 'git -C $repodir rev-parse --show-toplevel' == $repodir before\nbundling; a mismatch is a FAIL, never a silent wrong bundle. Ships a NEGATIVE\ntest (test-backup-identity-guard.sh, per TK-11431 amdt 3): injects a hollow\nchild .git, proves the fault reproduces, the guard FAILs it, and a valid repo\nstill PASSes. Test PASS.\n\nAUDIT (read-only, 1207 bundles): 3 mislabeled — animals (source .git since\nREPAIRED, self-heals on next run), govarbitrage.gutted-1789157670 + japan-enrich\n(hollow .git, need repair; guard now FAILs them instead of mislabeling). Repair\ndrafted to pending-approval (judgment work; gitify rule = don't re-init a .git).\n\nCo-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>\nClaude-Session: https://claude.ai/code/session_01UVgEj4WxSqB62ieGpgrixR"},{"hash":"e6b27ef","date":"2026-09-10 13:09:49 -0700","author":"Steve Abrams","subject":"daemon-health: watch the repo-bundle belt, not just the two DB mirrors","body":"backup-repos-to-henry.sh has always written a PASS/WARN/FAIL verdict to\ndata/repo-backup-latest.json, but nothing ever read it. A silently-dead bundler\nemitted zero alerts - the exact shape of the 12-day pg_dump death this skill\nexists to prevent.\n\nThat belt now matters more: as of today it is the ONLY off-machine copy of\n~/.claude (452 skill definitions) and its 73 nested skill repos.\n\nAdds a third check with three failure modes:\n  FAIL  last run > 36h ago (daily job + one missed run of slack)\n  WARN  bundler self-reported WARN/FAIL, or repos failed\n  FAIL  bundler PASSed but dotclaude.bundle is ABSENT - freshness alone is not\n        proof the thing you care about is in the set\nVerdict joins the existing rollup, so it reaches fleet-health-rollup and\ndw-canary-meta-watchdog via the skill's data/latest.json.\n\nNote the macOS trap:  parses a trailing-Z UTC stamp as LOCAL time,\nproducing a future epoch and a NEGATIVE age that silently passes the staleness\ntest. Fixed with -u and a negative clamp; caught by negative-testing rather\nthan by reading the code.\n\nNegative-tested: 40h stale -> FAIL, bundler WARN -> WARN, healthy -> PASS.\n\nCo-Authored-By: Claude Opus 5 <noreply@anthropic.com>"},{"hash":"7d6456f","date":"2026-09-10 13:06:42 -0700","author":"Steve Abrams","subject":"backup-repos-to-henry: cover ~/.claude and its 73 nested skill repos","body":"The source glob was $HOME/Projects/*/.git, so ~/.claude was missed entirely by\npath - leaving 452 skill definitions with NO off-machine copy of any kind. A\nfull audit found nothing else covered them either: Time Machine has no\ndestination configured, no launchd job writes ~/.claude, and rclone/iCloud/\nSyncthing/cron are all absent or dead. The only off-machine artifact was a\n6-month-stale MD-only zip.\n\nNested skill repos are bundled INDIVIDUALLY because the parent repo records\nthem as gitlinks - a parent-only bundle captures a 1-line pointer and none of\ntheir content. 73 skills are affected.\n\nBundle names are namespaced (dotclaude, dotclaude-skill-<name>) so they cannot\ncollide with a ~/Projects repo of the same basename.\n\nCo-Authored-By: Claude Opus 5 <noreply@anthropic.com>"},{"hash":"1256a2d","date":"2026-08-17 22:58:57 -0700","author":"Steve Abrams","subject":"backup-daemon-health: credit the user belt via its verified success log (fix false FAIL)","body":"launchd runs/exit-code resets to 0 on reload/reboot and shows '(never exited)',\nso the canary false-FAILed after the Aug pm2-fracture reboots even though the\nuser LaunchAgent wrote a verified Henry copy nightly. Now credit a belt when its\nown log shows a recent '[Henry] PASS: <prefix>_' (stricter than exit code —\nvalidates the outcome; anti-stale guard preserved via log-mtime recency).\n\nCo-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>"},{"hash":"9fc2571","date":"2026-08-15 20:32:34 -0700","author":"Steve Abrams","subject":"daemon-health: fix jq --argjson crash on '(never exited)' — the loaded-but-never-run belts (runs=0) returned non-JSON exit text, throwing jq so the heartbeat latest.json never landed and fleet-health-rollup went blind on the Henry mirror (the exact TK-10546 anti-pattern). Sanitize exit codes to a JSON number-or-null, add a never-ran vs exit:N status word, and rewrite the up=0 FAIL reason to say 'no scheduled writer has landed a clean run — kept fresh by manual rescue only' instead of the misleading 'both writers broken' (TK-10547)","body":"Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>"},{"hash":"672d698","date":"2026-08-15 11:25:10 -0700","author":"Steve Abrams","subject":"daemon-health: auto-detect belt strictness from the loaded root plist — accepted-down (lenient PASS) while it calls brew bash, auto-flip to strict belt-and-suspenders (one belt down = WARN) the moment Option B swaps it to the signed launcher. Makes the single sudo installer finish the root-belt promotion end-to-end, no manual flag flip (TK-10547)","body":""},{"hash":"4228153","date":"2026-08-15 07:45:16 -0700","author":"steve","subject":"pull-dw-dump: retry remote-dump SSH discovery on transient failure (TK-10547)","body":"A single 03:55 ssh timeout to Kamatera lost a full day's dw_unified backup and\nhard-FAILed the run (the canary correctly caught it 8/15). Add 4 attempts with\n15/30/60s backoff so a momentary network/SSH blip auto-recovers instead of\ncosting a day of backup. Read-only, idempotent (rsync skip-if-complete).\n\nCo-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>"},{"hash":"3c61bc3","date":"2026-08-15 07:11:26 -0700","author":"steve","subject":"daemon-health: accept single-belt operation as PASS (Steve 8/15, TK-10547)","body":"Steve accepted the user belt as the primary; the root belt (Option B) stays\ndown pending a flaky system-domain FDA grant on the ad-hoc-signed nas-backup-sh.\nOne belt down now = PASS (data safe) instead of WARN. Real-danger cases stay\nloud FAIL: BOTH belts down, or Henry missing/stale. REQUIRE_BOTH_BELTS=1\nrestores strict degraded-redundancy WARNs if the root belt is ever fixed.\n\nCo-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>"},{"hash":"d41f040","date":"2026-08-14 12:15:46 -0700","author":"Steve Abrams","subject":"belt-and-suspenders: stagger user-agent belts past their root siblings (dwdump 03:45->03:55, realestate 03:50->03:58) — avoids the write-collision once Option-B FDA lands, adds temporal redundancy, and makes tomorrow's log the proof the user belt fires unattended (Cody cycle-11 gate, TK-10547)","body":""},{"hash":"4a447c8","date":"2026-08-14 10:18:30 -0700","author":"steve","subject":"daemon-health: belt-and-suspenders model (Steve ruled 8/14 'Both', TK-10547)","body":"Check BOTH redundant writers per DB — the -root SYSTEM daemon (Option B,\ndurable, needs FDA on /usr/local/bin/nas-backup-sh) AND the gui/ USER agent\n(Option C, homebrew bash, proven writing). Verdict is outcome-based on Henry\nfreshness: both writers healthy + fresh = PASS; exactly one belt down = WARN\n(degraded redundancy, data safe); both down or Henry missing/stale = FAIL.\nCurrent reality = WARN until the FDA grant brings the root belt up.\n\nCo-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>"},{"hash":"272bd47","date":"2026-08-14 09:54:23 -0700","author":"Steve Abrams","subject":"daemon-health: fix false-PASS regression — restore mechanism-loud verdict (broken daemon=FAIL even when Henry manually-fresh) + watch the -root SYSTEM daemons not the retired gui/ user agents (Steve ruled 8/14, TK-10547)","body":""},{"hash":"d3111b7","date":"2026-08-14 09:18:56 -0700","author":"steve","subject":"Henry mirror: switch to USER LaunchAgents via homebrew bash (real fix, TK-10547)","body":"Root cause: the root daemons' signed launcher /usr/local/bin/nas-backup-sh is\nTCC-denied on the /Volumes/Henry external volume, so the nightly [ -w ] test\nfailed and the mirror silently skipped for 5 nights (Aug 9-13). NOT an FDA-on-\n/bin/bash issue as the ticket assumed.\n\nFix (empirically verified): the mirror runs fine as USER LaunchAgents launched\nvia /opt/homebrew/bin/bash (non-SIP, holds the volume grant — the loaded\nbackup-daemon-health agent reads Henry through it hourly). Swapped the dw_unified\nuser plist to homebrew bash to match the already-swapped realestate one; both\nenabled+bootstrapped, kickstart-verified writing [Henry] rsync ok. daemon-health\nrepointed to watch the user agents in gui/ domain with Henry-freshness as the\nauthoritative signal. Canary now PASS.\n\nCo-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>"},{"hash":"3ce75d8","date":"2026-08-14 09:17:00 -0700","author":"Steve Abrams","subject":"daemon-health: split the last_exit!=0 reason so the morning panel distinguishes 'mechanism broken, data safe' (Henry fresh) from 'broken AND data stale' — verdict stays FAIL either way (TK-10547, Cody cycle-1 gate)","body":""},{"hash":"217e950","date":"2026-08-14 09:11:03 -0700","author":"Steve Abrams","subject":"Option B finalizer: drop pipefail + make signature preflight exit-code-based & non-blocking (codesign -dv exits nonzero under root, poisoning the pipefail'd grep guard into a false FATAL) — TK-10547","body":""},{"hash":"a6b1b26","date":"2026-08-14 08:46:12 -0700","author":"Steve Abrams","subject":"Option B finalizer: one-paste sudo installer to swap both backup root-daemons onto the signed launcher + kickstart-verify (TK-10547)","body":""},{"hash":"204c335","date":"2026-08-14 07:34:08 -0700","author":"Steve","subject":"Option B prep: repoint both backup root-daemon plists at /usr/local/bin/nas-backup-sh (stable launcher, survives brew upgrade) — TK-10547","body":""},{"hash":"70a847e","date":"2026-08-13 22:48:31 -0700","author":"Steve","subject":"daemon-health: dual-write heartbeat to ~/.claude/skills/backup-daemon-health/data so fleet-health-rollup + meta-watchdog surface the Henry-mirror health on the morning panel (TK-10547)","body":""},{"hash":"a0216d5","date":"2026-08-13 21:18:47 -0700","author":"Steve","subject":"Add com.steve.backup-daemon-health LaunchAgent (hourly user-context Henry-mirror canary, --alert de-nagged) — Steve-approved (TK-10547)","body":""},{"hash":"1a0f985","date":"2026-08-13 21:17:40 -0700","author":"Steve","subject":"daemon-health --alert: add de-nag (fire only on worsening transition or 20h re-nag) so an hourly canary can't spam FAIL emails (TK-10547)","body":""},{"hash":"b1e42e2","date":"2026-08-13 21:03:09 -0700","author":"Steve","subject":"pull-dw-dump: write per-DB heartbeat latest-<db>.json so dw_unified vs realestate health don't overwrite the shared latest.json (TK-10547)","body":""},{"hash":"e1954e8","date":"2026-08-13 20:18:13 -0700","author":"Steve","subject":"daemon-health: emit PASS/WARN/FAIL JSON heartbeat + guarded --alert (user-context CNCP+George), so fleet-rollup reads it right and a broken Henry daemon self-alerts (TK-10547)","body":""},{"hash":"b7fbe23","date":"2026-08-13 19:11:18 -0700","author":"Steve","subject":"Add read-only daemon-health probe: surfaces root-daemon last_exit + Henry dump freshness so a nightly-failing daemon can't hide behind a manual PARTIAL (TK-10547)","body":""},{"hash":"20be61d","date":"2026-08-13 18:04:46 -0700","author":"Steve","subject":"pull-dw-dump: derive retention glob from $BASE so realestate leg counts/prunes its own dumps (was hardcoded dw_unified)","body":""},{"hash":"17bd6ce","date":"2026-08-07 08:43:33 -0700","author":"Steve Abrams","subject":"chore(nas): commit root-daemon installer scripts (TK-10323 artifacts, session close)","body":"gitleaks false positive: KEY=id_ed25519_wallco_20260530 is the SSH key FILENAME,\nnot key material — no secret in these scripts.\n\nCo-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>"},{"hash":"fb2f00f","date":"2026-08-07 08:21:47 -0700","author":"Steve Abrams","subject":"docs(nas): add DW-Backups activation checklist (root-context autofs, not Finder mount)","body":"Cleanup 2/2 from TK-10323: the NAS 2TB share isn't racked yet, so mounting\ncan't be scripted today. Documents the copy-paste activation steps for when it\narrives, and flags the root-vs-user mount gotcha — the nightly runs as a root\nLaunchDaemon so a per-user Finder SMB mount won't be seen; use autofs (Path B).\n\nCo-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>"},{"hash":"0003120","date":"2026-08-07 08:02:36 -0700","author":"auto-commit-fleet","subject":"auto-data-snapshot: 2026-08-07T08:00:20 (3 data files) — launchd/com.steve.nas-dwdump-mirror-root.plist launchd/com.steve.nas-realestate-dump-mirror-root.plist launchd/com.steve.nas-realestate-dump-mirror.plist","body":""},{"hash":"a442f8d","date":"2026-08-07 07:30:15 -0700","author":"auto-commit-fleet","subject":"auto-data-snapshot: 2026-08-07T07:29:27 (1 data files) — launchd/com.steve.nas-realestate-dump-mirror.plist.pre-bashswap.bak","body":""},{"hash":"e448b03","date":"2026-08-06 13:35:44 -0700","author":"Steve","subject":"Gate D (TK-10076): add realestate DB 3-2-1 backup — nightly prod pg_dump + Henry/NAS pull mirror; parameterize PGDUMP_TOC_FLOOR for small DBs","body":""},{"hash":"b0fe09e","date":"2026-07-31 09:09:26 -0700","author":"Steve Abrams","subject":"mirror FAIL alert: send from steve-office, not info@","body":""},{"hash":"404391e","date":"2026-07-03 08:08:07 -0700","author":"Steve","subject":"chore: macstudio3 migration — reconcile from mac2 + repoint paths (stevestudio2→macstudio3, node/npm/npx→/opt/homebrew)","body":""},{"hash":"ace1e09","date":"2026-06-26 12:49:37 -0700","author":"Steve","subject":"3-2-1: dual-destination dw_unified pull (Henry + NAS), runtime-probed rsync flags","body":""},{"hash":"eb6346c","date":"2026-06-26 11:49:33 -0700","author":"Steve","subject":"NAS setup package: dw_unified dump mirror + canary patch + runbook","body":""}]}