Claude Code Infra Eval — SUA-RE PG Draft (G1a/G1b) — VERDICT PASS (Path B, dot-pg-atomic-apply)
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ó cửa DDL/DML hợp lệ:
dot-pg-atomic-applychạy 1 file.sqlđã duyệt trong 1 transaction nguyên tử (psql -1, roleworkflow_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.sqlnằm trong allowlist + manifest, đã đúcbalo_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 ghimeta_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 .sql → INSERT … 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_nhap ở public.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%/ collectionsua_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_id ⇒ dâ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àoapproved-sql-manifest.tsv(sha256) →dot-pg-atomic-apply <file>.
- Tạo 2 bảng nháp — 1
.sql(CREATE TABLE IF NOT EXISTSsua_re_khai_niem_nhap+sua_re_ui_khai_niem_nhap: bigserial PK, UNIQUEconcept_key/item_key, FKconcept_id, jsonbaliases/near_concepts, CHECKmo_ta/purposelen≥15,status/is_currentdefault) → atomic-apply. (schemapublic, DBdirectus.) - Nạp concept seed — render 13 thẻ → INSERT…ON CONFLICT (
status 'chot'→'active') → atomic-apply. - 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.
- Nạp 1 companion scan mới (G1b) — Cowork xuất
UI_SCAN_JSONcho 1 UI thật → cổng JSON→SQL: validate purpose≥15, lậtis_current=falsebản cũ, INSERT bản mới, atomic → atomic-apply. - Tạo SELECT/VIEW delta — 1
.sqlCREATE VIEWv_sua_re_delta(7 nhóm §D) → atomic-apply; hoặc chạy SELECT trực tiếp read-only để đếm. - Owner duyệt candidate/review — 3 action (
map_existing/create_new/keep_pending): setowner_concept_id+decision_notequa 1.sql→ atomic-apply. (G3 nếu cần cổng quyết định chuyên dụng.) - 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:
- 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.
- 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. - 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. 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
- Làm 2 bảng PG nháp mà không đụng hệ chính? — CÓ.
dot-pg-atomic-applyđúc thẳng vàopublic.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. - 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. - 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_idnên không ngõ cụt — phase sau tạo bảng chính + 1 DOT promote (hoặc.sqlpromote 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.