Audit Standard · Lịch sử quyết định
Một phần của Audit Standard. Đổi gì từ v0.1 sang v0.2 và vì sao, từng quyết định §1.1 tới §1.15, và cách đối chiếu điểm số giữa hai phiên bản.
1. Đổi gì so với v0.1 và vì sao
Đối chiếu từng vấn đề trong hai bản phản biện — sửa hoặc ghi rõ vì sao giữ nguyên, không bỏ qua cái nào.
1.1 Vấn đề trung tâm: 44% bị đọc nhầm thành "hệ hỏng 57%"
L2/L3 của v0.1 ép mọi lý do KHÔNG PASS về cùng một ký hiệu (❌), nên báo cáo mất đúng phân biệt quan trọng nhất giữa ba loại: (a) chưa xây, (b) đã xây nhưng chưa chứng minh được, (c) đã xây và HỎNG.
Sửa — không phá bất biến nhị phân: KHÔNG PASS vẫn là KHÔNG PASS (một giá trị, không có "PASS một phần"). Nhưng từ v0.2, mọi dòng KHÔNG PASS trong báo cáo audit (không phải trong bảng chỉ báo — xem §1.7) phải kèm đúng một mã lý do:
| Mã | Tên | Nghĩa |
|---|---|---|
| CX | Chưa Xây | Phần hệ thống liên quan chưa tồn tại — không có code/bảng/tài liệu để chấm. |
| CM | Chưa chứng Minh | Phần đó có tồn tại, nhưng người chấm không đủ bằng chứng để kết luận PASS — gồm cả "chưa ai đi rà", "chưa có công cụ đo", "có code nhưng chưa test". |
| HH | Hỏng | Phần đó tồn tại, đã kiểm được, và kết quả cho thấy sai/vi phạm — có phản ví dụ cụ thể (L4). |
Đây là metadata của báo cáo, không phải trục điểm thứ hai đè lên PASS/KHÔNG PASS — hai người chấm cùng một chỉ báo cùng ra PASS hoặc KHÔNG PASS như trước; mã lý do chỉ mô tả tại sao nó KHÔNG PASS, ghi sau khi đã chấm xong. Khi tổng hợp: chỉ số đáng lo thật sự là %HH trong tổng KHÔNG PASS, không phải % KHÔNG PASS nói chung — CX và CM phản ánh độ chín của một hệ 2 ngày tuổi kể từ audit #001, không phải hệ hỏng.
1.2 [G]/[S]/[M] không được dùng trong v0.2
Một bản nháp trước đó gắn nhãn [G]/[S]/[M] cho từng chỉ báo mà không định nghĩa ở đâu — grep toàn repo không ra file nào khác chứa các nhãn này. Đây đúng là trục phân loại thứ hai chồng lên PASS/KHÔNG PASS mà ràng buộc bất biến cấm. v0.2 không dùng ba nhãn đó. Chỉ giữ hai ký hiệu đã có ý nghĩa rõ từ v0.1 (🔴 chặn release) cộng một ký hiệu mới có định nghĩa tường minh ngay dưới đây (🚸 Pilot Gate) — cả hai đều là tag lọc/ưu tiên, không phải giá trị chấm.
1.3 🔴 lệch pha với rủi ro thật + thêm 🚸 Pilot Gate
Phản biện chỉ ra hai lỗi ngược chiều nhau: (a) 🔴 chặn release vì lỗi sổ sách nhẹ (2 dòng migration mồ côi tự nó không phá dữ liệu) trong khi (b) rủi ro nặng hơn (schema drift thật giữa local và production) lại không 🔴. Đồng thời, 🔴 (chặn release nói chung, khái niệm dài hạn) không phân biệt được chỉ báo nguy hiểm cho đúng 10 gia đình thật trong 2 ngày tới với chỉ báo quan trọng về kiến trúc nhưng vô hại cho một pilot ngắn.
Sửa: tách đối soát production (§AS-04.6) thành 04.6.1 (đã đối chiếu + ghi lại lệch — rẻ, làm được hôm nay, 🔴) khác với 04.6.2 (0 lệch tuyệt đối — nợ dọn dần, không 🔴); nâng schema-diff thật (04.6.3) và bất biến tương thích ngược (04.6.4) lên 🔴 vì đó mới là rủi ro nặng. Thêm tag 🚸 Pilot Gate — orthogonal với 🔴, định nghĩa ở §3.3 — gắn cho ~18 chỉ báo chạm thẳng vào rủi ro dữ liệu trẻ em thật/mất dữ liệu không hồi phục trong khung 2 ngày tới. AS-04.5.5 (backup đã thử ít nhất 1 lần) được nâng lên 🔴 trong v0.2 vì lý do y hệt: pilot chạm dữ liệu thật, chưa từng restore-thử là rủi ro không thể chấp nhận, dù ở v0.1 nó chỉ là chỉ báo thường.
1.4 AS-04.6 "Đối soát production" — validated bằng bằng chứng thật, không phải giả định
Chạy trực tiếp trên production ngay lúc soạn tài liệu này (2026-08-15):
$ wrangler d1 execute nemo12-platform --remote --command "SELECT name FROM d1_migrations ORDER BY name"
→ 41 dòng
$ ls migrations/*.sql | xargs -n1 basename | sort
→ 39 fileHai dòng mồ côi: 0022_whale_country_detail.sql (file thật hiện đã đổi tên thành 0031_whale_country_detail.sql) và 0031_labs.sql (file thật hiện đã đổi tên thành 0037_labs.sql) — đúng y hệt sự cố "tái diễn" ghi trong bài học thực địa #4. AS-04.6.1 là chỉ báo có giá trị thật, đã chứng minh bắt được lỗi thật, ngay trong lúc viết chuẩn này — nó ở trạng thái KHÔNG PASS thật (mã HH) tính đến hôm nay, không phải ví dụ lý thuyết.
Đồng thời: git log --all --oneline -- migrations/0031_labs.sql và git fsck --unreachable --no-reflog đều 0 kết quả cho blob này — file chưa từng được commit ở bất kỳ ref nào git còn giữ. Ai đó chạy wrangler d1 migrations apply --remote thẳng từ working tree chưa commit. Hệ quả thiết kế quan trọng: mọi chỉ báo dựa vào git log --all làm nguồn sự thật (như 04.6.3–05 ở bản nháp trước) có điểm mù đúng bằng chính lỗi nó sinh ra để bắt. v0.2 đổi nguồn sự thật của toàn bộ §AS-04.6 sang d1_migrations remote thật (đối chiếu file trên đĩa), không dựa git history — xem thiết kế đầy đủ ở bảng AS-04.6.
1.5 "Toàn bộ lịch sử" → baseline có mốc
Chỉ báo bất biến tương thích ngược (AS-04.6.4) nếu quét "toàn bộ lịch sử" migrations/ thì vĩnh viễn không mở nổi một khi có một migration destructive nào đó từng chạy production trong quá khứ (không thể rút lại). v0.2 đổi thành: chỉ tính migration có applied_at (theo d1_migrations remote) sau mốc 2026-08-15 (ngày viết chuẩn v0.2) — không hồi tố.
1.6 Đường thoát "cố ý chưa có consumer" được khôi phục, thêm hạn 30 ngày
Một bản nháp trước xoá hẳn đường thoát "cố ý chưa có consumer" cho event registry — đi ngược đúng bài học #1: dựng hệ theo kiểu khai báo trước, nối consumer sau là bình thường; cấm hẳn nghĩa là gộp lại "đang xây dở" (bình thường) với "mồ côi vĩnh viễn" (hỏng thật) — đúng ba loại mà đợt nâng cấp này đang cố tách ra. v0.2 khôi phục đường thoát, kèm hạn: quá 30 ngày mà không gắn REQ nào thì tính là HH (mồ côi thật), không còn được coi là CX/cố ý nữa.
1.7 Ngưỡng "≥15 ký tự" cho lý do ✍️ — không đủ, thêm danh sách cấm cụm rỗng nghĩa
Đếm ký tự không đo được chất lượng lý do — "Đã quyết rồi, theo ý chủ dự án nhé" dài hơn 15 ký tự nhưng không giải thích gì. v0.2 giữ ngưỡng độ dài (điều kiện cần, dễ script hoá) cộng thêm một danh sách cấm cụ thể các cụm rỗng nghĩa (điều kiện đủ một phần, cũng script hoá được bằng grep case-insensitive): "theo ý chủ dự án", "đã quyết rồi", "tự quyết", "vì cần thiết", "hiển nhiên". Đây vẫn là heuristic không hoàn hảo — ghi rõ điều đó thay vì giả vờ nó hoàn chỉnh.
1.8 02.5.3 (rà mâu thuẫn quyết định ✍️) — gộp 5 điều kiện, đòi 100% cho một buổi trước pilot
v0.1/nháp trước gộp 5 điều kiện khác nhau vào một chỉ báo (tồn tại + ≤90 ngày + khai luật ưu tiên + liệt kê lệch + số Q đã rà == tổng Q) — khi trượt không biết trượt ở đâu, và điều kiện cuối đòi rà toàn bộ 165 dòng Q hiện tại cho một buổi chuẩn bị pilot là sai liều lượng. v0.2 tách: điều kiện tồn tại+ngày+luật ưu tiên giữ trong 02.5.3; bỏ yêu cầu "100% Q", thay bằng phạm vi đã rà phải khai rõ (toàn bộ hoặc tập con có lý do — ví dụ "chỉ rà Q đụng auth/dữ liệu trẻ em/thanh toán cho đợt pilot") — sampling minh bạch được chấp nhận, sampling giấu kín thì không.
1.9 AS-02.1.5 và AS-04.3.2 (biên PASS/FAIL không rõ) — viết lại, không xoá trắng
Hai chỉ báo này ("không có hai REQ mô tả cùng hành vi", "cột có ý nghĩa nghiệp vụ được giải thích") không có ranh giới đúng/sai — hai người chấm ra hai kết quả khác nhau, đúng vấn đề bài học #3 nêu.
- AS-02.1.5 viết lại thành phép đo gần đúng nhưng script hoá được: hai REQ description trùng ≥ 80% theo token-overlap thô bị coi là ứng viên trùng, phải gắn 🗄️ superseded hoặc bị bác bỏ tường minh (ghi lý do khác biệt) — biết đây là proxy không hoàn hảo cho "trùng nghĩa" thật, ghi rõ giới hạn thay vì giả vờ chính xác tuyệt đối.
- AS-04.3.2 (v0.1: "mỗi cột có ý nghĩa nghiệp vụ") xoá dạng cũ, thay bằng điều kiện đóng: mô tả cột không được rỗng và không được đúng bằng tên kiểu dữ liệu lặp lại (vd cột
statusmà mô tả là "status" hoặc "TEXT" thì KHÔNG PASS) — biên nhị phân rõ, script grep được. Bỏ hẳn yêu cầu "last_reviewed ≤ 90 ngày theo từng bảng" ở frontmatter file — thiết kế cũ có lỗi tự bào chữa: sửa một dòng bất kỳ trong file 1.700 dòng tự động bumplast_reviewedcho toàn bộ bảng, kể cả bảng chưa ai đọc lại. AS-01.5.1 (đã có, áp dụng cho mọi doc active) đã đủ kiểm độ tươi ở cấp file — không cần chỉ báo trùng lặp có lỗi thiết kế riêng.
1.10 AS-09.1 (luật tối giản) — từ "rà thủ công" sang có artifact kiểm được
Toàn bộ AS-09.1 ở v0.1 không có cách kiểm khách quan nào ("rà từng màn") — bài học #2 nêu đích danh cụm này. v0.2 đòi một artifact cụ thể docs/reference/ui-minimalism-audit.md: bảng route × app × 5 cột (một hành động chính / full-width / số element / hình-hơn-chữ / có là hub không) kèm bằng chứng (đường dẫn ảnh chụp hoặc file:line component), ngày ≤90 ngày. Artifact này chưa tồn tại — đúng, sẽ KHÔNG PASS (mã CX) cho tới khi ai đó dựng nó; nhưng ít nhất giờ có một con đường rõ ràng để biến 5 chỉ báo "chưa ai đi rà mãi mãi" thành "có thể rà một lần, ghi lại, rồi chỉ cập nhật phần đổi".
1.11 Chồng chéo phạm vi 03.1.4 ↔ 09.2.1/09.2.4
Cả hai đều kiểm vùng design-token nhưng ở hai bộ khác nhau — dễ bị cả hai người chấm bỏ qua vì nghĩ "việc của bộ kia". v0.2 thêm ghi chú tham chiếu chéo ngay trong bảng của cả hai chỉ báo (không gộp — mỗi cái kiểm góc khác nhau: 03.1.4 kiểm KHÔNG ĐỊNH NGHĨA LẠI token, 09.2.1 kiểm KHÔNG HARDCODE hex khi dùng).
1.12 AS-03.1.1/03.1.2 viện dẫn tài liệu chưa tồn tại
SDD-001 §2 không có "bảng cạnh cho phép" nào, và workers/api/src/index.ts:81 có createRoute( thật (health route) nằm ngoài modules/ — đúng ngoại lệ hợp lệ nhưng chưa được khai ở đâu. Chấm hôm nay sẽ trượt ngay vì tài liệu phụ trợ của chính chuẩn audit chưa được viết, không phải vì kiến trúc sai — dịch chuyển đúng lỗi bài học #1 sang tầng tài liệu-của-tiêu-chuẩn. v0.2 làm audit-standard.md tự chứa danh sách ngoại lệ (bảng "Ngoại lệ tường minh" ngay dưới AS-03), không phụ thuộc một file SDD chưa viết.
1.13 03.2.4 (contract:diff) không còn bắt buộc ở cả hai workflow
Yêu cầu contract:diff có mặt ở cả ci.yml và workflow deploy là phòng thủ kép giá trị biên thấp cho một repo một người tự review PR của chính mình. v0.2 chỉ đòi có mặt ở ít nhất một workflow chạy trên mọi push/PR (đã đúng thực tế: ci.yml chạy contract:diff trên mọi push tới mọi nhánh — xác nhận bằng grep, dòng 84).
1.14 Root cause thật của sự cố đa-agent: không có chỉ báo nào bắt "push thẳng main" / "apply migration ngoài CI"
Sự cố có hai lớp: (a) migration trùng số lọt production — AS-04.6 mới vá lớp này; (b) nguyên nhân gốc: agent chạy git add -A + push thẳng main, và/hoặc chạy wrangler d1 migrations apply --remote trực tiếp từ máy/phiên cục bộ ngoài pipeline CI (xác nhận bằng bằng chứng §1.4: 0031_labs.sql không có vết git nào). Không chỉ báo nào trong AS-01→AS-04 (bản trước) chạm lớp (b). gh api repos/.../branches/main/protection trả 403 "Upgrade to GitHub Pro" — branch protection kiểu GitHub thật sự không bật được trên plan hiện tại mà không đổi cấu hình/trả phí.
v0.2 thay nội dung AS-03.5.3 (v0.1: "app ↔ workflow đủ đôi" — chỉ báo yếu, gần như lặp lại 03.5.1) bằng chỉ báo mới nhắm thẳng root cause, có nhánh thoát tường minh cho repo cá nhân/plan free (không đòi hạ tầng chưa mua được) — xem bảng AS-03.5. ID được tái dùng cho nội dung khác hẳn — xem cảnh báo ở § Hướng dẫn chuyển đổi.
1.15 Khối lượng: quyết định KHÔNG mở thêm bộ mới, chỉ mở 1 tiêu chí
Một bản nháp trước đề xuất tới AS-11, AS-12 (mẫu số ước tính phình lên ~300+, +20%). Phản biện tính: dù không đổi gì khác, chỉ riêng việc phình mẫu số cũng khiến điểm hệ tụt từ 44% xuống ~36% trước khi ai sửa một dòng code — và làm ngưỡng 90%/REQ-DOC-06 càng xa hơn, đúng lúc cấm "hạ chuẩn". v0.2 không mở bộ mới, và chỉ thêm một tiêu chí mới (AS-04.6, 5 chỉ báo) — mẫu số đi từ 250 → 255 (+2%), thực chất làm sàn tuyệt đối (225) khó đạt hơn theo % (88% thay vì 90%), không dễ hơn. Nội dung root-cause quan trọng thứ hai (đa-agent/main) được gói vào bằng cách thay nội dung một chỉ báo yếu sẵn có (03.5.3) thay vì mở thêm chỗ.
Hướng dẫn chuyển đổi v0.1 → v0.2
Audit #001 (docs/quality/audits/2026-08-15.md) chấm theo v0.1. Audit #002 trở đi chấm theo v0.2. Hai điểm số KHÔNG so sánh trực tiếp được — mẫu số khác (250 vs 255) và một số chỉ báo đổi cách kiểm dù giữ nguyên ID. Đối chiếu như sau:
Chỉ báo MỚI hoàn toàn (không có ở v0.1) — 5 chỉ báo
AS-04.6.1, AS-04.6.2, AS-04.6.3, AS-04.6.4, AS-04.6.5 — không có điểm v0.1 để so, tính là chưa chấm lần nào.
ID tái dùng cho nội dung khác — không so PASS/KHÔNG PASS của ô này giữa hai lần audit
AS-03.5.3 — v0.1 là "app ↔ workflow đủ đôi" (Audit #001: ✅), v0.2 là "không apply migration production ngoài CI" (nội dung khác hẳn). PASS của Audit #001 tại ô này vô nghĩa cho v0.2 — phải chấm lại từ đầu.
Chỉ báo đổi cách kiểm đáng kể (ID giữ nguyên, phương pháp chấm đổi) — chấm lại, đừng copy kết quả cũ
| ID | Đổi gì |
|---|---|
| AS-02.1.5 | Từ "rà thủ công" sang proxy token-overlap ≥80% — Audit #001 ghi "chưa rà"; v0.2 có cách chấm, phải chấm lần đầu |
| AS-02.5.2 | Thêm danh sách cấm cụm rỗng nghĩa bên cạnh ngưỡng ký tự — có thể lật PASS cũ thành KHÔNG PASS nếu lý do ✍️ trùng cụm cấm |
| AS-02.5.3 | Bỏ yêu cầu "100% Q đã rà", thêm yêu cầu "khai rõ phạm vi" — dễ PASS hơn về khối lượng, khó hơn về minh bạch |
| AS-03.1.1 / AS-03.1.2 | Thêm bảng "Ngoại lệ tường minh" tự chứa trong chính file này — không còn phụ thuộc SDD-001 chưa có bảng cạnh cho phép |
| AS-03.2.4 | Chỉ cần có mặt ở 1 workflow thay vì 2 |
| AS-03.3.2 | Khôi phục đường thoát "cố ý chưa có consumer", thêm hạn 30 ngày |
| AS-04.3.2 | Viết lại hoàn toàn — điều kiện cũ ("ý nghĩa nghiệp vụ") không có biên rõ, không thể so với điều kiện mới (mô tả không rỗng, không trùng tên kiểu) |
| AS-04.6.4 (mới, nhưng liên quan) | Không hồi tố — chỉ tính migration từ 2026-08-15 trở đi |
| AS-09.1.1–09.1.5 | Từ "rà từng màn" (không có artifact) sang đòi docs/reference/ui-minimalism-audit.md — Audit #001 ghi "chưa rà" cho cả 5; v0.2 gần như chắc chắn vẫn KHÔNG PASS (mã CX, artifact chưa tồn tại) nhưng lý do khác: trước là "chưa ai đi rà", giờ là "chưa có artifact để rà vào" |
Cách đối chiếu điểm số đúng
Không có công thức quy đổi cơ học 109/250 (v0.1) → x/255 (v0.2) — làm vậy vi phạm chính L3 (không bằng chứng = không PASS) áp dụng cho bản thân phép quy đổi. Audit #002 phải chấm lại từ đầu theo v0.2, dùng Audit #001 làm tài liệu tham khảo cho các chỉ báo không đổi (đọc cột "Bằng chứng" ở Audit #001, xác nhận vẫn còn đúng tại thời điểm audit #002, không copy kết quả mù).