SDD-047 - Gull, Customer Understanding & Journey Intelligence
Thiết kế cho PRD-007. Nguồn: SRC-1094. Mọi quyết định dưới đây dựa trên Đợt 16 (Q-183..Q-192) trong open-questions, chủ dự án chốt 27.09.2026.
1. Phạm vi và ba ràng buộc
| Ràng buộc | Nguồn | Hệ quả thiết kế |
|---|---|---|
| Công khai, không đăng nhập | Q-184 | Route API nằm dưới /v1/public/gull/*, khai trong authCoverage.test.ts; không cookie, không phiên |
| Không có tính năng sửa; chủ dự án sửa DB bằng script | Q-187 | API chỉ có GET. Đường ghi duy nhất là scripts/gull-build-seed.mjs + workflow seed-data.yml (§5) |
UI bê nguyên từ learn.nemo12.com/ielts | Q-192 | Nền .n12-deep chuyển sang packages/design-system/deep.css, learn và Gull cùng import; NavBar/PageBody có canonical ở packages/design-system/ui/DeepChrome.tsx (§6) |
Dữ liệu là archetype và bằng chứng nghiên cứu, không có dữ liệu cá nhân của learner (Q-191): MVP không đọc bảng nào ngoài gull_*.
2. Kiến trúc
content/gull/knowledge.json ──(scripts/gull-build-seed.mjs: kiểm luật)──▶ scripts/seed-gull.sql
│ workflow seed-data
▼
gull.nemo12.com (apps/gull, SPA tĩnh) ──GET──▶ api.nemo12.com/v1/public/gull/* ──▶ D1 nemo12-platform, bảng gull_*- App
apps/gull(workernemo12-gull, custom domaingull.nemo12.com): React + Tailwind v4- shadcn + Motion, đúng bộ ba SRC-650. Không có worker script, chỉ static assets.
- API
workers/api/src/modules/gull/routes.ts: hai route GET. - D1: dùng chung
nemo12-platform, bảng tiền tốgull_(Q-190), migration0306.
3. Mô hình dữ liệu (migration 0306)
3.1 Bốn bảng
| Bảng | Vai trò |
|---|---|
gull_entities | Mọi thực thể của 12 loại (kind). Trường chung là cột; trường riêng từng loại là JSON fields |
gull_links | Quan hệ nhiều-nhiều (from_id, to_id, rel, program_id), có position cho thứ tự và note |
gull_program_context | Phần khác biệt của một Global Entity trong một Program (PRD-007 §4.3) |
gull_history | Bản cũ của thực thể, do trigger ghi trước mỗi UPDATE (chỉ khi nội dung đổi) và DELETE |
Vì sao một bảng cho 12 loại: chúng chung toàn bộ trường quản trị (status, owner, version, confidence, generated_by, human_verified_at…), và giá trị của Gull nằm ở quan hệ giữa các loại. Mười hai bảng là mười hai bộ cột quản trị phải giữ khớp nhau và mười hai lần JOIN để đi một mạch Persona → Evidence.
3.2 Quan hệ (rel)
| rel | Chiều |
|---|---|
has_stakeholder, has_persona, in_program | program ↔ stakeholder ↔ persona |
has_job, has_need, has_pain | persona/jtbd/stage/step → jtbd/need/pain |
for_persona, for_stakeholder, in_program | journey → persona/stakeholder/program |
contains (có position) | journey → stage → step |
at_touchpoint | step → touchpoint |
supports, contradicts | evidence → bất kỳ |
leads_to | pain/need → opportunity |
Client đọc quan hệ hai chiều, nên người ghi nối theo chiều nào cũng được; chỉ contains và supports/contradicts có nghĩa theo chiều.
3.3 Trạng thái (Q-189)
Một trục: hypothesis → draft → evidence_backed → validated → deprecated. "Reviewed" không phải trạng thái mà là cột reviewer + dòng lịch sử. Không xoá cứng: thực thể biến khỏi JSON thì seed chuyển nó sang deprecated.
4. Phép nhìn trên đồ thị (client)
GET /v1/public/gull/graph trả cả đồ thị (thực thể, quan hệ, context) trong một lượt, cache 60 giây. Kho vài trăm dòng nhỏ hơn một bài đọc IELTS; mọi màn là hàm thuần trong apps/gull/src/graph.ts, test bằng graph.test.ts trên chính dữ liệu seed.
| Hàm | Màn | Định nghĩa |
|---|---|---|
journeyTree | Journey Detail (F4) | stage theo position, step theo position, kèm pain/need/touchpoint/evidence và opportunity qua pain |
journeyMatrix | Journey Matrix (F3) | hàng = journey; cột = TÊN chặng (chuẩn hoá chữ thường) nên chặng cùng tên thẳng cột; thứ tự cột = vị trí tương đối trung bình |
programs(id) | mọi màn | chương trình trực tiếp, qua context, qua program_id của quan hệ, và kế thừa NGƯỢC về phía chủ (step → stage → journey → program). Không đi xuôi từ Evidence |
northStar | Dashboard | tri thức quan trọng (cờ important hoặc persona/jtbd/pain/journey) đồng thời có cấu trúc, có nối, còn mới (≤ 180 ngày) và có Evidence supports, không phải hypothesis |
painConcentration | Dashboard (F12) | step xếp theo tổng fields.severity của pain gắn vào |
researchGaps | Dashboard (F12) | xương sống chưa có Evidence; stage rỗng; persona chưa có JTBD |
crossProgram | Dashboard (F12) | pain/jtbd/need thuộc ≥ 2 chương trình |
search | Library (F10) | theo từ, bỏ dấu tiếng Việt, mọi từ phải có; khớp tên nặng gấp ba khớp thân |
5. Đường sửa dữ liệu (Q-187)
# 1. sửa content/gull/knowledge.json
node scripts/gull-build-seed.mjs # kiểm luật, ghi scripts/seed-gull.sql
# 2. commit cả hai file, push, gộp vào main
gh workflow run seed-data.yml -f file=scripts/seed-gull.sql \
-f verify="SELECT kind, COUNT(*) FROM gull_entities GROUP BY kind"Script chặn (và npm run check:code chạy --check nên CI cũng chặn):
- Pain
importantkhông phải hypothesis mà không có Evidencesupports(PRD-007 §4.4). evidence_backedmà không có Evidencesupports.- Tri thức
generated_by: "ai:*"ởvalidatedmà chưa cóhuman_verified_at(§19 của mô tả gốc). - id không phải slug tiếng Anh, quan hệ trỏ vào id không có, em dash / en dash.
scripts/seed-gull.sqllệchknowledge.json(quên chạy lại script).
SQL sinh ra idempotent: ON CONFLICT DO UPDATE chỉ tăng version khi nội dung đổi thật, nên chạy lại cùng dữ liệu không đẻ phiên bản hay lịch sử. Quan hệ và context dựng lại trọn mỗi lượt.
6. Giao diện
- Nhãn giao diện tiếng Anh, nội dung người nhập giữ nguyên thứ tiếng của nó (Q-188; ngoại lệ thứ tư trong CLAUDE.md).
- Khung:
NavBar+PageBody(bản sao y hệtpackages/design-system/ui/DeepChrome.tsx, cũng làapps/learn/src/ielts/Chrome.tsx), vệt đường dẫn bắt đầu bằngGULLviết đậm nhưNEMO IELTS, thẻ lớn kiểuHubTile, nhãn tròn chữ hoa,Cardshadcn, preset MotionStagger/Liftable. - Năm dạng URL:
/(Dashboard),/library[/<kind>][?q=],/e/<id>,/matrix. Journey, Persona, JTBD, Evidence đều là/e/<id>; khung chung, thứ tự khối quan hệ theo loại. - Khối "Why do we believe this?" đứng ngay sau mô tả ở mọi thực thể; hypothesis có ghi chú viền đứt "Treat it as a question, not a fact".
- Change history mở theo yêu cầu (
GET /v1/public/gull/entities/:id/history).
6.1 Journey theo nhóm người dùng (SRC-1095)
Chủ dự án 27.09.2026: cần một chỗ liệt kê các journey chính của một nhóm người dùng, và trang chi tiết từng journey có tổng quan rồi các thành phần, mỗi thành phần một card, mỗi card một dòng.
Trang
/journeysgom journey theo cặp (chương trình, stakeholder): IELTS + Student là IELTS Learners. Journey khaifields.tier=primary(thẻ to, "Main") hoặcsecondary(thẻ nhỏ, "Supporting"); không khai thì vào "Other journeys". HàmjourneySegments,journeyStats.IELTS Learners có đúng 5 journey,
graph.test.tsgiữ con số này:Hạng Journey id Chính IELTS Test Taker Journey journey-ielts-preparation(giữ id cũ để link không gãy)Chính Learning Session Journey journey-ielts-learning-sessionPhụ Onboarding Journey journey-ielts-onboardingPhụ Diagnosis & Re-assessment Journey journey-ielts-diagnosisPhụ Recovery Journey journey-ielts-recoveryTrang journey (
/e/<id>): khối Overview (hạng, mục tiêu, bắt đầu khi, kết thúc khi, năm con số: chặng, bước, pain, touchpoint, % bằng chứng; người liên quan), rồi Components: mỗi chặng một card rộng hết dòng, bước của chặng là card con xếp dọc. Bố cục cột ngang kiểu journey map cũ bị bỏ: nó bắt cuộn ngang và làm chữ gãy vụn trong cột hẹp.Màn đầu 20%, ẩn 80% (SRC-1096, chủ dự án 28.09.2026: "nhìn như hiện nay thì siêu rối rắm"). Trang journey chỉ hiện: tên, một câu tóm tắt, mục tiêu, bốn con số, và danh sách chặng THU GỌN (một dòng: số, tên, bao nhiêu bước, bao nhiêu pain). Bấm Steps/Pains/Touchpoints thì hiện đúng danh sách ấy kèm nơi xuất hiện; bấm chặng mở mục tiêu và các bước; bấm bước mới thấy ai làm, làm gì, cảm xúc, kênh. Bằng chứng, người liên quan, quản trị, lịch sử nằm sau "More about this journey". Accordion shadcn. Một dòng trên danh sách giải thích Stage (một chặng) khác Step (một việc cụ thể trong chặng).
Tên chặng và tên bước viết tiếng Anh (chủ dự án 28.09.2026), đúng nhãn giao diện Q-188; mô tả và các trường chi tiết giữ tiếng Việt.
7. Để sau (đợt 2)
| Việc | REQ | Vì sao chưa |
|---|---|---|
| Tìm theo nghĩa bằng Vectorize | REQ-GUL-15 | Kho 128 thực thể thì tìm theo từ đã trả lời được; Vectorize cần tạo index trên production qua workflow |
| AI Research Assistant (Claude API) trích Persona/JTBD/Pain từ ghi chép phỏng vấn | REQ-GUL-13 | Không có giao diện sửa, nên AI sẽ chạy như một script sinh JSON có generated_by: "ai:<model>", qua đúng cổng §5 |
| AI Relationship Discovery (hỏi bằng lời) | REQ-GUL-14 | Cần REQ-GUL-15 trước |
| Nối Evidence từ dữ liệu thật (Dory, journey_events) | Q-191 | Chỉ số tổng hợp hoặc trích dẫn đã bỏ tên |
8. Kiểm chứng
| Cổng | Kiểm gì |
|---|---|
workers/api/src/modules/gull/routes.test.ts | đọc không cần phiên; không có POST/PUT/PATCH/DELETE; trigger lịch sử chỉ ghi khi đổi thật; xoá để lại vết; seed chạy hai lần trên schema thật không đổi version |
authCoverage.test.ts | hai route công khai được khai có lý do |
apps/gull/src/graph.test.ts | ví dụ IELTS PRD-007 §7 đi trọn một mạch; mẫu xuyên chương trình IELTS+SAT; ma trận gom cột; North Star giảm khi tri thức cũ; tìm kiếm bỏ dấu |
gull-build-seed.mjs --check trong check:code | năm luật §5 |
contrast-check.mjs | đọc packages/design-system/deep.css sau khi khối .n12-deep chuyển khỏi learn |