Skip to content

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ộcNguồnHệ quả thiết kế
Công khai, không đăng nhậpQ-184Route 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 scriptQ-187API 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/ieltsQ-192Nề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 ​

text
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 (worker nemo12-gull, custom domain gull.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), migration 0306.

3. Mô hình dữ liệu (migration 0306) ​

3.1 Bốn bảng ​

BảngVai trò
gull_entitiesMọ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_linksQuan hệ nhiều-nhiều (from_id, to_id, rel, program_id), có position cho thứ tự và note
gull_program_contextPhần khác biệt của một Global Entity trong một Program (PRD-007 §4.3)
gull_historyBả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) ​

relChiều
has_stakeholder, has_persona, in_programprogram ↔ stakeholder ↔ persona
has_job, has_need, has_painpersona/jtbd/stage/step → jtbd/need/pain
for_persona, for_stakeholder, in_programjourney → persona/stakeholder/program
contains (có position)journey → stage → step
at_touchpointstep → touchpoint
supports, contradictsevidence → bất kỳ
leads_topain/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àmMànĐịnh nghĩa
journeyTreeJourney Detail (F4)stage theo position, step theo position, kèm pain/need/touchpoint/evidence và opportunity qua pain
journeyMatrixJourney 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ànchươ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
northStarDashboardtri 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
painConcentrationDashboard (F12)step xếp theo tổng fields.severity của pain gắn vào
researchGapsDashboard (F12)xương sống chưa có Evidence; stage rỗng; persona chưa có JTBD
crossProgramDashboard (F12)pain/jtbd/need thuộc ≥ 2 chương trình
searchLibrary (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) ​

bash
# 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):

  1. Pain important không phải hypothesis mà không có Evidence supports (PRD-007 §4.4).
  2. evidence_backed mà không có Evidence supports.
  3. Tri thức generated_by: "ai:*" ở validated mà chưa có human_verified_at (§19 của mô tả gốc).
  4. id không phải slug tiếng Anh, quan hệ trỏ vào id không có, em dash / en dash.
  5. scripts/seed-gull.sql lệch knowledge.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ệt packages/design-system/ui/DeepChrome.tsx, cũng là apps/learn/src/ielts/Chrome.tsx), vệt đường dẫn bắt đầu bằng GULL viết đậm như NEMO IELTS, thẻ lớn kiểu HubTile, nhãn tròn chữ hoa, Card shadcn, preset Motion Stagger/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 /journeys gom journey theo cặp (chương trình, stakeholder): IELTS + Student là IELTS Learners. Journey khai fields.tier = primary (thẻ to, "Main") hoặc secondary (thẻ nhỏ, "Supporting"); không khai thì vào "Other journeys". Hàm journeySegments, journeyStats.

  • IELTS Learners có đúng 5 journey, graph.test.ts giữ con số này:

    HạngJourneyid
    ChínhIELTS Test Taker Journeyjourney-ielts-preparation (giữ id cũ để link không gãy)
    ChínhLearning Session Journeyjourney-ielts-learning-session
    PhụOnboarding Journeyjourney-ielts-onboarding
    PhụDiagnosis & Re-assessment Journeyjourney-ielts-diagnosis
    PhụRecovery Journeyjourney-ielts-recovery
  • Trang 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ệcREQVì sao chưa
Tìm theo nghĩa bằng VectorizeREQ-GUL-15Kho 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ấnREQ-GUL-13Khô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-14Cần REQ-GUL-15 trước
Nối Evidence từ dữ liệu thật (Dory, journey_events)Q-191Chỉ số tổng hợp hoặc trích dẫn đã bỏ tên

8. Kiểm chứng ​

CổngKiể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.tshai route công khai được khai có lý do
apps/gull/src/graph.test.tsví 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:codenăm luật §5
contrast-check.mjsđọc packages/design-system/deep.css sau khi khối .n12-deep chuyển khỏi learn