SUA-RE Tooling Survey — Quy trình vận hành sửa-rẻ (VERDICT PA3, 2026-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, shaab30bb8d…, 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[-_]retrên bin/sql/specs = rỗng. Đúc 5 DOT (PA1) hay 1 DOT-6-action (PA2) TRƯỚC khi.sqltemplate ổ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ọidot-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>.jsonltrongui-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, DOTdot-pg-atomic-applychỉ 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ồisua_re_khai_niem_nhapnế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? | CÓ — 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; có 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 (có 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/(hardcodeALLOW_DIR_RAW, dòng 46; kiểmcase "$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.sqlcho RELsua-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-manifesttoà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òngsha256<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ớilocation_anchorlà 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: CÓ — sửa giá trị (code:'WF-0001'→…) hay nhãn render-từ-dữ-liệu không đổi pathitems[].code.text_marker: KHÔNG — đổi tên cột (Mã→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_keypath-kiểu ([]rỗng): CÓ, độc lập thứ tự. Item còn được khoá theocode(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
- Ư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. - 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 emconfig_keycùng khái niệm (đã sẵn: cảsteps[].lamlẫn<th>Người thực hiện</th>cùngduplicate_group:'role.nguoi_thuc_hien'), hoặc thêm marker nhẹ (1 class /data-ck). - 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 đã đượcduplicate_groupgiữ). - Tinh chỉnh BẮT BUỘC trước import (xem §12):
item_key/slot_keyphải băm từ anchor bền (ui_id + config_key_pathhoặc+ code_marker), loạiraw_labelvà chỉ số mảng ra khỏi key. Nếu đểraw_labeltrong key (như phác thảoitem_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ùngslot_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_purposetuỳ 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_scopechỉ 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 consua-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.sqllà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ạiraw_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ựaduplicate_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/
.sqlpromote (bản 07-06 §F để phase sau). - Có nên lên PA2 (
dot-sua-renhiề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):
item_key/slot_keyphải neo anchor bền, loạiraw_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ứaraw_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ồmraw_label/scan_id/index.raw_label,purposelà THUỘC TÍNH của slot (được version theois_current), không phải thành phần khoá. → sửa nhãn = CHANGE trên cùngslot_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.
- Bằng chứng: phác thảo 07-06 để
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-publishkhô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).