Skip to content

SDD-021 — Learning Entry & Sequencing ​

Câu hỏi tài liệu này trả lời: learner học theo trình tự nào, và ai quyết trình tự đó.

Trước SRC-384 hệ có hai câu trả lời chạy song song mà không ai chốt: một đường do hệ thống đề xuất (Recommendation Engine, đã chạy từ SRC-207) và một cây nội dung để learner tự duyệt (Compass, danh sách Phần 1..N). Hai thứ đó không mâu thuẫn về mặt kỹ thuật, nhưng trên màn hình thì có: learner mở app ra gặp một danh sách Phần trông như mục lục sách, còn lời đề xuất thì nằm chỗ khác. Kết quả là màn đầu tiên không trả lời được câu nào trong hai câu learner thực sự hỏi.

1. Hai câu hỏi, hai màn, không chồng nhau ​

MànTrả lời câu gìAi lái
Dashboard (/{school}/overview/{subject})"Hôm nay con làm gì" ở đầu trang, và "con đang đứng ở đâu, muốn đi đâu" ở phần dướihệ thống đề xuất phần trên, learner tự chọn phần dưới

Đúng một cửa. Compass bị bỏ khỏi menu (SRC-384, chủ dự án chốt phương án (a)): danh sách Phần 1..N không nói được vì sao nên vào Phần nào, trong khi Dashboard nói điều đó bằng con số và bằng màu. Giữ cả hai là bắt learner đọc hai lần cùng một cây nội dung, một lần có thông tin và một lần không.

Ba thẻ Phần liền nhau cùng ghi một câu ("Học xong phần này, con vững hơn ở Represent Quantity") là bằng chứng rõ nhất rằng màn đó không mang thông tin: một câu lặp lại ở mọi thẻ thì không giúp chọn được thẻ nào.

2. Hàng chờ (RecommendationQueue) ​

Nằm ở đầu Dashboard, trước mọi vòng tròn tiến độ.

text
┌──────────────────────────────────────────────┐
│  VIỆC HÔM NAY                                │
│  Đếm gộp tách  ·  còn 3 bước                 │
│  "Con làm phần này 6 ngày trước, sắp quên."  │
│  [ HỌC TIẾP ]                                │
├──────────────────────────────────────────────┤
│  · Giá trị theo vị trí      đang hổng        │
│  · Phân số của một cái bánh bước tiếp        │
└──────────────────────────────────────────────┘

Một việc lớn + đúng hai việc nhỏ (chủ dự án chốt). Mười việc chưa làm đọc ra như một món nợ, và learner sẽ đọc nó như bảng điểm chứ không như lời mời. Ba là đủ để thấy có đường đi mà chưa thấy nặng.

Không có nút "để sau" (chủ dự án chốt). Hoãn được một việc thì việc khó luôn là việc bị hoãn, và hàng chờ tự lọc thành danh sách những thứ learner đã biết làm. Learner muốn học chỗ khác thì cuộn xuống Dashboard chọn thẳng, đó mới là quyền chủ động thật, và nó để lại tín hiệu rõ hơn nhiều so với một cú bấm bỏ qua.

2.1 Thứ tự ưu tiên (REQ-INT-32) ​

Recommendation Engine đã xếp hạng theo priority; SRC-384 chốt thứ tự các loại:

BậcLoạiVì sao đứng đây
0đang dở một UnitBỏ dở giữa chừng rồi bị đẩy sang việc khác là cách nhanh nhất để không bao giờ hoàn thành cái gì. Đè lên mọi thứ.
1review sắp quênGiữ lại thứ đã học rẻ hơn học lại từ đầu, và cửa sổ ôn có hạn
2repair đang hổng và chặn đường phía sauMột lỗ hổng chặn 4 bài phía sau thì nó đắt gấp 4
3exam_prep sắp thiCó deadline thật, nhưng chỉ khi mốc đã gần
4new bước tiếpMặc định khi không có gì cấp hơn

Bậc 0 không phải một RecoKind mới: nó là một lượt kiểm tra trước, đọc learner_experience_state tìm Unit có ô đã động tới mà chưa xong.

2.2 Mỗi việc phải nói được vì sao ​

Giữ nguyên luật REQ-INT-14 đã có: mỗi đề xuất kèm why bằng tiếng người và from truy về model sinh ra nó. Việc nhỏ hiện nhãn ngắn (sắp quên · đang hổng · sắp thi · bước tiếp); việc lớn hiện cả câu.

3. Các lần đo (Diagnosis) ​

Nguyên tắc nền: phần lớn việc đo tan vào buổi học bình thường. Mỗi Assessment Experience learner làm đã là bằng chứng rồi, nên hệ thống hiếm khi cần hỏi thêm. Bài đo đứng riêng chỉ xuất hiện khi hệ thống thật sự mù, và luôn nói ra vì sao mình hỏi.

