KB-1F43

Điều 28: Luật Kỹ thuật Hiển thị — v2.0 BAN HÀNH (NT rà soát)

9 min read Revision 1
lawdieu-28displaytemplateenactedv2.0reviewed

ĐIỀU 28: LUẬT KỸ THUẬT HIỂN THỊ — v2.0 BAN HÀNH

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).


I. TUYÊN BỐ CỐT LÕI

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.

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

II. 5 NGUYÊN TẮC

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.

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.

NT-D3: CONFIG TRONG PG, KHÔNG TRONG TEXT

Config trong PG (JSONB) để validate, query, DOT thao tác. Text = documentation.

NT-D4: KHUÔN LÀ THỰC THỂ QUẢN TRỊ

Đăng ký design_templates, birth record, species SPE-TPL, DOT-health, PASS test.

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).


III. COLLECTION design_templates

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

★ REFERENCE TABLE cho status (NT4+NT10)

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.

★ PG TRIGGER enforce lifecycle (Tuyên ngôn ②)

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.

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.

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 đó.

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).


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.


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.

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

VIII. NUXT WHITELIST + COVERAGE SCANNER

Whitelist

Nuxt load → đọc template_code → SELECT design_templates WHERE status='active' → CÓ → render / KHÔNG → "Not found".

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%.


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

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

Đ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