KB-7EAA

SUA-RE Tooling Survey — Quy trình vận hành sửa-rẻ (VERDICT PA3, 2026-07-07)

27 min read Revision 1
sua-renhap-uitooling-surveydotpg-atomic-applyslot-keychange-scopePA3read-only2026-07-07

SUA-RE Tooling Survey — Quy trình vận hành sửa-rẻ

Phạm vi: KHẢO SÁT BỔ SUNG — công cụ/DOT để VẬN HÀNH "sửa-rẻ" theo từng bước (nối tiếp claude-code-infra-eval-2026-07-06.md). READ-ONLY. KHÔNG tạo DOT · KHÔNG tạo bảng · KHÔNG sửa UI · KHÔNG ghi PG · KHÔNG commit · KHÔNG chạy SQL ghi. Người chạy: Claude Code (Opus 4.8). Ngày: 2026-07-07. Đọc thật (read-only): source 214 DOT (/opt/incomex/dot/bin), dot-pg-atomic-apply (full), manifest + allowlist (/opt/incomex/dot/sql), UI seed (master-list.js, ui-contracts.js, master-list-quy-trinh-v1.html, MO* builders), PG live read-only. Không khảo sát lại kết luận đã PASS ở bản 07-06 (Path B, atomic-apply làm DDL, 2 bảng nháp, promote để phase sau) — chỉ dùng làm nền.


1. Verdict

PA3 — dùng dot-pg-atomic-apply + SQL template đã duyệt + report SQL read-only. KHÔNG làm DOT mới ở G1.

Bằng chứng dồn về PA3, không phải cảm tính:

  • 4/5 chức năng đã có cửa qua đúng 1 công cụ sẵn. create-core, import, decision đều quy về "author 1 .sql → thêm dòng sha256 vào manifest → dot-pg-atomic-apply <file>". match + report = SELECT/VIEW read-only (ILIKE, không cần extension). Không chức năng nào thiếu cửa.
  • Tiền lệ sống, chạy thật. balo-core-first-run.sql đã đi trọn đường gate (manifest dòng 4, sha ab30bb8d…, INSERT 0 2146) — đúng khuôn 2 bảng sửa-rẻ.
  • 0 DOT sửa-rẻ tồn tại + workflow CHƯA chạy lần nào. grep sua[-_]re trên bin/sql/specs = rỗng. Đúc 5 DOT (PA1) hay 1 DOT-6-action (PA2) TRƯỚC khi .sql template ổn định = xây khung cho quy trình chưa nghiệm thu → cả PA1 lẫn PA2 đều double với gate (mỗi action rốt cuộc là wrapper mỏng gọi dot-pg-atomic-apply).
  • Chốt chặn ghi = 1 điểm duy nhất. Mọi write đi qua manifest+sha256+guardrail+atomic-rollback. Thêm DOT = thêm điểm ghi phải audit. PA3 giữ blast-radius nhỏ nhất, đúng tinh thần "sửa-rẻ".

Điều kiện nâng cấp về sau (không phải bây giờ): khi G1 chạy vài vòng và 3 template (create-core, import-delta, decision) đã đông cứng + lặp đủ nhiều để thấy đau, MỚI cân nhắc PA2 (1 DOT dot-sua-re nhiều action) gói lại template — không chọn PA1. Đó là quyết định của phase sau, có dữ liệu vận hành thật, không quyết ở đây.

Không có blocker chặn. 1 tinh chỉnh thiết kế phải làm trước khi import (xem §8/§12: item_key/slot_key phải neo theo anchor bền, không theo nhãn) — đây là chỉnh công thức, không phải hạ tầng thiếu.


2. Quy trình QT-A — Lần đầu chuẩn hóa một UI

