Audit Standard · AS-04 tới AS-06
Một phần của Audit Standard. Chỉ báo AS-04 dữ liệu và migration (6 tiêu chí), AS-05 trí tuệ học tập và thuật toán, AS-06 nội dung và tri thức.
AS-04 — Dữ liệu & Migration (6 tiêu chí)
| ID | Chỉ báo | Cách kiểm |
|---|---|---|
| AS-04.1 | Kỷ luật viết migration | |
| AS-04.1.1 | 🔴🚸 Numbering: không trùng số (lỗ được phép từ SRC-1340) | ls migrations/*.sql | xargs -n1 basename | cut -c1-4 | sort | uniq -d phải rỗng — đã enforce trong ci.yml bước 7 |
| AS-04.1.2 | CREATE TABLE/CREATE INDEX có IF NOT EXISTS (ADD COLUMN miễn trừ) | grep -L "IF NOT EXISTS" migrations/*.sql đối chiếu file có CREATE TABLE/CREATE INDEX |
| AS-04.1.3 | 0 DML phá hoại không có WHERE lọc NULL/trạng thái | grep -n "DELETE FROM|UPDATE " migrations/*.sql đọc từng dòng, xác nhận có WHERE |
| AS-04.1.4 | 🔴🚸 npm run migrate:dryrun — toàn bộ sequence áp thành công từ D1 rỗng | chạy lệnh, exit 0 — đã enforce trong ci.yml bước 8 |
| AS-04.1.5 | Mọi file migration đã viết đều đã áp lên D1 production (chiều file→applied; chiều ngược — applied→file — kiểm ở AS-04.6.1) | wrangler d1 migrations list nemo12-platform --remote — "No migrations to apply" nghĩa là mọi file đã áp |
| AS-04.2 | Thiết kế schema | |
| AS-04.2.1 | Mọi bảng có PRIMARY KEY khai báo | schema dump: wrangler d1 execute nemo12-platform --remote --command "SELECT name FROM sqlite_master WHERE type='table'" đối chiếu từng CREATE TABLE có PK |
| AS-04.2.2 | 🔴 users.id là PK định danh; email chỉ UNIQUE, không bao giờ là đích FK | grep -n "REFERENCES users(email)" migrations/*.sql phải rỗng |
| AS-04.2.3 | Mọi cột *_id có REFERENCES, hoặc nằm trong danh sách miễn trừ tường minh ở data-dictionary.md | grep -oE "[a-z_]+_id" migrations/*.sql đối chiếu có REFERENCES cùng bảng/cột |
| AS-04.2.4 | Cột *_at kiểu TEXT (không INTEGER/DATETIME); bảng append-only có ≥1 cột thời điểm, 0 dòng lệch ISO-8601 UTC | grep -n "_at INTEGER|_at DATETIME" migrations/*.sql phải rỗng |
| AS-04.2.5 | Không json_extract/LIKE trên cột *_json trong WHERE/ORDER BY của workers/api/src (scripts/ chạy tay miễn trừ) | grep -rn "json_extract|LIKE '%" workers/api/src --include="*.ts" |
| AS-04.3 | Ngữ nghĩa & enum | |
| AS-04.3.1 | Mọi bảng D1 có mô tả trong reference/data-dictionary.md | đối chiếu — script gen-reference.mjs |
| AS-04.3.2 | Mỗi cột có mô tả nghiệp vụ không rỗng và không đúng bằng tên kiểu dữ liệu lặp lại (vd cột status mà mô tả = "status"/"TEXT" là KHÔNG PASS) — thay thế bản v0.1 "ý nghĩa nghiệp vụ được giải thích" không có biên rõ, xem §1.9 | đọc data-dictionary.md, script so mô tả cột với tên kiểu (case-insensitive) |
| AS-04.3.3 | Không bảng nào trong data-dictionary mà không tồn tại thật trong migrations | đối chiếu danh sách bảng |
| AS-04.3.4 | Mọi enum/trạng thái liệt kê đủ giá trị hợp lệ, khớp CHECK(...) ở tầng DB | grep -n "CHECK.*IN (" migrations/*.sql đối chiếu data-dictionary.md |
| AS-04.3.5 | Mỗi thực thể có cột trạng thái có trạng thái được vẽ trong reference/state-machines.md, tập giá trị khớp CHECK(...) | đối chiếu |
| AS-04.4 | Toàn vẹn vận hành | |
| AS-04.4.1 | Không script trong scripts/ ghi D1 production thiếu cờ --prod tường minh; script chạm production in tên DB+số dòng, dừng chờ xác nhận | đọc script — grep --remote không kèm confirm prompt |
| AS-04.4.2 | Mỗi seed script chạy 2 lần liên tiếp trên D1 local cho cùng số dòng (idempotent); script không chạy được local = KHÔNG PASS | chạy 2 lần, SELECT COUNT(*) so sánh |
| AS-04.4.3 | PRAGMA foreign_key_check production rỗng — chỉ có nghĩa khi AS-04.2.3 PASS (phụ thuộc tường minh) | wrangler d1 execute nemo12-platform --remote --command "PRAGMA foreign_key_check" |
| AS-04.4.4 | Bảng khai có khóa nghiệp vụ trong data-dictionary.md có UNIQUE index THẬT trong sqlite_master | SELECT * FROM sqlite_master WHERE type='index' đối chiếu |
| AS-04.4.5 | Thực thể song ngữ bắt buộc (subjects/strands/skill_nodes) 0 dòng thiếu cặp _en | SELECT COUNT(*) FROM skill_nodes WHERE title_en IS NULL = 0 |
| AS-04.5 | Riêng tư & lưu trữ | |
| AS-04.5.1 | Mỗi loại dữ liệu learner có chính sách retention khai báo (retain_days>0, rationale không rỗng) | đọc docs retention (khi tồn tại) |
| AS-04.5.2 | Có đường xóa dữ liệu theo yêu cầu (account/child), được đặc tả | docs + endpoint |
| AS-04.5.3 | Model version/snapshot có chính sách giữ lịch sử rõ ràng (giữ bao lâu, bao nhiêu bản — kiểm CHÍNH SÁCH, không kiểm khối lượng dữ liệu) | state-machines.md mô tả chính sách |
| AS-04.5.4 | Data minimization: mỗi trường PII có lý do tồn tại được ghi (danh sách đóng, một dòng/cột) | data-dictionary.md §PII |
| AS-04.5.5 | 🔴🚸 (nâng từ thường lên 🔴 trong v0.2 — pilot chạm dữ liệu trẻ em thật, xem §1.3) Có phương án backup/khôi phục D1 được ghi lại và đã thử ít nhất một lần: lệnh backup, nơi lưu, lệnh restore, số bảng/dòng sau restore, ngày diễn tập gần nhất | đọc docs/ops/backup-restore-drill.md — phải có đợt gần nhất KHÔNG cũ hơn đợt migration lớn gần nhất; một đợt cũ hơn schema hiện tại là bằng chứng cho một hệ không còn tồn tại |
| AS-04.6 | Đối soát production (tiêu chí mới — xem §1.4) | |
| AS-04.6.1 | 🔴🚸 Đối soát 2 chiều d1_migrations(remote) ↔ migrations/*.sql(đĩa): mọi lệch (mồ côi hoặc chưa áp) được liệt kê và giải thích trong docs/ops/migration-reconciliation.md, ngày đối soát ≤7 ngày. Điều kiện PASS không phải "0 lệch tuyệt đối" (đó là AS-04.6.2) mà là "mọi lệch đã biết, đã ghi" — biến rủi ro "không ai biết" thành nợ đã khai báo | wrangler d1 execute nemo12-platform --remote --command "SELECT name FROM d1_migrations ORDER BY name" so với ls migrations/*.sql | xargs -n1 basename | sort; đối chiếu file migration-reconciliation.md. Trạng thái thật 2026-08-15: 2 dòng mồ côi (0022_whale_country_detail.sql, 0031_labs.sql) chưa được ghi ở đâu → KHÔNG PASS, mã HH |
| AS-04.6.2 | (không 🔴 — nợ dọn dần, tách khỏi 04.6.1 để không chặn pilot vì lý do sổ sách nhẹ) Số dòng mồ côi hiện tại = 0 | cùng lệnh trên AS-04.6.1, đếm dòng lệch = 0 |
| AS-04.6.3 | 🔴🚸 Schema-diff một lần: dump schema production thật so với schema sinh từ migrate:dryrun cục bộ, chuẩn hoá khoảng trắng, khớp cả hai chiều — không cần script CI mới, một lần chạy tay có ghi lại là đủ | wrangler d1 execute nemo12-platform --remote --command "SELECT sql FROM sqlite_master WHERE type='table' ORDER BY name" so với wrangler d1 execute nemo12-platform --local --persist-to .wrangler/dryrun-d1 --command "SELECT sql FROM sqlite_master WHERE type='table' ORDER BY name" sau khi chạy npm run migrate:dryrun; ghi kết quả vào migration-reconciliation.md |
| AS-04.6.4 | 🔴🚸 Bất biến tương thích ngược kể từ 2026-08-15 (không hồi tố — xem §1.5): 0 pattern DROP COLUMN/RENAME COLUMN/ADD COLUMN NOT NULL không DEFAULT trong migration có applied_at sau mốc này | với migration mới sau mốc: grep -n "DROP COLUMN|RENAME COLUMN" migrations/00[4-9]*.sql; ADD COLUMN.*NOT NULL không kèm DEFAULT trên cùng dòng |
| AS-04.6.5 | (cảnh báo, không chặn cứng) Nếu một filename từng xuất hiện trong d1_migrations production (không phải git history — xem §1.4) không còn tồn tại trên đĩa và chưa có dòng giải thích trong migration-reconciliation.md, CI nhắc bổ sung | so d1_migrations remote với ls migrations/*.sql; nguồn sự thật là bảng remote, KHÔNG dùng git log --all |
AS-05 — Trí tuệ học tập & Thuật toán
| ID | Chỉ báo | Cách kiểm |
|---|---|---|
| AS-05.1 | Định nghĩa model & versioning | |
| AS-05.1.1 | Mỗi model (Learner, Goal, Context, Retention, Planning, Parent…) có trang mô tả: đầu vào, đầu ra, cách cập nhật | reference/models.md |
| AS-05.1.2 | Mỗi model có định danh version (vd plan-v1) ghi vào bản ghi sinh ra | SELECT DISTINCT model_version FROM learner_model_versions |
| AS-05.1.3 | Mỗi lần model đổi sinh một snapshot version lưu lại, truy được theo thời gian | SELECT COUNT(*) FROM learner_model_versions > 0, có created_at |
| AS-05.1.4 | Không model nào chạy production mà thiếu trang mô tả trong reference | đối chiếu code (workers/api/src/modules/*/models hoặc tương đương) với models.md |
| AS-05.1.5 | Thay đổi thuật toán làm đổi kết quả đều tăng version, không sửa tại chỗ | git log -p spot-check thay đổi thuật toán gần nhất |
| AS-05.2 | Đường evidence → model | |
| AS-05.2.1 | Mọi cập nhật learner model bắt nguồn từ evidence có nguồn gốc ghi lại | đọc code + SELECT * FROM learner_evidence LIMIT 5 |
| AS-05.2.2 | 🔴 Personal state / self-report không ghi thẳng vào Learner Model, chỉ qua evidence | đọc code — mọi route self-report đi qua learner_evidence trước |
| AS-05.2.3 | Evidence có tier / độ tin cậy phân loại rõ | schema learner_evidence.tier + code gán tier |
| AS-05.2.4 | Lần trả lời đầu tiên là evidence; các lần sửa lại không tạo evidence trùng | test + code: learning.test.ts kiểm hành vi sửa lại |
| AS-05.2.5 | Mỗi engine run ghi lịch sử (thời điểm, đầu vào, đầu ra) chứng minh đã chạy thật | SELECT COUNT(*) FROM engine_runs > 0 |
| AS-05.3 | Engine correctness | |
| AS-05.3.1 | Mỗi engine có unit test hành vi (input → output kỳ vọng) | find . -name "*.test.ts" đối chiếu 1 file/module engine |
| AS-05.3.2 | Không có test kiểu source-regex (kiểm chuỗi trong mã nguồn thay vì hành vi) | đọc test — không readFileSync mã nguồn rồi .includes() |
| AS-05.3.3 | Mỗi engine có test cho biên / ca rỗng (learner chưa có evidence nào) | đọc test — case evidence: [] |
| AS-05.3.4 | Stopping criteria của diagnosis/adaptive có test chứng minh nó dừng | đọc test |
| AS-05.3.5 | 🚸 Test chạy trong CI và chặn merge khi đỏ | ci.yml bước 3 npm test — không || true, không continue-on-error |
| AS-05.3.6 | 🚸 Test e2e không kiểm đồng bộ một thứ do mạng điền vào bất đồng bộ — dùng expect.poll hoặc chờ một dấu hiệu trên màn | đọc test: expect(<mảng do route handler điền>) không có await là một cuộc đua (SRC-1040) |
| AS-05.4 | Retention & Mastery | |
| AS-05.4.1 | 🔴 Mastery không tự suy giảm theo thời gian (SDD-017 §1) | test bất biến retention/periodic.test.ts |
| AS-05.4.2 | Retention có decay theo thời gian với công thức được ghi lại trong reference | reference/retention-model.md + test |
| AS-05.4.3 | Retention được cập nhật định kỳ (cron), không chỉ khi có evidence | reference/schedules.md + wrangler cron config |
| AS-05.4.4 | Job định kỳ chỉ ghi trường phái sinh, không đụng anchor — có test bất biến | retention/periodic.test.ts |
| AS-05.4.5 | Tham số review window / spaced repetition được ghi trong reference, không chôn trong code | đối chiếu reference/retention-model.md với hằng số trong code |
| AS-05.5 | Giải thích được | |
| AS-05.5.1 | Mỗi recommendation kèm lý do (reason) người dùng đọc hiểu được | UI + API response |
| AS-05.5.2 | Mỗi recommendation ghi model version (+ prompt version nếu dùng AI) | SELECT model_version, prompt_version FROM ... |
| AS-05.5.3 | Learning Plan mới ghi được diff so với bản trước và vì sao đổi | UI /ke-hoach + planning.ts |
| AS-05.5.4 | Mọi output quan trọng có confidence hoặc mức chắc chắn | schema — trường confidence |
| AS-05.5.5 | Learner/parent có đường xem lại lịch sử phiên bản model liên quan tới mình | UI |
AS-06 — Nội dung & Tri thức
| ID | Chỉ báo | Cách kiểm |
|---|---|---|
| AS-06.1 | Knowledge graph toàn vẹn | |
| AS-06.1.1 | Mọi node có subject / grade / level xác định | SELECT COUNT(*) FROM skill_nodes WHERE subject IS NULL OR grade IS NULL = 0 |
| AS-06.1.2 | Không node mồ côi (không thuộc module/unit nào) | SELECT COUNT(*) FROM skill_nodes WHERE strand IS NULL OR unit IS NULL = 0 |
| AS-06.1.3 | Quan hệ tiên quyết không có chu trình | DFS trong scripts/seed-unit-prereqs.mjs trên unit_prereqs (dừng hẳn khi thấy chu trình); skill_edges cho các Package cũ |
| AS-06.1.4 | Mỗi unit đủ số experience tối thiểu theo SDD-010 §9 (3 assessment + 6 learning) | SELECT unit, COUNT(*) FROM items GROUP BY unit đối chiếu ngưỡng |
| AS-06.1.5 | Tên node/module/unit đủ cặp vi/en | SELECT COUNT(*) FROM skill_nodes WHERE title_en IS NULL = 0 |
| AS-06.2 | Chất lượng item | |
| AS-06.2.1 | Mọi item published có quality profile đủ 8 chiều CC-QAF | schema — cột chất lượng trên items (khi tồn tại) |
| AS-06.2.2 | Mỗi chiều chất lượng có evidence kèm theo, không chỉ điểm số trần | đọc bản ghi |
| AS-06.2.3 | Có multi-evaluator variance check trước khi publish | đọc pipeline |
| AS-06.2.4 | Mỗi item link tới ≥1 node và ≥1 dạng bài | SELECT COUNT(*) FROM items WHERE node_id IS NULL = 0 |
| AS-06.2.5 | Item bị learner report có đường vào hàng đợi rà soát | UI Coral + content_reports nối vào hàng đợi thật |
| AS-06.3 | Vòng đời nội dung | |
| AS-06.3.1 | 🔴 Nội dung production không sửa trực tiếp; mọi sửa đi qua candidate version | đọc code/quy trình — cột lifecycle trên items |
| AS-06.3.2 | Mỗi candidate có trạng thái vòng đời rõ ràng | schema |
| AS-06.3.3 | Publish chỉ xảy ra khi vượt ngưỡng chất lượng đã khai báo bằng số | đọc code |
| AS-06.3.4 | Có lịch sử: ai/cái gì publish, khi nào | SELECT published_by, published_at FROM items |
| AS-06.3.5 | Rollback nội dung về version trước là thao tác được hỗ trợ, không phải sửa tay DB | UI/endpoint |
| AS-06.4 | Đề thi thật & provenance | |
| AS-06.4.1 | Mỗi đề thật ghi đủ: trường/hệ thi, vòng, môn, năm học | SELECT * FROM exams WHERE source='real' đối chiếu cột |
| AS-06.4.2 | 🔴 Lưu URL nguồn, không lưu file đề | schema source_ref + R2 — xác nhận không có file đề trong R2 |
| AS-06.4.3 | 🔴 Lời giải tự biên soạn, không sao chép nguyên văn nguồn ngoài | spot-check ≥5 đề, so văn bản với nguồn |
| AS-06.4.4 | Crawl chỉ nguồn công khai, tôn trọng robots/ToS, ghi provenance đầy đủ | đọc SDD-014 + code crawler |
| AS-06.4.5 | Đề authentic bất biến sau publish (sửa = tạo version mới) | đọc code |
| AS-06.5 | Media & bản quyền | |
| AS-06.5.1 | Mọi media public đã qua moderation trước khi hiển thị | đọc pipeline |
| AS-06.5.2 | Mỗi media ghi nguồn / giấy phép | schema |
| AS-06.5.3 | Không dùng hình ảnh/logo bên thứ ba không có quyền | rà thủ công — spot-check thư mục asset |
| AS-06.5.4 | Media đúng kích thước/format tối ưu theo SDD-009 | kiểm file — file + kích thước |
| AS-06.5.5 | Media chứa hình ảnh trẻ em có cơ chế đồng thuận trước khi public | đọc code |