---
url: >-
  https://docs.nemo12.com/architecture/sdd-029-public-site-ia/student-programs.md
description: >-
  Các chương trình học sinh trên nemo12.com: Tutors, NEMO GRAMMAR, NEMO ESSAY,
  dịch vụ dưới /students, NEMO SPEAK.
---

# SDD-029 · Tutors, Grammar, ESSAY, dịch vụ học sinh và SPEAK

Một phần của [SDD-029](./index.md). Các chương trình học sinh trên nemo12.com: Tutors, NEMO GRAMMAR, NEMO ESSAY, dịch vụ dưới /students, NEMO SPEAK.

## Lịch sử quyết định

### Nemo12 Tutors ở /students/tutors (SRC-1139, 29.09.2026)

Chỉ đạo chủ dự án 29.09.2026: cụm trang công khai giới thiệu dịch vụ gia sư, cùng khuôn và độ sâu
với trang IELTS. Định vị: "Học từ người đã đi qua đúng con đường bạn sắp đi"; ghép theo mục tiêu,
hành trình, chỗ hổng rồi mới tới người, không phải một danh sách gia sư.

| Trang | Việc |
| --- | --- |
| `/students/tutors` | Hub: hero có bộ chọn mục tiêu, lớp, mức hiện tại → xem trước hồ sơ ghép |
| `/students/tutors/how-matching-works` | Sơ đồ bốn bước Goal → Journey → Gaps → Matching, ca "5 người phù hợp nhất" |
| `/students/tutors/verified-profiles` | Hồ sơ xác minh: thành tích, số buổi, kết quả người học, nhận xét, huy hiệu Verified by Nemo12 |
| `/students/tutors/become-a-tutor` | Cho gia sư: ai tham gia, bằng chứng mang theo, miễn phí, nhận được gì |
| `/students/tutors/roles` | Nemo12 People: Tutors (học), Mentors/Dolphins (định hướng), Experts (giải việc cụ thể) |
| `/students/tutors/faq` | Câu hỏi hay gặp |

* Dữ liệu: `apps/web/src/site/tutorsProgram.ts`; trang: `apps/web/src/site/TutorsPages.tsx`; thanh
  điều hướng riêng trong `SiteNav.tsx`; hub `/students` có thẻ AI Teen, NEMO SPEAK, Nemo12 Tutors.
* **Dịch vụ đang mở:** listing hai phía đã chạy ở learn (SRC-1141, [SDD-050](../sdd-050-nemo-tutor.md)), chưa có ghép tự động hay đặt lịch. Mọi tên, số liệu, nhận xét là ví dụ minh hoạ và
  mang nhãn "Ví dụ minh hoạ" tại chỗ; bản tĩnh cũng nói vậy.
* **Đăng ký sớm (SRC-1141, thay bản mailto):** "Đăng ký sớm" trỏ `tutors.nemo12.com/new?side=family` (SRC-1175; trước đó `learn.nemo12.com/tutors/...`),
  "Đăng ký làm gia sư" trỏ `?side=tutor` (`TUTORS_SIGNUP_*`). Form, listing ẩn danh, Quan tâm và hàng
  chờ kết nối nằm ở [SDD-050](../sdd-050-nemo-tutor.md); FAQ công khai nói đúng mô hình ấy: listing, ẩn danh,
  Quan tâm, Nemo12 kết nối.
* **Lý do sản phẩm (không đưa lên chữ công khai):** miễn phí 100% lúc đầu để dựng cung và cầu; thu
  phí là bước sau. Gia sư là học sinh, sinh viên vừa đạt kết quả, nên mỗi hồ sơ xác minh vừa là
  bằng chứng tin cậy vừa là kênh phân phối (gia sư chia sẻ hồ sơ của mình). Càng nhiều người học
  thì ghép càng đúng, càng nhiều gia sư có kết quả thật; đó là hiệu ứng mạng của Nemo12 People.
* Test: `apps/web/src/site/studentServices.test.ts` (khối Nemo12 Tutors).

### Trang Grammar theo khuôn trang IELTS, học phí thôi lộ liễu (SRC-1143, 29.09.2026)

Chủ dự án 29.09.2026: `/students/grammar` và các trang con phải dùng cùng cấu trúc với trang IELTS;
phần học phí trên trang Grammar "quá nhiều và quá lộ liễu".

