KB-8CEA

Claude Code Infra Eval — SUA-RE PG Draft (G1a/G1b) — VERDICT PASS (Path B, dot-pg-atomic-apply)

17 min read Revision 1
sua-renhap-uiinfra-evalpre-flightdotpg-atomic-applypath-bg1a-g1bclaude-coderead-only2026-07-06

Claude Code Infra Eval — SUA-RE PG Draft

Phạm vi: KHẢO SÁT HẠ TẦNG TỐI THIỂU cho lát cắt PG nháp sửa-rẻ (G1a/G1b). READ-ONLY + đọc source/spec + query read-only. KHÔNG tạo bảng, KHÔNG nạp, KHÔNG promote, KHÔNG commit, KHÔNG sửa kế hoạch. Người chạy: Claude Code (Opus 4.8, VPS read-only). Ngày: 2026-07-06. Đọc: ke-hoach-nhap-ui-v0.md (Rev 6, rev 12) + seed sống trên VPS + source DOT + PG live (read-only). Đối chiếu §9 kế hoạch: bản này chỉ trả lời khảo sát HẸP A–F theo lệnh Owner, KHÔNG mở full gate G-A…G-J, KHÔNG redesign.


1. Verdict

PASS — đủ điều kiện chạy G1a/G1b bằng DOT, qua Path B (dot-pg-atomic-apply, PG-thuần).

  • cửa DDL/DML hợp lệ: dot-pg-atomic-apply chạy 1 file .sql đã duyệt trong 1 transaction nguyên tử (psql -1, role workflow_admin — "atomic core does DDL"). Kham đủ CREATE TABLE / VIEW / SEQUENCE / INSERT-UPSERT / FK / UNIQUE / CHECK / jsonb / timestamptz.
  • Tiền lệ sống: balo-core-first-run.sql nằm trong allowlist + manifest, đã đúc balo_thuc_the (bigserial-seq PK + GENERATED + CHECK + UNIQUE + INDEX + INSERT, PG-thuần, KHÔNG birth_registry). Đúng khuôn 2 bảng sửa-rẻ cần.
  • Path A (dot-collection-create) KHÔNG hợp — xem A2/A3: chỉ tạo "standard fields" (id SERIAL PK), bắt buộc ghi meta_catalog+schema_birth_registry+species (đúng thứ §7 muốn né), KHÔNG có --dry-run, KHÔNG rollback. Trượt ≥2/4 điều kiện §7 ⇒ đúng nhánh "vá nhiều → Path B" mà kế hoạch đã lường.

2 caveat KHÔNG chặn G1a/G1b (ghi rõ để Owner soi): (a) không có DOT teardown/DROP — DROP bị guardrail chặn, dot-rollback chỉ xoá Directus flow; bù bằng thiết kế idempotent (CREATE IF NOT EXISTS + UPSERT). (b) atomic-apply không có --dry-run — bù bằng gate manifest+sha256+guardrail (chặn TRƯỚC khi psql chạy) + rollback trong-txn khi lỗi.

F (promote) = có cửa nhưng phải dựng thêm phase sau — không phải ngõ cụt (giữ concept_key+owner_concept_id làm dây nối), nhưng chưa có DOT/bảng chính thức turnkey.


2. Bảng kết luận A–F (bằng chứng thật, không suy đoán)

A — DOT tạo bảng / nạp / view

