KB-3C42

P10B-2C-R2-FOLLOWUP d28-render-raw.tsv

10 min read Revision 1
p10bd28followupverify-render-diff

canonical_address|depth|section_type|title|body D38-DIEU28-ROOT|0|heading|ĐIỀU 28: LUẬT KỸ THUẬT HIỂN THỊ — v2.0 BAN HÀNH| D38-DIEU28-S0|1|paragraph|Preamble|> 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 Chuẩn" → "LUẬT KỸ THUẬT HIỂN THỊ" Kế thừa: v1.0 (S157). Mở rộng: +Collection PG, +Nuxt whitelist, +Checklist, +Quy trình test, +Chuyển giao, +Coverage scanner. Hội đồng: GPT 8.4/10 + Gemini 9.5/10. 2 vòng review. Đồng thuận ban hành. Rà soát: 13/13 NT — 0 vi phạm (S165-KB rà soát). D38-DIEU28-S1|1|heading|I. TUYÊN BỐ CỐT LÕI| D38-DIEU28-S1-P1|2|paragraph|I. TUYÊN BỐ CỐT LÕI (intro)|> 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ệ.

Mọ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. D38-DIEU28-S1-P2|2|paragraph|Phạm vi luật — 3 lớp rõ ràng|| Lớp | Ví dụ | Thuộc Điều 28? | |-----|-------|---------------| | Display template | DirectusTable, DirectusMatrix, TabPivot, DynamicEntityList | ✅ CÓ — phải đăng ký | | Hạ tầng UI | Layout shell, NavBar, ErrorPage, LoadingSpinner | ❌ KHÔNG — hạ tầng cố định | | PG trigger/function | BirthTrigger, pivot_matrix() | ❌ KHÔNG — backend |


D38-DIEU28-S2|1|heading|II. 5 NGUYÊN TẮC| D38-DIEU28-S2-P1|2|principle|NT-D1: NUXT = MÀN HÌNH, CHỈ RENDER TỪ KHUÔN|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. D38-DIEU28-S2-P2|2|principle|NT-D2: KHUÔN = SSOT, CODE 1 LẦN|1 loại giao diện = 1 khuôn. Instance = config. Sửa khuôn = tất cả instances cập nhật. D38-DIEU28-S2-P3|2|principle|NT-D3: CONFIG TRONG PG, KHÔNG TRONG TEXT|Config trong PG (JSONB) để validate, query, DOT thao tác. Text = documentation. D38-DIEU28-S2-P4|2|principle|NT-D4: KHUÔN LÀ THỰC THỂ QUẢN TRỊ|Đăng ký design_templates, birth record, species SPE-TPL, DOT-health, PASS test. D38-DIEU28-S2-P5|2|checklist|NT-D5: MÁY ĐÚC KHUÔN — CHECKLIST 8 BỘ PHẬN|| # | Bộ phận | Quên thì sao | |---|---------|-------------| | 1 | config_schema (JSONB) | Lỗi âm thầm | | 2 | PG validation (CHECK) | INSERT bậy | | 3 | Error boundary | Trang trắng | | 4 | Loading + Empty state | UI treo | | 5 | DOT-health | Lỗi không ai biết | | 6 | Test 5/5 PASS | Khuôn lỗi lan | | 7 | Documentation | Không dùng được | | 8 | Birth record (auto) | Không biết tồn tại |

Thiếu 1 = KHÔNG active. PG trigger enforce (§III).


