Skip to content

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ướcCách làmKết quả
Chốt phạm viorigin/main @ df9e373 (2026-09-03), cây riêng session/audit-2026-09-0312 app · 3 worker · 29 module API · 195 migration · 118 file test · 468 file docs
Máy chạy trướctypecheck · lint · test · check:docs · audit:bounds · contract:diff · verify:bindingstypecheck ✓ · 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 agent6 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 được13 + 17 + 18 + 22 + 24 + 23 = 117 phát hiện, sau khử trùng còn 106: 🔴 15 · 🟡 55 · ⚪ 36
Đối chiếu chéoCù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ất11 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ềuPhát hiệnBằng chứngChỉ báoTrạng thái
S-1Bảo mậtIDOR 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 learnercurriculum/routes.ts:56-186, service.ts:251-333AS-07.2.1 🔴, AS-07.2.3✅ sửa 5c5883b + test
S-2Bảo mậtIDOR 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àidictation/routes.ts:198-312, practice.ts:67-106AS-07.2.1 🔴, AS-10.5.5✅ sửa 5c5883b + test
S-3Bảo mậtLướ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-2authCoverage.test.ts:168, practice.ts:242-270AS-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-1Chi 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àyshared/aiBudget.ts:34, index.ts:302-335, prompts.ts:1424-1440AS-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-2Chi 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ángcurriculum/authoring.ts:133-137,185-190,348,472-474,510-513,541-548,615,672,867AS-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-3Chi 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-197AS-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 đắnMastery 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ònglearning/routes.ts:684-686,733-786,878-890AS-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-1Giao diệnCổ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-150AS-09.4.1, AS-08.4.1✅ sửa ef0e9b0 (8 file); 4 chương mới lên sau đó đang dọn
U-2Giao diệnBả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 TSXmd5 12 bộ file; packages/design-system/package.jsonAS-09.2.2⏳
U-3Giao diệnMenu 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àymarlins/AvatarMenu.tsx:35-42, web/SiteNav.tsx:97,110AS-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-1Vận hànhCode 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útci.yml:268-274, deploy-api.yml:43-51, scope.sh:114AS-04.6.4 🔴, AS-03.5.3⏳
I-2Vận hànhscope.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 xanhscope.sh:34AS-08.4.1✅ sửa 5fcae84
I-3Vận hànhrollback.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ạirollback.yml:27,80-81,125AS-08.4.3 🔴✅ sửa 5fcae84 (chưa diễn tập)
I-4Vận hànhDiễ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→0176docs/ops/backup-restore-drill.md:19-21, migration-reconciliation.md:8AS-04.5.5 🔴, AS-04.6.1 🔴⏳ việc tay
T-1DocsSDD-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ẫnsdd-002:82-96,214-218 ↔ knowledge/mastery.ts:42-68AS-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-2DocsREADME 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ó 195README.md:45-47, migrations/README.md:15-22, đoạn cuốiAS-01.4.1 🔴⏳
T-3Docsreference/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 docschedules.md:18-22,97 ↔ workers/api/wrangler.jsonc:14AS-01.4.1 🔴, AS-08.3.1⏳
T-4DocsPRD-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 testprd-001:641-644, sdd-012:6AS-01.3.2, AS-02.4.5✅ đợt 4: bốn REQ về ⏳ kèm lý do + ngày ở PRD §7
T-5TestTestkit 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 testrunlog.testkit.ts:73-84, 21 file gọi trong beforeEachAS-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 header X-Nemo12-User-Id không kiểm chứng; grant learning-agent scope user nê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ũ; /mcp khô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ằng issue-agent-token.mjs --user rồ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_name rơ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òng family_members (kể cả role learner, supporter) ghi được lời khai với source_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_days khô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: cron runDeletionRequests đ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/crawl khai nhưng không dùng; answer, lab evidence, dictation start/check không rate limit. shared/ratelimit.ts:31-40. ✅ đợt 4: submit gắn vào practice answer, lab evidence, dictation check; rule mới dictationCreate 20 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ỗ trong observability.ts còn chờ.