| Chỗ | Trước | Sau |
| --- | --- | --- |
| Hero | Một cột căn giữa, h1 lời hứa, ba nhãn số | Hai cột như IELTS: pill `NEMO GRAMMAR`, h1 câu hỏi, dòng phụ, nút; cột phải `GrammarLevelCard` chọn A1 tới C1, hiện số mảng, số bài và một bài mẫu thật |
| Khối học phí | `PricingBlock` đổ bảng giá API ở cuối trang (học sinh) hoặc giữa trang (bố mẹ) | Bỏ hẳn. Chỉ còn một dòng "Học phí Grammar" trong "Đọc tiếp" |
| Trang con | who-is-it-for, levels, how-it-works | Thêm questions-to-ask, faq, tuition. Không có diagnosis vì Grammar chưa có bài xếp level |
| `/…/grammar/tuition` | (chưa có) | Trang học phí riêng của Grammar theo khuôn `/…/ielts/tuition`, sửa lại ở SRC-1144 (xem mục dưới) |

Quyết định:

1. Số trên thẻ và trang lấy từ `grammarProgram.ts`; test `grammarPublic.test.ts` đối chiếu với
   `packages/grammar-catalog/catalog.json` (level, mảng, số bài, bài mẫu tồn tại). Từ SRC-1242 `GRAMMAR_LEVELS` sinh thẳng từ gói ấy, không còn bản chép tay.
