KB-D96C

GPT Review Round 3 — Điều 24 Luật Nhãn v1.2 (2026-03-15)

7 min read Revision 1

GPT Review Round 3 — Điều 24: Luật Nhãn v1.2

Ngày: 2026-03-15 Nguồn: Incomex Hội đồng AI (GPT) Phạm vi: Final consensus review, KHÔNG sửa luật gốc

Kết luận tổng quát

v1.2 rất sát mức chốt chính thức. Phần lớn các lỗ hổng lớn đã được xử lý đúng. Tuy nhiên tôi vẫn thấy 2 điểm 🔴 còn lại cần chốt trước khi gọi là bản cuối:

  1. Composite FK same-facet hiện còn lỗ hổng NULL ở parent_facet.
  2. DDL trigger per table vẫn chưa an toàn cho các bảng không có đủ name, description.

Ngoài 2 điểm đó, phần còn lại là 🟡 hoặc 🟢.


1. 12 điểm đã sửa — có điểm nào sửa sai hoặc tạo vấn đề mới?

🔴 Cần sửa

(a) Composite FK same-facet chưa khóa chặt vì parent_facet có thể NULL. Schema hiện có:

  • FOREIGN KEY (parent_id, parent_facet) REFERENCES taxonomy(id, facet_id)
  • CHECK (parent_facet = facet_id)

Nhưng trong PostgreSQL:

  • FK không enforce khi một cột tham chiếu là NULL
  • CHECK trả NULL thì vẫn pass

Nghĩa là có thể rơi vào case:

  • parent_id IS NOT NULL
  • parent_facet IS NULL
  • FK không chạy
  • CHECK cũng không fail chắc chắn

=> same-facet bị bypass.

Đề xuất: thêm ràng buộc cứng:

CHECK (
  (parent_id IS NULL AND parent_facet IS NULL)
  OR (parent_id IS NOT NULL AND parent_facet IS NOT NULL)
)

Hoặc gộp luôn vào chk_root_no_parent mở rộng.

(b) Trigger DDL UPDATE OF name, description chưa chắc áp dụng được cho mọi table. Function đã data-driven, tốt. Nhưng câu:

CREATE TRIGGER ... AFTER INSERT OR UPDATE OF name, description ON <table>

chỉ hợp lệ khi table đó thực sự có cả namedescription. Với các bảng thiếu một trong hai cột, DDL có thể fail.

Đề xuất: trigger generator phải tạo per-table đúng cột thực có:

  • bảng có name, descriptionUPDATE OF name, description
  • chỉ có nameUPDATE OF name
  • chỉ có descriptionUPDATE OF description
  • không có cả hai → chỉ AFTER INSERT hoặc bỏ keyword layer cho table đó

🟡 Nên cân nhắc

  • v_all_entity_codes dùng UNION ALL là ổn nếu code thật sự unique toàn hệ thống. Nếu vẫn còn legacy vi phạm uniqueness, DOT validate sẽ nhiễu. Không chặn deploy, nhưng nên verify uniqueness của code ở tất cả nguồn trước.
  • Trigger chống cycle/replaced_by duyệt ≤5 bước là an toàn dư một chút, không vấn đề.

🟢 Đồng ý

  • Chống cycle thật: đúng hướng
  • fn data-driven với to_jsonb(NEW): đúng
  • Soft reference + DOT validate qua v_all_entity_codes: chấp nhận được
  • Scored union top N: đúng
  • Gán leaf only: đúng
  • meta_catalog priority: đúng
  • skip_wide_warning: hợp lý
  • IF NEW<>OLD guard: đúng
  • Tắt Count junction Directus: đúng

2. Composite FK same-facet — có edge case nào với Directus CRUD?

🔴 Cần sửa

Có edge case triển khai: Directus không tự suy ra parent_facet từ parent_id nếu chỉ cấu hình M2O thông thường. Nghĩa là user/admin/DOT có thể chọn parent_id nhưng quên set parent_facet.

Khi đó:

  • nếu chưa thêm ràng buộc NOT-NULL theo cặp, schema có thể lọt dữ liệu hở
  • nếu đã thêm ràng buộc chặt, Directus create/update sẽ fail nếu UI không fill parent_facet

Đề xuất tốt nhất:

  • giữ composite FK
  • parent_facet không cho user nhập tay
  • dùng DB trigger BEFORE INSERT/UPDATE để tự fill parent_facet := facet_id của parent khi parent_id có giá trị

