[object Object]

← back to AbramsEgo

chore: replenish audit (fable) — 17+18 verified live, 12/12 canary verdicts non-null; 2 real gaps: cron cards missing last/next run+model (SPEC p4) + Kamatera panel silent-stale cache; enqueue tasks 20-21 + re-queue replenisher 22

b3fcfed48cfce328806f426536fcb6b81ce5beee · 2026-07-01 14:17:17 -0700 · Steve

Files touched

Diff

commit b3fcfed48cfce328806f426536fcb6b81ce5beee
Author: Steve <steve@designerwallcoverings.com>
Date:   Wed Jul 1 14:17:17 2026 -0700

    chore: replenish audit (fable) — 17+18 verified live, 12/12 canary verdicts non-null; 2 real gaps: cron cards missing last/next run+model (SPEC p4) + Kamatera panel silent-stale cache; enqueue tasks 20-21 + re-queue replenisher 22
---
 build-queue/{tasks => done}/19-replenish.md     |  0
 build-queue/tasks/20-cron-lastnext-run.md       | 29 +++++++++++++++++++++++++
 build-queue/tasks/21-kamatera-staleness-note.md | 27 +++++++++++++++++++++++
 build-queue/tasks/22-replenish.md               | 28 ++++++++++++++++++++++++
 data/local-fleet.json                           |  2 +-
 5 files changed, 85 insertions(+), 1 deletion(-)

