---
url: >-
  https://docs.nemo12.com/architecture/sdd-043-ai-teen-program/reading-gate-and-evidence.md
description: >-
  AI Teen (SDD-043) §10-11: lesson chia nhiều phần với ba câu kiểm sau mỗi phần;
  kết quả vào Evidence Registry và Learner Model.
---

# SDD-043 · Cổng đọc ba câu, Evidence Registry và Learner Model

Một phần của [SDD-043](./index.md). lesson chia nhiều phần với ba câu kiểm sau mỗi phần; kết quả vào Evidence Registry và Learner Model.

## 10. Cổng đọc: mỗi lesson chia nhiều phần, ba câu sau mỗi phần (SRC-1268)

Chỉ đạo chủ dự án 06.10.2026, cho MỌI lesson của mọi course AI Teen. Mẫu giao diện chung ở
[Guided Journey §10](../../design-system/guided-journey/index.md); luật soạn câu ở
[CD-8c](../../reference/course-design-standard/index.md). Mục này ghi phần riêng của AI Teen.

| Luật | Hiện thực |
| --- | --- |
| Bài đọc chia thành nhiều phần ngắn, mỗi lúc một phần | journey Bắt đầu = Câu hỏi + một bước có tên cho mỗi phần (`part-1` ... `part-n`), tên phần trên thanh, `Phần k` ở 375px |
| Cuối phần luôn có một nút | "Mình đọc xong rồi, kiểm tra nhanh nhé" (`GATE_BUTTON`); trong lúc chờ nút vẫn ở đó nhưng khoá, cạnh nó là "Làm lại sau 2:41" |
| Đúng 3 câu mcq siêu ngắn, phương án ngắn | 3 câu, mỗi câu 1 đáp án + 2 phương án nhiễu, mỗi nhiễu khai một hiểu lầm (`misconception`, không hiện cho learner) |
| Trả lời đủ 3 câu rồi mới nộp | "Nộp bài" khoá tới khi đủ |
| Chỉ báo tổng | "2/3 câu đúng"; câu hỏi đóng lại ngay khi nộp, không tô câu nào, không lời giải |
| Lời giải chỉ ở 3/3 | 3/3 thì hiện đáp án đúng và một câu vì sao cho cả ba câu |
| ~~Chỉ 100% mở phần sau~~ | **Bỏ từ SRC-1294** (chủ dự án 08.10.2026: "unlock everything"): không bước nào khoá; 3/3 chỉ làm phần ấy "xong". Trượt thì nút kiểm chờ 180 giây, nhưng nút sang phần sau vẫn ở đó |
| Trượt thì chờ 3 phút | 180 giây kể từ lúc lượt trượt kết thúc, đếm theo giờ SERVER |
| Còn nguyên khi tải lại | server trả `retry_in_ms`, client đổi thành mốc giờ máy; trình duyệt giữ bản sao |
| Xáo phương án khi làm lại | thứ tự xáo tất định theo (câu, số lượt), và không bao giờ trùng thứ tự lượt trước |
| Phần đã qua đọc lại không phải kiểm tra lại | mở thẳng nội dung, nhãn "Đã qua", nút sang phần sau |
| Tìm hiểu của lesson | giữ nguyên như cũ (SRC-1259), không bị khoá bởi cổng đọc |

**Nội dung nằm ở đâu.** `apps/learn/src/aiTeen/gates/<course>-u<n>.ts`, xuất `GATES`, mỗi lesson một
phần tử, cùng thứ tự `BODIES` của `lessons/`. Một phần khai `blocks` = số khối LIÊN TIẾP của bài đọc
mà nó hiện; các phần của một lesson phủ đủ mọi khối, đúng một lần. File bài đọc không đổi dòng nào:
cổng là một lớp đặt lên trên. Phủ: 18 unit, 84 lesson, 353 phần, 1059 câu từ SRC-1281 (AI Teen 1 sáu lesson mỗi unit, §12.6; SRC-1275: 72 lesson, 288 phần, 864 câu; trước đó 55 lesson, 214 phần, 642 câu; `gate.test.ts` kiểm từng
lesson: có cổng, phủ đủ khối, đúng 3 câu, 3 phương án khác nhau, mỗi nhiễu có hiểu lầm, có lời giải,
không ký tự cấm).