Khi nàoDàiThời gianLearner thấy nó là gì
Mở đầu: chưa có bằng chứng nào ở môn đó10 tới 12 câu6 tới 8 phút"Cho con vài câu để khỏi phải học lại thứ con đã biết"
Nhảy vùng mới: Module chưa có bằng chứng6 tới 10 câu3 tới 5 phútChính là Học vượt đang có (§5)
Quay lại: 7 ngày kể từ lần cuối làm bài của môn6 tới 8 câu, chỉ lấy từ phần đã vững4 phút"Xem con còn giữ được bao nhiêu"
Trước mốc thi trong Goaltheo blueprint đề15 tới 20 phútLuyện đề, đã có sẵn
Model bất định ở một mảng2 tới 3 câu chèn vào giữa buổi họckhông tốn thêm buổilearner không nhận ra đó là đo

Không bao giờ quá 10 phút, trừ luyện đề.

3.1 Đo mở đầu là bắt buộc, nhưng chỉ đúng một lần cho mỗi môn (REQ-ONB-10) ​

Chặn cứng: chưa làm xong thì không mở được Unit nào của môn đó. Nhưng điều kiện kích hoạt là chưa có bằng chứng nào, không phải "chưa từng làm bài đo": learner đã học từ trước (như tài khoản chủ dự án, đã vững 5 unit) thì không bị hỏi lại. Bắt một người đã chứng minh năng lực chứng minh lại là cách chắc chắn nhất để họ tin rằng hệ thống không nhớ gì về mình.

Phạm vi theo từng môn: đo Toán không nói được gì về Tiếng Việt.

Bài đo trải rộng: mỗi Package 2 tới 3 câu, đủ để biết nên bắt đầu từ Package nào; đo sâu để dành cho lúc thật sự vào học. Độ khó xuất phát lấy từ lớp khai trong Profile (REQ-ONB-02 đã bắt khai trước mọi hoạt động học nên dữ liệu này luôn có). Adaptive để sau (chủ dự án chốt): vòng đầu chọn câu tĩnh theo lớp, không đổi độ khó giữa chừng.

3.2 Đo lại sau 7 ngày (REQ-INT-31) ​

Mốc là 7 ngày kể từ lần cuối learner làm bài của môn đó, không phải 7 ngày kể từ lần đo. Learner học đều mỗi ngày thì không bao giờ chạm mốc này, và đúng như vậy: mỗi buổi học đã sinh bằng chứng mới rồi, hỏi thêm là hỏi thứ mình đã biết.

Bài đo lại chỉ lấy câu từ phần learner đã vững. Mục đích của nó là kiểm tra trí nhớ, không phải mở rộng bản đồ; trộn phần chưa học vào chỉ làm learner thấy mình tụt lùi sau kỳ nghỉ.

4. Diagnosis Report (REQ-LRN-24) ​

Trang riêng có URL: /{school}/report/{subject}, vào lại được bất cứ lúc nào từ Dashboard. Đây là thứ phụ huynh sẽ đòi xem, và cũng là mốc để vài tuần sau learner so mình đã đi được bao xa.

Không có điểm số. Một con số cuối bài đo biến việc đo thành việc bị chấm, mà đó đúng là thứ làm learner né bài đo lần sau.

Nội dung, đúng hai phần:

PhầnSố lượngVì sao chặn số lượng
Con đã vữngtối đa 5Đủ để thấy mình không bắt đầu từ số không, không đủ dài để thành bảng kê
Bắt đầu từ đâyđúng 1Liệt kê nhiều chỗ cần bắt đầu là trả learner về đúng trạng thái hoang mang mà bài đo lẽ ra phải gỡ

Phụ huynh thấy gì thì để sau (chủ dự án 2026-08-19), nhưng URL đã tách riêng nên khi làm thì không phải dỡ gì.

5. Chọn Unit quá sâu: hé cửa, không khoá (REQ-LRN-25) ​

Learner bấm vào một Unit sâu hơn nhiều so với bằng chứng hiện có thì vẫn vào được.

text
vào Unit  →  làm bài  →  sai 3 câu  →  hệ nói: "Chỗ này đứng trên [Unit X].
                                        Làm X khoảng 10 phút rồi quay lại."
                                        [ Đi tới X ]   [ Con cứ học tiếp ở đây ]

Không chèn Unit tiên quyết vào trước khi learner kịp thử. Learner tự thấy mình thiếu gì thì không sợ; bị chặn cửa thì mới sợ. Nút "con cứ học tiếp ở đây" luôn có mặt, và việc bấm nó là dữ liệu chứ không phải sự bướng bỉnh cần sửa.

Nói trước độ sâu để nỗi sợ thành phép tính: mỗi Module hiện một dòng dạng "Phần này thường học ở lớp 9. Con đang có bằng chứng ở mức lớp 6." Con số thì quyết định được, còn cảm giác thì không.

