KB-1D5E

P10B D28 Candidate Units R2 (27 units)

23 min read Revision 1
p10bdatadieu-28candidate-unitsr2

[ { "canonical_address": "D38-DIEU28-ROOT", "parent": null, "section_type": "heading", "sort_order": 0, "title": "ĐIỀU 28: LUẬT KỸ THUẬT HIỂN THỊ — v2.0 BAN HÀNH", "body": "", "body_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", "word_count": 0, "body_excerpt": "", "source_marker": "# ĐIỀU 28: LUẬT KỸ THUẬT HIỂN THỊ — v2.0 BAN HÀNH", "source_span": { "start_line": 1, "end_line": 1 } }, { "canonical_address": "D38-DIEU28-S0", "parent": "D38-DIEU28-ROOT", "section_type": "paragraph", "sort_order": 1, "title": "Preamble", "body": "> v2.0 BAN HÀNH | S150 (2026-04-01) | Huyên đề xuất + Claude soạn\n> Đổi tên: "Luật Khuôn Mẫu Chuẩn" → "LUẬT KỸ THUẬT HIỂN THỊ"\n> Kế thừa: v1.0 (S157). Mở rộng: +Collection PG, +Nuxt whitelist, +Checklist, +Quy trình test, +Chuyển giao, +Coverage scanner.\n> Hội đồng: GPT 8.4/10 + Gemini 9.5/10. 2 vòng review. Đồng thuận ban hành.\n> Rà soát: 13/13 NT — 0 vi phạm (S165-KB rà soát).", "body_sha256": "87c2850b9bd87854abd5bb9d57576f7fb01cb123fb9bc94888bfa76945e32704", "word_count": 73, "body_excerpt": "> v2.0 BAN HÀNH | S150 (2026-04-01) | Huyên đề xuất + Claude soạn > Đổi tên: "Luật Khuôn Mẫu", "source_marker": "", "source_span": { "start_line": 2, "end_line": 8 } }, { "canonical_address": "D38-DIEU28-S1", "parent": "D38-DIEU28-ROOT", "section_type": "heading", "sort_order": 2, "title": "I. TUYÊN BỐ CỐT LÕI", "body": "", "body_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", "word_count": 0, "body_excerpt": "", "source_marker": "## I. TUYÊN BỐ CỐT LÕI", "source_span": { "start_line": 11, "end_line": 11 } }, { "canonical_address": "D38-DIEU28-S1-P1", "parent": "D38-DIEU28-S1", "section_type": "paragraph", "sort_order": 1, "title": "I. TUYÊN BỐ CỐT LÕI (intro)", "body": "> Nuxt CHỈ render từ khuôn đã đăng ký trong PG. Không có khuôn = không có giao diện. Không có ngoại lệ.\n\nMọi giao diện hiển thị business data trong Incomex = 1 instance của 1 khuôn mẫu chuẩn (template). Khuôn được code 1 LẦN, đăng ký trong PG, kiểm soát bởi DOT. Instance = config data trong PG → khuôn render. Thêm giao diện mới = INSERT config, KHÔNG code.", "body_sha256": "8f5cd61da52341eee7fe49311dc828ffa3ad9a0ce31e501385cfce7f98b92fa4", "word_count": 72, "body_excerpt": "> Nuxt CHỈ render từ khuôn đã đăng ký trong PG. Không có khuôn = không có giao diện. Không có ngoạ", "source_marker": "", "source_span": { "start_line": 12, "end_line": 16 } }, { "canonical_address": "D38-DIEU28-S1-P2", "parent": "D38-DIEU28-S1", "section_type": "paragraph", "sort_order": 2, "title": "Phạm vi luật — 3 lớp rõ ràng", "body": "| Lớp | Ví dụ | Thuộc Điều 28? |\n|-----|-------|---------------|\n| Display template | DirectusTable, DirectusMatrix, TabPivot, DynamicEntityList | ✅ CÓ — phải đăng ký |\n| Hạ tầng UI | Layout shell, NavBar, ErrorPage, LoadingSpinner | ❌ KHÔNG — hạ tầng cố định |\n| PG trigger/function | BirthTrigger, pivot_matrix() | ❌ KHÔNG — backend |\n\n---", "body_sha256": "2b6958a58d8fb7786b5c428f4b11d431ea35ba82cc083decec908ed6eb81acf5", "word_count": 59, "body_excerpt": "| Lớp | Ví dụ | Thuộc Điều 28? | |-----|-------|---------------| | Display template | DirectusTa", "source_marker": "### Phạm vi luật — 3 lớp rõ ràng", "source_span": { "start_line": 18, "end_line": 26 } }, { "canonical_address": "D38-DIEU28-S2", "parent": "D38-DIEU28-ROOT", "section_type": "heading", "sort_order": 3, "title": "II. 5 NGUYÊN TẮC", "body": "", "body_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", "word_count": 0, "body_excerpt": "", "source_marker": "## II. 5 NGUYÊN TẮC", "source_span": { "start_line": 27, "end_line": 27 } }, { "canonical_address": "D38-DIEU28-S2-P1", "parent": "D38-DIEU28-S2", "section_type": "principle", "sort_order": 1, "title": "NT-D1: NUXT = MÀN HÌNH, CHỈ RENDER TỪ KHUÔN", "body": "Nuxt ĐƯỢC: đọc Directus API, render từ khuôn, phát event/submit payload. Nuxt KHÔNG ĐƯỢC: business logic, query DB, tạo component ngoài khuôn, hardcode, điều phối logic submit.", "body_sha256": "6ceb6981a022b8bcac665f6c2262923a61edfde50771a96fbec9ccc413918818", "word_count": 27, "body_excerpt": "Nuxt ĐƯỢC: đọc Directus API, render từ khuôn, phát event/submit payload. Nuxt KHÔNG ĐƯỢC: business l", "source_marker": "### NT-D1: NUXT = MÀN HÌNH, CHỈ RENDER TỪ KHUÔN", "source_span": { "start_line": 30, "end_line": 31 } }, { "canonical_address": "D38-DIEU28-S2-P2", "parent": "D38-DIEU28-S2", "section_type": "principle", "sort_order": 2, "title": "NT-D2: KHUÔN = SSOT, CODE 1 LẦN", "body": "1 loại giao diện = 1 khuôn. Instance = config. Sửa khuôn = tất cả instances cập nhật.", "body_sha256": "87babdafe2412250d51bcd1baae364ed140e99504910eb49fdaa240ebf7b10b1", "word_count": 18, "body_excerpt": "1 loại giao diện = 1 khuôn. Instance = config. Sửa khuôn = tất cả instances cập nhật.", "source_marker": "### NT-D2: KHUÔN = SSOT, CODE 1 LẦN", "source_span": { "start_line": 33, "end_line": 34 } }, { "canonical_address": "D38-DIEU28-S2-P3", "parent": "D38-DIEU28-S2", "section_type": "principle", "sort_order": 3, "title": "NT-D3: CONFIG TRONG PG, KHÔNG TRONG TEXT", "body": "Config trong PG (JSONB) để validate, query, DOT thao tác. Text = documentation.", "body_sha256": "ad33b434f46831785d967710656b2c5fdfa1439119b4fd7eb3288e13d0de1a90", "word_count": 13, "body_excerpt": "Config trong PG (JSONB) để validate, query, DOT thao tác. Text = documentation.", "source_marker": "### NT-D3: CONFIG TRONG PG, KHÔNG TRONG TEXT", "source_span": { "start_line": 36, "end_line": 37 } }, { "canonical_address": "D38-DIEU28-S2-P4", "parent": "D38-DIEU28-S2", "section_type": "principle", "sort_order": 4, "title": "NT-D4: KHUÔN LÀ THỰC THỂ QUẢN TRỊ", "body": "Đăng ký design_templates, birth record, species SPE-TPL, DOT-health, PASS test.", "body_sha256": "e9fcbe21136706252270a4b8d85a367f996fd21b9665cfda28fbe024a872fc47", "word_count": 10, "body_excerpt": "Đăng ký design_templates, birth record, species SPE-TPL, DOT-health, PASS test.", "source_marker": "### NT-D4: KHUÔN LÀ THỰC THỂ QUẢN TRỊ", "source_span": { "start_line": 39, "end_line": 40 } }, { "canonical_address": "D38-DIEU28-S2-P5", "parent": "D38-DIEU28-S2", "section_type": "checklist", "sort_order": 5, "title": "NT-D5: MÁY ĐÚC KHUÔN — CHECKLIST 8 BỘ PHẬN", "body": "| # | Bộ phận | Quên thì sao |\n|---|---------|-------------|\n| 1 | config_schema (JSONB) | Lỗi âm thầm |\n| 2 | PG validation (CHECK) | INSERT bậy |\n| 3 | Error boundary | Trang trắng |\n| 4 | Loading + Empty state | UI treo |\n| 5 | DOT-health | Lỗi không ai biết |\n| 6 | Test 5/5 PASS | Khuôn lỗi lan |\n| 7 | Documentation | Không dùng được |\n| 8 | Birth record (auto) | Không biết tồn tại |\n\nThiếu 1 = KHÔNG active. PG trigger enforce (§III).\n\n---", "body_sha256": "4c28808845a993cd9dd97c160f9576d2a6f8836889abd8831d9fc94876e3883a", "word_count": 103, "body_excerpt": "| # | Bộ phận | Quên thì sao | |---|---------|-------------| | 1 | config_schema (JSONB) | Lỗi âm th", "source_marker": "### NT-D5: MÁY ĐÚC KHUÔN — CHECKLIST 8 BỘ PHẬN", "source_span": { "start_line": 42, "end_line": 57 } }, { "canonical_address": "D38-DIEU28-S3", "parent": "D38-DIEU28-ROOT", "section_type": "heading", "sort_order": 4, "title": "III. COLLECTION design_templates", "body": "", "body_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", "word_count": 0, "body_excerpt": "", "source_marker": "## III. COLLECTION design_templates", "source_span": { "start_line": 58, "end_line": 58 } }, { "canonical_address": "D38-DIEU28-S3-P1", "parent": "D38-DIEU28-S3", "section_type": "technical_spec", "sort_order": 1, "title": "Schema", "body": "| Field | Kiểu | Constraint | Mục đích |\n|-------|------|-----------|----------|\n| id | SERIAL | PK | |\n| code | TEXT | UNIQUE NOT NULL | TPL-xxx |\n| name | TEXT | NOT NULL | Tên hiển thị |\n| description | TEXT | nullable | Mô tả + cách dùng |\n| version | INT | DEFAULT 1 | Version hiện tại |\n| config_schema | JSONB | NOT NULL | Quy tắc validate config |\n| component_path | TEXT | nullable | Nuxt component |\n| instance_collection | TEXT | FK → collection_registry.collection_name | Collection chứa instances — FK enforce |\n| instance_count | INT | DEFAULT 0 | AUTO cập nhật bởi PG trigger |\n| checklist_status | JSONB | DEFAULT '{}' | 8 bộ phận — DOT validate đủ 8 key |\n| test_results | JSONB | DEFAULT '{}' | 5 loại test — DOT verify |\n| migration_source | TEXT | nullable | Component gốc (chuyển giao) |\n| status | TEXT | FK → template_statuses.code, DEFAULT 'draft' | Lifecycle — FK enforce |\n| species_code | TEXT | DEFAULT 'template' | SPE-TPL |\n| composition_level | TEXT | DEFAULT 'material' | Lớp 4 |", "body_sha256": "4537ea23216a940bbe32b420a2b15735252695729f235699a122f8df6c28f7b0", "word_count": 203, "body_excerpt": "| Field | Kiểu | Constraint | Mục đích | |-------|------|-----------|----------| | id | SERIAL | PK ", "source_marker": "### Schema", "source_span": { "start_line": 61, "end_line": 79 } }, { "canonical_address": "D38-DIEU28-S3-P2", "parent": "D38-DIEU28-S3", "section_type": "technical_spec", "sort_order": 2, "title": "★ REFERENCE TABLE cho status (NT4+NT10)", "body": "sql\nCREATE TABLE template_statuses (code TEXT PRIMARY KEY, name TEXT NOT NULL, sort_order INT);\nINSERT INTO template_statuses VALUES\n ('draft', 'Nháp', 1),\n ('testing', 'Đang test', 2),\n ('active', 'Hoạt động', 3),\n ('deprecated', 'Lỗi thời', 4),\n ('retired', 'Ngừng', 5);\n\nALTER TABLE design_templates ADD CONSTRAINT fk_tpl_status\n FOREIGN KEY (status) REFERENCES template_statuses(code);\n\n\nThêm trạng thái mới = INSERT 1 row. KHÔNG ALTER TABLE.", "body_sha256": "7bf7ec1c11ffe6cd4223eeaf9c461fbbf34aa4e3ee0de03dabfe393a809b7858", "word_count": 59, "body_excerpt": "sql CREATE TABLE template_statuses (code TEXT PRIMARY KEY, name TEXT NOT NULL, sort_order INT); I", "source_marker": "### ★ REFERENCE TABLE cho status (NT4+NT10)", "source_span": { "start_line": 81, "end_line": 96 } }, { "canonical_address": "D38-DIEU28-S3-P3", "parent": "D38-DIEU28-S3", "section_type": "technical_spec", "sort_order": 3, "title": "★ PG TRIGGER enforce lifecycle (Tuyên ngôn ②)", "body": "sql\nCREATE FUNCTION fn_template_lifecycle_guard() RETURNS TRIGGER AS $$\nBEGIN\n -- CẤM nhảy cóc: draft→active (bỏ qua testing)\n IF OLD.status = 'draft' AND NEW.status = 'active' THEN\n RAISE EXCEPTION 'CẤM draft→active. PHẢI qua testing (5/5 PASS).';\n END IF;\n \n -- CẤM active khi checklist chưa đủ 8 bộ phận\n IF NEW.status = 'active' AND (\n SELECT COUNT() FROM jsonb_object_keys(NEW.checklist_status)\n ) < 8 THEN\n RAISE EXCEPTION 'CẤM active khi checklist < 8. Hiện: %', \n (SELECT COUNT() FROM jsonb_object_keys(NEW.checklist_status));\n END IF;\n \n RETURN NEW;\nEND;\n$$ LANGUAGE plpgsql;\n\nCREATE TRIGGER trg_template_lifecycle\n BEFORE UPDATE ON design_templates\n FOR EACH ROW WHEN (OLD.status IS DISTINCT FROM NEW.status)\n EXECUTE FUNCTION fn_template_lifecycle_guard();\n\n\n→ Agent KHÔNG THỂ active khuôn chưa test hoặc checklist thiếu.", "body_sha256": "bf6d2cbd10e38902f0143dad903dac15faefe0ed8930e96e8f0371a12212caff", "word_count": 114, "body_excerpt": "sql CREATE FUNCTION fn_template_lifecycle_guard() RETURNS TRIGGER AS $$ BEGIN -- CẤM nhảy cóc: ", "source_marker": "### ★ PG TRIGGER enforce lifecycle (Tuyên ngôn ②)", "source_span": { "start_line": 98, "end_line": 126 } }, { "canonical_address": "D38-DIEU28-S3-P4", "parent": "D38-DIEU28-S3", "section_type": "governance_process", "sort_order": 4, "title": "Quy tắc versioning", "body": "- Không breaking: KHÔNG tăng version. Instance tự hưởng.\n- Breaking: TĂNG version. Backward compatibility nội bộ.\n- CẤM: Sửa breaking mà không tăng version.", "body_sha256": "185030c2c9642e1916e5c037859300eb1e2ae06c66662ab1653e00885cb18d65", "word_count": 25, "body_excerpt": "- Không breaking: KHÔNG tăng version. Instance tự hưởng. - Breaking: TĂNG version. Backward compatib", "source_marker": "### Quy tắc versioning", "source_span": { "start_line": 128, "end_line": 131 } }, { "canonical_address": "D38-DIEU28-S3-P5", "parent": "D38-DIEU28-S3", "section_type": "governance_process", "sort_order": 5, "title": "Quy tắc instance", "body": "- Instance PHẢI có template_code (FK) + template_version (INT).\n- Khuôn upgrade → instances cũ giữ version cũ → render theo version đó.\n\n---", "body_sha256": "494cdc9c62326087bebc4c9097724446cda2bdd1461f871f4454e97dfff25370", "word_count": 24, "body_excerpt": "- Instance PHẢI có template_code (FK) + template_version (INT). - Khuôn upgrade → instances cũ g", "source_marker": "### Quy tắc instance", "source_span": { "start_line": 133, "end_line": 137 } }, { "canonical_address": "D38-DIEU28-S4", "parent": "D38-DIEU28-ROOT", "section_type": "process", "sort_order": 5, "title": "IV. QUY TRÌNH TEST KHUÔN — 5/5 PASS = ACTIVE", "body": "| Test | Tên | Tiêu chí PASS |\n|------|-----|--------------|\n| 1 | Validation Gate | 5/5 config sai bị reject |\n| 2 | Multi-Instance | 3/3 configs render đúng, Nuxt = PG |\n| 3 | Resilience | 5/5 edge cases không crash |\n| 4 | Truth Check | 100% ô Nuxt = PG, 0 sai |\n| 5 | Live Config | Sửa config → giao diện đổi ngay, không deploy |\n\nLifecycle: draft → testing (5/5 PASS) → active. PG trigger enforce (§III).\n\n---", "body_sha256": "609dfd525525789e37afc4cdba8886d023d37370891227a524753ee1410d12ea", "word_count": 89, "body_excerpt": "| Test | Tên | Tiêu chí PASS | |------|-----|--------------| | 1 | Validation Gate | 5/5 config sai ", "source_marker": "## IV. QUY TRÌNH TEST KHUÔN — 5/5 PASS = ACTIVE", "source_span": { "start_line": 139, "end_line": 151 } }, { "canonical_address": "D38-DIEU28-S5", "parent": "D38-DIEU28-ROOT", "section_type": "technical_spec", "sort_order": 6, "title": "V. DOT QUẢN TRỊ — PAIRED (NT12)", "body": "| DOT | Cấp | Chức năng | Paired với | Trigger |\n|-----|-----|----------|-----------|---------|\n| DOT-template-create | B | Tạo + đăng ký khuôn mới | DOT-template-health | on-demand |\n| DOT-template-health | A | Kiểm tra checklist, test re-run, coverage | — (self-monitoring) | cron (đọc từ dot_config) + manual |\n| DOT-template-coverage | A | Quét route/component ngoài registry | — | cron weekly + manual |\n\nDual-trigger (NT7):** Cron (đọc schedule từ dot_config) + on-demand manual.\n\n---", "body_sha256": "821c5b564843bfd860e087180d5db52bd5fe4affe8101bdc26e1ce2a4d8b7e5d", "word_count": 80, "body_excerpt": "| DOT | Cấp | Chức năng | Paired với | Trigger | |-----|-----|----------|-----------|---------| | DO", "source_marker": "## V. DOT QUẢN TRỊ — PAIRED (NT12)", "source_span": { "start_line": 153, "end_line": 163 } }, { "canonical_address": "D38-DIEU28-S6", "parent": "D38-DIEU28-ROOT", "section_type": "process", "sort_order": 7, "title": "VI. QUY TRÌNH TẠO KHUÔN MỚI", "body": "1. Kiểm tra khuôn phù hợp đã có? → CÓ → INSERT config. XONG.\n2. Thiết kế (4 bước HP) → 3. Đăng ký (DOT-template-create, status='draft') → 4. Implement (code 1 lần) → 5. Test (5/5) → 6. UPDATE status='testing' → 7. UPDATE status='active' → PG trigger verify checklist 8/8.\n\n---", "body_sha256": "02758f53e63682fccd5bf09b0f09b8f906d0501b9dc6161fa2d6c5e53c1803fc", "word_count": 51, "body_excerpt": "1. Kiểm tra khuôn phù hợp đã có? → CÓ → INSERT config. XONG. 2. Thiết kế (4 bước HP) → 3. Đăng ký (D", "source_marker": "## VI. QUY TRÌNH TẠO KHUÔN MỚI", "source_span": { "start_line": 165, "end_line": 170 } }, { "canonical_address": "D38-DIEU28-S7", "parent": "D38-DIEU28-ROOT", "section_type": "process", "sort_order": 8, "title": "VII. CHUYỂN GIAO COMPONENT HIỆN CÓ", "body": "### 4 CÓ = đưa vào: Tái sử dụng + Config-driven + Độc lập + Ổn định.\n### KHÔNG đưa vào: Hạ tầng UI, backend, dùng 1 lần, chưa ổn định.\n### Quy trình: Kiểm kê → Đăng ký (draft) → Bổ sung thiếu → Test 5/5 → Activate.\n\n| Component | Ứng viên |\n|-----------|----------|\n| DynamicEntityList/Detail | ✅ TPL-DYNAMIC-LIST/DETAIL |\n| TabPivotView | ✅ TPL-TAB-PIVOT |\n| CommentModule | ✅ TPL-003 |\n| WorkflowModule | ✅ TPL-004 |\n| TaskModule | ✅ TPL-005 |\n| InspectorDOT | 🔄 TPL-006 |\n| Layout/NavBar/Error | ❌ Hạ tầng |\n| BirthTrigger | ❌ Backend |\n\n---", "body_sha256": "d230d07300648f4b2d8f6e4c7daa8ee9a4e3b9b58c11bb2c8a1bc162a229e254", "word_count": 106, "body_excerpt": "### 4 CÓ = đưa vào: Tái sử dụng + Config-driven + Độc lập + Ổn định. ### KHÔNG đưa vào: Hạ tầng UI, ", "source_marker": "## VII. CHUYỂN GIAO COMPONENT HIỆN CÓ", "source_span": { "start_line": 172, "end_line": 189 } }, { "canonical_address": "D38-DIEU28-S8", "parent": "D38-DIEU28-ROOT", "section_type": "heading", "sort_order": 9, "title": "VIII. NUXT WHITELIST + COVERAGE SCANNER", "body": "", "body_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", "word_count": 0, "body_excerpt": "", "source_marker": "## VIII. NUXT WHITELIST + COVERAGE SCANNER", "source_span": { "start_line": 190, "end_line": 190 } }, { "canonical_address": "D38-DIEU28-S8-P1", "parent": "D38-DIEU28-S8", "section_type": "technical_spec", "sort_order": 1, "title": "Whitelist", "body": "Nuxt load → đọc template_code → SELECT design_templates WHERE status='active' → CÓ → render / KHÔNG → "Not found".", "body_sha256": "89e05c127dbced47821f4312be65449ff8d4850dea896e78aee1407f2ecf3b16", "word_count": 19, "body_excerpt": "Nuxt load → đọc template_code → SELECT design_templates WHERE status='active' → CÓ → render / KHÔNG ", "source_marker": "### Whitelist", "source_span": { "start_line": 193, "end_line": 194 } }, { "canonical_address": "D38-DIEU28-S8-P2", "parent": "D38-DIEU28-S8", "section_type": "technical_spec", "sort_order": 2, "title": "Coverage Scanner (DOT-template-coverage)", "body": "3 câu: (1) Route nào do template nào render? (2) Route ngoài registry? (3) Component ngoài whitelist?\nMục tiêu: 0 ngoài registry = coverage 100%.\n\n---", "body_sha256": "bdae72e594acf63b8330558bf99d1d788c6343c96d3b687a11ba60467744ec66", "word_count": 26, "body_excerpt": "3 câu: (1) Route nào do template nào render? (2) Route ngoài registry? (3) Component ngoài whitelist", "source_marker": "### Coverage Scanner (DOT-template-coverage)", "source_span": { "start_line": 196, "end_line": 200 } }, { "canonical_address": "D38-DIEU28-S9", "parent": "D38-DIEU28-ROOT", "section_type": "paragraph", "sort_order": 10, "title": "IX. QUAN HỆ VỚI LUẬT KHÁC", "body": "| Luật | Quan hệ |\n|------|---------|\n| Điều 0-G | Khuôn → birth_registry auto |\n| NT1 (SSOT) | design_templates = SSOT |\n| NT4 (Sẵn sàng) | Instance = config, ref table status |\n| NT10 (PG quản lý) | Config PG, status FK, lifecycle trigger |\n| NT12 (DOT cặp) | create(B)↔health(A) |\n| NT13 (Ưu tiên PG) | PG trigger lifecycle guard |\n| Điều 35 (DOT) | DOT lifecycle, dùng chung dot_config |\n| Điều 36 (Collection) | instance_collection FK, template_code FK enforce |\n\n---", "body_sha256": "b74e7071ca83fac4ca74dff9981ee85cfeac98c83734c9243e8070be8cbd5191", "word_count": 90, "body_excerpt": "| Luật | Quan hệ | |------|---------| | Điều 0-G | Khuôn → birth_registry auto | | NT1 (SSOT) | desi", "source_marker": "## IX. QUAN HỆ VỚI LUẬT KHÁC", "source_span": { "start_line": 202, "end_line": 215 } }, { "canonical_address": "D38-DIEU28-S10", "parent": "D38-DIEU28-ROOT", "section_type": "checklist", "sort_order": 11, "title": "X. NỢ KỸ THUẬT", "body": "| TD | Nội dung | Khi nào |\n|----|---------|--------|\n| TD-A | CI scanner cấm import ngoài whitelist | Sau ban hành |\n| TD-B | Runtime manifest từ PG | Sau ban hành |\n| TD-C | Permissions/scope per template | Multi-role |\n| TD-D | Performance budget | TPL-002 active |\n| TD-E | Tách _tests, _health tables | >50 khuôn |\n| TD-F | Semver, compatibility matrix | Khuôn upgrade |", "body_sha256": "80a41eacdf852780232087511b9cf936a06534d7aff35b2240f5dd72c800b445", "word_count": 75, "body_excerpt": "| TD | Nội dung | Khi nào | |----|---------|--------| | TD-A | CI scanner cấm import ngoài whitelist", "source_marker": "## X. NỢ KỸ THUẬT", "source_span": { "start_line": 217, "end_line": 226 } }, { "canonical_address": "D38-DIEU28-S11", "parent": "D38-DIEU28-ROOT", "section_type": "paragraph", "sort_order": 12, "title": "Footer", "body": "Điều 28 v2.0 BAN HÀNH | 13/13 NT ✅ | 6/6 Q ✅ | Ref table status | PG trigger lifecycle+checklist guard | DOT paired rõ | instance_collection FK | Dùng chung dot_config", "body_sha256": "a8c128769877b7c52cf9c0f914807765f4f2f2b6748d1077bcb3ff696928a5ce", "word_count": 33, "body_excerpt": "*Điều 28 v2.0 BAN HÀNH | 13/13 NT ✅ | 6/6 Q ✅ | Ref table status | PG trigger lifecycle+checklist gu", "source_marker": "", "source_span": { "start_line": 228, "end_line": 229 } } ]