Bước Nhiệm vụ Trigger Ai làm Sản phẩm bàn giao Ghi/lưu ở đâu Note
A1 Chỉ định UI cần chuẩn hóa Lệnh Owner Owner tên UI + surface_id phiếu/prompt 1 UI mỗi lượt
A2 Đọc UI + bóc khái niệm (scan toàn UI) Nhận chỉ định A1 Agent (đọc source read-only) scan artifact UI_SCAN_JSON: mỗi slot {local_name, purpose≥15, type_guess, location{file,kind,key|marker,evidence}} file cạnh UI trong ui-preview/ hoặc thêm 1 surface vào ui-contracts.js Lần đầu ĐƯỢC quét toàn file (khác QT-B)
A3 Đo purpose/mô tả ≥15 + render JSON→SQL scan_0 scan artifact xong Agent (render) + Owner (duyệt manifest) .sql INSERT scan_0 (item_key, scan_id='seed-0', is_current=true) /opt/incomex/dot/sql/sua-re/*.sql + 1 dòng trong approved-sql-manifest.tsv Dòng <15 ký tự → FAIL đúng thiết kế
A4 Nạp usage scan vào sua_re_ui_khai_niem_nhap manifest updated dot-pg-atomic-apply rows trong bảng nháp PG public.directus atomic, ON CONFLICT idempotent
A5 Nạp concept-ledger vào sua_re_khai_niem_nhap (nếu chưa) như A3/A4 Agent + gate 13 concept rows PG status:'chot'→'active'
A6 Match: exact/alias/ILIKE import xong Agent (SELECT/VIEW read-only) matched / review / candidate query output hoặc VIEW v_sua_re_delta cấm auto-match mờ — chỉ gợi ý
A7 Xuất review/candidate report match xong Agent report (chỉ review+candidate+delta) KB / file / prompt cho Owner KHÔNG bắt Owner xem matched
A8 Owner duyệt từng slot report ready Owner quyết định map_existing / create_new / keep_pending phiếu quyết định Agent KHÔNG tự quyết
A9 Apply decision Owner duyệt Agent render UPDATE .sql + manifest → gate set owner_concept_id + decision_note PG + sql/sua-re/ + manifest atomic

3. Quy trình QT-B — Lần sửa-rẻ (chỉ rà đúng slot)

Bước Nhiệm vụ Trigger Ai làm Sản phẩm bàn giao Ghi/lưu ở đâu Note
B1 Tạo phiếu sửa (change_scope) Có sửa UI Owner/Cowork phiếu (§9): ui_id, ui_file, slot_key, kind(add/edit/delete), old_label, new_label, old_purpose?, new_purpose?, reason, requester, at change-scope/<ui_id>.jsonl cạnh UI (§9) slot_key BẮT BUỘC — chỉ rõ 1 slot
B2 Lấy phiếu phiếu mới trong change-scope Agent đọc phiếu input = slot_key (+ old/new)
B3 Kiểm ĐÚNG slot (không quét toàn file) có phiếu Agent xác nhận slot tồn tại + trích new_purpose BƯỚC CẤM QUÉT LẠI TOÀN UI. Chỉ grep config_key path HOẶC marker của slot_key trong ui_file
B4 Render + import delta đúng slot slot verified Agent render JSON→SQL 1 slot + manifest .sql: lật is_current=false bản cũ của slot đó, INSERT bản mới sql/sua-re/ + manifest Chỉ 1 (vài) dòng — KHÔNG đụng slot khác
B5 Áp delta manifest dot-pg-atomic-apply 1 row mới is_current PG atomic; các slot đúng KHÔNG rung (chỉ slot bị sửa đổi is_current)
B6 Match delta đúng slot import xong Agent SELECT WHERE slot_key=… matched/review/candidate cho slot query Chỉ so 1 slot, không so toàn bảng
B7 (nếu review/candidate) Owner duyệt slot ra review/candidate Owner quyết định phiếu matched → bỏ qua bước này
B8 Apply decision Owner duyệt Agent + gate set owner_concept_id slot đó PG + manifest atomic

Cách "chỉ rà đúng slot" thành hiện thực (làm rõ theo yêu cầu §2):

  • Biết "đúng slot vừa sửa" bằng gì: phiếu mang slot_key = ui_id + location_anchor (config_key path hoặc code_marker/text_marker — §8). Cowork sửa ở đâu thì khai path/marker ở đó.
  • Ai tạo phiếu: Owner hoặc Cowork (người vừa sửa UI).
  • Phiếu lưu ở đâu: file change-scope/<ui_id>.jsonl trong ui-preview/ (cùng thư mục Cowork ghi UI).
  • Agent lấy phiếu: đọc dòng JSONL mới (read-only).
  • DOT nhận input bằng gì: không nhận phiếu trực tiếp — Agent render phiếu→.sql→manifest, DOT dot-pg-atomic-apply chỉ nhận 1 file .sql (đây là ranh giới ghi sạch).
  • Ghi vào bảng nào: sua_re_ui_khai_niem_nhap (delta), rồi sua_re_khai_niem_nhap nếu tạo concept mới.
  • Bước tuyệt đối không quét lại toàn file: B3 và B6 — chỉ định vị theo slot_key, không đọc lại cả UI, không so cả 30 item.

4. Bảng hạ tầng/công cụ

Hạ tầng/công cụ Phục vụ bước Trạng thái Bằng chứng thật Khuyến nghị
dot-pg-atomic-apply A3/A4/A5/A9, B5/B8 (create-core, import, decision) ĐÃ CÓ — dùng ngay bin/dot-pg-atomic-apply 153 dòng; 4 khoá; role workflow_admin; psql -1; tiền lệ balo Xương sống G1
allowlist /opt/incomex/dot/sql/ + approved-sql-manifest.tsv mọi bước ghi ĐÃ CÓ code dòng 46–47, 74–94; manifest 4 dòng Tạo nhánh con sua-re/ (§7)
SQL template sua-re/*.sql (create-core, import-delta, decision) A3/A4/A9, B4/B8 CẦN LÀM MỚI (chỉ là .sql, không phải DOT) 0 file sua-re tồn tại; khuôn = balo-core-first-run.sql Author 3 template, duyệt vào manifest
Cổng render JSON→SQL (scan artifact / phiếu → INSERT) A3, B4 CẦN LÀM MỚI (logic Agent, không cần binary) atomic-apply chỉ nhận .sql; không có bulk-JSON loader chung (§5) Agent render; giữ ngoài DOT ở G1
VIEW/SELECT delta (v_sua_re_delta) + ILIKE A6, B6 (match + report) ĐÃ CÓ cơ chế (SQL thuần) query_pg read-only chạy được; ILIKE là SQL lõi 7 nhóm delta = SELECT (bản 07-06 §D)
change-scope/<ui_id>.jsonl B1 CẦN LÀM MỚI (chỉ là file) thư mục ui-preview/ Cowork ghi được (MCP-writes) Nơi rẻ nhất cho phiếu (§9)
TEMPLATE-DOT-SCRIPT (chỉ khi lên PA2 phase sau) ĐÃ CÓ bin/TEMPLATE-DOT-SCRIPT Dùng nếu về sau gói PA2
dot-report-publish / dot-list-publish / dot-collection-track-readonly lộ-bề-mặt phase sau (KHÔNG G1) ĐÃ CÓ nhưng nặng/không hợp G1 §5 Chỉ dùng khi promote/hiển thị lên Nuxt

5. Inventory DOT/công cụ hiện có (chỉ gần 5 chức năng)

Nền: 214 DOT trong bin (đã lọc .bak/.stage/.pre). grep sua[-_]re = rỗng → greenfield.

CN1 — tạo core/bảng/schema qua SQL atomic

Tên dot-pg-atomic-apply
Source /opt/incomex/dot/bin/dot-pg-atomic-apply (153 dòng)
Input đúng 1 file .sql (path)
Output exit 0 = COMMIT / exit≠0 = ROLLBACK; log path+sha+PASS
Ghi PG? docker exec -i postgres psql -U workflow_admin -d directus -v ON_ERROR_STOP=1 -1 -f - < file
Dry-run/check? KHÔNG --dry-run; rollback-trong-txn khi lỗi (bù bằng gate trước psql)
Manifest/allowlist/guardrail? CÓ đủ 3 — allowlist /opt/incomex/dot/sql/ + manifest sha256 + guardrail tĩnh
Tái dùng cho sửa-rẻ? CÓ — trực tiếp (create-core, import, decision)
Nếu không dùng được n/a — chính là cửa chính

Phụ (schema-qua-Directus, KHÔNG phải tạo bảng nháp): dot-schema-apply — input spec JSON, thêm FIELD qua Directus API, có --dry-run, idempotent. Chỉ dùng phase-sau nếu lộ 2 bảng lên Directus. dot-schema-*-ensure (~30 cái) = mỗi cái hard-code 1 bảng governance cụ thể → không generic. dot-collection-create = loại (bản 07-06 A2/A3: ép id SERIAL, bắt buộc meta_catalog+birth_registry, không --dry-run, không rollback).

CN2 — import scan full/delta vào PG

Kết luận KHÔNG có loader generic. atomic-apply nhận .sql → INSERT/UPSERT hàng loạt atomic là đường duy nhất; seed JSON phải render JSON→SQL rồi qua gate.
Ứng viên gần dot-report-publish (snapshot TRUNCATE/INSERT, buộc tên ^v_report_, tạo Directus base-table id PK — không nạp vào sua_re_*); dot-registry-populate (scan nguồn cứng: dot_tools/ui_pages — không generic); dot-knowledge-ingest[-batch] (chỉ KB).
Ghi PG? có, nhưng đều ràng buộc subsystem riêng
Tái dùng? Không cho bảng nháp tuỳ ý → dùng JSON→SQL + atomic-apply

CN3 — match concept

Kết luận KHÔNG có DOT match generic. Match = SQL: exact concept_key/alias → matched; ILIKE → review; miss → candidate. ILIKE là SQL lõi, không cần pg_trgm.
Ứng viên gần (đều KHÁC việc) dot-species-map, dot-misclass-scanner (phân loài, subsystem); dot-field-duplicate-check, dot-id-collision-check (kiểm trùng, không map concept)
Tái dùng? Không → viết SELECT/VIEW

CN4 — apply Owner decision

Kết luận KHÔNG có DOT decision generic. Decision = UPDATE … SET owner_concept_id=…, decision_note=… trong 1 .sql qua gate.
Ứng viên gần (đều KHÁC) dot-content-approve (duyệt content Directus); dot-apr-propose/dot-apr-execute (subsystem APR)
Tái dùng? Không.sql UPDATE qua atomic-apply

CN5 — report review/candidate

Kết luận G1: SELECT/VIEW read-only (v_sua_re_delta, 7 nhóm) — đủ, rẻ nhất.
Ứng viên dot-report-publish ( nhưng nặng: buộc view_name ^v_report_[a-z0-9_]+$ dòng 118, ghi table_registry, tạo Directus base-table id PK → chỉ hợp phase-sau lộ-bề-mặt, KHÔNG cho draft PG-only); dot-list-publish (ghi table_registry cho collection đã track); dot-verify-loai-report (report cụ thể)
Tái dùng? G1: không (dùng SELECT); phase-sau lộ Nuxt: dot-report-publish/dot-list-publish

6. So sánh PA1 / PA2 / PA3

Tiêu chí PA1 — 5 DOT riêng PA2 — 1 DOT nhiều action PA3 — atomic-apply + SQL template
Số mắt xích vận hành Cao (5 binary + 5 test + 5 doc) Trung (1 binary, 6 action, 1 dispatcher) Thấp (0 binary mới; author .sql + 1 dòng manifest)
Double với DOT sẵn Cao — 4/5 chỉ là wrapper gọi gate Trung — vẫn wrap gate, gộp 1 chỗ Thấp — dùng thẳng gate, 0 trùng
Rủi ro SQL tay Thấp (đóng trong code) Thấp–trung Trung — bù bằng template author-1-lần + guardrail + CHECK + ON CONFLICT + atomic rollback
Dễ dùng lần sửa (QT-B) Trung (phải học 5 lệnh) Tốt (1 lệnh, action import-delta) Tốt (Agent render + 1 lệnh dot-pg-atomic-apply)
Dễ kiểm soát/audit Trung (5 điểm ghi) Trung (1 điểm ghi mới) Tốt nhất — 1 chốt ghi duy nhất đã có (manifest+sha+guardrail)
Dễ rollback/error Tuỳ mỗi DOT Tuỳ DOT Tốt — atomic -1 proven
Phục vụ "sửa-rẻ" Kém (đầu tư nặng trước khi chạy) Trung (còn sớm) Đúng nhất — rẻ, blast-radius nhỏ
Chín muồi để làm? Chưa (workflow chưa chạy) Chưa (chưa có .sql ổn định để gói) Có ngay (tiền lệ balo)

Kết luận: PA3 cho G1. PA2 là đích nâng cấp phase sau khi template đã đông cứng & lặp đủ nhiều; PA1 loại (chi phí/trùng lặp cao nhất, lợi ích thấp nhất).


7. Allowlist/path SQL (path thật, đã đọc source)

  • Thư mục SQL được phép: /opt/incomex/dot/sql/ (hardcode ALLOW_DIR_RAW, dòng 46; kiểm case "$RP/" in "$ALLOW_DIR"/*) dòng 74–77 — nhận mọi độ sâu con).
  • File manifest sha256: /opt/incomex/dot/sql/approved-sql-manifest.tsv (dòng 47). Định dạng TSV: sha256<TAB>relpath. Khoá: sha256 hiện tại phải == manifest (dòng 83–94), 1 dòng/relpath (cấm ambiguous).
  • Allowlist nằm ở đâu: chính là thư mục trên (path allowlist) + manifest (approval allowlist) — 2 lớp.
  • Tạo nhánh con sua-re/ được không: ĐƯỢC. REL = ${RP#"$ALLOW_DIR"/} (dòng 79) giữ nguyên đường con → /opt/incomex/dot/sql/sua-re/create-core.sql cho REL sua-re/create-core.sql. Tiền lệ đã có subdir: manifest chứa _selftest/atomic_happy.sql. → khai manifest dòng …<TAB>sua-re/create-core.sql.
  • Ai/công cụ cập nhật manifest: KHÔNG có tool. grep approved-sql-manifest toàn bin → chỉ dot-pg-atomic-apply (và nó chỉ ĐỌC). Manifest là TSV sửa tay — chủ ý là cổng người-duyệt.
  • Có tool add SQL vào manifest không: KHÔNG. Thêm = tự sha256sum <file> rồi append 1 dòng sha256<TAB>relpath (thao tác Owner/người duyệt, ngoài mọi DOT).
  • Tiền lệ balo-core-first-run.sql: path /opt/incomex/dot/sql/balo-core-first-run.sql; manifest dòng 4 = ab30bb8d2ee22881171268f0a18fc365d1e6ec46f7eef785103b82702e4259f0<TAB>balo-core-first-run.sql.

(Không khảo sát lại việc atomic-apply có làm DDL — đã PASS 07-06. Trên đây chỉ xác nhận path/manifest/tool.)


8. Slot_key — xác minh trên source thật

8.1 Đã xác minh (bằng chứng)

UI đã xác minh đầy đủ tới từng slot: DUY NHẤT master-list-quy-trinh-v1 (qua pilot ui-contracts.js, 30 item). MO* mới xác minh tới cấu trúc dữ liệu (grep), chưa liệt kê từng slot — ghi rõ ở 8.4.

Renderer master-list.js neo khái niệm theo 3 loại location.kind (không phải 1):

kind Ví dụ thật (ui-contracts.js) Bền?
config_key (path vào MASTER_CONFIG) items[].code, items[].name, items[].steps[].name, items[].steps[].items[].n, .trig, .min, items[].steps[].lam, .bao, items[].anchor, items[].maker, items[].links, items[].date, items[].status, items[].steps[].agent Bền
text_marker (chuỗi hiển thị trong renderer) <th>Mã</th>, <th>Tên</th>, <th>Tên task</th>, <th>Trigger</th>, Số phút, <th>Người thực hiện</th>, <th>Người được báo cáo</th>, <th>Ghi chú</th>, Đường dẫn 7 tầng, Chuyên môn, Người lập, Ngày lập, Trạng thái, Liên kết 4 Mẹ KHÔNG — marker = chính cái nhãn
code_marker (class/token CSS) qv-grp, qv-tot Bền

Nguồn: master-list.js dòng 186 (it.code), 309/377 (s.lam), 316/380 (x.n), 179 (<th>Mã</th> hard-code), 390 (header qv hard-code); master-list-quy-trinh-v1.html dòng 20–68 (MASTER_CONFIG, 7 item, keyed code).

8.2 slot_key = ui_id + config_key_path — đúng phần nào?

Đúng cho tập config_key (dữ liệu), NHƯNG phải tổng quát hóa cho phần nhãn-renderer. Trong 30 item pilot, ~14 là config_key, ~16 là text_marker/code_marker neo vào renderer (không có config path). Vậy công thức đúng phải là:

slot_key = ui_id + location_anchor, với location_anchor là MỘT trong: config_key:<path> (ưu tiên, bền) · code_marker:<class> (bền) · text_marker:<string> (dễ vỡ, tránh).

Lưu ý bậc: path dùng [] rỗng (vd items[].steps[].items[].n) là path KIỂU — định danh FIELD/khái niệm ("tên task"), KHÔNG định danh 1 dòng dữ liệu cụ thể. Đúng độ hạt cho sổ khái niệm sửa-rẻ (kê KHÁI NIỆM/nhãn, không kê từng bản ghi). Vì thế slot_key ở mức config_key = cột/field, đúng thứ sửa-rẻ theo dõi.

8.3 Bền khi sửa nhãn / đảo thứ tự?

  • Sửa nhãn/giá trị, path có giữ nguyên? config_key: — sửa giá trị (code:'WF-0001'→…) hay nhãn render-từ-dữ-liệu không đổi path items[].code. text_marker: KHÔNG — đổi tên cột (Mã QT) làm marker <th>Mã</th> vỡ → đây là mép chưa bền chính.
  • Đảo thứ tự item/bước, slot có giữ? config_key path-kiểu ([] rỗng): , độc lập thứ tự. Item còn được khoá theo code (byCode[it.code], dòng 250) → business key, đảo mảng không đổi danh tính. Cảnh báo: nếu ai đó khoá theo chỉ số (items[3].steps[1].items[2]) thì đảo thứ tự VỠ — hiện scheme KHÔNG dùng chỉ số, đừng để lọt. Bước/task không có id bền (chỉ vị trí mảng hoặc nhãn) → ở mức bản-ghi không bền, nhưng ở mức field/khái niệm thì bền qua path-kiểu.

8.4 MO* dùng config-key path không? (grep, không suy diễn)

MO* đều config-object-driven (không phải DOM thuần), nhưng path grammar mỗi họ khác nhau:

Họ Đối tượng dữ liệu (bằng chứng grep) Path grammar
MOT window.MOT_DATA (mot-data-v1.js dòng 8) — keys items/rows/cols/fields/steps khác MASTER_CONFIG
MOW const D={T7:{…nodes:…}} (mow-unified-canvas-v2.html dòng 269) — nodes/items/columns theo cây T7
MOIT/MOUT/MODx builders dùng items/columns/cols/fields theo cột/field

Chiến lược config_key neo khái niệm TỔNG QUÁT hóa được cho cả 4 Mẹ (mỗi Mẹ có 1 đối tượng dữ liệu trung tâm), nhưng path cụ thể phải khai theo từng họ — không có 1 path phổ quát kiểu MASTER_CONFIG.items[]. CHƯA liệt kê từng slot MO* (ngoài phạm vi lượt này); chỉ khẳng định: config-object có, DOM-thuần thì không.

8.5 Đề xuất xử lý mép

  1. Ưu tiên config_key ở đâu có đường dữ liệu (đã làm cho field dữ liệu). Bền, không cần marker mới.
  2. Nhãn-renderer: ưu tiên code_marker (class ô/cột: qv-stt, qv-mn, qv-nm, qv-tg, qv-note, qv-grp, qv-tot) thay vì chuỗi hiển thị. Nơi header CHƯA có class (vd <th>Người thực hiện</th>) → hoặc dựa vào anh em config_key cùng khái niệm (đã sẵn: cả steps[].lam lẫn <th>Người thực hiện</th> cùng duplicate_group:'role.nguoi_thuc_hien'), hoặc thêm marker nhẹ (1 class / data-ck).
  3. KHÔNG tạo hệ marker mới khi config path + class CSS sẵn + duplicate_group đã phủ. Chỉ thêm marker nhẹ cho nhãn-renderer vừa không có config path VỪA không có class — và cả khi đó vẫn là tuỳ chọn (concept đã được duplicate_group giữ).
  4. Tinh chỉnh BẮT BUỘC trước import (xem §12): item_key/slot_key phải băm từ anchor bền (ui_id + config_key_path hoặc + code_marker), loại raw_label và chỉ số mảng ra khỏi key. Nếu để raw_label trong key (như phác thảo item_key=hash(…,raw_label) bản 07-06), thì sửa nhãn = xoá+thêm, phá đúng nguyên tắc "sửa 1 nhãn không làm rung 30 item". Neo bền → sửa nhãn là CHANGE trên cùng slot_key.

9. Phiếu sửa / change_scope — mẫu tối thiểu

{
  "ui_id":       "master-list-quy-trinh-v1",
  "ui_file":     "master-list.js",
  "slot_key":    "master-list-quy-trinh-v1::code_marker:qv-note",
  "kind":        "edit",
  "old_label":   "Ghi chú",
  "new_label":   "Ghi chú nội bộ",
  "old_purpose": "Chú thích tự do cho task (mock rỗng).",
  "new_purpose": "Chú thích nội bộ cho task, chỉ admin thấy.",
  "reason":      "phân biệt ghi chú công khai vs nội bộ",
  "requester":   "cowork:huyen",
  "at":          "2026-07-07T10:00:00Z"
}
  • slot_key = ui_id::<kind>:<key|marker> — đủ để Agent định vị 1 slot, không quét toàn file (B3/B6).
  • kind ∈ {add, edit, delete}. add → không cần old_; delete → không cần new_.
  • old_purpose/new_purpose tuỳ có; purpose đo ≥15 khi import (FAIL nếu <15).

Chỗ lưu tối giản cho G1 — chọn: file cạnh UI ui-preview/change-scope/<ui_id>.jsonl (append 1 dòng/phiếu).

Nơi Rẻ? Phù hợp G1? Lý do
File cạnh UI (JSONL) Rẻ nhất Chọn Cùng thư mục Cowork ĐÃ ghi UI (MCP-writes); 0 hạ tầng mới; đúng chỗ phát sinh sửa; là input Agent render
KB document Trung Thay thế Bền + tìm kiếm được, nhưng nặng khi append từng phiếu; dùng nếu cần lưu chéo-phiên
PG (bảng change_scope) Đắt Phase sau Có cấu trúc nhất nhưng phiếu là INPUT (có trước row) → thêm bảng = thêm infra; nâng cấp khi lượng lớn
Chỉ trong prompt Rẻ Không Không lưu vết, không tái lập, không audit

Phiếu là input vận hành tạm (transient), KHÔNG phải SoT. SoT = bảng nháp PG sau import. Nên file JSONL co-located là đúng; lên PG change_scope chỉ khi volume lớn.


10. Review report — mẫu tối thiểu cho Owner

Chỉ đưa review + candidate + delta mới; KHÔNG liệt kê matched. Là 1 SELECT trên bảng nháp WHERE is_current AND (match_status IN ('review','candidate') OR <đổi/ mới so scan trước>).

BẢNG DUYỆT SỬA-RẺ · UI master-list-quy-trinh-v1 · scan 2026-07-07 · 3 mục cần Owner

#  slot_key                                   nhãn đang dùng        gợi ý map (near)            vì sao                          Owner chọn?
1  …::config_key:items[].steps[].agent        Cờ tự động (agent)    ~ role.nguoi_thuc_hien(med) tính-chất tự-động của bước,      [ ]map_existing
      (review · co_tu_dong)                                                                     chưa chắc là khái niệm riêng      [ ]create_new  [ ]keep_pending
2  …::config_key:items[].links                Liên kết 4 Mẹ         ~ workflow (low)            quan hệ lắp ráp giữa các Mẹ,      [ ]map_existing
      (candidate · lien_ket_me)                                                                 chưa chốt type relation           [ ]create_new  [ ]keep_pending
3  …::code_marker:qv-note                      Ghi chú → Ghi chú nội  (delta: purpose đổi)       nhãn+purpose vừa sửa (phiếu B1)   [ ]map_existing
      (delta · đổi purpose)                     bộ                                                                                 [ ]create_new  [ ]keep_pending

Mỗi dòng trả lời đủ: dòng nào · UI nào · slot nào · nhãn đang dùng · gợi ý concept + vì sao · Owner cần chọn (map_existing / create_new / keep_pending). (Có thể tái dùng khung renderUIContract lọc match_status≠matched.)


11. Cái đã có / cần sửa / cần làm mới

A. ĐÃ CÓ — dùng ngay:

  • dot-pg-atomic-apply (create-core/import/decision).
  • allowlist /opt/incomex/dot/sql/ + manifest TSV (nhận nhánh con sua-re/).
  • cơ chế SELECT/VIEW + ILIKE cho match & report (read-only).
  • TEMPLATE-DOT-SCRIPT (để dành cho PA2 phase sau).
  • tiền lệ balo-core-first-run.sql làm mẫu .sql.

B. ĐÃ CÓ nhưng cần sửa/điều chỉnh cách dùng:

  • Công thức item_key/slot_key: đổi sang neo anchor bền, loại raw_label+chỉ số (§8.5-4). Là chỉnh công thức author-time, không sửa hạ tầng.
  • Nhãn-renderer trong master-list.js: nơi thiếu class nên thêm marker nhẹ hoặc dựa duplicate_group (§8.5) — nếu muốn slot header bền. Tuỳ chọn, không chặn G1.

C. CẦN LÀM MỚI (đều KHÔNG phải DOT):

  • 3 SQL template trong sua-re/: create-core.sql, import-delta.sql (khuôn), decision.sql + dòng manifest.
  • Logic Agent render JSON→SQL (scan artifact/phiếu → INSERT/UPDATE).
  • File change-scope/<ui_id>.jsonl (phiếu sửa).
  • VIEW v_sua_re_delta (hoặc chạy SELECT trực tiếp).

D. CHƯA CHẮC — cần khảo sát tiếp (khi tới):

  • Liệt kê slot từng UI của MO* (MOW/MOT/MOIT/MOUT) + path grammar mỗi họ (8.4 mới xác cấu trúc).
  • Promote nháp→bảng chính thức + DOT/.sql promote (bản 07-06 §F để phase sau).
  • Có nên lên PA2 (dot-sua-re nhiều action) không — quyết sau khi G1 chạy vài vòng.

12. Blocker thật

KHÔNG có blocker hạ tầng chặn G1. Chỉ 1 điểm kỹ thuật phải xử trước khi import (không mở kiến trúc mới):

  1. item_key/slot_key phải neo anchor bền, loại raw_label + chỉ số mảng.
    • Bằng chứng: phác thảo 07-06 để item_key=hash(ui_id,scan_id,location_or_index,raw_label) — chứa raw_label (và scan_id). Nếu key chứa nhãn thì sửa nhãn = item_key đổi = xoá+thêm, vi phạm nguyên tắc "sửa 1 nhãn không làm rung 30 item" và làm delta §D-6/§D-7 (item mới / đổi purpose) không định danh được slot xuyên scan.
    • Cách xử (chỉnh công thức, author-time): slot_key = ui_id + location_anchor (config_key path / code_marker), không gồm raw_label/scan_id/index. raw_label, purpose là THUỘC TÍNH của slot (được version theo is_current), không phải thành phần khoá. → sửa nhãn = CHANGE trên cùng slot_key; slot khác đứng yên.
    • Không chặn hạ tầng — bảng/gate không đổi; chỉ là quy ước sinh khoá lúc render .sql.

Ghi nhận (không phải blocker):

  • Manifest sửa tay, không có tool add (§7) — là tính năng (cổng người-duyệt), không phải thiếu sót.
  • dot-report-publish không phục vụ được draft PG-only (buộc ^v_report_ + Directus base-table) — không sao, G1 report = SELECT read-only.
  • Slot header MO* chưa liệt kê (§8.4) — ngoài phạm vi, không chặn pilot master-list.

13. Chốt

Chỉ khảo sát. 0 DOT tạo · 0 bảng tạo · 0 ghi PG · 0 sửa UI · 0 commit · 0 SQL ghi. Mọi thao tác = read-only (source DOT, UI seed, PG SELECT). Verdict: PA3 — vận hành sửa-rẻ G1 bằng dot-pg-atomic-apply + 3 SQL template đã duyệt + report SELECT read-only; phiếu sửa = JSONL cạnh UI; slot_key neo anchor bền. PA2 để dành nâng cấp phase sau; PA1 loại. 1 tinh chỉnh công thức khoá phải làm trước import (§12).