Mục Kết luận Bằng chứng
A1 — cửa DDL chuẩn CÓ SẴN = dot-pg-atomic-apply /opt/incomex/dot/bin/dot-pg-atomic-apply (153 dòng). Chạy docker exec -i postgres psql -U workflow_admin -d directus -v ON_ERROR_STOP=1 -1 -f - < file. Header ghi rõ "atomic core does DDL". LÀM ĐƯỢC: CREATE TABLE/VIEW/SEQUENCE, INSERT, ALTER (trừ DROP), DO/PLpgSQL, CHECK/UNIQUE/FK. KHÔNG chỉ DML. 4 khoá: allowlist /opt/incomex/dot/sql/ + manifest approved-sql-manifest.tsv + sha256 khớp + guardrail (chặn DROP <object> incl. ALTER..DROP, DELETE FROM, TRUNCATE, txn-control top-level, meta-command, COPY..PROGRAM, CREATE EXTENSION/EVENT TRIGGER, và tên cấm meta_catalog|birth_registry|table_registry|qt001_|v_registry_). Teardown/rollback: chỉ rollback trong-txn khi lỗi (selftest rollback_on_fail.sql); KHÔNG có teardown DROP (bị guardrail chặn).
A2 — nếu A1 không phải DDL n/a A1 CHÍNH LÀ cửa DDL. dot-collection-create là cửa DDL khác nhưng nặng governance (xem A3). Không có dot-ddl-* riêng.
A3 — kiểu/ràng buộc CÓ SẴN qua A1 (đủ); dot-collection-create KHÔNG đủ Qua .sql của A1: bigserial/identity, text, jsonb, boolean, timestamptz, FK, UNIQUE, CHECK(len≥15) — tất cả kham (tiền lệ balo-core-first-run.sql: SEQUENCE + GENERATED + CHECK lop + UNIQUE khoa_nhan_dien). Ngược lại dot-collection-create (v2.0.0) Step 2 hard-code CREATE TABLE (id SERIAL PRIMARY KEY, …standard fields…) — KHÔNG khai được jsonb/UNIQUE(concept_key,item_key)/FK/CHECK≥15 trong 1 lần; ép id SERIAL (≠ bigserial kế hoạch).
A4 — nạp / UPSERT CÓ SẴN qua A1 (input = SQL); không có bulk-JSON loader chung A1 nhận file .sqlINSERT … ON CONFLICT = UPSERT hàng loạt, atomic (cả file 1 txn, lỗi giữa chừng rollback sạch). KHÔNG có --dry-run. Không có DOT loader nhận thẳng JSON/CSV cho bảng tuỳ ý (các loader tồn tại đều thuộc subsystem: fn_iu_*, dot-report-publish). ⇒ seed JSON phải render JSON→SQL rồi atomic-apply (đúng nhánh "JSON→SQL fallback" §9 G-I).
A5 — VIEW / SELECT delta CÓ SẴN CREATE VIEW không bị guardrail chặn ⇒ v_sua_re_* đúc qua A1 (tên sạch, không đụng prefix cấm). SELECT delta = SQL thuần (count DISTINCT, GROUP BY, WHERE is_current, join). Khảo sát này chạy thử SELECT qua MCP read-only OK.
A6 — rollback / teardown PARTIAL — rollback-trong-txn CÓ; teardown-DROP THIẾU Rollback ghi-lỗi: CÓ, chuẩn (atomic -1 + ON_ERROR_STOP, selftest xác nhận). Teardown bảng đã commit: KHÔNG có DOT (DROP bị guardrail chặn; dot-rollback = xoá Directus flow [DOT], không đụng PG). Bù: idempotent (re-run không cần teardown); §10 kế hoạch cũng nói "KHÔNG rollback DOT riêng nếu tool có đường lùi chuẩn" ⇒ đường lùi chuẩn = rollback-trong-txn ⇒ đủ cho G1.

B — PG nháp đứng độc lập

Mục Kết luận Bằng chứng
B1 — PG-only CÓ SẴN (GO) A1 đúc bảng thẳng vào PG, không cần Directus collection / vector / report / registry. Tiền lệ balo_thuc_the = BASE TABLE PG-thuần (objects = table + seq + index, 0 birth_registry).
B2 — tầng tối thiểu bắt buộc Chỉ gate A1 (birth/register = convention) Bảng PG trần qua A1 KHÔNG cần birth/meta_catalog/species. Không có global event trigger ép birth (A1 chặn CREATE EVENT TRIGGER; balo không có). birth/register/species = convention (chỉ dùng khi qua dot-collection-create hoặc lộ ra Directus), bỏ được trong gói nháp G1.
B3 — tên bảng / schema GIỮ Rev 6 (public schema, DB directus) Bằng chứng ủng hộ public: (1) dot-collection-track-readonly + dot-list-publish (DOT lộ-bề-mặt phase sau) kiểm bảng ở public; schema riêng sua_re sẽ vỡ đường lộ sau. (2) Directus 11 auto-introspect chỉ public. ⇒ không có lý do kỹ thuật đổi tên/schema. sua_re_khai_niem_nhap + sua_re_ui_khai_niem_nhappublic.directus là đúng.
B4 — verdict Path B GO Đủ tạo bảng (A3) + nạp (A4) + SELECT/view (A5) + rollback-trong-txn (A6) mà KHÔNG cần Directus/vector/report. Caveat = teardown-DROP (A6).

