KB-6709 rev 12

Quy định Balo Thông Tin Thực Thể (v1.6 chính thức — cổng atomic + live pg_catalog)

9 min read Revision 12
pg-read-pgbaloquy-dinhcensusv1.6dong-thuanatomic-gatedot-pg-atomic-applylive-pg-catalog

Quy định Balo Thông Tin Thực Thể (v1.8 — CHÍNH THỨC, đã đồng thuận; Pha A PASS)

Census = lớp ĐỌC (read-only). Balo = lớp GHI CHÚ (annotation) TRÊN kết quả đọc — có WRITE. Balo = MỘT bảng, mỗi thực thể MỘT DÒNG (thêm thực thể = thêm dòng, KHÔNG thêm cột). v1.6 (sau lần chạy HOLD đầu tiên): (a) live pg_catalog = nguồn thật cho Core, report-pg chỉ đối chiếu (snapshot cũ lệch → cảnh báo, không HOLD); (b) đường ghi atomic PG-native đi qua cổng dot-pg-atomic-apply (hệ trước đó thiếu đường này — chính là nguyên nhân HOLD lần đầu).


0. Đường ghi atomic PG-native — dot-pg-atomic-apply (cổng dùng chung)

Hệ trước đây không có DOT nào chạy được "một transaction CREATE+INSERT+VERIFY+COMMIT/ROLLBACK" thuần PG (mọi DOT đều đi qua Directus API, không atomic, kèm ghi bảng cấm). Balo Core (và mọi DDL atomic sau) đi qua cổng mới:

  • Input: một file .sql nằm trong allowlist repo (vd /opt/incomex/dot/sql/) — KHÔNG nhận SQL gõ tay lúc chạy.
  • Bốn khóa an toàn: (1) allowlist — file trong /opt/incomex/dot/sql/, realpath, không symlink, đuôi .sql; (2) manifest hash-lock — file phải có trong approved-sql-manifest.tsv và sha256 hiện tại khớp tuyệt đối (chống file lạ/sửa lén); (3) guardrail tĩnh chặn DROP/DELETE/TRUNCATE + transaction control (BEGIN/COMMIT/ROLLBACK/SAVEPOINT) + mọi dòng bắt đầu \ (psql meta-command như \!,\copy) + COPY PROGRAM/ALTER SYSTEM/CREATE EXTENSION/CREATE EVENT TRIGGER + tên bảng cấm; (4) atomic bắt buộc psql -v ON_ERROR_STOP=1 -1 -f — lỗi bất kỳ → rollback toàn bộ.
  • File SQL tự chứa phần VERIFY trong transaction (DO block, lệch → RAISE EXCEPTION → rollback). Không verify-sau-commit.
  • Tái dùng cho reconcile + mọi DDL atomic về sau. KHÔNG phải runner SQL tự do.

1. Hai PHA — không làm chung

  • Pha A — Balo Core (PG thuần, first-run only, ATOMIC qua dot-pg-atomic-apply): tạo bảng + sequence + phát balo rỗng + verify, trong một transaction. KHÔNG UI, KHÔNG reconcile.
  • Pha B — Balo UI (sau): drill-down report, MOUT, Nuxt, deploy.
  • Reconcile (còn/mới/mất, da_mat) = macro riêng, sau Core.

2. Hai loại ngăn

  • IN-SẴN (máy điền, không đoán): STT, Tên, Bảng/chỗ ở, khóa nhận diện.
  • PHẢI-GẬT (máy đoán = rác, để trống v0): Nhóm, Loài, Chuyên môn, Lớp, Ghi chú, Kiểm soát, Active.

Tự cấp ô trống OK; tự điền ngăn phải-gật CẤM.

3. Cấu trúc bảng balo_thuc_the (chốt)

