← back to Stack Map Viewer
Add 'date last run' to all drill cards (officers, agents, skills, projects)
3dcba1b3c5137c936bc306d3ec78105e8eab568c · 2026-09-22 13:13:56 -0700 · Steve Abrams
Every entity's 5W card now carries a LAST RUN row sourced from a TRUE
execution signal, never a fabricated one:
- officers/agents: newest ticket-event OR reversible-ledger entry under the
agent name (events.jsonl/ledger.jsonl are append-ordered -> last match = newest);
idle/missing-md agents show an explicit 'never recorded' rather than a guess
- skills: vendor _catalog scrape ts (scrapers) -> data/latest.json heartbeat
mtime (health skills) -> cron-driver attribution -> 'on-demand, no run record'
- projects: newest git commit (never the dir mtime, which moves on edit)
Frontend already renders info.lastRun; this fills it server-side.
grep -F name guard added. TK-12027.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018b1cDQdM4DFs1BGsqimvy3
Files touched
Diff
commit 3dcba1b3c5137c936bc306d3ec78105e8eab568c
Author: Steve Abrams <steve@designerwallcoverings.com>
Date: Tue Sep 22 13:13:56 2026 -0700
Add 'date last run' to all drill cards (officers, agents, skills, projects)
Every entity's 5W card now carries a LAST RUN row sourced from a TRUE
execution signal, never a fabricated one:
- officers/agents: newest ticket-event OR reversible-ledger entry under the
agent name (events.jsonl/ledger.jsonl are append-ordered -> last match = newest);
idle/missing-md agents show an explicit 'never recorded' rather than a guess
- skills: vendor _catalog scrape ts (scrapers) -> data/latest.json heartbeat
mtime (health skills) -> cron-driver attribution -> 'on-demand, no run record'
- projects: newest git commit (never the dir mtime, which moves on edit)
Frontend already renders info.lastRun; this fills it server-side.
grep -F name guard added. TK-12027.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018b1cDQdM4DFs1BGsqimvy3
---
server.js | 72 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++------
1 file changed, 66 insertions(+), 6 deletions(-)
diff --git a/server.js b/server.js
index 82600f2..598b510 100644
--- a/server.js
+++ b/server.js
@@ -14,6 +14,7 @@ const PROJECTS = path.join(HOME, 'Projects');
const LA = path.join(HOME, 'Library/LaunchAgents');
const PENDING = path.join(HOME, '.claude/yolo-queue/pending-approval');
const LEDGER = path.join(HOME, '.claude/yolo-queue/executed-reversible/ledger.jsonl');
+const TICKETS = path.join(HOME, '.claude/tickets/events.jsonl');
const AUTH = 'Basic ' + Buffer.from('admin:DW2024!').toString('base64');
const sh = (c) => { try { return execSync(c, { encoding: 'utf8', timeout: 15000 }); } catch { return ''; } };
@@ -233,6 +234,49 @@ function tableLastRun(tbl) {
return lr ? { ...lr, tbl } : null;
}
+// ── "date last run" for agents/officers/skills/projects ──────────────────────
+// HARD RULE (per CLAUDE.md TK-11431): a last-run must come from a TRUE execution
+// signal, never a fabricated one. A skill-DIR mtime moves when the skill is EDITED,
+// not when it RUNS — so it's a db-touch lie and is never used here. When no genuine
+// run signal exists we say so explicitly ("never recorded") rather than invent a date.
+const ageOf = (iso) => { const t = Date.parse(iso); if (isNaN(t)) return ''; const d = Math.floor((Date.now() - t) / 86400000); return d <= 0 ? 'today' : d === 1 ? '1 day ago' : d + ' days ago'; };
+const fmtWhen = (iso) => { const d = new Date(iso); return isNaN(d.getTime()) ? String(iso) : d.toLocaleString(); };
+// newest matching line in an append-only, chronological JSONL where "agent":"<name>"
+// (both events.jsonl and ledger.jsonl are append-ordered, so the LAST match is newest)
+function newestAgentEntry(file, name) {
+ if (!/^[a-z0-9][a-z0-9._-]*$/i.test(name)) return null; // names are filenames — guard grep -F against newlines/meta
+ const line = sh(`grep -F ${shq('"agent":"' + name + '"')} ${shq(file)} 2>/dev/null | tail -1`).trim();
+ if (!line) return null;
+ try { const j = JSON.parse(line); return j.ts ? { ts: j.ts, ticket: j.ticket || j.id || '', what: j.action || j.title || j.text || j.type || '' } : null; }
+ catch { return null; }
+}
+// last time an agent/officer actually RAN = newest ticket-event OR reversible-ledger entry under its name
+function agentLastRun(name) {
+ const cands = [];
+ const ev = newestAgentEntry(TICKETS, name); if (ev) cands.push({ src: 'ticket', ...ev });
+ const lg = newestAgentEntry(LEDGER, name); if (lg) cands.push({ src: 'ledger', ...lg });
+ if (!cands.length) return 'never recorded — no ticket or ledger activity logged under this agent';
+ cands.sort((a, b) => Date.parse(b.ts) - Date.parse(a.ts));
+ const c = cands[0];
+ const snip = String(c.what || '').replace(/\s+/g, ' ').slice(0, 80);
+ return fmtWhen(c.ts) + ' (' + ageOf(c.ts) + ') · via ' + c.src + (c.ticket ? ' ' + c.ticket : '') + (snip ? ' — ' + snip : '');
+}
+function agentMdDesc(id) {
+ const md = readText(path.join(AGENTS, id + '.md'));
+ const fm = md.match(/^---\n([\s\S]*?)\n---/);
+ if (fm) { const dm = fm[1].match(/^description:\s*([\s\S]*?)(?:\n[a-zA-Z_-]+:\s|\n*$)/m); if (dm) return dm[1].replace(/\s+/g, ' ').trim(); }
+ return '';
+}
+function agentInfo(id) {
+ const desc = agentMdDesc(id);
+ return {
+ description: desc || '(' + (id.startsWith('vp-') ? 'cabinet officer' : 'worker agent') + ' ' + id + ')',
+ who: id.startsWith('vp-') ? 'cabinet officer (vp-*) — routes + signs off gated work' : 'worker agent — owns & supervises, recruits subagents',
+ when: 'on-demand (invoked via Agent tool / officer routing)',
+ lastRun: agentLastRun(id),
+ };
+}
+
function skillInfo(id) {
const { dir, name, desc, body } = parseSkillMd(id);
const hay = desc + ' \n' + body.slice(0, 4000);
@@ -278,7 +322,16 @@ function skillInfo(id) {
const vendorTbl = tables.find(t => /_catalog$/.test(t)) || tables[0];
const lr = vendorTbl ? tableLastRun(vendorTbl) : null;
const hist = lr ? (lr.cadence === 'cadence' ? ' · ' + lr.scrapeDates + ' scrapes since ' + lr.firstScrape + ' (cadence lapsed)' : lr.cadence === 'once' ? ' · ⚠ scraped ONCE at onboarding, never refreshed' : ' · ⚠ no true scrape ever (db-touch only)') : '';
- const lastRun = lr ? (lr.last + (lr.age ? ' (' + lr.age + ')' : '') + ' · ' + Number(lr.rows).toLocaleString() + ' rows · ' + lr.tbl + hist) : null;
+ let lastRun;
+ if (lr) {
+ lastRun = lr.last + (lr.age ? ' (' + lr.age + ')' : '') + ' · ' + Number(lr.rows).toLocaleString() + ' rows · ' + lr.tbl + hist;
+ } else {
+ // no vendor table → next-strongest TRUE run signal, else say so honestly (never the dir mtime)
+ const hb = path.join(dir, 'data/latest.json');
+ if (existsP(hb)) { const m = new Date(mtimeP(hb)).toISOString(); lastRun = 'heartbeat ' + fmtWhen(m) + ' (' + ageOf(m) + ') · rewritten every run → data/latest.json'; }
+ else if (refP.length) { lastRun = 'driven by cron ' + refP[0].replace(/\.plist$/, '') + ' — no per-run timestamp recorded'; }
+ else { lastRun = 'on-demand — no local run record kept'; }
+ }
return {
description: desc || '(no description found in SKILL.md)',
what, why,
@@ -391,18 +444,18 @@ const DRILLERS = {
if (del) for (const nm of del[1].split(/,|and/).map(x => x.trim().replace(/\s+subagents?$/, '')).filter(Boolean)) {
if (existsP(path.join(AGENTS, nm + '.md')) && !seen.has('a' + nm)) { seen.add('a' + nm); children.push({ type: 'agent', id: nm, label: nm, meta: 'delegate' }); }
}
- return { title: 'officer · ' + id + ' (' + children.length + ')', breadcrumb: 'officer/' + id, children };
+ return { title: 'officer · ' + id + ' (' + children.length + ')', breadcrumb: 'officer/' + id, info: agentInfo(id), children };
},
agent(id) {
const md = readText(path.join(AGENTS, id + '.md'));
- if (!md) return { title: 'agent · ' + id, breadcrumb: id, children: [{ type: 'kv', id, label: '(no such agent .md)', meta: '' }] };
+ if (!md) return { title: 'agent · ' + id, breadcrumb: id, info: { description: '(no agent .md found — may be a skill or renamed)', lastRun: agentLastRun(id) }, children: [{ type: 'kv', id, label: '(no such agent .md)', meta: '' }] };
const children = [];
const tools = md.match(/^tools:\s*(.+)$/m);
if (tools) children.push({ type: 'kv', id: id + '#tools', label: 'tools', meta: tools[1].trim() });
const names = new Set([...md.matchAll(/skills\/([a-z0-9][a-z0-9-]+)/gi)].map(x => x[1]));
for (const nm of names) if (existsP(path.join(SKILLS, nm))) children.push({ type: 'skill', id: nm, label: 'skill: ' + nm, meta: 'invokes' });
if (id.startsWith('vp-')) { const off = DRILLERS.officer(id); for (const c of off.children) children.push(c); }
- return { title: 'agent · ' + id + ' (' + children.length + ')', breadcrumb: 'agent/' + id, children };
+ return { title: 'agent · ' + id + ' (' + children.length + ')', breadcrumb: 'agent/' + id, info: agentInfo(id), children };
},
canary(id) {
const dir = path.join(SKILLS, id);
@@ -417,11 +470,18 @@ const DRILLERS = {
const children = [];
const isRepo = existsP(path.join(dir, '.git'));
children.push({ type: 'kv', id: id + '#git', label: 'git repo', meta: isRepo ? 'yes' : 'no' });
- if (isRepo) { const st = sh(`cd ${shq(dir)} && git log -1 --format='%h %s' 2>/dev/null`).trim(); if (st) children.push({ type: 'kv', id: id + '#head', label: 'HEAD', meta: st }); }
+ // LAST RUN — for a project the true "last activity" signal is the newest git commit (never dir mtime)
+ let headSubj = '', lastRun;
+ if (isRepo) {
+ const ci = sh(`cd ${shq(dir)} && git log -1 --format='%cI' 2>/dev/null`).trim();
+ headSubj = sh(`cd ${shq(dir)} && git log -1 --format='%h %s' 2>/dev/null`).trim();
+ if (headSubj) children.push({ type: 'kv', id: id + '#head', label: 'HEAD', meta: headSubj });
+ lastRun = ci ? 'git commit ' + fmtWhen(ci) + ' (' + ageOf(ci) + ')' + (headSubj ? ' · ' + headSubj : '') : 'git repo — no commits yet';
+ } else { lastRun = 'not a git repo — no commit history'; }
let entries = [];
try { entries = fs.readdirSync(dir).filter(f => !f.startsWith('.') && f !== 'node_modules').sort().slice(0, 60); } catch {}
for (const e of entries) { const abs = path.join(dir, e); children.push({ type: 'file', id: abs, label: e + (isDirP(abs) ? '/' : ''), meta: isDirP(abs) ? 'dir' : fmtSize(sizeP(abs)) }); }
- return { title: 'project · ' + id, breadcrumb: 'project/' + id, children };
+ return { title: 'project · ' + id, breadcrumb: 'project/' + id, info: { description: headSubj || ('project ' + id), who: '~/Projects/' + id, lastRun }, children };
},
parked(id) {
const p = path.join(PENDING, id);
← 1a1a9cd scraper view: History column — cadence-lapsed vs onboarded-o
·
back to Stack Map Viewer
·
scraper staleness: add crawled_at + promote created_at (new- fa2db10 →