---
url: https://docs.nemo12.com/quality/audits/2026-08-15-002.md
description: >-
  Audit #002 ngày 15.08.2026 chấm theo AS v0.2: tổng kết, tách KHÔNG PASS theo
  mã lý do, 30 chỉ báo chặn release và Pilot Gate.
---

# Audit #002 — 2026-08-15

Chấm theo [Audit Standard AS-01..AS-10 v0.2](../audit-standard/index.md) (255 chỉ báo, 10 bộ, AS-04 có 6 tiêu chí) trên commit `89a3db46a1`, branch `main`, cộng production thật (D1 `nemo12-platform` remote, GitHub Actions thật qua `gh run list`/`gh api`, Cloudflare Queues/Access thật).

**Phương pháp:** ba lượt chấm độc lập được hợp nhất theo đúng thứ tự ưu tiên ghi trong nhiệm vụ — (1) chấm gốc theo cả 10 bộ, (2) chấm sâu riêng 18 chỉ báo 🚸 Pilot Gate, (3) một lượt **phản biện** chạy lại phần lớn bằng chứng bằng lệnh độc lập (không tin lời agent trước). Với mỗi chỉ báo: nếu phản biện có chạm tới, verdict cuối cùng là của phản biện (kể cả khi nó ghi đè verdict Pilot Gate); nếu không, chỉ báo trong danh sách Pilot Gate dùng verdict Pilot Gate; còn lại dùng verdict chấm gốc. Toàn bộ 255/255 chỉ báo đã có bằng chứng trong ba lượt trên — **không có chỉ báo nào phải tự chấm bổ sung** ở bước rà soát này.

## 1. Tổng kết

| Bộ | PASS/tổng | % | Mức |
| --- | --- | --- | --- |
| AS-01 Tài liệu & Đóng vết | 19/25 | 76% | Vững |
| AS-02 Yêu cầu & Sản phẩm | 17/25 | 68% | Đang dựng |
| AS-03 Kiến trúc & Hợp đồng API | 20/25 | 80% | Vững |
| AS-04 Dữ liệu & Migration | 15/30 | 50% | Đang dựng |
| AS-05 Trí tuệ học tập & Thuật toán | 17/25 | 68% | Đang dựng |
| AS-06 Nội dung & Tri thức | 8/25 | 32% | Sơ khai |
| AS-07 Bảo mật, Riêng tư & Tuân thủ | 16/25 | 64% | Đang dựng |
| AS-08 Tin cậy & Vận hành | 19/25 | 76% | Vững |
| AS-09 Trải nghiệm & Design System | 7/25 | 28% | Sơ khai |
| AS-10 AI Governance & Chi phí | 19/25 | 76% | Vững |
| **Toàn hệ** | **157/255** | **61,6%** | **Đang dựng** |

```text
Mức trưởng thành:  Sơ khai < 50% │ Đang dựng 50-69% │ Vững 70-84% │ Chín 85-94% │ Chuẩn mực ≥95%
AS-03 ████████████████░░░░  80%
AS-08 ███████████████░░░░░  76%
AS-01 ███████████████░░░░░  76%
AS-10 ███████████████░░░░░  76%
AS-02 █████████████░░░░░░░  68%
AS-05 █████████████░░░░░░░  68%
AS-07 ████████████░░░░░░░░  64%
AS-04 ██████████░░░░░░░░░░  50%
AS-06 ██████░░░░░░░░░░░░░░  32%
AS-09 █████░░░░░░░░░░░░░░░  28%
```