D38-DIEU28-S3|1|heading|III. COLLECTION design_templates| D38-DIEU28-S3-P1|2|technical_spec|Schema|| Field | Kiểu | Constraint | Mục đích | |-------|------|-----------|----------| | id | SERIAL | PK | | | code | TEXT | UNIQUE NOT NULL | TPL-xxx | | name | TEXT | NOT NULL | Tên hiển thị | | description | TEXT | nullable | Mô tả + cách dùng | | version | INT | DEFAULT 1 | Version hiện tại | | config_schema | JSONB | NOT NULL | Quy tắc validate config | | component_path | TEXT | nullable | Nuxt component | | instance_collection | TEXT | FK → collection_registry.collection_name | Collection chứa instances — FK enforce | | instance_count | INT | DEFAULT 0 | AUTO cập nhật bởi PG trigger | | checklist_status | JSONB | DEFAULT '{}' | 8 bộ phận — DOT validate đủ 8 key | | test_results | JSONB | DEFAULT '{}' | 5 loại test — DOT verify | | migration_source | TEXT | nullable | Component gốc (chuyển giao) | | status | TEXT | FK → template_statuses.code, DEFAULT 'draft' | Lifecycle — FK enforce | | species_code | TEXT | DEFAULT 'template' | SPE-TPL | | composition_level | TEXT | DEFAULT 'material' | Lớp 4 | D38-DIEU28-S3-P2|2|technical_spec|★ REFERENCE TABLE cho status (NT4+NT10)|```sql CREATE TABLE template_statuses (code TEXT PRIMARY KEY, name TEXT NOT NULL, sort_order INT); INSERT INTO template_statuses VALUES ('draft', 'Nháp', 1), ('testing', 'Đang test', 2), ('active', 'Hoạt động', 3), ('deprecated', 'Lỗi thời', 4), ('retired', 'Ngừng', 5);

ALTER TABLE design_templates ADD CONSTRAINT fk_tpl_status FOREIGN KEY (status) REFERENCES template_statuses(code);


Thêm trạng thái mới = INSERT 1 row. KHÔNG ALTER TABLE.
D38-DIEU28-S3-P3|2|technical_spec|★ PG TRIGGER enforce lifecycle (Tuyên ngôn ②)|```sql
CREATE FUNCTION fn_template_lifecycle_guard() RETURNS TRIGGER AS $$
BEGIN
  -- CẤM nhảy cóc: draft→active (bỏ qua testing)
  IF OLD.status = 'draft' AND NEW.status = 'active' THEN
    RAISE EXCEPTION 'CẤM draft→active. PHẢI qua testing (5/5 PASS).';
  END IF;
  
  -- CẤM active khi checklist chưa đủ 8 bộ phận
  IF NEW.status = 'active' AND (
    SELECT COUNT(*) FROM jsonb_object_keys(NEW.checklist_status)
  ) < 8 THEN
    RAISE EXCEPTION 'CẤM active khi checklist < 8. Hiện: %', 
      (SELECT COUNT(*) FROM jsonb_object_keys(NEW.checklist_status));
  END IF;
  
  RETURN NEW;
END;
$$ LANGUAGE plpgsql;

CREATE TRIGGER trg_template_lifecycle
  BEFORE UPDATE ON design_templates
  FOR EACH ROW WHEN (OLD.status IS DISTINCT FROM NEW.status)
  EXECUTE FUNCTION fn_template_lifecycle_guard();

→ Agent KHÔNG THỂ active khuôn chưa test hoặc checklist thiếu. D38-DIEU28-S3-P4|2|governance_process|Quy tắc versioning|- Không breaking: KHÔNG tăng version. Instance tự hưởng.

  • Breaking: TĂNG version. Backward compatibility nội bộ.
  • CẤM: Sửa breaking mà không tăng version. D38-DIEU28-S3-P5|2|governance_process|Quy tắc instance|- Instance PHẢI có template_code (FK) + template_version (INT).
  • Khuôn upgrade → instances cũ giữ version cũ → render theo version đó.

D38-DIEU28-S4|1|process|IV. QUY TRÌNH TEST KHUÔN — 5/5 PASS = ACTIVE|| Test | Tên | Tiêu chí PASS | |------|-----|--------------| | 1 | Validation Gate | 5/5 config sai bị reject | | 2 | Multi-Instance | 3/3 configs render đúng, Nuxt = PG | | 3 | Resilience | 5/5 edge cases không crash | | 4 | Truth Check | 100% ô Nuxt = PG, 0 sai | | 5 | Live Config | Sửa config → giao diện đổi ngay, không deploy |

