[object Object]

← 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 →