# Sales Trainer — Plan & Architecture App platform for Sales Training. Users develop **customer personas with pain points** from uploaded files + a natural-language description, then **chat with simulated customers** to practice closing a sale. Informed by two codebases: - **MiroFish** (`~/Gitea/MiroFish`) — original full-stack CrowdSight engine (Flask + Vue, OASIS, Zep, persona/report/chat). - **hermes-brain-and-tools** (`~/Gitea/hermes-brain-and-tools`) — clean-room CrowdSight plugin (seed → ontology → graph → environment; profile generator; durable workflow; report contract; social simulation). This app is a **standalone web app** (not a Hermes plugin) with its own **login/auth**, deployable via Dockerfile/EasyPanel (the user's established pattern). --- ## 1. Product goals 1. **Input**: setup form (product / customer segment / description) AND/OR upload files (`.pdf/.md/.txt`). Product info is used **primarily to extract pains** as the raw material for persona generation — it is just initial grounding. 2. **Analyze**: extract value propositions, features, pricing anchors, target profile, and an **initial pain-fit**. A generated persona is **reusable across products in the same/similar category** — it need not be locked to the exact uploaded product. 3. **Create persona pool**: generate 15 personas (5 × intent tier A/B/C), each marked with a buying tier, a set of pains (varied, not all product-solvable), negotiation levers, and an initiation mode (customer-initiated vs seller-initiated). Channel: Facebook / LINE. 4. **Standard report**: human-readable analysis report (per persona: background, pains, objections, price sensitivity, buying signals, revealable/latent fields) — downloadable. 5. **Sales Simulation (chat)**: trainee chats 1:1 with a persona (one-shot). The persona reacts realistically: negotiates, stalls, and **refuses to buy** unless the trainee actually resolves the persona's specific pain(s). On a result, the app reveals the pain and **why the close succeeded/failed** (short debrief + coaching). 6. **Training loop**: track each user's wins/losses; analyze the personas a user tends to lose against and **generate new harder/variant personas** (or manual form) to keep training. --- ## 2. Persona design (core requirement) ### 2.1 Intent tiers (2–3 levels) — REQUIRED | Tier | Name | Behavior | |------|------|----------| | **A** | **Ready-to-buy** (ตั้งใจซื้อ) | Has budget + authority + urgency. Short close window, but still expects the seller to confirm fit & handle 1–2 objections. Won't buy if the offer clearly misses their need. | | **B** | **Unsure / educating** (ไม่แน่ใจ) | Researching/considering. Needs discovery, trust-building, proof, comparison, and a clear reason to act now. High chance to stall or go silent. | | **C** | **Not interested but has pain** (ไม่สนใจแต่มี pain) | Unaware of the category/value, budget-constrained, or skeptical. Strong resistance, but has a real, unresolved pain — the ONLY path to a close is surfacing and resolving that pain. | ### 2.1b Tier counts - **5 personas per tier** → **15 personas minimum** per project (3 tiers × 5). - Each persona is a distinct, realistic individual — no two share the same background/income/personality combination. ### 2.2 Persona variety (REQUIRED) — every persona gets: - **Background** (life story / situation that justifies their behavior) - **Income & occupation** (varied across personas: salary, e.g. employee / freelancer / SME owner / executive / student / homemaker) - **Age group & lifestyle** (varied: age ranges, family stage, living situation, habits) - **Personality & temperament** (e.g. skeptical, analytical, impulsive, cautious, price-haggler, relationship-driven, impatient, distrustful) - **Communication style** (tone, vocabulary, formality, emoji usage, sentence length) - **Goal** when entering the chat + **objections** + **budget** + **decision timeline** - **Pains** (1–4), each with: description, root need, and **the specific condition(s)** the seller must satisfy to resolve it. **Consistency rule:** persona demographics (age band, occupation, lifestyle, income) must be **consistent with the product's target user** (from the sales kit / product data), and the **additional description / scenario field acts as a framing constraint** for persona creation (e.g. "product targets SME restaurants in Bangkok" → all 15 personas fit that world, with sensible income/lifestyle spread). ### 2.3 Realism rules (from user) - Customers **never buy easily**. Every close requires "earning" it. - **All tiers can lose — including Ready-to-buy (A).** A customer walks away (no sale) if the conversation is genuinely bad — e.g. rude/aggressive language, ignoring their needs, pushy pitching. Even a customer who desperately wants the product will refuse if the seller violates basic decency or trust. "Wanting it" never overrides "bad experience." - They **negotiate** (price, timeline, scope, add-ons). - They **refuse / end the chat** if the seller fails to address their pain. - Each persona has hidden pains the seller must **discover** (ask questions), not just pitch. ### 2.3a Negotiation — ALL tiers (REQUIRED) Every persona bargains over **concessions/benefits**, at every tier: - **Price reduction** (discount ask), - **Freebies / add-ons / bundling**, - **Delivery / fulfillment timeline** (especially for made-to-order / production goods), - Scope adjustments, payment terms, guarantees, etc. The seller must respond to negotiation realistically — give value in exchange, not just give in. The specific negotiable leverage each persona will push on is defined in the persona card. ### 2.3b Pain variety (REQUIRED) - **NOT every pain aligns with the product.** Persona pains may be: - **Directly solvable** by the product (the clear win), - **Partially solvable / nearby** (the product helps but doesn't fully close it — seller must manage expectation / bundle / reframe), or - **Unrelated/unsolvable** by this product (a genuine red herring — seller must recognize it and redirect rather than force a fit). - This mirrors reality: real customers rarely have exactly the pain a product solves. The seller must **diagnose which pain is which** and only claim what the product truly delivers. ### 2.3c Chat initiation — TWO modes (REQUIRED) The simulation supports **two initiation modes**, set per persona (a persona is either one or the other): - **Customer-initiated** (ลูกค้าทักก่อน): for products/brands marketed to drive engagement (e.g. cars, food, retail). The customer opens the chat; the seller responds. - **Seller-initiated** (ฝ่ายเราเปิดการขายก่อน): for products/services sold proactively / outbound (e.g. insurance, B2B services). **The customer does NOT message first — the seller must open the sale.** The simulator gives the seller an opening task to start the conversation with a cold/warm lead, and the persona reacts accordingly. Each persona card declares which mode applies (and, for customer-initiated, includes the persona's opener; for seller-initiated, the persona's initial mood/receptiveness). > Note: this supersedes any earlier "customer always messages first" rule. Initiation is > per-case: some personas are customer-initiated, some are seller-initiated. ### 2.3d Special tier-3 case — "wrong text / lost interest" (REQUIRED, ≥1 in tier C) - At least **one tier-C persona** is designed as a **false lead**: their opening message makes them *appear* ready to buy (e.g. "I need this, tell me the price"). But once the seller responds, the persona immediately reveals lost interest and wants to **end the conversation** ("never mind, forget it") — yet **deep down the pain still exists**. - To close: the seller must not take the "forget it" at face value; they must gently re-engage, rebuild a moment of connection, and surface the still-live pain without being pushy. High difficulty, frequent refusal — it tests resilience + empathy + non-pushy discovery. ### 2.3e Channel context (REQUIRED) - The simulated chat is presented as a real messaging thread. Primary channels: **Facebook** and **LINE**. A persona/case declares which channel it happens on (affects the look, and can color tone — e.g. LINE more casual, FB page vs Messenger). - Other channels optional later; v1 ships Facebook + LINE. ### 2.4 Persona lifecycle & one-shot rule (REQUIRED) - **1 persona ↔ many users**: a persona is a shared playable asset; any user who has not yet chatted it may practice on it. - **1 user ↔ 1 chat per persona max (one-shot)**: a user can chat a given persona **only once** — the outcome is final and that persona is "used" for that user. It cannot be re-chatted/replayed by the same user. (Reels/retry against it is not allowed.) - To practice again on a similar customer, the user must **generate a new persona** (2.5). ### 2.5 Persona sources & generation (REQUIRED) - **Admin-created baseline pool**: admin/upload-generated personas form the shared pool that **new users** can pick from and practice on. - **User-generated personas**: after practicing, a user can generate **their own persona** to train on, in two ways: 1. **Weak-area generation** (4 in feature list): auto-analyze the personas this user tends to lose against → generate a new, harder/variant persona targeting that weakness, as a "lock" to overcome it; **or** 2. **Manual form**: the user describes the persona they want to practice (target profile, situation, difficulty level) and the system generates it. - User-generated personas are private to that user (unlike the admin pool). ### 2.6 Data exposure by view (REQUIRED) - **Approve/edit view (admin)**: full persona data — pains, income, personality, negotiation levers, hidden details. Admin sees everything to review/edit/approve. - **Select/chat view (trainee)**: **only** the persona's name + basic info one would plausibly know up front (profession, age group, channel, initiation mode, product context). **Hidden**: pain, income, personality, budget, negotiation levers — anything you couldn't know without talking. Revealed only **after** the conversation ends (win/lose + debrief). - **Win/lose status**: the user can always see which personas they've **won** vs **lost** vs **not yet tried**. --- ## 3. App architecture ``` ┌────────────────────────────────────────────────────────────┐ │ Vue 3 Frontend (port 3000) │ │ Login · Dashboard · Project setup · Report · Chat view │ └───────────────▲────────────────────────────┬───────────────┘ │ HTTP/JSON (JWT) │ ┌───────────────┴────────────────────────────▼───────────────┐ │ Flask Backend (port 5001) │ │ │ │ auth/ → register, login, JWT, per-user isolation, ROLES │ │ api/ → projects, uploads, personas, report, chat, groups │ │ services/ → analyzer · persona generator │ │ → sales_kit (product facts + initial pain-fit) │ │ → report builder │ │ → sales_simulator (chat engine) │ │ storage/ → filesystem JSON per user (no SQL DB) │ │ llm_client/ → OpenAI/DeepSeek/custom OpenAI-compatible calls │ └────────────────────────────────────────────────────────────────────┘ ``` ### 3.0 Roles & permissions (multi-user corporate) | Role | Can do | |------|--------| | **Super Admin** | Manage all users, assign roles, manage org-level persona groups, view all data + analytics. | | **Admin** (Manager) | Create/edit/delete **persona groups** (define a product/offer + its personas), **manually edit any persona**, manage users in their scope, view analytics. | | **User** (trainee) | **Cannot create groups.** Only **selects an existing persona group** and practices (chat) on its personas; sees own results. | - Org model: `organization` → `users` → `persona_groups` → `projects`/`sessions`. - **Persona group** = a reusable packaged scenario: product definition (sales kit) + the 15 personas generated for it. Admins build groups **and may edit/re-analyze them later** (groups are editable; regenerate is allowed; **admin can hand-edit any persona field**). - A trainee's "project" is a **training session** bound to a group + a chosen persona. ### 3.0b Registration (confirmed) - **No self-registration.** Admin creates users and sends invites (email/account-creation). Only Super Admin and Admin can provision accounts. Roles: Super Admin / Admin / User (no Trainer). - **Repo root**: `~/Gitea/Sales Trainer/` - **Stack**: Flask 3 + Vue 3 (Vite) + JWT auth + filesystem JSON persistence + OpenAI-compatible LLM. - **LLM**: reuse the provider-agnostic pattern from MiroFish (`llm_client.py`): configurable `LLM_PROVIDER` / `LLM_BASE_URL` / `LLM_MODEL_NAME` / `LLM_API_KEY` via `.env`. Supports **OpenAI, DeepSeek, or any OpenAI-compatible custom model** (base URL + model name overridable). - **Persistence**: per-user `data//projects//…` JSON (mirrors MiroFish/Hermes durable file approach). No external DB needed for v1. ### 3.1 Data flow 1. **Setup (admin)** → create a persona group: form (product/segment/description) + upload files (+ channel & initiation-mode preferences). 2. **Analyze** (background, async) → - **Sales Kit** extracted from input (what we sell, features, pricing, value prop, target, use cases) **mainly for pain extraction** (reusable across same/similar category); - **Personas** generated (15 personas w/ tier, pains, background, negotiation levers, initiation mode, channel, latent/revealable fields). 3. **Approve/Edit (admin)** → review full persona data; edit any field; approve pool. 4. **Report** (background, async) → structured report assembled from personas (downloadable; full data). 5. **Chat** → a trainee picks a persona (one-shot) → real-time conversational close attempt, per the persona's initiation mode. - Each message: persona responds; the simulator also returns an internal **state update** (tier, pain-resolution progress, trust, buying-signal flags) — kept hidden. - On **close** or **refusal**, the chat terminates with a debrief that reveals latent fields. 6. **Training loop** → record win/lose; weak-area analysis; generate-new-persona (lock or manual). --- ## 4. Services detail ### 4.1 `analyzer` (Sales Kit extraction + initial pain-fit) Input: **setup form** with: - **Product** (what it is) — required, - **Initial customer segment** (optional), - **Additional description / scenario** (optional framing for persona creation), - **OR upload files** (`.pdf/.md/.txt`) carrying all of this — user may skip the form entirely. **Decision logic (clear/simple):** if files are uploaded, parse them for product + target + pains; if the form is also filled, the **form's explicit fields win** and file text fills the gaps (and is still analyzed for extra context / pain-fit). If only files → derive everything from files. If only the form → use the form. Output (JSON): `productName`, `category`, `valueProps[]`, `features[]`, `pricing` (budget anchors), `targetAudience` (incl. segment from form), `useCases[]`, `competitors[]`, `objectionHandlers[]`, and — critically — **`initialPainFit[]`**: the analyzer's first-pass judgement of **which pains the product can plausibly solve** (with evidence/claims from the inputs), so persona pains can be built partly against and partly away from this baseline. `scenario`/description is preserved as a **framing constraint** passed to persona generation. This grounds all persona + chat generation so the simulation stays on-product. ### 4.2 `persona_generator` Builds the **15 personas (5 × tier A/B/C)** for a given persona group. Prompt engineered to enforce: - exactly 5 personas per tier; - the required variety fields (background, income/occupation, personality, communication style, goals, budget, timeline, objections); - **pain variety** (2.3b): not every pain is product-solvable — include directly-solvable, partially-solvable, and unrelated pains; use `initialPainFit` as the baseline; - **the tier-C "wrong text / lost interest" special persona** (2.3d) — at least 1; - **chat opener** for every persona + whether they initiate friendly/blunt/indifferent; - **initiation mode** (customer-initiated vs seller-initiated) and **channel** (facebook/line); - **latent vs revealable fields** (2.6) so the UI can hide what a real seller wouldn't know; - JSON output schema (strict), language follows project `language` (en/th). ### 4.2b `weak_area_analyzer` (win/loss insight → new persona) - Tracks each user's per-persona outcomes (won/lost) and their scores. - On request, analyzes the user's **losses**: which tier / initiation mode / channel / pain-type / objection-type / negotiation-style they tend to lose against. - Produces (a) a **summary insight** ("you lose most against seller-initiated, price-hardball persona; you rarely handle discount + delivery-time pressure together"), and (b) a **spec** to **generate a new persona** targeting that weakness (as a 'lock' to overcome) — or the user can use the manual persona form instead. - A user can also see their **win/lose status board** (which personas won/lost/not-tried). ### 4.3 `sales_simulator` (chat engine) — the heart State machine per chat session: ``` READY → (customer or seller opens per mode) TALKING ⇄ (negotiating/objecting/thinking) → CLOSED | REFUSED | TIMEOUT ``` - **Initiation depends on the persona's mode (2.3c)**: - **customer-initiated**: the customer sends the first message; the seller responds. - **seller-initiated**: the customer does NOT message first — the simulator gives the seller an **opening task** ("open the sale"), and the seller must start the conversation; the persona then reacts as a cold/warm lead. - **Channels**: Facebook or LINE (2.3e) — affects presentation + some tone. - **One-shot (2.4)**: a user may not start a second session on a persona they've already finished (won or lost). Backend enforces it. - **Hidden/latent data (2.6)**: the trainee only ever sees revealable fields during the chat; pain / income / personality / budget / negotiation levers / opener are latent and hidden until the end. - **Hidden pain state**: each persona has hidden unresolved pains. The simulator tracks per-pain resolution. Pitching without discovery does NOT resolve pain. - **Pain-fit realism (2.3b)**: since not all pains are product-solvable, the simulator must let sellers mis-diagnose — claiming to solve an unrelated pain must backfire (trust down) or lead down a dead end, while the real (product-solvable) pain stays unresolved. - **Per message**: LLM plays the persona in-character (using persona card + sales kit + chat history + current internal state). Returns: - `reply` (the persona's in-character message), - `internal`: updated `{ trust, painProgress, buyingSignals, tier, mayRefuse }`. - **Close trigger**: the judge-LM decides if the seller has satisfied the pain-resolution conditions (visible+hidden) AND the persona verbally accepts the offer/price. Only then `CLOSED`. - **Refuse trigger**: if trust collapses, the seller pushes a hard pitch without addressing pain, or after N failed attempts → `REFUSED`. - **Negotiation**: personas actively counter (price, scope, timeline, freebies, delivery). Seller must handle these on top of resolving pain. - **Timeout/abandon**: persona goes silent if seller is repetitive/low-value. **Internal signals are NEVER shown live.** Trust/pain/buying meters stay hidden during the conversation (default) — the trainee reads the customer's words only. **Debrief (on CLOSED or REFUSED) — REQUIRED, shown only at conversation end:** reveal the latent fields (pain, income, personality, budget, negotiation levers, hidden opener), then: - Keep the summary **short**, then give **coaching**: for each message that hurt the score, suggest **how the seller should have responded** so the trainee understands and can improve. - **CLOSED**: brief note on the persona's `pain` (what it was) + why it closed. - **REFUSED**: the unaddressed pain(s), where trust was lost, which pain was mis-diagnosed (if any) — each with **a concrete "better reply" suggestion**. - A judge-LM (separate from the persona LLM) produces the score + coaching. - Toggle: admin can choose to show live meters **per persona group** if desired; default hidden. --- ## 5. API surface (v1) | Method | Path | Purpose | |--------|------|---------| | POST | `/api/auth/login` | JWT login | | GET | `/api/auth/me` | current user + role | | POST/PUT | `/api/admin/users` | admin: create users + invite, assign roles (super-admin/admin/user) | | GET | `/api/admin/users` | admin: list users | | POST | `/api/groups` | admin: create persona group (product files/description) | | GET | `/api/groups` | list persona groups visible to role (user: selectable only) | | GET | `/api/groups/` | get group + personas (admin: full; user: practice view) | | POST | `/api/groups//analyze` | admin: trigger analyze (sales kit + 15 personas) | | GET | `/api/groups//report` | get/download report | | GET | `/api/groups//personas` | list personas (grouped by tier; user sees revealable fields only) | | GET | `/api/groups//personas/` | persona card (admin: full; user: revealable only) | | PUT | `/api/groups//personas/` | admin: hand-edit a persona | | POST | `/api/groups//reanalyze` | admin: re-run analyze / regenerate personas | | POST | `/api/groups//personas//chat` | user: start/send message → persona reply + hidden state (one-shot enforced) | | GET | `/api/groups//personas//session` | user: session state/history | | GET/POST | `/api/sessions` | user: my training sessions + debriefs (own results) | | GET | `/api/sessions//debrief` | user: end-of-chat debrief (pain + reason + coaching + latent reveal) | | GET | `/api/me/board` | user: win/lose status board per persona (won/lost/not-tried) | | GET | `/api/me/weak-areas` | user: analyze which personas I tend to lose against (insight) | | POST | `/api/me/personas/generate` | user: generate own persona — body: {mode: "weak-area" \| "manual", ...spec} | | GET | `/api/me/personas` | user: list my generated (private) personas + status | | GET | `/api/analytics` | admin: aggregate trainee analytics (close rate, avg score, hardest personas) | All `/api/*` except login require `Authorization: Bearer `; data scoped by role + org. Admins call analyze/persona-edit endpoints; trainees read revealable fields + run one-shot sessions + generate their own personas. --- ## 6. Frontend views (Vue 3 + Vite) 1. **Login** (no self-registration — accounts created by admin) 2. **Dashboard** — (admin) persona groups + user mgmt; (user) selectable groups + my sessions 3. **Admin: Group Builder** — setup form (product/segment/description) + upload files; pick channel (Facebook/LINE) + initiation-mode mix; trigger analyze → approve/edit the 15 personas 4. **Admin: User Management** — no self-registration; create + invite users, set roles 5. **Personas (approve/edit, admin only)** — **full data** for all 15 personas; edit any field, approve 6. **Personas (select, user)** — win/lose status board + list of available personas; each card shows **only revealable info** (name, profession, age group, channel, initiation mode, product context) 7. **Report** — rendered analysis report + download (admin; full persona data) 8. **Simulation (chat)** — Facebook/LINE-style thread. **Internal signals + latent fields hidden** during chat. Initiation per mode: customer opens OR seller gets an "open the sale" task. **One-shot enforced.** On end → **debrief overlay**: reveal latent fields + short summary + coaching (how to improve weak-score replies) + pain + reason + score. Result saved; persona marked won/lost for this user. 9. **Gen persona (user)** — generate own persona: **weak-area** (from my loss analysis) or **manual form** (describe target persona). Private to the user. 10. **Weak-areas (user)** — insight: which personas I tend to lose against + generate-a-lock CTA 11. **Admin Analytics** — aggregate trainee results (close rate, avg score, hardest personas). i18n: en + th (mirrors MiroFish pattern). Role-based navigation (admin vs trainee). --- ## 7. Security, config, deploy - **Auth**: JWT (HS256) with password hashing (werkzeug `generate_password_hash`). Secrets in `.env`. No raw tokens/keys in UI. **No self-registration** — only admin-provisioned accounts. - **LLM credentials**: `.env` only, never shipped/logged. - **File safety**: upload allowed types + size caps; parse text server-side; strip anything executable; keep raw uploads out of any served path. - **LLM route discipline**: the chat + persona gen + judge are the only LLM-touching callers. - **Deploy**: single `Dockerfile` (python:3.11 + Node 18, build Vue → serve static via Flask or nginx) + `docker-compose.yml` with `.env`, per the user's EasyPanel pattern. Local tests via `http.server` / Flask dev (no Docker on local Mac). --- ## 8. Milestones (build order) — ALL COMPLETE ✅ - **M0 — Scaffold & auth**: repo, Flask app factory, JWT auth, roles (super-admin/admin/user), user + org store, **admin-user creation/invite (no self-registration)**. ✅ - **M1 — Input & analyze**: setup form (product/segment/description) + file upload/parse with precedence rule, sales-kit extraction + **initial pain-fit**, JSON storage. ✅ - **M2 — Persona groups & persona generation**: group model (editable/re-analyzeable); 15 personas (5 × tier) with variety, pain variety, negotiation levers, chat openers, **initiation mode (customer/seller)**, **channel (facebook/line)**, **latent vs revealable fields**, and the tier-C "wrong text" special case; **admin hand-edit persona endpoints**. ✅ - **M3 — Report**: assemble + render + download report. ✅ - **M4 — Chat simulation**: stateful persona chat (per-mode initiation: customer opens OR seller "open the sale" task), Facebook/LINE thread, negotiation, all-tiers-can-lose, close/refuse logic, **hidden signals + latent fields**, **one-shot enforcement**, **separate judge LLM** for scoring, **short debrief with coaching** (latent reveal + pain + reason + how to improve). ✅ - **M5 — Trainee loop**: win/lose status board, weak-area analysis, **user-generated personas** (weak-area "lock" + manual form), my-sessions. ✅ - **M6 — Frontend polish**: login, role-based dashboard, group builder (+ persona approve/edit UI), user mgmt, personas (select view), report, chat UI, debrief overlay, gen-persona, weak-areas, **admin analytics dashboard**, EN+TH i18n. ✅ (SPA served by Flask; verified live) - **M7 — Deploy & docs**: Dockerfile, docker-compose, README, engineering-handoff docs, E2E verification with honest status reporting. ✅ (mock-LLM E2E; real-key + Docker pending) > Status: prototype complete + verified with mock LLM. **Pending: real-LLM live smoke test > and remote Docker/EasyPanel validation** (see docs/HANDOFF.md). --- ## 9. Acceptance criteria 1. Multi-user corporate, **no self-registration**: super-admin/admin create + invite users; roles Super Admin / Admin / User (no Trainer). **User (trainee) cannot create** — selects a group + practices. 2. Setup accepts **form (product / segment / description) AND/OR uploaded files**, with a clear decision rule (form-wins, file-fills-gaps; files-only → derive all). 3. Analyzer produces sales kit **+ initial pain-fit**; persona demographics (age, occupation, lifestyle, income) are **varied AND consistent with product + scenario framing**. 4. → 15 personas (5 × tier A/B/C); **admin can edit any persona and re-analyze the group**. 5. Persona group has **≥1 tier-C "wrong text / lost interest" special persona**. 6. Report is human-readable + downloadable. 7. **All tiers can lose** — bad conversation (e.g. rude language) = no sale, even for ready-to-buy. 8. **All tiers negotiate** concessions (price / freebies / delivery timeline / scope / payment). 9. Initiation is **per-persona mode**: customer-initiated (customer opens) OR seller-initiated (seller gets an "open the sale" task). Channels **Facebook + LINE**. Internal signals + latent fields are **hidden** during chat; only revealable fields shown. 10. **One-shot rule**: a user can chat a persona only once (won/lost = final); the same persona stays playable for other users. 11. **Two persona views**: admin sees full data (approve/edit); trainee sees only revealable info (name, profession, age group, channel, initiation mode) — latent fields revealed only after result. 12. **Win/lose status board**; **weak-area analysis**; **user-generated personas** (weak-area "lock" OR manual form), private to the user. 13. Debrief is **short + coaches**: suggests how to improve on weak-score messages; reveals pain + reason. Scored by a **separate judge LLM**; **no speed factor** in scoring. 14. Product data is used primarily for **pain extraction**; personas are reusable across the same/similar product category. 15. **Admin analytics dashboard** aggregates trainee results (close rate, avg score, hardest personas). 16. Single `docker compose up` runs the whole app (or EasyPanel build); LLM pickable as OpenAI / DeepSeek / any OpenAI-compatible custom model via `.env`. 17. E2E tests pass; honest report of any blocked stages. --- ## 10. Confirmed decisions (all from user) - Standalone web app (Flask + Vue + JWT), Docker/EasyPanel deploy. - Multi-user corporate, **no self-registration** — Super Admin / Admin / User (no Trainer). Admin creates + invites users. - **Admin** builds/edits persona groups, **hand-edits any persona**, re-analyzes groups, sees analytics. - LLM: **OpenAI, DeepSeek, or custom OpenAI-compatible** (configurable via `.env`). - **5 personas per tier → 15 min per group.** - Persona variety: **age-group, occupation, lifestyle, income** — consistent with product + scenario framing. - Internal chat signals **hidden**, revealed at end; **short debrief + coaching** (how to answer better on weak-score messages). - **All tiers can lose**; **all tiers negotiate** concessions. - **Separate judge LLM** for close/refuse + scoring; **no speed factor** in scoring. - **Admin analytics dashboard** (aggregate trainee results). - UI **EN + TH**. - Product **user-input via form AND/OR uploaded file** with clear precedence; analyzer computes initial pain-fit. Product data mainly for **pain extraction** — personas reusable across same/similar category. - Personas have **pain variety** (not only product-solvable), demographically consistent with product. - **Initiation per persona**: customer-initiated OR seller-initiated (open-the-sale task). - **Channels**: Facebook + LINE. - **Two views**: admin full data; trainee revealable-only (latent hidden until result). - **One-shot rule**: 1 user = 1 chat per persona; persona shared across users. - **User-generated personas**: weak-area "lock" OR manual form (private). - **Win/lose board** + **weak-area analysis**. - **≥1 tier-C "wrong text / lost interest" persona.** ## 11. Remaining open questions (low-risk; defaults noted) - **Invite delivery**: email link to set password, or admin pre-sets a temporary password? (Default: admin sets temporary password on account creation; optional email later.) - **Analytics granularity**: just group-level aggregates, or drill-down per persona/trainee? (Default: group + per-persona close rate + avg score; per-trainee detail on request.) - **Scoring weights** (beyond dropping speed): pain 40 / trust 30 / objection-handling 30 ok? - **Persona edit UI**: full form for all fields, or JSON editor for power users? (Default: structured form for common fields + JSON for advanced.) _Plan is ready for build. Confirm the low-risk defaults above if you disagree, otherwise I begin M0._