Documentation Conventions
Quy ước cho toàn bộ tài liệu Nemo12. Mọi tài liệu mới (do người hay AI viết) phải tuân theo file này.
1. Một nguồn duy nhất
- Chỉ có một canonical documentation source: thư mục
docs/. - Không tạo bản copy "cho AI" hay "cho human" riêng. Cùng một file phục vụ cả hai.
- Tài liệu đầu vào (bạn gửi thêm) được ghi nhận vào intake.md với ID
SRC-xxx, sau đó nội dung được canonical hóa vào PRD/SDD tương ứng. Bản gốc không lưu lại thành file riêng để tránh hai nguồn sự thật.
2. Loại tài liệu và ID scheme
| Prefix | Loại | Vị trí | Ví dụ |
|---|---|---|---|
SRC-xxx | Source — tài liệu đầu vào được tiếp nhận | ghi trong intake.md | SRC-001 |
REQ-<nhóm>-xx | Requirement — yêu cầu đánh số | định nghĩa trong PRD | REQ-PLT-01 |
PRD-xxx | Product Requirements Document | product/ | PRD-001 |
SDD-xxx | System Design Document | architecture/ | SDD-001 |
QG-xxx | Quality Gate | quality/quality-gates.md | QG-003 |
ADR-xxx | Architecture Decision Record | architecture/decisions/ (khi cần) | ADR-001 |
Nhóm requirement được định nghĩa tại PRD-001 §5 — đó là nguồn sự thật duy nhất về danh sách nhóm. Hiện có (2026-08-14), xếp theo domain:
| Domain | Nhóm REQ |
|---|---|
| Người dùng | ACC (identity/family), LRN (learner), VIS (views), PAR (parent), MEN (mentor), ONB (onboarding), POR (student portrait), UX (UI/UX learner-first) |
| Trường & luyện thi | SCH (schools), EXAM (exam banks), PED (pedagogy), ADAPT (adaptive mode) |
| Trí tuệ học tập | INT (learner intelligence) |
| Nội dung & tri thức | KNW (knowledge & quality), CNT (content plane), LAB (labs), MED (media) |
| Tương tác | INX (interaction — active, SDD-005) |
| Nền tảng | BRD (brand/public web), PLT (platform/tech), NFR (reliability), SEC (security), DOC (docs system) |
| Cộng đồng (đa phần pending) | GAM, COM (forum COM-03 active), GRW, TRU |
(Nhóm CUR trong bản cũ của file này chưa từng có REQ nào — curriculum thuộc KNW; không dùng.)
3. Frontmatter schema (bắt buộc)
Mỗi document bắt đầu bằng YAML frontmatter:
---
id: sdd-002 # ID duy nhất, lowercase
type: prd | sdd | quality | convention | index | adr
title: Learner Intelligence System
description: "Câu tiếng Việt <=160 ký tự: trang trả lời câu hỏi gì" # SRC-1322, bắt buộc
owner: platform # team/người chịu trách nhiệm
status: active | active | deprecated
version: 0.2
last_reviewed: 2026-08-13 # ngày review gần nhất (absolute date)
sources: [SRC-003, SRC-604] # tài liệu đầu vào mà file này canonical hóa
satisfies: [REQ-INT-01, REQ-INT-02] # requirements mà file này thiết kế/đáp ứng
verified_by: [QG-005] # quality gates kiểm chứng
ai_readable: true
---Trường description (SRC-1322) là dòng agent đọc trong /llms.txt để quyết định có mở trang hay không: một câu tiếng Việt, tối đa 160 ký tự, nói trang trả lời câu hỏi gì, đặt trong ngoặc kép. Áp cho MỌI file trong docs/, kể cả curriculum/ (ở đó frontmatter chỉ gồm title và description). Cổng check-docs-agents.mjs thi hành; chuẩn đầy đủ ở Docs cho AI agent.
Trường sources, satisfies, verified_by là xương sống của traceability — tooling và AI đọc từ đây; traceability.md là view tổng hợp cho con người.
4. Lifecycle
draft → active → deprecateddraft: đang viết hoặc chờ tài liệu đầu vào bổ sung; chưa dùng làm căn cứ implement.active: đã review, là căn cứ cho implement.deprecated: giữ lại cho lịch sử, ghi rõ file thay thế.
Khi sửa nội dung đáng kể: tăng version, cập nhật last_reviewed.
5. Quy ước viết
- Tiếng Việt cho phần diễn giải, giữ nguyên thuật ngữ kỹ thuật tiếng Anh (learner model, evidence, workflow…).
- Diagram dùng Mermaid trong code fence (VitePress render được, AI đọc được, Git diff được). ASCII tree dùng cho cấu trúc thư mục.
- User story theo format: As a [Nemo/Marlin/Dolphin/...], I want [goal], so that [benefit] — kèm ID và acceptance criteria.
- Mỗi bảng requirement trong PRD có cột: ID, mô tả, priority (MUST/SHOULD/COULD), nguồn (SRC-xxx).
- Ngày tháng luôn viết tuyệt đối (2026-08-13), không viết "tuần trước".
- Định danh kỹ thuật: 100% tiếng Anh (SRC-604, chỉ đạo chủ dự án 2026-08-26). Tên biến/hàm/ bảng/cột, địa chỉ email hệ thống, khoá cấu hình, tên file, slug/URL mới — tất cả tiếng Anh; cấm tiếng Việt bỏ dấu kiểu
khong-tra-loi@,viec-can-lam. Slug tiếng Việt đã có URL công khai thì khi đổi phải giữ bản cũ làm bí danh — link người dùng đã lưu không được gãy. Chữ người dùng đọc trên UI vẫn tiếng Việt; trao đổi với chủ dự án vẫn tiếng Việt. Vì sao thành luật: địa chỉkhong-tra-loi@nemo12.comtừng lọt tới tay người nhận thư — định danh kỹ thuật là thứ lộ ra ngoài, không chỉ chuyện thẩm mỹ trong repo.
5.2 Link trong docs/ không được trỏ ra ngoài docs/ (SRC-746)
docs.nemo12.com dựng bằng VitePress với srcDir: docs/. Link tương đối được giải nghĩa trong gốc ấy, nên một link [SKILL] trỏ tới ../../.claude/skills/x/SKILL.md là một link chết dù file có thật trên đĩa: VitePress chặn cả bản build, job Deploy docs đỏ, và deploy của mọi phiên đứng lại.
Ngày 16.09.2026 lỗi này xảy ra hai lần trong vài giờ, do hai phiên khác nhau, và cả hai lần npm run check:docs ở máy đều xanh.
Muốn trỏ tới một file ngoài docs/ thì viết đường dẫn trong dấu nháy ngược, đừng làm nó thành link: `apps/pearl/content/completion/`. Người đọc vẫn tìm được file, bản build không gãy.
Cổng: scripts/check-docs-vitepress-links.mjs, nằm trong npm run check:docs. Nó chỉ bắt đích là trang (.md hoặc không đuôi) — link tới một file mã (.ts) được VitePress coi là tài nguyên chứ không phải trang nên không làm gãy build, và một cổng bắt rộng hơn thứ thật sự hỏng là một cổng sẽ bị tắt đi.
5.1 Hai luật về chữ người dùng đọc (SRC-748)
Chỉ đạo chủ dự án 2026-09-16. Phạm vi là mọi chữ người ngoài đọc: trang web, email, và nội dung nạp qua SQL script. Không phải chỉ app.
a. Chỉ dùng ký tự có trên bàn phím. Năm ký tự bị cấm trong chữ người dùng đọc: em dash —, en dash –, nháy kép cong “ ”, nháy đơn cong ' ', ba chấm một ký tự …. Thay lần lượt bằng dấu phẩy/hai chấm, dấu trừ, ", ', .... (SRC-751 và SRC-753, chỉ đạo 2026-09-16: "viết như người đang gõ phím"). Không phím nào gõ ra năm ký tự đó, nên mọi chỗ chúng xuất hiện đều là do trình soạn thảo tự thay hoặc người dán vào. Em dash ngăn hai mệnh đề nên thay bằng dấu phẩy hoặc hai chấm; en dash nối hai vế ngang hàng hoặc một khoảng nên thay bằng dấu trừ: 09:00 - 11:00, Thành ngữ - tục ngữ.
Một cái bẫy đã gặp thật khi dọn: trong mã nguồn, thay “ bằng " trần sẽ ĐÓNG SỚM chuỗi nháy kép của TypeScript và làm hỏng file, nên phải biết đang đứng trong loại chuỗi nào (\" trong chuỗi nháy kép, " trong JSX và backtick). Trong D1, cột options_json là JSON lưu dạng text: thay " trần vào đó làm hỏng cả 55 bản ghi, phải ghi \". Đã parse thử toàn bộ trên bản sao trước khi chạy.
Chú thích trong mã và biểu thức chính quy phải khớp dữ liệu cũ thì được miễn, vì chúng không tới tay ai.
b. Ngày hiển thị luôn DD.MM.YYYY. Dấu chấm chứ không phải gạch chéo: 03/09 và 09/03 là thứ người Việt và người Mỹ đọc ngược nhau, còn dấu chấm thì không ai đọc theo kiểu Mỹ. Dùng dmy() của workers/api/src/shared/time.ts ở phía worker; phía app dùng hàm dmy cục bộ nối bằng ".". Ngoại lệ có chủ đích: toLocaleDateString("en-CA") cho ra YYYY-MM-DD và trong repo này luôn dùng làm khoá so sánh ngày, không phải chữ cho người đọc, nên không đụng tới. Ngày trong tài liệu vẫn viết 2026-09-16 theo mục 5 ở trên: đó là chữ cho người làm, không phải cho người dùng.
Kiểm bằng cây SẠCH, không kiểm bằng cây làm việc. Hai cổng này đọc file trên đĩa, mà cây làm việc chung luôn có sửa dở của phiên khác: một dòng vi phạm còn trên main nhưng đã bị ai đó sửa trong cây thì cổng chạy ở máy KHÔNG thấy, và CI thì thấy. Đã dính thật một lần (SRC-753, templatesIelts.ts:59). Trước khi tin kết quả:
git worktree add --detach /tmp/wt-clean origin/main
cd /tmp/wt-clean && node scripts/check-no-emdash.mjs && node scripts/check-date-format.mjsĐây là biến thể của bẫy "check:docs local nói dối" trong CLAUDE.md, chỉ khác là nó cắn theo chiều ngược lại: ở đó cây làm việc khiến gate xanh giả vì THỪA file, ở đây vì THIẾU nguyên trạng.
Hai cổng thi hành, đều nằm trong npm run check:code:
| Cổng | Quét gì |
|---|---|
scripts/check-no-emdash.mjs | 8 app + workers/api/src/modules/email (không dung thứ) và scripts/*.sql + migrations/*.sql (có trần, chỉ được đi xuống) |
scripts/check-date-format.mjs | Mọi .ts/.tsx trong apps/, workers/, packages/: chặn .reverse().join("/") và toLocaleDateString("vi-VN") |
Dữ liệu đã nạp thì phải backfill. SRC-749 và SRC-751 (2026-09-16) là hai lần chạy đầu tiên theo luật này: 197 câu hỏi + 7 kỹ năng (em dash) rồi 60 câu hỏi + 5 kỹ năng (en dash) đang nằm trong D1 được thay bằng dấu trừ qua scripts/seed-strip-emdash-d1.sql và scripts/seed-strip-endash-d1.sql, có bản sao trước khi sửa ở scripts/backup/. Chọn gạch nối chứ không phải dấu phẩy vì đã đọc mẫu thật trước: em dash ở đó luôn ngăn hai mệnh đề và có dấu cách hai bên, đổi sang phẩy thì câu vốn đã nhiều phẩy thành một dãy phẩy không đọc được.
Vì sao .sql có trần mà app và email thì không: file .sql trong repo là sổ ghi những lần nạp đã chạy. Sửa một chuỗi trong đó không đổi được một chữ nào đang nằm trong D1, nên bắt sửa hết ngay chỉ tạo một đợt commit vô nghĩa và cảm giác sai là "đã sạch". Cái sửa thật nằm ở backfill dữ liệu. Trần chỉ được đi xuống: nạp mới mà mang thêm em dash là gate đỏ.
6. Quy trình tiếp nhận tài liệu mới
Khi có tài liệu đầu vào mới:
- Ghi một dòng vào sổ intake bằng
node scripts/src-new.mjs: cấpSRC-xxx, ngày nhận, tóm tắt. Dòng nằm ở mảnh 100 sốdocs/intake/src-NNNN-MMMM.md(SRC-1322). - Xác định nội dung thuộc PRD nào / SDD nào — cập nhật file hiện có hoặc tạo file mới theo ID scheme.
- Thêm/ cập nhật
REQ-xxxtrong PRD nếu tài liệu chứa yêu cầu mới. - Cập nhật frontmatter (
sources,satisfies) của các file bị ảnh hưởng. - Cập nhật traceability.md — đánh dấu trạng thái SDD và Quality Gate.
7. Render
Docs được render bằng VitePress tại apps/docs/ (deploy: docs.nemo12.com, theo SDD-001). Thanh bên tự sinh theo mảng sản phẩm từ apps/docs/.vitepress/structure.mjs (SRC-1024); index.md là mục lục duy nhất. Đường dẫn file giữ theo loại tài liệu (product/, architecture/…): mảng là lớp xếp, không phải việc dời file (SRC-030, Q-084). File nào buộc phải dời thì thêm dòng chuyển hướng vào docs/public/_redirects; cổng check-docs-structure kiểm cả hai. Tài liệu vẫn đọc tốt dưới dạng plain Markdown trên Git — đây là yêu cầu thiết kế, không phải tình trạng tạm.