---
url: https://docs.nemo12.com/product/visitor-state/model-and-speak.md
description: >-
  Sáu State, hai mươi Stage, luật chuyển, journey-summary từ learn, bố cục
  /speak, khu Yên tâm, hậu quả đầu trang và facts IELTS.
---

# State + Stage: mô hình, bằng chứng từ learn và thí điểm /speak

## Mô hình: 6 State, 20 Stage (SRC-1221)

> Chủ dự án 03.10.2026: "deploy 4 State now, upgrade to 6 later". Lượt SRC-1221 là lượt nâng ấy.
> Tên State và Stage hiển thị bằng tiếng Anh, có số thứ tự.

| State | Stage |
| --- | --- |
| 1. Discover (`discover`) | 1.1 Just arrived · 1.2 Comparing · 1.3 Checking price |
| 2. Consider (`consider`) | 2.1 Wants a trial · 2.2 Trial booked · 2.3 Trial done |
| 3. Start (`start`) | 3.1 Enrolled · 3.2 Starting point · 3.3 Has a plan |
| 4. Learning (`learning`) | 4.1 On plan · 4.2 Practicing · 4.3 Reviewing mistakes · 4.4 Checking progress |
| 5. Needs attention (`attention`) | 5.1 Falling behind · 5.2 Stuck · 5.3 Overloaded · 5.4 Thinking of stopping |
| 6. Ready (`ready`) | 6.1 Milestone reached · 6.2 Ready for the exam · 6.3 New goal |

Biến thể theo chương trình: NEMO SPEAK không có bài chẩn đoán nên bỏ 3.2 (3.3 Has a plan thành
3.2); NEMO WALK đọc 3.2 là "Quiz passed". Mỗi Stage có năm dòng Need, Main focus, Main action, How
you get here, What comes next (`STAGE_INFO`; IELTS ghi đè Focus và Action bằng `IELTS_STAGE_INFO`).

### Luật chuyển

* **Tự động chỉ đi tới.** Tín hiệu trên www chỉ sống trong State 1 và 2: mở trang học phí -> 1.3;
  mở lộ trình, chẩn đoán, chỉnh phiếu điểm, quay lại ngày thứ hai -> 1.2; bấm học thử hoặc đăng ký
  -> 2.1. Từ 2.2 trở đi chỉ bằng chứng từ learn (hoặc chính người dùng chọn) mới mở được.
* **Needs attention là ngoại lệ duy nhất**: vào và ra theo bằng chứng. learn cho biết 7 ngày trở
  lên không học -> 5.1 Falling behind, và lưu chỗ đang đứng (`prev`); learn thấy học lại (dưới 7
  ngày) -> quay về đúng chỗ ấy, hoặc xa hơn nếu learn cho thấy tiến bộ mới. Chưa từng ghi danh thì
  không bao giờ bị đưa vào State 5.
* **Chọn tay luôn được**, ở mọi State. Bằng chứng không bao giờ kéo người dùng lùi dưới Stage họ tự
  chọn; một Stage của State 5 chọn tay không có `prev` nên bằng chứng "học lại" không gỡ nó.
* Mỗi lần bằng chứng đổi Stage có một dòng nhật ký (`log`: thời điểm, lý do, từ, tới; 12 dòng gần
  nhất). Khung điều khiển ở đáy hiện dòng mới nhất ("Moved by evidence: ..."); thẻ IELTS hiện câu
  tiếng Việt tương ứng ("Vì learn cho biết bạn đã làm bài chẩn đoán").

| Bằng chứng từ learn | Tới |
| --- | --- |
| SPEAK `trialBooked` | 2.2 Trial booked |
| SPEAK `trialDone` | 2.3 Trial done |
| IELTS `enrolled`, SPEAK `enrolled` | 3.1 Enrolled |
| IELTS `diagnosticDone`, WALK `quizPassed` | 3.2 Starting point (WALK: Quiz passed) |
| IELTS `hasPlan` | 3.3 Has a plan |
| IELTS `hasPlan` và `daysInactive` < 7 | 4.1 On plan |
| IELTS `mistakesToReview` > 0 | 4.3 Reviewing mistakes |
| IELTS `daysInactive` >= 7 | 5.1 Falling behind (lưu chỗ cũ) |
| IELTS `milestoneReached` | 6.1 Milestone reached |

### Lưu trữ và chuyển đổi từ bản 4 State