diff --git a/build-queue/tasks/19-replenish.md b/build-queue/done/19-replenish.md
similarity index 100%
rename from build-queue/tasks/19-replenish.md
rename to build-queue/done/19-replenish.md
diff --git a/build-queue/tasks/20-cron-lastnext-run.md b/build-queue/tasks/20-cron-lastnext-run.md
new file mode 100644
index 0000000..6efa6bd
--- /dev/null
+++ b/build-queue/tasks/20-cron-lastnext-run.md
@@ -0,0 +1,29 @@
+# AbramsEgo task 20 — cron cards: last/next run (SPEC panel 4 literal miss)
+
+Read SPEC.md. Panel 4 spec text: "every scheduled job: status, schedule, **last/next run**, model."
+The live cron grid (114 jobs) shows status pill + schedule + program + created 🕓 chip, but NO
+last-run and NO next-run — the collector never computes them. Job fields today:
+`label, loaded, pid, lastExit, state, schedule, program, created, modified` (verified live 2026-07-01).
+
+Surgical, ~12 min, two touch points:
+
+1. `server.js` — `readCronPlistMeta()` (~line 204) already parses `StartInterval` seconds and
+   `StartCalendarInterval` Hour/Minute. Extend it:
+   - `nextRun`: for `StartInterval s` → can't know phase, so emit `null` and let the UI show the
+     interval; for calendar jobs → compute the next occurrence of `HH:MM` from now (today or
+     tomorrow) as ISO. One small helper, no deps.
+   - `lastRun` (best-effort): parse `StandardOutPath`/`StandardErrorPath` from the plist XML; if
+     the file exists, `fs.statSync(...).mtimeMs` → ISO. Missing/no-log → `null`.
+   - `model` (best-effort): regex the raw XML ProgramArguments for `--model\s*<\/string>\s*<string>([^<]+)`
+     or a `claude-(opus|sonnet|haiku|fable)[\w.-]*` token; else `null`.
+   Add `lastRun, nextRun, model` to the job object in `collectCrons()` (~line 252).
+2. `public/index.html` — cron card render (~line 526): add one `.subcard-when` line
+   `last ${whenChip(j.lastRun)||'—'} · next ${whenChip(j.nextRun)|| esc(j.schedule||'—')}` and, when
+   `j.model` present, a small `<span class="pill">` with the model. Keep the existing created 🕓 chip.
+
+Verify: `pm2 restart abramsego && sleep 3 && curl -s -u admin:'DW2024!' http://127.0.0.1:9773/api/data | node -e "const d=JSON.parse(require('fs').readFileSync(0));console.log(d.crons.jobs.filter(j=>j.lastRun||j.nextRun).length,'jobs with last/next run')"`
+— expect a clearly nonzero count, and eyeball one calendar job's nextRun is in the future.
+
+Commit: `git add -A && git commit -m "feat: cron cards show last/next run + model (SPEC panel 4)" --author="Steve <steve@designerwallcoverings.com>"`
+
+Do NOT restyle other panels. Local only, no deploy.
diff --git a/build-queue/tasks/21-kamatera-staleness-note.md b/build-queue/tasks/21-kamatera-staleness-note.md
new file mode 100644
index 0000000..e71c932
--- /dev/null
+++ b/build-queue/tasks/21-kamatera-staleness-note.md
@@ -0,0 +1,27 @@
+# AbramsEgo task 21 — Kamatera panel: surface cache staleness (silent-stale gap, task-17 class)
+
+Read SPEC.md. `collectKamateraFleet()` (`server.js` ~lines 146–166) serves the panel entirely from
+the 5-min disk cache (`data/kamatera-fleet.json`). When the SSH refresh FAILS, it silently keeps
+serving the old cache — no `note`, no `stale` flag — and the frontend never renders `k.fetchedAt`,
+so an hours-stale fleet reads as live. This is the exact failure class task 17 fixed for Fleet·Local
+(which now shows "⚠ pm2 slow — cached snapshot"). Kamatera has the data (`fetchedAt` is in the
+payload, verified live) but zero staleness surface.
+
+Surgical, ~10 min, two touch points:
+
+1. `server.js` — in `collectKamateraFleet()`: on SSH failure (`!r.ok` or JSON parse throw) record
+   the error on a module-level `kamLastErr`; in the return, compute
+   `ageMin = Math.round((Date.now()-Date.parse(c.fetchedAt))/60000)` and add
+   `stale: ageMin > 12` and, when stale or `kamLastErr`, `note: 'ssh refresh failing — cached '+ageMin+'m ago'`.
+   Clear `kamLastErr` on a successful refresh.
+2. `public/index.html` — `$('kam-up')` render (~line 340): append the panel-level freshness chip +
+   warn note, same pattern as `loc-up` one line below:
+   `+(whenChip(k.fetchedAt)||'')+(k.note?' <span class="warn" style="font-size:11px" title="'+esc(k.error||'')+'">⚠ '+esc(k.note)+'</span>':'')`
+
+Verify: `pm2 restart abramsego && sleep 3 && curl -s -u admin:'DW2024!' http://127.0.0.1:9773/api/data | node -e "const d=JSON.parse(require('fs').readFileSync(0));console.log('fetchedAt',d.kamatera.fetchedAt,'stale',d.kamatera.stale)"`
+— expect fresh fetchedAt + `stale false` on a healthy run, and the 🕓 chip visible next to the
+Kamatera up-count in the browser.
+
+Commit: `git add -A && git commit -m "fix: Kamatera fleet panel surfaces cache age + ssh-failure note (no more silent stale)" --author="Steve <steve@designerwallcoverings.com>"`
+
+Do NOT touch the local-fleet path (task 17 owns it). Local only, no deploy.
diff --git a/build-queue/tasks/22-replenish.md b/build-queue/tasks/22-replenish.md
new file mode 100644
index 0000000..39ea7a0
--- /dev/null
+++ b/build-queue/tasks/22-replenish.md
@@ -0,0 +1,28 @@
+# AbramsEgo task 22 — 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 ~3 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 20 (cron cards last/next run + model) and 21 (Kamatera panel cache-staleness
+     chip + ssh-failure note) 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 23), 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.
diff --git a/data/local-fleet.json b/data/local-fleet.json
index ab7e73d..295774e 100644
--- a/data/local-fleet.json
+++ b/data/local-fleet.json
@@ -1 +1 @@
-{"fetchedAt":"2026-07-01T21:12:10.154Z","procs":[{"name":"pm2-logrotate","status":"online","cpu":1,"memMB":42,"restarts":0,"uptimeMs":332569,"startedAt":"2026-07-01T21:06:37.585Z"},{"name":"abramsego","status":"online","cpu":3,"memMB":60,"restarts":1,"uptimeMs":31255,"startedAt":"2026-07-01T21:11:38.899Z"}]}
\ No newline at end of file
+{"fetchedAt":"2026-07-01T21:17:09.972Z","procs":[{"name":"pm2-logrotate","status":"online","cpu":0,"memMB":46,"restarts":0,"uptimeMs":632387,"startedAt":"2026-07-01T21:06:37.585Z"},{"name":"abramsego","status":"online","cpu":3,"memMB":63,"restarts":1,"uptimeMs":331073,"startedAt":"2026-07-01T21:11:38.899Z"},{"name":"api-token-dashboard","status":"online","cpu":1,"memMB":55,"restarts":1,"uptimeMs":197306,"startedAt":"2026-07-01T21:13:52.666Z"}]}
\ No newline at end of file

← 5b8c855 fix: canary board — derive verdicts for nested shapes (6/12  ·  back to AbramsEgo  ·  feat: cron cards show last/next run + model (SPEC panel 4) 0fa5291 →