Audit #005 — Hệ tài liệu
Audit chuyên đề, không phải audit toàn hệ. Audit #004 đã chấm cả 255 chỉ báo và cho AS-01 (Tài liệu & Đóng vết) 18/25; bản này đào sâu đúng bộ đó bằng những phép đo mà chuẩn không có sẵn công cụ, để biến "18/25" thành danh sách việc làm được.
Không đặt thang điểm mới. Audit Standard đã cấm trục chấm thứ hai chồng lên PASS/KHÔNG PASS (§1.2). Mỗi phát hiện dưới đây vì vậy được nối thẳng về chỉ báo AS tương ứng, hoặc ghi rõ là ngoài phạm vi chỉ báo hiện có — chính chỗ đó là đề xuất mở rộng chuẩn.
1. Hệ tài liệu đang là gì
Số file .md trong docs/ | 93 |
| Tổng dòng | 19.849 |
| Theo loại | reference 40 · sdd 32 · index 11 · quality 6 · prd 3 · convention 1 |
| Theo trạng thái | active 78 · draft 15 |
| Dài nhất | reference/data-dictionary.md (2.167 dòng, sinh tự động) |
Cổng check-docs.mjs (QG-001) xanh: 93/93 file đủ frontmatter, id duy nhất, không link nội bộ gãy, trace satisfies ↔ REQ đóng.
2. Phát hiện
Xếp theo mức hại, không theo thứ tự đo.
D1 · Sổ tiếp nhận chạy nhanh gấp đôi việc canonical hoá (AS-01.1.3)
232/415 SRC (56%) chưa xuất hiện ở bất kỳ PRD, SDD, QG hay reference nào ngoài chính intake.md.
phân bố theo khoảng SRC: 0-49: 2 · 50-99: 25 · 100-149: 14 · 150-199: 35
200-249: 24 · 250-299: 45 · 300-349: 43 · 350-399: 34 · 400+: 10Đây không phải nợ của riêng giai đoạn đầu: khoảng 250-399 (những chỉ đạo gần đây nhất) đóng góp 122/232. Nghĩa là tốc độ tiếp nhận đang vượt tốc độ đưa vào tài liệu chuẩn, và khoảng cách ngày càng rộng chứ không hẹp lại.
Hệ quả cụ thể, không phải lý thuyết: một quyết định chỉ nằm ở intake.md thì không ai tìm ra nó khi đọc tài liệu thiết kế. Ví dụ đang chạy: cột Parents của admin (SRC-404) và cổng phủ xác thực (SRC-412) đều có mã trên production mà không REQ nào mô tả.
D2 · Tài liệu yêu cầu gốc vẫn là draft sau 438 dòng REQ (ngoài phạm vi chỉ báo)
prd-001-nemo12-platform.md mang status: draft, trong khi nó chứa 438 dòng REQ, trong đó 258 đánh ✔ (đã làm). Một tài liệu mà hơn một nửa nội dung đã thi hành xong và đang chạy cho người dùng thật thì không còn là bản nháp.
Không chỉ báo AS nào bắt được điều này: AS-01.2.5 chỉ đòi status thuộc tập cho phép, không hỏi trạng thái ấy có còn đúng không. Đề xuất mở rộng chuẩn.
D3 · last_reviewed là trường trang trí (ngoài phạm vi chỉ báo)
25/93 doc có last_reviewed CŨ HƠN ngày sửa file thật. Vài ca nặng:
| Doc | Khai last_reviewed | Sửa thật lần cuối |
|---|---|---|
docs/index.md | 2026-08-15 | 2026-08-20 |
docs/intake.md | 2026-08-15 | 2026-08-20 |
traceability.md | 2026-08-14 | 2026-08-19 |
prd-001-nemo12-platform.md | 2026-08-15 | 2026-08-19 |
reference/engines.md | 2026-08-14 | 2026-08-19 |
AS-01.5.1 vẫn PASS vì ngưỡng của nó là 90 ngày, quá rộng để bắt được chuyện này. Nhưng ý nghĩa của trường đã mất: người đọc thấy last_reviewed: 2026-08-14 sẽ tưởng nội dung đã đứng yên từ hôm đó, trong khi nó vừa đổi hôm qua. Đề xuất: cổng CI chặn commit sửa nội dung doc mà không cập nhật last_reviewed — đo được, rẻ, và đóng luôn AS-01.5.2 (hiện là mã CM vì không ai spot-check nổi bằng tay).
D4 · Một doc mồ côi, và nó đúng là doc của chỉ báo 🔴 (AS-01.3.4)
docs/ops/backup-restore-drill.md không được file nào trong docs/ trỏ tới. Nó là tài liệu duy nhất phục vụ AS-04.5.5 🔴🚸 (diễn tập backup/restore), tức chỉ báo đang chặn Pilot Gate. Doc tồn tại nhưng không có đường đi tới thì khi cần dùng thật sẽ không ai tìm ra.
D5 · Hai SDD active mô tả bảng chưa từng được xây (ngoài phạm vi chỉ báo)
Đối chiếu mọi cụm "bảng `x`" trong doc status: active với 101 bảng thật trong migrations/:
| Doc | Bảng được mô tả | Trạng thái thật |
|---|---|---|
sdd-010-lab-platform.md:61 | inquiries | không tồn tại |
sdd-009-media.md:38 | media_links | không tồn tại |
Hai câu ấy viết ở thì khẳng định chứ không phải thì dự định, nên người đọc SDD sẽ đi tìm bảng không có. (Ba ca khác mà phép đo bắt được là âm tính giả, đã soi tay: alignment_scores được nêu trong một câu nói rõ "không có bảng này và không nên có"; onboarding_learner_profiles là bảng của hệ chuyenchon cũ, không phải của Nemo12; d1_migrations là bảng do wrangler tự tạo.)
D6 · Ba đường dẫn mã nguồn trong docs đã chết (ngoài phạm vi chỉ báo)
| Doc | Đường dẫn | |
|---|---|---|
sdd-001-platform.md:137 | packages/shared/config | không tồn tại |
open-questions/index.md:118 | packages/ai | không tồn tại |
intake.md:105 | scripts/whale-data-p1..p3.json | không tồn tại |
check-docs.mjs kiểm link .md nội bộ nhưng không kiểm đường dẫn mã nguồn nhắc trong văn bản. Đây là loại drift âm thầm: doc vẫn "xanh" trong khi chỉ sai.
D7 · Con số của chính bộ chuẩn còn hai chỗ nói bản cũ (AS-01.4.x tinh thần)
docs/index.md:34 và docs/quality/quality-gates.md:41 vẫn ghi Audit Standard có 250 chỉ báo, trong khi chuẩn hiện hành là v0.2 với 255. Hai trang này là cửa vào của cả hệ tài liệu, nên sai ở đây lan xa nhất.
D8 · 15 doc draft, 12 trong số đó là SDD
prd-001, open-questions, chuyenchon-app-requirements, legacy-feature-inventory, và SDD-012 → SDD-021. Phần lớn SDD trong nhóm này mô tả hệ đã chạy thật (SDD-017 Retention, SDD-019 Mentor Albums, SDD-020 School Registry, SDD-021 Learning Entry Sequencing đều có mã trên production). Cùng loại vấn đề với D2: nhãn trạng thái không theo kịp thực tế.
3. Việc sinh ra từ audit này
| # | Việc | Đóng | Cỡ |
|---|---|---|---|
| T1 | Sửa hai con số 250 → 255 ở index.md và quality-gates.md | D7 | rất nhỏ |
| T2 | Nối ops/backup-restore-drill.md vào docs/index.md | D4, AS-01.3.4 | rất nhỏ |
| T3 | Sửa ba đường dẫn chết; đổi hai câu SDD sang thì dự định hoặc xây bảng | D5, D6 | nhỏ |
| T4 | Cổng CI: sửa nội dung doc thì bắt buộc cập nhật last_reviewed | D3, AS-01.5.2 | vừa |
| T5 | Cổng CI: đường dẫn mã nguồn nhắc trong docs phải tồn tại | D6 | nhỏ |
| T6 | Chuyển status của các SDD đang chạy thật từ draft sang active; quyết định riêng cho PRD-001 | D2, D8 | vừa |
| T7 | Canonical hoá 232 SRC còn treo, bắt đầu từ SRC có mã đang chạy production | D1, AS-01.1.3, AS-02.4.2 | LỚN |
T7 là việc lớn và không làm một lần được. Đề xuất cách chia: mỗi phiên sửa mã cho một SRC thì canonical hoá luôn SRC đó, và mỗi tuần dọn một khối 20 SRC cũ theo thứ tự mới nhất trước (vì SRC mới còn nhớ được bối cảnh, SRC cũ thì phải khảo cổ).
4. Trace
| Nguồn | Chuẩn | Ghi vào |
|---|---|---|
| SRC-416 (chỉ đạo: "audit toàn bộ hệ thống tài liệu, hệ thống admin này, tạo ra các audit report, sau đó fix dần") | audit-standard/index.md v0.2 — bản này bổ trợ AS-01, không thay thang điểm | báo cáo này + index + việc T1..T7 |
5. Đã sửa — 2026-08-20, ngay sau khi chấm
| Việc | Kết quả kiểm được |
|---|---|
| T1 | Hai chỗ nói Audit Standard có 250 chỉ báo (index.md, quality-gates.md) sửa thành 255. Kèm dọn cây thư mục ở index.md, vốn còn ghi SRC-001..111, Q-001..127, SDD-001..017 trong khi thực tế là SRC-001..416 · Q-001..137 · SDD-001..021, và thiếu hẳn thư mục ops/. Con số lấy bằng cách đếm từ chính các file, không gõ tay. → D7 đóng |
| T2 | ops/backup-restore-drill.md và ops/migration-reconciliation.md nay có một dòng riêng trong bản đồ theo domain của index.md. Đếm lại: 0 doc mồ côi. → D4 đóng |
| T3 | Bốn câu khẳng định sai sửa thành khẳng định trung thực, mỗi câu nói rõ thứ đó chưa dựng và hiện đang nằm ở đâu: packages/shared/config (SDD-001), packages/ai ×2 (open-questions), bảng media_links (SDD-009), bảng inquiries (SDD-010). → D5, D6 đóng |
| T5 | scripts/check-docs-drift.mjs, chạy trong npm run check:docs nên nằm trong CI. Bắt ba loại trôi mà QG-001 không thấy: đường dẫn mã nguồn đã chết · doc mô tả bảng D1 chưa tồn tại · doc mồ côi |
Cổng chống trôi có thật sự bắt được không
Thêm một dòng drift giả vào reference/engines.md rồi chạy:
✗ SRC-416 — 2 chỗ tài liệu đã trôi khỏi mã đang chạy:
docs/reference/engines.md:296 đường dẫn không tồn tại: workers/api/src/khong-he-ton-tai.ts
docs/reference/engines.md:296 mô tả bảng chưa tồn tại: bang_ma_khong_coGỡ dòng đó ra thì xanh lại. Cổng có răng, không phải xanh giả.
Chính cổng này lỗi ngay lần đầu lên CI, và đó là bài học đáng giữ. Nó xanh ở máy nhưng đỏ trên CI, vì nó đo bằng existsSync trên thư mục làm việc: apps/data là app đã ngừng, git không còn tệp nào, nhưng ở máy vẫn sót một thư mục rỗng chứa .env cũ. Một cổng cư xử khác nhau ở hai nơi thì tệ hơn là không có cổng, vì nó dạy người ta bỏ qua kết quả CI. Đã sửa: cổng nay đo theo danh sách tệp git theo dõi, thứ giống hệt nhau ở mọi máy. Kèm thêm nhóm từ khoá thoát hiểm cho các câu nói về thứ đã ngừng dùng (hai câu về apps/data là tài liệu đúng, không phải drift).
Hai đường thoát hiểm cố ý, vì không có chúng thì cổng sẽ ép tài liệu nói dối:
- Câu văn tự nói ra rằng thứ đó chưa dựng, hoặc cố ý không dựng, thì không tính là trôi. Ví dụ SDD-020 viết "dựng bảng
schoolsmới là tạo ra hai nguồn sự thật" — đó là một quyết định kiến trúc, không phải một lời sai. - Sổ ghi chép theo thời gian được miễn trừ:
intake.mdvà toàn bộquality/audits/. Chúng ghi điều đã đúng tại một thời điểm, và luật của chính hệ audit là không sửa báo cáo cũ. Bắt một cuốn sổ cập nhật theo mã hiện tại là phá đúng công dụng của nó.
| T4 | scripts/check-docs-fresh.mjs thi hành đúng một luật đã viết sẵn ở conventions.md §4 ("Khi sửa nội dung đáng kể: tăng version, cập nhật last_reviewed") — luật có từ đầu, chỉ chưa ai thi hành. Phép đo: last_reviewed không được cũ hơn ngày commit gần nhất chạm vào chính file đó. Đo bằng git chứ không bằng mtime, vì mtime đổi cả khi chỉ git checkout. → D3 đóng, và AS-01.5.2 từ mã CM thành đo được bằng máy |
Nói thẳng về cách sửa 27 file lệch ngày: đây là một lần chỉnh đồng loạt cho khớp, KHÔNG phải một lần đọc lại nội dung 27 file. Gọi nó là "đã review" thì đúng chữ mà sai nghĩa. Giá trị nằm ở chỗ khác: từ nay cổng buộc mỗi lần sửa phải tự bump trong cùng commit, nên khoản nợ này không tích tụ lại được nữa. Báo cáo audit được miễn trừ khỏi cổng: last_reviewed của chúng là ngày chấm, và bắt chúng bump mỗi lần có ai gõ một dấu phẩy là làm hỏng đúng thứ khiến một báo cáo có giá trị.
| T6 | 9 doc chuyển draft → active, mỗi doc kèm bằng chứng đo được chứ không kèm cảm nhận |
| Doc | Bằng chứng chuyển sang active |
|---|---|
| SDD-013 Coral | module coral + app Coral đã deploy |
| SDD-015 Student Portrait | module portraits + 6 portrait thật |
| SDD-016 Orca | module orca + 14 competition thật |
| SDD-017 Retention | module retention + 164 dòng learner_retention + cron 21:00 UTC |
| SDD-018 Community Events | module events + bảng có dòng |
| SDD-019 Mentor Albums | 4 mentor_profiles + /v1/public/albums đang phục vụ |
| SDD-020 School Registry | 3 target_schools + trang trường công khai đang chạy |
| SDD-021 Learning Entry Sequencing | 667 cạnh unit_prereqs + hé cửa đang chạy trong learn |
conventions.md | chính nó là luật mà bốn cổng CI đang thi hành, mà lại mang nhãn "bản nháp" |
Hai doc CỐ Ý giữ draft, và đó là câu trả lời đúng: SDD-012 (Real Exam Bank) vì production có 0 dòng exams nào source_kind='real', và SDD-014 (Exam Acquisition) vì chưa có module crawler nào. Nhãn draft ở hai chỗ này đang nói đúng sự thật.
Bốn doc để chủ dự án quyết, vì đây là quyết định sản phẩm chứ không phải vệ sinh tài liệu: prd-001 (438 REQ, 258 đã ✔ — nhưng đổi trạng thái tài liệu yêu cầu gốc là việc của người sở hữu nó), open-questions/index.md, chuyenchon-app-requirements.md, legacy-feature-inventory.md. Riêng workflows/two-door-onboarding.md giữ draft đúng: grep toàn repo không thấy mã nào thi hành luồng hai cửa.
Hai lần cổng mới tự vấp, và vì sao đáng ghi lại
Cả hai cổng dựng trong đợt này đều xanh ở máy, đỏ trên CI ngay lần đầu. Cùng một nguyên nhân gốc: chúng đo một thứ không giống nhau ở hai nơi.
| Cổng | Đo sai chỗ nào | Sửa thành |
|---|---|---|
| Chống trôi | existsSync trên thư mục làm việc. apps/data là app đã ngừng, git không còn tệp nào, nhưng ở máy còn sót thư mục rỗng chứa .env cũ | Đo theo git ls-files — danh sách giống hệt ở mọi máy |
last_reviewed | git log -1 trên bản clone nông. actions/checkout@v4 mặc định fetch-depth: 1, nên mọi tệp trả về cùng một commit và 55 doc đỏ oan | Nhận ra clone nông thì bỏ qua và nói rõ, thay vì đỏ oan; đồng thời đặt fetch-depth: 0 cho ci.yml và deploy-docs.yml để cổng thật sự chạy ở đó |
Bài học chung, đáng đắt hơn cả hai cổng: một cổng cư xử khác nhau giữa máy và CI thì tệ hơn là không có cổng, vì nó dạy người ta bỏ qua kết quả CI. Và khi cổng không đủ dữ kiện để chấm thì nói thẳng là không chấm được vẫn tốt hơn là đỏ oan.
T7 — đợt đầu, và một con số tự nó nói ra vấn đề
Canonical hoá 6 REQ cho ba chỉ đạo mà mã đã chạy production nhưng tài liệu không mô tả:
| REQ mới | Biến chỉ đạo nào thành luật |
|---|---|
REQ-PLT-15 | Bảng learner của admin hiện phụ huynh, và supporter KHÔNG tính là phụ huynh (SRC-404) |
REQ-PLT-16 | Màn hình admin phân biệt ba trạng thái đang tải · rỗng · hỏng; danh sách bị cắt phải nói ra (SRC-416) |
REQ-PLT-17 | Hành động không hoàn tác được phải xác nhận; thao tác rủi ro thấp làm hàng loạt thì không hỏi (SRC-416) |
REQ-PLT-18 | Mọi endpoint vận hành có đường vào từ giao diện (SRC-416) |
REQ-SEC-07 | Phủ xác thực chứng minh được, đo bằng bảng route lúc chạy chứ không bằng grep (SRC-412) |
REQ-DOC-07 | Tài liệu không được trôi khỏi mã đang chạy (SRC-416) |
Cổng check-docs.mjs bắt ngay lỗi của chính đợt này: REQ-DOC-07 lúc đầu để trống ô SDD, và luật "REQ active phải trỏ tới một doc thiết kế" chặn lại. Đúng thứ một cổng nên làm.
Nhưng con số tổng thì đi ngược:
| Lúc chấm audit | Sau đợt sửa này | |
|---|---|---|
| Tổng SRC trong sổ | 415 | 440 |
| Chưa canonical hoá | 232 | 263 |
Đóng được 3, sổ dài thêm 25. Khoảng cách rộng ra ngay trong chính phiên đang thu hẹp nó. Đây là bằng chứng sống cho D1, và nó nói rằng đuổi theo bằng tay là cách sai: chừng nào việc canonical hoá còn là một việc RIÊNG làm sau, nó sẽ luôn thua tốc độ ra chỉ đạo.
Đề xuất đổi cách, thay cho việc đuổi theo: buộc ràng buộc vào lúc GHI SỔ chứ không phải lúc dọn dẹp — một dòng SRC mới phải khai luôn nó sẽ nằm ở REQ/SDD nào, hoặc khai rõ "chưa canonical hoá" kèm hạn. Khi đó cổng đo được, và nợ không sinh thêm nữa; phần 263 dòng cũ mới đáng đem ra dọn dần.