* Khoá mới `n12.visitor.v2`, dạng `{ <chương trình>: { state, stage, manual, updated, prev?, log?, ielts? } }`.
  Mọi lần đọc ghi bọc `try/catch`; dữ liệu hỏng bị bỏ, không tin.
* Lần đọc đầu sau khi nâng: chưa có `v2` thì đọc `n12.visitor.v1` và đổi theo bảng dưới
  (`V1_STAGE_MAP`), giữ nguyên câu trả lời của learner model IELTS và đổi luôn tên Stage trong
  nhật ký của nó. Có `v2` rồi thì `v1` bị bỏ qua (không đổi hai lần).

| Cũ (4 State) | Mới |
| --- | --- |
| Just arrived, Exploring | 1.1 Just arrived |
| Comparing, Wants a plan | 1.2 Comparing |
| Checking price | 1.3 Checking price |
| Wants a trial, Has questions, Signing up | 2.1 Wants a trial |
| Studying | 4.1 On plan |
| Returning | 5.1 Falling behind (chỗ cũ: 4.1) |

"Wants a plan" gộp vào 1.2 vì xem lộ trình là một cách so sánh; lộ trình của chính learner nay là
module của 3.3. "Has questions" gộp vào State 2: 2.1 có câu hỏi và nút hỏi qua email, 2.2 có các
câu nên hỏi trong buổi thử.

* Bản dựng sẵn (prerender) luôn ở 1.1; trạng thái riêng chỉ áp sau khi React chạy, nên SEO và bản
  tĩnh không đổi.

Mã: `apps/web/src/site/visitorState.ts` (thuần, có test), `journeySummary.ts` (bằng chứng learn,
thuần + fetch), `VisitorPanel.tsx` (store, `LearnEvidenceSync`, điều khiển ở đáy), `speakFocus.ts`,
`SpeakPage.tsx`, `ieltsModel.ts`, `ieltsFocus.ts`, `IeltsVisitor.tsx` (dải trang con IELTS). Thẻ
Stage đầu trang chủ IELTS (`IeltsStageHero`) đã gỡ ở SRC-1273, khi cả `/ielts` lẫn `/parents/ielts`
thành công cụ khám phá.

## Bằng chứng từ learn: `GET /v1/me/journey-summary` (SRC-1221)

Sau khi trang hydrate, www gọi endpoint này MỘT lần mỗi phiên trình duyệt với `credentials:
"include"` (cookie phiên learn, `Domain=.nemo12.com`). Lỗi gì cũng im lặng: không đổi gì. Kết quả
giữ trong `sessionStorage` (`n12.journey.v1`) của tab ấy.

```json
{ "data": { "signedIn": true, "programs": {
  "ielts": { "enrolled": true, "diagnosticDone": true, "hasPlan": false, "lastActiveAt": "2026-09-20",
             "daysInactive": 13, "mistakesToReview": null, "milestoneReached": null, "examDate": "2027-06" },
  "speak": { "trialBooked": true, "trialDone": false, "enrolled": false },
  "walk":  { "quizPassed": null } } } }
```

| Trường | Nguồn | Ghi chú |
| --- | --- | --- |
| `ielts.enrolled` | có dòng `learner_ielts_membership` (trial hay member) | |
| `ielts.diagnosticDone` | có dòng `ielts_diag4_reports` | |
| `ielts.hasPlan` | có dòng `learner_ielts_plan_cells` | |
| `ielts.lastActiveAt`, `daysInactive` | `MAX(day)` của `learning_events`, so với ngày học hôm nay | chỉ ngày, không giờ |
| `ielts.milestoneReached` | ước lượng overall của báo cáo chẩn đoán mới nhất >= mốc thấp nhất của mục tiêu đang chạy | không có mục tiêu hay báo cáo: `null` |
| `ielts.examDate` | `target_date` của mục tiêu đang chạy, cắt còn `YYYY-MM` | |
| `ielts.mistakesToReview` | chưa có nguồn phía server (learn tự tính trên client) | luôn `null` |
| `speak.trialBooked`, `trialDone` | `speak_trial_signups`; xong khi giờ kết thúc của buổi đã qua | |
| `speak.enrolled` | có dòng `speak_course_enrollments` | |
| `walk.quizPassed` | `walk_quiz_attempts.passed = 1` | |

### Luật quyền riêng tư