# Cột Kiểu Ràng buộc
1 stt bigint PK DEFAULT nextval('balo_stt_seq'); bất biến, không tái dùng
2 nhom varchar NULL (không CHECK)
3 ten text NULL (in-sẵn)
4 noi_luu text NULL (in-sẵn)
5 loai varchar NULL; tham chiếu mềm entity_species.species_code (người điền tay)
6 chuyen_mon varchar NULL (anchor cây 7 tầng MOW, chưa có → trống)
7 lop varchar NULL; CHECK lop IS NULL OR lop IN ('nguyen_tu','phan_tu','hop_chat','vat_lieu','san_pham','cong_trinh')
8 ghi_chu text NULL
9 kiem_soat boolean NOT NULL DEFAULT false (v0 false hết)
10 active varchar NULL (không CHECK v0)
11 khoa_nhan_dien text NOT NULL UNIQUE — "ID đối tượng" truy ngược + đối chiếu
cờ da_mat boolean NOT NULL DEFAULT false (tạo sẵn, v0 không set)
tự-suy loai_census varchar GENERATED ALWAYS AS (split_part(khoa_nhan_dien,':',1)) STORED — KHÔNG insert tay
audit date_created timestamptz NOT NULL DEFAULT now()
audit date_updated timestamptz NOT NULL DEFAULT now() (không trigger v0)
  • CREATE SEQUENCE balo_stt_seq + ALTER SEQUENCE balo_stt_seq OWNED BY balo_thuc_the.stt. Index: khoa_nhan_dien (unique), loai_census.
  • FK loài: species_code UNIQUE → FK nullable; nếu không → validate mềm. Đừng để FK chặn phát balo.

4. Khóa nhận diện chuẩn theo loại (từ pg_catalog LIVE, KHÔNG information_schema)

Loại Nguồn khoa_nhan_dien
table pg_class relkind='r' + pg_namespace table:schema.relname
view pg_class relkind='v' + pg_namespace view:schema.relname
function pg_proc prokind='f' + pg_namespace function:schema.name(+pg_get_function_identity_arguments(oid)+)
trigger pg_trigger tgisinternal=false + pg_class + pg_namespace trigger:schema.relname.tgname
  • stt = ID hồ sơ (bất biến). khoa_nhan_dien = khóa nhận diện. Identifier có dấu chấm/ký tự bất thường gây mơ hồ → HOLD.
  • Bộ lọc schema CHUẨN (dùng NHẤT QUÁN cho Gate + INSERT + VERIFY + reconcile sau): n.nspname NOT IN ('pg_catalog','information_schema') AND n.nspname NOT LIKE 'pg_toast%'. Cùng một bộ lọc cho cả đếm và phát balo → count nội bộ luôn khớp. (Định nghĩa loại: table relkind='r', view relkind='v', function prokind='f', trigger NOT tgisinternal.)

5. Phạm vi v0 = 4 LOẠI: table, view, function, trigger

  • Bỏ column + 7 loại census còn lại → làm sau, có chọn lọc. Chưa làm ≠ thiếu/sai.

6. Chuyên môn = anchor cây 7 tầng MOW (1 cột, chưa có PG → trống). ## 7. Lớp = 6 lớp tiếng Việt cố định (CHECK), không kế thừa composition_level bẩn. ## 8. Loài = tái dùng entity_species (parent_id+depth gom chi/họ).

9. Kho lưu (Core) — 2 object quản trị chính + phụ thuộc

2 object quản trị chính: balo_thuc_the + balo_stt_seq (MỚI). Được phép có object phụ thuộc do constraint/index đã khai trong SQL duyệt (pkey, unique key trên khoa_nhan_dien, index loai_census) — đây là tất yếu của PG, KHÔNG phải mở scope. entity_species (tái dùng). Đường ghi: cổng dot-pg-atomic-apply. KHÔNG đụng table_registry/MOUT/meta_catalog/birth_registry/qt001_/v_registry_.