**Lượt kiểm tra nằm ở đâu.** Không có bảng mới, không migration: một dòng `ai_teen_journeys` (§9.4)
mỗi unit, scope `<course>:u<n>:gates` (CHECK của bảng đã nhận dạng này), `state_json` là map
`"<lesson>-<part>"` -> `passed`, `attempts`, `last_correct`, `last_attempt_at` (giờ server). Trạng thái
nhỏ và chỉ đọc theo learner + course, nên một bảng riêng không đáng một migration. `readAllJourneys`
loại scope `*:gates` ra, và `PUT /v1/ai-teen/journeys` không ghi được vào chúng (`parseScope` từ chối). API:
`GET /v1/ai-teen/courses/{id}/gates?learner_id=` trả mọi phần đã thử kèm `retry_in_ms`;
`POST /v1/ai-teen/courses/{id}/gates` nhận `{unit, lesson, part, correct, total}`. Gửi khi còn trong
thời gian chờ thì KHÔNG ghi, trả 200 `accepted: false` kèm thời gian còn lại; phần đã qua không bao
giờ bị khoá lại. Quyền như kết quả bài tập (§8): đọc cần quyền với learner, ghi còn phải đã ghi danh.
Số câu đúng do client đếm, cùng lý do §8. Learner chưa ghi danh: không gửi server, chỉ giữ ở
`localStorage` (`nemo12.aiTeen.gates.<learner>.<course>`); gộp với bản server thì phần đã qua thắng,
giữa hai lượt trượt thì thời gian chờ dài hơn thắng.

**Thanh Bắt đầu dài hơn năm bước.** Luật SRC-1259 "mỗi journey vài bước" có một ngoại lệ: Bắt đầu được tới
7 bước (Câu hỏi + tối đa 6 phần), vì chủ dự án đòi NHIỀU phần ngắn; thanh có tên cuộn ngang được.
Lesson đã chạm mà chưa qua phần nào nay mở ở Bắt đầu (phần đầu chưa qua), không còn mở thẳng Tìm hiểu.

**Code và kiểm chứng.** Luật thuần ở `apps/learn/src/aiTeen/gate.ts` (`shuffledOptions`, `score`,
`totalLine`, `showExplanations`, `retryIn`, `countdown`, `afterAttempt`, `fromServer`, `mergeGates`),
giao diện ở `ReadingGate.tsx`, trí nhớ ở `useGates.ts`, bước thanh ở `journeys.ts`. API:
`workers/api/src/modules/aiTeen/gates.ts` + `gates.test.ts` (180 giây theo giờ server, tải lại còn
nguyên, phần đã qua không khoá lại, mỗi phần đếm riêng, không lẫn vào danh sách hành trình, 400/401/404/409). e2e
`apps/learn/e2e/aiTeen.spec.ts` thêm ba ca dùng `page.clock` (đủ luồng trượt, chờ, tải lại, xáo, qua;
phần đã qua; chưa ghi danh) và hai trạng thái ở 320/375px.

## 11. Evidence Registry và Learner Model (SRC-1257)

Chỉ đạo chủ dự án 06.10.2026: nối AI Teen vào Learner Model và Evidence Registry. Đường ghi ở
`workers/api/src/modules/aiTeen/evidence.ts`, lát cắt ở `workers/api/src/modules/aiTeen/layer.ts`,
`subject_id = 'ai_teen'`.

| Việc của learner | Nguồn (`EVIDENCE_SOURCES`) | Node | Ghi khi |
| --- | --- | --- | --- |
| Trắc nghiệm trong lesson | `ai_teen_concept` (0.6) | `ai-teen:{course}:u{n}` | lần thử ĐẦU mỗi câu |
| Bài thực hành | `ai_teen_practice` (0.45) | `ai-teen:{course}:u{n}` | lần đầu tick đủ checklist |
| Performance Task tự chấm | `ai_teen_task` (0.5) | `ccs:{skill}`, mỗi skill của task một dòng | rời Assess lần đầu |
| Level tự đánh giá | `self_report` (0.5), type `ai_teen_self_level` | `ccs:{skill}` | rời Where am I? lần đầu |

* ✍️ **Mỗi việc một dòng**, khoá bằng `event_id` cố định: bấm lại, tick ra tick vào, lưu lại cùng
  trạng thái không đẻ thêm bằng chứng (sổ bằng chứng không phải sổ công sức, bài học SRC-849).
* ✍️ **Trắc nghiệm chỉ ghi lần thử đầu**: các lần sau learner đã thấy phản hồi của lần trước.
* ✍️ **Tự chấm theo rubric quy về skill thế nào**: rubric không nói tiêu chí nào đo skill nào, nên
  mỗi skill của task nhận mức TRUNG BÌNH của cả rubric (thang 1-5). Task chưa có rubric thì learner
  tự chấm thẳng theo skill (thang 1-4).
* **Skill của từng task ở server** (`taskSkills.ts`): client gửi bản tự chấm, server không tin client
  nói "rubric ấy đo skill nào". Bản kê này được `program.test.ts` bên learn đối chiếu với nội dung.
* **Learner Model** dựng lại ở nền tại mốc chuyển chặng (rời Where am I?, rời Assess, xong unit).
  Lát cắt `ai_teen` (SDD-002 §24) giữ cạnh nhau level tự khai, level task tự chấm và level đích, cùng
  số unit xong, số câu hiểu khái niệm đúng ngay lần đầu và số bài thực hành xong.
* Xem lát cắt: hai trang riêng, xem §12.
* ✍️ Cả ba nguồn mới `feeds_mastery: false`, và bằng chứng đều là tự chấm: lát cắt không gọi đó là
  năng lực đã xác minh. Mentor duyệt hoặc AI nhận xét Performance Task là đợt sau.
