Shipped
Partial / unverified
Deferred (needs external setup)
Not needed (user decision)
🚀 SESSION 2026-04-28 — MAXXL FULLY LIVE ON PROD
11 ledger patches (068→078) · openclaw + Claude Sonnet collaboration
Two-LLM session: openclaw owned DEV patches 068-075, Claude Sonnet ran PROD MAXXL migration (076-078). Full backup before. Zero rebuilds.
✅ openclaw on DEV — 8 patches (068-075)
- 068 SERVER mp-routes.js — schedule btn now finds published contacts (status filter fix)
- 069 BUNDLE MAXXL_PROPER api path /api/mp → /skynet-api/api/mp
- 070 SERVER apollo-integration.js — added enrichPerson method (was crashing)
- 071 SOURCE skynet.tsx — 8 fifaSelected copy-paste bugs fixed in TS source (queued for next rebuild)
- 072 BUNDLE Skynet TM tab crash (fetchTmStats undefined) + tmTab refresh
- 073 BUNDLE Soccerdonna verify mapping (.name→.full_name) + polling array
- 074 SERVER Nightly + Soccerdonna import tolerance (drop bad ids → fallback to all approved)
- 075 SERVER Agent import tolerance (same pattern)
✅ Claude Sonnet on PROD — promote 068-075 (skip 071) + MAXXL migration 076-078
- 068-070+074-075 applied to PROD via openclaw with chattr unlock/relock — backend healthy after restart
- 072-073 applied to PROD CRM bundle
- 071 SKIPPED — needs rebuild, queued (would lose MAXXL IIFEs that aren't yet codified)
- 076 NOARG Replicated 15 mp_* tables DEV → PROD skynet.db (idempotent IF NOT EXISTS)
- 077 NOARG Copied 5 backend files (mp-routes, mp-helpers-v2, mp-crm-bridge, mp-send-worker, contact-identity), fixed DB path /skynet-dev → /skynet, mounted /api/mp + send-worker in PROD server.js
- 078 BUNDLE Extracted 90KB MAXXL IIFE block from DEV bundle (UI_V5 + PROPER + HIDE_CMP_BTN), appended to BOTH PROD bundle filenames (B5BSXjE7 + Dtu0_nu2), chattr +i relocked
✅ PROD verification (curl-based)
- https://crm.pros11.com/ → 200 ✓
- https://crm.pros11.com/skynet → 200 ✓
- https://crm.pros11.com/assets/index-Dtu0_nu2.js → 200, 2,557,979 bytes (was 2,467,221 + 90KB MAXXL) ✓
- https://crm.pros11.com/skynet-api/api/mp/campaigns → 200 [] (isolated PROD db) ✓
- https://crm.pros11.com/skynet-api/api/mp/sender-mailboxes → 200 ✓
- MAXXL strings verified in served bundle: __MAXXL_UI_V5__ + __MAXXL_PROPER_V1__ + __MAXXL_PROPER_API_PATH_V1__ + __MAXXL_HIDE_CMP_BTN__ + maxxl-skynet-page ✓
📦 Pre-flight backup
- /backups/FULL-CRM-SKYNET-20260428_145639/ — 3.9GB, 24 files (PG dumps both DBs, SQLite gz, all code dirs, ledger, configs, openclaw workspace)
- Rollback ready: extract Smrt-CRM-FINAL.tar.gz + restore skynet.db + pm2 restart
⚠️ Known caveats / what may still need fix
- PROD MAXXL UI = pure IIFE — neither DEV nor PROD skynet.tsx React source has MAXXL tab. The MAXXL_UI_V5 IIFE injects everything (tab button, body class, panel). Fragile if React re-renders nav too aggressively.
- Source backport queued — patches 071 (Skynet TS bugs) + MAXXL React component still pending in /Smrt-CRM-FINAL/client/src/pages/skynet.tsx. Next rebuild needs both BEFORE running, or features lost.
- PROD send-worker no SMTP creds — pm2 logs show "channels: resend=false brevo=false hostinger=false". Add RESEND_API_KEY/BREVO_API_KEY to PROD env (pm2 id 7 / skynet-backend) before users send real campaigns.
- 3 truly impossible bugs (minified vars: fifaSelected→nightlySelected/soccerdonnaSelected/agentSelected) → backend tolerance patches 074+075 mask them. Real fix = rebuild after 071 source.
- MAXXL bug 8 (no retry on AI draft) + bug 10 (template toggle DOM sync) still unfixed — embedded in template literals / dual React state. Defer to source backport.
- Roadmap HTTPS — DNS A record missing for roadmap.pros11.com → 187.77.74.76. HTTP-only nginx vhost in place.
- Mimo v2.5 fallback to gemini in OpenClaw — provider-catalog patch applied but still not selected. Needs deeper debug.
🧪 Manual browser verification needed
- Open https://crm.pros11.com/skynet in fresh browser (or Cmd+Shift+R hard refresh)
- Look for "⚡ MAXXL" tab in Skynet nav (10th tab) — IIFE adds it on load
- Click → should see empty campaigns list (PROD db isolated, no test data)
- Try create campaign → add contacts → verify dedup works
- If MAXXL tab missing: check browser console for IIFE errors
✅ SESSION 2026-04-25 SHIPPED — P0 #1–8 ALL DONE on DEV
15 ledger patches (052→066) + UI v6 + UI v7 Calendly
All P0 items from previous handoff shipped to DEV. Awaiting browser test + your call on prod backport.
✅ Patches landed (DEV only)
- 052 contact_identity table + indexes (Postgres)
- 053 backfill 120,060 identities from existing CRM contacts
- 054 + 055 dedup helper wired into MAXXL contacts-add route
- 056 MAXXL→CRM bridge: auto-create company + contact when MAXXL adds new lead
- 057 + 059 fetchLeadsForCampaign fallback fixed (returns real CRM contacts when Apify capped)
- 058 + 060 send worker (60s nodemailer cron, drains mp_campaign_messages)
- 061 contact edit/delete + company block (mp_blocked_companies table + 5 endpoints + auto-skip wireup)
- 062 UI v6: Company column + ✏️/✖/🚫 buttons + Blocked Companies floating panel
- 063 free DuckDuckGo search (web hints, no email — last-resort lead suggestor)
- 064 tone-matching backend (sender style ingest endpoints)
- 065 news/funding signals via Tavily — recentSignals[] in research dossier
- 066 reply→Calendly: booking_intent classifier + auto-reply cron + UI v7 settings button
- polish: send-worker mirror to email_communications, sparse research auto-fetch from PG, prompt() → modal block dialog, MANIFEST entries 052-066
🧪 Verified end-to-end
- 9 real CRM football contacts inserted in MAXXL campaign 4 ("My Test"), each w/ 11-section DeepSeek dossier
- Tavily signals returned 3 real Manchester United news bullets in test dossier
- Send worker test: msg sent live via Hostinger SMTP (cleaned up after)
- Bridge test: MAXXL contact auto-created CRM company "Bridge Test FC" + contact (cleaned up after)
⚠️ Open from this session
- Source backport — RULE #4: dist patches MUST land in /Smrt-CRM-FINAL/. 15 patches not yet backported. Separate session.
- PROD schema apply — contact_identity table only on smrt_crm_dev. PROD smrt_crm awaits user OK.
- is_dup policy — currently allows known CRM contacts into MAXXL. Tighten if double-outreach concern.
- Per-contact sender round-robin from mp_sender_mailboxes, not just SMTP_USER fallback.
- UI v6 polish — verify Company column appears + buttons fire after hard-refresh in browser.
📌 SESSION HANDOFF (pre-shipping) — original P0 list before this session shipped them
2026-04-25 · MAXXL post-compact handoff (now mostly outdated, kept as audit trail)
⚡ MAXXL = MoneyPrinter clone, lives in Skynet (port 3010 dev, 3006 prod). Branded MAXXL not MoneyPrinter (legal).
🌐 URLs / paths
- UI:
https://dev.pros11.com/skynet→ 10th tab "⚡ MAXXL" - Backend:
https://dev.pros11.com/skynet-api/api/mp/*(proxied tolocalhost:3010/api/mp/*) - Skynet routes file:
/skynet-dev/backend/mp-routes.js(~900 lines) - Helpers file:
/skynet-dev/backend/mp-helpers-v2.js - Mount in server.js: line ~3994 area, marker
/*__MP_ROUTES_MOUNT_V1__*/ - Frontend overlay: in
/Smrt-CRM-DEV/dist/public/assets/index-Dtu0_nu2.jsat end of bundle, marker/*__MAXXL_UI_V5__*/+ inline patches - SQLite tables: 12 tables prefixed
mp_*in/skynet-dev/backend/skynet.db - PROD: NOT YET PROMOTED. PROD bundle still chattr +i on its old state.
🔥 Sub-agent finding (CRITICAL — invalidates earlier assumptions)
- MoneyPrinter does NOT use Apollo/Apify. Real stack: OpenClaw + Bright Data residential proxies (live LinkedIn scraping). YC product previous name "ClawGTM" = "OpenClaw for Sales" — smoking gun.
github.com/brightdata/openclaw-pluginships 47 scrapers (LinkedIn/Crunchbase/X/Reddit). Privacy policy lists ONLY Google Workspace as third party — no PDL/Coresignal/Apollo disclosed. - MoneyPrinter LLM = Claude Sonnet (NOT DeepSeek). User dossier quality matches Claude prose. Founder's viral LinkedIn: "emails that don't sound like ChatGPT" = Claude tell. OpenClaw playbook recommends Claude 3.5 Sonnet. Currently MAXXL uses DeepSeek-v4-flash everywhere → quality gap explained.
- Apify cap on user account until 2026-05-09. Pipeline Labs scraper returns "Monthly usage hard limit exceeded". Fallback to existing CRM contacts wired but not firing because early-return path skips it.
📦 What's BUILT on DEV (verified working)
- SQLite schema: 12 mp_ tables (campaigns, sequences, campaign_contacts, contact_research, contact_variables, campaign_messages, sender_mailboxes, campaign_blacklist, campaign_folders, assistant_chats, campaign_analyses, linkedin_accounts)
- Backend endpoints:
/api/mp/{campaigns, sequences, contacts, /:id/sequences/draft, /:id/contacts/:cid/publish, /:id/publish-all, /:id/contacts/:cid/stop, /:id/schedule, /sequences/:id/edit-with-ai, /campaigns/:id/blacklist, /assistant/chat, /folders, /sender-mailboxes, /conversations, /scraper/by-company, /webhook-stats, /linkedin/connect, /auto-add/tick} - AI sequence drafter (DeepSeek v4-flash, var-aware: pulls extracted vars first, drafter only uses known keys)
- Per-contact research dossier v2: 11 sections matching MoneyPrinter exactly (contactInfo with roleDescription / summary / companyInfo with whyThisCompany / fundingInfo / researchFindings { contactProfile / relevanceToOurCompany / companyOverview / painPoints / ourCompanyValueProps / proofPointsToMention / personalizationAngles } / templateVariables)
- Custom variable extractor (in research output)
- Schedule with var resolution + auto signature append (pulls users.email_signature)
- Per-step inline manual edit + Edit-with-AI inline popover + Delete + delay number stepper + Regenerate-all
- Per-campaign Analyze + Per-campaign Blacklist UI (working — verified via curl)
- AI Assistant tool-using chat (CREATE_CAMPAIGN auto-navigates, LIST_CAMPAIGNS, GET_ANALYTICS) — branded MAXXL on DeepSeek (verified)
- Sub-page nav inside MAXXL: 🏠 Home · 📋 Campaigns · 📨 Inbox · 🎯 Find Leads (5 modes) · 📧 Senders (with Google OAuth button) · 💬 Assistant
- Inbox = unified replies port from main CRM email_replies
- Create Campaign modal — 2 paths (AI / Manual 4-step wizard) exact MoneyPrinter field mapping (Campaign / Targeting / Sequence / Launch)
- Per-contact lifecycle: pending → generating → draft → published → active → sent (statuses + colors)
- 📤 Publish per-contact + 🚀 Publish All Draft + ⏸ Stop button
- 🔄 Auto-Add Now manual trigger button
- Auto-add cron worker every 5min on active campaigns (LLM extracts Apollo filters from target_company_text + target_role_text)
- 🔬 Research drilldown UI = full slide-out panel matching MoneyPrinter (all 11 sections, color-coded headers)
- Horizontal Skynet tablist + bulletproof sibling-panel hide
- 5s blink poll = KILLED (was breaking textarea focus); replaced with 🔄 Refresh button
⚠️ KNOWN BROKEN / STUBBED (must fix next session)
- fetchLeadsForCampaign() Apollo path — works code-wise but Apify account capped till 2026-05-09. Switch to Bright Data + OpenClaw per sub-agent finding.
- Fallback to existing CRM contacts — code present but not executing on early-return path (no runId case). Bug fix: move fallback BEFORE return.
- No send worker. Messages sit in
mp_campaign_messageswith status=awaiting_review forever. Need worker that picks up published+pending → calls Brevo or Gmail OAuth → updates status=sent. Bridge to existing main-CRM email_queue path. - NO global dedup logic across CRM/Skynet/MAXXL. Currently 3 separate stores (Postgres contacts table, Skynet agent_contacts SQLite, MAXXL mp_campaign_contacts SQLite). Same person can be added 3× with different IDs. NEEDS NEW SHARED DEDUP TABLE + API.
- NO import-to-CRM flow. When MAXXL adds a contact (via auto-add or Find Leads), it goes into mp_campaign_contacts only. Should ALSO upsert to main CRM
contactstable (Postgres) so it appears in Skynet/CRM contact lists. Mirror the existing Skynet "import to CRM" button pattern. - Pending AI Reviews on Skynet Overview shows empty on DEV (works on PROD). Pre-existing UI query mismatch — agent_contacts has 6,426 pending data identical to PROD. Standalone fix ~20min.
- Campaign blacklist UI — backend/UI verified working via curl, but user reported it as "mock" — possibly tested before v5 patch took effect. Re-verify next session.
- Lead Scraper modes 4+5 = stub bridges only; need wire-up to Skynet Pipeline Labs CSV upload.
- Bright Data + OpenClaw integration = TBD whole new infrastructure; 1-2 days work.
- Tone-matching from sent emails not built (high-impact feature).
- News/funding signal layer (Crunchbase + news scrape per lead) not built.
- Multi-channel sequence orchestration with branching not built (LI accept → wait 2d → DM).
- Reply classifier → auto-Calendly not built (we have classifier from earlier UD work but not wired to MAXXL+Calendly).
- Daily performance brief email not built.
- Email signature auto-append shipped backend, not yet visually tested in delivered email.
✅ USER DECISIONS LOCKED 2026-04-25 (pre-compact)
- LLM: KEEP DeepSeek v4-flash for now. Skip Claude Sonnet swap.
- Scraper: Try FREE fallbacks first (raw Playwright on our server, free Phantombuster trial, Apify alt actors). Bright Data = roadmap option, not immediate spend.
- Build order next session: #1 Global dedup → #2 Import-to-CRM → #5 Send worker (F) → #6 Tone-matching from sent emails (B) → #7 News/funding signals (C) → #4 Free scraper fallback → #9 Multi-step LI+Email branching (D — depends on LinkedIn channel) → #8 Reply classifier → auto-Calendly (E).
- Skip for now: #3 Anthropic Claude swap (deepseek first), #10 LinkedIn channel until basics done, #11 Daily brief, #12 Pending AI Reviews fix.
- Reply classifier → auto-Calendly explained: sentiment classifier (already shipped UD-P3) + NEW booking-intent classifier → if positive + intent → auto-reply with Calendly link + read user's Google Calendar availability → on prospect pick → write meeting back via Google Calendar API + Slack/email notify.
🎯 P0 NEXT-SESSION PRIORITIES (in suggested order)
- GLOBAL DEDUP system (user explicit ask 2026-04-25). New shared table
contact_identityin Postgres with: email_lower (unique), linkedin_url_normalized (unique), full_name_lower + company_name_lower composite. CRM, Skynet, MAXXL ALL must check this before insert. Add APIPOST /api/contact-identity/upsert+GET /api/contact-identity/lookup?email=&linkedin=. Migration: backfill from existing Postgres contacts + Skynet agent_contacts. - Import-to-CRM flow from MAXXL (user explicit ask). When MAXXL adds contact via auto-add or Find Leads → mirror to Postgres
contacts+companiestables viaPOST /api/v1/contacts(already exposed). Use shared dedup from #1. - Anthropic Claude Sonnet wiring (sub-agent finding: MoneyPrinter = Claude not DeepSeek). User said previously Anthropic key wired in env but couldn't find it — check
/Smrt-CRM-FINAL/.envand gmail-poller env first; if missing, ask user for ANTHROPIC_API_KEY. Then add provider switching in mp-helpers:callLLM(prompt, opts)routes by envAI_PROVIDER=claude|deepseek|mimo. Use Claude for: research dossier, sequence drafter, campaign analyze, AI assistant chat. Keep DeepSeek for: cheap classification, edit-with-AI quick rewrites. - Bright Data + OpenClaw scraper as Apify replacement. Sign up Bright Data (~$50/mo Web Unlocker tier). Install
github.com/brightdata/openclaw-plugin. Replace fetchLeadsForCampaign() path: Bright Data → LinkedIn people search → email-find via secondary call. Handle rate limits properly. Until then: fix the existing CRM-contacts fallback. - Send worker. Cron in Skynet every 60s: SELECT from mp_campaign_messages WHERE status='pending' AND scheduled_at < NOW() LIMIT 10 → call Gmail OAuth send (preferred — already wired in main CRM gmail-poller path) OR Brevo → update status=sent + log to main CRM email_communications. THIS unblocks actual outreach.
- Tone-matching from sent emails. Per-user: ingest last N sent emails from Gmail (already have OAuth) → embed style → condition every drafter prompt with style profile. Endpoint
POST /api/mp/users/:uid/learn-tone. - Hiring/news/funding signal layer. Per-lead at research time: scrape company's recent job postings (LinkedIn jobs / Greenhouse / Lever public) + Crunchbase news → include in dossier "Recent Signals" section + use in personalization vars.
- Reply classifier → auto-Calendly. We already have sentiment classifier (shipped UD-P3). Add: positive sentiment + has booking-intent → auto-reply with Calendly link + write meeting back to user calendar via Google Calendar API.
- Multi-step LI+Email orchestration with branching (depends on LinkedIn channel #10).
- LinkedIn channel — Playwright per-user, burner-first, free LI account, hard cap 8/day. 10-14 days. See dedicated LinkedIn section below.
- Daily performance brief email at 9PM (S effort).
- Pending AI Reviews UI fix on Skynet Overview (~20min).
🔑 ENV / CREDENTIALS STATE
DEEPSEEK_API_KEY— set in/Smrt-CRM-FINAL/.env+/skynet-dev/backend/.env✅MIMO_API_KEY— set ✅ANTHROPIC_API_KEY— NOT FOUND in env files. User MEMORY says wired. Need to locate or get from user.APIFY_API_KEY=apify_api_3O5BEtvS4ySb7J5qceoWbpEmO279bE4wr5hf— capped till 2026-05-09GMAIL_CLIENT_ID+GMAIL_CLIENT_SECRET+GMAIL_REDIRECT_URI— set on smrt-crm-dev ✅ (added 2026-04-25)- Need to acquire: Bright Data API token (~$50/mo signup) for OpenClaw scraping
- LinkedIn: nothing (will use FREE account + browser cookie capture, no Premium)
📋 MAXXL FILE INDEX
- /skynet-dev/backend/mp-routes.js — all backend endpoints
- /skynet-dev/backend/mp-helpers-v2.js — drafter v2, sig fetch
- /skynet-dev/backend/skynet.db — 12 mp_ tables
- /skynet-dev/backend/server.js — line ~3994: mounts /api/mp
- /Smrt-CRM-DEV/dist/public/assets/index-Dtu0_nu2.js — UI overlay v5
- /Smrt-CRM-FINAL/.env — main CRM secrets
- /skynet-dev/backend/.env — Skynet DEV secrets (DeepSeek + Mimo)
- /Smrt-CRM-DEV/.env — DEV CRM secrets
- /root/crm-patches/ — all ledger patch scripts
⚙️ HOW TO TEST MAXXL ON DEV (quick start)
https://dev.pros11.com/skynet→ click "⚡ MAXXL" tab (10th)- 📋 Campaigns → "+ New Campaign" → 2-path modal opens (AI / Manual)
- Click Manual → 4-step wizard fills all MoneyPrinter fields (name/instructions/Email/LI toggles/seq counts/LI note → Targeting w/ AI generate buttons → Sequence preview AUTO-DRAFTED with vars → Launch toggles)
- Existing test campaign: id=4 "My Test" status=active. Click 🔄 Auto-Add Now → currently returns 0 contacts (Apify cap; need #1+#2+#4 P0 fixes)
- OR add test contact via API:
curl POST /api/mp/contacts/test-id/researchwith body fields - Click 🔬 on any contact → full research drilldown panel slides in
- Per-step ✨ Edit with AI → inline popover (no more prompt())
✅ Session 2026-04-23 summary (patches 115→232)
- Reply tracking via Brevo Inbound Parsing — MX records reply.pros11.com → Brevo · Reply-To injection (replies+queue_id@reply.pros11.com) · Patches 203A/B/C · first reply ingested OK
- Pipeline Labs / Skynet sacred edits — PUT accepts email/phone/linkedin/first_name/last_name (208) · Apollo + Soccerdonna refresh now NULLIF-guarded (214) · Pending AI Reviews refreshes pred from current source row at approve-time (215)
- Permission audit + alignment — 105 UI perms vs ~25 enforced before. After: 19 backend orphan gates wired (228) · Skynet review action-specific perms (230) · admin bypass on requirePermission (229) · 4 perm layers aligned (223) · live propagation (224 ≤30s)
- Smrt Hub visibility — Documents+categories now visible to all roles (225/226/227 + DB backfill) · admin bypass on canUserAccess
- Permission UX — Promote-to-Review bar gated (216) · Bug Reports nav+route gated · single Access Denied UI (no double 404) (220/221) · analytics own-scope server-enforced (218/219)
- apiRequest .ok bug — fixed 3 sites (notification prefs, email-demo status, delete deal) where backend saved but UI always errored
🔬 MoneyPrinter — 100% AUDIT (every modal + flow + drilldown captured)
2026-04-24 · authenticated walkthrough · all features documented
12 deep-dive items captured:
- Per-step Edit pencil = "Edit with AI" popover (textarea: "What changes would you like to make?" + Generate button + voice mic). AI rewrites that step from prompt.
- Per-step delay edit = inline number stepper (−/+ days, ✕/✓). 0 = Immediately.
- Email signature auto-appended to every generated email (full Smrt Stats signature with phone/disclaimer/legal footer).
- Campaign Analyze modal = AI reviews entire campaign + suggests changes. Tabs: Current Analysis / History. Footer: "Apply N Changes" review-and-approve.
- Campaign Insights = Outreach Overview slide-up — per-campaign chart of initial vs follow-ups by sender, channel filter (Email/LinkedIn), 7d/30d range.
- Research Report (per-contact) = AI-generated dossier with: Contact Info (Name/Role/Headline/Location/Connections/LinkedIn/Why-this-person) + Summary (long-form 1st-person career narrative) + Company Info (Name/Website/Founded/Employees/Industries/About/Key Roles) + Funding (Total Investment) + Research Findings (Why-Tomasz-and-PZPN-Are-a-Good-Fit-for-Smrt-Stats — AI's reasoning + pain points listed).
- Add Contacts modal = picker from existing CRM contacts. Tags filter + search + per-row Add/Added.
- 3-dot campaign menu: Duplicate Campaign / Disable Campaign (red).
- 3-dot per-contact menu (in campaign): Stop Sequence / Delete Contact / Delete and Blacklist Company.
- AI Assistant conversational campaign builder — caught full chat: user asks "3 high-ROI ideas", AI proposes (pro-club priced-out-of-Wyscout + agencies + federations), iterates ("worldwide football federations except Swiss/German"), refines ("direct federation use vs federations enabling clubs"), creates the actual campaign as proposal user can approve.
- Conversations filters: Channel / Date / Response Type. Inbox / All tabs.
- Lead Scraper mode breakdown: Mode 1 (find people at company) = standalone modal w/ Company Name + Website OR LinkedIn URL + Role description + N contacts (1-20) + 0.1 cred/contact. Mode 2 (ICP) + Mode 3 (jobs) = redirect to Create Campaign (filters live in campaign editor's Targeting). Mode 4 (people CSV upload) = Enable Contact Enrichment toggle + minimal CSV (full_name + email/linkedin_url/website) + AI auto-enriches missing + optional NL filter ("Only extract executive founders") + 0.1 cred/lead. Mode 5 (company CSV) = name + linkedin_url/website, max 500/upload, optional NL filter ("Don't extract from recruiting firms").
- Advanced Campaign Settings = per-campaign blacklisted companies (vs global blacklist in Org settings).
- Sender selector dropdown = mailbox rotation per contact (fernandez@smrtstats vs ivan@smrtstats).
- Contact GENERATING state (yellow badge) — AI personalization happens async per contact after they're added to campaign.
- Notifications bell: empty popover ("You're all caught up!").
⚡ MAXXL — MoneyPrinter clone LIVE on Skynet DEV (10th tab)
2026-04-25 v5 shipped · LinkedIn channel = next session
Where:
Architecture: All MAXXL features in Skynet (intelligence layer). Reads org_knowledge from main CRM postgres directly. CRM = system of record.
Built (v5):
⚠️ Known issues:
Data sources for MoneyPrinter: Bundle minified, vendor names stripped. Industry knowledge: most likely Apollo.io (300M+ profile DB, $0.04-0.10/contact), People Data Labs (enrichment), Hunter.io/Snov.io (email verification), Apify Sales Navigator scraper (LinkedIn profile data). SMRT CRM already has all wired in Skynet — MAXXL Lead Scraper bridges to existing Pipeline Labs flow.
dev.pros11.com/skynet → click "⚡ MAXXL" tab. Backend: Skynet DEV port 3010 at /api/mp/*. Frontend: IIFE in main CRM bundle, scoped to body.maxxl-skynet-page.Architecture: All MAXXL features in Skynet (intelligence layer). Reads org_knowledge from main CRM postgres directly. CRM = system of record.
Built (v5):
- ✅ Schema (12 mp_ tables in skynet.db)
- ✅ AI sequence drafter (DeepSeek, var-aware: pulls extracted vars first, drafter only uses known keys)
- ✅ Per-contact research dossier (Smrt-Stats-aware via org_knowledge)
- ✅ Variable extractor + handshake fix
- ✅ Schedule with var resolution + auto signature append (users.email_signature)
- ✅ Per-step manual edit (textarea auto-save) + Edit-with-AI inline popover (no more prompt) + Delete + delay editor
- ✅ Per-campaign Analyze (AI score+issues+suggestions) + Per-campaign Blacklist UI
- ✅ AI Assistant tool-using chat (CREATE_CAMPAIGN, LIST, ANALYTICS)
- ✅ Sub-pages: 🏠 Home / 📋 Campaigns / 📨 Inbox / 🎯 Find Leads / 📧 Senders / 💬 Assistant
- ✅ Create Campaign modal — 2 paths (AI chat / Manual 4-step wizard exactly matching MoneyPrinter fields)
- ✅ Senders w/ Google OAuth button (reuses gmail-poller /api/auth/gmail/start)
- ✅ Inbox = unified replies (cross-campaign from main CRM email_replies)
- ✅ Find Leads 5-mode UI
- ✅ GENERATING worker (research-async flips status pending→generating→active)
- ✅ Horizontal Skynet tablist (no vertical wrap) + bulletproof sibling-panel hide
- ✅ Conversations port + Lead Scraper bridge endpoints + folders/senders/blacklist/analyses backend
⚠️ Known issues:
- Pending AI Reviews shows "0" on DEV (UI component query mismatch — DEV agent_contacts has 6,426 pending, identical to PROD; pre-existing bug, not MAXXL)
- Lead Scraper modes 4+5 (CSV uploads) = stub bridges, need wire-up to existing Skynet Pipeline Labs flow
- Lead Scraper mode 1 calls /api/skynet/scrape-company which doesn't exist yet — need Skynet route wrapping Apify Pipeline Labs Apollo actor
Data sources for MoneyPrinter: Bundle minified, vendor names stripped. Industry knowledge: most likely Apollo.io (300M+ profile DB, $0.04-0.10/contact), People Data Labs (enrichment), Hunter.io/Snov.io (email verification), Apify Sales Navigator scraper (LinkedIn profile data). SMRT CRM already has all wired in Skynet — MAXXL Lead Scraper bridges to existing Pipeline Labs flow.
🔗 LinkedIn channel — deferred to dedicated session (~10-14 days)
FREE LI account · burner-first · no SaaS purchase
Why deferred: single-session can't safely deliver multi-day project. Wrong shortcut = ban Ivan's professional LinkedIn account. Backend table mp_linkedin_accounts ready + endpoint stubs at /api/mp/linkedin/* placed.
Why build vs buy: Heyreach/Expandi/Dripify all use SAME browser-automation approach (no official LinkedIn API for messaging). Buying = same ban risk + $39-300/mo. Building = full control + free + integrates directly with MAXXL DB.
Plan (next session, 10-14 days):
Open source / cheap alternatives (rejected):
Why build vs buy: Heyreach/Expandi/Dripify all use SAME browser-automation approach (no official LinkedIn API for messaging). Buying = same ban risk + $39-300/mo. Building = full control + free + integrates directly with MAXXL DB.
Plan (next session, 10-14 days):
- Install Playwright + Chromium on srv1373075 (~500MB, headless via Xvfb)
- Cookie capture: user pastes
li_atfrom LI DevTools → encrypted in mp_linkedin_accounts.session_cookie_encrypted - Use FREE LI account (no $99/mo Premium needed — 12 connections/day cap is normal)
- Burner-first: fresh @gmail account → 2 weeks testing → if survives, onboard real account
- Worker microservice (Node + Playwright, separate PM2 process)
- Hard cap 8/day connections (less than LI's 12 soft cap = safety margin)
- Anti-detection: random 15-90s delays, mouse jitter, viewport randomization
- Connection-state poller (/mynetwork/invitation-manager) every 30min
- Inbox poller (/messaging/) every 15min → ingest to email_replies (source='linkedin')
- Kill switch: detect "Are you a robot?" → freeze + alert
- Multi-account team: each member adds own LI → combined 8/day × N capacity
- 2 full weeks burner testing before promoting to real accounts
Open source / cheap alternatives (rejected):
- Linked Helper $15/mo desktop — runs on user's PC, lower ban risk, but can't integrate with MAXXL DB
- Apify linkedin-sales-navigator-scraper $0.10/profile — read-only LEAD SOURCING, no messaging
- tomquirk/linkedin-api Python free — unofficial private endpoints, breaks frequently, highest ban risk, NOT recommended
- Dripify $39-89/mo — same automation, same risk, costs money
🖨️ MoneyPrinter (crm.hireroger.com) deep dive — original audit reference
audit 2026-04-24 · €250/mo Startup plan · YC S24 · Next.js+Vercel+Express+Azure
What it is: AI SDR / outbound sales engine. Generates per-lead personalized email + LinkedIn + cold-call sequences. Human-review-and-approve gate by default. Built on top of: lead scraper (300M+ profiles claimed), Google Workspace mailbox marketplace ($3/mo + $1/mo warming), LinkedIn account linking (12 req/day per user), parallel dialer with AI live teleprompter, conversational AI assistant.
User loves: per-contact AI personalization with custom variables (
Pricing tiers: Startup $250 (375 leads/mo, 1125 sequences) → Basic $800 (1200 / 3600 + AI research per lead) → Standard $2000 (3750 / 11250 + auto enrich + 3 competitors) → Premium $3000 (6000 / 18000 + 5 competitors + Slack support) → Enterprise (custom).
Stack: frontend Next.js App Router on Vercel · backend Express on Azure App Service · Segment analytics · custom auth (Bearer rgr_live_*) · public API
User loves: per-contact AI personalization with custom variables (
{{Federation Name}}, {{regionorspecificcompetition}}) extracted from research; Email + LinkedIn coordinated cadence; Edit-with-AI / Regenerate buttons on every sequence; AI chat side panel inside campaign editor; org-level "Detailed Analysis" knowledge base feeds personalization; intent signals (job postings, competitor followers).Pricing tiers: Startup $250 (375 leads/mo, 1125 sequences) → Basic $800 (1200 / 3600 + AI research per lead) → Standard $2000 (3750 / 11250 + auto enrich + 3 competitors) → Premium $3000 (6000 / 18000 + 5 competitors + Slack support) → Enterprise (custom).
Stack: frontend Next.js App Router on Vercel · backend Express on Azure App Service · Segment analytics · custom auth (Bearer rgr_live_*) · public API
POST /apis/v1/campaigns for contact ingest only · HubSpot integration native.
MP-1FEAT
Analytics dashboard — single page
Top KPIs: Total Contacts, Total Companies, Active Campaigns, Total Replies, Total Sent. Time-range selector (Last 30 days etc.). 4 charts per sender: Initial Emails / Email Follow-ups / LinkedIn Sent / LinkedIn Follow-ups. Per-sender colored series.
/analytics
MP-2FEAT
Campaigns list — folders + multi-channel stats
Folder-grouped campaign table: name, contacts (X/Y reached), sequences (Gmail count + LinkedIn count), Total Response, Link Clicks, Created At. Top stats: Total Reached / Total Replies + per-channel breakdown (Gmail Reached/Link Clicks/Replied + LinkedIn Reached/Connection Accepted/Replied). Approval-pending banner. Show disabled toggle. Search.
/campaigns
MP-3FEAT
Campaign editor wizard — 4 steps
Step 1 Campaign: Name + free-text Instructions (campaign goal in plain English) + Email/LinkedIn channel toggles with per-mailbox rate limits (25/day, 12 req/day).
Step 2 Targeting: Auto-add-leads toggle + Leads-per-day slider (50) + People-per-company input + Target Company text (with "Re-validate" + "Generate With AI" buttons) + Target Role text (with "Generate With AI") + Job Posting Signal toggle + Competitor Followers Signal toggle + Advanced settings.
Step 3 Sequence: Email + LinkedIn channel tabs with separate sequences. Subject template with variables. Multi-step ladder (Initial / Follow-up 1 / 2 / 3 / 4 / 5 with custom delays per step e.g. 3d/5d/30d/60d/120d). Each step: AI-personalized body with
Step 4 Launch: Per-channel include toggles + Reply notifications recipient picker + "Skip Manual Review" toggle (auto-send vs human-approve gate).
Step 2 Targeting: Auto-add-leads toggle + Leads-per-day slider (50) + People-per-company input + Target Company text (with "Re-validate" + "Generate With AI" buttons) + Target Role text (with "Generate With AI") + Job Posting Signal toggle + Competitor Followers Signal toggle + Advanced settings.
Step 3 Sequence: Email + LinkedIn channel tabs with separate sequences. Subject template with variables. Multi-step ladder (Initial / Follow-up 1 / 2 / 3 / 4 / 5 with custom delays per step e.g. 3d/5d/30d/60d/120d). Each step: AI-personalized body with
{{first_name}} {{Federation Name}} {{Sender first name}} {{regionorspecificcompetition}} custom-extracted variables. Edit-pencil per step. Top-right: Regenerate + "Edit with AI" buttons (re-draft entire sequence with AI prompt).Step 4 Launch: Per-channel include toggles + Reply notifications recipient picker + "Skip Manual Review" toggle (auto-send vs human-approve gate).
/campaigns/:id/edit
MP-4FEAT
Campaign run view — per-contact preview
Two-pane: LEFT 70-contact list (avatar, name, channel icons, ACTIVE state, date, 3-dot menu) with search + filter + bulk-select + pagination. RIGHT selected-contact panel: profile + role + per-contact "Research Report" (View research →) + sender selector dropdown (rotation per contact) + Email/LinkedIn channel sub-tabs + full per-contact rendered sequence (subject + body with variables resolved) + per-step status badges (SCHEDULED/PENDING/SENT). "Next run in 6h 27m" timer. Find More Leads / Add Contacts / Edit Campaign / Analyze / Insights buttons.
/campaigns/:campaignId/:runId
MP-5FEAT
AI personalization engine — variables auto-extracted
Variables aren't a hardcoded merge-tag list. AI extracts CUSTOM variables per campaign based on research about each lead's company. Examples seen:
{{Federation Name}} (federation rather than just company), {{regionorspecificcompetition}} (extracted from country + most-relevant league for that lead). Each follow-up uses different angle (coverage / customization / social proof / federation-specific) — AI assembles per-step value props from the org "Detailed Analysis" + Business Features list.MP-6FEAT
Conversations — unified inbox (email + LinkedIn replies)
Inbox / All tabs. Search bar + filter. Paginated reply list. Right pane: select to view thread. Empty state when no replies.
/conversations
MP-7FEAT
Parallel Dialer — third channel (phone)
"Powered by MoneyPrinter AI". Select campaign → start dialing. (1) Parallel dialing — dials multiple at once, only connects when picked up. (2) Screening filter — recorded message plays first, only connects when real person responds. (3) AI Teleprompter — live AI-suggested script per prospect when they pick up.
/dialer
MP-8FEAT
Domains & Mailboxes — full deliverability stack
Three onboarding paths: (A) Purchase managed Google Workspace mailboxes ($3/mo + $1/mo warming, automatic premium warmup, fully managed) ←recommended. (B) Prewarmed Domains marketplace (Roger-owned domains, ready immediately). (C) Connect existing Gmail (OAuth, no domain config). Tabs: Domains / GSuite Mailboxes for management.
/settings/domains
MP-9FEAT
LinkedIn — per-user account linking + team capacity
Per-user LinkedIn account connect (OAuth or session). Daily limit cap 12 requests/user (warning above = ban risk). "Organization access" toggle to share LI as sender across team. Invite Team Members CTA — more LI accounts = higher daily team capacity. LinkedIn Users table: Name / LI Username / Connected status / Org Consent.
/settings/linkedin
MP-10FEAT
Lead Scraper — 5 ingest modes
Auto-find: (1) by company URL (semantic search inside that company); (2) by ICP (industry + size + location + title + seniority); (3) by recently-posted-jobs (intent signal — "We use Salesforce" type cues). Upload: (4) people CSV; (5) company CSV + role → AI finds matching people. Recent Scrapes history table.
/lead-scraper
MP-11FEAT
Contacts CRM — list + side panel
Paginated list (100/page, 268 here). Columns: name+role+company / email + LinkedIn handle / Campaigns assigned / Created At / Actions. Filter, Search by name/email/company, New Contact, Import CSV, Export CSV. Click row → right slide-out panel with all contact + company fields. Bulk actions when selected: Delete / Add to campaign / Add tag.
/contacts
MP-12FEAT
Org knowledge base — feeds AI personalization
Organization Settings = the AI brain:
- Org Name + Website (URL → Sync button auto-pulls)
- Short Company Description
- Detailed Analysis (long-form free text — "everything MoneyPrinter knows about your organization") — used for sequence drafting
- Business Features (multi-card list — each becomes a value-prop angle for follow-ups)
- Social Proof (multi-card list — testimonials + partnerships)
- Blacklisted Companies (global do-not-contact)
/settings/organization · /settings/profile
MP-13FEAT
AI Assistant — global conversational chat
"Hey Ivan! What can I help with?" landing. Suggested chips: targeting prompts ("Target Head of Scouting at European football clubs..."), analytics queries ("Who replied to my last campaign?", "Which campaign has best reply rate?"). Past Chats history list. Voice input (mic icon). Same brain as the per-campaign side panel — natural language to create campaigns, modify targeting, query data.
/ai
MP-14FEAT
Public API + HubSpot integration
POST https://api.hireroger.com/apis/v1/campaigns with Bearer rgr_live_* token. Body: {fullName, email, linkedin, website, campaigns:[ids]}. Contact ingest only — no read/update/delete. HubSpot native integration: Connect button → bidirectional contact sync + outreach activity tracking./settings/apis · /settings/hubspot
🚀 SMRT-CRM clone roadmap — 14 features mapped to ledger patches
2026-04-24 plan · effort + dependencies + already-have notes
Verdict: SMRT CRM already has ~50% of the core data model (contacts, deals, email_communications, email_replies, brevo_events, gmail_oauth_tokens, campaigns, pipeline_rules, sentiment classifier just shipped). The two BIG gaps are: (A) LinkedIn outreach channel and (B) AI sequence personalization with custom-extracted variables. Phone dialer (MP-7) and managed-mailbox marketplace (MP-8) are out of scope (heavy infra). HubSpot integration (MP-14b) is low-priority since SMRT IS the CRM.
Effort scale: S = 1-2 days · M = 3-7 days · L = 1-3 weeks · XL = 1-2 months · XXL = quarter+. All built ledger-patch style on DEV first.
Effort scale: S = 1-2 days · M = 3-7 days · L = 1-3 weeks · XL = 1-2 months · XXL = quarter+. All built ledger-patch style on DEV first.
MPC-1HAVE
Analytics dashboard (MP-1)
Already shipped. /unified-dashboard (now Email Dashboard) Overview tab covers most KPIs. Add per-sender chart series + time-range selector if not present.
SMRT-Next UD-P1..P7 (shipped 2026-04-24)
MPC-2M
Campaigns list with folders + multi-channel stats (MP-2)
Existing
/api/campaigns returns flat list. Need: folders table (campaign_folders) + folder_id FK on campaigns + UI grouping. Per-channel stats already in email_communications + (future) linkedin_messages. Effort: M. Backend: 1 migration + 2 endpoints. Frontend: extend existing tab. No new infra.MPC-3L
Campaign editor wizard — 4 steps (MP-3)
Brand-new flow. 4-step wizard React component (campaign / targeting / sequence / launch) with sticky stepper top + side AI chat panel. Backend: extend campaigns table with instructions, target_company_text, target_role_text, leads_per_day, people_per_company, signals (jsonb), skip_manual_review fields. Effort: L (1-3 weeks) — UI alone is ~1 week.
MPC-4L
Campaign run view — per-contact preview pane (MP-4)
Two-pane layout, contact list + per-contact rendered sequence preview. SMRT already has Pipeline Labs / Skynet flows that show similar lists. Reuse pattern. Effort: L. Backend: per-contact rendered sequence endpoint (
GET /api/campaigns/:id/contacts/:contactId/sequence returns rendered subject + body per step). Frontend: new tabbed component.MPC-5XL
AI personalization with custom-extracted variables (MP-5) — 🔑 BIG ONE
The actual magic. Pipeline:
- For each new contact added to campaign, run research pass (LLM with web/LinkedIn data → JSON of contact + company facts).
- From research output, AI extracts custom variables based on campaign goal (e.g. for federation campaign: "FederationName", "regionOrSpecificCompetition"). Save as
contact_campaign_variablesjsonb. - For each sequence step, prompt LLM with: org Detailed Analysis + Business Features + step number + previous-step-context + extracted variables → returns final subject + body with vars resolved.
- Render sequence into
email_queue+linkedin_message_queuewith scheduled-at timestamps respecting delays.
MPC-6HAVE
Conversations / unified inbox (MP-6)
Already shipped today. /unified-dashboard → Reply Tracking + Email Log tabs (UD-P4). For LinkedIn replies need MPC-9 first.
SMRT-Next UD-P4 (shipped 2026-04-24)
MPC-7SKIP
Parallel Dialer (MP-7) — out of scope
Cold-calling not in SMRT positioning (SDR-replacement = email-first). Twilio Programmable Voice + parallel dialer logic + AI teleprompter integration = quarter+ of work for a feature SMRT doesn't need. Recommend skip.
MPC-8SKIP
Managed mailbox marketplace (MP-8) — partial / skip
SMRT is internal CRM, not multi-tenant SaaS. Buying Google Workspace mailboxes for clients on SMRT's behalf = not the business model. Keep: existing Brevo + Gmail OAuth. Add: warmup status indicator per connected mailbox (S, ~1 day) — useful even without marketplace. Skip the storefront.
MPC-9XL
LinkedIn outreach channel (MP-9) — 🔑 BIG ONE
The other big gap. Two paths:
- Path A — official LinkedIn API: requires partner approval (extremely hard for SaaS). Realistically blocked.
- Path B — browser automation per user: each team member runs a headless Chromium with their LI session cookie. Send connection requests + DMs via DOM automation. Daily caps (12/day) to avoid bans. This is what MoneyPrinter and most peers do.
linkedin_accounts table (user_id, session_cookie_encrypted, daily_limit, last_used_at) + linkedin_message_queue + worker microservice (Playwright on the server, runs per-account at controlled cadence). Effort: XL (1-2 months). Risk: LI may detect + block. Pre-req for MPC-3 LinkedIn channel + MPC-6 LinkedIn replies.
MPC-10L
Lead Scraper — 5 ingest modes (MP-10)
SMRT already has Skynet (ingest from Apify Apollo, Soccerdonna, Hunter, Snov). Map MoneyPrinter modes to SMRT:
- (1) by company URL → reuse Skynet "scrape company" Apify flow
- (2) by ICP → wrap Apollo search API with ICP filter form
- (3) by job postings → integrate JobsAPI / RemoteOK / direct LinkedIn jobs scraper (NEW — M effort)
- (4) people CSV upload → already exists in Skynet
- (5) company CSV → AI find people → combine (1)+(4) (M)
MPC-11HAVE
Contacts CRM (MP-11)
Already shipped. /contacts page mature with import/export/filters/bulk actions/per-contact modal. Maybe add tags + side-panel polish.
MPC-12M
Org knowledge base for AI (MP-12) — pre-req for MPC-5
New table
org_knowledge with fields: detailed_analysis (text), business_features (jsonb array), social_proof (jsonb array), blacklisted_companies (jsonb array). Plus users.custom_ai_instruction (text per user). UI in /settings/organization. Effort: M. Pre-req for MPC-5 (AI personalization).
MPC-13M
AI Assistant chat — global + per-context (MP-13)
Conversational AI panel. Two surfaces: global at /ai-assistant + side-panel inside campaign editor. Tools available to LLM: read campaign data, modify campaign fields, query email_log, query analytics, create campaign from prompt. Effort: M. Reuse existing DeepSeek/Sonnet wiring. Tool-use pattern.
MPC-14S
Public API + HubSpot integration (MP-14)
Public API for contact ingest: SMRT already has internal endpoints. Just expose
POST /api/v1/contacts with Bearer token + rate limit (S, 1-2 days). HubSpot integration: skip for now — SMRT IS the CRM, not a sidecar. Revisit if user wants to keep dual systems.MPC-15M
Edit-with-AI / Regenerate buttons (MP-3.5)
Standalone polish: add "Edit with AI" + "Regenerate" buttons to ANY draft email or sequence step in SMRT. User opens prompt input → "Make this shorter" / "Add urgency" / "Translate to Spanish" → AI rewrites in place. Reuse existing Sonnet/DeepSeek. Effort: M. Lightweight win even before full MPC-5 lands.
Recommended build order (90-day plan):
- Week 1-2: MPC-12 Org knowledge base (small, unblocks AI personalization)
- Week 1-2 parallel: MPC-15 Edit-with-AI buttons everywhere (immediate user-visible win, low risk)
- Week 2-3: MPC-2 Campaign folders + multi-channel stats refactor
- Week 3-6: MPC-5 AI personalization with extracted variables (the core magic — biggest piece, builds on MPC-12)
- Week 6-9: MPC-3 + MPC-4 Campaign editor wizard + run view (depends on MPC-5)
- Week 9-12: MPC-13 AI Assistant chat (global + side-panel)
- Week 6-13 parallel track: MPC-9 LinkedIn channel (Playwright per-user) — slowest, riskiest, can ship in parallel
- Week 12-14: MPC-10 Lead scraper consolidation (mostly UI on existing Skynet)
- Week 14: MPC-14 public ingest API + final polish
🧹 Email Dashboard (formerly Unified Dashboard) — audit + 5 patches shipped 2026-04-24
UD-P1..P5 + flicker-V3 LIVE on PRODUD-A1LIVE
Email Analytics panel — REAL
Backend
GET /api/dashboard/email-analytics queries email_communications (status counts, openRate, replyRate, bounceRate, 30d timeline, last 10 emails). Frontend reads it. ✅ Live data./unified-dashboard → Overview → Email Analytics card
UD-A2LIVE
Campaign Mgmt panel — REAL
Backend
GET /api/campaigns returns campaign rows. Frontend computes total/active/totalSteps/avgSteps. ✅ Live data./unified-dashboard → Overview → Campaign Management card
UD-A3LIVE
Pipeline Automation panel — REAL
Backend
GET /api/pipeline-rules returns rule rows. Frontend computes total/active/onReply/onOpen counts. ✅ Live data./unified-dashboard → Overview → Pipeline Automation card
UD-A4PARTIAL
CRM Metrics panel — REAL but missing pipelineValue
Backend
GET /api/analytics returns totalCompanies, totalContacts, activeDeals, conversionRate, revenueMTD, commissionMTD. Frontend ALSO renders pipelineValue which backend does NOT return → ALWAYS falls back to mock €1,250,000. Real value queried now: €13,827,000 open across 18 deals. Also: hardcoded fallback numbers 30,784 / 119,999 / 245 shown if API errors./unified-dashboard → Overview → CRM Metrics card
UD-A5MOCK
Lead Scoring panel — HARDCODED
Frontend has
const leadData = { averageScore: 72.5, highQualityLeads: 150 } hardcoded — NO backend endpoint. Yellow "Mock Data" badge visible. Real data avail in contacts.icp_score: avg 74, high-quality (≥80) 33, scored 101 / 119,999./unified-dashboard → Overview → Lead Scoring card
UD-A6STALE
Footer "Implementation Status" text — STALE
Footer says "Email Analytics: Phase 1 complete (database), UI mock data" → outdated, email IS live. "Lead Scoring: Phase 3 planned, mock data for now" → must drop after UD-P2 lands.
/unified-dashboard → Overview → bottom card "Implementation Status"
UD-P1PROD
Backend — pipelineValue + /api/dashboard/lead-scoring
Extended
New
GET /api/analytics with pipelineValue = SUM(value) WHERE stage NOT IN ('Closed Won','Closed Lost'). Own-scope filter inherited.New
GET /api/dashboard/lead-scoring → { averageScore, highQualityLeads, scoredCount, totalContacts } from contacts.icp_score. Own-scope filter applied.
DEV verified → PROD promoted
__SMRT_NEXT_UD_BACKEND_V1__
UD-P2PROD
Frontend — killed all mock fallbacks + badges
① Hardcoded
② Mock Data badge → Live Data (force-swap regardless of card title).
③ Fallback numbers
④
⑤ Footer "mock data" wording rewritten to live-source descriptions.
⑥ Pre-existing bug fixed:
leadData → (window.__sn_ud_lead||{averageScore:0,highQualityLeads:0}) with IIFE fetch.② Mock Data badge → Live Data (force-swap regardless of card title).
③ Fallback numbers
30,784 / 119,999 / 245 / 1,250,000 → 0.④
pipelineValue||125e4 → pipelineValue||0.⑤ Footer "mock data" wording rewritten to live-source descriptions.
⑥ Pre-existing bug fixed:
step_count string concat ("00000000003") → +Number(...) at 2 sites.
DEV verified → PROD promoted
__SMRT_NEXT_UD_FRONTEND_V1__ + V2 (decoupled patch)
UD-P3PROD
Reply classifier (sentiment + auto-suppress + sentiment-aware rules)
Schema:
Seeded 4 rules: Negative→Closed Lost (any stage); Positive: Prospecting→Qualification, Qualification→Proposal, Proposal→Negotiation.
Classifier: keyword (NEG: unsubscribe/no interest/remove me/etc.; POS: interested/schedule/let's talk/etc.) → DeepSeek LLM fallback for ambiguous →
Hooks: Brevo Inbound webhook →
Cron (B): every 60s scans
Negative reply auto-suppress:
pipeline_rules + from_stage, sentiment_filter (default 'any'), UNIQUE(name). email_replies + rules_applied_at + index on pending. Old "Move to Proposal on Email Reply" rule deleted.Seeded 4 rules: Negative→Closed Lost (any stage); Positive: Prospecting→Qualification, Qualification→Proposal, Proposal→Negotiation.
break after first match → no cascade.Classifier: keyword (NEG: unsubscribe/no interest/remove me/etc.; POS: interested/schedule/let's talk/etc.) → DeepSeek LLM fallback for ambiguous →
{sentiment, confidence, reason}. Reuses existing sentiment if gmail-poller already classified.Hooks: Brevo Inbound webhook →
setImmediate(__sn_classify_reply) after INSERT. Manual POST /api/email-replies/:id/reclassify for testing.Cron (B): every 60s scans
email_replies WHERE direction='inbound' AND received_at > NOW() - 7d AND rules_applied_at IS NULL LIMIT 50 → catches gmail-poller path + any future ingestion sources.Negative reply auto-suppress:
INSERT INTO email_suppression (email, reason='reply:negative'). Verified end-to-end on DEV with 2 test contacts/deals (Prospecting → Qualification, then → Proposal; another → Closed Lost + suppressed).
/unified-dashboard → Pipeline Automation tab (rule list with from/sentiment badges)
__SMRT_NEXT_REPLY_CLASSIFIER_V1__ + REPLY_CRON_V1 + FIX1 (break)
UD-P3-UIPROD
Pipeline Rules CRUD + custom modal UI
Backend: POST
Frontend: custom modal hijacks "New Rule" + "Edit" buttons. 7 fields: name, trigger, sentiment_filter, from_stage, action_type, action_value (target stage), active toggle. Edit prefills from
Display: each rule card gets extra row with
/api/pipeline-rules extended with from_stage + sentiment_filter (ON CONFLICT name DO UPDATE). New PATCH + DELETE endpoints by id.Frontend: custom modal hijacks "New Rule" + "Edit" buttons. 7 fields: name, trigger, sentiment_filter, from_stage, action_type, action_value (target stage), active toggle. Edit prefills from
/api/pipeline-rules by name match.Display: each rule card gets extra row with
from: X + sentiment: X color-coded badges. Pre-existing "trigger_typeedCount" typo display fixed.
/unified-dashboard → Pipeline Automation → New Rule / Edit per row
__SMRT_NEXT_PIPELINE_RULES_CRUD_V1__ + RULE_FORM_UI_V1
UD-P4PROD
Email Log + Reply Tracking + Webhook Config tabs (3 new tabs)
Backend endpoints:
GET /api/email-log— paginated UNION ofemail_communications+email_replies. Filters: search (email/subject/contact), status, direction, days (7/30/90/365). 50/page.GET /api/replies-feed— inbound replies w/ sentiment, contact, snippet, deal-created marker, rules-applied marker. Search + sentiment filter.GET /api/webhook-stats— Brevo webhook URL + Inbound parse domain, total events, 7d events, 7d inbound replies, last timestamps, events-by-type breakdown frombrevo_events.
/email-tracking 404 page (unrouted).
/unified-dashboard → Email Log / Reply Tracking / Webhook Config tabs
__SMRT_NEXT_EMAIL_LOG_V1__ + REPLY_WEBHOOK_TABS_BE_V1__ + FE_V1__
UD-P5PROD
Rename "Unified Dashboard" → "Email Dashboard" (label only)
5 string-replace sites in main bundle. URL preserved:
Sidebar: auto-renamed via same string replace (single source).
Page H1: auto-renamed.
Cleanup: deleted orphan
/unified-dashboard unchanged. Perm key preserved: canAccessUnifiedDashboard unchanged. Component name preserved: UnifiedDashboard unchanged. Only the user-facing label changed → no bookmarks/imports/perms break.Sidebar: auto-renamed via same string replace (single source).
Page H1: auto-renamed.
Cleanup: deleted orphan
index-B5BSXjE7.js chunk (older Vite build artifact, not loaded by HTML, but could've been served from browser cache).
Sidebar shows "Email Dashboard" → /unified-dashboard route loads
__SMRT_NEXT_EMAIL_DASHBOARD_V1__ (label rename + Email Log tab IIFE)
UD-P6PROD
Polish — compact big numbers, kill mock badges, tab CSS
Big numbers: €13,827,000 → €13.8M (compact). 30,784 → 30.8K. Tooltip preserves original. Auto-applies to
Mock badge force-kill: any "Mock Data" badge swapped to "Live Data" regardless of card. Lead Scoring desc rewritten to "Live ICP scores from CRM contacts (X of Y scored)".
Footer "Phase X" wording: rewritten to live-source descriptions.
Tab CSS: tabs forced to single-row flex with horizontal scroll if narrow (was wrapping when 9 tabs).
.text-2xl.font-bold over 9 chars.Mock badge force-kill: any "Mock Data" badge swapped to "Live Data" regardless of card. Lead Scoring desc rewritten to "Live ICP scores from CRM contacts (X of Y scored)".
Footer "Phase X" wording: rewritten to live-source descriptions.
Tab CSS: tabs forced to single-row flex with horizontal scroll if narrow (was wrapping when 9 tabs).
__SMRT_NEXT_UD_POLISH_V1__
UD-P7PROD
Email Dashboard flicker fix v3 (strict scope)
Problem: 1-3s delay before custom tabs (Email Log etc.) appeared. Old tabs flashed first, then patched.
v1+v2 had bug: CSS rule
v3 fix: body class
Lesson learned: NEVER use unscoped global selectors in IIFE-injected CSS. Always gate by body class + path check.
v1+v2 had bug: CSS rule
[role="tablist"]:not([data-sn-patched]) { visibility:hidden } matched every tablist app-wide (Analytics, Skynet, etc.) → broke top-bar tab switchers across CRM. EMERGENCY rollback to PRE-FLICKER state.v3 fix: body class
sn-on-dash set ONLY when location.pathname matches /unified-dashboard. CSS rules scoped body.sn-on-dash [role="tablist"]. Other pages = body class absent = CSS rules dormant. Sync MutationObserver early-returns if not on dashboard. Tab inject verifies tablist contains "Overview" or "Email Templates" tab text BEFORE adding sn tabs. Cleanup on nav-away.Lesson learned: NEVER use unscoped global selectors in IIFE-injected CSS. Always gate by body class + path check.
__SMRT_NEXT_DASH_FLICKER_V3__
Final state PROD: Email Dashboard 100% real data. 9 tabs single row no flicker. Reply classifier active (Brevo + cron paths). Sentiment-aware rules auto-move deals. Negative replies auto-suppress contact email.
📚 Lessons learned (2026-04-24 session)
add to RTK / process docsL-1RULE
psql -c with multi-statement string = single transaction
psql -c "stmt1; stmt2;" wraps all in implicit tx. One failure rolls back ALL. Lost ALTER TABLE columns when later INSERT failed. Fix: separate psql -c per statement, OR use heredoc with explicit BEGIN/COMMIT.L-2RULE
Schema drift DEV vs PROD — bundle sync ≠ data sync
DEV had old
pipeline_stages rows (Prospecting renamed to Qualification, Closed Lost deleted on Apr 16). DEV missing contacts.icp_score, email_communications.subject/direction columns. Bundle sync only copied JS, not row-level data. Fix: add periodic config-table diff (pipeline_stages, roles, settings, smart_hub_categories) DEV vs PROD.L-3RULE
Test patches on DEV END-TO-END before promotion
Reply classifier had cascade bug (positive reply moved deal Prospecting→Qualification→Proposal→Negotiation in one shot). Caught only via end-to-end test with real deal + simulated reply. Smoke test "online status 200" insufficient. Fix: always exercise actual API path with real data on DEV before promoting.
L-4RULE
CSS in IIFE: NEVER unscoped global selectors
flicker-kill v1+v2 used
[role="tablist"]:not([data-sn-patched]) globally → hid every tablist on every page. Broke Analytics/Skynet/etc. top bars. Fix: always gate by body class + path check (body.sn-on-page [selector]).L-5RULE
Vite chunks may have stale orphan files
PROD had
index-B5BSXjE7.js from Apr 18 build, not referenced by current HTML, but served if requested. Browser cache could load it from previous session. Fix: after every patch session, delete orphan chunks not referenced by index.html (move to /tmp first as safety).🛡️ DEV environment workflow (Option A — DEV-only test queue)
prod sacred · 2026-04-23DEV-1DONE
DEV path + DB isolated
/Smrt-CRM-DEV/ separate from /Smrt-CRM-FINAL/ · port 5001 vs 5000 · DB smrt_crm_dev vs smrt_crm · 7 users + 120k contacts in DEV · PROD bundle
chattr +i, DEV mutable.DEV-2DONE
Nginx vhost dev.pros11.com → :5001
/etc/nginx/sites-enabled/dev.pros11.com created (HTTP only). Mirrors crm.pros11.com config (gzip, /skynet-api/ proxy, JS/CSS no-cache, CORS).
DEV-3DONE
DNS + HTTPS certbot
A record dev.pros11.com → 187.77.74.76 propagated. Let's Encrypt cert deployed (expires 2026-07-22, auto-renew). HTTP→HTTPS redirect active.
DEV-4DONE
CRM bundles synced PROD → DEV baseline
DEV /dist/index.js + /dist/public/assets/index-Dtu0_nu2.js = current PROD. New feature patches layered on top of DEV only. Backups saved as
.PRE-DEV-BASELINE-TS.DEV-5DONE
Skynet FULL isolation (port 3010)
/skynet-dev/backend/ independent (467MB clone, different inodes, own SQLite). PM2 id 9 skynet-backend-dev on :3010. Email-pattern cron DISABLED on dev. Scheduled scrapers off — manual only. CRM-DEV bundle hardcoded localhost:3006 → :3010 (10 sites). Dev nginx /skynet-api/ → :3010.DEV-6SHARED
Shared external services (be mindful)
Apify + Apollo + Hunter + Snov + Brevo API keys SHARED — manual button clicks on dev hit real services (real cost, real emails). Gmail-poller + email-microservice write to PROD DB only. Optional:
DEV_DRY_RUN=true Brevo intercept to be added if needed.RULEPROMISE
Workflow contract
All NEW feature patches → DEV bundle ONLY. PROD bundle untouched until user explicitly says "promote feature X to prod". Per-feature decision: 👍 promote · 👎 revert DEV.
🎯 SMRT-Next-inspired feature roadmap (DEV test queue)
audit 2026-04-23 · TIER 1+2 only · skip irrelevantC-1DEV
Command palette (Cmd+K) — LIVE on dev
Universal search + 8 quick-nav buttons + live contact/company search (200ms debounce). Arrow keys + Enter + Escape. Pure vanilla DOM IIFE.
Trigger: ⌘K / Ctrl+K anywhere on dev.pros11.com
__SMRT_NEXT_01_CMDK_V1__
C-2REDUNDANT
Side-panel preview — existing 👁 already does this
Existing CRM cards already have 👁 ✏ ✉ 🗑 action icons; the 👁 opens detail modal. New side panel adds noise without value. IIFE loaded but inert (no row matches). Suggest removing.
N/A — existing 👁 in card top-right already opens detail
__SMRT_NEXT_02_SIDEPANEL_V1__ (loaded but no-op on card layout)
C-3UI REMOVED
Customer 360 timeline — backend kept, UI removed
Vertical event list per contact: email_sent + email_reply + activity + deal_event + ownership_transfer + skynet_import (cross-table from Skynet via /api/skynet/contact-history/:id). Type filter checkboxes, relative+absolute timestamps.
/contacts/:id → 📊 Timeline button
__SMRT_NEXT_03_TIMELINE_V1__
C-4DEV
Real-time duplicate detection
POST /api/contacts/check-duplicates returns top 5 candidates by email/name match with score. Frontend live panel appears top-right on contact create form (400ms debounce).
/contacts → Add Contact → live ⚠ panel
__SMRT_NEXT_04_REALTIME_DEDUP_V1__
C-5UI REMOVED
WhatsApp framework — backend kept, UI removed
Provider-agnostic stub: WHATSAPP_PROVIDER=twilio|360dialog|wati|empty (DEV_DRY_RUN). DB table whatsapp_messages. POST /api/whatsapp/send + GET thread + POST webhook. iMessage-style chat overlay on contact page.
/contacts/:id → 📱 WhatsApp button (right side, green)
__SMRT_NEXT_05_WHATSAPP_V1__ — needs provider env vars
C-6DEV
Field-level permissions (deal financials)
Hides deal.value/commission/probability for users WITHOUT canViewDealValues. Helper __sn6_filterDeal wraps all 4 res.json sites on /api/deals routes. Sets _redacted marker for frontend rendering hint.
Trainee role: deal cards show empty €value
__SMRT_NEXT_06_FIELD_PERMS_V1__
C-7DEV
Pipeline bottleneck chart
GET /api/analytics/pipeline-bottleneck returns per-stage count + value_sum + avg_days_in_stage + stuck_count(>30d) + bottleneck_score. Inherits own-scope filter from C-218. Chart overlay with color-coded bars.
/analytics → ⚡ Bottleneck Chart button
__SMRT_NEXT_07_BOTTLENECK_V1__
C-8DELETED
AI Action metering — DELETED (user said "I don't like it")
Table ai_actions_log + global helpers __sn8_log (CRM) + __sn8_log_remote (Skynet bridge). 6 CRM AI sites instrumented (DeepSeek). Skynet Apollo + future LLM calls relayed to CRM via POST /api/internal/ai-log. Dashboard overlay shows totals, per-provider/user/feature.
Cmd+Shift+A — opens metering overlay
__SMRT_NEXT_08_AI_METERING_V1__
C-9DEFERRED
Outbound webhooks — DEFERRED (user "we don't need")
Table outbound_webhooks + global.__c9_emit helper (HMAC signed POST). CRUD endpoints. Events: deal.created/updated/won, contact.created/updated, reply.received, lead.imported. Admin overlay for URL + event subscription.
Cmd+Shift+W — opens webhook admin
__SMRT_NEXT_09_OUTBOUND_WEBHOOKS_V1__
C-10DELETED
AI agent builder — DELETED per user feedback
Table custom_agents + CRUD endpoints + POST /api/custom-agents/:id/run with {{field}} placeholder substitution. Skynet POST /api/skynet/agent/exec runs DeepSeek with output_schema. Builder overlay (Cmd+Shift+G) + per-row 🤖 buttons on Skynet/contact/deal pages.
Cmd+Shift+G — Agent Builder · per-row 🤖 buttons
__SMRT_NEXT_10_AGENT_BUILDER_V1__
C-11DEV
Permission audit log
Table permission_audit_log. Hooked into PUT /api/roles/:roleName/permissions to log every diffed key change (who/when/role/key/old→new). GET /api/permissions/audit reads back. Overlay shows table view.
Cmd+Shift+P — opens audit log overlay
__SMRT_NEXT_11_PERM_AUDIT_LOG_V1__
C-12REDUNDANT
Kanban — /pipeline is already kanban
Existing /pipeline page already renders columns per stage with drag-drop cards. Toggle adds nothing. IIFE loaded but inert.
N/A — visit /pipeline for the existing kanban
__SMRT_NEXT_12_KANBAN_V1__ (loaded but ignored)
HF-08DEV — FINAL
🛠 Hotfix-08 — final cleanup (round 5)
🗑 Launcher bar DELETED entirely.
🗑 C-10 Agent Builder DELETED — keyboard shortcut blocked, overlay killed, per-row 🤖 buttons removed by MutationObserver.
🆕 ⌘K in top bar: 🔍 search icon button injected next to bug reporter / theme toggle. Click opens Cmd+K palette.
🆕 📜 Audit Log on /permissions: button injected next to "Add Role" (right of "9 roles" badge). Opens audit log panel inline.
🆕 New perm key
🗑 C-10 Agent Builder DELETED — keyboard shortcut blocked, overlay killed, per-row 🤖 buttons removed by MutationObserver.
🆕 ⌘K in top bar: 🔍 search icon button injected next to bug reporter / theme toggle. Click opens Cmd+K palette.
🆕 📜 Audit Log on /permissions: button injected next to "Add Role" (right of "9 roles" badge). Opens audit log panel inline.
🆕 New perm key
canViewPermissionAuditLog — replaces defunct canSeeSmrtNextLauncher. Settings category. Toggle per role.
/permissions → 📜 Audit Log button · Top bar → 🔍 Cmd+K
__SMRT_NEXT_HF08_V1__
HF-06/07DEV
🛠 Hotfix-06+07 — flicker killed, C-8 retired
Synchronous flicker killer: MutationObserver removes
C-8 AI metering retired: 📊 button removed from launcher. Cmd+Shift+A blocked. Backend code remains but unused.
Launcher reduced to 3 buttons: ⌘K (search) · 📜 (audit log) · 🤖 (agents). Webhooks + Bottleneck + AI$ all dropped per user feedback.
__sn_launcher_toggle + __sn_deals_btn + __sn7_btn the moment they're added to DOM (before paint). No more visible flicker on /permissions or top bar.C-8 AI metering retired: 📊 button removed from launcher. Cmd+Shift+A blocked. Backend code remains but unused.
Launcher reduced to 3 buttons: ⌘K (search) · 📜 (audit log) · 🤖 (agents). Webhooks + Bottleneck + AI$ all dropped per user feedback.
Top-right launcher (super_admin or canSeeSmrtNextLauncher)
__SMRT_NEXT_HF06/07_V1__
HF-05DEV
🛠 Hotfix-05 — feedback round 4 applied
Modal injection killed: Timeline + WhatsApp + Agents buttons no longer added to contact view modal. MutationObserver actively removes any
Bottleneck filter relocated: from floating top-right button into /deals Search & Filter card grid (6th cell, next to "Type to search companies"). Button: 🐢 Stuck deals · click prompts threshold (default 60d) · filters cards in-place.
C-8 metering proper UI: renders period selector tabs (Today/7d/MTD/All) · 3 totals cards (Cost / Calls / Tokens) · 3 breakdown tables (Provider / User / Feature). Empty state explains how metering activates.
/permissions blink fix: kills HF-03 launcher toggle box every 500ms. No more flicker.
__sn_modal_buttons if reinjected.Bottleneck filter relocated: from floating top-right button into /deals Search & Filter card grid (6th cell, next to "Type to search companies"). Button: 🐢 Stuck deals · click prompts threshold (default 60d) · filters cards in-place.
C-8 metering proper UI: renders period selector tabs (Today/7d/MTD/All) · 3 totals cards (Cost / Calls / Tokens) · 3 breakdown tables (Provider / User / Feature). Empty state explains how metering activates.
/permissions blink fix: kills HF-03 launcher toggle box every 500ms. No more flicker.
/deals → Search & Filter → 🐢 Stuck deals · 📊 launcher → AI metering proper view
__SMRT_NEXT_HF05_V1__
HF-04DEV
🛠 Hotfix-04 — feedback round 3 applied
Contact-id capture for modal injection: hooked window.fetch to record any /api/contacts/UUID URL → captures id BEFORE modal opens. Now Timeline + WhatsApp buttons inject correctly inside contact modal.
Companies dedup: new POST /api/companies/check-duplicates endpoint + dedicated IIFE that fires on company create form. Side panel top-right shows possible matches.
Bottleneck → Deals filter: removed bottom-left button from /analytics. New "🐢 Show stuck deals" button at top of /deals page. Click → prompt for threshold (default 60d) → filters cards to show only deals updated >Nd ago. Click again to clear.
C-8 metering "All time": overlay now clicks "All time" period button on open, falls back to direct API call ?period=all if not present.
Real perm key for launcher: dropped localStorage hack. New perm
Removed: /permissions localStorage checkbox.
Companies dedup: new POST /api/companies/check-duplicates endpoint + dedicated IIFE that fires on company create form. Side panel top-right shows possible matches.
Bottleneck → Deals filter: removed bottom-left button from /analytics. New "🐢 Show stuck deals" button at top of /deals page. Click → prompt for threshold (default 60d) → filters cards to show only deals updated >Nd ago. Click again to clear.
C-8 metering "All time": overlay now clicks "All time" period button on open, falls back to direct API call ?period=all if not present.
Real perm key for launcher: dropped localStorage hack. New perm
canSeeSmrtNextLauncher in admin perm UI list (Navigation category). Toggle per role like any other perm. Admin/super_admin always allowed.Removed: /permissions localStorage checkbox.
All visible per super_admin. Other roles need toggle in /permissions → role → "See SMRT-Next Launcher Bar"
__SMRT_NEXT_HF04_*
HF-03DEV
🛠 Hotfix-03 — feedback round 2 applied
Launcher bar: 4 buttons only (⌘K · 📜 · 📊 · 🤖). Removed ⚡ + 🔗 per user request. Super_admin only by default. Mobile-hidden <640px. Repositioned right:330px (no longer overlapping bug report icon).
/permissions page: "Show launcher bar" checkbox at top — per-user localStorage override (non-super-admin can opt-in if admin allows).
Cmd+K palette: dropped "Next Features" header. Quick actions only show when user has permission.
Contact modal injection: Timeline + WhatsApp buttons appear at bottom of contact view modal (no longer requires /contacts/:id route).
C-10 Agent Builder: 3 concrete safe examples replace generic prompt — Email follow-up drafter / Lead qualifier / Company researcher. Strong "results to ai_shadow_log for REVIEW only — never auto-applied" warning.
C-8 metering: default period set to "All time" so empty data state more visible.
Removed from launcher (still accessible via direct URL/code): C-7 bottleneck (user said don't need), C-9 webhooks (user said don't need).
/permissions page: "Show launcher bar" checkbox at top — per-user localStorage override (non-super-admin can opt-in if admin allows).
Cmd+K palette: dropped "Next Features" header. Quick actions only show when user has permission.
Contact modal injection: Timeline + WhatsApp buttons appear at bottom of contact view modal (no longer requires /contacts/:id route).
C-10 Agent Builder: 3 concrete safe examples replace generic prompt — Email follow-up drafter / Lead qualifier / Company researcher. Strong "results to ai_shadow_log for REVIEW only — never auto-applied" warning.
C-8 metering: default period set to "All time" so empty data state more visible.
Removed from launcher (still accessible via direct URL/code): C-7 bottleneck (user said don't need), C-9 webhooks (user said don't need).
Top-right launcher bar — super_admin only or opt-in via /permissions
__SMRT_NEXT_HOTFIX_01/02/03_V1__
Skipped: BPMN process designer · Service ticketing module · Marketing campaign designer · Quote/contract module · Marketplace · Mobile native app · Account hierarchy · Field-level encryption · IP restrictions · E-signature.
Reasons: vertical mismatch with football scouting CRM, low ROI, or existing equivalent (RTK / Brevo / DocuSign elsewhere).
📦 Patches 115→232 (sessions 2026-04-19 → 2026-04-23)
live in prod (NOT dev — prod-baked)115-189Skynet stakeholder lifecycle hardening + email subsystem stabilization (multi-session)various __PATCH_NNN__
190-194Re-import block (lead-status 409) + email_unique_idx (Postgres)__P190B_LEAD_STATUS_V1__, __P194A_EMAIL_UNIQUE_IDX_V1__
195-199Skynet duplicate gates · IMPORTED badge stays visible · email_communications upsert__P199_SHOW_IMPORTED_V1__
200-202IMPORTED badge stacked · zero email events in Activity Log (Email Journal only)__P200_*, __P202_*
203A/B/CBrevo Inbound Parsing reply tracking · Reply-To injection · MX records reply.pros11.com__P203_INBOUND_PARSER_V1__
204-207Pipeline Labs filter · mark-imported-smart enrich return · CRM enrichment COALESCE__P205_SMART_ENRICH_V1__, __P207_FULL_ENRICH_V1__
208PUT /api/pipeline-labs/leads/:id accepts email/phone/linkedin/first_name/last_name__P208_PLABS_EDIT_FULL_V1__
209getContactsPaginated includes linkedinUrl (was missing → "Not provided" in modal)__P209_LINKEDIN_IN_PAGINATED_V1__
210nightly_leads import-approved uses lead.linkedin_url (was lead.linkedinUrl undefined)__P210_NIGHTLY_IMPORT_LINKEDIN_V1__
214SACRED user edits across all fields — Apollo+Soccerdonna NULLIF-guarded__P214_SACRED_USER_EDITS_V1__
215A/BPending AI Reviews approve refreshes pred from current source row (lead-fetch endpoint)__P215A_LEAD_FETCH_V1__, __P215B_APPROVE_FRESH_PRED_V1__
216Promote bar gated (canAccessSkynet) · Bug Reports nav+route gated (canViewBugReports)__P216_PERM_FIXES_V1__
217+222Unified Dashboard admin UI cleanup (added then dedup'd)__P222_REVERT_217_V1__
218+219/api/analytics/v2 server-side own-scope force · leaderboard+activity scope-filter__P218_ANALYTICS_OWN_SCOPE_V1__, __P219_LEADERBOARD_SCOPE_V1__
220+221Single Access Denied UI (Ia returns null; route-guard overlay handles UI; no more double 404)__P221_UNIFY_PERM_DENY_V1__
2234 perm layers aligned · ws() React Query staleTime 10min→30s + refetchOnFocus__P223_PERM_ALIGN_V1__
224__permGate refetches on focus + 30s polling — admin perm toggles propagate live__P224_PERMGATE_REFETCH_V1__
225+226+227Smrt Hub docs+categories admin bypass + DB backfill all roles__P225_*, __P226_*, __P227_*
22819 backend orphan perm gates wired (activities/documents/reminders/bulk/email queue/Skynet granular)__P228_REAL_PERM_GATES_V1__
229requirePermission admin bypass (was super_admin only)__P229_ADMIN_BYPASS_REQ_PERM_V1__
230Skynet review action-specific perms · meetings POST/DELETE gated · promote-to-review → canPromoteLeadToReview__P230_REVIEW_MEETINGS_V1__
231+232apiRequest .ok bug fixed at 3 sites: notification prefs · email-demo status · delete deal__P231_APIREQUEST_OK_FIX_V1__, __P232_DELETE_DEAL_OK_V1__
✅ Earlier shipped (ledger 001–062)
live in prod023Validation fast queries__VALIDATION_FAST_QUERIES_V1__
024Email queue REAL Brevo worker (30s tick)__EMAIL_QUEUE_REAL_WORKER_V1__
025Email signature + sender + journal__EMAIL_SIG_SENDER_JOURNAL_V1__
026Signature UI in dashboard__SIGNATURE_UI_V1__
027HTML signature support__HTML_SIGNATURE_V1__
028Signature paste handler(no marker — wraps 027)
049-057Ownership transfer workflow: 3-step approval, aliases, status column, auto-reason__OWNERSHIP_*__, __TRANSFER_*__
058-062Security lockdown, SQL fixes, input validation, perf, /api/roles unstomped__SECURITY_LOCKDOWN_V1__, __SQL_BUG_FIXES_V1__, __PERF_AND_PERMS_V1__
📧 Email subsystem (HIGHEST PRIORITY)
user 2026-04-17029DONE
DNS sender verification
DKIM at
brevo1/brevo2 selectors live. smrtstats.com + smrtstats.ai both authenticated + verified by Brevo. 4 senders active.verified via dig + Brevo /v3/senders/domains
029.1DONE
Webhook → mail-journal activities
Both
/api/email/webhook AND /api/brevo/webhook insert activity rows on opens/clicks/replies/bounces/spam (60s dedup). Visible in contact journal automatically.patches 063+064+065 — __EMAIL_WEBHOOK_JOURNAL_V1__, __BREVO_WEBHOOK_JOURNAL_V1__
UI: any contact modal → Journal section
030DONE
Brevo webhook live
Webhook URL
/api/brevo/webhook → table brevo_events. Receives delivered/opened/click/email_replied. Updates email_communications + auto-suppresses bounces (033) + fires pipeline rules (040).verified 2026-04-17, extended in this session
031DONE
email_comms write marker + campaign_id
Re-applied 2026-04-18. Worker INSERT now includes campaign_id and __EMAIL_COMMS_WRITE_V1__ marker. 2 INSERT sites patched. Health 200.
patch 031 (re-applied) — __EMAIL_COMMS_WRITE_V1__
032DONE
message_id column migration
ALTER TABLE applied. Worker writes Brevo messageId to dedicated
message_id column. error column stays NULL on success.patch 066 — __EMAIL_QUEUE_MESSAGE_ID_V1__
033DONE
Bounce auto-suppress on webhook
Brevo webhook now inserts into
email_suppression for hard_bounce/soft_bounce/blocked/invalid_email/spam/complaint/unsubscribed. Worker pre-send check filters against it. Verified live with hard_bounce test.patch 067 — __BOUNCE_AUTOSUPPRESS_V1__
UI: /unified-dashboard → Email Bounces tab
034DONE
Discipline ENFORCEMENT + bulk approval UI
Daily-cap check on send-bulk-campaign (super_admin=∞, admin=1000, supervisor=500, sales_manager=200, trainee=100). Over-cap → 429 + insert into
bulk_send_approvals. Admin endpoints + UI tab. ENFORCE_SEND_LIMITS=true loaded.patches 093+094+095 — __DISCIPLINE_ENFORCE_V1__, __BULK_APPROVAL_UI_V1__, __FRIENDLY_429_TOAST_V1__
UI sender: friendly 429 toast on bulk send · UI admin: /settings → Bulk Send Approvals tab
035DONE
Contact "Journal" unified timeline
GET /api/sales-notes returns merged feed: Sales Notes + Email events + Meetings + Deals + Ownership transfers. Frontend renders type badges (NOTE/EMAIL/DEAL/etc.) + ←→ direction arrows for inbound/outbound emails.
patches 068+069+088 — __CONTACT_JOURNAL_V1__, __JOURNAL_TYPE_AWARE_V1__, __JOURNAL_INBOUND_TOAST_V1__
UI: any contact modal → Journal section
036DONE
Inbound parser via Brevo
Took option A: Brevo Inbound Parser delivers
email_replied via existing webhook. ALTER TABLE added direction column. Replies tagged direction='inbound'. No separate IMAP needed.patch 070 — __INBOUND_PARSER_V1__
UI: contact Journal shows ← arrow on inbound replies
🤖 Skynet AI layers (HIGHEST PRIORITY tied with email)
single agent runtime, shadow first, ≥95% precision037DONE
Layer 1 — SD turnover detector
4 endpoints under /api/skynet (shadow-log generic + turnover specific + review queue + PATCH approve/reject). Operator-review UI in /skynet → Pending AI Reviews. Verified live with 5 Olímpic test entries.
patches 071+072 — __SKYNET_L1_TURNOVER_V1__, __SKYNET_AI_REVIEWS_UI_V1__
UI: /skynet → Overview tab → "Pending AI Reviews" purple card
038DONE
Layer 5 — Reply classifier
DeepSeek (deepseek-chat) + heuristic fallback. Categories: positive/negative/ooo/unsubscribe/question/neutral/bounce. Auto-detects subtle replies the heuristic misses. Logs to ai_shadow_log → Pending AI Reviews.
patch 073 — __SKYNET_L5_CLASSIFIER_V1__
UI: contact modal → Skynet AI Actions panel → ✦ Classify reply
039DONE
Deal auto-create on positive reply
When operator clicks Approve on a reply_classifier+positive entry: creates Deal in Prospecting stage with title "Positive reply — <Name> — <date>", assigned to approver. Skips if contact already has open deal. Toast confirms.
patches 074+088+090 — __DEAL_AUTOCREATE_V1__, AI Reviews approve toast
UI: /skynet → Pending AI Reviews → Approve on positive reply row
040DONE
Pipeline stage auto-advance
Brevo webhook now invokes pipeline_rules engine inline. Each rule (e.g. "Reply → Qualification") fires per event. Activity logged with from_stage/to_stage. Verified Prospecting→Proposal→Qualification on test reply.
patch 075 — __PIPELINE_AUTO_V1__
UI: /unified-dashboard → Pipeline Automation tab (existing) — rules now actually fire
041NOT NEEDED
Layer 2 — Enrichment agent
User decision 2026-04-18: "for the crm 120k it's too expensive right now (skip)". Workflow is Skynet-Verify-then-import — post-import bulk enrichment of 120k existing contacts unnecessary. Apollo + Hunter keys exist on disk if reopened later.
/skynet/backend/.apollo-key + .hunter-key (22 + 41 chars)
042DONE
Layer 3 — ICP scoring
Deterministic 0–100 score per contact: Company type (30) + Country priority (25) + Position seniority (25) + Sports (10) + Status (10) + women's-football tilt (+5). Bulk-scored 100 contacts: avg 73.5, 64 high-priority. Re-score & glossary in modal.
patches 076+081+082+085+086+087+090 — __SKYNET_L3_ICP_V1__, __ICP_SCORE_UI_V1__
UI: /contacts (HOT/WARM/MID/LOW badge) · contact modal → ICP Score field + (?) help + Re-score
043DONE
Layer 4 — First-touch drafting
DeepSeek-chat (Sonnet-class). Auto-detects language from company.country (es/it/de/fr/pt/en). Personalizes with name + company + ICP. Verified Italian draft for Frosinone. ~$0.0003/draft.
patches 077+083+091 — __SKYNET_L4_DRAFT_V1__, __SKYNET_ACTIONS_PANEL_V1__
UI: contact modal → Skynet AI Actions panel → ✦ Draft first-touch email → edit → Send via CRM
044DONE
Layer 6 — Meeting-prep brief
POST /api/skynet/meeting-brief generates structured JSON: headline + contact_summary + company_context + recent_signals + talking_points + open_questions. Pulls last 30d activities + last 5 AI signals. UI button on meetings TBD (0 upcoming meetings to test against).
patch 078 — __SKYNET_L6_BRIEF_V1__
UI button: TBD (endpoint ready, no upcoming meetings to integrate against)
📊 Discipline + Leadership (Days 0–60 + measurement)
strategic plan v3045DONE
Leadership dashboard
GET /api/leadership/metrics returns per-rep: sends/delivered/opened/replied/bounced/complaints/deals_created/deals_advanced/deals_won/active_pipeline_value/avg_icp/high_priority. Period selector (today/week/month/quarter). Verified 4 reps, €9.4M pipeline. Per user request, MOVED into /analytics as 2nd tab.
patches 079+084+092 — __LEAD_DASH_V1__, __LEADERSHIP_DASH_UI_V1__, __LEADERSHIP_INTO_ANALYTICS_V1__
UI: /analytics → "Leadership" tab
046DEFERRED
WhatsApp capture (off-channel → CRM)
Deferred: needs Meta WhatsApp Business API OR Twilio/MessageBird/360Dialog account + phone number registration (multi-day Meta verification). Once provider chosen, build /api/whatsapp/webhook analogous to Brevo webhook (write to email_communications with channel='whatsapp', insert activity, fire pipeline rules, classify via reply_classifier).
Goal marker: __WHATSAPP_CAPTURE_V1__
📦 All 52 patches shipped this session (063 → 114c)
2026-04-18063+064email/webhook → activities (mail journal)__EMAIL_WEBHOOK_JOURNAL_V1__
065brevo/webhook → activities__BREVO_WEBHOOK_JOURNAL_V1__
031 (re)email_comms write marker + campaign_id__EMAIL_COMMS_WRITE_V1__
066email_queue.message_id column__EMAIL_QUEUE_MESSAGE_ID_V1__
067Bounce auto-suppress__BOUNCE_AUTOSUPPRESS_V1__
068Unified journal feed__CONTACT_JOURNAL_V1__
069Journal type badges__JOURNAL_TYPE_AWARE_V1__
070direction='inbound' tag__INBOUND_PARSER_V1__
071Skynet shadow-log infra + turnover__SKYNET_L1_TURNOVER_V1__
072Pending AI Reviews UI__SKYNET_AI_REVIEWS_UI_V1__
073Reply classifier (DeepSeek)__SKYNET_L5_CLASSIFIER_V1__
074Approve→Deal/Suppress actions__DEAL_AUTOCREATE_V1__
075Pipeline auto-advance from webhook__PIPELINE_AUTO_V1__
076ICP scoring (deterministic)__SKYNET_L3_ICP_V1__
077First-touch drafting__SKYNET_L4_DRAFT_V1__
078Meeting brief endpoint__SKYNET_L6_BRIEF_V1__
079Leadership metrics API__LEAD_DASH_V1__
080Bug fixes: spamScore + const reassign__TWO_BUG_FIXES_080_V1__
081ICP UI in contact list table + modal__ICP_SCORE_UI_V1__
082GET /api/contacts/:id includes icp_score__CONTACT_ICP_IN_RESPONSE_V1__
083Skynet AI Actions panel (Draft + Classify)__SKYNET_ACTIONS_PANEL_V1__
084Leadership dashboard UI page__LEADERSHIP_DASH_UI_V1__
085Contacts list ICP merge__CONTACTS_LIST_ICP_V1__
086ICP badge in card view__ICP_CARD_VIEW_V1__
087ICP (?) help glossary popover__ICP_HELP_BTN_V1__
088Journal direction arrows + Approve toast__JOURNAL_INBOUND_TOAST_V1__
089Leadership sidebar nav (superseded by 092)__LEADERSHIP_NAV_V1__
090Re-score toast + AI Reviews fallback toast__FIX_5_BUGS_090_V1__
091DOM overlays escape Radix focus trap__OVERLAY_PE_FIX_V1__
092Leadership moved INTO /analytics tab__LEADERSHIP_INTO_ANALYTICS_V1__
093Discipline ENFORCE backend (cap + endpoints)__DISCIPLINE_ENFORCE_V1__
094Bulk Send Approvals UI tab__BULK_APPROVAL_UI_V1__
095Friendly 429 toast on cap-exceed__FRIENDLY_429_TOAST_V1__
096POST /api/skynet/stakeholder-detected — generalises 037 to all 10 football roles__STAKEHOLDER_DETECTED_V1__
097Skynet bridge: LinkedIn batch → CRM stakeholder-detected (cross-source dedup)__SKYNET_LINKEDIN_BRIDGE_V1__
098X-Skynet-Token service auth bypass for server-to-server calls__SKYNET_SERVICE_TOKEN_V1__
099AI Reviews card: Name · Role @ Club + LinkedIn ↗ link__AI_REVIEWS_BETTER_TARGET_V1__
100Stakeholder lifecycle: Approve→queue email; on delivered→CRM contact, on bounce→email_failed badge__STAKEHOLDER_APPROVE_SEND_V1__
101Approve overlay for stakeholder_detected (editable email/subject/body)__STAKEHOLDER_APPROVE_OVERLAY_V1__
102Dark-mode-readable badges (64 sites patched with dark: variants)__DARK_MODE_BADGES_V1__
103Neutral email tone (no "congratulations on new role" assumption)__NEUTRAL_EMAIL_TONE_V1__
104Overlay: Language + Template + Append-signature controls__OVERLAY_LANG_TPL_SIG_V1__
105Fix lifecycle: drop non-existent contacts.source column from INSERT__LIFECYCLE_SOURCE_FIX_V1__
106Style memory loop: AI learns operator voice via few-shot from approved emails__STYLE_MEMORY_LOOP_V1__
107Modals only close on × / Cancel / successful send (no backdrop close)__OVERLAY_NO_BACKDROP_CLOSE_V1__
108Company dedup (alias map + fuzzy match) + email worker contact_id auto-resolve__COMPANY_DEDUP_AND_JOURNAL_FIX_V1__
109aSkynet POST /promote-to-review + /mark-imported + 4-table schema migration__PROMOTE_TO_REVIEW_V1__
109bCRM stakeholder-detected captures sourceTable+sourceId; lifecycle posts back mark-imported__PROMOTE_TO_REVIEW_CRM_V1__
110Floating "Promote to Review" bar on /skynet pages (5-source dropdown)__PROMOTE_TO_REVIEW_UI_V1__
110bCRM proxy /api/skynet-bridge/promote-to-review (server-side bridge token)__PROMOTE_TO_REVIEW_PROXY_V1__
111Lifecycle writes email_communications row + /companies?open=ID auto-opens modal__WEBHOOK_LIFECYCLE_JOURNAL_V1__ + __COMPANIES_AUTO_OPEN_V1__
112Email Journal: 5 columns (Status / Subject / Sent / Delivered / Opened)__JOURNAL_SUBJECT_COL_*_V1__
113aPOST /api/skynet/compose-and-send + /draft-stakeholder-adhoc (style-aware)__COMPOSE_AND_SEND_V1__
113bDOM observer: ✉ Compose button on every Skynet row with email → unified Approve overlay__COMPOSE_OVERLAY_V1__
114aGmail OAuth2 handler (/api/auth/gmail/start + /callback + /status)__GMAIL_OAUTH_HANDLER_V1__
114bgmail-poller PM2 service: polls Gmail every 60s, matches In-Reply-To → email_queueservices/gmail-poller.cjs
114cBidirectional poll (INBOX + SENT) + direction column; outbound captured but not classifiedgmail-poller.cjs (bidirectional)