Như vậy:

  • schema vẫn mạnh
  • Directus UX không bị bắt người dùng nhớ field phụ
  • tránh dữ liệu lệch giữa parent_idparent_facet

Nếu không muốn thêm trigger fill, thì Directus flow/hook phải đảm bảo điền, nhưng DB trigger đáng tin hơn.


3. v_all_entity_codes — ALTER VIEW mỗi khi thêm table mới, có ổn không?

🟡 Nên cân nhắc

Chấp nhận được ở v1.2.

Lý do:

  • thêm managed collection là sự kiện hiếm
  • Rule hệ thống đã có CI/CD guard khi thêm collection mới phải cập nhật meta_catalog
  • ALTER VIEW lúc thêm loại mới là chi phí chấp nhận được

Cách dynamic hơn có thể làm bằng function đọc meta_catalog rồi dynamic SQL, nhưng sẽ phức tạp hơn và khó enforce ở DB-level/Directus hơn.

Kết luận: giữ VIEW tĩnh + cập nhật cùng migration mỗi khi thêm collection mới là hợp lý hơn cho v1.2.


4. Scored union "Cùng nhóm" — nên per-facet hay across facets?

🟢 Đồng ý

Giữ per-facet đơn giản ở v1.2 — chỉ theo domain (FAC-01) là đúng.

Lý do:

  • “Cùng nhóm” bản chất gần nhất với cùng chuyên môn/domain
  • trộn thêm action/audience vào score ngay ở v1.2 sẽ làm khái niệm đổi nghĩa từ “cùng nhóm” sang “liên quan tổng hợp”
  • UI sẽ khó giải thích hơn

🟡 Nên cân nhắc

Phase sau có thể thêm:

  • Cùng nhóm = per-facet domain
  • Liên quan mạnh = weighted multi-facet

Nhưng hiện tại nên giữ 1 nghĩa rõ ràng.


5. Cycle detection trigger — ≤5 có cần giảm xuống ≤3 không?

🟢 Đồng ý

≤5 không có vấn đề. Không cần giảm.

Với depth max=2, đúng là cycle thực tế ngắn hơn. Nhưng để 5:

  • không gây hại đáng kể
  • tăng dư địa an toàn nếu có legacy bẩn hoặc migration lỗi

Đây là guardrail kỹ thuật, không phải logic business chính.


6. Tổng thể còn góc khuất nào không?

🔴 Cần sửa

Tôi còn thấy đúng 2 góc khuất chặn deploy — chính là 2 điểm đầu:

  1. lỗ hổng NULL trong composite FK same-facet
  2. trigger DDL chưa an toàn cho bảng thiếu name/description

🟡 Nên cân nhắc

  • entity_labels hiện có index đơn entity_code, label_code. Với query scored union lớn hơn về sau, có thể thêm index ghép (label_code, entity_code) để tối ưu join ngược. Không chặn deploy.
  • v_all_entity_codes nên loại log/non-managed nếu DOT validate chỉ dành cho managed/business scope; nếu không, cần mô tả rõ chủ ý giữ cả taxonomy/meta/system_issues trong VIEW.

🟢 Đồng ý

Ngoài 2 điểm trên, tôi không còn thấy lỗ hổng kiến trúc lớn mới nào.


Phán quyết cuối theo format

🔴 Cần sửa (chặn deploy)

  1. Khóa chặt parent_facet để composite FK same-facet không bị bypass bởi NULL.
  2. Sinh trigger per-table đúng cột thực có; không dùng chung UPDATE OF name, description cho mọi bảng nếu schema không đồng nhất.

🟡 Nên cân nhắc (không chặn deploy)

  1. Giữ v_all_entity_codes là VIEW tĩnh, ALTER VIEW khi thêm collection mới — chấp nhận được.
  2. Giữ Cùng nhóm per-facet domain ở v1.2; multi-facet để phase sau.
  3. Có thể thêm index ghép (label_code, entity_code) sau.
  4. Xác nhận lại uniqueness toàn hệ thống của code ở các nguồn union.

🟢 Đồng ý (không thay đổi)

  1. 10/12 điểm còn lại đều đúng hướng và không tạo vấn đề mới đáng kể.
  2. Cycle trigger ≤5 là ổn.
  3. Scored union top N là lựa chọn đúng.
  4. Soft reference + DOT validate là quyết định thực tế hợp lý.

Kết luận chốt

Chưa đạt đồng thuận cuối cùng. Nếu sửa xong 2 điểm 🔴 trên, tôi sẽ xem v1.2 là đủ sạch để phê duyệt chính thức.