6. Ba bậc hiện ra (REQ-LRN-23) ​

Mỗi Unit trong B21 đã mang ba bậc can_do: Đang hình thành · Đạt · Vững.

  • Trên thẻ Unit ở Dashboard: ba chấm nhỏ, không chữ. Hiện mà không thêm dòng chữ nào lên màn hình, nên không phạm luật tối giản SRC-048.
  • Chữ giải thích từng bậc chỉ nằm trong popover dấu hỏi.

Vào Module nào cũng được, nhưng vào ở bậc "Đạt". Bậc Vững chỉ mở khi đã có bằng chứng ở bậc Đạt. Learner lớp 7 lao vào một module lớp 10 sẽ gặp đúng phần cốt lõi, không gặp phần dành cho người đã thạo. Đây là chỗ làm learner sợ, và cắt nó đi không mất gì.

7. Học vượt và "đo khi nhảy vùng mới" là MỘT (REQ-INT-33) ​

Hai thứ này đang làm y hệt việc nhau: 6 tới 10 câu trải trên mọi node của Unit, qua thì cả Unit ghi nhận đã qua. Gộp làm một, giữ đúng một tên Học vượt, vì learner đã quen tên đó. Luật hiển thị và mở khoá của Học vượt vẫn nằm ở SDD-010 §9.1 và không đổi.

Khác biệt duy nhất còn lại là ai khởi xướng: hệ thống mời khi learner nhảy vào vùng chưa có bằng chứng, hoặc learner tự bấm. Cùng một Experience, cùng một cách chấm.

7.1 Đổi tên và đổi chỗ đặt (REQ-LRN-42 — SRC-485) ​

Chủ dự án hỏi thẳng: "tôi vẫn không hiểu khi nào thì cần học vượt, khi nào thì không. Có thể bỏ hết cơ chế đi không?" Câu trả lời đã chốt: giữ cơ chế, đổi tên và chỗ đặt, vì cơ chế có việc thật (learner đã chắc một Unit mà vẫn phải đi hết sáu ô là bị phạt vì giỏi), còn chỗ gây khó hiểu nằm ở hai thứ khác:

  • Tên "Học vượt" mô tả kết quả, không nói ĐIỀU KIỆN dùng. Tên mới "Con biết rồi?" là câu hỏi learner tự trả lời được ngay.
  • Chỗ đặt cũ là ô đầu tiên của chuỗi dọc, tức đúng vị trí của "bước 1", nên nó đọc thành việc phải làm trước. Nay nó nằm NGOÀI chuỗi, trong một khung riêng phía trên kèm câu nói rõ khi nào dùng; đường đi bên dưới bắt đầu bằng một ô học thật.

Cơ chế không đổi: chỉ mở ở Unit chưa học, một lượt, phải đúng tất cả, trượt thì chờ 3 tiếng (SRC-141).

7.2 Màn kết quả và cách trình bày đề (REQ-LRN-43, REQ-LRN-44) ​

Một nút chính duy nhất, không đổi theo kết quả (SRC-488). Bản trước để "Xem lại N chỗ sai" làm nút chính mỗi khi có câu sai, tức learner làm tốt thì được đi tiếp còn làm chưa tốt thì bị chặn bằng một nút to đếm lỗi. Nay "Tiếp tục" luôn là việc chính; rà soát bài làm và làm lại xuống hàng link nhỏ. Dòng "làm lại N lượt" bị bỏ: nó là chi tiết cơ chế của lúc đang làm, đứng cạnh điểm số thì đọc thành lời chê kèm điểm.

Tách đề đọc hiểu không được cắt tại dấu lược (SRC-487). Luật cũ gộp mọi ký tự nháy vào một lớp ký tự nên "Don't judge a book by its cover." hiện thành lead "Don" và đoạn trích "t judge a book…". Luật mới: dấu mở và dấu đóng phải cùng một cặp, dấu mở phải đứng đầu chuỗi hoặc sau ký tự không phải chữ/số và ngay sau nó là chữ, dấu đóng thì ngược lại.

8. Luật ↔ nguồn ↔ test ​

LuậtNguồn đúngTest canh chừng
Menu không còn Compass; hàng chờ ở đầu Dashboard§1, §2e2e: menu không có mục Compass; /overview có khối hàng chờ trên cùng
Đúng 1 việc lớn + 2 việc nhỏ§2e2e đếm số thẻ trong hàng chờ
Không có nút "để sau"§2e2e: không tồn tại nút đó
Việc đang dở luôn là việc lớn§2.1 bậc 0unit test pickToday()
Thứ tự review → repair → exam_prep → new§2.1unit test pickToday()
Chưa có bằng chứng thì chặn cứng cho tới khi đo xong§3.1e2e: learner mới không mở được Unit
Đã có bằng chứng thì không bị hỏi lại§3.1unit test điều kiện kích hoạt
Mốc đo lại = 7 ngày kể từ lần cuối làm bài§3.2unit test theo now truyền vào
Report không có điểm số, tối đa 5 "đã vững", đúng 1 "bắt đầu từ đây"§4unit test dựng report + e2e
Sai 3 câu ở Unit quá sâu thì gợi tiên quyết, luôn có nút học tiếp§5e2e
Ba bậc hiện bằng ba chấm, chữ chỉ trong popover§6e2e

