{ "meta": { "name": "Consultation AI suggestion — logic source of truth", "version": "1.1.0", "generated_for": "Superhairpieces branch portal, hairstylist form", "note": "This file is the ONLY place these rules are defined. The prototype HTML and the markdown spec are both generated from it. If you change a rule, change it here and regenerate." }, "new_questions": [ { "id": "hair_length", "section": "Hair & Scalp Assessment", "label": "Length Wanted On Top", "required": true, "impact_pct": 100, "why": "Over 6 inches is a custom order; below about 1.5 inches the base shows. Without it every consultation silently assumes 3-6 inches.", "options": [ "short", "medium", "long", "very long" ], "maps_to": "hairLength", "engine_values": [ 0, 1, 2, 3 ], "option_labels": [ "Short — about 1.5–2″", "3″–6″ (standard)", "Longer than 6″", "Very long — 12″ and up" ], "note": "Replaces the old top_length question and reuses the existing hair_length column with a new option set. Values are stored as the slugs above, not as the labels. The engine treats a hair_length written before SHP_CONFIG.hairLengthCutover as unanswered — set that date to the deploy date." }, { "id": "remaining_thickness", "section": "Hair & Scalp Assessment", "label": "Thickness Of Remaining Hair", "required": true, "impact_pct": 76, "why": "Volume, not haircut. Baldness level says how much is gone; this says what the system blends into.", "options": [ "Still thick and full", "Average", "Fine, or thinning too" ], "maps_to": "q4", "engine_values": [ 0, 1, 2 ] }, { "id": "hairline_back", "section": "Hair & Scalp Assessment", "label": "Does The Client Wear The Front Pushed Back?", "required": true, "impact_pct": 66, "why": "Decides whether a base with a visible front edge is viable. Often the difference between three months of wear and one.", "options": [ "Pushed back, forehead exposed", "Sometimes", "Falls forward", "Not settled yet" ], "maps_to": "q2", "engine_values": [ 0, 1, 2, 3 ] }, { "id": "side_length", "section": "Hair & Scalp Assessment", "label": "Hair At The Sides And Back", "required": false, "impact_pct": 39, "why": "Under 1 inch there is nothing for clips to grip, and a thick poly edge would show. Geeta's rule, Aug 2026.", "options": [ "Shaved, faded or under 1″", "1″ or more" ], "maps_to": "sideStyle", "engine_values": [ 0, 1 ] }, { "id": "area_size", "section": "Measurements", "label": "Coverage Area — Palm Test", "required": false, "channel": "online", "impact_pct": null, "why": "Online consultants cannot measure — 49% of online consultations have no measurement at all. This substitutes for it.", "options": [ "Palm alone covers it", "Whole hand, fingers spread", "Spreads past the hand", "Well past — most of the top" ], "maps_to": "areaSize", "engine_values": [ "small", "standard", "large", "large" ] } ], "existing_fields_used": [ { "id": "hairpiece_type", "fill_pct": 100, "maps_to": "q8 (coverage route)" }, { "id": "baldness_level", "fill_pct": 67, "maps_to": "areaSize fallback" }, { "id": "density", "fill_pct": 95, "maps_to": "qDensity" }, { "id": "hair_texture", "fill_pct": 95, "maps_to": "texture" }, { "id": "scalp_condition", "fill_pct": 85, "maps_to": "q3 (heat) + sweat rule" }, { "id": "sweats_from_scalp_often", "fill_pct": 94, "maps_to": "hard rule: no skin bases" }, { "id": "priority", "fill_pct": 80, "maps_to": "q1 — highest weighted input (x3)" }, { "id": "preferred_maintenance_location", "fill_pct": 95, "maps_to": "q6" }, { "id": "has_worn_hair_system_before", "fill_pct": 99, "maps_to": "q5 with previous_hair_system_experience" }, { "id": "previous_hair_system_experience", "fill_pct": 30, "maps_to": "q5 tier — 'By Myself' unlocks delicate bases" }, { "id": "base_measure_width", "fill_pct": 69, "maps_to": "areaSize (preferred over palm test)" }, { "id": "base_measure_length", "fill_pct": 69, "maps_to": "areaSize" }, { "id": "base_type", "fill_pct": 3.2, "maps_to": "hard constraint on base family when answered" } ], "existing_fields_collected_but_unused": [ { "id": "company_and_product_from_previous_hair_system", "fill_pct": 25, "opportunity": "What a switching client wore before — the strongest signal available for competitor switchers." }, { "id": "wants_similar_product_to_worn_before", "fill_pct": 23, "opportunity": "A direct statement of intent the suggestion never sees." }, { "id": "hairstylist_internal_note", "fill_pct": 50, "opportunity": "The stylist's actual reasoning, in their own words." }, { "id": "wants_patch_test", "fill_pct": 16, "opportunity": "Implies suspected adhesive sensitivity." } ], "rules": [ { "id": "R-SWEAT", "name": "Sweaty scalp excludes skin bases", "source": "Team ruling", "when": "sweats_from_scalp_often = 1 OR scalp_condition includes 'sweaty'", "then": "Remove every SKIN-family product from the suggestion. If all picks were skin, substitute the hybrid fallback set.", "note": "47% of men's consultations record sweating. Skin traps heat and the bond fails early." }, { "id": "R-BASE", "name": "A known base type constrains the family", "source": "Mehmet, Sept 2026", "when": "base_type answered and not 'not sure'", "then": "Suggest only within that family. Clients arriving from another company often already know what worked.", "note": "'not sure' is the most common answer and is ignored." }, { "id": "R-CONFLICT", "name": "Sweat beats stated preference", "source": "Derived from R-SWEAT + R-BASE", "when": "base_type = 'poly/skin' AND client sweats", "then": "R-SWEAT wins — suggest lace or hybrid, and say why in the basis line.", "note": "Honouring the request would sell a bond that fails. Change this only if the team decides otherwise." }, { "id": "R-EXPERIENCE", "name": "Experience tier gates the delicate bases", "source": "Quiz engine (Cindy, Geeta)", "when": "has_worn_hair_system_before = 1", "then": "previous_hair_system_experience = 'By Myself' -> tier 2 (experienced, unlocks delicate). Anything else -> tier 1 (still gated).", "note": "BUG FIXED Sept 2026: every returning client was previously mapped to tier 1 and never saw the delicate tier. Changes the answer in 47% of cases." }, { "id": "R-SIZE", "name": "Order of trust for sizing", "source": "Engine", "when": "always", "then": "Measured base width+length > palm test > inferred from baldness level.", "note": "So the palm test only applies when no measurement was taken — which is the online case." }, { "id": "R-INCOMPLETE", "name": "Refuse to guess on a thin form", "source": "Design decision", "when": "any of: priority, density, hairpiece_type, baldness_level, hair_texture, hair_length, remaining_thickness missing", "then": "Show no suggestion. Name the missing fields instead.", "note": "Half of men's consultations are missing a core field. A confident suggestion on a half-filled form is worse than none." }, { "id": "R-THREE", "name": "Always suggest three, never one", "source": "Measured", "when": "always", "then": "Return up to 3 ranked products. Never a single confident pick.", "note": "Consultants agree with each other only ~20% of the time on identical client profiles. Cross-validated ceiling is 19% top-1, 36% top-3." }, { "id": "R-LOG", "name": "Log every acceptance and override", "source": "Design decision", "when": "stylist adds a suggestion, removes one, searches the catalogue, rates the suggestions up or down, or saves", "then": "POST the entry. Actions: accepted, unaccepted, picked_manually, searched_manually, opened_catalogue, rated (up/down with optional reason chip and comment), saved (with suggested vs chosen and what was overridden). A thumbs-up is recorded too — agreement is signal the override log alone cannot show.", "note": "The disagreement between suggestion and final choice is the most valuable output of this feature." } ], "channel_behaviour": [ { "channel": "online", "location_value": "Online", "hide": [ "base_measure_width", "base_measure_length" ], "show": [ "area_size" ], "require": [ "photos" ], "why": "Base measurement fill is 49% online vs 80% in store — consultants are being asked for something they cannot take." }, { "channel": "in_store", "location_value": "any branch address", "hide": [ "area_size" ], "show": [ "base_measure_width", "base_measure_length" ], "require": [ "base_measure_width", "base_measure_length", "baldness_level", "hairstylist_internal_note" ], "why": "In store fills measurement and baldness level well. The internal note is filled 35% in store vs 76% online — that knowledge is being lost." } ], "integration": { "module": "shp-suggest-core.js", "adapter": "SHP_FIELDS at the top of the module — one line per field, mapping form values to the engine.", "api": [ "shpSuggest(formValues) -> { ok, picks[3] | custom, missing[], why }", "shpRenderBlock(mountEl, formValues, onAdd) — renders the panel", "shpLogFinalChoice(formValues, chosenSkus) -> log entry to POST", "shpSetGallery(fn) — optional, supply full product image sets by SKU" ], "engine": "Same engine as the customer quiz, so the portal and the storefront cannot contradict each other.", "note_field_rename": "The form field 'Discussed hair system(s)' is renamed 'Your recommendation'. Suggestions are added before the conversation happens, so 'discussed' would overstate what took place, and the new name does not claim one either. It also answers the panel above it: the model suggests, the stylist recommends. Products added from a suggestion carry an AI badge on the chip, so the record shows how much of the final choice came from the tool." }, "known_gaps": [ "Colour and stock: the suggestion can recommend a product unavailable in the client's shade. Needs an inventory feed, not a question.", "Custom orders: the tool says THAT something is custom, not what to build. No workshop rules are encoded.", "Women's: the matrix is men's only. ~15% of consultations are not.", "Baldness level is a TWO-STEP picker: 3 groups (Advanced/Noticeable/Early Thinning) opening 7 Norwood levels (assets 1- to 7-.png). The artwork currently served is the WOMEN'S set; the men's files should replace it at the same paths." ], "deliberately_not_wired": [ { "id": "has_allergies_or_sensitivities", "fill_pct": 94, "reason": "Stylist reads it and applies judgment. Adhesive reactions are a conversation, not a filter — the stylist decides attachment with the client in front of them. (Mehmet, Sept 2026)" }, { "id": "measure_method", "fill_pct": 96, "reason": "Context for the stylist, not the engine. They already know how reliable their own measurement is." }, { "id": "other_goals", "fill_pct": 14, "reason": "Free text. Read by the stylist during the consultation; not parsed." } ] }