{"slug":"dw-pitch-followup","total":66,"limit":100,"offset":0,"since":null,"commits":[{"hash":"dcb29c4","date":"2026-07-15 11:04:43 -0700","author":"Steve Abrams","subject":"chore: lint, refactor (dedup repaint per unique email), v0.4.1 (session close)","body":""},{"hash":"229cd17","date":"2026-07-15 10:56:36 -0700","author":"Steve","subject":"fix(notes): FileMaker note-save silently no-op'd — write rep-1 bare key + read-back verify","body":"Root cause: repeating-field rep 1 must be written as the BARE field name (\"Internal notes\"), not \"Internal notes(1)\" — the Data API accepts the (1) form with success code 0 but silently ignores it, so notes logged as saved but never persisted (verified: a real save was empty on read-back). Also updateRecord reports committed:true after any non-erroring PATCH without confirming the field changed. Fix: write bare key AND read the record back, throwing if the note is not actually present — never report a save we cannot see.\n\nCo-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>"},{"hash":"109d6a3","date":"2026-07-15 10:48:35 -0700","author":"Steve","subject":"Make invoice # on the note chip + notes editor clickable — opens the invoice in FileMaker Pro via fmp:// GotoInvoice","body":""},{"hash":"a923a04","date":"2026-07-15 10:45:44 -0700","author":"Steve Abrams","subject":"fix: ✓ emailed date chip appears immediately after send — repaint live cards by DOM lookup (active.card was detached after board re-renders), update SENT_IDX in memory for same-client cards in other lists, and refresh sent-index on tab focus for cross-device visibility","body":""},{"hash":"177117e","date":"2026-07-15 10:44:52 -0700","author":"Steve","subject":"Show invoice # + current internal note on every pitch card — batched FM read (50/query, 10-min cache), chip updates live on save","body":""},{"hash":"58c847e","date":"2026-07-15 10:40:25 -0700","author":"Steve","subject":"Add Internal-notes editor to pitch cards — 🗒 Notes button next to Done reads/writes FileMaker Invoice::Internal notes with dated entries","body":""},{"hash":"e35cf91","date":"2026-07-15 10:35:57 -0700","author":"Steve Abrams","subject":"auto-save: 2026-07-15T10:35:56 (2 files) — server.js lib/fm-notes.js","body":""},{"hash":"e90255c","date":"2026-07-15 10:23:03 -0700","author":"Steve","subject":"pitch: scheduled refresh now also pre-triages the whole board (build-lists → pretriage), so every card is Follow-up scored","body":""},{"hash":"5e218be","date":"2026-07-15 10:15:31 -0700","author":"Steve","subject":"pitch: Follow up? YES/NO chip on every card + per-column ⚡ Triage (batch, cached)","body":"Server keeps a persistent follow-up cache (data/followup-cache.json): analyzeFollowup writes each verdict; drawer opens + a new POST /api/followup-batch (concurrency 3, skips <24h-fresh) fill it. GET /api/followup-cache feeds card badges at boot. Cards show a green \"Follow up\" / tan \"Hold\" chip from cache; ⚡ Triage per column batches all un-cached clients with live progress; opening a drawer lights that card immediately. All local Ollama, $0.\n\nCo-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>"},{"hash":"8e921dc","date":"2026-07-15 10:09:49 -0700","author":"Steve","subject":"pitch: auto follow-up triage — reads the email thread → Follow up? YES/NO + notes (delays, deadlines, status)","body":"New /api/followup-analysis reads the client thread via George and runs local Ollama (hermes3:8b, $0) to decide follow-up yes/no with a reason, concrete notes (delays/deadlines/promises/status), and a deadline date. Drawer shows a prominent Follow up? badge (green YES / red NO) auto-loaded on open, cached 6h per client, with a ↻ re-analyze. Deadline sanitized to real date-or-empty.\n\nCo-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>"},{"hash":"e039b5a","date":"2026-07-15 10:01:50 -0700","author":"Steve","subject":"pitch: click any email in the history to expand its full body","body":"New /api/email?id=&account= fetches the full message via George and strips HTML to readable text. Each email row is now clickable → lazy-loads + expands the body inline (caret ▸/▾), collapses on re-click, cached per message.\n\nCo-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>"},{"hash":"8f32c4b","date":"2026-07-15 09:58:31 -0700","author":"Steve","subject":"pitch: add 3x/day launchd rebuild so the board never goes stale (bootstrap = Steve paste)","body":""},{"hash":"e592d47","date":"2026-07-15 09:52:22 -0700","author":"Steve","subject":"pitch: email history collapsed by default, lazy-loads on expand (George search only fires when opened)","body":""},{"hash":"a4bc2e8","date":"2026-07-15 09:48:27 -0700","author":"Steve","subject":"pitch: Details drawer shows full email history with each client","body":"New /api/emails?email= (George read-only) returns all emails to/from a client across info@ + steve-office, deduped, newest first, with in/out direction from the From header. Drawer renders the thread list (direction chip · date · subject · snippet), lazy-loaded on open alongside last-corresponded.\n\nCo-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>"},{"hash":"71574cf","date":"2026-07-14 13:29:59 -0700","author":"Steve","subject":"5x: 2 clean sweeps (7/7 six-way + 51 controls, 0 defects) after card/UX + George + FM batch","body":""},{"hash":"9aa4cdc","date":"2026-07-14 11:21:53 -0700","author":"Steve","subject":"pitch: letter panel shows product thumbnails + DW product-page links","body":"renderLetterGallery draws a small strip under the letter — 40px thumbnail + link to the DW product page for each referenced sampled item (image/url from dw_unified). Shown on panel open. 476/600 list1 clients have images.\n\nCo-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>"},{"hash":"c6994a4","date":"2026-07-14 11:20:16 -0700","author":"Steve","subject":"pitch: list2 letter follows up on the specific QUOTE (number, total, items) not generic samples","body":"New quote-followup branch in composeDeterministic for list2: cites the actual quote #(s), total, and quoted items with amounts, and asks to convert to an order. Sanitizes leaked dimension strings out of the project ref and strips empty \"(Ref.)\" from item names. Verified against a real $6,693.60 two-quote client.\n\nCo-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>"},{"hash":"f9b6323","date":"2026-07-14 11:18:05 -0700","author":"Steve","subject":"pitch: Sent log is now LIVE — merge Gmail Sent (recipient-enriched) with the tool log","body":"georgeSentLetters now fetches each sent letter full message for its `to` (Gmail search omits recipient), bounded to 60 newest, parallel. collectSent merges live+local, dedups by recipient+day, 60s cache. Sent log + per-card emailed chip now show through today (was frozen at the last tool-send). Verified newest = Jul 14.\n\nCo-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>"},{"hash":"6c560f3","date":"2026-07-14 11:15:53 -0700","author":"Steve","subject":"pitch: FIX critical George 401 — resolve GEORGE_AUTH from secrets master (restores sends + threading + last-corr)","body":"The app resolved an empty George password, so every George call 401d — sends, reply-threading, and \"last corresponded\" were silently failing (and pm2 --update-env wiped the process-env cred on restart). Now resolves the canonical GEORGE_AUTH (\"user:pass\") from secrets-manager/.env, surviving restarts. Verified: lastcorr returns a real thread.\n\nCo-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>"},{"hash":"c6d5609","date":"2026-07-14 11:12:10 -0700","author":"Steve","subject":"pitch: card/UX batch — dismiss-completed, park vendor memos, quote/order label fix, FM deep-links, live sent-index, expandable columns","body":"- Dismiss any card as ✓ Done: instant DOM removal + persisted, never re-shown (localStorage done: flag; filterSort/flatRows exclude)\n- Park Vendor Memos Overdue (list3): dropped from board + bucket via PARKED set (un-park = 1 line); grid tracks now match visible column count\n- Fix quote-vs-order contradiction: postFlag shows BOOKED orders only; drawer relabels \"Quoted since we sent samples\" when none booked\n- Per-invoice details: drawer splits Orders vs Quotes, each row shows item detail; goodLbl drops stripMfr punctuation-junk items\n- FileMaker deep-links: invoice# and client acct now Find the exact record (fmpInvoice/fmpClient script+param, USE_FM_SCRIPT on) — needs GotoInvoice/GotoClient scripts in FM\n- Real \"emailed on <date>\" chip from server /api/sent-index (cross-browser, 311 recipients from the send log)\n- /api/sent now merges the local log with a live Gmail Sent view (George), 60s cache, invalidated on send\n- Expand/shrink any board column (⤢ toggle, persisted)\n\nCo-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>"},{"hash":"d4a8d30","date":"2026-07-14 11:00:42 -0700","author":"Steve Abrams","subject":"auto-save: 2026-07-14T11:00:40 (1 files) — public/index.html","body":""},{"hash":"a59c63b","date":"2026-07-14 10:46:56 -0700","author":"Steve","subject":"pitch: redefine \"Invoiced · No Order\" list as real quotes never booked (totals + details)","body":"- buildList2 candidate pool flipped from sample-only (maxTotal<=$50) to real-dollar invoices\n- main() keeps only accounts with an open merch quote (quoted_total>0) and NO booked order (lifetime_sales==0), deriving quote_total / quote_count / quote_items\n- cards show \"$X quoted · no order\" chip + quoted item details; sorts now quote-based\n- drawer reads \"No orders booked · $X in open quotes\" for these never-booked prospects\n- list2: 57 accounts (e.g. $92,250 Briseyda Garcia quote, never ordered)\n\nCo-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>"},{"hash":"395d98e","date":"2026-07-13 12:25:24 -0700","author":"Steve Abrams","subject":"chore: lint (esc XSS autofix + enrich-disco psql guard), refactor, v0.4.0 (session close)","body":"Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>"},{"hash":"4c40379","date":"2026-07-13 12:24:58 -0700","author":"Steve Abrams","subject":"auto-save: 2026-07-13T12:24:56 (1 files) — public/index.html","body":""},{"hash":"0b34230","date":"2026-07-13 11:54:50 -0700","author":"Steve Abrams","subject":"auto-save: 2026-07-13T11:54:48 (2 files) — public/index.html scripts/enrich-disco.js","body":""},{"hash":"a22a58a","date":"2026-07-13 11:24:41 -0700","author":"Steve Abrams","subject":"auto-save: 2026-07-13T11:24:39 (2 files) — public/index.html scripts/build-lists.js","body":""},{"hash":"93dde31","date":"2026-07-12 23:46:24 -0700","author":"Steve Abrams","subject":"chore: v0.3.0 (session close — public tunnel + auth hardening: lockout, socket-guard, cred rotation, headers)","body":""},{"hash":"02b62f2","date":"2026-07-12 12:04:27 -0700","author":"Steve Abrams","subject":"yolo contrarian-gate fixes: socket-anchored /api/send guard (#1) + suppress audit log (#2)","body":""},{"hash":"211aef3","date":"2026-07-12 12:00:43 -0700","author":"Steve Abrams","subject":"yolo: baseline security headers (nosniff, frame-DENY, no-referrer) on public endpoint — closes contrarian 'no helmet' gap","body":""},{"hash":"09bfb88","date":"2026-07-12 11:35:06 -0700","author":"Steve Abrams","subject":"contrarian FIX FIRST #3: rotate public cred off shared DW2024! via app-local .env (envLocal reader)","body":""},{"hash":"577f14f","date":"2026-07-12 11:30:02 -0700","author":"Steve Abrams","subject":"contrarian FIX FIRST #2: per-IP brute-force lockout on public Basic Auth (10 fails/15m → 429)","body":""},{"hash":"cf2ad96","date":"2026-07-12 11:28:55 -0700","author":"Steve Abrams","subject":"contrarian FIX FIRST #1: block /api/send for tunnel-origin requests (loopback-only mail send)","body":""},{"hash":"10b262d","date":"2026-07-12 11:22:05 -0700","author":"Steve Abrams","subject":"5x: REPORT.md — 2 sweeps, app clean, M3 networkidle proven CDN artifact","body":""},{"hash":"d29d6ef","date":"2026-07-12 11:20:17 -0700","author":"Steve Abrams","subject":"5x sweep 1: add auth-exempt /favicon.ico 204 route (kills per-load console 404)","body":""},{"hash":"d691649","date":"2026-07-08 06:47:01 -0700","author":"Steve Abrams","subject":"chore: syntax-verified session changes, v0.1.1 → v0.2.0 (session close)","body":""},{"hash":"ee864df","date":"2026-07-08 06:35:12 -0700","author":"Steve Abrams","subject":"Schedule unsubscribe scan (launchd com.steve.dw-pitch-unsub-scan, 8am+8pm) — auto-honors client opt-outs with ?apply=1, logs every run; plist versioned in repo","body":""},{"hash":"e2ce4dc","date":"2026-07-08 06:34:28 -0700","author":"Steve Abrams","subject":"auto-save: 2026-07-08T06:34:26 (1 files) — scripts/scan-unsubscribes.sh","body":""},{"hash":"a9f7c6a","date":"2026-07-08 06:32:30 -0700","author":"Steve Abrams","subject":"CAN-SPAM: compliant footer (physical address 15442 Ventura Blvd #102 + UNSUBSCRIBE) on every send; suppression list honored at send (not overridable); tightened unsubscribe scanner (to:info@ + opt-out-at-start, dry-run by default, ?apply=1 to suppress)","body":""},{"hash":"cc4f468","date":"2026-07-08 06:27:09 -0700","author":"Steve Abrams","subject":"Stop leaking internal 'From job: dw-pitch-followup' banner onto client emails (no_source_tag:true); verified sent body is clean","body":""},{"hash":"ff19558","date":"2026-07-07 16:43:52 -0700","author":"Steve Abrams","subject":"yolo/contrarian fixes: build-time TODAY (fix aging bug) + 30-day double-email frequency guard at send (list1∩list2 dedup) with 'Send anyway' override","body":""},{"hash":"d191c5c","date":"2026-07-07 16:06:42 -0700","author":"Steve Abrams","subject":"Auto-swap to a thank-you letter when client has a BOOKED order since sampling (references the order + project); quote-since-sampling still gets the follow-up (chase the close)","body":""},{"hash":"4f8a57a","date":"2026-07-07 15:53:03 -0700","author":"Steve Abrams","subject":"Flag invoices placed SINCE samples sent (possible conversion): card banner + panel detail with #, total, sku/item, quote/booked, date","body":""},{"hash":"b6e5737","date":"2026-07-07 15:44:58 -0700","author":"Steve Abrams","subject":"Fix sticking Send button (reset #pSend/#pRegen/#pCopy on panel open); stamp emailed date/time in panel+card+sendstat; keep permanent sent-log.jsonl of every email + /api/sent + 'Sent log' viewer","body":""},{"hash":"a4a77d7","date":"2026-07-07 15:40:02 -0700","author":"Steve Abrams","subject":"Make panel 'Send now (info@)' prominent — filled gold, bold, right-aligned with divider (was subtle outline, easy to miss)","body":""},{"hash":"b2b886f","date":"2026-07-07 15:30:50 -0700","author":"Steve Abrams","subject":"Email: thumbnail + product-link gallery for recent samples (dw_unified sku→handle/image, dashed-sku match, ACTIVE-only links); panel shows thumbnails too; separate sample-reminder heading from list","body":""},{"hash":"eb905c7","date":"2026-07-07 15:20:07 -0700","author":"Steve Abrams","subject":"Letter greeting uses FileMaker contact first name (Find-a-client Name field); block letter + red 'Reorder Samples??' when samples ordered but 0 shipped (panel Generate disabled too)","body":""},{"hash":"2246a8e","date":"2026-07-07 15:09:22 -0700","author":"Steve Abrams","subject":"Rewrite client letter to spec: ~3 sentences, client-type aware (designer/architect trade line), final-purchase ask, witty stock/samples ask, project-details line, ends w/ question, rotating Steve/Samantha/Marcus sign-off, sample list w/ dates, url+phone (HTML href on send), PS; no new-year/no mfr#; fix name parsing (parentheticals/ALL-CAPS)","body":""},{"hash":"a350bea","date":"2026-07-07 14:58:22 -0700","author":"Steve Abrams","subject":"Right-panel detail view (context+last-corresponded on click+letter); quote/booked per invoice; lifetime sales (booked-only) on card+panel; current project from invoice sidemark; lifetime-sales sort","body":""},{"hash":"5305246","date":"2026-07-07 14:46:31 -0700","author":"Steve Abrams","subject":"Expand sort to all fields incl Invoice total (merch_total); add sort control to One-list view (defaults to invoice-total desc)","body":""},{"hash":"e3aa21c","date":"2026-07-07 14:42:25 -0700","author":"Steve Abrams","subject":"Revert invoice links to file-open (skip GotoInvoice deep-link for now)","body":""},{"hash":"43c0d5a","date":"2026-07-07 14:40:41 -0700","author":"Steve Abrams","subject":"Invoice links: enable fmp:// record-jump (USE_FM_SCRIPT=true → GotoInvoice script param)","body":""},{"hash":"56b2381","date":"2026-07-07 14:36:05 -0700","author":"Steve Abrams","subject":"Enrich cards with samples sent/pending + merchandise invoices (>$250, real orders) w/ fmp links + total; MERCH_MIN=250; sample SKUs shown as-is in admin details","body":""},{"hash":"28a382b","date":"2026-07-07 14:31:11 -0700","author":"Steve Abrams","subject":"auto-save: 2026-07-07T14:31:09 (2 files) — public/index.html scripts/build-lists.js","body":""},{"hash":"c660a28","date":"2026-07-07 14:24:21 -0700","author":"Steve Abrams","subject":"Compact cards + Columns/One-list layout toggle + ▸details expander (samples/merch/last-corresponded); lazy /api/lastcorr via George","body":""},{"hash":"306b791","date":"2026-07-07 14:16:17 -0700","author":"Steve Abrams","subject":"Add 'Send now (info@)' — emails generated letter via George info@ account with external-send approval token (Steve-authorized), confirm-guard + in-thread + persisted sent marker","body":""},{"hash":"182e2a6","date":"2026-07-07 14:01:02 -0700","author":"Steve Abrams","subject":"auto-save: 2026-07-07T14:01:00 (2 files) — public/index.html server.js","body":""},{"hash":"5d7759e","date":"2026-07-07 13:52:27 -0700","author":"Steve Abrams","subject":"Redesign viewer: 3-column board (all lists at once) + List/Grid toggle + density scale; per-column sort, cross-column select/export","body":""},{"hash":"8a5b1ac","date":"2026-07-07 13:35:12 -0700","author":"Steve Abrams","subject":"Frontend: build API URLs from location.host so fetches work even when page opened with inline credentials","body":""},{"hash":"0524e78","date":"2026-07-07 13:30:43 -0700","author":"Steve Abrams","subject":"Skip Basic Auth for direct-loopback access so the localhost viewer loads without inline creds (tunnel still gated)","body":""},{"hash":"2507156","date":"2026-07-07 09:30:33 -0700","author":"Steve","subject":"chore: version bump (session close — anti-hallucination guardrails)","body":""},{"hash":"9abe8a2","date":"2026-07-07 09:11:31 -0700","author":"Steve","subject":"pitch-followup: anti-hallucination guardrails + LLM reliability","body":"- Whitelist product names: the writer may ONLY name a pattern that's in the\n  client's actual sampled set (buildFacts now returns allowed[]); empty →\n  model names nothing, speaks generically. Reject-guard throws on any ™/®\n  or 'X Collection' name not on the whitelist → falls back to the accurate\n  deterministic draft. No invented collection names can reach a client draft.\n- Fix subject-greeting leak (subject is now a standalone line) + guarantee\n  the body opens with 'Hi <name>,'; strip stray ™/®.\n- Reliability: keep_alive 30m + widen Ollama timeout 30s→90s so drafts stop\n  cold-timing-out to the generic fallback. Verified across seeds."},{"hash":"9027026","date":"2026-07-03 08:07:58 -0700","author":"Steve","subject":"chore: macstudio3 migration — reconcile from mac2 + repoint paths (stevestudio2→macstudio3, node/npm/npx→/opt/homebrew)","body":""},{"hash":"d2a48d9","date":"2026-06-30 15:43:24 -0700","author":"Steve","subject":"ui: show last-sample-sent date as a chip on every card (uses existing when_iso; removed redundant .when line)","body":""},{"hash":"351e8f9","date":"2026-06-30 15:39:03 -0700","author":"Steve Abrams","subject":"Pitch follow-up: hide mfr# from UI + client letters (toggle), LLM-customized letters, Generate/Regenerate buttons","body":""},{"hash":"91a8b82","date":"2026-06-30 14:41:12 -0700","author":"Steve Abrams","subject":"auto-save: 2026-06-30T14:41:08 (1 files) — deploy/","body":""},{"hash":"a518ea6","date":"2026-06-30 13:35:57 -0700","author":"Steve Abrams","subject":"DW client/vendor follow-up pitch viewer: 3 read-only FileMaker lists + George-grounded letters + select/export (no sends)","body":""}]}