9. Cái gì KHÔNG làm ở vòng này ​

Ghi ra để lần sau không ai tưởng là quên:

  • Adaptive trong bài đo mở đầu (chủ dự án: "sẽ làm sau").
  • Phụ huynh xem Diagnosis Report (chủ dự án: "tôi nghĩ sau"). URL đã tách sẵn.
  • Phụ huynh ghim việc vào hàng chờ (chủ dự án: "tôi nghĩ sau").

8. Nguồn của quan hệ prerequisite (SRC-396) ​

Hé cửa ở §5, bậc "đang hổng" của hàng chờ ở §2.1, và câu "đang chặn N bài sau" đều đọc từ một nguồn: bảng unit_prereqs — prerequisite graph ở cấp Unit.

Vì sao cấp Unit chứ không cấp node: một Unit chứa tới ba node là biến thể theo lớp của cùng một ý, nên đồ thị cấp node buộc phải chọn "node chốt" cho mỗi Unit. Bản sinh máy đầu tiên (SRC-395) chọn node mức cao nhất, và 53/171 cạnh vì thế đi từ lớp cao xuống lớp thấp — hé cửa từng gọi tên một bài lớp 7 cho một em lớp 2. Sai kiểu ấy không làm gãy gì, nên không ai thấy cho tới khi đi đếm.

Luật của bảng:

  • Cạnh soạn tay theo quan hệ ngữ nghĩa, không sinh theo thứ tự bày trên bản đồ. Mỗi cạnh mang một câu rationale_vi và câu đó hiện thẳng cho learner ở màn hé cửa.
  • Cạnh được phép xuyên Package — 72/225 cạnh hiện là loại này, và đó là phần đắt nhất: "Giải phương trình một ẩn" (B2) đứng trên "Dấu bằng nghĩa là hai bên cân nhau" (B1).
  • Hệ tự soạn tự chốt (REQ-PLT-12): cạnh vào đời ở status='auto' và có hiệu lực ngay. Coral là chỗ người đọc lại và nâng cấp (reviewed/edited), không phải cổng duyệt. Chỉ rejected mới bị runtime bỏ qua, và dòng vẫn được giữ để lần sinh sau không đề xuất lại.
  • Không chu trình — scripts/seed-unit-prereqs.mjs dừng hẳn nếu phát hiện (AS-06.1.3).

§8b. Độ phủ và file nguồn của graph (SRC-423, SRC-425, SRC-433, SRC-435) ​

