← Back to CRM Conceptual · static library Lead Process

Lead Process Visualization UNDERSTAND THE LOOP

How the lead's journey routes through walker / chokepoint / ownership / journey loop
Built 2026-05-15 per operator ask · Companion to Operator Compass

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.

AI authors (drafts, classifies, decides)
Chokepoint (blocks + escalates)
Walker (monitors + emits)
ORQ (review queue + batching)
Operator (judges + closes)
Learning loop (signals refine the system)

📍 The journey, end-to-end

flowchart TD Start([Lead message inbound
WA · SMS · IG · email]):::ai Webhook[Webhook receive
writes message row]:::ai S0{§0 tree
last_real_inbound_at}:::ai Classify[chat/classify caller
intent + tier + stage]:::ai AA9{Next-Action Decider
decideNextAction · chokepoint}:::chokepoint Idle((no action
journey idle)):::operator Draft[chat/entry → draftMessage
LLM authors reply]:::ai Tree{Tree router
6 hard trees + tries match}:::ai TreeDraft[Tree-bound draft
fact-refs from business_knowledge]:::ai LLMDraft[Free-form LLM draft]:::ai Gates{Pre-send gates
opener · persona · LOW_EFFORT}:::chokepoint Verify{Verifier
off-brand · hallucination · rule}:::chokepoint Choke{Booking chokepoint
Option A 2026-05-14}:::chokepoint A7[A.7 handover
Let me check with Jack]:::ai Block[Blocked + workboard task
operator surfaces]:::orq Urgent[🚨 URGENT Katie-class task
book NOW]:::orq Send[Send via channel
insertMessage + skills]:::ai ActionChains[Action chains
BOOKING marker · handler-lock · journey update]:::ai Transition{Journey transition
Lead → Booked → Member}:::ai Scheduler((Walker scheduler
every 30 min · 8-20 UK)):::walker Walker[Walker opinions
at-risk · stalled · idle · etc.]:::walker ORQ{ORQ chokepoint
off · shadow · live · batching}:::orq Surface[Workboard surfaces
importance × urgency sort]:::orq Operator[Operator acts
Follow up · Take over · Close]:::operator Feedback[Feedback signals
signal_kind · close_reason]:::learning Confidence((Confidence accrual
branch · rule · pattern)):::learning Start --> Webhook Webhook --> S0 S0 --> Classify Classify --> AA9 AA9 -->|reply needed| Draft AA9 -->|no action| Idle Draft --> Tree Tree -->|match| TreeDraft Tree -->|no match| LLMDraft TreeDraft --> Gates LLMDraft --> Gates Gates -->|blocked| Block Gates -->|pass| Verify Verify -->|block| Block Verify -->|pass| Choke Choke -->|verbal booking +
offered_slot match| Urgent Choke -->|uncertain| A7 Choke -->|pass| Send A7 --> Send Send --> ActionChains ActionChains --> Transition Transition -.member events.-> Scheduler Scheduler --> Walker Walker --> ORQ ORQ -->|emit| Surface Block --> Surface Urgent --> Surface Surface --> Operator Operator --> Feedback Feedback -.refines.-> Confidence Confidence -.tunes.-> Tree Confidence -.tunes.-> Verify Confidence -.tunes.-> Walker %% MISSING LOOP — operator-2026-05-15: when operator marks Follow up / Take over / Close, %% no active reroute back to AI classifier. Captured for analytics only. Deferred to %% 2026-06-01 monthly methodology review (see monthly-methodology-review.md "Queued"). Operator -. ❌ NOT BUILT .-> Classify classDef ai fill:#fff8e1,stroke:#e2bd23,stroke-width:2px,color:#1a1a1a classDef chokepoint fill:#ffebee,stroke:#c62828,stroke-width:2px,color:#1a1a1a classDef walker fill:#e3f2fd,stroke:#4a6fa5,stroke-width:2px,color:#1a1a1a classDef orq fill:#f3e5f5,stroke:#6a1b9a,stroke-width:2px,color:#1a1a1a classDef operator fill:#e8f5e9,stroke:#2e7d32,stroke-width:2px,color:#1a1a1a classDef learning fill:#fff3e0,stroke:#f57c00,stroke-width:2px,color:#1a1a1a linkStyle 35 stroke:#c62828,stroke-width:2px,color:#c62828

🎭 Roles — who does what + where it lives in code

chat/classify AI

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.

crm/chat/classify.js
L1 skills + tier rubric · L2 message history + journey state · L3 Next-Action Decider chokepoint enforces output shape · L4 classification-feedback-loop

