---
url: https://docs.nemo12.com/quality/audits/2026-08-28-001.md
description: >-
  Audit #012 (28.08.2026): chất lượng các workflow sinh nội dung course của
  Content Foundry (LessonForge), phát hiện và đợt sửa.
---

# Audit #012 — Content Foundry (workflow sinh course content)

Audit **chuyên đề** cho xưởng sinh nội dung `workers/foundry` (SRC-632, [SDD-027](../../architecture/sdd-027-content-foundry/index.md)):
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](../audit-standard/index.md)).
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](../../reference/course-design-standard/foundations.md), thang course [A/B/C](../course-audit.md), 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](../quality-gates.md) (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](../content-standards.md), 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_keys` trù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_reason` nên
  output cắt cụt lọt qua như `ok:true` (bắt thêm bằng `usage.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 step `phien ban` đầu run); chính
  `WRITING_RULES` chứ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éo `Promise.all` sập làm mất sổ D1 của mọi
  model đã trả tiền (catch theo từng model).
* **Test (4):** `index.ts` chư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ẫn `unitCtx` brief→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](../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.
