[object Object]

← back to AbramsEgo

chore: replenish audit (fable) — 2 real gaps: Fleet·Local panel dead live (no last-good cache, error swallowed) + 6/12 canary verdicts null incl a live scraper FAIL; enqueue tasks 17-18 + re-queue replenisher 19

7492a848c1242f17e617b64dd0cb3dc94a2bdfc2 · 2026-07-01 13:58:43 -0700 · Steve

Files touched

Diff

commit 7492a848c1242f17e617b64dd0cb3dc94a2bdfc2
Author: Steve <steve@designerwallcoverings.com>
Date:   Wed Jul 1 13:58:43 2026 -0700

    chore: replenish audit (fable) — 2 real gaps: Fleet·Local panel dead live (no last-good cache, error swallowed) + 6/12 canary verdicts null incl a live scraper FAIL; enqueue tasks 17-18 + re-queue replenisher 19
---
 build-queue/{tasks => done}/16-replenish.md       |  0
 build-queue/tasks/17-localfleet-lastgood-cache.md | 30 +++++++++++++++++++
 build-queue/tasks/18-canary-verdict-fallback.md   | 35 +++++++++++++++++++++++
 build-queue/tasks/19-replenish.md                 | 28 ++++++++++++++++++
 4 files changed, 93 insertions(+)

