← 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
M launchd/com.steve.dw-pitch-rebuild.plistA scripts/pretriage.jsA scripts/refresh.sh
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 →