[object Object]

← back to Dw Pitch Followup

pitch: scheduled refresh now also pre-triages the whole board (build-lists → pretriage), so every card is Follow-up scored

e90255cd61e7a3fe7cb8b26752c8acf4d69eefcb · 2026-07-15 10:23:03 -0700 · Steve

Files touched

Diff

commit e90255cd61e7a3fe7cb8b26752c8acf4d69eefcb
Author: Steve <steve@designerwallcoverings.com>
Date:   Wed Jul 15 10:23:03 2026 -0700

    pitch: scheduled refresh now also pre-triages the whole board (build-lists → pretriage), so every card is Follow-up scored
---
 launchd/com.steve.dw-pitch-rebuild.plist |  4 ++--
 scripts/pretriage.js                     | 40 ++++++++++++++++++++++++++++++++
 scripts/refresh.sh                       | 12 ++++++++++
 3 files changed, 54 insertions(+), 2 deletions(-)

diff --git a/launchd/com.steve.dw-pitch-rebuild.plist b/launchd/com.steve.dw-pitch-rebuild.plist
index e6314d5..7c75d18 100644
--- a/launchd/com.steve.dw-pitch-rebuild.plist
+++ b/launchd/com.steve.dw-pitch-rebuild.plist
@@ -6,8 +6,8 @@
   <string>com.steve.dw-pitch-rebuild</string>
   <key>ProgramArguments</key>
   <array>
-    <string>/opt/homebrew/bin/node</string>
-    <string>scripts/build-lists.js</string>
+    <string>/bin/sh</string>
+    <string>scripts/refresh.sh</string>
   </array>
   <key>WorkingDirectory</key>
   <string>/Users/macstudio3/Projects/dw-pitch-followup</string>
diff --git a/scripts/pretriage.js b/scripts/pretriage.js
new file mode 100644
index 0000000..62f0026
--- /dev/null
+++ b/scripts/pretriage.js
@@ -0,0 +1,40 @@
+// pretriage.js — pre-warm the follow-up cache for the WHOLE board so every card shows a
+// Follow up? verdict without anyone opening it. Runs against the live server (which owns the
+// persistent cache); the batch endpoint skips any verdict fresh <24h, so re-runs are cheap.
+// Local Ollama only → $0. Meant to run right after build-lists.js (see refresh.sh).
+
+const BASE = process.env.PITCH_URL || 'http://127.0.0.1:9768';
+const CHUNK = 40;
+
+async function main() {
+  const t0 = Date.now();
+  let L;
+  try { L = await (await fetch(BASE + '/api/lists', { signal: AbortSignal.timeout(15000) })).json(); }
+  catch (e) { console.error('[pretriage] server unreachable at ' + BASE + ' — skipping:', e.message); process.exit(0); }
+
+  const seen = new Set(), clients = [];
+  for (const list of ['list1', 'list2']) for (const r of (L[list] || [])) {
+    const email = String(r.email || '').trim(); const lc = email.toLowerCase();
+    if (!email || seen.has(lc)) continue; seen.add(lc);
+    const q = (Array.isArray(r.merch_invoices) ? r.merch_invoices : []).find((x) => !x.booked);
+    clients.push({ email, company: r.company || '', quote: q && q.invoice ? ('#' + q.invoice) : '' });
+  }
+  console.log('[pretriage] ' + clients.length + ' unique clients with email to score');
+
+  let done = 0, analyzed = 0;
+  for (let i = 0; i < clients.length; i += CHUNK) {
+    const chunk = clients.slice(i, i + CHUNK);
+    try {
+      const r = await fetch(BASE + '/api/followup-batch', {
+        method: 'POST', headers: { 'Content-Type': 'application/json' },
+        body: JSON.stringify({ clients: chunk }), signal: AbortSignal.timeout(600000),
+      });
+      const j = await r.json(); analyzed += (j.analyzed || 0);
+    } catch (e) { console.error('[pretriage] chunk @' + i + ' failed:', e.message); }
+    done += chunk.length;
+    console.log('[pretriage] ' + done + '/' + clients.length + ' processed (' + analyzed + ' freshly scored) · ' + ((Date.now() - t0) / 1000).toFixed(0) + 's');
+  }
+  console.log('[pretriage] done in ' + ((Date.now() - t0) / 1000).toFixed(0) + 's ($0 — local Ollama)');
+}
+
+main().catch((e) => { console.error('[pretriage] FATAL', e); process.exit(1); });
diff --git a/scripts/refresh.sh b/scripts/refresh.sh
new file mode 100755
index 0000000..305204e
--- /dev/null
+++ b/scripts/refresh.sh
@@ -0,0 +1,12 @@
+#!/bin/sh
+# refresh.sh — rebuild the pitch lists from FileMaker, then pre-triage the whole board.
+# Run by launchd (com.steve.dw-pitch-rebuild) 3x/day + on boot. Both steps are $0 (local).
+set -e
+DIR="$(cd "$(dirname "$0")/.." && pwd)"
+cd "$DIR"
+NODE=/opt/homebrew/bin/node
+echo "=== refresh $(date) ==="
+"$NODE" scripts/build-lists.js
+# pre-triage hits the running server (owns the follow-up cache); non-fatal if it can't
+"$NODE" scripts/pretriage.js || echo "[refresh] pretriage skipped/failed (non-fatal)"
+echo "=== refresh done $(date) ==="

← 5e218be pitch: Follow up? YES/NO chip on every card + per-column ⚡ T  ·  back to Dw Pitch Followup  ·  auto-save: 2026-07-15T10:35:56 (2 files) — server.js lib/fm- e35cf91 →