Audit Standard · AS-01 tới AS-03
Một phần của Audit Standard. Chỉ báo AS-01 tài liệu và đóng vết, AS-02 yêu cầu và sản phẩm, AS-03 kiến trúc và hợp đồng API, kèm ngoại lệ tường minh.
AS-01 — Tài liệu & Đóng vết
| ID | Chỉ báo | Cách kiểm |
|---|---|---|
| AS-01.1 | Nguồn duy nhất & sổ tiếp nhận | |
| AS-01.1.1 | Mọi đặc tả hệ thống nằm trong docs/; không tồn tại bản copy song song "cho AI" / "cho human" ở nơi khác | find . -name "*.md" -not -path "*/node_modules/*" — mọi kết quả ngoài docs/ phải là README gốc hoặc nội dung app (apps/*/content), không phải đặc tả |
| AS-01.1.2 | Mỗi chỉ đạo/tài liệu đầu vào có đúng một dòng SRC-xxx trong intake.md, đủ ngày tuyệt đối + tóm tắt + kết quả canonical hóa | đọc các mảnh docs/intake/src-*.md |
| AS-01.1.3 | Không có SRC nào đã tiếp nhận mà nội dung chưa xuất hiện trong ít nhất một PRD/SDD/QG | grep -rn "SRC-<n>" từng SRC id trong docs/ — phải có ≥1 hit ngoài intake.md |
| AS-01.1.4 | Số SRC không trùng (lỗ số được phép từ SRC-951; con trỏ "Nguồn tiếp theo" bỏ từ SRC-1339) | script: đếm dòng | SRC-\d+ | trong intake.md, so uniq với tổng dòng |
| AS-01.1.5 | Không file .md nào ngoài docs/ (trừ README.md gốc và nội dung app) đóng vai trò đặc tả hệ thống | đọc kết quả AS-01.1.1, xác nhận từng file ngoại lệ là nội dung không phải đặc tả |
| AS-01.2 | Frontmatter & metadata | |
| AS-01.2.1 | 100% file trong docs/ mở đầu bằng YAML frontmatter | node scripts/check-docs.mjs |
| AS-01.2.2 | Mọi frontmatter đủ trường bắt buộc: id, type, title, owner, status, version, last_reviewed, ai_readable | script |
| AS-01.2.3 | id duy nhất toàn docs, lowercase, khớp vai trò file | script |
| AS-01.2.4 | type thuộc tập cho phép (prd/sdd/quality/convention/index/adr/reference/workflow) | script |
| AS-01.2.5 | status ∈ {draft, active, deprecated}; file deprecated ghi rõ file thay thế | script + đọc file deprecated (nếu có) |
| AS-01.3 | Đóng vết hai chiều | |
| AS-01.3.1 | Mỗi REQ active nằm trong satisfies của ≥1 doc thiết kế (SDD hoặc reference) | scripts/check-docs.mjs mục 2 |
| AS-01.3.2 | Mỗi REQ active có ≥1 QG ở cột QG của PRD-001 §5 | đọc PRD-001, mọi dòng ` |
| AS-01.3.3 | Mỗi SDD khai sources trỏ tới SRC tồn tại trong intake | script |
| AS-01.3.4 | Không có doc mồ côi: mọi file được liên kết từ index.md hoặc từ một doc khác | script (mục 4 check-docs.mjs bắt link gãy, không bắt file không được trỏ tới — bổ sung: grep -rL đối chiếu danh sách file với danh sách link đích) |
| AS-01.3.5 | Mọi US map tới ≥1 REQ, và mọi REQ priority MUST map tới ≥1 US hoặc WF | đọc PRD-001 §6 |
| AS-01.4 | Trang sinh tự động khớp source | |
| AS-01.4.1 | 🔴🚸 npm run gen:reference chạy xong không tạo diff so với HEAD (bỏ qua dòng last_reviewed) | npm run gen:reference && git diff --exit-code -I '^last_reviewed: ' -- docs/reference — đã enforce trong ci.yml bước 5 |
| AS-01.4.2 | Mọi bảng D1 có trong migrations/ đều xuất hiện ở reference/data-dictionary.md | so bảng — script gen-reference.mjs tự sinh, đối chiếu số bảng ở đầu file |
| AS-01.4.3 | Mọi route đăng ký trong router xuất hiện ở reference/api.md | so bảng — npm run contract:diff --print đối chiếu route count |
| AS-01.4.4 | Mọi event type phát trong code xuất hiện ở reference/events.md | so EVENT_REGISTRY (workers/api/src/shared/events.ts) với bảng trong events.md |
| AS-01.4.5 | Trang sinh tự động ghi rõ "sinh tự động — không sửa tay" ở đầu file | đọc — dòng ⚙️ Trang này sinh tự động… |
| AS-01.5 | Tươi mới & vòng đời | |
| AS-01.5.1 | Mọi doc active có last_reviewed cách ngày audit ≤ 90 ngày | script |
| AS-01.5.2 | Mỗi lần sửa nội dung đáng kể có tăng version trong cùng commit | git log -p -- <file> spot-check ≥5 commit sửa nội dung |
| AS-01.5.3 | Không có internal link gãy trong toàn bộ docs | node scripts/check-docs.mjs mục 4 |
| AS-01.5.4 | Ngày viết tuyệt đối; không có "tuần trước / gần đây / sắp tới" trong doc active | grep -rn "tuần trước|gần đây|sắp tới" docs/ — mọi hit phải là ngôn ngữ UI hiển thị cho người dùng, không phải mốc thời gian tài liệu |
| AS-01.5.5 | Mọi quyết định AI tự quyết mang ký hiệu ✍️ và nằm trong open-questions/index.md | đọc open-questions/index.md |
AS-02 — Yêu cầu & Sản phẩm
| ID | Chỉ báo | Cách kiểm |
|---|---|---|
| AS-02.1 | Chất lượng REQ catalog | |
| AS-02.1.1 | Mỗi REQ có ID đúng scheme REQ-<nhóm>-xx, nhóm thuộc bảng nhóm PRD-001 §5 | grep -oE "REQ-[A-Z]+-[0-9]+" docs/product/prd-001/req/*.md | sort -u đối chiếu bảng nhóm ở conventions.md §2 |
| AS-02.1.2 | Mỗi REQ mô tả testable: phán quyết đúng/sai được bằng quan sát, không dùng từ mơ hồ ("tốt", "nhanh", "dễ dùng", "thân thiện", "đẹp", "tối ưu") | grep -inE "tốt|nhanh|dễ dùng|thân thiện|đẹp|tối ưu" docs/product/prd-001/req/*.md — mỗi hit trong dòng REQ (không phải văn xuôi) là 1 phản ví dụ (L4) |
| AS-02.1.3 | Mỗi REQ có priority đúng một trong MUST/SHOULD/COULD — cấm M?, S? | grep -nE "| M\? || S\? || C\? |" docs/product/prd-001/req/*.md phải rỗng |
| AS-02.1.4 | Mỗi REQ ghi nguồn SRC-xxx hoặc nguồn người + ngày | đọc bảng REQ |
| AS-02.1.5 | Không có hai REQ mô tả cùng một hành vi (trùng nghĩa) — đo bằng token-overlap thô: hai description trùng ≥80% từ, chưa gắn 🗄️ superseded hoặc chưa có ghi chú phân biệt tường minh | script token-overlap trên cột "yêu cầu" của mọi REQ; đây là proxy gần đúng, không phải semantic dedup hoàn hảo — ghi rõ giới hạn trong báo cáo |
| AS-02.2 | User story & acceptance criteria | |
| AS-02.2.1 | Mọi US đúng format As a … / I want … / so that … | grep -c "As a \*\*" docs/workflows/*.md khớp tổng số US |
| AS-02.2.2 | Persona trong US thuộc tập persona đã định nghĩa (Nemo/Marlin/Dolphin/Staff…) | đối chiếu PRD §personas |
| AS-02.2.3 | Mỗi US có ≥1 acceptance criteria quan sát được | đọc — bảng US phải có cột AC riêng, không chỉ 3 cột (ID·story·REQ) |
| AS-02.2.4 | Acceptance criteria mô tả kết quả người dùng thấy, không mô tả giải pháp kỹ thuật | đọc — AC không chứa tên bảng D1/tên hàm/tên biến |
| AS-02.2.5 | Mỗi US MUST đã implement chỉ ra được màn hình/endpoint thật | đối chiếu app — click qua route thật hoặc grep path trong apps/*/src |
| AS-02.3 | Persona, JTBD & phủ vai trò | |
| AS-02.3.1 | Mỗi persona active có ≥1 JTBD được ghi | đọc parent-jtbd.md + PRD |
| AS-02.3.2 | Mỗi JTBD có ≥1 REQ phục vụ | trace |
| AS-02.3.3 | Ba cộng đồng (Nemos, Marlins, Dolphins) đều có luồng onboarding được đặc tả | đọc docs/workflows/ — có file riêng cho mỗi cộng đồng, hoặc ghi rõ dùng chung |
| AS-02.3.4 | Mỗi school (Turtle, Shark, Octopus, Squid, Ray, Whale) có mô hình riêng hoặc khai báo dùng chung tường minh | SDD-011 |
| AS-02.3.5 | Không có màn hình đang chạy production mà không thuộc persona nào | rà route từng app, đối chiếu persona trong PRD |
| AS-02.4 | Phạm vi & ưu tiên | |
| AS-02.4.1 | Mọi REQ trạng thái ✔ chỉ ra được artifact thực thi (code/migration/nội dung) | trace |
| AS-02.4.2 | Không có tính năng đang chạy production mà thiếu REQ tương ứng | rà ngược từ app — liệt kê route/feature, đối chiếu PRD |
| AS-02.4.3 | Backlog ứng viên (CC-*, FEAT-*) tách bạch khỏi REQ active, không lẫn bảng | grep -c "CC-|FEAT-" docs/product/prd-001/req/*.md phải là 0 trong bảng REQ chính |
| AS-02.4.4 | Mỗi FEAT legacy được phân loại: đã port / đã thay / cố ý bỏ (kèm lý do) | legacy-feature-inventory.md cột "Phân loại" đóng {đã-port, đã-thay, cố-ý-bỏ, chưa-quyết}; 0 dòng chưa-quyết |
| AS-02.4.5 | Không REQ MUST nào ở trạng thái ⏳ quá 30 ngày mà không có ghi chú lý do + ngày tuyệt đối | đọc PRD §7 |
| AS-02.5 | Quyết định mở & phê duyệt | |
| AS-02.5.1 | Mọi câu hỏi mở có ID Q-xxx và trạng thái (chờ / đã trả lời — trả lời không rỗng hoặc dòng ✍️) | grep -c "| Q-" docs/open-questions/index.md đối chiếu số Q có trả lời/✍️ |
| AS-02.5.2 | Mọi dòng ✍️ có lý do ≥15 ký tự và không thuộc danh sách cụm rỗng nghĩa ("theo ý chủ dự án", "đã quyết rồi", "tự quyết", "vì cần thiết", "hiển nhiên"); mỗi bảng ✍️ nằm dưới tiêu đề có ngày tuyệt đối | grep -inE "theo ý chủ dự án|đã quyết rồi|tự quyết|vì cần thiết|hiển nhiên" docs/open-questions/index.md phải rỗng trong các dòng ✍️; đo độ dài phần lý do sau ✍️ |
| AS-02.5.3 | Artifact "Rà mâu thuẫn giữa các quyết định ✍️" tồn tại, ≤90 ngày, khai luật ưu tiên, liệt kê mọi chỗ lệch tìm thấy, và khai rõ phạm vi đã rà (toàn bộ hoặc tập con có lý do — sampling minh bạch được chấp nhận cho đợt trước pilot) | đọc artifact — không còn bắt buộc "số Q đã rà == tổng Q"; bắt buộc dòng "Phạm vi: …" không rỗng |
| AS-02.5.4 | Bảng "Việc còn chờ chủ dự án" có cột Trạng thái, mọi dòng có giá trị | đọc open-questions/index.md §Còn chờ hoặc README |
| AS-02.5.5 | Mọi Q đã trả lời có cột "Đã vào" không rỗng (doc+§, path repo, hoặc vận-hành); link resolve | script: với mỗi Q có trả lời, kiểm cột "Đã vào" khác rỗng và (nếu là path) existsSync |
AS-03 — Kiến trúc & Hợp đồng API
| ID | Chỉ báo | Cách kiểm |
|---|---|---|
| AS-03.1 | Ranh giới module & monorepo | |
| AS-03.1.1 | Mọi createRoute( nằm trong workers/api/src/modules/, trừ các ngoại lệ liệt kê tường minh trong bảng "Ngoại lệ tường minh" ngay dưới bảng này | grep -rln "createRoute(" workers/api/src | grep -v "/modules/" — mỗi hit phải có mặt trong bảng ngoại lệ, nếu không là KHÔNG PASS (L4) |
| AS-03.1.2 | Đồ thị phụ thuộc module 0 chu trình và 0 import chéo trực tiếp giữa hai module (phải qua shared/ hoặc event), trừ cạnh liệt kê tường minh trong bảng "Ngoại lệ tường minh" | grep -rn "from \"\.\./\.\./[a-z]" workers/api/src/modules --include="*.ts" loại trừ import shared/ |
| AS-03.1.3 | Không fetch( nào trong apps/*/src ngoài đúng một src/api.ts/app | grep -rln "fetch(" apps/*/src | grep -v "/api.ts" phải rỗng |
| AS-03.1.4 | Không app định nghĩa lại CSS custom property đã có trong packages/design-system/tokens/*.css (file art/SVG miễn trừ) — xem thêm AS-09.2.1 (góc khác: hardcode hex khi dùng token, không phải định nghĩa lại token) | grep -rn "^\s*--[a-z-]" apps/*/src --include="*.css" đối chiếu tên biến đã có trong tokens/*.css |
| AS-03.1.5 | Mọi thư mục cấp 1 của apps//workers/ có tên trong README §cấu trúc và ngược lại; thư mục ngừng phải ghi rõ "đã ngừng" | đối chiếu ls apps workers với README |
| AS-03.2 | Hợp đồng API | |
| AS-03.2.1 | OpenAPI sinh từ router; không tồn tại file OpenAPI viết tay ngoài openapi.snapshot.json (chính là snapshot của bản sinh) | find . -iname "*.yaml" -o -iname "openapi*.json" | grep -v snapshot phải rỗng |
| AS-03.2.2 | Mọi operation có ≥1 response 2xx kèm schema JSON; mọi requestBody kèm schema JSON | npm run contract:diff -- --print | jq đếm operations thiếu schema |
| AS-03.2.3 | Mọi path trong spec sinh ra bắt đầu /v1 | npm run contract:diff -- --print | jq '.paths | keys' |
| AS-03.2.4 | 🔴🚸 Không breaking change trong /v1 (xoá field/route, đổi kiểu, thêm required ở request) so với openapi.snapshot.json; có mặt ở ít nhất một workflow chạy trên mọi push (nay là ci.yml) | npm run contract:diff exit 0 — hiện chạy trong ci.yml bước 6 trên mọi push/PR |
| AS-03.2.5 | Probe 404/401/400 trên production trả {error:{code,message}}, không rò ZodError/stack | curl thử endpoint sai, đọc body |
| AS-03.3 | Hợp đồng event/queue | |
| AS-03.3.1 | Mọi entry EVENT_REGISTRY có version(int)+payload(zod object) | đọc workers/api/src/shared/events.ts |
| AS-03.3.2 | Đối soát hai chiều: mọi tên publishEvent() gọi ∈ registry; mọi entry registry có ≥1 điểm gọi thật HOẶC ghi rõ "cố ý chưa có consumer — lý do + ngày" trong reference/events.md — quá 30 ngày không có REQ liên kết thì tính HH (mồ côi thật), không còn là CX | grep -rn "publishEvent(" workers/api/src/modules đối chiếu tên event với EVENT_REGISTRY keys |
| AS-03.3.3 | Tên event khớp domain.entity.action, action ở tập đóng thì quá khứ | grep -oE '"[a-z]+\.[a-z]+\.[a-z]+"' workers/api/src/shared/events.ts |
| AS-03.3.4 | assertNoFreeText() tồn tại+được gọi trong publishEvent; không z.string() trần/field PII không bị cắt | đọc workers/api/src/shared/events.ts — có hàm assertNoFreeText (xác nhận: tồn tại thật, dòng cuối file) |
| AS-03.3.5 | Số trong wrangler.jsonc (tên queue, max_batch_size, max_retries, DLQ) khớp đúng queues.md | đối chiếu workers/api/wrangler.jsonc với reference/queues.md |
| AS-03.4 | Vệ sinh secret/config | |
| AS-03.4.1 | 🔴 Không pattern key/secret/token hardcode trong source hoặc lịch sử git | grep -rE "(api[_-]?key|secret|token)\s*[:=]\s*['\"][A-Za-z0-9]{16,}" --include="*.ts" + git log -p --all | grep -E cùng pattern |
| AS-03.4.2 | Base URL API xuất hiện ≤1 lần/app, chỉ trong src/api.ts; literal nemo12.com còn lại phải là host trong README | grep -rn "nemo12.com" apps/*/src | grep -v "src/api.ts" |
| AS-03.4.3 | Mọi biến môi trường bắt buộc được khai kiểu trong workers/api/src/env.ts | node scripts/verify-bindings.mjs Lớp A |
| AS-03.4.4 | Mọi binding (D1/KV/R2/Queue/AI) được CI verify là tồn tại thật trên Cloudflare, có trong ci.yml | npm run verify:bindings Lớp C — cần CLOUDFLARE_API_TOKEN; không có token thì lùi về A+B (ghi rõ trong kết quả, không coi là PASS đủ Lớp C) |
| AS-03.4.5 | reference/config.md liệt kê đủ mọi biến cấu hình đang dùng (danh sách đóng khớp wrangler.jsonc) | npm run verify:bindings Lớp B |
| AS-03.5 | Cấu trúc & kỷ luật pipeline deploy | |
| AS-03.5.1 | Mỗi wrangler.jsonc có workflow deploy tương ứng với path filter đúng | .github/workflows/ đối chiếu apps/workers |
| AS-03.5.2 | Mỗi wrangler.jsonc khai custom_domain/routes (không *.workers.dev trần) | đọc wrangler config |
| AS-03.5.3 | 🚸 (nội dung mới, thay "app↔workflow đủ đôi" — xem §1.14) Không thao tác wrangler d1 migrations apply --remote nào chạy trực tiếp từ máy/phiên cục bộ ngoài đường ci.yml trên main và workflow_dispatch của deploy-app.yml. PASS nếu: (a) branch protection required-review bật trên main — kiểm gh api repos/<org>/<repo>/branches/main/protection (200) — HOẶC (b) plan hiện tại không bật được (403 "Upgrade to GitHub Pro") và có quy ước ghi tường minh trong README/CLAUDE.md và AS-04.6.1 không phát sinh dòng mồ côi mới nào trong 7 ngày gần nhất (bằng chứng gián tiếp là không ai apply tay) | gh api repos/.../branches/main/protection; nếu 403 thì đọc README + đối chiếu AS-04.6.1 |
| AS-03.5.4 | Bước apply-migration đứng trước bước wrangler deploy | đọc ci.yml và deploy-app.yml — xác nhận: dòng apply ở trước bước Deploy ở cả hai |
| AS-03.5.5 | Mọi URL production trong README trả 2xx/3xx; khớp đúng tập custom_domain trong mọi wrangler.jsonc | curl -sI từng URL trong README, đối chiếu domain list |
Ngoại lệ tường minh (AS-03.1.1 / AS-03.1.2)
Danh sách đóng — thêm dòng mới khi có ngoại lệ mới, không thêm ngoại lệ ngoài bảng này:
| Chỉ báo | Vị trí | Lý do |
|---|---|---|
| AS-03.1.1 | workers/api/src/index.ts:81 — healthRoute | Health check là route hạ tầng cấp worker, không thuộc nghiệp vụ một module cụ thể nào; xác nhận đây là hit duy nhất ngoài modules/ tính đến 2026-08-15 (grep -rln "createRoute(" workers/api/src | grep -v "/modules/" → 1 dòng) |
| AS-03.1.2 | (bảng trống tới 2026-08-15; 10 cạnh dưới đây khai ngày 2026-08-20 — Audit #004, SRC-412) | Tính đến 2026-08-15, grep import chéo giữa 19 module trả 0 kết quả (Audit #001 xác nhận). Audit #004 đo lại và thấy 10 cạnh mới, tất cả đều cố ý; khai ra đây thay vì im lặng chấp nhận |
| AS-03.1.2 | coral/routes.ts → content/quality, content/lifecycle, content/review-queue | Coral là mặt quản trị của chính miền content (SDD-013): tách qua shared/ sẽ tạo một lớp trung chuyển chỉ có đúng một người gọi. Cạnh một chiều, không có chu trình |
| AS-03.1.2 | learning/routes.ts → knowledge/mastery | Chấm một câu trả lời và cập nhật mastery là một giao dịch nghiệp vụ; đẩy qua event sẽ khiến learner thấy điểm cũ ở màn hình ngay sau khi trả lời |
| AS-03.1.2 | learning/routes.ts → retention/service, content/lifecycle, models/service | Ba lời gọi đọc/kích hoạt sau khi có bằng chứng mới (câu nhắc ôn, cờ nội dung AI, dựng lại model nếu cũ). models/service được gọi qua runAllModelsInBackground/refreshIfStaleInBackground nên không chặn request |
| AS-03.1.2 | progress/routes.ts → knowledge/mastery | Dùng lại đúng một hàm thuần stateFor(mastery, confidence); chép sang shared/ sẽ thành hai định nghĩa của cùng một ngưỡng |
| AS-03.1.2 | admin/observability.ts → models/service, models/contextEvents | Admin là chỗ bấm chạy engine và đọc taxonomy sự việc; nó phải gọi thẳng vào miền model để có bằng chứng thi hành đồng bộ (workflow_run + engine_run trong cùng lượt) |