Next-Action Decider Chokepoint

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.

crm/decide-next-action.js
L1 decision rules · L2 signal + contact context · L3 emit/skip arbitration · L4 outcome column closes the loop

Tree router AI

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.

crm/a7/tree-loader.js + tree-router.js
L1 6 trees + facts · L2 match against inbound text + journey state · L3 branch confidence enforces routing · L4 graduated-branch-confidence accrual

Pre-send gates Chokepoint

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).

crm/conversation-gates.js
L1 rule banks · L2 applied per-send · L3 blocks at chokepoint · L4 blocked-message audit table

Verifier Chokepoint

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.

crm/conversation-verifier.js
L1 brand + rules · L2 draft + context · L3 verdict enforces · L4 verifier-decision audit

Booking chokepoint Chokepoint

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.

crm/booking-chokepoint.js + send/index.js:830-882
L1 booking skill + offered_slots · L2 draft+inbound at decision time · L3 blocks send + creates URGENT task · L4 booking_chokepoint_audit + operator_feedback

A.7 handover AI

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.

crm/a7/ + crm/send/index.js handover branch
L1 5-layer hallucination prevention · L2 tool_use + confidence signals · L3 handover blocks LLM draft · L4 handover_reason in audit

Walker scheduler + opinions Walker

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.

crm/scheduler.js:304 + crm/journey/walker.js + journey/opinions/*.js
L1 opinion rules in code · L2 contact state queries · L3 cadence law + evidence gates · L4 walker_batching_audit (partial — instrumentation gap)

ORQ chokepoint + batching ORQ

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.

crm/operator-review-queue.js
L1 4-state ownership model · L2 queue lookup at dispatch · L3 mode-gated dispatch · L4 resolution signals close the loop

Workboard + operator actions Operator

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.

crm/routes/workboard.js + Build 4 surface
L1 operator workboard rule · L2 focus surface query · L3 VALID_AFFORDANCES validation · L4 close-with-reason → affordance bank evolves

Learning loop Learning

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.

crm/scripts/digest-conversations.js + journey-state.js
L1 conversations + outcomes · L2 nightly aggregation · L3 proposes via workboard (operator approves) · L4 business_knowledge inserts

Action chains AI

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.

crm/action-chains.js + journey.js
L1 action types + transitions · L2 message parse + state lookup · L3 idempotent creates · L4 ai_decisions audit per chain

📚 L1-L4 across all systems (the diagnostic vocabulary)

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

🔬 Worked example — Katie msg 20937 (2026-05-09)

The case that named Option A

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:

1. Inbound Katie: "Monday morning is good 👍"
2. §0 tree sets last_real_inbound_at
3. chat/classify → intent: booking_confirmation, tier: T1
4. Next-Action Decider (decideNextAction) → reply needed
5. chat/entry draftMessage → LLM emits draft with handover flag (AI uncertain about which day/time without calendar context)
6. A.7 handover path activates → would have sent "Let me check with Jack" hardcoded text
7. NEW Option A Booking chokepoint runs FIRST on the handover text + Katie's inbound
    → Verbal pattern match: let me check with jack
    → offered_slot match: "Monday morning" ↔ operator-sent "Monday morning" ✓
    → Verdict: BLOCK · action: emit URGENT workboard task
8. Handover send SUPPRESSED — Katie receives nothing from AI
9. Workboard surfaces URGENT task: "🚨 BOOKING MISSED — Katie picked offered slot, AI deferred"
10. Operator opens workboard, sees URGENT, sends booking confirmation directly to Katie
11. Action chains on operator's send: creates appointment, journey transitions Lead → Booked
12. Feedback signal: close_reason=booked_by_operator → branch confidence accrues for this shape

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

⚠ Open gap — operator-resolution → AI-classifier loop (deferred to 2026-06-01 review)

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:

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)

🎯 Why this shape matters

The journey loop has three pillars (per crm/methodology/decisions/2026-05-03-three-pillar-architecture.md):

  1. Journey-as-backbone — every contact has journey_stage (Lead/Booked/Member) + journey_status (active/closed) + contact_type. One canonical axis.
  2. Chokepoint pattern — every AI action flows through a chokepoint (Next-Action Decider, booking, send-pipeline gates, A.7). Nothing bypasses + everything audits.
  3. Operator projection — workboard is the operator-visible surface for everything that needs judgment. AI never auto-escalates; chokepoints emit, operator decides.

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.