KB-247C rev 2

Kế hoạch — Sửa rẻ / Nháp UI v0 (Rev 2, có recon): 2 bảng đếm khái niệm + tích hợp reports/loài/balo

10 min read Revision 2
sua-renhap-uiconcept-ledgerpg-draftreportsloai-speciesbalodotdesign-onlyv0rev2recon

KẾ HOẠCH — "SỬA RẺ" / NHÁP UI v0

2 bảng đếm khái niệm + tích hợp reports / loài / balo

Trạng thái: THIẾT KẾ TRÊN GIẤY. Chưa chạy DDL, chưa insert gì. Chờ Owner duyệt rồi mới cho DOT thực thi. Tác giả: Fable (Reviewer #3) · Ngày: 2026-07-06 · Rev 2 (bổ sung recon thực địa + 6 guard toàn vẹn).


⚠️ RECON THỰC ĐỊA (Rev 2 — đọc TRƯỚC, đã kiểm bằng query PG thật vào DB directus)

Trước khi duyệt, đã tra dữ liệu thật và phát hiện HAI điều làm sai kế hoạch cũ nếu chạy DOT ngay:

  1. LOÀI "QUY TRÌNH" ĐÃ TỒN TẠI. entity_species đã có workflow (display_name "Quy trinh", code SPE-WKF, prefix WF), cùng cả họ: workflow_step, task, page, collection, table_ui, catalog (Danh mục hệ thống). ⇒ KHÔNG tạo loài "quy trình" mới — làm vậy là tự đẻ khái niệm trùng, đúng thứ hệ này sinh ra để diệt. Phải TÁI DÙNG loài có sẵn (xem §5.1).

  2. REPORTS DÙNG DIRECTUS COLLECTION, KHÔNG DÙNG view_sql. Mọi dòng table_registry hiện có đều trỏ collection=<Directus collection thật> + page_url, view_name=NULL. ⇒ Muốn 2 bảng lên portal thì chúng phải được đăng ký thành Directus collection (qua DOT), KHÔNG phải "một view + view_sql" như Rev 1 giả định. Việc tag loài (species_collection_map) cũng trỏ theo collection_name ⇒ cũng cần là collection.

Hệ quả: phần đếm (§1–4) vững, làm được ngay ở GĐ1 với bảng thường. Phần tích hợp (§5) cần agent xác nhận công thức đăng ký Directus-collection thật (soi một collection mới thêm gần đây hoặc doc khuôn) TRƯỚC khi chạy GĐ2 — không đoán.


0. VÌ SAO CÓ BẢN NÀY

Bài toán gốc: Cowork vẽ một UI → đếm UI đó dùng bao nhiêu đầu mối, không đếm trùng, cái nào có thì tái dùng. UI phức tạp thì Cowork đếm tay nhầm/chậm. Đây là việc PG giỏi (đếm distinct, gom trùng, dò cái đã có). Chuyển phần đếm/khớp sang PG; retire machinery JS pilot (ui-contracts.js, verify-contracts.js, tab UI). Tư duy đã xây (phân vai định-nghĩa vs nơi-dùng, khử trùng, họ dễ nhầm) giữ nguyên.


1. NGUYÊN TẮC BẤT DI (chống phình lại)

  1. 100% DOT cho DDL + ghi dữ liệu. Không psql trực tiếp, không sửa schema bằng Directus UI, không DDL ngoài DOT.
  2. PG → app chỉ qua Directus/Nuxt bằng KHUÔN CÓ SẴN (đăng ký collection + table_registry + SharedDirectusTable). Không Codex ở Nuxt.
  3. v0 chỉ 2 bảng. Không bảng scan/ui/alias riêng.
  4. v0 chỉ exact / alias / ILIKE. KHÔNG pg_trgm, KHÔNG Qdrant, KHÔNG fuzzy auto-match.
  5. KHÔNG XÂY MỚI — chỉ INSERT vào registry sẵn có + tái dùng khuôn. Vượt ra → DỪNG, hỏi Owner.

2. HAI BẢNG (schema) — DB directus, tên _nhap, status='draft'

(Lý do DB directus: 4 ổ cắm tích hợp đều ở đó; để incomex_metadata phải FDW cross-DB.)

Bảng 1 — sua_re_khai_niem_nhap (DANH MỤC = ĐỊNH NGHĨA)

concept_id varchar PK · ten · loai (∈ workflow/step/task/field/role/org) · mo_ta text (định nghĩa chuẩn, 1 bản/khái niệm) · ranh_gioi · aliases jsonb · match_policy (auto|review) · status · note · date_created/date_updated.

Bảng 2 — sua_re_ui_khai_niem_nhap (NƠI DÙNG = bảng kê từng UI/scan)

item_id bigint PK · ui_id · ui_name · ui_file · scan_id · scan_at · is_current bool · raw_label · purpose · location · dedupe_key · concept_id (FK, nullable) · match_status · suggested_concept_id (nullable) · owner_note · date_created/date_updated.

6 GUARD TOÀN VẸN (GPT chỉnh — biến luật thành ràng buộc máy)

  1. purpose chặn máy: purpose text NOT NULL CHECK (length(trim(purpose)) >= 15). Thiếu mô tả thì DOT không ghi được — đây là "cơ chế nhắc Cowork khỏi quên".
  2. dedupe_key bắt buộc: NOT NULL. Cùng ui_id+scan_id, các nhãn cùng bản chất phải cùng dedupe_key; chưa rõ thì candidate.<slug>/review.<slug>, KHÔNG để trống. (Đây là cọc đếm "sau khử trùng".)
  3. Luật match_status: ∈ raw/matched/candidate/review/deferred. CHECK (match_status <> 'matched' OR concept_id IS NOT NULL) — matched bắt buộc có concept_id. suggested_concept_id chỉ là gợi ý, KHÔNG thay concept_id.
  4. Guard scan-current (không cần bảng scan riêng): scan lại một UI → (a) update dòng current cũ của ui_id thành is_current=false; (b) insert scan mới is_current=true; (c) DOT chạy kiểm sau ghi: SELECT ui_id, count(DISTINCT scan_id) FROM … WHERE is_current GROUP BY ui_id HAVING count(DISTINCT scan_id)>1 — trả về dòng nào → FAIL/rollback.
  5. Registry idempotent (§5): trước mọi insert vào entity_species/species_collection_map/balo_thuc_the/table_registry phải SELECT kiểm tồn tại; đã có → update/bỏ qua. Chạy lại DOT KHÔNG được nhân đôi (species_code, collection_name, report row).
  6. Tách execution: GĐ1 (2 bảng + migrate + SELECT đếm) và GĐ2 (reports/loài/balo) KHÔNG gộp một lần chạy — phải chứng minh đếm nhanh chạy trước.

BA LUẬT DÙNG (nền "sửa rẻ")

  1. purpose bất biến + không rỗng (guard 1). 2. Định nghĩa chuẩn 1 bản/khái niệm (bảng 1); mô tả công dụng tại chỗ nhiều bản (bảng 2), bất biến. 3. Scan lại = INSERT dòng mới; dòng cũ is_current=false, KHÔNG XÓA.

"Cùng khái niệm, 3 mô tả hơi khác": KHÔNG hợp nhất. Giữ 1 định nghĩa chuẩn + 3 mô tả tại chỗ làm bằng chứng. Lệch nhiều → v0 KHÔNG tự cảnh báo, chỉ SELECT rà khi cần.


3. VÒNG VẬN HÀNH

Cowork vẽ → agent liệt kê thô (raw_label+purpose+location+dedupe_key) → DOT insert scan mới (match_status='raw') → PG SELECT đếm + so danh mục (exact→alias→ILIKE gợi ý) → Owner duyệt candidate/review → DOT cập nhật (khái niệm mới vào bảng 1; usage gắn concept_id) → cơ sở viết hợp đồng thông tin. Cowork hết đếm mắt; PG đếm.


4. SELECT MẪU (5–7 câu)

  1. Đếm khái niệm 1 UI (current): count(DISTINCT dedupe_key) WHERE ui_id=:ui AND is_current.
  2. Thô→sau khử trùng: count(*) vs count(DISTINCT dedupe_key).
  3. Khớp/treo: LEFT JOIN danh mục, nhóm theo match_status.
  4. Hàng chờ chốt: WHERE match_status IN ('candidate','review') AND is_current + purpose + suggested.
  5. Dùng ở bao nhiêu nơi: GROUP BY concept_id → count(DISTINCT ui_id), count(*).
  6. Lịch sử mô tả 1 khái niệm (bằng chứng): mọi purpose theo scan_at (gồm bản cũ).
  7. Matrix khái niệm×UI: view/report xuất ra, KHÔNG phải cấu trúc lưu.

5. TÍCH HỢP reports / loài / balo — REV 2: theo recon thực địa

Tiền đề: 2 bảng phải là Directus collection trước (recon #2). GĐ2 phải mở đầu bằng agent soi công thức đăng ký collection thật (một collection thêm gần đây, hoặc doc khuôn), rồi mới DOT.

5.1 Loài — TÁI DÙNG, KHÔNG tạo mới (recon #1)

Loài "quy trình" đã tồn tại = workflow. Đề xuất Fable: không tạo loài mới; tag 2 bảng bằng loài có sẵn — sua_re_khai_niem_nhap hợp nhất với catalog (Danh mục hệ thống); sua_re_ui_khai_niem_nhap là bảng kê, có thể catalog hoặc table_ui. HOẶC nếu Owner muốn cả hai nằm trong dòng họ workflow theo ý "mọi thứ để làm quy trình" → tag workflow. → Câu hỏi nghiệp vụ cho Owner (§8). Tuyệt đối không insert species_code mới trùng nghĩa.

5.2 Gắn loài ↔ bảng — species_collection_map

Insert (idempotent) 2 dòng: species_code=<loài Owner chọn>collection_name='sua_re_khai_niem_nhap' / 'sua_re_ui_khai_niem_nhap', is_primary=true.

5.3 Đeo balo — balo_thuc_the

Insert (idempotent) 2 dòng: ten=tên bảng, noi_luu=DB/đường dẫn, nhom/loai phản ánh loài, khoa_nhan_dien=tên bảng, active.

5.4 Đúc lên portal — table_registry (khuôn có sẵn, theo mẫu THẬT)

Theo mẫu các dòng hiện có: table_id, name, collection=<tên bảng>, page_url=/knowledge/registries/<...> (hoặc trang Owner chỉ), fields (jsonb cột hiển thị), module, primary_key, status. KHÔNG dùng view_sql. → 2 dòng hiện trên portal, bấm ra bảng qua SharedDirectusTable.


6. GIAI ĐOẠN

  • GĐ0 (giấy): bản này. Owner duyệt §8.
  • GĐ1 (DOT: 2 bảng thường + đường ghi): tạo 2 bảng (kèm 6 guard §2), migrate 13 khái niệm (từ concept-ledger.js v0.5) + 30 dòng usage (từ ui-contracts.js), JS đóng băng → PG là SSOT nháp. Chạy SELECT §4. → đếm nhanh HOẠT ĐỘNG. (Chưa cần Directus collection — đếm qua MCP/DOT là đủ.)
  • GĐ2 (đăng ký collection + reports/loài/balo): mở đầu bằng recon công thức collection thật → DOT đăng ký 2 collection → insert §5.1–5.4 (idempotent). → lên portal, gắn loài, đeo balo.
  • GĐ sau (HOÃN): pg_trgm khi ~300 khái niệm; Qdrant nếu cần; tab UI. Không làm bây giờ.

7. RỦI RO + ROLLBACK

  • 2 SSOT (JS+PG): migrate xong JS đóng băng ngay. - Auto-match mạnh: v0 exact/alias/ILIKE; fuzzy chỉ gợi ý. - Draft hiểu nhầm chính thức: tên _nhap, status=draft, ghi rõ trên portal. - Tạo trùng loài/collection/report: guard 5 idempotent + recon #1. - Rollback: mọi thứ qua DOT; drop bảng/xóa registry qua DOT; KHÔNG tự drop ngoài DOT.

8. CHỜ OWNER CHỐT

  1. purpose tối thiểu 15 ký tự (đã thành CHECK — guard 1). ✔ đề xuất chốt.
  2. Loài (recon #1): tái dùng catalog (Fable nghiêng), hay table_ui, hay workflow cho cả hai? — cần Owner quyết. (KHÔNG tạo loài mới.)
  3. v0 không tự bật review khi mô tả lệch — chỉ SELECT rà. ✔ đề xuất chốt (Fable+GPT đồng ý).
  4. Đặt 2 bảng ở DB directus. ✔ đề xuất chốt (vì reports/loài/balo ở đó).
  5. GĐ2 phải recon công thức Directus-collection thật trước khi DOT (recon #2). ✔ đề xuất chốt.

9. GUARD ĐƠN GIẢN

v0 = 2 bảng + vài SELECT + cắm registry sẵn có + đúc portal bằng khuôn cũ. HẾT. Vượt ra (thêm bảng/component/cơ chế thông minh/tự cảnh báo) → DỪNG, hỏi Owner. Phức tạp = thất bại.