diff --git a/build-queue/tasks/16-replenish.md b/build-queue/done/16-replenish.md
similarity index 100%
rename from build-queue/tasks/16-replenish.md
rename to build-queue/done/16-replenish.md
diff --git a/build-queue/tasks/17-localfleet-lastgood-cache.md b/build-queue/tasks/17-localfleet-lastgood-cache.md
new file mode 100644
index 0000000..8c2a7a3
--- /dev/null
+++ b/build-queue/tasks/17-localfleet-lastgood-cache.md
@@ -0,0 +1,30 @@
+# AbramsEgo task 17 — Fleet · Local panel is dead live: last-good cache + visible error
+
+Read SPEC.md. SPEC panel 2 (Fleet health, Mac2 pm2) is broken on the RUNNING instance right now:
+`/api/data` → `localFleet = {"procs":[],"error":"Command failed: …pm2 jlist"}` — no `up`/`total`
+at all — because the Mac2 pm2 daemon crawls under swap pressure and the 8s `sh()` timeout kills
+`pm2 jlist`. The front end then silently shows `—/— up` + "no local pm2 procs" and drops the
+`error` field on the floor. Even the AI chat reads "Local fleet: undefined/undefined online."
+The Kamatera collector already solved this exact problem with a last-good disk cache
+(`KAM_CACHE`, `server.js:129–149`); the local collector never got one.
+
+1. `server.js` `collectLocalFleet()` (lines 108–125):
+   - Add `const LOCAL_CACHE = path.join(DATA_DIR, 'local-fleet.json');` next to `KAM_CACHE` (~line 129).
+   - Bump the `sh(pm2Bin, ['jlist'], 8000)` timeout to `20000` (daemon is slow, not dead).
+   - On success: `fs.writeFileSync(LOCAL_CACHE, JSON.stringify({fetchedAt:new Date().toISOString(), procs}))`
+     before returning (keep the current return shape).
+   - On failure (`!r.ok` or JSON parse fail): read `LOCAL_CACHE`; if present return
+     `{procs:c.procs, up:c.procs.filter(p=>p.status==='online').length, total:c.procs.length,
+     fetchedAt:c.fetchedAt, note:'pm2 slow — cached snapshot', error:r.err}`; if no cache yet,
+     return the current `{procs:[], error}` **plus** `up:null, total:null, note:'pm2 unreachable — no cached snapshot yet'`.
+2. `public/index.html` (~lines 342–343): surface degradation instead of silence — after the
+   `loc-up` counts, when `l.note` is set append
+   `<span class="warn" style="font-size:11px" title="${esc(l.error||'')}">⚠ ${esc(l.note)}</span>`,
+   and pass `l.note` as the empty-text for `#loc-list` (same pattern as `k.note` in `renderKamFleet`).
+3. Seed the cache: run `~/.npm-global/bin/pm2 jlist` once in a shell (let it take its time) to
+   confirm the daemon answers; the next snapshot after restart writes the cache on first success.
+
+Verify: `pm2 restart abramsego && sleep 35 && curl -s -u admin:'DW2024!' http://127.0.0.1:9773/api/data | node -e "let s='';process.stdin.on('data',d=>s+=d).on('end',()=>{const f=JSON.parse(s).localFleet;console.log(f.up,'/',f.total,f.note||'live')})"`
+— expect real numbers (live or cached+note), never silent `undefined/undefined`.
+Commit: `git add -A && git commit -m "fix: local fleet panel — last-good cache + visible degradation note (was silently dead)" --author="Steve <steve@designerwallcoverings.com>"`
+Local only, no deploy. Do not restyle other panels.
diff --git a/build-queue/tasks/18-canary-verdict-fallback.md b/build-queue/tasks/18-canary-verdict-fallback.md
new file mode 100644
index 0000000..ac511af
--- /dev/null
+++ b/build-queue/tasks/18-canary-verdict-fallback.md
@@ -0,0 +1,35 @@
+# AbramsEgo task 18 — Canary board: 6 of 12 verdicts render null "—" (nested-shape parser gap)
+
+Read SPEC.md. The canary board (SPEC panel 2/6 surface) shows a dim `—` verdict for HALF the
+canaries — disk-space-canary, dw-canary-meta-watchdog, keep-alive, dw-leak-scanner,
+dw-scraper-canary, dw-backup-canary — because `collectCanaries()` (`server.js:151–173`, verdict
+extraction at line 163) only reads a **top-level** `verdict`/`status`, but those skills write
+nested shapes. Worse: dw-scraper-canary's latest.json currently has `"fail":1` (a REAL vendor
+FAIL) and dw-backup-canary has `"warn":9` — both invisible on the board AND missed by the
+alert banner (`index.html:370` only counts FAIL-ish verdicts).
+
+Observed shapes in `~/.claude/skills/<name>/data/latest.json`:
+- disk-space-canary: per-unit verdicts in `units[].verdict` (worst-of wins)
+- dw-scraper-canary / dw-backup-canary / dw-canary-meta-watchdog: top-level `fail` / `warn` counters
+- dw-leak-scanner: `leaking` counter (`leaking:0` = clean)
+- keep-alive: `dead` / `restarted` counters
+- timestamps live in `ts` (disk/swap) or `at` (keep-alive), which line 164 doesn't read.
+
+1. In `server.js` after line 163, when `verdict` is still null, derive it:
+   ```js
+   if (!verdict) {
+     const worst = (vs) => { const V = vs.filter(Boolean).map(v => String(v).toUpperCase());
+       return V.some(v => /FAIL|CRIT|DOWN/.test(v)) ? 'FAIL' : V.some(v => /WARN|DEGRAD/.test(v)) ? 'WARN' : V.length ? 'PASS' : null; };
+     if (Array.isArray(j.units)) verdict = worst(j.units.map(u => u.verdict));
+     else if ((j.fail > 0) || (j.leaking > 0) || (j.dead > 0)) verdict = 'FAIL';
+     else if (j.warn > 0) verdict = 'WARN';
+     else if (j.fail === 0 || j.leaking === 0 || j.dead === 0) verdict = 'PASS';
+   }
+   ```
+2. Line 164: extend the scanned fallback → `j.scanned_at || j.checked_at || j.ts || j.at || null`.
+
+Verify: `pm2 restart abramsego && sleep 35 && curl -s -u admin:'DW2024!' http://127.0.0.1:9773/api/data | node -e "let s='';process.stdin.on('data',d=>s+=d).on('end',()=>{JSON.parse(s).canaries.canaries.forEach(c=>console.log(c.name,c.verdict))})"`
+— expect zero `null` verdicts; dw-scraper-canary = FAIL (and it now appears in the ⚠ alert banner),
+dw-backup-canary = WARN, disk-space-canary = WARN (Mac2 unit is at 11% free).
+Commit: `git add -A && git commit -m "fix: canary board — derive verdicts for nested shapes (6/12 were null, incl a live scraper FAIL)" --author="Steve <steve@designerwallcoverings.com>"`
+Local only, no deploy. Do not restyle the board.
diff --git a/build-queue/tasks/19-replenish.md b/build-queue/tasks/19-replenish.md
new file mode 100644
index 0000000..eb5fe2f
--- /dev/null
+++ b/build-queue/tasks/19-replenish.md
@@ -0,0 +1,28 @@
+# AbramsEgo task 19 — REPLENISH: fresh-eyes full-completion audit (Fable 5)
+
+You are the queue replenisher. Read SPEC.md FIRST, then audit the RUNNING dashboard, not the
+file bookkeeping.
+
+1. Pull the live surface: `curl -s -u admin:'DW2024!' http://127.0.0.1:9773/api/data` and read
+   `public/index.html` + `server.js`. Check every SPEC panel (1–8) is present AND wired to real
+   data (no dead placeholders), and every Steve hard rule holds:
+   - 🕓 created date+time chip visible on EVERY admin card/row (full ISO in `title=`).
+   - sort `<select>` + density slider + infinite scroll on every grid that can exceed ~40 rows,
+     localStorage-persisted. (Officers panel is compliant via the panel-level fetchedAt chip —
+     officers carry no per-record timestamp; the local fleet list at ~2 procs was deliberately
+     left without grid controls in task 15 — don't re-flag those.)
+   - every cost figure labeled; free/local shows `$0 (local)`.
+   - Verify tasks 17 (local-fleet last-good cache + degradation note) and 18 (canary verdict
+     fallback for nested shapes) actually landed on the live instance.
+2. For each REAL gap found (broken data, missing SPEC feature, hard-rule violation): write ONE
+   surgical <15-min task file to `build-queue/tasks/NN-<slug>.md` (NN starting at 20), following
+   the style of `build-queue/done/12-officers-datetime-chip.md` — name exact files/lines, one
+   verify command, one commit line. BOUND: enqueue at most 3 tasks, highest-value first. Also
+   re-enqueue a copy of this replenisher as the LAST task (next number) ONLY if you enqueued ≥1 task.
+3. If NO real gaps: enqueue nothing, do NOT re-enqueue yourself — the loop rests, the build is
+   at full completion. Don't invent busywork to justify the run.
+4. Move this file to `build-queue/done/` and commit
+   `chore: replenish audit (fable) — <verdict>` with author `steve@designerwallcoverings.com`.
+
+Local only. No deploy, no DNS, no publish — draft any gated need to
+~/.claude/yolo-queue/pending-approval/ instead.

← d3f2f9c chore: mark task 15 done (Kamatera fleet grid controls lande  ·  back to AbramsEgo  ·  auto-save: 2026-07-01T14:06:35 (5 files) — build-queue/tasks 27d1b76 →