* Đây là dữ liệu của CHÍNH người đang đăng nhập, chỉ hiện cho chính họ trên máy họ. Server chỉ đọc
  learner có `learners.user_id` = user của phiên; tài khoản bố mẹ không có learner riêng nhận toàn
  `null`, không bao giờ nhận dữ liệu của con hay của người khác.
* Chỉ trả có/không, số đếm và ngày thô (`YYYY-MM-DD`, `YYYY-MM`). Không tên, email, id, điểm, câu
  trả lời hay chữ tự do. Không biết thì `null`, không đoán.
* Chưa đăng nhập hoặc cookie hỏng: 200 `{ signedIn: false }` với toàn `null`, không 401 (trang công
  khai không được sinh lỗi console). Vì thế route nằm trong danh sách PUBLIC của
  `authCoverage.test.ts`, có lý do ghi kèm.
* `Cache-Control: private, no-store`, `Vary: Cookie`. CORS chỉ `*.nemo12.com` và `nemo12.com`, có
  credentials (middleware chung của API).
* www không gửi kết quả này đi đâu khác, không ghi vào `localStorage`; chỉ Stage đã suy ra và dòng
  lý do được lưu trong `n12.visitor.v2` của máy ấy.

Test: `workers/api/src/modules/journeySummary/routes.test.ts` (chưa đăng nhập, dữ liệu của chính
mình, không thấy dữ liệu người khác, tài khoản bố mẹ, CORS), `apps/web/src/site/journeySummary.test.ts`.

## /speak: visual hierarchy theo từng Stage

Luật: MỘT khối chính (đứng đầu, chứa nút tô màu DUY NHẤT của trang, nằm trên màn đầu ở 375px), vài
khối phụ ngay dưới với tiêu đề nhỏ hơn, phần còn lại gập trong "Xem thêm về NEMO SPEAK". Trên cùng
luôn có dòng nhận diện "NEMO SPEAK · Nói tiếng Anh cùng bạn học mỗi tuần, có mentor" để khách biết
mình đang ở trang nào; ảnh minh hoạ lớn chuyển xuống phần gập để không đẩy khối chính khỏi màn đầu.

Bảng dưới là bản 4 State (SRC-1214). Bản 6 State nằm trong `SPEAK_FOCUS` (`speakFocus.ts`): mỗi
Stage một khối chính trong bảy khối sẵn có (hậu quả, nỗi sợ, vòng luyện, nghiên cứu, bảy bậc, yên
tâm, sách) và một nút; từ 3.1 trở đi nút dẫn về `learn.nemo12.com/speak` hoặc email.

| Stage (bản cũ) | Chính (nút) | Phụ | Gập |
| --- | --- | --- | --- |
| Mới ghé | Hậu quả khi không luyện nói (carousel) · Đăng ký 2 buổi học thử miễn phí | Vòng luyện, Yên tâm trước khi bắt đầu | còn lại |
| Đang tìm hiểu | Vòng luyện · Học thử | Bảy bậc, Yên tâm | còn lại |
| Đang so sánh | Nghiên cứu đứng sau · Xem học phí | Yên tâm, Vòng luyện | còn lại |
| Xem học phí | Yên tâm trước khi bắt đầu · Xem học phí chi tiết | Bảy bậc | còn lại |
| Muốn xem lộ trình | Bảy bậc · Học thử | Vòng luyện, Yên tâm | còn lại |
| Muốn học thử | Yên tâm trước khi bắt đầu · Học thử | Nỗi sợ | còn lại |
| Còn câu hỏi | Yên tâm trước khi bắt đầu · Hỏi qua email | Nghiên cứu | còn lại |
| Đăng ký | Yên tâm trước khi bắt đầu · Đăng ký trên learn | (không) | còn lại |
| Đang học | Bảy bậc · Mở lịch nói trên learn | Vòng luyện | còn lại |
| Quay lại | Vòng luyện · Quay lại lịch nói | Nỗi sợ | còn lại |

## Khu "Yên tâm trước khi bắt đầu"

Một khu gom mọi thứ giảm rủi ro cho learner, chỉ dùng dữ liệu thật, mỗi thẻ gập sẵn với một dòng
tóm tắt:

| Thẻ | Nguồn |
| --- | --- |
| Học thử: NEMO START 2 buổi x 90 phút; 3 lượt thử riêng cho buổi thứ Tư và Chủ nhật trong 09 và 10.2026; đăng ký trước 2 giờ; diễn tiến một buổi 90 phút | `apps/web/src/site/tabs/speaking.ts`, `workers/api/src/modules/speakPair/schedule.ts`, `apps/learn/src/ielts/speakSessionPlan.ts`, `speakProgram.ts` |
| Ai hướng dẫn bạn: bốn mentor, tên, vai, chuyên môn, tiểu sử, trường cũ (dấu đã xác minh), link hồ sơ | `/v1/public/mentors` (bảng `mentor_profiles`), cùng slug với learn `SpeakMentors.tsx` |
| Bạn cần chuẩn bị gì: tài khoản learn, Zoom và thiết bị có micro, 90 hoặc 120 phút mỗi buổi | `tabs/speaking.ts`, learn `SpeakSchedule.tsx` |
| Học phí: NEMO START miễn phí, NEMO SPEAK 1 và 2 mỗi khoá 1.980.000đ | `tabs/speaking.ts` (khớp learn `SpeakPair.tsx`) |
| Bằng chứng thật: tin nhắn học viên, NEMO WALK, nghiên cứu gốc | SRC-1169, `walkClaims.ts` |
| Còn câu hỏi: hello@nemo12.com | `programNavData.ts` |

Còn thiếu, cần chủ dự án cung cấp: độ tuổi hoặc trình độ tối thiểu, chính sách hoàn tiền hay cam
kết, số học viên hoặc số buổi đã chạy, ảnh mentor (hồ sơ hiện chưa có `photo_media_id`), nhóm Zalo
công khai. Lệch cần chốt: `tabs/speaking.ts` mục Lộ trình ghi "học thử miễn phí 3 buổi", nơi khác
ghi "2 buổi học thử".

## Hậu quả đặt đầu trang

Ý của chủ dự án: danh sách hậu quả khi nhu cầu không được đáp ứng đứng đầu mỗi trang chương trình,
mỗi lần một ý, bấm mở xem số liệu và nguồn. `RiskCarousel` nay nhận `items` và `title` để dùng cho
mọi chương trình; /speak dùng `speakRisks.ts` sẵn có. Danh sách cho IELTS, SAT, AP, GRAMMAR, ESSAY,
MATH, AI TEEN làm ở lượt sau, khi hạ tầng này mở ra các trang ấy.

### Hậu quả cho sáu chương trình còn lại (SRC-1220)

Chủ dự án 03.10.2026: SAT (`/sat`, `/parents/sat`), AP (`/ap`, `/parents/ap`), GRAMMAR
(`/grammar`, `/parents/grammar`), ESSAY (`/essay`, `/parents/essay`), MATH (`/math`) và AI TEEN
(`/ai-teen`) mở thân trang bằng `RiskCarousel quiet` (khối `data-part="program-risks"`), ngay
dưới hero. Dữ liệu một nguồn ở `apps/web/src/site/programRisks.ts`, khoá theo ProgramId; trang bố
mẹ dùng bản nói về "con". /nemo-walk giữ danh sách của SPEAK; IELTS giữ module Stage 1.1.

* Mỗi chương trình 4 đến 6 ý; mỗi ý một câu claim ngắn, một hai câu chi tiết, và link nguồn.
* Chỉ trích nguồn công khai đã mở ra kiểm (DOI qua Crossref/PubMed, trang trường và College Board,
  GOV.UK, IELTS, UNESCO, Carnegie, OECD, WEF). Chủ dự án 03.10.2026: nguồn không mở được thì tìm
  nguồn khác thay; ý nào không tìm được nguồn thì viết lại thành ý có nguồn, nên ý nào cũng có link.
* Nút chính của trang vẫn là nút đặc duy nhất: carousel dùng kiểu `quiet`.
* Test `programRisks.test.ts` gác: đủ sáu chương trình, 4 đến 6 ý, mỗi ý ít nhất một link https,
  không em dash, ngoặc cong hay dấu ba chấm, bản bố mẹ có chữ "con".

## Discover facts đầu trang IELTS (SRC-1233)

> **Đã gỡ khỏi đầu trang (SRC-1273, 07.10.2026).** Khối chuyện năm thẻ không còn trang nào dựng:
> `/ielts` bỏ nó ở SRC-1247, `/parents/ielts` bỏ ở SRC-1273. Bốn thẻ số liệu nay hiện trong bước
> "Vì sao" của công cụ khám phá trên cả hai trang (lựa chọn "Xét tuyển đại học Việt Nam"), bản bố
> mẹ dùng câu `line.parent`. Giao diện chuyện (`IeltsFactsStory`), thẻ cầu nối và hình minh hoạ
> từng thẻ đã xoá; dữ liệu và luật nguồn bên dưới vẫn đúng và vẫn gác bằng `ieltsFacts.test.tsx`.

