The mental model: a lead's message enters at the top of this diagram and routes through up to 18 stages before becoming a member (or churning). At each stage, one of four kinds of actor is in charge: AI (drafts + decides), Chokepoint (blocks + escalates), Walker (monitors + emits opinions), or Operator (judges + closes).
The diagram below shows the full path. The cards underneath explain each system's role and where it lives in the codebase. The orange feedback arrows at the bottom show how operator signals refine the system over time — that's the learning loop closing.
Read top-to-bottom for the journey; side-to-side at each stage for which actor owns it.
First pass on inbound. Determines intent (booking-ask, info-ask, objection, hardship, etc.) + tier (T0/T1/T2/T3 based on engagement signals) + journey stage. Cheapest classifier → routes work downstream.
function: decideNextAction() · legacy codename: AA9
Decides what the AI should do next for each contact × signal combination. 16-of-16 callers live (eval-test excluded). Writes next_action_decisions audit row per decision. Schema fix 2026-05-14: reason VARCHAR(100) → TEXT closes ~96% silent-drop rate.
Matches inbound against 6 hard trees (booking, pricing, address, schedule, hardship, etc.) with fact-refs from business_knowledge. Pre-drafted, deterministic, low-hallucination path. Falls back to LLM-free-form if no tree matches at confidence threshold.
Deterministic regex/rule gates BEFORE the verifier. Examples: LOW_EFFORT_OPENER_PATTERNS (catches generic "still interested?" outbounds), persona-claim gate ("I'm an AI" → blocked), §0 tree (WhatsApp 24h window check via last_real_inbound_at).
LLM-as-judge on the draft. Blocks (not rewrites — Change 4 Sophie 2026-05-01) if violates brand voice, hallucinates facts, or breaks any specific rule. Operator-visible note created on block.
Detects "verbal booking confirmation without [BOOKING:...] marker" — the Katie-class failure (lead says yes to a slot, AI defers via "Let me check with Jack"). Option A shipped 2026-05-14: now runs BEFORE A.7 handover so the handover path can't bypass it. Urgent workboard task on catch.
When AI confidence falls below threshold OR draft hallucinates, A.7 fires: sends a holding message ("Let me check with Jack and get back to you 👋"), sets handler='team', creates operator task. After Option A: this path NOW routes through chokepoint first.
Runs every 30 min during 8-20 UK. Iterates contacts × opinion-set. Each opinion (at-risk-attendance-drop, stalled-followup, team-handler-idle, post-apt-outcome, etc.) has applies_when + evidence_required gates. Healthy walker SKIPS most contacts (correct behavior); emits only when conditions match + cadence-law allows.
Operator Review Queue is where walker emits + chokepoint blocks land before reaching workboard. Three modes per opinion: off / shadow / live. Batching: collapses same (contact, opinion) emits within a window so operator sees one item not ten. 40-row historic cleanup 2026-05-14 restored per-member singleton baseline.
The operator's surface for AI-emitted items needing judgment. Three buttons per row: Follow up (you'll handle later) · Take over (engage handler-lock, AI defers) · Close (resolved with reason). Importance × urgency 2-axis sort.
Operator signals (close-with-reason, edit-then-send, dismiss, take-over) feed back into the system. Branch confidence accrues. Skills get tuned via nightly digest (3:30am). Weekly replay reinforces winning sequences (Mon 6:15am). Read-only consolidation — AI never self-modifies rules.
Side-effects fired after a successful AI send. Detects [BOOKING:YYYY-MM-DDTHH:MM] markers + creates appointments. Engages handler-lock on team-outbound. Triggers journey transitions (Lead → Booked → Member). The marker contract is what makes AI-bookings auditable.
Per crm/methodology/concepts/knowledge-to-action-pattern.md — every AI-driven feature has four layers. Chat AI is the mature reference; the rest are at varying maturity.
| System | L1 Knowledge | L2 Retrieval | L3 Enforcement | L4 Learning |
|---|---|---|---|---|
| Chat AI (drafter + verifier) | Skills + business_knowledge + 6 trees | Tree router + getFormattedHistory + offered_slots | Pre-send gates + verifier + booking chokepoint | ORQ resolution signals + branch confidence + nightly digest |
| Next-Action Decider (decideNextAction) | Decision rules in code | Signal + journey state at invocation | 16/16 caller chokepoint live | next_action_decisions audit (post-fix landing rows) |
| Booking chokepoint | appointment-booking skill + offered_slots table | Draft + inbound + offered_slots at decision time | Block + URGENT task (Option A live) | booking_chokepoint_audit + operator_feedback column |
| Walker | Opinion rules in journey/opinions/*.js | Contact state queries every 30min | Cadence law + evidence_required gates | walker_batching_audit (partial — per-step instrumentation gap) |
| ORQ chokepoint | 4-state ownership model + affordance bank | Queue lookup at dispatch | off / shadow / live + batching collapse | signal_kind + close_reason refine affordances |
| Workboard / operator surface | Operator Workboard Rule + Function→Goals→Process→Outputs schema | Focus surface query + importance×urgency sort | VALID_AFFORDANCES validation at insert | Operator close-with-reason → affordance evolution |
| Marketing (substrate ready, code wiring Mon 19+) | 7 skill MDs + brand-consistency | Not yet wired (Mon 19 work) | Not yet wired (Phase 2 chokepoint design) | Not yet wired (Phase 2-3 campaign signal capture) |
| Programming (operator-led) | 18 framework docs + movement_library + signature-shapes | SHALLOW — progGenerateWarmup doesn't query substrate | WEAK — localStorage anti-repeat (volatile) | MISSING — selected_count/rejected_count not captured |
Katie Shepherd was a real lead. The operator (Jack) sent her three slots via WhatsApp: "Monday I am free in the morning or can make a call around 3pm or 7:30pm". Katie replied "Monday morning is good 👍". 9 seconds later, the AI sent "Let me check with Jack and get back to you 👋" — deferring instead of booking.
This was the canonical Katie-class failure. Booking chokepoint was BUILT to catch this exact shape ("verbal booking + offered_slot match → AI defers"). But it didn't fire. Why? Because the A.7 handover at send/index.js:830-882 short-circuited the send pipeline BEFORE the chokepoint check at line 1037. The chokepoint never saw Katie's case.
How it routes through the diagram NOW:
last_real_inbound_atlet me check with jack ✓Before Option A: silent failure — Katie received the wrong message, operator never knew until manual review.
After Option A: caught — chokepoint catches the case before send; operator surfaces in real-time.
Diagnostic: crm/docs/CHOKEPOINT-PROBE-DIAGNOSTIC-2026-05-14.md · Patch: commit d43ce0b · Doc: crm/docs/SPRINT-COMPLETION-AUDIT-2026-05-14.md
What's missing (the red dashed ❌ NOT BUILT arrow in the diagram above): when operator clicks Follow up / Take over / Close on a workboard task, the signal is captured in operator_review_queue.signal_kind — but does NOT actively re-fire AI classification or schedule a next AI action. The Next-Action Decider (decideNextAction) fires passively on the lead's next inbound. If the lead never messages in, the resolution rots in the closed pile.
Why it matters: a stalled lead surfaces to operator → operator marks "Follow up in 3 days" → nothing actively schedules AI's next action 3 days later. The L4 learning loop closure (per knowledge-to-action-pattern.md) is partial — signals captured for analytics, but no active behavioural feedback.
Why deferred: operator 2026-05-15: "to get completion as priority, but possibly we could do a monthly quality review, once more chats have been had? June 1st?" The visualization completes the picture honestly; the FIX waits for 2.5 weeks of real chat data to inform which option (A/B/C below) earns its keep.
Three shapes queued for June 1:
recordSignal accepts requeue_at; the Next-Action Decider scheduler picks up at that time. Smallest unblock.Decision criteria (data-informed at June 1): count of operator-resolved ORQ rows + fraction with future-intent signal_kind in the next 2.5 weeks. <5/wk → defer further; 5-20/wk → Option A; 20+/wk → Option B/C earns the build.
Logged at crm/methodology/concepts/monthly-methodology-review.md §"Queued for 2026-06-01" · Surfaced by operator during lead-process visualization walk-through · Validates the operator-compass concept (visual review surface catches gaps MD walls hide)
The journey loop has three pillars (per crm/methodology/decisions/2026-05-03-three-pillar-architecture.md):
journey_stage (Lead/Booked/Member) + journey_status (active/closed) + contact_type. One canonical axis.And the learning loop (the 4th layer, implicit in the diagram's orange arrows): every operator action becomes signal that refines the system. Next-Action Decider audit + ORQ resolution signals + branch confidence accrual + nightly digest = the system gets better at its job over time without operator manually editing rules.
The diagram looks complex because it IS complex — but each stage exists for a specific failure mode (Katie, Sophie, Lauren, Holly, Marc, Soph, Rosie). Names attached to gaps that drove each chokepoint addition. The story of this codebase = a sequence of named failures + the architectural fix each one earned.