10. Cách chạy Core (atomic, first-run only, qua cổng)

  1. Gate khảo sát (read-only): entity_species khớp? cây MOW chưa có? balo/sequence CHƯA tồn tại? identifier không mơ hồ? cổng dot-pg-atomic-apply đã sẵn sàng? Lệch → HOLD.
  2. Một file SQL balo chạy qua cổng: CREATE sequence → CREATE table → ALTER OWNED BY → CREATE INDEX → INSERT 4 loại (từ live pg_catalog, ORDER BY cố định sort_kind 1–4, insert ten/noi_luu/khoa_nhan_dien) → VERIFY (DO block) trước COMMIT.
  3. VERIFY trong transaction:
    • Nguồn thật = live pg_catalog. Đếm 4 loại từ live; nếu query không đọc được hoặc 4 loại tự mâu thuẫn → RAISE (rollback).
    • report-pg chỉ đối chiếu tham khảo: lệch do snapshot cũ → ghi cảnh báo, KHÔNG rollback/HOLD.
    • loai_census distinct = đúng 4; khoa_nhan_dien UNIQUE; scope đúng (chỉ 2 object); không đụng bảng cấm. Lệch → RAISE (rollback).
    • Tất cả PASS → COMMIT.

11. TỰ ĐỘNG HAY KHÔNG — Core dừng ở thời điểm chạy

  • Core = chụp ảnh MỘT lần rồi dừng; đối tượng sinh sau KHÔNG tự đeo thẻ.
  • KHÔNG có máy chạy ngầm. Phát thẻ là lệnh BẤM-LẠI-ĐƯỢC, không phải máy tự chạy.
  • Cái mới bắt qua nhịp đối chiếu (macro sau, người bấm): còn → giữ STT; mới → cấp STT + balo rỗng; mất → da_mat=true (không xé thẻ). Bấm bao nhiêu lần cũng an toàn.
  • Phân loại KHÔNG BAO GIỜ tự động — Loài/Nhóm/Lớp/Chuyên môn luôn người ghi tay.

12. Nguyên tắc giữ

  • DOT 100% cho mọi WRITE; đường ghi atomic = cổng dot-pg-atomic-apply; KHÔNG xây runner SQL tự do, KHÔNG psql direct WRITE.
  • Atomic: xong sạch hoặc rollback. Lỗi sau commit → DỪNG báo người, KHÔNG tự DROP/DELETE/TRUNCATE.
  • Nguồn thật của Core = live pg_catalog; report-pg chỉ đối chiếu (lệch snapshot → cảnh báo, không HOLD).
  • Verdict: PASS / HOLD: <lý do> / FAIL: <lý do>.
  • Census=đọc, Balo=ghi chú. Core trước; UI + reconcile + 8 loại còn lại = pha sau, do người bấm — không tự động.

13. Trạng thái thực thi — PHA A PASS (2026-06-26)

  • Balo Core đã chạy xong (PASS) qua cổng atomic. Commit SQL+manifest f8eb7c2. File: /opt/incomex/dot/sql/balo-core-first-run.sql (sha256 trong manifest).
  • 2146 dòng: table 383 · view 699 · function 654 · trigger 410. Ngăn phải-gật trống 100%; in-sẵn đầy 100%; khóa distinct, UNIQUE pass.
  • Self-inclusion (đúng, KHÔNG phải lệch count): balo_thuc_the vừa tạo đã thành table trong catalog, nên INSERT cùng transaction phát thẻ cho chính nó → table 382→383, tổng 2145→2146. Census là ảnh chụp tại thời điểm tạo, gồm cả bảng balo. Không chạy lại.
  • Nợ nhẹ: refresh report-pg snapshot (đang báo table 381, cũ); commit dot repo local-only (push khi cần backup).
  • Pha sau (người bấm): điền nhãn (loài/nhóm/lớp/kiểm soát) · Pha B UI để xem+sửa balo · nhịp đối chiếu · 8 loại census còn lại.