**Điểm hệ so với sàn REQ-DOC-06 (≥225/255, ~88%):** 157/255 = 61,6% — thiếu **68 chỉ báo** để chạm sàn mở người dùng thật theo REQ-DOC-06. Đây là ngưỡng dài hạn cho toàn hệ, **không phải điều kiện cho đợt pilot 2 ngày cụ thể sắp tới** — theo quyết định ✍️ [Q-128](../../open-questions/batch-2026-08-13.md#q-128--pilot-gate-thay-req-doc-06-làm-điều-kiện-mở-thử-2-ngày-2026-08-15), điều kiện go/no-go cho pilot là **🚸 Pilot Gate** (§4 dưới đây), không phải con số này.

**Điều kiện release (dài hạn, §3.1 audit-standard/index.md):** KHÔNG ĐẠT — vẫn còn 9/30 chỉ báo 🔴 KHÔNG PASS (xem §3), 2 bộ dưới 60% (AS-06 32%, AS-09 28%), điểm hệ 61,6% < 70%.

## 2. Đọc đúng — tách KHÔNG PASS theo mã lý do

| Mã | Số lượng | % trong tổng 98 KHÔNG PASS |
| --- | --- | --- |
| **HH** (hỏng — có phản ví dụ thật) | 56 | 57% |
| **CX** (chưa xây) | 29 | 30% |
| **CM** (chưa chứng minh) | 13 | 13% |

> Chỉ số đáng lo thật sự là **%HH trong tổng KHÔNG PASS = 57%** — cao hơn mong đợi cho một hệ 0 ngày tuổi kể từ Audit #001 (cùng ngày). Nghĩa là hơn một nửa số KHÔNG PASS không phải "chưa kịp làm" mà là **có bằng chứng cụ thể cho thấy nó sai** — code tồn tại, chạy được, và ra kết quả không đúng luật. Đây là hệ quả trực tiếp của một ngày có ~10 phiên AI cùng sửa song song: nhiều tính năng mới (Orca, community events, Whale, labs) được dựng nhanh nhưng docs/schema/pipeline chất lượng không theo kịp, và một script sửa nội dung thẳng production (`apply-review-fixes.mjs`) đã bỏ qua toàn bộ vòng đời publish đã xây.
>
> Ngược lại, 30% CX là kỳ vọng hợp lý ở giai đoạn này (ngân hàng đề thật, Media System, artifact luật tối giản AS-09.1 đều chưa tới lượt xây) và 13% CM là nợ **rẻ nhất** để đóng — phần lớn chỉ cần ai đó ngồi rà/viết test, không cần sửa kiến trúc.

## 3. 🔴 Chặn release — 30 chỉ báo (v0.2), 9 đang KHÔNG PASS

| Chỉ báo | Mã | Sự thật quan sát được |
| --- | --- | --- |
| **AS-06.3.1** | HH | `scripts/apply-review-fixes.mjs` UPDATE thẳng 679 dòng `items` production qua REST API, bypass hoàn toàn route `PATCH` (chặn 409) và toàn bộ vòng đời candidate→publish đã xây |
| **AS-06.4.2** | CX | 0 đề thi thật (`source='real'`) tồn tại; R2 bucket nội dung rỗng — không có gì để kiểm "chỉ lưu URL không lưu file" |
| **AS-06.4.3** | CX | 0 đề thật để spot-check lời giải tự biên soạn |
| **AS-07.1.1** | HH | `GET /v1/coral/labs` thiếu `requireSession` trong danh sách `.use()` — tái hiện được: `curl` ẩn danh vào route chạy local → HTTP 500 crash (session=undefined cho MỌI caller, kể cả staff hợp lệ) |
| **AS-07.3.2** | HH | `enforceConsent()` không có lời gọi thật nào trong repo; cơ chế duy nhất (`requireConsent`) chỉ chặn khi phụ huynh **đã rút** đồng thuận, không chặn khi **chưa từng được hỏi** — 0 dòng `consents` production, 0 màn hình consent frontend |
| **AS-09.3.1** | CM | axe-core thật chạy trên 3 màn chính: rule `color-contrast` luôn rơi vào `incomplete` vì hero dùng gradient phức tạp — không đo được dứt khoát bằng công cụ tự động, cần đo pixel-level |
| **AS-10.3.1** | CX | `buildLearnerContext()` chỉ được gọi trong test, 0 route sản xuất nào gọi — AI hiện **không chạm learner data qua bất kỳ đường nào**, kể cả Context Builder; bất biến "đường duy nhất" đúng một cách rỗng (tính năng nó bảo vệ chưa tồn tại) |
| **AS-04.6.3** | HH | Schema-diff thật: `whale_scholarships` lệch DDL text giữa production và `migrate:dryrun` cục bộ (dấu phẩy đầu dòng khác — hệ quả 2 lịch sử ALTER khác nhau), mâu thuẫn tuyên bố "giống hệt production" ghi trong `0031_whale_country_detail.sql` |
| **AS-03.5.3** | HH | Branch protection 403 (plan không phải Pro); README/CLAUDE.md không có câu cấm tường minh "không apply migration production ngoài CI"; và chính AS-04.6.1 đang có 2 dòng mồ côi phát sinh cùng ngày — cả 3 nhánh thoát trong "cách kiểm" đều fail |

**21/30 🔴 còn lại đã PASS**, gồm cả 4 chỉ báo mới/nâng cấp trong v0.2: `AS-04.6.1` (đối soát 2 mồ côi đã ghi lại đúng), `AS-04.6.4` (0 vi phạm tương thích ngược kể từ baseline), `AS-01.4.1`, `AS-03.2.4`, `AS-03.4.1`, `AS-04.1.1`, `AS-04.1.4`, `AS-04.2.2`, `AS-05.2.2`, `AS-05.4.1`, `AS-07.2.1`, `AS-07.2.2`, `AS-07.2.3`, `AS-07.5.1`, `AS-07.5.2`, `AS-08.2.2`, `AS-08.4.3`, `AS-10.1.1`, `AS-10.1.2`, `AS-10.3.5`, `AS-10.4.1`.

## 4. 🚸 Pilot Gate — điều kiện mở thử 2 ngày tới

Theo [Audit Standard §3.3](../audit-standard/index.md#33-pilot-gate-điều-kiện-mở-thử-2-ngày-tới) và quyết định ✍️ [Q-128](../../open-questions/index.md), đây — không phải điểm hệ toàn phần — là điều kiện go/no-go cho đợt mở thử 10 phụ huynh thật sắp tới. **100% 18 chỉ báo dưới đây phải PASS.**

| Chỉ báo | PASS? | Bằng chứng |
| --- | --- | --- |
| AS-07.1.1 | ❌ HH | Tái hiện thật: `curl` local → HTTP 500 trên `/v1/coral/labs` (thiếu `requireSession`) |
| AS-07.2.1 | ✅ | Mọi module chạm `learner_id` gọi `canAccessLearner`/`requireLearnerAccess`/tương đương |
| AS-07.2.3 | ✅ | 3 file `authz.test.ts`, 35/35 PASS, có test phủ định family khác |
| AS-07.3.2 | ❌ HH | Đồng thuận không chặn learner mới chưa từng được hỏi; 0 màn hình consent |
| AS-07.1.4 | ✅ | `curl` ẩn danh admin/dolphin/coral đều 302 Cloudflare Access login |
| AS-04.6.1 | ✅ | Đối soát 41 remote vs 39 đĩa, 2 mồ côi đã ghi lại đúng trong `migration-reconciliation.md` |
| AS-04.6.3 | ❌ HH | Schema-diff thật phát hiện `whale_scholarships` lệch DDL production vs local dryrun |
| AS-04.6.4 | ✅ | 0 vi phạm tương thích ngược trong migration kể từ baseline 2026-08-15 |
| AS-04.1.1 | ✅ | 39 file liên tục 0001-0039; CI enforce xanh tại commit snapshot |
| AS-04.5.5 | ❌ CX | `docs/ops/backup-restore-drill.md` chưa tồn tại — 0 lần diễn tập backup/restore |
| AS-03.5.3 | ❌ HH | Cả 3 nhánh thoát của "cách kiểm" đều fail (xem §3) |
| AS-08.4.1 | ✅ | `ci.yml`+`deploy-api.yml` không escape hatch; `gh run list` xác nhận chạy thật |
| AS-08.2.2 | ✅ | `wrangler queues list` thật xác nhận cả `nemo12-events` và `nemo12-events-dlq` tồn tại |
| AS-01.4.1 | ✅ | `gen:reference` 0 diff, xác nhận độc lập nhiều lần |
| AS-03.2.4 | ✅ | `contract:diff` 139 route, không breaking change, có ở cả 2 workflow |
| AS-10.3.1 | ❌ CX | Context Builder chưa được route sản xuất nào gọi — AI chưa chạm learner data qua đường nào |
| AS-10.3.5 | ✅ | `context-builder.test.ts` (tên khác quy ước glob trong "cách kiểm") 12/12 PASS, cách ly A/B thật |
| AS-10.4.1 | ✅ | Cả 2 đường sinh nội dung AI đều có cổng người/ngưỡng trước khi publish |

**Go/No-go: 12/18 — KHÔNG ĐẠT 100%. Kết luận: NO-GO cho đợt mở thử 2 ngày tới ở trạng thái hiện tại.**

6 chỉ báo chặn: `AS-07.1.1`, `AS-07.3.2`, `AS-04.6.3`, `AS-04.5.5`, `AS-03.5.3`, `AS-10.3.1`. Ba trong số này (`AS-07.1.1`, `AS-03.5.3`, `AS-04.6.3`) từng được lượt chấm Pilot Gate ban đầu đánh PASS dựa trên đọc tĩnh (grep/diff) — lượt phản biện chạy lại bằng chứng động (khởi động dev server thật, phân tích DDL text thật, đối chiếu 3 điều kiện "cách kiểm" từng câu) và lật lại thành KHÔNG PASS. `AS-10.3.1` bị lật theo hướng ngược lại: từ PASS (cả chấm gốc lẫn Pilot Gate) sang KHÔNG PASS/CX, vì phản biện chỉ ra bất biến "Context Builder là đường duy nhất" đang đúng một cách rỗng — chưa có tính năng AI nào chạm learner data để có đường vòng.

## 5. Chi tiết theo chỉ báo (10 bộ, 255 chỉ báo)

### AS-01 — Tài liệu & Đóng vết · 19/25 (76%)

| Chỉ báo | | Mã | Bằng chứng |
| --- | --- | --- | --- |
| **01.1 Nguồn duy nhất & sổ tiếp nhận** | | | |
| 1.1 | ❌ | HH | Xác nhận bằng lệnh production thật: cả 4 tên migration (2 cũ+2 mới) đều tồn tại remote, chứng minh tiền đề `migrations/README.md` §3 sai, mâu thuẫn `docs/ops/migration-reconciliation.md` |
| 1.2 | ✅ | — | `intake.md` 128 dòng SRC-001..128 đủ 5 cột, 0 lỗi định dạng ngày, dòng cuối khớp SRC-129 |
| 1.3 | ❌ | HH | 28 SRC ID không có hit ngoài `intake.md`, xác nhận lại đúng |
| 1.4 | ✅ | — | 128 dòng, 128 ID duy nhất, liên tục 001..128, "Nguồn tiếp theo"=129 khớp |
| 1.5 | ❌ | HH | Cùng bằng chứng 1.1 — `migrations/README.md` là đặc tả hệ thống thật, mâu thuẫn nội dung |
| **01.2 Frontmatter & metadata** | | | |
| 2.1 | ✅ | — | `check-docs.mjs` QG-001 PASS, 64 docs sạch frontmatter |
| 2.2 | ✅ | — | 64 file đủ 8 trường bắt buộc |
| 2.3 | ✅ | — | 64 id duy nhất, lowercase, khớp vai trò file |
| 2.4 | ✅ | — | 0 file có type ngoài tập cho phép |
| 2.5 | ✅ | — | 0 file status ngoài {draft,active,deprecated}, 0 deprecated hiện tại |
| **01.3 Đóng vết hai chiều** | | | |
| 3.1 | ✅ | — | `check-docs` mục 2: 0 lỗi trace closure; mọi REQ ✔ có SDD |
| 3.2 | ✅ | — | 221 REQ ✔-M đều có QG-\d+ |
| 3.3 | ✅ | — | 18/18 SDD có `sources:`; 0 tham chiếu SRC hỏng |
| 3.4 | ❌ | HH | 2 file mồ côi: `reference/config.md`, `ops/migration-reconciliation.md` — 0 markdown link trỏ tới, không có trong `reference/index.md` |
| 3.5 | ❌ | HH | REQ-LRN-19/20/21 (MUST active) thiếu hoàn toàn khỏi §6, 0 US/WF nào nhắc tới |
| **01.4 Trang sinh tự động khớp source** | | | |
| 4.1 🔴🚸 | ✅ | — | `gen:reference` chạy lại, `git diff --stat` rỗng — xác nhận độc lập nhiều lần |
| 4.2 | ✅ | — | 79 bảng CREATE TABLE khớp 79 mục trong `data-dictionary.md` |
| 4.3 | ✅ | — | `contract:diff`: 139 route /v1, `api.md` khai đúng |
| 4.4 | ✅ | — | `events.md` 25 publish/21 event khớp `EVENT_REGISTRY`, 0 diff |
| 4.5 | ✅ | — | cả 3 trang tự sinh có dòng cảnh báo "sinh tự động" |
| **01.5 Tươi mới & vòng đời** | | | |
| 5.1 | ✅ | — | 0 file active có `last_reviewed` >90 ngày |
| 5.2 | ❌ | HH | 5/5 commit spot-check sửa REQ đáng kể không bump `version` (PRD-001 vẫn 0.6) |
| 5.3 | ✅ | — | `check-docs` mục 4: 0 link gãy trong 64 file |
| 5.4 | ✅ | — | 11 hit "tuần trước/gần đây/sắp tới", đếm lại đúng — tất cả là ngôn ngữ UI hoặc tự tham chiếu luật |
| 5.5 | ✅ | — | 49 dòng ✍️ trong `open-questions/index.md`, spot-check tham chiếu chéo khớp |

### AS-02 — Yêu cầu & Sản phẩm · 17/25 (68%)

| Chỉ báo | | Mã | Bằng chứng |
| --- | --- | --- | --- |
| **02.1 Chất lượng REQ catalog** | | | |
| 1.1 | ❌ | HH | Nhóm REQ `ORC` (9 REQ, Orca competitions) dùng thật nhưng không có trong bảng nhóm `conventions.md` §2 |
| 1.2 | ❌ | HH | REQ-BRD-07 dòng 243 dùng "tốt hơn" — khớp điều kiện cơ học của cách-kiểm (hit trong dòng REQ) |
| 1.3 | ✅ | — | 0 kết quả M?/S?/C? trong PRD-001 |
| 1.4 | ✅ | — | 259 dòng REQ §5, 0 dòng cột Nguồn rỗng |
| 1.5 | ✅ | — | proxy token-overlap, cặp trùng cao nhất 75% < ngưỡng 80% |
| **02.2 User story & acceptance criteria** | | | |
| 2.1 | ✅ | — | 83/83 US đúng format `As a **...**` |
| 2.2 | ✅ | — | 12 biến thể persona, mọi biến thể phân rã về 4 persona gốc |
| 2.3 | ✅ | — | cột AC riêng, 0/83 dòng rỗng |
| 2.4 | ✅ | — | AC mô tả hành vi quan sát được, 0 hit tên bảng/hàm/biến kỹ thuật |
| 2.5 | ❌ | HH | US-38/REQ-COM-03 ✔ nhưng không có màn hình forum learner thật (0 route/UI dùng) |
| **02.3 Persona, JTBD & phủ vai trò** | | | |
| 3.1 | ✅ | — | 4 persona active đều có ≥1 JTBD ghi nhận |
| 3.2 | ✅ | — | mọi JTBD có REQ cụ thể phục vụ |
| 3.3 | ✅ | — | 3 cộng đồng đều có WF onboarding riêng, status active |
| 3.4 | ❌ | HH | REQ-SCH-10 trỏ SDD-011 cho Octopus nhưng SDD-011 xác nhận 0 hit Octopus |
| 3.5 | ✅ | — | §8 bản đồ màn hình↔persona↔REQ tồn tại; sampling không thấy màn hình mồ côi |
| **02.4 Phạm vi & ưu tiên** | | | |
| 4.1 | ✅ | — | 0/259 REQ ✔ thiếu SDD/QG |
| 4.2 | ✅ | — | sampling learn/marlins/web: mọi route đối chiếu được với REQ |
| 4.3 | ✅ | — | 40 hit CC-/FEAT- đều là cột Nguồn hợp lệ, 0 dòng backlog lẫn bảng REQ chính |
| 4.4 | ❌ | CX | `legacy-feature-inventory.md` (status draft) không có cột "Phân loại" 4 giá trị như chỉ báo yêu cầu |
| 4.5 | ✅ | — | chỉ 1 REQ MUST ⏳ (REQ-INT-19), có lý do+ngày đầy đủ |
| **02.5 Quyết định mở & phê duyệt** | | | |
| 5.1 | ✅ | — | 165 Q, mọi Q rỗng ở bảng chính đều có dòng ✍️ tương ứng |
| 5.2 | ❌ | HH | 16/29 dòng ✍️ Đợt 1 vi phạm ngưỡng ≥15 ký tự (đếm lại khớp) |
| 5.3 | ✅ | — | artifact rà mâu thuẫn ✍️ có ngày hôm nay, khai luật ưu tiên, liệt kê 6 chỗ lệch, khai phạm vi rà |
| 5.4 | ❌ | HH | bảng "Việc còn chờ chủ dự án" chỉ 2 cột, thiếu cột Trạng thái |
| 5.5 | ❌ | CX | 0 kết quả "Đã vào" trong toàn file — cơ chế đóng vết Q chưa xây |

### AS-03 — Kiến trúc & Hợp đồng API · 20/25 (80%)

| Chỉ báo | | Mã | Bằng chứng |
| --- | --- | --- | --- |
| **03.1 Ranh giới module & monorepo** | | | |
| 1.1 | ✅ | — | 1 hit `createRoute` ngoài `modules/` (health route), khớp bảng ngoại lệ |
| 1.2 | ❌ | HH | Lệnh literal chỉ bắt import 2-cấp (0 hit); lệnh mở rộng đúng ý đồ chỉ báo tìm ra 23 import chéo 1-cấp giữa module, không qua `shared/` |
| 1.3 | ✅ | — | `fetch(` chỉ nằm trong đúng 1 file `src/api.ts`/app |
| 1.4 | ✅ | — | 0 custom property tự định nghĩa lại trong `apps/*/src/*.css` |
| 1.5 | ✅ | — | `ls apps/ workers/` khớp README; `apps/data` đã ngừng ghi rõ |
| **03.2 Hợp đồng API** | | | |
| 2.1 | ✅ | — | 0 file openapi.yaml/json ngoài snapshot |
| 2.2 | ❌ | HH | `PATCH /v1/content/items/{itemId}` thiếu response 2xx (chỉ 401/409) trong OpenAPI sinh ra |
| 2.3 | ✅ | — | 124 path, 0 path không bắt đầu `/v1` |
| 2.4 🔴🚸 | ✅ | — | `contract:diff` 139 route, không breaking change; có ở cả `ci.yml` và `deploy-api.yml` |
| 2.5 | ❌ | HH | `POST /v1/auth/google` body rỗng → rò nguyên ZodError thô, tái hiện 2 lần |
| **03.3 Hợp đồng event/queue** | | | |
| 3.1 | ✅ | — | 21 entry `EVENT_REGISTRY` đều có `version`+`payload` zod object |
| 3.2 | ✅ | — | đối soát 2 chiều `publishEvent()`↔registry khớp 100% |
| 3.3 | ✅ | — | 21 tên event `domain.entity.action`, action thì quá khứ |
| 3.4 | ✅ | — | `assertNoFreeText` tồn tại+được gọi; 0 `z.string()` trần trong payload |
| 3.5 | ✅ | — | `wrangler.jsonc` khớp đúng số `queues.md` |
| **03.4 Vệ sinh secret/config** | | | |
| 4.1 | ✅ | — | 0 pattern key/secret/token hardcode trong source hiện tại lẫn git history (đúng regex gốc) |
| 4.2 | ✅ | — | host `nemo12.com` chỉ dùng cho điều hướng chéo app hợp lệ |
| 4.3 | ✅ | — | `verify-bindings.mjs --offline`: Lớp A sạch |
| 4.4 | ❌ | CM | `gh run` CI thật: KV/R2 không verify được vì token thiếu scope, lặp lại ổn định |
| 4.5 | ✅ | — | `verify-bindings.mjs` Lớp B sạch, `config.md` khớp mọi binding+var |
| **03.5 Cấu trúc & kỷ luật pipeline deploy** | | | |
| 5.1 | ✅ | — | 9 `wrangler.jsonc` ↔ 9 `deploy-*.yml`, path filter khớp |
| 5.2 | ✅ | — | cả 9 `wrangler.jsonc` có `routes`/`custom_domain`, không `*.workers.dev` trần |
| 5.3 | ❌ | HH | 403 Pro + 0 câu cấm tường minh trong README/CLAUDE.md + 2 mồ côi thật phát sinh cùng ngày — cả 3 nhánh thoát đều fail |
| 5.4 | ✅ | — | `deploy-api.yml`: apply migration đứng trước deploy worker |
| 5.5 | ✅ | — | 9/9 URL production trả 2xx/3xx, khớp `custom_domain` |

### AS-04 — Dữ liệu & Migration · 15/30 (50%)

| Chỉ báo | | Mã | Bằng chứng |
| --- | --- | --- | --- |
| **04.1 Kỷ luật viết migration** | | | |
| 1.1 🔴🚸 | ✅ | — | 39 file liên tục 0001-0039; CI enforce xanh tại commit snapshot |
| 1.2 | ✅ | — | `grep -L IF NOT EXISTS`: 0 câu lệnh thật thiếu (5 hit ban đầu đều trong comment) |
| 1.3 | ✅ | — | 0 `DELETE FROM`; 4 `UPDATE` đều có WHERE lọc cụ thể |
| 1.4 🔴🚸 | ✅ | — | `migrate:dryrun` 39/39 OK, exit 0 |
| 1.5 | ✅ | — | `wrangler d1 migrations list --remote` → No migrations to apply |
| **04.2 Thiết kế schema** | | | |
| 2.1 | ✅ | — | script Python độc lập parse CREATE TABLE, 0/78 bảng thiếu PK |
| 2.2 🔴 | ✅ | — | 0 `REFERENCES users(email)`; `users.id` là PK, email chỉ UNIQUE |
| 2.3 | ❌ | HH | 67/68 cột `*_id` thiếu REFERENCES, chỉ 1 miễn trừ tường minh trong dictionary |
| 2.4 | ❌ | HH | `community_events.starts_at` lệch UTC (+07:00 thay vì Z) trên dữ liệu thật |
| 2.5 | ✅ | — | 0 `json_extract`/`LIKE '%'` trên cột `*_json` trong WHERE/ORDER BY |
| **04.3 Ngữ nghĩa & enum** | | | |
| 3.1 | ✅ | — | chạy lại `gen-reference.mjs`, `git diff` `data-dictionary.md` rỗng |
| 3.2 | ❌ | HH | 160 dòng mô tả cột rỗng hoàn toàn (`—`) |
| 3.3 | ❌ | HH | 79 mục doc vs 78 bảng thật — `interaction_votes_rebuilt` là bảng ma (RENAME không track) |
| 3.4 | ❌ | HH | CHECK nhiều dòng bị cắt cụt (`learner_goal_entries.kind`, `model_kind` thiếu enum) |
| 3.5 | ❌ | HH | `community_events`/`community_event_registrations` status enum không có mục trong `state-machines.md` |
| **04.4 Toàn vẹn vận hành** | | | |
| 4.1 | ❌ | HH | 5 seed script mặc định `--remote` production, dùng `--yes`, không dừng xác nhận |
| 4.2 | ❌ | HH | 20/23 seed script hardcode DB ID production, không nhánh chạy local |
| 4.3 | ❌ | CM | `PRAGMA foreign_key_check` rỗng nhưng chỉ có nghĩa khi AS-04.2.3 PASS — đang fail nên không phải bằng chứng toàn vẹn thật |
| 4.4 | ❌ | CX | 0 kết quả "khóa nghiệp vụ" trong `data-dictionary.md` — artifact chưa tồn tại |
| 4.5 | ❌ | HH | 24 dòng thiếu cặp `_en` trên production (subjects 4, strands 20) |
| **04.5 Riêng tư & lưu trữ** | | | |
| 5.1 | ❌ | HH | 4/10 chính sách retention có `keep_days=NULL` (không >0) |
| 5.2 | ✅ | — | endpoint `deletion-requests` đầy đủ, đặc tả luồng trong `data-semantics.md` §7 |
| 5.3 | ✅ | — | `data-semantics.md` §6 tự gắn mã AS-04.5.3, chính sách rõ ràng có lý do |
| 5.4 | ✅ | — | `data-semantics.md` §5: 17 trường PII, danh sách đóng, lý do không rỗng |
| 5.5 🔴🚸 | ❌ | CX | `docs/ops/backup-restore-drill.md` chưa tồn tại — 0 lần diễn tập |
| **04.6 Đối soát production** | | | |
| 6.1 🔴🚸 | ✅ | — | đối soát độc lập 41 remote vs 39 đĩa, 2 mồ côi khớp `migration-reconciliation.md` |
| 6.2 | ❌ | HH | 2 dòng mồ côi vẫn tồn tại, DELETE dọn nợ chưa chạy |
| 6.3 🔴🚸 | ❌ | HH | schema-diff thật: `whale_scholarships` lệch DDL text production vs local dryrun |
| 6.4 🔴🚸 | ✅ | — | baseline 2026-08-15, 0 migration vi phạm tương thích ngược |
| 6.5 | ✅ | — | 2 dòng mồ côi đều đã có dòng giải thích trong `migration-reconciliation.md` |

### AS-05 — Trí tuệ học tập & Thuật toán · 17/25 (68%)

| Chỉ báo | | Mã | Bằng chứng |
| --- | --- | --- | --- |
| **05.1 Định nghĩa model & versioning** | | | |
| 1.1 | ❌ | HH | `models.md` đánh dấu `learning_plan` "🕓 Phase 2" nhưng model chạy thật production (route/UI sống) |
| 1.2 | ✅ | — | `algorithm_version` đúng cột: `learning_plan/plan-v1`, `retention/retention-v1` |
| 1.3 | ✅ | — | COUNT=2, có `generated_at`, truy vết được |
| 1.4 | ❌ | HH | cùng bằng chứng 1.1 |
| 1.5 | ❌ | HH | đổi công thức mastery (thêm `reliability`) không tăng version; module không có hằng version nào (6 module khác đều có) |
| **05.2 Đường evidence → model** | | | |
| 2.1 | ✅ | — | `learner_evidence` có nguồn rõ; mọi route ghi mastery đều batch nguyên tử với evidence |
| 2.2 🔴 | ✅ | — | 4 vị trí duy nhất UPDATE `learner_skill_state`, đều evidence-gated |
| 2.3 | ✅ | — | cột `reliability` REAL, gán giá trị phân biệt rõ theo ngữ cảnh |
| 2.4 | ❌ | CM | 0 file test cho `modules/learning`, không test dedupe `event_id` |
| 2.5 | ❌ | HH | 8/8 dòng `engine_runs` có `input_json=NULL` — không lời gọi `runEngine` nào truyền input |
| **05.3 Engine correctness** | | | |
| 3.1 | ❌ | CM | Mastery/Readiness/Context engine không có test riêng |
| 3.2 | ✅ | — | 0 file test dùng `readFileSync` để source-regex |
| 3.3 | ❌ | CM | `updateMastery(null,...)` — ca evidence đầu tiên — chưa từng được test |
| 3.4 | ❌ | CX | chọn cố định 15 câu diagnostic, không có logic stopping criteria |
| 3.5 🚸 | ✅ | — | `ci.yml` không escape hatch; `gh run list` xác nhận cơ chế gate hoạt động thật |
| **05.4 Retention & Mastery** | | | |
| 4.1 🔴 | ✅ | — | `periodic.test.ts` test bất biến trực tiếp refresh không ghi decay vào anchor |
| 4.2 | ✅ | — | `retention-model.md` khớp CFG trong `engine.ts`; 27 test |
| 4.3 | ✅ | — | cron `0 21 * * *` khớp `schedules.md`; `workflow_runs` có dòng WF-17 succeeded |
| 4.4 | ✅ | — | UPDATE chỉ đụng cột phái sinh, không đụng anchor; có test bất biến |
| 4.5 | ✅ | — | tham số review window trong `retention-model.md` khớp CFG code |
| **05.5 Giải thích được** | | | |
| 5.1 | ✅ | — | `PlanBlock/PlanPhase.why` render trực tiếp ra UI `PlanPage.tsx` |
| 5.2 | ✅ | — | response trả cả `algorithm_version` và `model_version` |
| 5.3 | ✅ | — | `diffPlans()` + UI hiển thị đổi gì/vì sao đổi cho cả bản hiện hành lẫn bản cũ |
| 5.4 | ✅ | — | cột `confidence` tồn tại, test xác nhận not null trong \[0,1] |
| 5.5 | ✅ | — | endpoint `model-versions` dùng `requireLearnerAccess`; UI `PlanPage` có lịch sử bản cũ |

### AS-06 — Nội dung & Tri thức · 8/25 (32%)

| Chỉ báo | | Mã | Bằng chứng |
| --- | --- | --- | --- |
| **06.1 Knowledge graph toàn vẹn** | | | |
| 1.1 | ✅ | — | 0/274 node thiếu subject_id/grade/level |
| 1.2 | ✅ | — | 0/274 node thiếu strand/unit |
| 1.3 | ✅ | — | DFS 3-màu độc lập trên 233 cạnh prereq: 0 chu trình |
| 1.4 | ❌ | HH | lệnh gốc lỗi (cột `unit` không tồn tại trên `items`); sửa bằng JOIN `skill_nodes`: 69/222 unit <6 item, 21/222 unit =0 item |
| 1.5 | ✅ | — | 0/274 node thiếu title_en |
| **06.2 Chất lượng item** | | | |
| 2.1 | ❌ | HH | 2146/2146 item published có `quality_json` NULL — 100% không có hồ sơ 8 chiều |
| 2.2 | ❌ | HH | cùng dữ liệu 2.1 — 0 evidence per-dimension tồn tại |
| 2.3 | ❌ | HH | `item_versions`=0 dòng dù 679 item đã bị sửa qua script bypass hoàn toàn pipeline chấm |
| 2.4 | ✅ | — | 0 item thiếu node_id/role |
| 2.5 | ❌ | CX | UI Coral "Chất Lượng" chỉ placeholder "Chưa có dữ liệu"; learn không có nút báo câu sai |
| **06.3 Vòng đời nội dung** | | | |
| 3.1 🔴 | ❌ | HH | `scripts/apply-review-fixes.mjs` UPDATE thẳng `items` production, bypass hoàn toàn route PATCH chặn 409 |
| 3.2 | ✅ | — | `item_versions.status` enum CHECK rõ ràng ở tầng schema |
| 3.3 | ❌ | HH | 679 item sửa nội dung mà chưa từng qua `publishVersion`/`evaluatePublishGate` |
| 3.4 | ❌ | HH | 0/2146 item có `published_by`/`published_at` |
| 3.5 | ✅ | — | route rollback qua API tồn tại (dù `item_versions` rỗng nên chưa thực thi) |
| **06.4 Đề thi thật & provenance** | | | |
| 4.1 | ❌ | CX | 0 đề `source=real`, toàn bộ 10 exams là `generated` |
| 4.2 🔴 | ❌ | CX | 0 đề real để kiểm; R2 bucket content rỗng |
| 4.3 🔴 | ❌ | CX | 0 đề real, không spot-check được ≥5 |
| 4.4 | ❌ | CX | SDD-014 status draft, 0 code crawler tồn tại |
| 4.5 | ❌ | CX | cột `immutable_after_publish` không route nào đọc/ép, không có `exam_versions` |
| **06.5 Media & bản quyền** | | | |
| 5.1 | ❌ | CX | SDD-009 active nhưng 0 bảng media, 0 route `/v1/media` tồn tại |
| 5.2 | ❌ | CX | cùng nguyên nhân — không có registry nguồn/giấy phép media |
| 5.3 | ✅ | — | 0 file bitmap toàn repo, toàn bộ SVG tự vẽ/sinh tham số, 0 hotlink ngoài |
| 5.4 | ❌ | CX | 0 media thật upload qua hệ để đo kích thước |
| 5.5 | ❌ | CX | Media System chưa tồn tại nên cơ chế đồng thuận ảnh trẻ em cũng chưa có |

### AS-07 — Bảo mật, Riêng tư & Tuân thủ · 16/25 (64%)

| Chỉ báo | | Mã | Bằng chứng |
| --- | --- | --- | --- |
| **07.1 Xác thực & phiên** | | | |
| 1.1 🔴🚸 | ❌ | HH | `GET /v1/coral/labs` thiếu `requireSession`; tái hiện HTTP 500 thật trên dev server local độc lập, route chị em `overview` → 401 sạch |
| 1.2 | ✅ | — | `resolveSession`: hạn dùng, rotate sau 7 ngày, `revokeSession` khi logout |
| 1.3 | ✅ | — | `verifyGoogleIdToken` kiểm issuer/audience/alg RS256; 7/7 test PASS |
| 1.4 🚸 | ✅ | — | `curl` ẩn danh admin/dolphin/coral đều 302 Cloudflare Access login |
| 1.5 | ✅ | — | 0 hit bypass/skip auth trong source |
| **07.2 Phân quyền backend** | | | |
| 2.1 🔴🚸 | ✅ | — | mọi module chạm `learner_id` gọi `canAccessLearner`/tương đương |
| 2.2 🔴 | ✅ | — | 0 so sánh `role===email` trong authz logic |
| 2.3 🔴🚸 | ✅ | — | 3 file `authz.test.ts`, 35/35 PASS, có test phủ định family khác |
| 2.4 | ❌ | CM | `audit_log actor_role='mentor'` COUNT=0 (chưa có mentor thật dùng hệ) |
| 2.5 | ❌ | HH | `permissions.md` §6.2 nói Admin luôn 📝 nhưng `admin/observability.ts` 0 hit `logAccess`/`canAccessLearner` |
| **07.3 Dữ liệu trẻ em & đồng thuận** | | | |
| 3.1 | ❌ | CX | `data-dictionary.md` không có mục đích thu thập cho bất kỳ trường nào |
| 3.2 🔴🚸 | ❌ | HH | 0 lời gọi `enforceConsent` thật; `requireConsent` chỉ chặn khi đã rút, không chặn khi chưa từng hỏi; 0 màn hình consent frontend |
| 3.3 | ❌ | CM | code đúng (role check trước ghi) nhưng 0 file test `portraits/*.test.ts` |
| 3.4 | ✅ | — | chỉ 1 lời gọi ra ngoài thật: Google JWKS certs, không gửi dữ liệu learner |
| 3.5 | ❌ | CX | backend `data-usage` đầy đủ nhưng 0 màn hình marlins gọi/hiển thị |
| **07.4 Audit log** | | | |
| 4.1 | ❌ | HH | 3 route `admin/observability.ts` đọc dữ liệu learner cụ thể không `logAccess()` lần nào |
| 4.2 | ✅ | — | schema `audit_log` đủ actor/role/action/target/occurred_at/ip/user_agent/request_id |
| 4.3 | ❌ | HH | 1 dòng `audit_log` thật chứa nguyên văn `note` chưa redact (thao tác tay wrangler) |
| 4.4 | ✅ | — | 0 UPDATE/DELETE `audit_log` thật trong code |
| 4.5 | ✅ | — | `GET /v1/admin/audit-log` filter đầy đủ, tự ghi log truy vấn |
| **07.5 Bề mặt tấn công** | | | |
| 5.1 🔴 | ✅ | — | `git log -p --all` đúng regex gốc: 0 secret |
| 5.2 🔴 | ✅ | — | 0 path debug/seed trong `index.ts` và toàn bộ module |
| 5.3 | ✅ | — | mọi route qua `createRoute`+zod schema; 0 `c.req.json()` thô |
| 5.4 | ✅ | — | 1 hit `dangerouslySetInnerHTML`, nguồn không phải input người dùng trực tiếp |
| 5.5 🚸 | ✅ | — | `RATE_LIMITS` qua D1, `enforceRateLimit` trước verify JWT và trước route AI, 8/8 test PASS |

### AS-08 — Tin cậy & Vận hành · 19/25 (76%)

| Chỉ báo | | Mã | Bằng chứng |
| --- | --- | --- | --- |
| **08.1 Production-ready** | | | |
| 1.1 | ❌ | CX | 0 hit tier/availability gắn worker cụ thể trong `wrangler.jsonc` hay docs |
| 1.2 | ❌ | CX | SDD-006 §16 chỉ NÊU câu hỏi, không có bản trả lời cho subsystem nào |
| 1.3 | ❌ | CX | 9 SDD mới nhất hầu hết 0 hit fallback/degrad; Orca (feature mới, chạy thật) 0 dòng |
| 1.4 | ❌ | HH | health route luôn HTTP 200 dù `database=error`; `rollback.yml` smoke-test chỉ đọc mã HTTP |
| 1.5 | ✅ | — | 0 TODO/FIXME chặn đường trong source |
| **08.2 Idempotency & DLQ** | | | |
| 2.1 | ✅ | — | `dedupeKey`/`claimMessage`/`markProcessed` qua KV; 5 test PASS |
| 2.2 🔴🚸 | ✅ | — | `wrangler queues list` thật xác nhận cả 2 queue tồn tại trên Cloudflare |
| 2.3 | ✅ | — | GET/POST dead-letters list+replay, chặn replay 2 lần |
| 2.4 | ✅ | — | `max_retries=5` khớp `wrangler.jsonc`, backoff luỹ thừa có trần 300s |
| 2.5 | ✅ | — | `withLock` qua KV cho cron retention-refresh, test PASS |
| **08.3 Observability** | | | |
| 3.1 | ✅ | — | 9/9 `wrangler.jsonc` có `observability` |
| 3.2 | ✅ | — | middleware `requestId` mount đầu tiên, `X-Request-Id` mọi response |
| 3.3 | ❌ | HH | `app.onError` không log request_id/path; 2 đường 5xx hoàn toàn không log |
| 3.4 | ✅ | — | `GET /v1/admin/run-metrics` query engine_runs/workflow_runs/dead_letters |
| 3.5 | ✅ | — | `workflow_runs` COUNT=1 (>0) trên production thật |
| **08.4 CI/CD & rollback** | | | |
| 4.1 🚸 | ✅ | — | `ci.yml`+`deploy-api.yml` không escape hatch; `gh run list` xác nhận có run failure thật gần đây bị fail-fast đúng cơ chế |
| 4.2 | ✅ | — | 8/9 `deploy-*.yml` có `paths` filter khớp đúng thư mục |
| 4.3 🔴🚸 | ✅ | — | `deploy-api.yml`: apply migration trước dòng deploy worker |
| 4.4 | ✅ | — | `rollback.yml` đủ 9 worker, `wrangler rollback` thật, smoke-test (chưa từng chạy thật lần nào nhưng cách-kiểm chỉ đòi đọc cấu trúc) |
| 4.5 | ✅ | — | 0 workflow có gate thủ công ngoài migration-apply (đã ghi lý do) |
| **08.5 Suy giảm duyên dáng & phụ thuộc ngoài** | | | |
| 5.1 | ✅ | — | AI/OAuth/R2 đều có try/catch fallback xác nhận được |
| 5.2 | ❌ | CM | không thể mock lỗi AI (bị cấm sửa code trong phiên chấm); 0 test tự động mô phỏng |
| 5.3 | ✅ | — | `AbortSignal.timeout` duy nhất 1 điểm; AI dùng `withTimeout` riêng 30s |
| 5.4 | ✅ | — | `withCache` qua KV TTL; dùng thật ở `/v1/subjects`; 4 test PASS |
| 5.5 | ✅ | — | `curl` production 401/404 sạch, không leak stack |

### AS-09 — Trải nghiệm & Design System · 7/25 (28%)

| Chỉ báo | | Mã | Bằng chứng |
| --- | --- | --- | --- |
| **09.1 Luật tối giản (SRC-048)** | | | |
| 1.1–1.5 | ❌×5 | CX | `docs/reference/ui-minimalism-audit.md` chưa tồn tại — artifact bắt buộc của v0.2 |
| **09.2 Token & component** | | | |
| 2.1 | ❌ | HH | `engine.tsx` có hex trần trùng hệt giá trị đã có trong `palette.ts` nhưng không import — vi phạm luật tự đặt ra |
| 2.2 | ❌ | HH | `AboutMarlins`/`FindingNemo`/`Tour` đều fork 3 bản y hệt (packages+marlins+web/learn), không phải re-export |
| 2.3 | ✅ | — | chỉ 1 file `.css` toàn cục/app, 0 style-in-JSX theo trang |
| 2.4 | ✅ | — | mỗi `.theme-X` chỉ override 4 biến accent |
| 2.5 | ❌ | HH | 7 hit `text-xs` (12px) dưới sàn `--text-floor:14px` |
| **09.3 Accessibility** | | | |
| 3.1 🔴🚸 | ❌ | CM | axe-core thật trên 3 màn: `color-contrast` luôn `incomplete` do gradient phức tạp |
| 3.2 | ✅ | — | thử Tab THẬT (không `.focus()` JS) trên learn+marlins: `focus-visible` outline 3px hoạt động đúng — bằng chứng ban đầu (outline:none) do dùng `.focus()` lập trình |
| 3.3 | ❌ | HH | resize mobile 375px, đo thật: nhiều link nav/hero dưới 44px chiều cao |
| 3.4 | ✅ | — | mọi `<img>` thật đều có `alt=""` |
| 3.5 | ❌ | CM | luồng "trả lời câu hỏi" sau Google OAuth thật, không thể đăng nhập để thử Tab đầu-cuối |
| **09.4 Ngôn ngữ & nội dung UI** | | | |
| 4.1 | ✅ | — | quét đủ 8/8 file `apps/admin/src`, 0 hit tiếng Việt ngoài comment |
| 4.2 | ❌ | HH | `api.ts request()` không bắt lỗi network-level; `TypeError: Failed to fetch` render thẳng ra UI |
| 4.3 | ❌ | HH | `Learning.tsx` vs `ProgressViews.tsx` dùng 2 bộ nhãn khác nhau cho cùng 4 mã trạng thái |
| 4.4 | ❌ | HH | cùng bằng chứng 4.2 |
| 4.5 | ❌ | CM | không rà được màn sau đăng nhập (rào OAuth thật) |
| **09.5 Hiệu năng cảm nhận** | | | |
| 5.1 | ❌ | CX | 0 hit ngưỡng LCP khai trong SDD nào |
| 5.2 | ❌ | CM | không có lighthouse/axe cài sẵn; PerformanceObserver thật trả rỗng |
| 5.3 | ✅ | — | `states.tsx` tự dẫn AS-09.5.3, convention loading/empty/error áp dụng 25/34 file |
| 5.4 | ✅ | — | 0 request .png/.jpg trên 3 màn, toàn bộ SVG |
| 5.5 | ❌ | HH | `submit()` chờ round-trip server mới set đúng/sai — đánh đổi cố ý nhưng vẫn phản ví dụ theo L1 |

### AS-10 — AI Governance & Chi phí · 19/25 (76%)

| Chỉ báo | | Mã | Bằng chứng |
| --- | --- | --- | --- |
| **10.1 Đường AI duy nhất** | | | |
| 1.1 | ✅ | — | 1 hit duy nhất `env.AI.run` qua gateway id `nemo12` |
| 1.2 | ✅ | — | 1 model tất định, không vòng lặp thử nhiều model |
| 1.3 | ✅ | — | 1 hit `@cf/` model id đầy đủ tường minh |
| 1.4 | ✅ | — | 0 hit `env.AI`/`ai.run` trong `apps/*/src` |
| 1.5 | ❌ | HH | `ai-registry.md` (last_reviewed 2026-08-14) còn ghi "prompt chưa có registry" — mâu thuẫn `prompts.ts` đã tồn tại từ 2026-08-15 |
| **10.2 Prompt registry** | | | |
| 2.1 | ✅ | — | 0 prompt inline trong modules; cả 3 prompt nằm trong `shared/prompts.ts` registry |
| 2.2 | ✅ | — | mỗi PromptSpec có trường `version:number` |
| 2.3 | ✅ | — | 1 commit refactor giữ prompt cũ làm version 1, thêm version 2 mới |
| 2.4 | ✅ | — | mỗi PromptSpec có purpose/expects/constraints điền đầy đủ |
| 2.5 | ✅ | — | mỗi PromptSpec.output là `z.ZodType` thật, `safeParse` trước khi trả |
| **10.3 Context Builder & ranh giới dữ liệu** | | | |
| 3.1 🔴🚸 | ❌ | CX | `buildLearnerContext()` chỉ được gọi trong test — AI hiện không chạm learner data qua bất kỳ đường nào, bất biến "đường duy nhất" đúng một cách rỗng |
| 3.2 | ✅ | — | `PURPOSE_FIELDS` khai rõ sections+mastery_limit theo purpose; 12/12 test PASS |
| 3.3 | ✅ | — | `learnerRef()` băm SHA-256 bí danh; `assertNoIdentifiers` chặn cứng |
| 3.4 | ❌ | HH | `item_versions` không có cột model/prompt_version/input_ref; `learning_experiences` CÓ `provenance_json` — bất đối xứng |
| 3.5 🔴🚸 | ✅ | — | `context-builder.test.ts` (tên khác quy ước glob) 12/12 test PASS, cách ly A/B thật |
| **10.4 Đánh giá & chống ảo giác** | | | |
| 4.1 🔴🚸 | ✅ | — | cả 2 đường sinh nội dung AI đều có cổng người/ngưỡng trước publish |
| 4.2 | ✅ | — | 2 evaluator LLM + rule engine tất định; `PUBLISH_MIN_EVALUATORS=2` |
| 4.3 | ✅ | — | `HUMAN_SPOT_CHECK_RATE=0.2` deterministic, test phân bố lệch <0.03 |
| 4.4 | ✅ | — | `source='ai-generated'` gán tường minh, dùng ở nhiều tầng UI |
| 4.5 | ✅ | — | learner tự báo → `content_reports`→review_queue→staff resolveReview khép vòng |
| **10.5 Chi phí & hạn mức** | | | |
| 5.1 | ❌ | CX | `ai-registry.md` không có cột/mục ước tính chi phí theo loại lời gọi AI |
| 5.2 | ❌ | CM | Cloudflare AI Gateway API 403 thiếu scope; không có cơ chế thay thế ở tầng ứng dụng |
| 5.3 | ✅ | — | `RATE_LIMITS.aiGenerate=30/giờ` qua D1, `enforceRateLimit` trước 2 route AI tốn tiền |
| 5.4 | ❌ | CX | 0 cron/queue nào gọi AI; cả 3 lời gọi AI đều đồng bộ theo HTTP request staff |
| 5.5 | ✅ | — | 2 route sinh nội dung AI đều `requireStaff` + rate-limited |

## 6. Đối chiếu với Audit #001 (định tính — mẫu số khác, không so % trực tiếp)

Audit #001 (57dc4fa, v0.1, 250 chỉ báo) chấm 109/250 (44%). Audit #002 (89a3db46a1, v0.2, 255 chỉ báo) chấm 157/255 (61,6%). Theo đúng [Hướng dẫn chuyển đổi v0.1→v0.2](../audit-standard/history.md#hướng-dẫn-chuyển-đổi-v01-→-v02), không quy đổi cơ học — đối chiếu định tính:

**Đã sửa thật, có bằng chứng (từng 🔴 ở #001, nay PASS):**

* `AS-04.1.1` numbering migration (0022 trùng → đã đổi tên, đánh CI enforce)
* `AS-07.1.1`/`AS-07.2.1`/`AS-07.2.3` authz backend + test — phần lớn PASS (trừ 1 route `coral/labs` mới lọt lưới, xem dưới)
* `AS-01.4.1` gen:reference (91 dòng diff → 0 diff)
* `AS-03.2.4` contract:diff (0 chạy đâu → chạy 2 workflow)
* `AS-04.1.4` migrate:dryrun (không có → CI enforce 39/39)
* `AS-05.3.5`/`AS-08.4.1` CI chạy test/typecheck/lint trước deploy (không workflow nào chạy → enforce thật, có bằng chứng `gh run list`)
* `AS-10.3.5` test cách ly learner A/B (không tồn tại → `context-builder.test.ts` 12/12 PASS)
* `AS-10.4.1` AI không publish thẳng (ghi thẳng DB → có gate/threshold)

**Tái diễn — CÙNG LOẠI lỗi, khác chi tiết cụ thể (bài học quan trọng nhất):**

* **Migration mồ côi/trùng số**: #001 bắt được `0022` trùng số → sửa xong nhưng đợt sửa (đổi tên `0022_whale_country_detail.sql`→`0031_...`, `0031_labs.sql`→`0037_labs.sql`) lại tự sinh ra đúng loại lỗi mới — 2 dòng mồ côi khác trong `d1_migrations` remote, xác nhận thật bằng `wrangler d1 execute --remote`. `docs/ops/migration-reconciliation.md` (mới, do chính đợt v0.2 tạo ra sau sự cố) đã ghi lại minh bạch — AS-04.6.1 PASS đúng nghĩa "nợ đã khai báo", nhưng AS-04.6.2 (0 lệch tuyệt đối) vẫn KHÔNG PASS và AS-04.6.3 phát hiện thêm 1 lớp mới: schema DDL text bản thân cũng lệch (`whale_scholarships`). Kết luận: cơ chế đối soát mới (AS-04.6) đang hoạt động đúng như thiết kế — phát hiện lỗi thật ngay khi nó xảy ra, thay vì để im lặng.
* **Authz backend cho learner data**: #001 bắt 21/102 endpoint chưa rõ chứng minh auth → #002 sau khi vá gần hết, một route MỚI (`GET /v1/coral/labs`, tính năng Coral labs không tồn tại ở #001) lại thiếu `requireSession` — cùng loại lỗ hổng, route khác.

**Mới xuất hiện do tính năng mới (không có ở #001, vì tính năng chưa tồn tại lúc đó):**

* Toàn bộ AS-06.2/06.3 (content lifecycle bypass qua `apply-review-fixes.mjs`) — #001 ghi nhận `items` chưa có cột lifecycle nào (CX thuần); #002 cho thấy cột lifecycle **đã được xây** (`item_versions`, `quality_json`) nhưng bị **bỏ qua hoàn toàn** bởi một script vận hành — từ "chưa xây" (CX) chuyển thành "xây rồi nhưng có đường vòng" (HH), nặng hơn về bản chất dù cùng nhóm chỉ báo.
* Nhóm REQ `ORC` (Orca competitions), community events (`AS-04.2.4`, `AS-04.3.5`), Whale (`AS-04.6.1/6.3`), Octopus school (`AS-02.3.4`) — toàn bộ là chỉ báo mới KHÔNG PASS gắn với tính năng mới build từ SRC-122 đến SRC-128 (2026-08-15) mà tài liệu/schema chưa theo kịp code.
* `AS-10.3.1` Context Builder: #001 ghi CX vì component chưa tồn tại; #002 component đã được xây kỹ (12/12 test cách ly) nhưng **chưa được nối vào bất kỳ route AI sản xuất nào** — vẫn CX xét theo đúng ý nghĩa "đường duy nhất AI chạm learner data" vì chưa có gì đi qua đường đó.

## 7. Kết luận

**Pilot Gate (điều kiện thật cho đợt mở thử 2 ngày tới, theo ✍️ Q-128): 12/18 — NO-GO.** Không mở thử ở trạng thái hiện tại. 6 chỉ báo chặn, xếp theo mức độ nghiêm trọng cho 10 gia đình thật: `AS-07.3.2` (đồng thuận không chặn thu thập dữ liệu trẻ em chưa được hỏi — rủi ro pháp lý trực tiếp), `AS-07.1.1` (1 route learner data crash/không xác thực), `AS-04.5.5` (0 lần diễn tập backup/restore — không có đường lùi nếu dữ liệu trẻ em thật hỏng), `AS-04.6.3` (schema drift thật giữa production và migration trên đĩa), `AS-03.5.3` (chưa chặn được đúng nguyên nhân gốc sự cố đa-agent 2026-08-15), `AS-10.3.1` (không ảnh hưởng pilot nếu pilot không chạm tính năng AI cá nhân hoá — nhưng bất biến chưa có gì để kiểm chứng thật).

**Điểm hệ toàn phần: 157/255 (61,6%), mức "Đang dựng"** — cách sàn REQ-DOC-06 (≥225/255) còn 68 chỉ báo. Đây KHÔNG phải điều kiện chặn pilot 2 ngày tới (đã có Pilot Gate thay thế theo ✍️ Q-128), nhưng vẫn là ngưỡng dài hạn chưa đạt cho việc mở rộng người dùng thật ra ngoài phạm vi pilot nhỏ này.

**%HH = 57% trong tổng KHÔNG PASS** là tín hiệu quan trọng nhất của lần chấm này: hơn một nửa số điểm trừ không phải "chưa kịp xây" mà là có bằng chứng cụ thể cho sai — đặc biệt tập trung ở AS-04 (schema/vận hành), AS-06 (nội dung có đường vòng bypass vòng đời), và AS-09 (UI có 2 chuẩn khác nhau cho cùng khái niệm). Đây là hệ quả trực tiếp của nhiều phiên AI chỉnh sửa song song trong cùng một ngày.

## 8. Hành động sinh ra từ audit này

Xếp theo thứ tự bắt buộc: **Pilot Gate chưa PASS lên đầu tuyệt đối**, sau đó **HH nói chung** (đã hỏng, có phản ví dụ), rồi **CM** (chứng minh — thường rẻ), cuối cùng **CX** (xây — tốn nhất).

### 8.1 Pilot Gate — bắt buộc trước khi mở thử (6 việc, chặn go/no-go)

| # | Hành động | Đóng được chỉ báo | Ước lượng |
| --- | --- | --- | --- |
| P1 | Thêm `/v1/coral/labs` vào danh sách `.use(path, requireSession)` trong `coral/routes.ts` | AS-07.1.1 🔴🚸 | 15 phút |
| P2 | Đổi `requireConsent()` sang chặn khi **granted != 1** (không chỉ khi đã rút); dựng màn hình consent tối thiểu trong luồng nhận lời mời marlins trước khi kích hoạt learner | AS-07.3.2 🔴🚸 | 1 ngày |
| P3 | Chạy 1 lần thật: backup D1 → restore vào DB test → đếm bảng/dòng khớp → ghi `docs/ops/backup-restore-drill.md` | AS-04.5.5 🔴🚸 | nửa ngày |
| P4 | Chuẩn hoá lại migration `whale_scholarships` (viết lại 1 migration mới `CREATE TABLE` sạch thay vì chuỗi ALTER lịch sử) hoặc ghi nhận lệch DDL vào `migration-reconciliation.md` kèm xác nhận vô hại | AS-04.6.3 🔴🚸 | nửa ngày |
| P5 | Thêm câu cấm tường minh "không chạy `wrangler d1 migrations apply --remote` từ máy/phiên cục bộ" vào README §CI/CD hoặc CLAUDE.md; dọn 2 dòng mồ côi (DELETE trong migration-reconciliation.md) | AS-03.5.3 🔴🚸 | 1 giờ |
| P6 | Quyết định tường minh (✍️ hoặc chủ dự án xác nhận): nếu pilot không chạm tính năng AI cá nhân hoá learner, ghi rõ AS-10.3.1 là nợ chấp nhận được cho đợt này; nếu có, nối route AI cá nhân hoá đầu tiên qua `buildLearnerContext()` trước khi bật | AS-10.3.1 🔴🚸 | 1 giờ (quyết định) hoặc 1 ngày (nối code) |

### 8.2 🔴 chặn release còn lại, ngoài Pilot Gate (3 việc)

| # | Hành động | Đóng được chỉ báo | Ước lượng |
| --- | --- | --- | --- |
| R1 | Chặn hoặc rút quyền chạy `scripts/apply-review-fixes.mjs` thẳng vào production; mọi sửa item phải qua `createCandidate`→`publishVersion` | AS-06.3.1 🔴 | nửa ngày |
| R2 | Bắt đầu ngân hàng đề thật: thu thập ≥1 đề `source='real'` kèm URL nguồn + lời giải tự biên soạn | AS-06.4.2 🔴, 6.4.3 🔴 | 2-3 ngày (phụ thuộc nguồn đề) |
| R3 | Đo contrast WCAG AA bằng công cụ đo pixel thật (Lighthouse DevTools hoặc thước màu tay) cho vùng gradient hero, không chỉ axe-core | AS-09.3.1 🔴🚸 | nửa ngày |

### 8.3 HH khác — có phản ví dụ, cần sửa code/dữ liệu (53 chỉ báo, nhóm theo chủ đề)

| # | Hành động | Đóng được chỉ báo | Ước lượng |
| --- | --- | --- | --- |
| H1 | Chạy 1 pipeline chấm quality 8 chiều cho 2146 item published thật (kể cả 679 item vừa sửa qua script); ghi `evaluator_count`/`quality_score` | AS-06.2.1, 6.2.2, 6.2.3, 6.3.3, 6.3.4 | 2 ngày |
| H2 | Vá 67 cột `*_id` thiếu REFERENCES (hoặc miễn trừ tường minh trong dictionary nếu polymorphic); sửa `starts_at` UTC; vá 4 CHECK cắt cụt trong generator; xoá mục ma `interaction_votes_rebuilt`; điền 160 mô tả cột rỗng | AS-04.2.3, 4.2.4, 4.3.2, 4.3.3, 4.3.4, 4.3.5 | 2 ngày |
| H3 | Sửa 5+20 seed script: thêm `--local` mặc định hoặc dừng xác nhận trước `--remote`; điền 24 dòng thiếu cặp `_en`; sửa 4 policy `keep_days=NULL` thành số cụ thể | AS-04.4.1, 4.4.2, 4.4.5, 4.5.1 | 1 ngày |
| H4 | Đăng ký nhóm REQ `ORC` vào `conventions.md` §2; nối REQ-LRN-19/20/21 vào bảng §6; sửa "tốt hơn" ở REQ-BRD-07; điền mục Octopus vào SDD-011; dựng UI forum tối thiểu cho US-38 hoặc rút REQ-COM-03 khỏi ✔ | AS-02.1.1, 2.1.2, 2.2.5, 2.3.4, 1.3.4, 1.3.5 | 1 ngày |
| H5 | Thêm `logAccess()` vào 3 route `admin/observability.ts`; redact lại 1 dòng `audit_log` chứa note nguyên văn; sửa `permissions.md` §6.2 cho đúng thực tế | AS-07.2.5, 7.4.1, 7.4.3 | nửa ngày |
| H6 | Sửa `app.onError`/`errorResponse` ghi kèm `request_id`+path+method vào log; sửa health route trả HTTP 503 khi `database=error` | AS-08.1.4, 8.3.3 | nửa ngày |
| H7 | Thêm hằng `MASTERY_VERSION`; gắn trang mô tả `learning_plan` trong `models.md`; truyền `input` vào mọi `runEngine()`; lưu `model/prompt_version/input_ref` vào `item_versions` khi tạo qua AI | AS-05.1.1, 5.1.4, 5.1.5, 5.2.5, 10.3.4 | 1 ngày |
| H8 | Import `palette.ts` vào `engine.tsx` (xoá 3 hex trần); chuyển 3 component fork (`AboutMarlins`/`FindingNemo`/`Tour`) sang import từ `packages/design-system`; nâng 7 chỗ `text-xs`; thống nhất 1 bộ nhãn trạng thái `STATE_LABEL` dùng chung giữa `Learning.tsx`/`ProgressViews.tsx`; sửa nav/hero link ≥44px trên mobile | AS-09.2.1, 9.2.2, 9.2.5, 9.4.3, 9.3.3 | 1 ngày |
| H9 | Bọc `try/catch` cho lỗi network-level trong `api.ts request()`, dịch sang tiếng Việt thay vì render thẳng `e.message` | AS-09.4.2, 9.4.4 | nửa ngày |
| H10 | Cập nhật `ai-registry.md` (số dòng, thêm generate-experience, xoá tuyên bố "chưa có prompt registry") | AS-10.1.5 | 1 giờ |
| H11 | Cập nhật `migrations/README.md` §3 khớp thực tế production (sửa khuyến nghị INSERT OR IGNORE thành đúng theo `migration-reconciliation.md`) | AS-01.1.1, 1.1.5, 1.1.3 (phần liên quan) | 1 giờ |
| H12 | Bump `version` PRD-001 đúng quy ước khi sửa REQ đáng kể; thêm cột "Trạng thái" vào bảng "Việc còn chờ chủ dự án"; sửa 16 dòng ✍️ Đợt 1 dưới ngưỡng 15 ký tự | AS-01.5.2, 2.5.2, 2.5.4 | nửa ngày |
| H13 | UI "chấm client-side trước, gửi evidence async" cho câu trả lời đầu tiên (nếu chấp nhận đánh đổi lộ đáp án tạm thời) — hoặc ghi nợ ✍️ chấp nhận độ trễ hiện tại | AS-09.5.5 | 1 ngày (nếu làm) |

### 8.4 CM — chứng minh, thường rẻ (13 chỉ báo)

| # | Hành động | Đóng được chỉ báo | Ước lượng |
| --- | --- | --- | --- |
| C1 | Xin/renew Cloudflare API token có scope KV+R2+AI Gateway đọc, chạy lại `verify:bindings` Lớp C và kiểm chi phí AI Gateway | AS-03.4.4, 10.5.2 | 1 giờ (phụ thuộc quyền cấp token) |
| C2 | Viết test cho `updateMastery`/`computeReadiness`, ca `evidence: []`/`null` đầu tiên, dedupe `event_id` trong `modules/learning` | AS-05.2.4, 5.3.1, 5.3.3 | 1 ngày |
| C3 | Viết `portraits/*.test.ts`; ghi audit_log mẫu cho mentor thật đầu tiên khi có mentor dùng hệ | AS-07.3.3, 7.2.4 | nửa ngày |
| C4 | Đo contrast bằng công cụ pixel-level thay axe (đã gộp vào P6/R3 ở trên) | AS-09.3.1 | — |
| C5 | Tạo tài khoản test đăng nhập Google thật (không phải mock) để rà Tab đầu-cuối và ngôn ngữ ở màn sau đăng nhập | AS-09.3.5, 9.4.5 | nửa ngày |
| C6 | Cài lighthouse/axe CLI vào devDependencies thật (không phải npx tạm), đo CLS thật | AS-09.5.2 | nửa ngày |
| C7 | Viết test mô phỏng lỗi AI (mock `env.AI.run` throw) và xác nhận UI/response vẫn sống | AS-08.5.2 | nửa ngày |

### 8.5 CX — xây mới, tốn nhất (29 chỉ báo, nhóm lớn)

| # | Hành động | Đóng được chỉ báo | Ước lượng |
| --- | --- | --- | --- |
| X1 | Dựng `docs/reference/ui-minimalism-audit.md` (bảng route×app×5 cột, kèm ảnh/`file:line`) | AS-09.1.1..5 | 2 ngày |
| X2 | Ngân hàng đề thật + crawler tôn trọng robots/ToS (đã gộp một phần vào R2) | AS-06.4.1, 6.4.4, 6.4.5 | 3-5 ngày |
| X3 | Media System tối thiểu (bảng `media`, route upload, moderation) nếu tính năng ảnh thật sự cần trước pilot mở rộng | AS-06.5.1, 6.5.2, 6.5.4, 6.5.5 | 3 ngày |
| X4 | Viết 5 câu trả lời production-ready SDD-006 §16 cho từng subsystem; khai tier availability theo worker; định nghĩa "hỏng thì thấy gì" cho Orca/Whale/community events | AS-08.1.1, 8.1.2, 8.1.3 | 1 ngày |
| X5 | Cột "Phân loại" 4 giá trị cho `legacy-feature-inventory.md`; cột "Đã vào" cho mọi Q trong `open-questions/index.md`; khóa nghiệp vụ trong `data-dictionary.md` | AS-02.4.4, 2.5.5, 4.4.4 | 1 ngày |
| X6 | Khai ngưỡng LCP trong SDD; ước tính chi phí AI theo loại lời gọi trong `ai-registry.md`; xác nhận 0 cron/queue gọi AI là chủ đích, ghi rõ nếu sau này thêm | AS-09.5.1, 10.5.1, 10.5.4 | nửa ngày |
| X7 | Nối tính năng AI cá nhân hoá đầu tiên qua Context Builder (nếu roadmap có) — đã liệt ở P6 | AS-10.3.1 | — |
| X8 | Dựng UI Coral "Chất Lượng" thật (thay placeholder) nối `content_review_queue`; thêm nút "báo câu sai" trong learn | AS-06.2.5 | 1-2 ngày |
| X9 | Mục đích thu thập cho từng trường trẻ em trong `data-dictionary.md`; UI marlins hiển thị `data-usage` | AS-07.3.1, 7.3.5 | 1 ngày |
| X10 | Stopping criteria có test cho diagnostic (thay vì cố định 15 câu) | AS-05.3.4 | 1-2 ngày |

## 9. Ghi chú phương pháp

* Bằng chứng thu bằng lệnh thật trên commit `89a3db46a1` (snapshot ghim, độc lập với thư mục làm việc chính đang bị nhiều phiên AI khác sửa song song): `check-docs.mjs`, `gen-reference.mjs`, `contract:diff`, `migrate:dryrun`, `verify:bindings`, `vitest run`, `wrangler d1 execute --remote` trên D1 production thật, `wrangler queues list`/`gh run list`/`gh api` (trạng thái sống, gọi ra ngoài snapshot theo đúng ngoại lệ được phép), và grep/rà source + dựng lại dev server local độc lập để tái hiện 1 lỗi động (AS-07.1.1).
* **Ba lượt hợp nhất**: chấm gốc (255/255 chỉ báo, không có ID nào bỏ sót so với `audit-standard/index.md`) → chấm sâu 18 chỉ báo Pilot Gate → một lượt phản biện chạm 77/255 chỉ báo (chủ yếu xác nhận lại PASS bằng lệnh độc lập, và tìm phản ví dụ mới hoặc lật lại verdict Pilot Gate khi bằng chứng động mâu thuẫn với bằng chứng tĩnh). Verdict cuối cùng ở mỗi ID dùng lớp muộn nhất đã chạm tới nó.
* **5 chỉ báo bị lượt phản biện lật verdict so với lớp ngay trước nó**: `AS-03.5.3`, `AS-04.6.3`, `AS-07.1.1` (Pilot Gate PASS dựa trên đọc tĩnh → phản biện lật lại KHÔNG PASS bằng bằng chứng động, tức quay về đúng kết luận chấm gốc ban đầu), `AS-04.4.3` (chấm gốc PASS → phản biện lật KHÔNG PASS/CM vì phụ thuộc AS-04.2.3 đang fail), `AS-10.3.1` (cả chấm gốc lẫn Pilot Gate đều PASS → phản biện lật KHÔNG PASS/CX, chỉ báo duy nhất lật thật theo hướng "tưởng đạt hoá ra chưa" so với kết luận ban đầu của TOÀN BỘ hai lượt trước). Ngoài ra `AS-09.3.2` được phản biện lật từ KHÔNG PASS (chấm gốc) sang PASS — phát hiện bằng chứng gốc dùng `.focus()` JS thay vì phím Tab thật nên sai.
* Theo **L2** (cấm N/A) và **L3** (không bằng chứng = KHÔNG PASS): mọi KHÔNG PASS đều kèm mã CX/CM/HH theo đúng định nghĩa §1.1 audit-standard/index.md — không dùng chung một ký hiệu ❌ vô phân biệt như Audit #001.
* AS-09.3.5 và một phần AS-09.4.5 vẫn bị chặn bởi rào Google OAuth thật (không có tài khoản test) — chưa rà được các màn sau đăng nhập; ghi CM thay vì suy đoán PASS.
