Quy định Balo Thông Tin Thực Thể (v1.6 chính thức — cổng atomic + live 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
.sqlnằ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ó trongapproved-sql-manifest.tsvvà sha256 hiện tại khớp tuyệt đối (chống file lạ/sửa lén); (3) guardrail tĩnh chặnDROP/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ộcpsql -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_codeUNIQUE → 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: tablerelkind='r', viewrelkind='v', functionprokind='f', triggerNOT 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)
- 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. - 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. - 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_censusdistinct = đúng 4;khoa_nhan_dienUNIQUE; 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_thevừ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.