Recommendation planner — template whitelist

The catalog of 18 templates is filtered by issue family, evidence-level floor, and predicates into the whitelist the LLM is locked to.

flowchart TD
    IN["Cluster + EvidencePack + evidenceLevel (L0..L4)"]
    IN --> CATALOG["18-template catalog"]
    CATALOG --> F1{"Issue-family match?"}
    F1 -->|"No"| DROP1["filteredOut"]
    F1 -->|"Yes"| F2{"Evidence-level floor?
(L3+ for concrete_fix)"} F2 -->|"No"| DROP2["filteredOut"] F2 -->|"Yes"| F3{"ALL required
predicates satisfied?"} F3 -->|"No"| DROP3["filteredOut"] F3 -->|"Yes"| F4{"ANY contraindicated
predicate?"} F4 -->|"Yes"| DROP4["filteredOut"] F4 -->|"No"| RANK["Rank: concrete_fix > ab_test >
investigation / monitor"] RANK --> WHITELIST["selected[] templateKeys"] WHITELIST --> FB{"Empty?"} FB -->|"Yes"| GENERIC["monitor-persistent-weak-signal"] FB -->|"No"| OUT["PlannerResult →
allowedTemplateKeys"] GENERIC --> OUT style WHITELIST fill:#c8e6c9 style GENERIC fill:#fff9c4

Playbook precedence

17 playbooks (8 groups) are walked in group order; the first matching, non-suppressed playbook drives the rank-1 hypothesis. See the full playbook catalog →

flowchart LR
    G1["1 · measurement"] --> G2["2 · technical
performance · js_error · dead_click"] G2 --> G3["3 · interaction
rage_click · form_anxiety"] G3 --> G4["4 · discovery
search_opportunity"] G4 --> G5["5 · merchandising
barrier · variant_friction"] G5 --> G6["6 · trust
checkout_anxiety"] G6 --> G7["7 · scope
mobile_experience"] G7 --> G8["8 · fallback
funnel_leak"]

Confidence — MIN is the bottleneck

Six independent factors; user-facing confidence is the minimum, so the weakest dimension is surfaced rather than averaged away.

flowchart TD
    F1["statisticalStrength"]
    F2["evidenceCorroboration"]
    F3["specificity"]
    F4["dataQuality"]
    F5["alternativeExplanations"]
    F6["actionability"]
    F1 & F2 & F3 & F4 & F5 & F6 --> MIN["userFacingConfidence
= MIN(F1..F6)"] MIN --> BOTTLE["Bottleneck = lowest factor
(shown in UI)"] MIN --> CLS{"Classification from rank-1"} CLS -->|"matchedStronger ≥ 2
softContrary = 0
confidence ≥ 0.8"| CONFIRMED["confirmed_issue"] CLS -->|"otherwise"| STRONG["strong_hypothesis"] style MIN fill:#c8e6c9 style CONFIRMED fill:#81c784