C — Seed thật + mapping

Mục Kết luận Bằng chứng
C1 — concept-ledger 13 concept (khớp Rev 6) /opt/incomex/docs/mcp-writes/ui-preview/concept-ledger.js window.CONCEPTS v0.5, updated 2026-07-06T03:53:44Z. schema = [id,type,name,description,boundary,examples,non_examples,aliases,locations,status,note,match_policy]. type_vocab = 6 [workflow,step,task,field,role,org]. 13 thẻ, tất cả status:'chot'; 2 thẻ (field.ngay_lap, field.trang_thai_quy_trinh) mang match_policy:'review', còn lại vắng (=auto).
C2 — ui-contracts 30 usage (27 matched / 2 candidate / 1 review — khớp Rev 6) .../ui-contracts.js window.UI_CONTRACTS v0.1-chot2, cùng mốc. 1 surface master-list-quy-trinh-v1, 30 item. schema item = {local_name, purpose(≥10), type_guess, concept_id, match_status, location{file,kind,key/marker,evidence}, duplicate_group, near_concepts, note}. Đếm: 27 matched + 2 candidate (candidate.lien_ket_me) + 1 review (review.co_tu_dong).
C3 — map → 2 bảng CÓ SẴN, sạch (map trực tiếp + vài biến đổi) Xem 2 bảng map dưới.
C4 — luật match tối thiểu CÓ SẴN (ILIKE đủ, KHÔNG cần extension) exact concept_key/alias → matched; ILIKE (nhẹ) → review; không thấy → candidate. ILIKE là SQL lõi, KHÔNG cần pg_trgm. Fuzzy auto-match: cấm (chỉ gợi ý, Owner quyết).

Map Bảng 1 sua_re_khai_niem_nhap ← concept-ledger.items[]:

Cột bảng Nguồn seed Trạng thái
concept_key (UNIQUE) item.id trực tiếp
ten item.name trực tiếp
concept_kind (enum 6) item.type (type_vocab khớp enum) trực tiếp
mo_ta (CHECK≥15) item.description trực tiếp — cần đo ≥15 (G-E)
ranh_gioi item.boundary trực tiếp
aliases (jsonb) item.aliases trực tiếp
match_policy item.match_policy (vắng→'auto') trực tiếp
status item.status 'chot''active' biến đổi (G-H)
note item.note trực tiếp
id, date_created, date_updated máy sinh
examples/non_examples/locations THỪA (chủ ý) — giữ ở seed artifact per §2

Map Bảng 2 sua_re_ui_khai_niem_nhap ← ui-contracts.surfaces[].items[]:

Cột bảng Nguồn seed Trạng thái
ui_id surface.surface_id trực tiếp
ui_file item.location.file trực tiếp
raw_label item.local_name trực tiếp
purpose (CHECK≥15) item.purpose trực tiếp — seed min chỉ ≥10 → CẦN đo, có thể vài dòng <15 (G-E)
location item.location.key|marker biến đổi (làm phẳng object)
concept_id (FK bigint) resolve item.concept_id(=concept_key) → khai_niem.id biến đổi; null cho candidate/review (G-G)
dedupe_key item.duplicate_group matched=concept_key trực tiếp; pending → pending.<hash(raw_label+purpose)> biến đổi
near_concepts (jsonb) item.near_concepts ({id,why,confidence}) trực tiếp
item_key, scan_id, scan_at, is_current máy sinh ở cổng (item_key=hash(ui_id,scan_id,location_or_index,raw_label); scan_id=hash canonical JSON)
owner_concept_id, decision_note null (Owner quyết sau)
type_guess, match_status, location.kind, evidence, note KHÔNG lưu — gợi ý/suy ở view (match_status suy §11-3)

D — Delta review tối thiểu

CÓ SẴN — 7 nhóm đều ra được bằng SELECT/view (điều kiện: có scan_id + is_current, cả hai đã nằm trong schema Bảng 2). Không cần bảng history riêng (current+history chung 1 bảng, tiền lệ iu_staging_record).

