← back to Designerwallcoverings
scripts/canary-board/canary-board.mjs
91 lines
// canary-board — run every DW canary and aggregate into ONE red/green health pane.
// Proves the tools built tonight actually run, and gives Steve a single morning view.
// READ-ONLY: each underlying canary is read-only; this only runs them + reads their
// JSON. Network canaries are time-boxed and degrade to UNKNOWN (never fail the board).
import { execFileSync } from 'node:child_process';
import fs from 'node:fs';
const HOME = process.env.HOME;
const PROJ = `${HOME}/Projects/designerwallcoverings`;
const YQ = `${HOME}/.claude/yolo-queue`;
const today = new Date().toISOString().slice(0, 10);
const OUT = `${YQ}/canary-board-${today}.json`;
const MD = `${YQ}/canary-board-${today}.md`;
function run(cmd, args, opts = {}) {
try { execFileSync(cmd, args, { encoding: 'utf8', timeout: opts.timeout || 60000, stdio: ['ignore', 'ignore', 'ignore'], cwd: opts.cwd || PROJ }); return true; }
catch (e) { return false; }
}
function readJSON(p) { try { return JSON.parse(fs.readFileSync(p, 'utf8')); } catch { return null; } }
const board = [];
// 1) canary-meta-watchdog (launchd freshness) — fast local
run('node', ['scripts/canary-meta-watchdog/canary-meta-watchdog.js']);
{
const j = readJSON(`${YQ}/canary-meta-watchdog-${today}.json`);
if (j) board.push({ canary: 'meta-watchdog', verdict: j.summary?.FAIL ? 'FAIL' : 'PASS',
headline: `${j.total} jobs · FAIL ${j.summary?.FAIL||0} / WARN ${j.summary?.WARN||0} / PASS ${j.summary?.PASS||0}` });
else board.push({ canary: 'meta-watchdog', verdict: 'UNKNOWN', headline: 'no output' });
}
// 2) contract-scope-auditor — fast local DB
run('node', ['scripts/contract-scope-auditor/contract-scope-auditor.mjs']);
{
const j = readJSON(`${YQ}/contract-scope-audit-${today}.json`);
if (j) board.push({ canary: 'contract-scope', verdict: j.verdict, headline: (j.findings||[]).map(f=>`${f.sev}:${f.check}`).join(' ') || 'clean' });
else board.push({ canary: 'contract-scope', verdict: 'UNKNOWN', headline: 'no output' });
}
// 3) scraper-contract-test — fast local DB
run('node', ['scripts/scraper-contract-test/scraper-contract-test.mjs']);
{
const j = readJSON(`${YQ}/scraper-contract-test-2026-06-16.json`);
if (j) board.push({ canary: 'scraper-contract', verdict: j.summary?.FAIL ? 'FAIL' : j.summary?.WARN ? 'WARN' : 'PASS',
headline: `${j.vendors} vendors · ${Object.entries(j.summary).map(([k,v])=>`${k}=${v}`).join(' ')}` });
else board.push({ canary: 'scraper-contract', verdict: 'UNKNOWN', headline: 'no output' });
}
// 4) dw-map-auditor — fast local DB (existing skill)
run('bash', ['run.sh'], { cwd: `${HOME}/.claude/skills/dw-map-auditor` });
{
const j = readJSON(`${HOME}/.claude/skills/dw-map-auditor/data/latest.json`);
if (j) board.push({ canary: 'dw-map-auditor', verdict: j.verdict, headline: `${j.fail||0} FAIL / ${j.warn||0} WARN (Kravet MAP + Nicolette)` });
else board.push({ canary: 'dw-map-auditor', verdict: 'UNKNOWN', headline: 'no output' });
}
// 5) cf-dns-snapshot — read latest report (ran this session; re-run is fast/read-only)
run('node', ['scripts/cf-dns-snapshot/cf-dns-snapshot.js'], { timeout: 90000 });
{
const snap = readJSON(`${PROJ}/scripts/cf-dns-snapshot/snapshots/cf-zones-latest.json`);
board.push({ canary: 'cf-dns-drift', verdict: snap ? 'PASS' : 'UNKNOWN',
headline: snap ? `${snap.zone_count} zones baseline (diff vs HEAD)` : 'no snapshot' });
}
// 6) dw-scraper-canary — NETWORK (storefront fetch); time-boxed, degrade to UNKNOWN
const ok = run('node', ['canary.mjs'], { cwd: `${HOME}/.claude/skills/dw-scraper-canary`, timeout: 120000 });
{
const j = readJSON(`${HOME}/.claude/skills/dw-scraper-canary/data/latest.json`);
if (j) board.push({ canary: 'dw-scraper-canary', verdict: j.verdict || (j.fail? 'FAIL':'PASS'), headline: 'output-side (public feed) count/leak/image' });
else board.push({ canary: 'dw-scraper-canary', verdict: ok ? 'PASS' : 'UNKNOWN', headline: ok ? 'ran (no latest.json schema)' : 'network/timeout — not a failure' });
}
const rank = { FAIL: 0, WARN: 1, UNKNOWN: 2, PASS: 3 };
board.sort((a, b) => rank[a.verdict] - rank[b.verdict]);
const counts = board.reduce((m, b) => (m[b.verdict] = (m[b.verdict]||0)+1, m), {});
const overall = board.some(b => b.verdict === 'FAIL') ? 'FAIL' : board.some(b => b.verdict === 'WARN') ? 'WARN' : 'PASS';
const report = { generated_at: new Date().toISOString(), overall, counts, canaries: board };
fs.writeFileSync(OUT, JSON.stringify(report, null, 2));
let md = `# DW Canary Board — ${new Date().toISOString().slice(0,16)}\n\n`;
md += `**Overall: ${overall}** · ${Object.entries(counts).map(([k,v])=>`${k}=${v}`).join(' · ')} · ${board.length} canaries\n\n`;
md += `| Canary | Verdict | Headline |\n|---|---|---|\n`;
for (const b of board) md += `| ${b.canary} | ${{FAIL:'🔴',WARN:'🟠',UNKNOWN:'⚪',PASS:'🟢'}[b.verdict]} ${b.verdict} | ${b.headline} |\n`;
md += `\n_All read-only. Built/aggregated by overnight loop cycle 16. $0._\n`;
fs.writeFileSync(MD, md);
console.log(`[canary-board] OVERALL ${overall} · ${Object.entries(counts).map(([k,v])=>`${k}=${v}`).join(' ')}`);
for (const b of board) console.log(` ${{FAIL:'🔴',WARN:'🟠',UNKNOWN:'⚪',PASS:'🟢'}[b.verdict]} ${b.canary.padEnd(20)} ${b.headline}`);
console.log(`Report: ${MD}`);