Lifecycle: draft → testing (5/5 PASS) → active. PG trigger enforce (§III).


D38-DIEU28-S5|1|technical_spec|V. DOT QUẢN TRỊ — PAIRED (NT12)|| DOT | Cấp | Chức năng | Paired với | Trigger | |-----|-----|----------|-----------|---------| | DOT-template-create | B | Tạo + đăng ký khuôn mới | DOT-template-health | on-demand | | DOT-template-health | A | Kiểm tra checklist, test re-run, coverage | — (self-monitoring) | cron (đọc từ dot_config) + manual | | DOT-template-coverage | A | Quét route/component ngoài registry | — | cron weekly + manual |

Dual-trigger (NT7): Cron (đọc schedule từ dot_config) + on-demand manual.


D38-DIEU28-S6|1|process|VI. QUY TRÌNH TẠO KHUÔN MỚI|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ý (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.


D38-DIEU28-S7|1|process|VII. CHUYỂN GIAO COMPONENT HIỆN CÓ|### 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, backend, dùng 1 lần, chưa ổn định.

Quy trình: Kiểm kê → Đăng ký (draft) → Bổ sung thiếu → Test 5/5 → Activate.

Component Ứng viên
DynamicEntityList/Detail ✅ TPL-DYNAMIC-LIST/DETAIL
TabPivotView ✅ TPL-TAB-PIVOT
CommentModule ✅ TPL-003
WorkflowModule ✅ TPL-004
TaskModule ✅ TPL-005
InspectorDOT 🔄 TPL-006
Layout/NavBar/Error ❌ Hạ tầng
BirthTrigger ❌ Backend

D38-DIEU28-S8|1|heading|VIII. NUXT WHITELIST + COVERAGE SCANNER| D38-DIEU28-S8-P1|2|technical_spec|Whitelist|Nuxt load → đọc template_code → SELECT design_templates WHERE status='active' → CÓ → render / KHÔNG → "Not found". D38-DIEU28-S8-P2|2|technical_spec|Coverage Scanner (DOT-template-coverage)|3 câu: (1) Route nào do template nào render? (2) Route ngoài registry? (3) Component ngoài whitelist? Mục tiêu: 0 ngoài registry = coverage 100%.


D38-DIEU28-S9|1|paragraph|IX. QUAN HỆ VỚI LUẬT KHÁC|| Luật | Quan hệ | |------|---------| | Điều 0-G | Khuôn → birth_registry auto | | NT1 (SSOT) | design_templates = SSOT | | NT4 (Sẵn sàng) | Instance = config, ref table status | | NT10 (PG quản lý) | Config PG, status FK, lifecycle trigger | | NT12 (DOT cặp) | create(B)↔health(A) | | NT13 (Ưu tiên PG) | PG trigger lifecycle guard | | Điều 35 (DOT) | DOT lifecycle, dùng chung dot_config | | Điều 36 (Collection) | instance_collection FK, template_code FK enforce |


D38-DIEU28-S10|1|checklist|X. NỢ KỸ THUẬT|| TD | Nội dung | Khi nào | |----|---------|--------| | TD-A | CI scanner cấm import ngoài whitelist | Sau ban hành | | TD-B | Runtime manifest từ PG | Sau ban hành | | TD-C | Permissions/scope per template | Multi-role | | TD-D | Performance budget | TPL-002 active | | TD-E | Tách _tests, _health tables | >50 khuôn | | TD-F | Semver, compatibility matrix | Khuôn upgrade | D38-DIEU28-S11|1|paragraph|Footer|Đ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 (27 rows)

Back to Knowledge Hub knowledge/dev/laws/dieu38-trien-khai/reports/p10b-2c-r2-followup-d28-2026-04-30/d28-render-raw.tsv