# Nhóm Cách
1 tổng item thô count(*) WHERE ui_id=:ui AND is_current
2 tổng distinct dedupe count(DISTINCT dedupe_key) …
3 matched join concept_id IS NOT NULL / dedupe=concept_key
4 review suy: exact/alias fail + ILIKE hit (hoặc match_policy='review')
5 candidate suy: không khớp + không ILIKE
6 item mới vs scan trước so scan_id/is_current (bản cũ is_current=false)
7 item đổi purpose vs scan trước so purpose theo scan_at giữa 2 scan

scan_id/is_current: đã có trong schema Rev 6 ⇒ giữ lịch sử scan cũ được (insert dòng mới, bản cũ is_current=false), không xoá lịch sử.

E — Va chạm tên

SẠCH — 0 va chạm (query read-only, DB directus):

  • sua_re_khai_niem_nhap, sua_re_ui_khai_niem_nhap: chưa tồn tại (0 dòng information_schema.tables).
  • v_sua_re_* / v_report_sua_re: chưa tồn tại (0 dòng).
  • table_registry (page_url %sua-re% / collection sua_re%): 0 dòng.
  • directus_collections (%sua_re%): 0 dòng.
  • schema sua_re: không tồn tại.

Tổng query name-collision: rowcount = 0.

F — Đường promote (chỉ "có cửa không", không thiết kế)

Mục Kết luận Bằng chứng
F1 — pattern staging→canonical CÓ, proven, nhưng nặng/chuyên biệt Schema iu_core tồn tại: iu_staging_record, iu_staging_payload (BASE TABLE) + v_iu_staging_record, v_iu_staging_payload_observability. Thư viện ~90 fn_iu_* (public) gồm vòng đời promote-có-rollback thật: fn_iu_structure_op_plan / _apply / _verify / _rollback, fn_iu_compose, fn_iu_cut_from_manifest, fn_iu_op_cut, fn_iu_op_verify_cut, fn_iu_supersede, fn_iu_retire, fn_iu_birth_gate_layer1/2. KHÔNG có fn_iu_create chung; entrypoint là IU-collection-specific + kèm birth-gate/three-axis-envelope/vector-sync/Qdrant ⇒ KHÔNG phải "promote concept rows → bảng concept chính" dùng ngay được.
F2 — bảng chính thức ứng viên THIẾU (chưa có nhà canonical cho concept) Tìm concept|contract: chỉ có dot_agent_api_contract, qt001_capability_contract (subsystem riêng) + ~30 v_*_contract VIEW (registries-pivot, "contract" nghĩa KHÁC). Không có bảng catalog concept / hợp đồng thông tin chung. (meta_catalog là nhà kho governance + là tên CẤM ở gate A1.)
F3 — nếu chưa có CẦN TẠO bảng chính thức ở phase sau Ghi nhận. Không thiết kế ở đây.
F4 — nguyên tắc promote ghi nhận Promote phải là DOT (approved rows → bảng chính → đánh dấu promoted → giữ link nguồn nháp), KHÔNG copy tay. Bảng 2 đã giữ concept_key+owner_concept_iddây nối sẵn, không ngõ cụt. Cửa khả thi phase sau: (a) tái dùng gate A1 với 1 .sql promote đã duyệt (INSERT INTO canonical SELECT … WHERE owner_concept_id IS NOT NULL), hoặc (b) DOT promote nhỏ mới.
F5 — vector/Directus/report khi promote Để phase sau (Ghi nhận: cửa no-vector đã tồn tại & proven — collection_registry_vector_policy (BASE TABLE) + v_collection_vector_eligibility (VIEW) — dùng khi đăng ký collection, ngoài phạm vi G1a/G1b PG-only.)

3. Công thức dựng tối thiểu nếu PASS (chuỗi DOT — CHƯA CHẠY)

