Audit #012 — Content Foundry (workflow sinh course content)
Audit chuyên đề cho xưởng sinh nội dung workers/foundry (SRC-632, SDD-027): Cloudflare Workflow LessonForge (WF-19), sổ model + AI Gateway, rubric máy, schema hợp đồng, và toàn bộ lớp chuẩn/tài liệu bao quanh. Không đặt thang điểm mới (lý do). Câu hỏi dẫn đường của chủ đạo SRC-633: hệ tự sinh, tự chấm, tự nâng cấp — thì cái gì đang giữ chất lượng của chính nó, và giữ ở những đâu?
1. Phương pháp
Rà bằng 11 agent theo 3 vòng: 5 chiều rà độc lập (ngữ nghĩa Cloudflare Workflows · độ lệch rubric↔chuẩn CD · chất lượng prompt · vận hành/governance · chuẩn và tài liệu) → mỗi chiều một agent phản biện mở lại đúng file, xác minh hoặc bác từng finding trên code thật → một agent soát độ đầy đủ tìm vùng chưa ai rà. Kết quả: 38 finding qua vòng phản biện (0 bị bác, 3 được chỉnh chi tiết) + 6 finding từ vòng soát = 44 thô — khử trùng lặp giữa các chiều còn 40 finding: 9 nặng, 17 vừa, 14 nhẹ. Mọi finding đều kèm bằng chứng file:dòng.
Sau khi sửa, chính bản sửa lại bị rà một vòng phản biện thứ hai (6 agent trên diff: đúng đắn code · test có đo đúng lời hứa · docs nhất quán) — ra thêm 12 finding (6 code, 4 test, 2 docs), sửa cả 12 trong phiên; chi tiết ở §4b.
2. Chuẩn so sánh (đọc từ chính chuẩn đang hiệu lực, không tự bịa)
| Chiều | Chuẩn đối chiếu |
|---|---|
| Ngữ nghĩa workflow | Hợp đồng step.do/retry/replay của Cloudflare Workflows; chính lời hứa trong comment của workflow.ts ("chạy tiếp từ chỗ hỏng", "đã trả tiền thì không trả lần hai") |
| Rubric ↔ chuẩn | CD-7/CD-8, thang course A/B/C, bài học 7 vòng soạn tay trong skill /course-design (§5b-5g) |
| Prompt | CD-4/CD-6/CD-9 (những gì model PHẢI biết mới tuân được), khuôn JSON của course-content-to-sql.mjs |
| Vận hành | QG-004 (migrations), QG-010 (AI governance: gateway, prompt registry versioned) |
| Chuẩn & tài liệu | Nguyên tắc "sửa chuẩn: doc trước, code sau" (ghi ngay trong header lessonSpec.ts), sổ Content Standards, sổ workflow WF-xx |
3. Phát hiện nặng nhất (9 🔴)
| # | Phát hiện | Vì sao nặng | Trạng thái |
|---|---|---|---|
| 1 | Retry của Workflows là code chết: callModel nuốt mọi exception và trả {ok:false}, mà Workflows chỉ retry khi callback THROW — cấu hình retries chưa từng chạy | Một cú nghẽn model 10 giây thành thất bại vĩnh viễn của trial, nhiễu thẳng vào bảng so sánh model — đúng cái lý do chọn Workflows bị vô hiệu ở bước đắt nhất | ✅ đã sửa |
| 2 | content_runs kẹt running vĩnh viễn khi workflow chết giữa chừng hoặc LESSON_FORGE.create ném lỗi — chỗ duy nhất đổi status là step cuối | Bảng admin đầy run "đang chạy" giả, không phân biệt nổi với run thật, số liệu so model thiếu hụt im lặng | ✅ đã sửa |
| 3 | Schema cho phép check kind reflection, trái CD-7.4 (câu kiểm cuối bài phải máy chấm được) — zod, rubric, D1 cùng thả | Lesson máy sinh có thể mang toàn câu kiểm không chấm được: điều kiện "vượt lesson = qua câu kiểm" gãy | ✅ đã sửa |
| 4 | Rubric thiếu bất biến đếm chỗ nhiễu (skill §5g: số misconception ≤ mcq×3, ≥1 mcq) — đúng cổng chặn cứng của audit-course.mjs | Draft đạt 100 ở foundry vẫn chết cứng ở cổng hạ nguồn, phí trọn lượt sinh + tiền model; §5g là "nguyên nhân gốc của gần như mọi lỗ phủ" | ✅ đã sửa |
| 5 | Brief không mang key concepts của unit nên model không thể tuân CD-4.4 (concepts ⊆ key concepts) — lỗi thiếu đầu vào, không vòng sửa nào cứu được | Draft trượt đúng bất biến mà quy trình soạn tay kiểm trước commit | ✅ đã sửa |
| 6 | Vòng sửa vứt bỏ toàn bộ Luật viết (system prompt một câu) và chỉ biết tên bài + tên unit | Bản sửa tái phạm giọng SGK / em dash / bịa link — chính hai kiểu hỏng mà prompt vòng sinh dựng lên để chặn | ✅ đã sửa |
| 7 | Không stamp version của prompt/rubric vào R2 lẫn D1 — vi phạm QG-010 mà SDD-027 khai verified_by | Đổi một tiêu chí rubric là điểm tuần này hết so được với tuần trước, và không dấu vết nào cho biết | ✅ đã sửa (0165) |
| 8 | workflows.md khẳng định "chưa dùng Cloudflare Workflows" trong khi LessonForge đang chạy thật; không mã WF nào cấp cho nó | Trang reference để debug đang nói ngược sự thật ở đúng câu cảnh báo in đậm | ✅ đã sửa (WF-19) |
| 9 | Rubric 12 tiêu chí chỉ tồn tại trong code, không doc quality nào khai từng dòng + điểm — trái nguyên tắc "doc trước, code sau" của chính header file | Cái máy sinh lặng lẽ là nguồn chuẩn duy nhất; muốn đổi ngưỡng không có doc nào để "sửa trước" | ✅ đã sửa |
4. Phát hiện vừa và nhẹ (17 🟡 · 14 ⚪ — rút gọn, đủ bằng chứng trong code mới)
🟡 đã sửa trong phiên: PASS 90 trên thang có 44/100 điểm chết do schema bảo đảm sẵn (tái phân bổ còn 13, thêm L13-L16); outcome_seq ma qua được cả zod lẫn rubric lẫn audit B7 (superRefine + L12 chỉ đếm mối nối có thật); first hỏng hẳn vẫn đốt tiền vòng sửa với bản thảo rỗng (bỏ); TrialResult trộn ok của bản đầu với error của bản sửa thành dòng "ok=1 kèm error" (kế toán theo đúng nguồn phán quyết); max_tokens 4096 cắt JSON giữa chừng không tên lỗi (8192 + finish_reason); extractJson regex tham lam vồ từ { đầu tới } cuối (đếm ngoặc cân bằng); brief thiếu Misconception Map + lesson anh em (thêm cả hai); prompt gài bẫy schema (minutes: 0 vi phạm min(1), mẫu outcome_seq luôn 1); luật phủ CD-8 "câu kiểm viết NGƯỢC từ misconception" không có trong prompt (thêm); story.question bị rubric trừ điểm vì luật model chưa từng nghe (thêm vào prompt); gateway không metadata đối soát (thêm run_id/model_key/step); workflow.ts + index.ts 0 test (thêm 41 test — xem §6); thiếu dòng CS cho artifact "bản thảo máy sinh" (CS-11, sửa luôn QG-012 đang khai CS-01..CS-09 lệch từ khi có CS-10); bản thảo đạt không có đường được khai nào ra production (SDD-027 §7); không endpoint liệt kê run (thêm GET /runs + proxy admin); ước token 4 ký tự/token lệch đôi cho tiếng Việt và không phân biệt số đo với số ước (hệ số 2 + cờ usage_estimated, migration 0165).
⚪ đã sửa trong phiên: model chạy tuần tự dù độc lập (song song, wall time MAX thay vì TỔNG); judge() ngoài step làm replay đổi phán quyết khi redeploy (vào step cham:); R2 không lưu bản thô vòng sửa (lưu cả repair_raw/repair_score); rubric không bắt em dash dù prompt cấm (L14); misconception trùng nguyên văn error (L15); vòng sửa cùng nhiệt 0.4 với vòng sinh (0.15); chưa dùng JSON mode dù cả hai provider hỗ trợ (response_format theo cờ json_mode của sổ model); không trần chi phí (FOUNDRY_DAILY_USD_CAP, 429 khi chạm); thiếu upload_source_maps; Trace của course-design-standard không khai CD-8 đã có bản thi hành máy; problems/model_keys trả chuỗi JSON lồng JSON qua HTTP (parse tại biên); audit log foundry.run.start chỉ ghi số byte (thêm target_label + model_keys); vòng sửa chỉ được xem 12000 ký tự đầu của raw (đưa draft đã parse, raw nâng trần 20000).
📋 còn mở (ghi vào SDD-027 §6/§8): vòng judge bằng model thứ hai; nút duyệt trên admin; trừ điểm trùng nội dung giữa các bài (ba mục này vốn khai "chưa làm" từ SDD-027 v0.1, không thuộc 40 finding); đặt R2 lifecycle rule cho prefix foundry/ — việc làm tay trên dashboard, phần chính sách đã khai ở §8; cron quét run running quá hạn khi cả catch lẫn D1 cùng hỏng. Tổng kết: 40/40 finding đã xử lý bằng code hoặc chuẩn trong phiên; 1 việc tay còn chờ (lifecycle rule).
4b. Vòng phản biện thứ hai — rà chính bản sửa
Diff của đợt sửa được 6 agent rà lại (3 chiều × find + skeptic). 12 finding, sửa cả 12:
- Code (6):
model_keystrùng nhau lọt validate làm hai chuỗi step TRÙNG TÊN dùng chung cache và đè nhau trong D1/R2 (khử trùng ở cả hai biên); Workers AI không cófinish_reasonnên output cắt cụt lọt qua nhưok:true(bắt thêm bằngusage.completion_tokens ≥ trần); tiền của lượt gọi HỎNG biến mất khỏi kế toán làm trần chi phí mù đúng kịch bản nó phải chặn (chi phí đi theo message của exception, parse lại ở catch); version stamp đọc hằng số module lúc replay nên redeploy giữa run làm stamp nói dối (đóng băng vào stepphien banđầu run); chínhWRITING_RULESchứa một em dash làm mồi cho lỗi L14, và L14 phạt cả en dash mà luật chưa cấm (sửa cả hai phía); một model chết ở step chấm/lưu kéoPromise.allsập làm mất sổ D1 của mọi model đã trả tiền (catch theo từng model). - Test (4):
index.tschưa có test (thêm 9: trần 429, cap "0" tắt chốt, fail-open, create hỏng → failed + 502, parse tại biên); ống dẫnunitCtxbrief→judge chưa được pin (thêm test concepts ngoài danh sách phải bị nêu tên); lời hứa "vòng sửa mang đủ brief" chưa được pin (assert big idea + problem cụ thể trong message vòng sửa); hàng đợi response của fake env dựa thứ tự gọi ngầm giữa các model chạy song song (đổi sang kịch bản THEO MODEL). - Docs (2): số học finding trong chính báo cáo này không khớp (đã sửa: 44 thô → 40 sau khử trùng); số test khai 20 trong khi thực tế khác (đã sửa thành số cuối ở §6).
5. Cơ chế giữ chất lượng sau đợt sửa — giữ ở NHỮNG ĐÂU
Trả lời thẳng câu hỏi dẫn đường: sau SRC-633, chất lượng của hệ tự-sinh được giữ ở bảy chỗ, mỗi chỗ một việc, không chồng nhau:
| Chỗ | Giữ cái gì |
|---|---|
1 · Prompt (WRITING_RULES + brief đủ ngữ cảnh unit) | luật viết + thông tin mà thiếu nó model không thể đúng; dùng CHUNG cho sinh lẫn sửa |
2 · Schema zod (lessonDraftSchema) | hợp đồng hình dạng: JSON méo, mối nối ma, check kind ngoài CD-7.4 không bao giờ lọt vào phần còn lại |
| 3 · Rubric máy L1-L16 (thang ở course-audit.md) | điều kiện CẦN đo được bằng máy; bất biến đếm chỗ nhiễu chặn TRƯỚC khi tốn tiền sinh lại |
| 4 · Vòng sửa có kỷ luật | chạy khi còn bất kỳ chỗ hỏng nào, nhiệt thấp, chỉ nhận khi thật sự hơn |
5 · Version stamp (prompt_version + rubric_version, migration 0165) | tính so sánh được của mọi con số theo thời gian — QG-010 |
6 · Cổng hạ nguồn (audit-course.mjs + người duyệt, SDD-027 §7) | bản thảo đạt máy vẫn phải qua mắt người và qua audit course trước khi chạm learner |
| 7 · Chuẩn thành văn (CD-8 Trace, CS-11, WF-19, rubric doc-first) | phiên sau sửa chuẩn có đúng MỘT chỗ để sửa trước, máy không lặng lẽ định nghĩa lại chuẩn |
6. Bằng chứng thi hành đợt sửa
workers/foundry: 56/56 test, 4 file (models 17 · rubric 15 · workflow 15 · index 9 — 41 test mới so với 15 ở HEAD), tsc -b sạch cả foundry lẫn api; migration 0165 áp dryrun sạch, chạy lại no-op; code chịu được cảnh code lên trước cột lên sau (INSERT đủ cột có đường lùi về khuôn 0164 — luật deploy của skill d1-migrate); npm run check:docs PASS. Toàn bộ finding-fix map ở §3-§4b; diff cùng commit với báo cáo này.