2. Bài mẫu chép nguyên văn từ file nội dung của learn, không viết câu mới.
3. Không đổi URL nào, nên không thêm bí danh; `/…/university/grammar/*` vẫn 301 qua `studentServiceAlias` / `parentServiceAlias`.
4. Không chép con số tiền nào vào web: giá sống trong API. (Bản đầu của mục này viết "Grammar
   không có bảng giá riêng" và cho trang học phí dẫn sang học phí IELTS; SRC-1144 gỡ điều đó.)

### Grammar là chương trình riêng, ngang hàng IELTS, SAT, Speaking (SRC-1144, 30.09.2026)

Chủ dự án 30.09.2026: Grammar là chương trình riêng biệt, cấp độ tương đương IELTS, SAT hay
Speaking. Nó KHÔNG nằm trong gói học nào của IELTS. SRC-1143 đã viết sai điều này ở trang học phí,
FAQ và mục trên của SDD này.

| Chỗ | Sau SRC-1143 (sai) | Sau SRC-1144 |
| --- | --- | --- |
| `/…/grammar/tuition` | Một câu "nằm trong gói học chung với IELTS" và nút sang `/…/ielts/tuition` | Khuôn trang `IeltsTuitionPage`: vệt đường dẫn, dải tối có `h1`, dòng hoàn tiền 30 ngày, "Bước tiếp theo". Giá: chỉ các dòng `pricing_items` có mã bắt đầu `grammar-`; chưa có dòng nào thì không bảng giá, chỉ một dòng "Học phí NEMO GRAMMAR đang được cập nhật." |
| FAQ "Học phí Grammar là bao nhiêu?" | "nằm trong gói học chung với IELTS" | Chương trình riêng, ngang hàng IELTS, SAT, Speaking; giá ở trang Học phí Grammar |
| Bản tĩnh `/…/grammar/tuition` (`routes.ts`) | Cùng câu sai, link sang học phí IELTS | Câu chương trình riêng, không con số |
| Chân trang, cột "Chương trình" (bản IELTS và bản SAT) | Chỉ IELTS, SAT | IELTS, SAT, Grammar, NEMO SPEAK |

Quyết định:

1. Nguồn giá: bảng `pricing_items` qua `/v1/public/pricing`, sửa ở admin, nhưng trang CHỈ lấy
   dòng có mã bắt đầu bằng **`grammar-`** (`GRAMMAR_PRICE_PREFIX` trong `GrammarPages.tsx`). Các
   dòng khác của bảng là giá khoá IELTS; hiện chúng trên trang Grammar là dựng lại đúng sự nhầm lẫn
   mục này gỡ. **Chủ dự án / admin thêm giá Grammar** bằng một dòng `pricing_items` có mã như
   `grammar-tuition` (thêm dòng khác cùng tiền tố nếu cần, ví dụ `grammar-trial`), `kind` theo đúng
   loại (`tuition`, `addon`...). Tới ngày 30.09.2026 bảng chưa có dòng `grammar-` nào, nên trang chỉ
   hiện "Học phí NEMO GRAMMAR đang được cập nhật.", dòng hoàn tiền và "Bước tiếp theo".
2. Máy tính theo band của IELTS không dùng cho Grammar: Grammar không có band để tính.
3. Test `grammarPublic.test.ts`: không trang Grammar nào (bản tĩnh, FAQ, mã trang) chứa "gói IELTS",
   "gói học chung với IELTS" hay "IELTS package"; dòng giá IELTS không bao giờ hiện trên trang học
   phí Grammar, dù bảng có hay chưa có dòng `grammar-`.

### NEMO ESSAY: cụm trang giới thiệu trên nemo12.com (SRC-1152, 30.09.2026)

Chủ dự án 30.09.2026: làm cụm trang giới thiệu NEMO ESSAY như IELTS, SAT, SPEAK, GRAMMAR. Cùng
ngày chủ dự án bổ sung: học có hướng dẫn, 12 buổi, học phí 8.000.000đ cho 12 buổi; và bản đồ chín
mảng kỹ năng essay.

| Chỗ | Nội dung |
| --- | --- |
| `/students/essay`, `/parents/essay` | Trang môn theo khuôn Grammar: hero cột trái (pill NEMO ESSAY, tiêu đề là câu hỏi, câu dẫn, nút) và thẻ "Tìm câu chuyện" cột phải; sáu bước; khối nỗi lo; dải chín kỹ năng; "Đọc tiếp" |
| `/…/essay/who-is-it-for` | "Essay dành cho ai" / "Essay có hợp với con không": bốn nhóm người đọc và năm loại bài làm nhãn |
| `/…/essay/journey` | Hành trình 6 bước Discover, Collect, Select, Develop, Refine, Assemble, mỗi bước "xong thì cầm gì"; thẻ 12 buổi |
| `/…/essay/ielts-vs-essay` | Vì sao IELTS cao chưa chắc viết essay hay: bốn tiêu chí IELTS Writing đặt cạnh sáu thứ bài luận cần |
| `/…/essay/skills` | Bản đồ kỹ năng: chín mảng, thang suy ngẫm, ví dụ Show Don't Tell, bảng "mỗi bài một mặt của em", thẻ hồ sơ kỹ năng "Ví dụ minh hoạ" |
| `/…/essay/questions-to-ask` | Sáu câu nên hỏi, cùng khuôn IELTS/Grammar, không nói Nemo12 trả lời ra sao |
| `/…/essay/faq` | Tám câu; câu "Làm sao để đăng ký" nói thông tin tham gia sẽ được thông báo |
| `/…/essay/tuition` | Cùng khuôn Học phí Grammar, lọc dòng `pricing_items` có mã `essay-` |

Mã: `apps/web/src/site/essayProgram.ts` (chữ, dùng được trong Node), `EssayPages.tsx` (giao diện),
khoá `university/essay` trong `subjectCopy.ts`, `ESSAY_SUBPAGE` trong `App.tsx`, `essay` trong
`STUDENT_SERVICES` và `PARENT_SERVICES` (`goals.ts`) nên địa chỉ `/…/university/essay/*` trả 301.
Môn `essay` trong `GOALS` đổi `built: false` thành `true`, nên nó lên lưới trang mục tiêu "Vào đại
học" của cả hai vai cạnh IELTS, SAT, Grammar; chân trang cột "Chương trình" (bản IELTS và bản SAT)
thêm "NEMO ESSAY". Test: `essayPublic.test.ts`.

#### Định vị

* **Không phải "dạy viết văn", không phải khoá viết tiếng Anh.** Là chương trình bài luận xin học
  đại học (University Application Essay Program): giúp học sinh tìm câu chuyện định nghĩa mình,
  phát triển giọng riêng, rồi tự kể thành bài luận thật. Câu khẩu hiệu giữ tiếng Anh làm nhãn:
  "Discover your story. Find your voice. Tell your story."
* **Nhận định chính cho học sinh Việt Nam: giỏi tiếng Anh không đồng nghĩa viết được bài luận.**
  Bài luận cần hiểu mình, kể chuyện, suy ngẫm, tư duy phản biện, giọng riêng, cấu trúc; IELTS
  Writing chấm Task Response, Coherence and Cohesion, Lexical Resource, Grammatical Range and
  Accuracy. Ví dụ IELTS 7.5 / SAT 1450 / GPA 9.2 ghi rõ trên trang là con số ví dụ, không phải kết
  quả của ai.
* **"Đừng bắt đầu từ Writing."** Thứ tự: Identity, Experience, Story, Reflection, Idea, Narrative,
  Voice, Writing, Revision. Chín mảng kỹ năng giữ tên tiếng Anh làm nhãn, giải thích tiếng Việt.
* **Không viết hộ.** Trang nói đúng một câu chính sách "Nemo12 không viết hộ" và "giọng của em đứng
  trước câu chữ đẹp", nhắc AI. Không bịa thêm chi tiết chính sách (cam kết, kiểm tra đạo văn...).
* Hai vai khác nhau thật: học sinh đọc bảy nỗi lo "Em không có câu chuyện gì cả"...; bố mẹ đọc sáu
  nỗi lo (không biết con có chuyện gì, sợ viết hộ và AI, sợ không authentic, sợ lỡ hạn, không biết
  trường nào cần bài nào).

#### Lý do JTBD (chỉ ở đây, không lên trang công khai)

Việc người học "thuê" chương trình làm: biến trải nghiệm, tính cách, giá trị và tham vọng của mình
thành những bài luận cho hội đồng tuyển sinh thấy mình là ai ngoài điểm số. Kiến trúc sản phẩm
nội bộ đi theo việc ấy (Student Portrait, Experience Bank, ... , Essay Portfolio). Trang công khai
**không** in các tên đó hay chữ JTBD: phụ huynh và học sinh không cần học từ vựng nội bộ để hiểu,
nên chúng hiện ra thành hành trình sáu bước đơn giản. Test chặn các tên ấy trên bản tĩnh.

#### Thẻ "Tìm câu chuyện" ở hero

Năm gợi ý ("Một lần em thay đổi suy nghĩ", "Một việc em làm dù không ai bắt", "Một người ảnh hưởng
tới em", "Một thất bại em học được điều gì", "Một cộng đồng em thuộc về"). Chọn tối đa hai; mỗi gợi
ý hiện loại bài có thể hợp và một câu hỏi đào sâu. Chạy hoàn toàn phía trình duyệt, không gửi và
không lưu gì.

#### Học phí

* Số do chủ dự án đưa: **8.000.000đ cho 12 buổi**, học có hướng dẫn.
* Chỉ ở trang `/…/essay/tuition`, lấy từ `/v1/public/pricing`, lọc mã `essay-`
  (`ESSAY_PRICE_PREFIX`). Lối vào: một dòng trong "Đọc tiếp" của trang môn và một dòng dưới FAQ.
* Seed: **`scripts/seed-essay-pricing-2026-09-30.sql`**, dòng `essay-course`, `kind = 'tuition'`,
  8000000, `unit_vi` "/ khoá · 12 buổi", `seq` 111, idempotent. **Chưa chạy**: điều phối chạy qua
  workflow seed-data sau khi gộp. Trước khi chạy, trang hiện "Học phí NEMO ESSAY đang được cập nhật."
* Test `essayPublic.test.ts` chặn mọi dạng con số học phí (8.000.000, 8,000,000, 8 triệu...) trên
  mọi bản tĩnh Essay và trong mã nguồn `EssayPages.tsx`, `essayProgram.ts`, `subjectCopy.ts`.

#### Cờ và việc chờ chủ dự án

1. **Lịch từng buổi (syllabus 12 buổi) chưa có.** Trang chỉ nói 12 buổi đi qua đủ sáu bước, lịch cụ
   thể sẽ công bố. Không bịa buổi nào học gì.
2. **Cách tham gia chưa có.** Không có luồng đăng ký riêng cho Essay; IELTS/Grammar trỏ vào app học,
   Tutors trỏ form trên learn. Nút chính và nút "Đọc tiếp" mở `mailto:hello@nemo12.com` với tiêu đề
   "Quan tâm NEMO ESSAY" (`ESSAY_CONTACT_HREF`). Có luồng thật thì chỉ sửa hằng số ấy.
3. **Ai dẫn chương trình chưa có.** Trang nói "người hướng dẫn", không tên, không hồ sơ, không khối
   Mentors.
4. **Hình thức (trực tuyến hay trực tiếp, nhóm hay một kèm một) chưa có.** Trang không nói.
5. Thẻ hồ sơ kỹ năng (Story Discovery 80%, Reflection 35%...) là **ví dụ minh hoạ**, ghi rõ trên
   trang; Nemo12 chưa có công cụ đo kỹ năng essay.

### NEMO ESSAY: sáu thẻ nỗi lo, lưới ba cột, thư viện bài luận công khai (SRC-1159, 30.09.2026)

Chủ dự án 30.09.2026, sửa cụm trang Essay (SRC-1152).

* **Nỗi lo đúng sáu thẻ, ba thẻ mỗi dòng.** Bản học sinh có bảy thẻ; "Em không biết viết về bản
  thân" gộp vào "Em không có câu chuyện gì để kể" vì cả hai cùng là nỗi lo "không có gì để kể", và
  câu trả lời của thẻ bị bỏ ("bắt đầu từ một cảnh cụ thể") chuyển sang thẻ gộp. Năm thẻ còn lại
  viết gọn lại. Bản bố mẹ vốn đã sáu thẻ. Lưới `grid-cols-1 md:grid-cols-3` ở cả hai vai.
* **Lưới 3, 6, 9 thẻ xếp ba cột.** Sáu bước ở trang môn (trước là 4 + 2) dùng `lg:grid-cols-3`
  qua cờ `threeUp` của `Steps`, CHỈ bật cho Essay: các môn khác có sáu bước giữ nguyên bố cục. Dải
  chín kỹ năng, trang Hành trình (sáu thẻ) và trang Bản đồ kỹ năng (chín thẻ) cũng thành lưới ba
  cột, một cột trên điện thoại. Bốn thẻ "Dành cho ai" giữ 2 x 2; câu hỏi nên hỏi và FAQ là danh
  sách, không phải lưới.
* **Trang `/{vai}/essay/library`**, cùng nội dung hai vai: danh mục 12 bài mẫu Nemo12 biên soạn
  (`essayLibrary.ts`, chép tay từ `apps/learn/src/essay/content/essays/*.json`; web không import mã
  của app khác). Mỗi bài: tên, loại bài, chủ đề, số chữ, câu mở đầu, một hai câu "vì sao đáng học"
  do Nemo12 viết, nút "Đọc trên learn" tới `learn.nemo12.com/essay/library/<id>` (cần đăng nhập).
  Sáu chip lọc theo loại bài. Nhãn "Bài mẫu do Nemo12 biên soạn" và lời dặn học cách nghĩ, đừng chép.
* **Ranh giới nội dung trả phí:** trang công khai và bản tĩnh chỉ mang câu đầu tiên của mỗi bài;
  thân bài và bản dịch song ngữ ở lại learn. Test chốt từng câu còn lại không xuất hiện.
* **Lối vào:** nút phụ thứ hai trong hero (`ctaTertiary` trong `subjectCopy.ts`), danh sách link
  trang môn, "Đọc tiếp" của mọi trang con, FAQ (một câu hỏi mới về bài mẫu + link cuối trang),
  sitemap và vệt đường dẫn qua `routes.ts`.
* learn.nemo12.com/essay đã có thẻ "Library" ở trang chủ khu Essay, không cần sửa.

Test: `apps/web/src/site/essayLibrary.test.ts` (khớp id/tên/loại/chủ đề/số chữ/câu đầu với JSON của
learn, route hai vai, không thân bài, sáu thẻ nỗi lo, lưới ba cột, các lối vào).

### NEMO GRAMMAR: năm khoá, mỗi khoá 8 buổi, học phí theo tháng (SRC-1145, 30.09.2026)

Chủ dự án 30.09.2026: NEMO GRAMMAR là chương trình riêng gồm 5 khoá. Mỗi khoá 8 buổi, 1 buổi mỗi
tuần, nên một khoá kéo dài 2 tháng. Học phí 2.300.000đ/tháng cho một khoá, tức 4.600.000đ cho trọn
khoá. Sau mỗi buổi có khá nhiều bài tập về nhà.

| Chỗ | Trước | Sau SRC-1145 |
| --- | --- | --- |
| Thẻ chọn level ở hero | Tên level, số mảng, số bài | Thêm tên khoá "NEMO GRAMMAR {level}" và dòng "8 buổi · 1 buổi/tuần · 2 tháng" |
| `/…/grammar/levels` | "Level nào cũng mở sẵn" | Năm level là năm khoá, mỗi khoá 8 buổi, 1 buổi/tuần, 2 tháng |
| `/…/grammar/how-it-works` | Năm phần của một bài | Thêm thẻ "Bài tập sau mỗi buổi", nói thẳng đây là khối lượng thật |
| `/…/grammar/who-is-it-for` | Chọn level theo kỳ thi | Thêm nhịp khoá và bài tập sau mỗi buổi |
| `/…/grammar/faq` | Sáu câu | Thêm "Một khoá học bao lâu", "Học hết cả chương trình mất bao lâu" (5 × 2 = 10 tháng nếu học liền), "Bài tập về nhà có nhiều không" |
| `/…/grammar/questions-to-ask` | Năm câu | Sáu câu: thêm câu hỏi về khối lượng bài tập và cách chữa |
| Trang Grammar (hai vai) | "Dùng chung tài khoản và gói học với sân luyện IELTS" | Bỏ "gói học" (trái SRC-1144); thêm nhịp khoá và bài tập |
| `/…/grammar/tuition` | "Học phí NEMO GRAMMAR đang được cập nhật." | Hai dòng `pricing_items` sau khi chạy seed |

Quyết định:

1. **Năm khoá = năm level A1 tới C1 là quyết định của phiên làm việc, chưa được chủ dự án xác
   nhận.** Lý do: trang đã nói "Năm level từ A1 tới C1" và danh mục learn có đúng năm level. Tên
   khoá "NEMO GRAMMAR A1" tới "NEMO GRAMMAR C1" (`grammarCourseName()` trong `grammarProgram.ts`).
   Chủ dự án chốt khác thì chỉ sửa `GRAMMAR_COURSE*` và `grammarCourseName` trong file ấy.
2. **Giá chỉ ở trang học phí.** Mọi trang khác chỉ nói nhịp khoá, không con số tiền; test
   `grammarPublic.test.ts` chặn số dạng `x.xxx.xxx`, "triệu", "đ/tháng" trên mọi trang Grammar trừ
   `/tuition`.
3. **Dữ liệu giá:** file seed **`scripts/seed-grammar-pricing-2026-09-30.sql`** (idempotent,
   `ON CONFLICT(code) DO UPDATE`), chạy qua workflow seed-data sau khi gộp. Hai dòng, cả hai `kind =
   'tuition'` (`bundle` sẽ hiện dưới nhãn "Combo", sai nghĩa):
   * `grammar-course-monthly`: 2.300.000, `unit_vi` "/ tháng", `seq` 101, giá chính đứng đầu.
   * `grammar-course-total`: 4.600.000, `unit_vi` "/ khoá · 8 buổi", `seq` 102, dòng phụ.
4. **Không ước lượng số giờ bài tập.** Chủ dự án chỉ nói "khá nhiều"; trang nói đúng như vậy.
5. **learn không đổi.** `apps/learn/src/grammar` không nêu cấu trúc khoá (buổi, tuần, tháng) nên
   không có chỗ lệch. Nó vẫn trình bày năm level như một danh mục tự học; ghép với lịch học theo
   buổi là việc khác, chưa làm.

### Dịch vụ học sinh ở thẳng dưới /students (SRC-1138, 29.09.2026)

Chỉ đạo chủ dự án 29.09.2026: năm dịch vụ của học sinh bỏ tầng `/university` khỏi URL.

| Địa chỉ cũ (301) | Địa chỉ mới |
| --- | --- |
| `/students/university/ielts/**` | `/students/ielts/**` |
| `/students/university/sat/**` | `/students/sat/**` |
| `/students/university/grammar/**` | `/students/grammar/**` |
| `/students/university/speaking`, `/speak` | `/students/speaking` |
| `/students/university/ai-teen` | `/students/ai-teen` (trang mới) |

* **Một nguồn cho đường dẫn:** `subjectPath()` trong `apps/web/src/site/goals.ts` trả dạng mới cho vai
  học sinh; `internalPath()` đổi ngược về dạng nội bộ để `App.tsx`, thanh điều hướng và chân trang
  dùng lại nguyên nhánh cũ. Vệt đường dẫn là "Học sinh > IELTS", không còn "Vào đại học" ở giữa.
* **301 thật ở worker:** `aliasFor()` trong `routes.ts` gộp `ALIASES`, `teacherCertAlias` và
  `studentServiceAlias`; worker và bộ định tuyến phía trình duyệt cùng gọi nó. Cả cây con được
  chuyển, giữ query.
* **Không dời:** trang mục tiêu `/students/university` (vẫn là trang chọn giữa các kỳ thi, và vẫn
  giữ Toán, Tiếng Anh ở `/students/university/{math,english}`), và toàn bộ bản bố mẹ
  `/parents/university/**`.
* **AI Teen** (`/students/ai-teen`): cùng khuôn trang môn IELTS: hero hai cột với bộ chọn course,
  dải Journey năm bước, ba course với đủ 18 unit, skill theo bốn domain CCS kèm thang bốn mức, FAQ,
  CTA sang `learn.nemo12.com/ai-teen`. Dữ liệu ở `aiTeenProgram.ts`, bản chép của
  `apps/learn/src/aiTeen/` (SDD-043); `aiTeenProgram.test.ts` đối chiếu từng trường. Trang không nói
  học phí, lịch hay ngày khai giảng vì nguồn chưa có.
* **Nội dung D1:** `scripts/seed-students-urls-src-1138.sql` (idempotent) đổi link cũ trong
  `articles`, `article_services`, `media_videos`; chạy qua workflow seed-data sau khi gộp.
* Test: `apps/web/src/site/studentServices.test.ts`, `apps/web/worker/aliases.test.ts`.

### NEMO SPEAK ở /speak (SRC-1195, 03.10.2026)

Chỉ đạo chủ dự án 03.10.2026: NEMO SPEAK đổi địa chỉ từ `/speaking/**` sang `/speak/**` (trang
chính và năm trang tab roadmap, curriculum, methodology, outcomes, resources). Bí danh cũ
`/speak` -> `/speaking` của SRC-1138 đã gỡ, nên không có vòng chuyển hướng.

* **Id nội bộ giữ `speaking`.** `programSegment()` trong `studentPaths.ts` đổi id ra đoạn URL công
  khai `speak`; `programHref`, `programBase`/`tabHref` đều đi qua nó. `studentInternal("/speak/**")`
  trả `/students/speaking/**` để bộ định tuyến dùng lại nhánh cũ, `studentPublic` đổi ngược.
* **301 thật ở worker:** `speakingAlias()` nằm trong `aliasFor()`, giữ trang con và query;
  `/students/speaking` và `/students/university/speaking` đi thẳng một bước tới `/speak`.
* **Không dời:** `/ielts/speaking/**` (nội dung IELTS) và
  `learn.nemo12.com/speak`. Không có trang `/parents/speaking`.
* Test: `apps/web/worker/aliases.test.ts`, `apps/web/src/site/studentServices.test.ts`.

#### Cụm bài viết theo sang /speak/articles (SRC-1197, 03.10.2026)

Chỉ đạo chủ dự án 03.10.2026: cụm bài viết D1 của NEMO SPEAK (SRC-1132) cũng dời từ
`/speaking/articles/**` sang `/speak/articles/**`.

* **301:** `speakingAlias()` không còn chừa `/articles`, nên `/speaking/articles/<category>/<slug>?q`
  trả 301 tới `/speak/articles/<category>/<slug>?q` (cả `feed.xml`) trước khi tới handler bài viết.
* **Id dịch vụ giữ `speaking`:** khoá D1, `sitemap-articles-speaking.xml` và ảnh
  `/art/articles/speaking/**` không đổi. `worker/articles/handler.ts` ánh xạ đoạn URL `speak` sang
  dịch vụ `speaking`.
* **Chịu được thứ tự deploy:** handler lấy `base_path` từ hằng `BASE_PATH` trong code, đè giá trị
  trong D1, nên canonical, breadcrumb, feed và sitemap đúng `/speak` ngay khi code lên, dù seed đã
  chạy hay chưa. Trước seed, link trong thân bài còn trỏ `/speaking/articles` và đi qua 301.
* **Dữ liệu:** bản soạn `apps/web/content/articles/speaking/**` và `scripts/seed-articles-speaking.sql`
  đã đổi sang `/speak/articles`; D1 production được vá bằng `scripts/seed-speak-articles-src-1197.sql`
  (idempotent, chạy qua workflow seed-data sau khi web deploy).
* Test: `apps/web/worker/articles/handler.test.ts`, `apps/web/worker/aliases.test.ts`.
