Audit #013 — Toàn hệ theo 6 chiều (2026-09-03, commit df9e373)
Chỉ đạo SRC-674: rà toàn bộ hệ thống, ra báo cáo, rồi nâng cấp dần theo báo cáo (thứ tự sửa tự quyết, commit liên tục, 30 phút push + merge một lần). Bản này là báo cáo; phần "Trạng thái" trong các bảng dưới được cập nhật ngay trong phiên khi một phát hiện được sửa và merge.
Giống Audit #005 (2026-08-26), lần này không chấm lại 255 chỉ báo của Audit Standard v0.2: thang điểm toàn hệ gần nhất là Audit #011 và một lần chấm điểm nữa không đổi được gì nếu chưa sửa. Mỗi phát hiện dưới đây nối về chỉ báo AS tương ứng để lần chấm điểm kế thừa được.
1. Phương pháp và phạm vi
| Bước | Cách làm | Kết quả |
|---|---|---|
| Chốt phạm vi | origin/main @ df9e373 (2026-09-03), cây riêng session/audit-2026-09-03 | 12 app · 3 worker · 29 module API · 195 migration · 118 file test · 468 file docs |
| Máy chạy trước | typecheck · lint · test · check:docs · audit:bounds · contract:diff · verify:bindings | typecheck ✓ · lint ✓ · contract ✓ (298 route, 0 breaking) · bindings ✓ · check:docs ✗ (em dash, xem U-1) · test ✗ local (timeout, xem T-5) · audit:bounds ✗ (đã biết, REQ-KNW-17) |
| Rà bằng agent | 6 agent đọc code song song, mỗi agent một chiều, bắt buộc bằng chứng file:dòng, tự loại phát hiện không xác minh được | 13 + 17 + 18 + 22 + 24 + 23 = 117 phát hiện, sau khử trùng còn 106: 🔴 15 · 🟡 55 · ⚪ 36 |
| Đối chiếu chéo | Cùng một lỗi được ≥2 chiều nhìn thấy độc lập thì ghi một dòng, giữ chiều nhìn rõ nhất | 11 dòng trùng (em dash U-1, migration-trước-code I-1/T-2, README số migration C-10/I-15/T-11, cổng em dash chỉ quét 2 app U-10/I-22, bundle P-10/U-7, dictation không rate limit S-9/P-12…) |
Sáu chiều và chỉ báo AS đối chiếu: S bảo mật & riêng tư (AS-07) · P chi phí & hiệu năng (AS-10, AS-08) · C đúng đắn & tin cậy (AS-03, AS-04, AS-08) · U trải nghiệm & design system (AS-09) · I vận hành, CI, công cụ (AS-08, AS-04, AS-03.5) · T test & lệch tài liệu (AS-01, AS-05, AS-06).
Bối cảnh cần biết khi đọc: hoá đơn Cloudflare tháng 8/2026 nhảy bất thường (D1 đọc dòng ~$158, Workers AI ~$86, hoá đơn IN-77746678). Một phiên khác đang xử lý migration 0196_hot_path_indexes và công tắc KV ai_cron_paused; báo cáo này không lặp hai việc đó.
2. Mười lăm phát hiện 🔴 (chặn phát hành hoặc đang mất tiền/dữ liệu)
| # | Chiều | Phát hiện | Bằng chứng | Chỉ báo | Trạng thái |
|---|---|---|---|---|---|
| S-1 | Bảo mật | IDOR module curriculum: 6 route bài ngẫm / chặng / cụm nhận learner_id qua query/body mà không có requireLearnerAccess; tài khoản bất kỳ đọc và ghi đè đoạn văn trẻ tự viết về mình của mọi learner | curriculum/routes.ts:56-186, service.ts:251-333 | AS-07.2.1 🔴, AS-07.2.3 | ✅ sửa 5c5883b + test |
| S-2 | Bảo mật | IDOR module dictation: 8 route learner (options · start · list · audio · check · leaderboard · delete) không chốt quyền; kẻ có một tài khoản liệt kê/nghe/xoá bài của learner khác và khởi động workflow sinh bài (model + TTS) cho learner_id bất kỳ, mỗi id 3 bài | dictation/routes.ts:198-312, practice.ts:67-106 | AS-07.2.1 🔴, AS-10.5.5 | ✅ sửa 5c5883b + test |
| S-3 | Bảo mật | Lưới authCoverage lớp 2 mù: chỉ quét {learnerId} trong path nên hai module trên lên production mà CI xanh; bảng xếp hạng còn trả learner_id của mọi trẻ cùng khối, tức cung cấp sẵn danh sách id để khai thác S-1/S-2 | authCoverage.test.ts:168, practice.ts:242-270 | AS-07.2.1 🔴, AS-05.3.2 | ✅ sửa 5c5883b: quét cả learner_id trong schema, bảng xếp hạng bỏ id |
| P-1 | Chi phí | Cron */5 gọi AI không có trần USD/ngày; runPrompt bỏ res.usage nên không ghi token/USD ở đâu. Trần lý thuyết 30 lượt × 288 nhịp = 8.640 lượt/ngày ≈ $26/ngày | shared/aiBudget.ts:34, index.ts:302-335, prompts.ts:1424-1440 | AS-10.5.1 🔴, AS-10.5.2 | ✅ đợt 3: runPrompt đọc usage, log ai_call, đồng hồ tiền trong ngân sách nhịp; trần AI_DAILY_USD_CAP=3 USD/ngày ở KV, chạm là nhịp không gọi model |
| P-2 | Chi phí | 9/12 chặng xưởng curriculum sinh lại mãi target đã bị từ chối (chỉ story · outcomes · focus có log loại trừ); hai chặng dịch lấy LIMIT không ORDER BY nên dòng hỏng đứng đầu hàng chờ vĩnh viễn. Mỗi target kẹt ≈ 288 lượt/ngày ≈ $26/tháng | curriculum/authoring.ts:133-137,185-190,348,472-474,510-513,541-548,615,672,867 | AS-10.5.1 🔴 | ✅ đợt 3: notRecentlyLogged ở cả 9 chặng (rejected nghỉ 7 ngày, failed 1 ngày), chặng dịch có ORDER BY + khoá theo dòng; test trên SQLite thật |
| P-3 | Chi phí | Máy dịch b21 chạy ngoài ngân sách chung, RETRY_CAP=12 hỏi lại từng mẩu kẹt mỗi nhịp, đẩy nhịp cron sát trần khoá 240 s (khoá KV không nguyên tử nên nhịp sau chạy chồng) | index.ts:326, b21/translate.ts:130-136,190-197 | AS-10.5.1 🔴, AS-08.2.1 | ✅ đợt 3: runB21Translation(env, budget) xin lượt cho cả lô lẫn hỏi lại; mẩu kẹt sau hỏi lại ghi content_authoring_log stage b21-translate, nghỉ 7 ngày |
| C-1 | Đúng đắn | Mastery bị áp hai lần khi POST answer lặp: learner_evidence là INSERT OR IGNORE nhưng learner_skill_state là ON CONFLICT DO UPDATE vô điều kiện; phiên đã completed vẫn nhận answer. Hồ sơ năng lực nói N+1 lần đo trong khi sổ bằng chứng có N dòng | learning/routes.ts:684-686,733-786,878-890 | AS-05.2.2 🔴, AS-04.2.2 | ✅ đợt 3: soi learner_evidence.event_id cùng chuyến với prev, lượt lặp trả feedback kèm duplicate: true và không ghi gì; phiên không in_progress → 409; lab cùng cách; test D1 gọi hai lần |
| U-1 | Giao diện | Cổng em dash (SRC-379) đỏ trên main với 408 chỗ trong 8 chương ebook + ebookLibrary; CI không thấy vì check:docs chỉ chạy khi commit đụng docs/ (xem I-5) | check-no-emdash.mjs, ci.yml:148-150 | AS-09.4.1, AS-08.4.1 | ✅ sửa ef0e9b0 (8 file); 4 chương mới lên sau đó đang dọn |
| U-2 | Giao diện | Bản canonical trong packages/design-system lệch với app, app lệch nhau: palette.ts của web/coral/b21 còn aqua101 #F2F2F2 (bug SRC-182 "hình biến mất" tái diễn ở nemo12.com), Tour/ReferralPanel canonical còn n12-*, SwimmingLoader mentors lệch, shadcn card/dialog lệch. Gốc: package chỉ exports CSS, không app nào import được TSX | md5 12 bộ file; packages/design-system/package.json | AS-09.2.2 | ⏳ |
| U-3 | Giao diện | Menu avatar Marlins hiện 👤 Profile · ⚙️ Settings · ↪ Logout, nav nemo12.com hiện Login: chữ tiếng Anh + emoji làm icon ở nơi bố mẹ bấm mỗi ngày | marlins/AvatarMenu.tsx:35-42, web/SiteNav.tsx:97,110 | AS-09.4.1, AS-09.4.2 | ✅ đợt 3: "Hồ sơ · Cài đặt · Đăng xuất" với icon lucide, "Đăng nhập" ở nav web |
| I-1 | Vận hành | Code api lên production TRƯỚC migration, không cổng nào chặn: ci.yml deploy api trên mọi push đụng workers/api hoặc migrations/, migration chỉ áp khi bấm tay deploy-api.yml; lý do cũ ("token deploy không cần quyền D1") đã hết hiệu lực vì cùng một token. 20 commit có migration trong 7 ngày, 30 lần bấm tay từ 26/08, commit 23b2368 lệch ~12 phút | ci.yml:268-274, deploy-api.yml:43-51, scope.sh:114 | AS-04.6.4 🔴, AS-03.5.3 | ⏳ |
| I-2 | Vận hành | scope.sh lấy mốc "lần ci xanh gần nhất" không lọc --event push: một lần bấm tay ci xanh trên main (không deploy gì) làm mọi thay đổi trước đó không bao giờ được deploy, màn hình vẫn xanh | scope.sh:34 | AS-08.4.1 | ✅ sửa 5fcae84 |
| I-3 | Vận hành | rollback.yml chưa từng chạy, ghi nemo12-mentors trong khi worker thật là nemo12-dolphin, thiếu 6/15 worker, trỏ docs/operations không tồn tại | rollback.yml:27,80-81,125 | AS-08.4.3 🔴 | ✅ sửa 5fcae84 (chưa diễn tập) |
| I-4 | Vận hành | Diễn tập backup/restore (22/08, mốc 67 migration) và đối soát migration (20/08) đều quá hạn so với chính ngưỡng Pilot Gate (AS-04.5.5, AS-04.6.1 ≤7 ngày); từ đó thêm 129 migration và một vụ đổi số 0162→0176 | docs/ops/backup-restore-drill.md:19-21, migration-reconciliation.md:8 | AS-04.5.5 🔴, AS-04.6.1 🔴 | ⏳ việc tay |
| T-1 | Docs | SDD-002 mô tả thuật toán mastery/confidence khác code: doc α = reliability×recency×difficulty×independence×lr, confidence có source_diversity ("10 câu cùng dạng → 0.48"); code lr = (0.35+0.35·surprise)/(1+0.6·count)·reliability, confidence 1−0.55^n (10 câu cùng dạng → 0.9975); rapid 2500 ms cố định thay vì max(2.5s, 20% expected_time). reference/engines.md ghi đúng code, tức SDD và reference mâu thuẫn | sdd-002:82-96,214-218 ↔ knowledge/mastery.ts:42-68 | AS-05.4.5, AS-05.1.4 | ✅ đợt 4 (docs): SDD-002 §7/§8/§18 thêm khối "v1 ĐANG CHẠY" với công thức thật và nói rõ phần chưa có (diversity factor, expected_time); việc hiện thực source_diversity_factor ghi là việc kế, chưa có SRC |
| T-2 | Docs | README và migrations/README.md nói migration được áp trước code khi push; thực tế push chỉ deploy code (cùng gốc với I-1). migrations/README.md tự mâu thuẫn giữa §1 và đoạn cuối, và ghi "số tiếp theo 0070" khi đã có 195 | README.md:45-47, migrations/README.md:15-22, đoạn cuối | AS-01.4.1 🔴 | ⏳ |
| T-3 | Docs | reference/schedules.md khai 1 cron, api chạy 4 (0 21, 0 19, 10 *, */5) + foundry */15; luật "không có cron ẩn" (REQ-DOC-02) bị vi phạm 3 lần; cổng freshness không bắt vì chỉ đo ngày commit của chính doc | schedules.md:18-22,97 ↔ workers/api/wrangler.jsonc:14 | AS-01.4.1 🔴, AS-08.3.1 | ⏳ |
| T-4 | Docs | PRD-001 đánh ✔ (đã hiện thực) REQ-EXAM-05..08 trong khi Real Exam Bank chưa build: SDD-012 draft, không migration nào tạo exam_problems, 'authentic' không có trong code, module exams 0 test | prd-001:641-644, sdd-012:6 | AS-01.3.2, AS-02.4.5 | ✅ đợt 4: bốn REQ về ⏳ kèm lý do + ngày ở PRD §7 |
| T-5 | Test | Testkit nạp lại 195 migration (11,7 MB) ở mỗi beforeEach: npm test local 18 file / 96 test đỏ vì Hook timed out 10000ms; CI test 52 s (23/08) → 222 s (03/09); mỗi migration nội dung mới cộng vào MỌI test | runlog.testkit.ts:73-84, 21 file gọi trong beforeEach | AS-05.3.5, AS-08.4.1 | ✅ đợt 7: khuôn schema VACUUM INTO một lần mỗi worker + copy file mỗi test; 34 file D1 chạy 13 s (trước ~300 s, 96 test đỏ giả) |
(Bảng trên có 19 dòng vì S-1/S-2/S-3 và T-2/T-3 là hai cụm cùng gốc; đếm theo gốc lỗi là 15.)
3. Phát hiện 🟡 theo chiều (55, rút gọn, đủ bằng chứng trong code)
3.1 Bảo mật & riêng tư (S-4 … S-10)
- S-4 Tool Plane: nhánh token "đường cũ" (
tp_agents.token_hash) tin headerX-Nemo12-User-Idkhông kiểm chứng; grant learning-agent scopeusernên ai cầm token tự nhận là admin và đọc hồ sơ mọi trẻ; token production đang cấp đúng đường cũ;/mcpkhông rate limit.tool-plane/src/auth/authentication.ts:49-70,scripts/tool-plane/seed-*.sql. 🟡 đợt 5: token đường cũ không được đóng vai người có vai nội bộ (admin/staff/mentor) qua header — đúng kịch bản khai thác; vai gia đình vẫn đi qua để ba token đang cấp không gãy, kèm log cảnh báo. Việc tay còn lại: phát hành lại 3 token bằngissue-agent-token.mjs --userrồi bỏ hẳn nhánh cũ; rate limit/mcp. - S-5 Thư tổng kết (tên trẻ + điểm + kỹ năng yếu) BCC mặc định về Gmail cá nhân, hardcode trong mã, không khai ở trang minh bạch dữ liệu.
digest/email.ts:38,wrangler.jsonc:74,privacy/routes.ts:297-302. ✅5fcae84: địa chỉ chỉ từ cấu hình, trang minh bạch khai bên nhận này khi bật. Quyết định giữ BCC là của chủ dự án (chỉ đạo 27/08), giữ nguyên. - S-6 Diễn đàn và sự kiện:
author_namerơi về email khi user chưa códisplay_name(tài khoản Workspace for Education) và hiện cho mọi gia đình.forum/routes.ts:33,events/routes.ts:80. ✅5fcae84: nhãn ẩn danh khi ghi, che@khi đọc. - S-7
POST /v1/learners/{id}/context-events: mọi dòngfamily_members(kể cả rolelearner,supporter) ghi được lời khai vớisource_role='parent'; Planning Engine và mentor đọc như lời bố mẹ.models/routes.ts:54-62. ✅5fcae84: chỉ owner/guardian. - S-8 Yêu cầu xoá dữ liệu chỉ được INSERT (
data_deletion_requests), không mã nào thi hành; không endpoint export;retention_policies.keep_dayskhông có job xoá; trang "dữ liệu nào đang lưu" kể 6 nhóm trong khi ~44 bảng cólearner_id.privacy/routes.ts:354-357,policies.ts:7-26. Sửa: cronrunDeletionRequestsđi qua danh sách bảng sinh từmigrations/(test khớp số bảng),GET /v1/privacy/export. - S-9
RATE_LIMITS.submit/crawlkhai nhưng không dùng; answer, lab evidence, dictation start/check không rate limit.shared/ratelimit.ts:31-40. ✅ đợt 4:submitgắn vào practice answer, lab evidence, dictation check; rule mớidictationCreate20 lượt/ngày/tài khoản cho khởi động workflow sinh bài. - S-10 Admin cấp/thu quyền, giao mentor, liệt kê learner không ghi
audit_log(trong khi replay DLQ, sync retention có).admin/routes.ts:62-110,observability.ts:259,453. ✅ đợt 4 cho ba route ởadmin/routes.ts(role.granted/revoked,mentor.assigned/unassigned,learner_data.list); hai chỗ trongobservability.tscòn chờ.
3.2 Chi phí & hiệu năng (P-4 … P-12)
- P-4
assessment_responseskhông có chỉ mục theolearner_id: ba câu nóng (bắt đầu luyện, mở cockpit, Learner Model engine) quét cả bảng. Đính chính sau đối chiếu với phiên giữ 0196: bảng hiện chỉ ~829 dòng nên chưa góp đồng nào vào hoá đơn tháng 8; đây là chỉ mục phòng trước cho bảng tăng nhanh nhất hệ, không phải chỗ đang chảy máu (chỗ đó làitemsthiếu chỉ mục dẫn đầunode_id, mục 1-3 của 0196). ✅ Hai chỉ mục đã được gộp vào0196_hot_path_indexes(mục 4,(learner_id, created_at DESC, item_id)để câuSELECT DISTINCT item_idđọc trọn trên chỉ mục), chờ phiên đó push. - P-5 Cron phút 10 mỗi giờ sàng lọc lại 1.548 candidate "giữ lại" y hệt, UPDATE + enqueue mỗi giờ (~37k ghi/ngày cho việc không đổi kết quả).
content/backfill.ts:187-226. ✅ đợt 5: chỉ chọn candidate chưa cóquality_jsonhoặc sàng lần cuối quá 7 ngày. - P-6 Cockpit ~30 lượt D1 tuần tự mỗi lần mở: đọc
skill_nodescủa môn 5 lần,COUNT itemstheorolekhông chỉ mục,FOUNDATION_SQLchạy 3 lần.knowledge/routes.ts:597-813. - P-7 WF-04 (8 engine, ~40 SELECT + ~35 ghi tuần tự) được
awaittrong request nộp bài chẩn đoán/đề thi/onboarding, learner chờ 1,5-3 s;learning/routes.ts:343đã làm đúng (runAllModelsInBackground). ✅ đợt 5 cho 4 chỗ (nộp đề thi, 3 route khai mục tiêu/lịch thi); nộp chẩn đoán giữ đồng bộ vì cockpit trả về ngay sau cần model mới. - P-8 Endpoint đọc công khai (whale, b21, 9 route showcase public) đập thẳng D1 mỗi request, chỉ 2 chỗ dùng
withCache;Cache-Control: publictrên custom domain không được CDN cache nếu không dùng Cache API. 🟡 đợt 7:withCacheKV cho/v1/whale/countries(1 h) và ba endpoint đọc b21 (5 phút); showcase public và Cache API còn chờ. - P-9 Không có chỉ số
rows_read/chi phí D1 nào trong log; hoá đơn bất ngờ kế tiếp sẽ lại lộ ra khi Cloudflare gửi invoice. Sửa: proxyenv.DBcộng dồnmeta.rows_readtheo request/cron tick,logEvent d1_heavykhi >50k dòng; scriptwrangler d1 insightshằng ngày; billing notification. - P-10 / U-7 Bundle: learn
index1.052 kB (explore/configs 249 kB import tĩnh), marlins 867 kB (parentCourses207 kB ở trang chủ), web 606 kB;React.lazycả repo = 1; khôngmanualChunks. - P-11 Mọi request có phiên = 1 UPDATE
sessions.last_seen_attuần tự trước handler (ghi D1 đắt gấp 1000× đọc).auth/service.ts:171-174. ✅ đợt 5: chỉ ghi khi dấu cũ hơn 15 phút. - P-12 / S-9 Dictation Forge: learner tự kích AI (Llama + TTS), hạn mức bỏ qua bài
failed, không rate limit.
3.3 Đúng đắn & tin cậy (C-2 … C-11)
- C-2 Xoay phiên đua nhau: revoke rồi INSERT phiên mới, 3-5 request song song cùng cookie cũ đúng ngày thứ 7 → "Phiên đã hết hạn" giả hoặc hai phiên mới, một mồ côi 30 ngày.
auth/service.ts:132-172. ✅ đợt 6: tạo con trước, giành revoke cha bằngUPDATE … WHERE revoked_at IS NULL+meta.changes, thua thì xoá con của mình; token vừa xoay còn ân hạn 60 s (chỉ khi có con, logout thật thì không); test 5 request song song → 1 con, 0 lần 401. - C-3 Accept lời mời không guard
status='pending'trong UPDATE; hai tài khoản dùng cùng mã → cả hai vào family,learners.user_idbị ghi đè.invitations/routes.ts:266-319,384-419. ✅ đợt 6: giành lời mời bằng UPDATE có điều kiện trước,changes ≠ 1→ 409;UPDATE learners … AND status='invited'. - C-4 73 khối
catch {}"chưa chạy migration" nay nuốt mọi lỗi D1, cả đường ghi: INSERTassessment_sessionslỗi tạm → INSERT lại khôngadaptive_json→ chẩn đoán tĩnh không ai biết.knowledge/routes.ts:164-174,content/routes.ts:106-110. - C-5 "Ngày" tính UTC ở 8 chỗ, +07 ở 2 chỗ;
daysBetweenđịnh nghĩa hai lần khác ngữ nghĩa. Orca báo "hết hạn đăng ký" từ 19:01 VN ngày hạn chót;days_to_examlệch +1 lúc 00:00-07:00; lab hai lượt sáng/chiều thành hai evidence. ✅ đợt 7:shared/time.tsvớivnDate/daysUntilVn, 6 chỗ đổi sang ngày lịch VN. - C-6
dictation_setsghi timestampdatetime('now')(có dấu cách) trong khi cả hệ dùng ISOT…Z; Safari đọc thành Invalid Date.0190:34-35,58,dictation/routes.ts:140. - C-7 Hợp đồng API mù ở 242/298 operation (
z.any()), 78as never: đổi tên field response không làmcontract:diffđỏ. - C-8 Tạo bản ghi không idempotent:
semester_exam_schedulekhông UNIQUE(learner_id, subject_id, exam_date)→ Constraint Model liệt kê hai kỳ thi cùng ngày. ✅ đợt 6 ở tầng route (tìm dòng cùng môn + ngày trước khi INSERT); UNIQUE ở bảng chờ migration. - C-9 Foundry:
content_runstreorunningvĩnh viễn nếu D1 lỗi lúc ghi trạng thái cuối, không sweeper; cron và nút tay bị 429 "trần song song" cho tới khi sửa tay. ✅ đợt 6: cron*/15quét runrunningquá 2 giờ →failed, chạy trước cả cờFOUNDRY_AUTO_DRAIN. - C-10 / I-15 / T-11
migrations/README.md:15"số tiếp theo0070" lệch 126; không script cấp số nhưsrc-new.mjs; hai phiên từng lấy trùng 0162. - C-11
DELETE lab_favorites … .catch(() => null)rồi trả 200: bỏ thích thất bại vẫn báo thành công.
3.4 Giao diện & design system (U-4 … U-16)
- U-4
api.tscủa learn/mentors/coral/web ném lỗi kỹ thuật tiếng Anh thẳng ra màn hình trẻ (res.json()không bọc;e.messagehiện ởApp.tsx:86,208…); marlinsapi.ts:100-128(humanError) là mẫu đúng. ✅ đợt 8:humanError+ bọcfetch/res.json()ở learn, coral, web, mentors (mentors giữ tiếng Anh theo quy ước của nó). - U-5 Không app nào xử lý 401 giữa chừng:
mechỉ nạp lúc mount; cookie hết hạn → mọi nút báo "Chưa đăng nhập" trên dashboard, chỉ F5 mới thoát. ✅ đợt 8:api.tsphátnemo12:session-expiredCHỈ khi thật sự mất phiên (401 không body, hoặc AUTHORIZATION_ERROR với đúng hai câu "Chưa đăng nhập"/"Phiên đã hết hạn…"), App của learn/marlins/mentors/coral/admin lắng nghe →setMe(null)→ màn đăng nhập; không đụng nhánh "thiếu vai" sẵn có của coral/admin. - U-6 Không
ErrorBoundaryở app nào: một exception khi render = màn trắng câm. ✅ đợt 8:components/ErrorBoundary.tsx+ bọc<App/>trongmain.tsxở 6 app (nút "Tải lại", log component stack). - U-8 Modal tự chế (
div fixed inset-0 onClick) ở AvatarMenu 5 app, ProgressViews, MasteryMap, DataQuality; shadcndialog.tsxcó ở 5 app nhưng 0 import: không focus trap, không Escape, khôngaria-modal. - U-9 Chữ dưới sàn 14 px (AS-09.2.5): learn 159
text-xs,text-[10px]/[11px]; buttonsize="xs"24 px. - U-10 / I-22 Cổng em dash chỉ quét learn + marlins; web
CorePages.tsx:20-39, coral ~24 dòng, admin ~15, mentors ~8 đang hiển thị em dash. ✅ đợt 6:ROOTS= 8 app, đỏ khi quét 0 file; dọn 203 chỗ ở 25 file (web/coral/admin/mentors); sửa luôn bộ lọc regex quá rộng từng nuốt cả chữ JSX giữa hai dấu/(hai vi phạm thật bị giấu). - U-11
useEffectgọi API không có cờ huỷ ở gần như mọi màn chính (Learning 16, App 13, ProgressViews 10, FamilyWorkspace 10): đổi con nhanh → dữ liệu con A đè màn con B. - U-12 25 chỗ
.catch(() => {})+thenkhôngcatch→ skeleton đứng mãi khi API hỏng. - U-13 Hex cứng trong JSX ngoài vùng art:
marlins/Metrics.tsx:47-49,118-123,Courses.tsx:237,277,learn/CoursesPanel.tsx:149,Learning.tsx:1623-1715,b21/App.tsx:103. - U-14 Admin (SRC-098 đòi tiếng Anh) còn ~77 dòng tiếng Việt +
window.confirmlẫn hai thứ tiếng. - U-15
scripts/gen-sitemap.mjsở gốc là bản cũ sinh 20 URL đã thành alias; bản thật ởapps/web/scripts/. ✅ đợt 5: xoá bản gốc. - U-16 Learn khởi động bằng chữ trần "Đang tải…";
Ebooks.tsx:336-344loadChapterkhôngcatch.
3.5 Vận hành, CI, công cụ (I-5 … I-15)
- I-5 Các cổng kiểm code (deps, design-stack, shadcn-tokens/adoption, english-identifiers, no-emdash, dictation, labs-ib) treo dưới
check:docs, mà CI và hook chỉ chạycheck:docskhi đụngdocs/. Commit thêmframer-motion: latest+ code: mọi cổng xanh, deploy. Đây là gốc của U-1. - I-6
wrangler deploykhông retry; 504 của Cloudflare (run 33777956153) làm job đỏ sau khi mọi cổng xanh, các app đứng sau bịskipped. - I-7
scope.shbỏ sót 4 phụ thuộc build:package-lock.json(đổi lockfile không test, không deploy);$designchoapp_docs;content/dictationcho admin;gen-pearl-from-docs.mjscho pearl. - I-8 Concurrency group ci / deploy-* / rollback tách nhau → hai
wrangler deploycùng worker chồng nhau, last write wins. - I-9 Không alerting/uptime nào;
/v1/healthvàmcp /healthsống nhưng không ai canh; DLQ đầy lúc 02:00 không ai biết. - I-10
wait-for-ci.shfallback chỉ typecheck/lint/test, thiếu contract:diff / migrate:dryrun / verify:bindings màdeploy-api.ymlkhai; timeout 25 ≠ 30. ✅ đợt 7: fallback chạy đủ bộ (+check:code), timeout 30 phút. - I-11 Hook pre-commit không chạy
gen:referencekhi đụngmigrations/haymodules/→ 3/10 run đỏ gần nhất là loại này. - I-12
nemo12-apikhôngupload_source_maps, khôngworkers_dev: false(foundry/tool-plane có). - I-13
scripts/d1.mjs+ 5 script--remote --yesghi thẳng production từ máy cá nhân, không cờ--prod, trái luật "chỉ qua GitHub Actions". 🟡 đợt 5:d1.mjschặn mọi câu ghi trừ khiNEMO12_ALLOW_REMOTE_WRITE=1, chỉ tớiseed-data.yml; 5 script gọiwrangler d1 execute --remote --yestrực tiếp còn chờ. - I-14
docs/opsthiếu runbook cho đúng các sự cố đã gặp: rollback, migration-trước-code, Access outage, replay DLQ. ✅ đợt 7 ba runbook:ops/rollback.md,ops/migration-order.md,ops/dlq-replay.md(Access outage còn chờ: cần xác nhận trên dashboard, token không có scope Access).
3.6 Test & lệch tài liệu (T-6 … T-16)
- T-6 Sáu module 0 test:
exams(submit → evidence → mastery),portraits,coral(21 route ghi),forum,whale,orca;auth/routes.tskhông có test hành vi cho hai lỗ CWE-613/CWE-287 đã vá. - T-7
retention-model.mdnhãn tiếng Việt cũ (dang_chac…) trong khi API trảsolid/refresh/at_risk/relearn. - T-8 SDD-001 (
last_reviewed03/09) mô tả 6 app, 15 module ảo, 7 package; thực tế 12 app, 29 module, 1 package; không nhắc foundry/tool-plane. ✅ đợt 7: §1/§2/§9 ghi đúng 12 app, 3 worker, 29 module thật, 1 package, luồng CI một job. - T-9
.claude/memory/nemo12-project-state.md(mọi phiên đọc lúc khởi động) có 5 khẳng định sai:data.nemo12.comcòn sống, docs deploy tay, migration tới 0009, test bằng curl, "có cổng grepAI.run". - T-10
workflows.mdnói chỉ có một Cloudflare Workflow thật; hiện có ba (ItemForge,DictationForgekhông được nhắc). - T-12 SDD-017 §4 luật retrieval fail khác code; hệ số
recency = exp(−Δt/4S)không có trong reference. ✅ đợt 4. - T-13 Sau khi gỡ QG-014 (SRC-672), nội dung soạn tay tầng Course không có cổng máy nào trong CI;
audit-course.mjschỉ được gọi từ doc; không cộtprovenance. - T-14
intake.mdcó 9 dòng sai số cột (SRC-122, 404, 432..436, 548, 552) →check-src-canonicalmù 5 SRC. ✅ đợt 5: gộp ô bị|cắt,|x|trị tuyệt đối viếtabs(x), 5 dòng thiếu ô trạng thái được đóng ✅ (đều là việc đã xong 2026-08-21). - T-15 E2E chỉ ở learn, API mock hoàn toàn, không kích khi
workers/apiđổi. - T-16 "
prompts.tslà đường duy nhất chạm model" không có cổng;AI.rungọi trực tiếp ởhttp.ts,context-builder.ts,dictation/tts.ts,dictation/forge.ts. ✅ đợt 5: rule eslintno-restricted-syntaxcấmenv.AI.runtrongworkers/api/srcngoài allowlist 3 file (prompts · tts · forge) có lý do;http.ts/context-builder.tschỉ nhắc trong chú thích, không gọi.
4. Phát hiện ⚪ (36, liệt kê để không thất lạc)
S-11 không security header ở API lẫn app tĩnh, media thiếu nosniff · S-12 ranh giới tin cậy = cả *.nemo12.com (cookie Domain, CORS, CSRF) · S-13 tool-plane tools/list bỏ qua policy, request id hai nguồn · P-13 foundry cron */15 quét toàn bộ lesson lọc ở JS · P-14 digest quét toàn bộ learner_evidence bằng biểu thức ngày · P-15 retention refresh dựng lại reviewContext cho từng learner · P-16 queue consumer no-op nhưng vẫn KV claim/mark mỗi event · P-17 SELECT * bảng rộng, admin không LIMIT · C-12 CREATE TABLE/INDEX thiếu IF NOT EXISTS (0063, 0174) · C-13 cột thêm nhưng không ai đọc (immutable_after_publish, solution_authored_by, prerequisites_note) · C-14 103 export mồ côi (fetchWithTimeout 0 caller, TTS không timeout — ✅ đợt 5 TTS có withTimeout), nowIso/safeJson trùng · C-15 vote diễn đàn đọc-rồi-ghi · C-16 withLock KV không nguyên tử · C-17 232 any tập trung 6 file · C-18 22 event chưa consumer nghiệp vụ · U-17 6 gói @radix-ui/react-* thừa, @types/three ở dependencies · U-18 div onClick không role/tabIndex · U-19 analytics opt-out mặc định, cần quyết định trong docs privacy · U-20 1,9 MB ảnh cover social không ai tham chiếu · U-21 exhaustive-deps chỉ warn · U-22 8 vite.config + 8 tsconfig sao chép · I-16 13 workflow không timeout-minutes · I-17 action ghim major tag, rollback dùng wrangler@4 trôi · I-18 nội suy ${{ steps.scope.outputs.test_ws }} vào run: · I-19 seed-data.yml thiếu permissions/concurrency/timeout · I-20 apps/web lệch dải version, motion 13.1.1 → 13.2.0, không Dependabot · I-21 (✅ đợt 5) eslint không phủ apps/web/scripts, design-system/scripts · I-22 3 cổng PASS rỗng khi glob trống · I-23 /v1/health luôn 200 kể cả database: error · I-24 main không branch protection (GitHub free) · T-17 ~100 quyết định ✍️ chờ chủ dự án, mục "AI Gateway còn chờ" lỗi thời · T-18 authCoverage lớp 2 là source-regex, chưa ghi ngoại lệ QG-005 · T-19 migration-reconciliation.md "57 migration" · T-20 top-10 doc đứng yên khi code đổi (schedules 33 commit, workflows 38, sdd-022 30…) · T-21 check-coverage/bounds.test PASS rỗng · T-22 SRC-674 ✅ trước khi có canonical (đóng bằng chính file này) · T-23 CI test theo workspace, apps không chạy lại khi api đổi.
5. Những gì hệ đang làm TỐT (giữ làm khuôn)
- Xác thực: Google id_token và Access JWT ghim issuer/audience/alg và có test ký khoá thật; phiên 256-bit, DB chỉ giữ SHA-256, cookie
HttpOnly; Secure; SameSite=Lax, xoay 7 ngày, logout revoke đúng bản (auth/service.ts,verify.test.ts,access.test.ts). - Lưới xác thực lớp 1 hỏi bảng route lúc chạy, danh sách PUBLIC đóng có lý do, có sàn chống xanh giả (
authCoverage.test.ts:31-119). - SQL bind toàn bộ, không secret trong repo hay lịch sử git; input người gõ trong workflow đi qua
env;npm audit --omit=dev= 0. - Một đường duy nhất chạm model qua AI Gateway có timeout và metadata (
shared/prompts.ts,foundry/models.ts); foundry có 5 chốt + trần USD/ngày; chia lô IN-list ≤100 đã thành tiền lệ. - Testkit dựng D1 thật từ chính
migrations/(lệch tên cột là đỏ, không phải mock); digest chống gửi trùng ở tầng dữ liệu (PK +meta.changes); queue consumer idempotent đúng thứ tự. - CI một job, mọi cổng đỏ thật, mốc deploy cộng dồn,
migrate:dryruntừ D1 rỗng, số migration liên tục,gen:referencediff,contract:diff;wrangler.jsonckỷ luật (observability 15/15,compatibility_dateđồng nhất, DLQ khai báo). - Frontend: một nguồn route SEO cho web (
routes.ts→ prerender → sitemap → aliases); ebook/KaTeX/three tách chunk đúng chỗ nặng; nền a11y (focus-visible,min-h-11,alt,lang); cổng ratchet shadcn-adoption/tokens/design-stack, 0 import cấm. - Docs reference đúng hằng số đang chạy (
engines.md,retention-model.md); sổ intake 674 SRC liên tục không trùng; 0.skip/.only, 0 TODO trong 3 worker.
6. Thứ tự sửa (tự quyết theo SRC-674) và trạng thái
Nguyên tắc xếp: (1) dữ liệu trẻ em đang hở → (2) tiền đang chảy → (3) cổng đang nói dối (vì cổng dối thì mọi sửa sau đó không được bảo vệ) → (4) lỗi im lặng với learner → (5) docs dẫn người sau đi sai → (6) phần còn lại theo chi phí/lợi ích.
| Đợt | Việc | Đóng | Trạng thái |
|---|---|---|---|
| 1 | Chốt quyền curriculum + dictation, mở rộng lưới authCoverage, bảng xếp hạng bỏ learner_id, 2 file test authz | S-1 S-2 S-3 | ✅ 5c5883b, merge 7a97af1 |
| 1 | Dọn 408 em dash 8 chương ebook | U-1 | ✅ ef0e9b0; phiên viết ebook (SRC-673) sau đó tự dọn toàn bộ 13 quyển từ nguồn JSON (1e12e91) và thêm luật vào thước soạn |
| 1 | BCC digest chỉ từ cấu hình + khai minh bạch; forum/events không lộ email; context-events chỉ owner/guardian; scope.sh --event push; rollback.yml đúng tên worker | S-5 S-6 S-7 I-2 I-3 | ✅ 5fcae84 |
| 2 | Tách check:code khỏi check:docs, CI + hook chạy khi có code | I-5 | ✅ đợt 2 |
| 2 | Mở cổng em dash ra mọi apps/*/src (dọn web/coral/admin/mentors trước) | U-10 I-22 | ✅ đợt 6 |
| 2 | Áp migration trước "Deploy foundry/api/tool-plane" trong ci.yml (apply + xác nhận "No migrations to apply"); sửa README, migrations/README.md, skill d1-migrate, memory | I-1 T-2 T-11 T-9 | ✅ đợt 2 |
| 2 | schedules.md đủ 5 cron + luật đối chiếu triggers.crons; workflows.md thêm 2 workflow | T-3 T-10 | ✅ đợt 2 |
| 3 | Trần USD/ngày cho cron AI + ghi usage; loại target đã rejected ở 9 chặng authoring; b21 translate vào ngân sách chung + ghi sổ mẩu kẹt | P-1 P-2 P-3 | ✅ đợt 3 |
| 3 | Soi trùng event_id trước khi ghi + status='in_progress' ở practice answer/lab | C-1 | ✅ đợt 3 |
| 3 | Marlins AvatarMenu + web Login sang tiếng Việt, icon lucide | U-3 | ✅ đợt 3 |
| 4 | Testkit dựng schema một lần mỗi worker + hookTimeout tường minh | T-5 | ✅ đợt 7: VACUUM INTO file khuôn một lần mỗi worker, mỗi test copy file (~10 ms); 34 file test D1 chạy 13 s thay vì ~300 s |
| 4 | wrangler deploy retry 3 lần (.github/scripts/deploy.sh); scope.sh thêm lockfile/$design cho docs/content/dictation cho admin/gen-pearl cho pearl; một group production cho ci-main, 12 deploy-*, rollback, seed-data; timeout-minutes cho 13 workflow; seed-data có permissions | I-6 I-7 I-8 I-16 I-19 | ✅ đợt 4 |
| 4 | /v1/health trả 503 + status: degraded khi D1 lỗi; api bật upload_source_maps; bỏ thích lab không nuốt lỗi D1; retention-model.md nhãn tiếng Anh | I-23 I-12 C-11 T-7 | ✅ đợt 4 |
| 4 | SDD-002 §7/§8/§18 và SDD-017 §4 ghi đúng v1 đang chạy; retention-model.md §3.7 recency; PRD-001 REQ-EXAM-05..08 về ⏳ | T-1 T-12 T-4 | ✅ đợt 4 |
| 5 | Palette canonical đồng bộ, export TSX từ design-system, cổng md5 bản sao | U-2 | ⏳ |
| 5 | Guard status ở invitation accept; xoay phiên không đua + ân hạn; lịch thi học kỳ idempotent; foundry quét run treo | C-3 C-2 C-8 C-9 | ✅ đợt 6 |
| 5 | shared/time.ts (vnDate, daysUntilVn) cho 6 chỗ tính "ngày": Orca hạn đăng ký, days_to_exam, Goal daysUntil, Constraint days_away, Context daysLeft, khoá ngày của lab evidence; digest dùng chung | C-5 | ✅ đợt 7 (còn coral/routes.ts "ngày kể từ lần rà", không ảnh hưởng learner) |
| 5 | Chỉ mục assessment_responses | P-4 | ✅ gộp vào 0196 của phiên khác, chờ push |
| Tay | Diễn tập rollback trên nemo12-compass; chạy lại drill backup/restore + đối soát migration; bật Cloudflare notification + uptime; phát hành lại token tool-plane trói user | I-3 I-4 I-9 S-4 | 📋 chờ chủ dự án |
6b. Kết phiên 2026-09-04 — đã sửa gì, còn gì
Phiên chạy từ 2026-09-03 23:5x tới 2026-09-04 ~02:00 giờ VN trên nhánh session/audit-2026-09-03, merge về main 11 lần (7a97af1 · c71de42 · fb6e79d · 6b06659 · 931b82c · 9edd232 · b4fe614 · e1655d5 · 93dc698 · 3428b2f · 94e43f1). Ba lần main đỏ trong phiên, đều do chính phiên này gây ra và tự sửa trong vòng một lượt: intake.md không bump last_reviewed (→ src-new.mjs nay tự bump), emails.md mồ côi (của phiên khác, nối vào mục lục), và bước quét run treo của foundry đứng trước cờ AUTO_DRAIN làm một test bất biến đỏ (→ đứng sau cờ).
Đã đóng bằng code hoặc docs trong phiên: 🔴 S-1 S-2 S-3 P-1 P-2 P-3 C-1 U-1 U-3 I-1 I-2 I-3 T-1 T-2 T-3 T-4 T-5 (15/19 dòng của §2; còn U-2 và I-4 chờ) · 🟡 S-5 S-6 S-7 S-9 S-10 C-2 C-3 C-5 C-8 C-9 C-11 P-5 P-7 P-11 U-4 U-5 U-6 U-10 I-5 I-6 I-7 I-8 I-10 I-12 I-14 T-7 T-8 T-9 T-10 T-11 T-12 T-14 (một phần: S-4 S-8 P-4 P-8 I-13) · ⚪ C-14 (TTS timeout) I-16 I-19 I-21 I-22 I-23 T-16 U-15 T-22.
Còn mở, xếp theo giá trị:
| Việc | Đóng | Vì sao chưa làm trong phiên |
|---|---|---|
Diễn tập rollback.yml trên nemo12-compass; chạy lại backup/restore drill + đối soát d1_migrations; bật Cloudflare Notifications + uptime cho /v1/health | I-3 I-4 I-9 | Việc tay chạm production/dashboard, cần chủ dự án |
Phát hành lại 3 token tool-plane trói user, bỏ nhánh token cũ; rate limit /mcp | S-4 | Token nằm ở vault ngoài repo |
Cron thi hành data_deletion_requests + export dữ liệu + retention theo keep_days | S-8 | Cần quyết phạm vi xoá (44 bảng có learner_id) với chủ dự án |
packages/design-system export TSX, gỡ 12 bộ bản sao component, cổng md5 | U-2 | Đụng 8 app cùng lúc; nên làm ở phiên riêng có worktree |
React.lazy theo route + manualChunks cho learn/marlins/web | U-7/P-10 | Cần đo bundle trước/sau, không làm vội |
Cockpit: cache node môn, gộp FOUNDATION_SQL, index items(subject_id, role) | P-6 | Cần migration (chờ 0196 lên) và đo độ trễ |
rows_read proxy cho env.DB + wrangler d1 insights hằng ngày | P-9 | Thiết kế log cần bàn ngưỡng |
Chỉ mục learner_evidence(occurred_at) cho digest; assessment_responses đã vào 0196 | P-14 P-4 | Chờ 0196 của phiên khác lên main rồi đánh 0197 |
z.any() → schema thật cho route learner/parent đọc; lint cấm thêm z.any() | C-7 | Việc dài, từng module một |
Test cho exams submit, auth logout, portraits familyRole, privacy deletion | T-6 | Mỗi cái 3-5 case, có testkit nhanh rồi |
Mức AI_DAILY_USD_CAP (đang 2 USD/ngày) và có giữ BCC digest về Gmail cá nhân không | P-1 S-5 | Quyết định của chủ dự án |
SDD-002: hiện thực source_diversity_factor cho confidence | T-1 (phần code) | Đổi thuật toán learner-facing, cần SRC riêng |
7. Trace
- Nguồn: SRC-674 (chỉ đạo 2026-09-03).
- Đáp ứng: REQ-DOC-05. Chỉ báo AS liên quan ghi từng dòng ở §2, §3.
- Nhánh làm việc:
session/audit-2026-09-03, merge vềmaintheo nhịp 30 phút; sổ merge ở cuối phiên được ghi vào index.