3.2 Chi phí & hiệu năng (P-4 … P-12) ​

  • P-4 assessment_responses không có chỉ mục theo learner_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à items thiếu chỉ mục dẫn đầu node_id, mục 1-3 của 0196). ✅ Hai chỉ mục đã được gộp vào 0196_hot_path_indexes (mục 4, (learner_id, created_at DESC, item_id) để câu SELECT 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_json hoặ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_nodes của môn 5 lần, COUNT items theo role không chỉ mục, FOUNDATION_SQL chạy 3 lần. knowledge/routes.ts:597-813.
  • P-7 WF-04 (8 engine, ~40 SELECT + ~35 ghi tuần tự) được await trong 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: public trên custom domain không được CDN cache nếu không dùng Cache API. 🟡 đợt 7: withCache KV 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: proxy env.DB cộng dồn meta.rows_read theo request/cron tick, logEvent d1_heavy khi >50k dòng; script wrangler d1 insights hằng ngày; billing notification.
  • P-10 / U-7 Bundle: learn index 1.052 kB (explore/configs 249 kB import tĩnh), marlins 867 kB (parentCourses 207 kB ở trang chủ), web 606 kB; React.lazy cả repo = 1; không manualChunks.
  • P-11 Mọi request có phiên = 1 UPDATE sessions.last_seen_at tuầ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ằng UPDATE … 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_id bị 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: INSERT assessment_sessions lỗi tạm → INSERT lại không adaptive_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_exam lệch +1 lúc 00:00-07:00; lab hai lượt sáng/chiều thành hai evidence. ✅ đợt 7: shared/time.ts với vnDate/daysUntilVn, 6 chỗ đổi sang ngày lịch VN.
  • C-6 dictation_sets ghi timestamp datetime('now') (có dấu cách) trong khi cả hệ dùng ISO T…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()), 78 as never: đổi tên field response không làm contract:diff đỏ.
  • C-8 Tạo bản ghi không idempotent: semester_exam_schedule khô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_runs treo running vĩ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 */15 quét run running quá 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 theo 0070" 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.ts củ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.message hiện ở App.tsx:86,208…); marlins api.ts:100-128 (humanError) là mẫu đúng. ✅ đợt 8: humanError + bọc fetch/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: me chỉ 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.ts phát nemo12:session-expired CHỈ 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/> trong main.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; shadcn dialog.tsx có ở 5 app nhưng 0 import: không focus trap, không Escape, không aria-modal.
  • U-9 Chữ dưới sàn 14 px (AS-09.2.5): learn 159 text-xs, text-[10px]/[11px]; button size="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 useEffect gọ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(() => {}) + then không catch → 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.confirm lẫ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-344 loadChapter không catch.

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ạy check:docs khi đụng docs/. Commit thêm framer-motion: latest + code: mọi cổng xanh, deploy. Đây là gốc của U-1.
  • I-6 wrangler deploy khô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.sh bỏ sót 4 phụ thuộc build: package-lock.json (đổi lockfile không test, không deploy); $design cho app_docs; content/dictation cho admin; gen-pearl-from-docs.mjs cho pearl.
  • I-8 Concurrency group ci / deploy-* / rollback tách nhau → hai wrangler deploy cùng worker chồng nhau, last write wins.
  • I-9 Không alerting/uptime nào; /v1/health và mcp /health sống nhưng không ai canh; DLQ đầy lúc 02:00 không ai biết.
  • I-10 wait-for-ci.sh fallback chỉ typecheck/lint/test, thiếu contract:diff / migrate:dryrun / verify:bindings mà deploy-api.yml khai; 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:reference khi đụng migrations/ hay modules/ → 3/10 run đỏ gần nhất là loại này.
  • I-12 nemo12-api không upload_source_maps, không workers_dev: false (foundry/tool-plane có).
  • I-13 scripts/d1.mjs + 5 script --remote --yes ghi thẳng production từ máy cá nhân, không cờ --prod, trái luật "chỉ qua GitHub Actions". 🟡 đợt 5: d1.mjs chặn mọi câu ghi trừ khi NEMO12_ALLOW_REMOTE_WRITE=1, chỉ tới seed-data.yml; 5 script gọi wrangler d1 execute --remote --yes trực tiếp còn chờ.
  • I-14 docs/ops thiế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.ts không có test hành vi cho hai lỗ CWE-613/CWE-287 đã vá.
  • T-7 retention-model.md nhãn tiếng Việt cũ (dang_chac…) trong khi API trả solid/refresh/at_risk/relearn.
  • T-8 SDD-001 (last_reviewed 03/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.com còn sống, docs deploy tay, migration tới 0009, test bằng curl, "có cổng grep AI.run".
  • T-10 workflows.md nói chỉ có một Cloudflare Workflow thật; hiện có ba (ItemForge, DictationForge khô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.mjs chỉ được gọi từ doc; không cột provenance.
  • T-14 intake.md có 9 dòng sai số cột (SRC-122, 404, 432..436, 548, 552) → check-src-canonical mù 5 SRC. ✅ đợt 5: gộp ô bị | cắt, |x| trị tuyệt đối viết abs(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.ts là đường duy nhất chạm model" không có cổng; AI.run gọi trực tiếp ở http.ts, context-builder.ts, dictation/tts.ts, dictation/forge.ts. ✅ đợt 5: rule eslint no-restricted-syntax cấm env.AI.run trong workers/api/src ngoài allowlist 3 file (prompts · tts · forge) có lý do; http.ts/context-builder.ts chỉ 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) ​

  1. 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).
  2. 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).
  3. 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.
  4. 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ệ.
  5. 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ự.
  6. CI một job, mọi cổng đỏ thật, mốc deploy cộng dồn, migrate:dryrun từ D1 rỗng, số migration liên tục, gen:reference diff, contract:diff; wrangler.jsonc kỷ luật (observability 15/15, compatibility_date đồng nhất, DLQ khai báo).
  7. 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.
  8. 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.

ĐợtViệcĐóngTrạng thái
1Chốt quyền curriculum + dictation, mở rộng lưới authCoverage, bảng xếp hạng bỏ learner_id, 2 file test authzS-1 S-2 S-3✅ 5c5883b, merge 7a97af1
1Dọn 408 em dash 8 chương ebookU-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
1BCC 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 workerS-5 S-6 S-7 I-2 I-3✅ 5fcae84
2Tách check:code khỏi check:docs, CI + hook chạy khi có codeI-5✅ đợt 2
2Mở 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, memoryI-1 T-2 T-11 T-9✅ đợt 2
2schedules.md đủ 5 cron + luật đối chiếu triggers.crons; workflows.md thêm 2 workflowT-3 T-10✅ đợt 2
3Trầ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ẹtP-1 P-2 P-3✅ đợt 3
3Soi trùng event_id trước khi ghi + status='in_progress' ở practice answer/labC-1✅ đợt 3
3Marlins AvatarMenu + web Login sang tiếng Việt, icon lucideU-3✅ đợt 3
4Testkit dựng schema một lần mỗi worker + hookTimeout tường minhT-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
4wrangler 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ó permissionsI-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 AnhI-23 I-12 C-11 T-7✅ đợt 4
4SDD-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
5Palette canonical đồng bộ, export TSX từ design-system, cổng md5 bản saoU-2⏳
5Guard status ở invitation accept; xoay phiên không đua + ân hạn; lịch thi học kỳ idempotent; foundry quét run treoC-3 C-2 C-8 C-9✅ đợt 6
5shared/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 chungC-5✅ đợt 7 (còn coral/routes.ts "ngày kể từ lần rà", không ảnh hưởng learner)
5Chỉ mục assessment_responsesP-4✅ gộp vào 0196 của phiên khác, chờ push
TayDiễ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 userI-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ĐóngVì 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/healthI-3 I-4 I-9Việ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 /mcpS-4Token nằm ở vault ngoài repo
Cron thi hành data_deletion_requests + export dữ liệu + retention theo keep_daysS-8Cầ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 md5U-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/webU-7/P-10Cầ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-6Cần migration (chờ 0196 lên) và đo độ trễ
rows_read proxy cho env.DB + wrangler d1 insights hằng ngàyP-9Thiết kế log cần bàn ngưỡng
Chỉ mục learner_evidence(occurred_at) cho digest; assessment_responses đã vào 0196P-14 P-4Chờ 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-7Việc dài, từng module một
Test cho exams submit, auth logout, portraits familyRole, privacy deletionT-6Mỗ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ôngP-1 S-5Quyết định của chủ dự án
SDD-002: hiện thực source_diversity_factor cho confidenceT-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ề main theo nhịp 30 phút; sổ merge ở cuối phiên được ghi vào index.