Chương trình phủ graph nối tiếp SRC-396 chốt ba luật:

  • Mọi môn đang hiện có graph soạn tay, mỗi cạnh mang rationale_vi. Độ phủ hiện tại: cả 11 môn đang hiện (sáu môn cốt lõi + Tin học + GenAI/AP/IELTS/SAT ở SRC-433/435).
  • File soạn trong repo là bản gốc, bản trong D1 chỉ là bản nạp (SRC-423): graph Ngữ văn từng chỉ tồn tại trong D1 vì file soạn của đợt cũ đã mất — sửa một cạnh khi ấy là mò trong bảng chứ không phải sửa văn bản có diff. scripts/*-unit-prereqs.json là nơi sửa; nạp lại xoá cạnh status='auto' không còn trong file (cạnh người đã xem lại thì giữ).
  • Cổng chặn cạnh đi ngược lớp chạy ngay lúc nạp (SRC-425): một prerequisite phải nằm ở lớp bằng hoặc thấp hơn bài nó chặn; cổng này bắt được 19 cạnh ngược đang chạy trên production, trong đó 16 nằm trong graph Toán do chính đợt SRC-396 sinh ra. Môn không còn file nguồn dọn bằng scripts/drop-backwards-prereqs.mjs — GỠ chứ không đảo chiều, vì rationale_vi đang mô tả chiều cũ, và một lý do mô tả ngược quan hệ còn khó phát hiện hơn là không có cạnh.

9. Việc đang dở đè lên mọi đề xuất (SRC-396) ​

§2.1 đã đặt "đang dở" ở bậc 0, nhưng cho tới SRC-396 bậc ấy gần như không bao giờ kích hoạt: learner_experience_state CHỈ được ghi lúc làm xong. Bỏ dở giữa chừng không để lại vết nào, nên hàng chờ không thấy gì đang dở và rơi xuống bậc "bước tiếp" — tức là mời learner một việc mới, đúng lúc họ vừa bỏ dở một việc.

Hai chỗ sửa: mở một Experience ra là ghi status='started' ngay (POST practice/start nhận thêm exp_key), và câu lấy việc dở lọc trong HAVING để lấy unit dở gần nhất, thay vì lấy unit chạm gần nhất rồi loại đi nếu nó đã xong.

Kèm một ràng buộc để việc này không đẻ ra lối tắt: luật mở khoá ô kế tiếp vẫn chỉ tính completed/failed, KHÔNG tính started — nếu không thì mở bài ra rồi thoát ngay sẽ trở thành cách nhanh nhất để đi tiếp.

10. Khung ba cột cho MỌI trang trong môn, và hai view của bản đồ (SRC-441, SRC-442) ​

Chủ dự án 2026-08-21, kèm hai ảnh chụp:

  • Mọi trang trong một môn dùng khung BA CỘT: rail trái (SRC-291) · nội dung · panel phải. Panel phải mang status của chính learner — chuỗi ngày, XP hôm nay, số Unit đã vững (XpBar, SRC-307) — trước đây chỉ màn Học có, nay Dashboard/Plan/Labs/Luyện đề/Lịch thi/Hồ sơ đều có. Lý do như SRC-291: cùng một môn mà mỗi trang một khung thì learner phải học lại cách đi ở từng trang. Đang làm bài (immersive) vẫn ẩn cả hai cột rìa.
  • "Con đã làm gần đây" là một card riêng như mọi card khác, không còn là cụm chip trần.
  • Toàn bộ vùng Mastery nằm trong MỘT card, có hai view chuyển được:
    • View "đường đi" (mặc định, giữ cách hiển thị cũ) — nhưng Unit Card bỏ link "Vào học" và bỏ ba chấm bậc: cả thẻ đã là một nút, dòng lệnh và dải chấm là chữ/ký hiệu thừa trên một thẻ vốn phải tối giản (SRC-048).
    • View "tất cả" (mới): bày toàn bộ Package, mỗi Package một card chứa các Module card, mỗi Module card chứa các Unit card; Module và Unit card mang một bar-chart ngang (đúng MasteryBar — không vẽ kiểu khác để hai view vẫn là một ngôn ngữ). Bấm bất kỳ card nào mở popover: "I will be able to…" + nút Focus — Focus dùng đúng API ghim SRC-418, nên mọi luật ghim (tối đa 3, một loại, tự rụng khi vững) giữ nguyên; Package không ghim được nên popover của Package không có nút Focus, chỉ có mục tiêu và số liệu.
  • Trang Kế hoạch giảm chữ tăng hình (SRC-442): các đoạn "Vì sao:" rời khỏi mặt thẻ vào popover dấu i (một luật với SRC-304); tiến độ chặng nói bằng thanh, không bằng câu; "Kế hoạch đã đổi gì" rút còn một dòng. Cùng tinh thần luật tối giản SRC-048: mặt thẻ là hình và con số, chữ giải thích đứng sau một cú bấm.

§10b. Chỉnh sau khi nhìn bản chạy thật (SRC-444) ​

Bốn quyết định bổ sung của chủ dự án khi xem production:

  • Chữ không được trôi ngoài lề: "Vì sao lại là việc này" nằm TRONG card Việc hôm nay (nó giải thích chính việc đó), "Con đang ở đâu" nằm TRONG card Mastery (nó là câu hỏi của chính bản đồ).
  • View "Tất cả" là mặc định và bày MỘT cột cho thoáng — bản hai cột nhồi chữ vào thẻ hẹp.
  • Panel phải mở đầu bằng card môn học hiện tại — mọi con số bên dưới đều thuộc bối cảnh đó — rồi tới khu ghim "Con đang tập trung vào" và "Con đã làm gần đây": chúng là lối đi nhanh của learner, không phải nội dung bản đồ, nên đứng ở rìa. Hai cột đồng bộ trạng thái ghim qua một sự kiện trần (n12:pins), không nhấc state lên App.
  • "I will be able to…" phải hiện khi bấm card — thiếu là do script đồng bộ chỉ quét file được index.md link tới (index Ngữ văn chỉ link 3/8 file); sửa thành quét mọi file trong thư mục. Unit không có trang Pearl thì popover không có câu này — lấp bằng nội dung Pearl, không chế ở UI.

§10c. Một cách bày duy nhất (SRC-445) ​

Chủ dự án bỏ hẳn view "đường đi": bản đồ chỉ còn cách bày "tất cả" và vì thế không còn công tắc nào — một cách bày thì không bắt learner chọn trước khi đọc. Popover của thẻ trở thành điểm hội tụ: câu "I will be able to…" đứng đầu, Unit có nút Vào học (popover chính là bước xác nhận, thay dialog SRC-396 cũ), Module/Unit có nút Focus. Khu "Con đang tập trung vào" ở panel phải cũng bỏ: thẻ đang focus mang một ngôi sao nhỏ ngay trên card — dấu hiệu tại chỗ thay cho một khu riêng.

Hai việc dữ liệu cùng đợt: cột name_vi của strands B1-B5 và V1-V5 từng chứa tên tiếng Anh ("decode") — sửa theo phần tên Việt trong ngoặc của Pearl; Pearl thiếu 5 unit của cây V đã được soạn bổ sung ngay trong Pearl (nguồn) rồi sync xuống, không chế câu ở UI. 692 unit thuộc strand ngoài cây Pearl vẫn không có i_will — treo ở SRC-445 chờ chủ dự án chốt hướng.

11. Học theo từng UNIT — trang chi tiết Unit (SRC-541, SRC-573) ​

Chủ dự án 2026-08-24: "learner học theo từng Unit. Khi vào làm một Learning Experience hay Assessment Experience thì thực ra đang đứng trong một Unit… Thoát ra thì về trang xem chi tiết một Unit."

Trang chi tiết Unit dựng trong apps/learn/src/Learning.tsx, cùng khuôn với màn Experience (một card, nút thoát ở góc, tiêu đề Unit kèm "Đã đi x/y chặng"). Danh sách Experience lấy từ chính unitPath() mà Đường học dùng — một nguồn cho hai bề mặt nên chúng không thể lệch nhau. Mỗi dòng ghi rõ Learning Experience hay Assessment Experience, kèm nhãn Lớp G · B21 x/12.

Ba lối vào đều đổ về đây:

Lối vàoTrướcNay
Bấm một Unit từ Dashboardcuộn tới vị trí Unit trên cả Đường học dàivào thẳng trang chi tiết
Thoát một Experiencenhảy về Toàn cảnh (SRC-499)về Unit đang đứng
Nút "Học tiếp"—mở đúng chặng kế chưa xong

Đổi đích thoát là đảo có chủ ý so với SRC-499: học theo Unit thì thoát một bài vẫn phải đứng trong Unit đó, không bị ném ra cả bản đồ.

SRC-573 — trang này ban đầu chưa dịch. Bật EN rồi bấm vào một Unit thì gặp nguyên tiếng Việt, chủ dự án đọc ra thành "bấm vào lại nhảy sang một cái gì đó khác". Bài học: một trang mới phải đi qua bảng lang.tsx ngay từ đầu, vì người viết luôn xem bản tiếng Việt nên tự kiểm sẽ không thấy.

12. Chọn bài theo TẦM LỚP, cả hai nhánh (SRC-545, SRC-573) ​

Chủ dự án 2026-08-24: "đã khai lớp 8 mà hệ vẫn liên tục đưa bài lớp 2, lớp 5", và "đầu năm lớp 8 thì phải đưa bài lớp 7 để ôn cực kỳ chắc lớp 7, sau đó mới lớp 8".

Bộ chọn có hai nhánh đưa bài ra, và ban đầu chỉ sửa một nhánh nên lỗi vẫn còn:

Nhánh bài NỀN (SRC-545). Cửa sổ G-3..G xếp theo "chặn nhiều Unit nhất" khiến learner lớp 8 vớ phải Unit có node lớp 5. Nay ba vòng nới dần, vòng nào có bài thì dừng:

  1. G-1..G, xếp lớp thấp trước — ôn nền năm ngoái rồi mới tới lớp hiện tại;
  2. G-3..G — cho learner hổng sâu hơn;
  3. toàn môn — thà học bài xa lớp còn hơn màn hình trống.

Nhánh VIỆC ĐANG DỞ (SRC-573). Bài lớp 5 do hệ mời sai tầm trước bản vá; learner bấm vào một lần là nó thành "đang dở" và tự giữ chỗ đầu hàng mãi mãi. Một lời mời sai một lần, sửa gốc rồi mà hậu quả vẫn tự nuôi nó. Nay việc đang dở lệch quá 2 lớp thì thôi làm việc chính (isOutOfGradeSpan() trong queue.ts), xuống thẻ "đã làm gần đây" và vào lại được bất cứ lúc nào — chuyện thứ tự ưu tiên, không phải chặn đường.

Ba ranh giới cố ý: ngưỡng 2 chứ không phải 1 (ôn lại bài năm ngoái, năm kia là bình thường và đáng khuyến khích); Unit cao hơn lớp learner không bao giờ bị hạ (học vượt là quyền của learner); thiếu dữ liệu lớp thì luật tự tắt.

Bài lớp thấp hơn cửa sổ vẫn tới được với learner — nhưng qua đường đo thấy hổng (repair/urgent) hoặc tự chọn trên bản đồ, không qua lời mời mặc định.

13. Dọn cây: tách Unit lệch tầm và chuẩn hoá thang level (SRC-547, SRC-548) ​

Tách 27 Unit gộp node lệch ≥ 2 lớp (SRC-547). Cây B21 cố ý dạy lại cùng kỹ năng ở tầm cao hơn (spiral), nhưng các bản mang cùng tên Unit nên giao diện gộp làm một — learner lớp 8 mở Unit ra gặp hàng "Lớp 2" ngay trên hàng "Lớp 5". Tách bằng hậu tố " (lớp G)" trên tên Unit; node.id giữ nguyên nên mọi evidence cũ còn nguyên.

Ba sổ ăn theo tên Unit phải dời cùng:

  • learner_experience_state: khoá node-scoped dời theo đuôi :<node id>; khoá mức-unit (skip/mid/final) không quy được về bản nào nên để nguyên — chấp nhận mất dấu "đã xong" của Đo giữa/Đo cuối ở các Unit từng gộp (đo lại được, evidence gốc không mất).
  • unit_prereqs: khoá cũ trỏ về bản thấp nhất — prerequisite nghĩa là nền.
  • labs: gắn theo node_id nếu trùng, không thì bản gần tầm nhất.

Chuẩn hoá thang level (SRC-548). Môn Toán trộn hai thang: node seed qua toLevel100 mang 0-100, đợt b21g89 mang thẳng B21 1-12. Đường học sắp thứ tự bằng level nên đang so hai thước lẫn nhau. Luật đổi: level ≤ 12 và |level − grade| ≤ 3 thì quy về thang 100.

Chỉ đổi Toán — và đây là phạm vi thu hẹp có chủ ý, không phải làm dở: kiểm tra lộ ra Tin/Sinh/Hoá dùng level làm số thứ tự trong module (1..5, hơn 338 node), đổi hàng loạt là phá thứ tự của chúng.

14. Cổng readiness vào Unit — thang sửa có ngân sách (SRC-618, REQ-LRN-45) ​

Chủ dự án 2026-08-27: "cần cơ chế readiness để đảm bảo học sinh học đủ pre-requisites trước khi bước vào một unit mới. Nhưng đừng đệ quy mãi mãi để dẫn learner liên tục đi ngược về quá khứ."

§5 đã có "hé cửa": learner vào một Unit quá sâu, sai 3 câu rồi hệ mới gợi bài tiên quyết. Cổng ở mục này trả lời câu còn thiếu — nhìn trước khi learner trượt — và trả lời câu khó hơn nhiều: lùi tới đâu thì dừng.

Phần khó không phải tìm bài tiên quyết; unit_prereqs (§8) đã có sẵn và 72/225 cạnh xuyên Package. Chính vì xuyên Package nên đi ngược mãi là dẫn một em lớp 8 về bài lớp 2 mà không câu lệnh nào sai — đúng kiểu hỏng đã phải vá một lần ở nhánh bài nền (§12, SRC-545).

14.1 Ba trạng thái cửa, và một trạng thái thứ tư quan trọng hơn cả ba ​

Điểm vào cửa = trung bình mức đạt của các Unit tiên quyết trực tiếp, mỗi Unit tính clamp01(mastery / 0.5).

ĐiểmCửaLearner thấy gì
≥ 0.7xanhKhông thấy gì cả — không có việc để nói thì đừng nói (SRC-048)
0.4 – 0.7vàngMột câu, không dựng đường vá
< 0.4đỏĐề nghị thang sửa, kèm tổng số phút
confidence < 0.4chưa biếtMời đo 3 câu, không mời học lại

Ô cuối là đòn bẩy chống-đệ-quy mạnh nhất, và nó là một luật chứ không phải một tối ưu: chưa đo ≠ đang hổng. Đo một Unit tốn 3 phút, học lại tốn 30. Nếu mặc định coi chưa-đo là hổng thì learner mới — người chưa có bằng chứng nào — bị lùi về tận đáy môn ngay ngày đầu, đúng nỗi lo chủ dự án đặt ra. Mọi nhánh chưa có bằng chứng vì thế kết thúc bằng một probe, không bằng một bậc thang.

Hệ quả ở tầng dữ liệu: learner_skill_state có dòng prior (evidence_count = 0, mastery 0.5) cho learner chưa làm gì. Đọc ẩu thì dòng ấy thành "đã đo và hổng". Cổng phân biệt mastery = null (chưa đo) với mastery = 0 (đã đo, đang hổng) — và có test canh đúng chỗ đó.

14.2 Bốn cái phanh, chạy cùng lúc, chạm cái nào cũng dừng ​

PhanhNgưỡngVì sao đúng con số đó
Lớpkhông lùi tới Unit ở lớp < sànĐúng hằng số GRADE_SPAN_MAX = 2 hàng chờ đang dùng (§12). Một hằng số dùng hai chỗ thì không lệch nhau được
Độ sâutối đa 2 hopHop 1 giải thích được ("bài này đứng trên bài kia"). Hop 3 thì lý do hết liên quan tới việc learner vừa bấm vào
Số lượngtối đa 3 Unit, hiện 1 việc chínhCùng luật với Diagnosis Report §4 và hàng chờ §2
Thời giantối đa ~30 phútNỗi sợ đo được thì quyết định được; cảm giác thì không

Thêm một luật làm chuỗi hội tụ trước khi bốn phanh kịp chạm: chỉ đi sâu qua một Unit khi nó đã đo VÀ thật sự hổng. Unit đạt ngưỡng → dừng nhánh, không xét ông tổ của nó. Unit chưa đo → thành probe, cũng dừng. Bốn phanh là lưới an toàn, không phải cơ chế chính.

Cắt thì phải nói ra. Mỗi lần cổng bỏ bớt, nó trả về truncated: { reason, remaining } và màn hình ghi "còn N phần nữa, để sau". Cắt im lặng đọc ra thành "đã phủ hết" — đó là cách một cổng tự động mất lòng tin mà không ai chỉ ra được lúc nào.

14.3 Sàn lùi: bằng chứng thắng lời khai ​

sàn = min(lớp khai − 2, trung vị lớp của bằng chứng đã vững − 1)

Quyết định có chủ ý. Một em khai lớp 8 nhưng mọi bằng chứng nằm ở lớp 5 thì sàn G−2 = 6 chặn mất đúng chỗ cần vá, và cổng trở thành thứ ngăn em ấy học. Ngược lại, bằng chứng không bao giờ nới sàn lên cao hơn lời khai: một em mới vào chưa có bằng chứng gì thì trung vị rỗng, và khi ấy G−2 là con số duy nhất còn đáng tin. Không biết lớp thì luật theo lớp tự tắt chứ không đoán.

14.4 Cổng không khoá cửa, và chạm sàn thì gọi người ​

can_enter_anyway luôn true. REQ-LRN-25 đã chốt "hé cửa, không khoá" và mục này không đổi luật đó: chỗ duy nhất chặn cứng vẫn là đo mở đầu (§3.1, REQ-ONB-10). Learner tự thấy mình thiếu gì thì không sợ; bị chặn cửa thì mới sợ. Nút "Con cứ học tiếp ở đây" luôn có mặt, và bấm nó được ghi lại như dữ liệu (learner_gate_offers.entered_anyway), không phải như một thao tác bỏ qua.

Chống ping-pong bằng hai con số trong learner_gate_offers:

  • nghỉ 7 ngày cho mỗi cặp (learner, Unit) — lời mời lặp lại dạy learner cách bấm bỏ qua;
  • thử 2 lần tới sàn mà vẫn không qua → cổng im và gắn cờ cho mentor/phụ huynh. Đây là kết thúc trung thực của đệ quy: máy hết cách thì gọi người, không dẫn learner đi vòng nữa.

14.5 Cấp Module thì KHÔNG đệ quy ​

Đệ quy nguy hiểm nhất ở cấp Module vì một Module hổng kéo theo cả Package. Cấp Module vì thế dùng đúng một probe 6–10 câu lúc vào lần đầu — chính là cơ chế "Con biết rồi?" đã có (§7), đổi vai. Qua thì vào; không qua thì kết quả probe chỉ ra một Unit để bắt đầu, và từ đó mới áp thang sửa cấp Unit. Một tầng đệ quy duy nhất, không lồng nhau.

14.6 Luật ↔ nguồn ↔ test ​

LuậtNguồnTest canh chừng
Nền vững thì cổng im hẳn§14.1entryGate.test.ts "cổng im lặng"
Chưa đo thì mời ĐO, không mời học lại§14.1entryGate.test.ts + entryGate.d1.test.ts (evidence_count = 0)
Chuỗi hổng dài 10 bậc vẫn chỉ lùi 2 hop§14.2entryGate.test.ts "PHANH ĐỘ SÂU"
Tối đa 3 Unit · 30 phút · không dưới sàn§14.2ba test "PHANH …"
Cắt thì nói ra remaining§14.2"NÓI RA là đã cắt"
Bằng chứng hạ sàn, không nới sàn§14.3bốn test gradeFloor
Không bao giờ khoá cửa§14.4"cổng KHÔNG BAO GIỜ khoá cửa"
Nghỉ 7 ngày · thử 2 lần thì gọi người§14.4"chống ping-pong"
Thiếu bảng sổ thì cổng vẫn chạy§14.4entryGate.d1.test.ts

14.7 Cái gì KHÔNG làm ở vòng này ​

  • Probe cấp Module chưa nối dây: §14.5 chốt luật, nhưng lần này cổng mới chạy ở cấp Unit.
  • Cờ mentor chưa có mặt ở app Dolphin: escalate đã có trong dữ liệu, chưa có màn hình đọc nó.
  • Ước lượng phút lấy từ effort_minutes của node, cộng dồn theo Unit — thô, nhưng thà thô mà nói ra còn hơn không nói giá.