Path B. Mỗi .sql = author → thêm vào approved-sql-manifest.tsv (sha256) → dot-pg-atomic-apply <file>.

  1. Tạo 2 bảng nháp — 1 .sql (CREATE TABLE IF NOT EXISTS sua_re_khai_niem_nhap + sua_re_ui_khai_niem_nhap: bigserial PK, UNIQUE concept_key/item_key, FK concept_id, jsonb aliases/near_concepts, CHECK mo_ta/purpose len≥15, status/is_current default) → atomic-apply. (schema public, DB directus.)
  2. Nạp concept seed — render 13 thẻ → INSERT…ON CONFLICT (status 'chot'→'active') → atomic-apply.
  3. Nạp usage seed scan_0 — render 30 item → resolve FK concept_key→id, sinh item_key + scan_id='seed-0', is_current=true, INSERT…ON CONFLICT → atomic-apply.
  4. Nạp 1 companion scan mới (G1b) — Cowork xuất UI_SCAN_JSON cho 1 UI thật → cổng JSON→SQL: validate purpose≥15, lật is_current=false bản cũ, INSERT bản mới, atomic → atomic-apply.
  5. Tạo SELECT/VIEW delta — 1 .sql CREATE VIEW v_sua_re_delta (7 nhóm §D) → atomic-apply; hoặc chạy SELECT trực tiếp read-only để đếm.
  6. Owner duyệt candidate/review — 3 action (map_existing/create_new/keep_pending): set owner_concept_id+decision_note qua 1 .sql → atomic-apply. (G3 nếu cần cổng quyết định chuyên dụng.)
  7. Phase sau — promote — tạo bảng concept chính thức (F3) + DOT promote (F4): INSERT INTO canonical SELECT … WHERE owner_concept_id IS NOT NULL, đánh dấu promoted, giữ link. KHÔNG làm ở G1.

4. Blocker / caveat

KHÔNG có blocker chặn G1a/G1b. Cửa DOT hợp lệ tồn tại (A1) + tiền lệ chạy thật (balo). Các điểm cần Owner biết:

  1. Teardown-DROP thiếu DOT (A6) — KHÔNG chặn G1 (idempotent bù), nhưng nếu muốn "xoá sạch thí nghiệm" thì phải Owner-approve 1 thao tác DROP ngoài gate (guardrail chặn DROP). Đề nghị: chấp nhận cho G1 (không cần teardown), quyết sau nếu cần.
  2. atomic-apply không --dry-run — bù bằng gate manifest+sha256+guardrail (chặn trước psql) + rollback-trong-txn. Không phải blocker.
  3. Promote (F) chưa turnkey — có pattern (IU) + có dây nối (owner_concept_id), nhưng bảng concept chính thức + DOT promote phải dựng phase sau. Không chặn G1.
  4. purpose/mo_ta ≥15: seed ui-contracts đặt min chỉ ≥10 ⇒ khi nạp thật cần đo 43 chuỗi (13 mô tả + 30 purpose), dòng <15 sẽ FAIL import-gate (đúng thiết kế). Việc đo cụ thể = bước G-E lúc thực thi, không thuộc khảo sát này.

5. Ba câu cho Owner

  1. Làm 2 bảng PG nháp mà không đụng hệ chính?CÓ. dot-pg-atomic-apply đúc thẳng vào public.directus, PG-thuần, không Directus/vector/report/birth (tiền lệ balo_thuc_the). Path A (dot-collection-create) KHÔNG hợp — ép id SERIAL + bắt buộc ghi meta_catalog/birth/species. Dùng Path B.
  2. Nạp seed thật và đếm/so bằng PG?CÓ. Seed thật đủ (13 concept + 30 usage, đã verify trên VPS), map vào 2 bảng sạch (vài biến đổi rõ), nạp qua atomic-apply (JSON→SQL), 7 nhóm delta ra bằng SELECT/VIEW + ILIKE (không cần pg_trgm). scan_id/is_current đã có ⇒ so scan cũ/mới được.
  3. Promote sau này có cửa DOT hay phải tạo thêm?Có cửa, nhưng phải dựng thêm phase sau. Pattern staging→canonical proven (IU: fn_iu_structure_op_apply/rollback…) nhưng nặng/chuyên biệt; chưa có bảng concept chính thức (F2 THIẾU). Bảng 2 giữ concept_key+owner_concept_id nên không ngõ cụt — phase sau tạo bảng chính + 1 DOT promote (hoặc .sql promote qua gate A1). KHÔNG làm ở G1.

6. Chốt

Chỉ khảo sát. 0 bảng tạo · 0 nạp · 0 promote · 0 commit · 0 sửa kế hoạch. Mọi thao tác PG = read-only qua công cụ hợp lệ; source DOT đọc read-only. Verdict: PASS — chạy được G1a/G1b tối giản bằng DOT (Path B), với 2 caveat không chặn + promote để phase sau.