---
url: https://docs.nemo12.com/architecture/sdd-038/evidence.md
description: >-
  Evidence Registry, IELTS Learner Model, sân luyện ghi vào sổ bằng chứng, bảng
  Where am I strong và luật Strong
---

# SDD-038 · Sổ bằng chứng và thang band

> Một phần của [SDD-038 Cổng IELTS](index.md). Phần đầu là đặc tả đang áp dụng, mỗi mục giữ số § cũ; diễn biến và lý do các vòng quyết định nằm ở mục **Lịch sử quyết định** cuối trang.

## 5b. Evidence Registry và IELTS Learner Model (chỉ đạo 2026-09-17, vòng 4)

**Một cửa ghi.** `shared/evidence.ts` là chỗ duy nhất ghi vào `learner_evidence`. Trước đó bảy module tự viết câu INSERT riêng, mỗi chỗ tự chọn một hằng số `reliability` (0.85 · 0.8 · 0.75 · 0.7 · 0.6 · 0.5) mà không chỗ nào nói vì sao và không chỗ nào biết chỗ kia chọn số mấy. Bảng `EVIDENCE_SOURCES` nay trả lời hai câu cho từng nguồn: đáng tin tới đâu, và **có được phép đẩy mastery không**.

`feeds_mastery` là khác biệt quan trọng nhất. "Ghi vào sổ" và "được đổi hồ sơ năng lực" là hai việc: một nhận định của model về bài viết **phải** được ghi (nó là việc đã xảy ra với learner), nhưng **không** được đẩy mastery (nó là ý kiến, không phải phép đo). Chốt đặt ở đường ghi chứ không ở lời dặn: nguồn khai `false` mà vẫn truyền `mastery_before/after` thì cặp số ấy bị bỏ.

Nhịp 2 nối bốn nguồn trước nay im lặng: chép chính tả, bài viết, đoạn ghi âm, và quiz luật chơi. Bài viết và ghi âm vào sổ **ngay khi nhận**, không đợi chấm — việc đã xảy ra là "đã viết một bài", không phải "đã được model cho một con số".

**Lỗ ấy đã vá** (chỉ đạo chủ dự án 2026-09-17): `apps/ielts` nay có đăng nhập. Thêm **đăng nhập, không phải thêm tường** — 1.560 câu và 140 trang nội dung ở đó đọc được từ ngoài vào, và đó là lý do chúng tồn tại. Người chưa đăng nhập luyện y như cũ, không bị chặn một câu nào; đăng nhập rồi thì bài làm mới được ghi về hồ sơ. Phiên dùng chung cookie `.nemo12.com`, nên ai đã đăng nhập ở learn thì mở trang này ra là đã có tài khoản sẵn.

Ba chi tiết của lần vá này đáng giữ:

* **Một BÀI một lượt, không phải một CÂU một lượt** (bảng `ielts_practice_attempts`, migration 0230). Learner chấm từng câu khi làm, nhưng đơn vị có nghĩa là cả bài; ghi từng câu sẽ đẻ mười ba dòng cho một bài đọc và một đống bài dở dang không bao giờ kết thúc.
* **Chốt chống gửi trùng nằm trong `ref`, không trong state.** `useEffect` chạy lại mỗi lần state đổi và chạy hai lần ở StrictMode; một cờ trong state chỉ được đặt sau render kế tiếp, tức là vẫn kịp gửi hai lượt.
* **Router riêng ở server.** Mọi đường khác của `modules/ieltsPractice` cố ý không có `requireSession` vì chúng phục vụ trang công khai; đường ghi này thì gác vai. Gộp chung một router là sớm muộn có người thêm một đường ghi vào nhầm bên, và bên ấy không gác gì cả.
* **Không có `anonymous_id`,** và cố ý không có: gắn định danh cho người chưa đăng nhập là dựng một hồ sơ theo dõi sau lưng họ.