Chủ dự án 04.10.2026: đầu `/ielts` và `/parents/ielts`, ngay dưới câu hỏi + claim, có một chuỗi
ngắn "Vì sao IELTS quan trọng với lựa chọn đại học", không phải lời chào bán. Bốn thẻ số liệu và
một thẻ cầu nối, lướt ngang (snap) trên điện thoại, nút Trước/Tiếp trên máy tính. Dữ liệu ở
`apps/web/src/site/ieltsFacts.ts`, giao diện ở `IeltsFactsStory.tsx`.

| Thẻ | Số lớn | Nguồn (ngày đăng) |
| --- | --- | --- |
| IELTS là một phần của tuyển sinh | 102 trường (2025, số tổng hợp, không phải số của Bộ); hơn 100 trường (2023) | Tuyensinh247 25.06.2025; VTV 16.07.2023 |
| Ngày càng nhiều thí sinh | NEU: khoảng 11.000 (2023), gần 12.000 (2024), khoảng 22.000 (2025), khoảng 23.000 (2026); ĐH Việt Đức gần 800 (2021), gần 2.600 (2024) | VnExpress 09.07.2023, 08.08.2024, 08.08.2025, 16.07.2026 |
| Không chỉ là môn tiếng Anh | Điểm quy đổi từ chứng chỉ có trọng số tối đa 50% | Đại biểu Nhân dân 24.03.2025 |
| Cần đúng mức band | NEU 2024: sàn 5.5, khoảng 85% thí sinh đạt từ 6.5 | VnExpress 08.08.2024 |
| Cầu nối | Đo điểm xuất phát, biết chỗ cần cải thiện, có lộ trình; nút viền (outline) | |

Luật: mỗi số có tên nguồn + ngày DD.MM.YYYY + link, giữ nguyên "gần"/"khoảng" như nguồn; không
bao giờ chia số dùng IELTS cho tổng thí sinh cả nước (hai bộ dữ liệu khác nhau). Thẻ cầu nối dùng
nút viền vì nút tô màu duy nhất của trang thuộc thẻ Stage. State 1 (Discover) và bản prerender
thấy đầy đủ; từ State 2 trở đi khối gập lại thành một dòng `details`. Test:
`apps/web/src/site/ieltsFacts.test.tsx`.

**Một thẻ mỗi lần (SRC-1245, chủ dự án 05.10.2026).** Khối không còn lướt ngang ba thẻ cùng lúc:
mỗi lần hiện đúng MỘT thẻ, có nút Trước/Tiếp, chấm vị trí kèm "2/5", phím mũi tên và vuốt; không
tự chạy. Chuyển thẻ bằng `motion` (`m`, tắt khi hệ điều hành bật giảm chuyển động). Cả năm thẻ vẫn
nằm trong HTML dựng sẵn (thẻ không hiện mang `hidden`) để giữ số liệu và nguồn cho SEO. Mỗi thẻ có
một hình minh hoạ từ engine bìa sách (`heroBannerSvg`, khung `tile` bên cạnh chữ trên máy tính,
khung `compact` phía trên chữ ở 375px), `aria-hidden` vì chữ vẫn là chữ thật:

| Thẻ | Hình chính | Bảng màu | Vì sao |
| --- | --- | --- | --- |
| 102 trường | `door` (toà nhà trường) | `sky` | các trường xét IELTS; xanh trời của học đường |
| khoảng 23.000 | `chart` (cột tăng) | `aqua` | thí sinh tăng mỗi năm; xanh lục của tăng trưởng |
| tối đa 50% | `balance` (cân có dấu %), hình mới | `lavender` | IELTS được cân cùng môn khác, trần 50% |
| 5.5 → 6.5 | `stairs` (bậc thang có cờ) | `sunset` | leo từ sàn 5.5 lên 6.5 |
| Cầu nối | `bridge` (cầu vòm), hình mới | `mint` | từ số liệu sang bước tiếp theo |

Test kiểm nét viền hình chính đạt 3:1 với cả hai đầu nền (WCAG 1.4.11). Khối được tách khỏi phần
trên và dưới bằng đường kẻ `border-y border-border` và khoảng cách dọc rộng.