**IELTS Learner Model có bảng riêng** (`ielts_learner_models`, migration 0229). `layer.ts` vẫn là chỗ chứa toàn bộ suy luận và không lặp lại ở đâu; `model.ts` lo ba thứ nó cố ý không lo: đọc bằng chứng từ sổ, ước lượng bốn band, và **lưu ảnh chụp**. Trước đó lát cắt dựng lại mỗi lần mở trang rồi vứt đi, nên hệ không nói được "Reading của con đã đi từ 5.5 lên 6.0" — câu mà một đứa trẻ luyện sáu tháng cần nghe nhất.

Band ước lượng theo đúng ba nguồn, từ đáng tin nhất: `mentor_band` → `ai_band` → điểm tự khai. **Không có nguồn thứ tư**: bài đo hai mươi tư câu không được quy ra band (giới hạn đã ghi ở `diagnostic.ts`), nên Listening và Reading thường chỉ có số khi learner tự khai. Một ô trống nói đúng điều đó còn hơn một con số nói sai.

Ảnh chụp chỉ ghi khi **có gì đổi** (thêm bằng chứng, đổi bản thuật toán, hoặc một band đổi) — mở trang mười lần không đẻ mười dòng giống nhau. Câu đọc "ảnh mới nhất" phá hoà bằng `rowid DESC`: `created_at` chỉ tới mili-giây, và hai ảnh trong cùng một mili-giây là chuyện xảy ra thật.

## 5b-bis. Sân luyện nối trọn vào sổ bằng chứng (SRC-849, chỉ đạo 2026-09-19)

Rà lại sau SRC-792 thì **ba chỗ đứt**, và cả ba đứt im lặng — API trả 201, learner thấy đúng màn hình, chỉ cuốn sổ là trống:

| Đường learner đi | Trước SRC-849 | Sau |
| --- | --- | --- |
| `POST /learning-events` — bài tập năng lực nhỏ, vocab, replay, read | chỉ vào `learning_events` | lượt **có đo** vào sổ với nguồn `ielts_micro`, rồi tính lại model |
| `POST /ielts-ladder/writing` + `/speaking` (track `practice`) | chỉ vào `ielts_productions` + `learning_events` | vào sổ với `ielts_writing`/`ielts_speaking`, rồi tính lại model |
| Suy luận của lát cắt | `evidenceBySkill` chỉ đọc `assessment_responses` của bài đo | thêm `practiceEvidence.ts` đọc thẳng Evidence Registry |

**Hai cuốn sổ, không phải một.** `learning_events` là sổ **công sức** (mở lại một đoạn nghe, tra một từ, ngồi hai mươi phút); `learner_evidence` là sổ **bằng chứng**, mỗi dòng phải trả lời được "đúng mấy trên mấy". Nên chỉ lượt `items > 0` mới vào sổ thứ hai. Đổ lượt không đo vào đó sẽ hỏng hai chỗ cùng lúc: `confidence` của lát cắt tăng theo số dòng, nên bấm phát lại nhiều thành "hệ tự tin hơn" mà không chứng minh thêm gì; và `evidence_count` là chốt ghi ảnh chụp, nên mỗi lần bấm lại đẻ một dòng lịch sử giống hệt dòng trước.

**Rổ luyện tập đứng RIÊNG với rổ bài đo.** Dồn chung thì câu "bài đo đúng 4/6" thành một câu nói dối, và `calibrate` đem lời khai đi so với một hỗn hợp mà chính nó không biết thành phần. Bài đo là một lượt **đo** có kiểm soát; buổi luyện là learner tự chọn bài, thấy đáp án ngay, làm lại được. Hai thứ ấy không cộng được.

**Bằng chứng luyện tập KHÔNG đẩy band** (chủ dự án chốt 2026-09-19). Thứ tự `mentor_band` → `ai_band` → tự khai giữ nguyên, không có nguồn thứ tư. Rổ luyện nuôi ba thứ khác, tất cả đều nói được mà không cần một con số 0..9:

* **độ tin cậy**, trần riêng 0.15 và mỗi câu chỉ đáng một phần sáu câu bài đo. Không có trần ấy thì một learner cày ba trăm câu luyện đạt tin cậy tối đa trong khi hệ **chưa từng đo họ lần nào** — tức là hệ tự tin nhất đúng lúc nó biết ít nhất;
* **xu hướng** 14 ngày so với 14 ngày trước, chỉ nói khi **cả hai** cửa sổ đủ 6 câu. "Đang khá lên" dựa trên ba câu là một lời hứa dữ liệu không chống lưng được;
* **thứ tự ưu tiên**, biên độ 3 — bằng một nửa phần bài đo. Kỹ năng đang **tụt** được cộng thêm, kỹ năng đang **lên** không bị trừ: hai vế cố ý không đối xứng, vì đẩy một kỹ năng đang có đà xuống cuối danh sách là làm learner bỏ dở đúng thứ đang chạy tốt.

**Kỹ năng lấy từ đâu ra.** `learner_evidence` không có cột `skill`, và thêm một cột chỉ cho IELTS vào bảng dùng chung cho mọi môn là sai chỗ. Quy ước mới: `node_id = 'ielts:{skill}'`. Dòng ghi **trước** quy ước (`node_id` rỗng) vẫn đọc được qua `source` + `type`, nên bằng chứng đã nằm sẵn trong sổ đếm được ngay chứ không bắt learner luyện lại từ đầu để lịch sử của chính mình có giá trị.

**Điều kiện ghi ảnh chụp đổi theo.** Trước SRC-849, chỉ cần `evidence_count` khác lần trước là ghi một dòng mới. Điều ấy đúng khi sổ chỉ nhận bài đo và bài nộp — những việc hiếm; từ khi mỗi bài tập nhỏ là một mẩu thì nó biến một buổi hai mươi bài thành hai mươi dòng lịch sử có bốn band y hệt nhau, đúng thứ §5b cấm. Nay so **kết quả**: bốn band, độ tin cậy, và xu hướng từng kỹ năng. Luật này tự thưa dần — `confidence` chạm trần rồi thì buổi luyện thứ năm mươi không ghi dòng nào nữa, tức là lịch sử dày lúc đang đổi nhanh và thưa lúc đã ổn định, đúng hình dạng người đọc muốn thấy. `evidence_count` vẫn được lưu trên dòng (nó trả lời "ảnh này dựng trên bao nhiêu mẩu"), chỉ là không còn quyết định có ghi hay không.

`IELTS_MODEL_VERSION` lên **2**. Ảnh chụp cũ vẫn đọc được đúng như lúc ra đời, và màn admin **nói ra** khi ảnh đang xem do bản cũ sinh: người vận hành cần biết điều đó trước khi kết luận "hệ đổi ý về đứa trẻ này" — có thể chỉ là cách tính đã đổi, và hai nguyên nhân dẫn tới hai hành động khác hẳn nhau.

**Xem ở admin:** `GET /v1/admin/learners/{id}/ielts-model`, màn hình ở `admin.nemo12.com/#/models/{learnerId}/ielts`. Trang bày ba tầng theo đúng thứ tự người vận hành cần đọc — hệ đang nói gì → bằng chứng nào nuôi kết luận ấy → đã đi từ đâu tới đâu. Thiếu tầng giữa thì tầng đầu là một lời tuyên bố không kiểm được, mà đó đúng là thứ bảng lịch sử dựng ra để tránh. Mở trang **không** tự tính lại: người đi điều tra mà làm thay đổi hiện trường thì không còn điều tra được nữa, nên tính lại là một nút bấm, và cái bấm ấy để lại một dòng lịch sử.

## 22. The "Where am I strong?": hai cot, bo chu lap (SRC-877, 2026-09-19)

**Hai cot.** Muoi nang luc xep mot cot doc day moi thu dung sau no xuong duoi man hinh, nen learner phai cuon het bang moi thay duoc cac loi vao luyen tap. Nay `sm:grid-cols-2` - mot cot o man hep, hai cot tu 640px.

Vach ngan doi tu `divide-y` sang vien duoi tung o. `divide-y` chi dung voi MOT cot doc; trong luoi hai cot no ke ca nhung canh khong phai ranh gioi giua hai dong lien nhau. O cuoi moi cot van giu vien, va do la co y: hai cot khong nhat thiet dai bang nhau nen bo vien theo `last:` se cat nham mot ben.

**Bo chu lap, nhung chi o cho no lap.** Voi learner moi, ca muoi dong deu mang dung mot cau *"8 more to rate"* va dung mot chu *"not yet"* - muoi lan noi cung mot dieu, va dieu ay thi thanh rong da noi roi, noi bang hinh. Hai chu nay nay chi bien mat KHI CHUA XEP DUOC BAC.

Dong DA co bac giu nguyen ca nhan (*Solid*) lan phan so (*9/12*). Go luon hai thu ay thi gon hon nua nhung la go mat chinh thong tin: phan so that noi luon co mau, con co mau la thu quyet dinh con so dang tin toi dau (xem muc 5c).

**Mot lo hong cong kiem duoc lap cung luc.** The nay truoc do KHONG co ca e2e nao - duong `ielts-skills` chua co mock nen `mastery` luon undefined va the tu an di, lang le. Nay fixture tra ve muoi nang luc TRON HAI TRANG THAI (co bac va chua du cau), vi chinh cho giao giua hai trang thai ay la noi chu thua moc ra.

Ma: `MasteryRow` va `MasteryCard` trong `apps/learn/src/ielts/Hub.tsx`. Cong kiem: mot ca trong `apps/learn/e2e/smoke.spec.ts`.

## 55. Bảng "Where am I strong?" đọc theo thang band (SRC-941, 2026-09-21)

**Chín vạch, không phải mười.** Chín là số bậc của thang band IELTS, nên mỗi vạch đọc ra đúng một
band thay vì một phần mười vô danh. Cùng một cái liếc mắt, nhưng nay nó trả lời được câu learner
thật sự mang đi thi. Số vạch nằm ở hằng `BARS` trong `apps/learn/src/ielts/Hub.tsx` — một chỗ cho
cả bốn kỹ năng, vì thẻ chỉ có một bản.

**Nhãn bậc xuống cạnh cụm vạch, phần trăm biến mất.** Hai thứ ấy đi thành cặp: phần trăm và cụm
vạch nói y hệt một điều, chỉ khác cách viết, nên bỏ một giữ một là bỏ đúng phần thừa; còn nhãn thì
thêm thứ cụm vạch không nói được — *bấy nhiêu vạch ấy gọi là gì*.

Đây **không** phải lần quay lại của mấy nhãn bị gỡ ngày 2026-09-20 (§31): thứ bị gỡ khi ấy là
PHÂN SỐ ("9/12"), vốn mời learner so tử số hai dòng có mẫu số khác nhau. Nhãn ở đây chỉ có một từ,
và một từ thì so được với nhau. Dòng chưa đủ câu vẫn **không** có nhãn nào: ở đó chưa có bậc để
gọi tên, và một chữ thay thế kiểu "not yet" đúng là thứ chỉ đạo hôm ấy đã gỡ.

Bản mẫu vẽ chữ "Band 7.5" cho từng năng lực nhỏ. Hệ thống thật **không có** band ở mức ấy —
`workers/api/src/modules/ieltsSkills/mastery.ts` xếp bậc chứ không chấm band — nên nhãn mang đúng
bậc thật. Bịa một con số band vào đây là in ra một điểm thi chưa ai chấm.

**Mỗi dòng là một khối HAI DÒNG** (chỉ đạo vòng hai cùng ngày, kèm bản mẫu): tên ở dòng trên,
cụm vạch ở dòng dưới, pill đè vào đúng ô mà con số phần trăm chiếm trong bản mẫu, mũi tên neo bên
phải, tất cả nằm trong một nền bo góc. Bản một dòng trước đó phải buộc tên vào một cột rộng cố
định (`w-48`) vì đó là cách duy nhất giữ mép trái của mọi cụm vạch thẳng hàng, và cái giá là một
tên ngắn như "Detail" bị bỏ lại cách cụm vạch của chính nó gần nửa cột. Xếp hai dòng thì hết cả
hai cùng lúc: cụm vạch bắt đầu ở mép trái của khối nên tự nó thẳng hàng ở mọi dòng và ở cả hai cột
của lưới, còn tên dài bao nhiêu cũng được vì nó không còn đẩy thứ gì. Mũi tên là dấu hiệu bấm
được: cả dòng vốn đã là link, nhưng trên màn cảm ứng thì không có động tác rê chuột để phát hiện
ra điều đó.

**Đầu thẻ căn trái.** Tiêu đề lớn ở mép trái với một dòng nhãn nhỏ phía trên, câu hướng dẫn cách
đọc bảng chuyển từ cuối thẻ lên ngay dưới tiêu đề, và lối đi "Or practise by task type" thành một
nút bên phải cùng hàng. Câu hướng dẫn in sau bảng thì learner chỉ gặp lúc đã đọc xong và đã tự
đoán ra một luật nào đó, thường là luật sai; lối đi phụ đứng dưới đáy thì learner đã cuộn qua nó
trước khi kịp hỏi *"còn cách chia nào khác không"*. Ba thẻ số liệu phía trên cũng căn trái theo
cùng lẽ: mắt đọc một hàng thẻ bằng cách bám một đường dọc bên trái, mà tiêu đề căn giữa cho mỗi
thẻ một điểm bắt đầu khác nhau tuỳ độ dài câu hỏi.

Gác: `apps/learn/e2e/smoke.spec.ts` — ca "Where am I strong: hai cột…" nay đếm **chín** vạch, đòi
nhãn bậc hiện ra trên dòng đã xếp bậc, và đòi **không** còn dấu `%` nào trên dòng. Số vạch là thứ
hỏng im lặng: mười vạch vẫn vẽ ra một hàng đẹp, chỉ là mỗi vạch không còn đọc ra một band nào cả.
Toàn bộ 262 ca của bộ e2e chạy xanh trên bản build thật.

## 111. Luật "Strong" chặt hơn (SRC-1032, 25.09.2026)

* **Mỗi bài chỉ tính lần làm gần nhất.** Làm lại bài đã quen đáp án không phải bằng chứng mới.
* **"Solid" và "Strong" cần ít nhất `MIN_ADVANCED` = 5 câu ở bậc 3 trở lên** (tiếng Anh). Đủ tỉ lệ
  mà thiếu thì dừng ở "Getting there". Câu trong bài đọc/nghe thật (`ielts_subskill_results`) tính
  là nâng cao.

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

### Từ mục 5b-bis. Sân luyện nối trọn vào sổ bằng chứng (SRC-849, chỉ đạo 2026-09-19)

Chỉ đạo: *"Hãy đảm bảo là khi learner vào học ielts trong learn.nemo12.com/ielts, thì mọi evidence đều được lưu vào evidence registry, rồi sau đó được suy luận và tính toán để update IELTS Learner Model. Rồi tôi có thể xem IELTS Learner Model trong admin.nemo12.com."*

### Từ mục 22. The "Where am I strong?": hai cot, bo chu lap (SRC-877, 2026-09-19)

Chi dao chu du an: *"tach cai bang WHERE AM I STRONG thanh 2 cot. Bo bot cac text '8 more to rate' hay 'not yet' di cho gon."*

### Từ mục 55. Bảng "Where am I strong?" đọc theo thang band (SRC-941, 2026-09-21)

Chỉ đạo chủ dự án 2026-09-21, kèm một bản mẫu giao diện: sửa trang kỹ năng theo bản mẫu ấy, và
với riêng bảng năng lực thì *"chuyển 'Band 7.5' xuống chỗ 70%, bỏ 70% đi"* và *"chuyển số lượng
box của mỗi sub-skill từ 10 xuống còn 9"*, cho **cả bốn** trang kỹ năng.

### Từ mục 111. Luật "Strong" chặt hơn (SRC-1032, 25.09.2026)

Chủ dự án hỏi vì sao learner "Hồng" mới làm ba bài Main Idea đã "Strong". Dữ liệu production:
warm-up L1 3/3 làm hai lần + một bài L1 5/5 = 11/11, vượt `MIN_ITEMS` 8 và ngưỡng 85%. Đúng luật cũ,
sai về ý nghĩa. Chủ dự án đồng ý sửa hai chỗ:

Với Hồng: nay 8 câu, "Getting there".
