Chuẩn format báo cáo sau khi quét UI — v0.2 (bản thử)
Chuẩn format báo cáo sau khi quét UI — v0.2 (bản thử)
Trạng thái: v0.2 — BẢN THỬ, chưa chốt. Khuôn này rút từ dữ liệu thật của UI
master-list-quy-trinh-v1(config-driven) và đối chiếu 30 slot đang có trong PG. Nó sẽ được chỉnh sau đợt quét thử 2 UI (master-list,mow-unified-canvas) vì canvas có thể thuộc dạng UI khác. Đừng coi đây là chuẩn cuối cho tới khi có ghi "v1.0 — CHỐT". v0.2 bổ sung so với v0.1: file evidence riêng (mục 8); chuẩn hóaraw_label/purpose(mục 2b); luậtanchor_diffkhông tự sửa (mục 6); quy tắc quét độc lập (mục 1b).
0. Mục đích
Một khuôn chung để mọi bên quét UI đều xuất ra cùng một dạng, nhờ đó máy so sánh (diff) chỉ báo khác biệt thật, không báo nhiễu hình thức.
Các bên phải theo khuôn này:
- Agent quét (Claude Code, Codex…).
- DOT scanner (khi viết ở bước sau).
Lưu ý quan trọng — đây KHÔNG phải format của PG. Format quét chỉ chứa thứ quét ra được. PG có thêm nhiều cột chỉ sinh ra sau khi ghi sổ (id, scan_id, is_current, concept_id, dedupe_key, owner_decision…). Quan hệ giữa hai bên là phép chiếu ở mục 5, không phải bắt hai bên giống hệt nhau.
1. Định dạng file
- Một file JSONL: mỗi dòng một slot, một object JSON.
- Encoding UTF-8, tiếng Việt có dấu để nguyên.
- Sắp xếp: theo
slot_keytăng dần (abc). Bắt buộc — để hai bản luôn cùng trật tự, diff mới sạch. - Tên file:
<ui_id>__<who>.jsonlvớiwho∈ {claude,codex,dot} — giá trị cố định, viết thường, không đặt tên khác (vdmaster-list-quy-trinh-v1__claude.jsonl). - Lưu theo
run_idđể không ghi đè lần chạy trước. Đường dẫn:/opt/incomex/docs/mcp-writes/sua-re/scans/manual-baseline/<run_id>/<ui_id>/<ui_id>__<who>.jsonlvà file evidence cùng thư mục.run_idchỉ nằm ở path, KHÔNG đưa vào từng dòng JSONL — nếu nhét vào dòng thì hai lần chạy khácrun_idsẽ làm mọi dòng khác nhau, diff nhiễu toàn bộ.
1b. Quét độc lập (bắt buộc)
Mỗi bên quét (Claude Code, Codex) tự quét, KHÔNG đọc file kết quả của bên kia trước khi hoàn thành bản của mình. Mục đích của việc quét đôi là bắt "cả hai cùng thiếu"; nếu một bên nhìn bài bên kia thì mất hết tác dụng đó. Chỉ sau khi cả hai đã nộp file mới được so.
2. Các trường của một dòng (đúng thứ tự, đủ trường)
| Trường | Bắt buộc | Giá trị |
|---|---|---|
ui_id |
có | định danh UI, vd master-list-quy-trinh-v1 |
location_anchor |
có | địa chỉ bền của slot (mục 3) |
slot_key |
có | ui_id::location_anchor (ghép đúng, không thêm bớt) |
anchor_type |
có | config_key | code_marker | text_marker |
source_layer |
có | config | js | dom | text |
raw_label |
có | nhãn hiển thị lúc quét (chỉ để người đọc, KHÔNG phải khóa) |
kind |
có | concept (vào sổ khái niệm) | flow_data (dữ liệu luồng, KHÔNG vào sổ) |
purpose |
có | mô tả mục đích, hoặc "" nếu chưa rõ |
purpose_status |
có | explicit | inferred | unknown |
Không thêm trường tự phát. Cần trường mới → đề xuất ở report, không nhét vào dòng.
Cách viết "rỗng" — chỉ một, bắt buộc (chống nhiễu-giả):
- Cấm
null. Cấm bỏ thiếu field. Mỗi dòng luôn đủ đúng 9 field. - Giá trị chưa rõ → dùng chuỗi rỗng
""(vớipurposethì kèmpurpose_status="unknown"). Không bên nào được dùngnullhay bỏ trống, vìnull/""/ thiếu-field là ba dạng khác nhau, máy diff sẽ báo lệch giả. - Serialize 9 field đúng thứ tự bảng trên (ui_id → location_anchor → slot_key → anchor_type → source_layer → raw_label → kind → purpose → purpose_status). JSON không coi thứ tự key là nghĩa, nhưng xuất cùng thứ tự thì diff dòng-theo-dòng mới sạch.
2b. Chuẩn hóa raw_label và purpose (chống nhiễu-giả)
raw_label: giữ nguyên nhãn nhìn thấy, nhưng trim khoảng trắng đầu/cuối và gộp khoảng trắng liên tiếp thành một. Không đổi chữ hoa/thường, không dịch.purpose: viết ngắn, tối đa một câu. Nếu không chắc →purpose=""vàpurpose_status="unknown". Không bịa purpose cho đẹp báo cáo — thà đểunknown.purpose_status:explicitnếu nguồn (config/comment/code) nói rõ;inferrednếu suy nhẹ từ tên field nhưng chưa chắc;unknownnếu không có căn cứ.
2c. Kiểm chéo anchor_type ↔ tiền tố location_anchor
Hai trường này phải nhất quán, vì location_anchor đã mang tiền tố loại:
location_anchorbắt đầuconfig_key:→anchor_typephải =config_key.- bắt đầu
code_marker:→anchor_type=code_marker. - bắt đầu
text_marker:→anchor_type=text_marker.
Lệch giữa hai trường (vd anchor config_key:... mà anchor_type="text_marker") → file fail format, phải sửa trước khi nộp. Đây là loại sai âm thầm, không bắt bằng luật thì file "trông đúng" mà thật ra hỏng.
3. Luật dựng location_anchor (phần chống lệch-giả quan trọng nhất)
Anchor phải có tiền tố loại, theo thứ tự ưu tiên:
config_key:<đường-dẫn-trong-config>— bền nhất. Vdconfig_key:MASTER_CONFIG.items[].maker.code_marker:<marker-ổn-định>— cho slot tính toán/hiển thị nằm trong JS. Vdcode_marker:qv-tot.text_marker:<chuỗi-hiển-thị>— yếu nhất, chỉ khi không có gì bền hơn.
Quy tắc chuẩn hóa bắt buộc (hai bên sai lệch nhau ở đây là hỏng diff):
- Mảng luôn viết
[]rỗng, KHÔNG số. Đúng:items[].steps[].items[].min. Sai:items[0].steps[2].items[1].min. Một thuộc tính xuất hiện ở 1.000 phần tử vẫn là một slot. - Nhiều leaf luôn đi cùng nhau và cùng một điểm trên UI → gộp về object cha, KHÔNG tách leaf. Vd
items[].maker(không tách.t/.n),items[].links(không tách.mot/.moit/.mout). (Đây là bài học thật từ master-list: PG neo ở object-level.) - KHÔNG đưa
raw_labelvào anchor/slot_key. Nhãn đổi được; địa chỉ thì không. - KHÔNG dùng chỉ số, id runtime, hay giá trị dữ liệu trong anchor.
4. Ranh giới kind: khái niệm UI vs dữ liệu luồng
concept— loại thuộc tính: mã quy trình, số phút, trạng thái, người thực hiện, tổng phút… Cái này vào sổ khái niệm.flow_data— nội dung cụ thể của một luồng: "WF-0001 gồm task nào", "task A mấy phút", input/output của một task cụ thể. Quét ra thì ghi nhận vớikind=flow_datađể đối chiếu, nhưng không đưa vào sổ khái niệmsua_re_*.
Phân biệt nhanh: gộp [] xong mà vẫn là một loại thông tin → concept. Nếu là một bản ghi/giá trị cụ thể → flow_data.
5. Phép chiếu sang PG (để đối chiếu, KHÔNG phải để trùng khuôn)
Khi so bản quét với sổ PG (sua_re_ui_khai_niem_nhap), ghép theo:
| Bản quét | ↔ | PG |
|---|---|---|
slot_key |
= | slot_key |
location_anchor |
= | location_anchor |
raw_label |
~ | raw_label (chỉ tham khảo, có thể khác do đổi nhãn) |
kind=concept |
→ | dòng usage trong sổ |
kind=flow_data |
→ | KHÔNG có trong sổ (đúng) |
Các cột PG không có trong bản quét (đúng, không phải lỗi): id, item_key, scan_id, is_current, concept_id, owner_concept_id, dedupe_key, match_status, owner_decision. Chúng sinh ra ở bước ghi sổ, không phải bước quét.
6. Kết quả so sánh (diff) — các nhóm
Khi so hai bản quét (hoặc quét-vs-PG), mỗi slot rơi vào đúng một nhóm:
match— cùngslot_key, cùngkind.only_A/only_B— chỉ một bên có (bên kia sót, hoặc là slot thật mới).anchor_diff— cùng chỗ trên UI nhưng hai bên đặt anchor khác cấu trúc (vd một bên gộp object, một bên tách leaf). Đây là chưa thống nhất quy tắc, không phải thiếu.label_or_purpose_diff— cùngslot_keynhưngraw_label/purposekhác.kind_diff— cùngslot_keynhưng một bên gọiconcept, bên kiaflow_data. Phải xử, vì ảnh hưởng có vào sổ hay không.
anchor_diff và kind_diff KHÔNG được agent tự sửa cho khớp. Khi hai bản lệch kiểu object-level vs leaf-level (hoặc concept vs flow_data), agent không được tự chọn bên thắng rồi sửa bản kia cho bằng. Ghi nguyên trạng vào diff, nêu lý do mỗi bên, để Owner/quy tắc quyết. Chỉ khi lệch rõ ràng là một bên sót (only_A/only_B) thì mới là chuyện thiếu-đủ.
Diff chỉ được phản ánh khác biệt thật. Nếu diff báo lệch mà soi ra do format (thứ tự, tiền tố, khoảng trắng) → đó là lỗi format, sửa khuôn, không phải lệch nội dung.
7. Câu nhớ
Cùng khuôn — cùng trật tự — anchor cùng luật, thì diff mới nói thật.
Quét ra concept với flow_data; sổ chỉ nhận concept.
Format quét ≠ format PG; nối nhau bằng slot_key, không bắt giống hệt.
Lệch anchor/kind thì hỏi Owner, không tự sửa cho bằng nhau.
8. File evidence đi kèm (bắt buộc, tách khỏi JSONL)
Mỗi UI, mỗi bên quét, ngoài file JSONL phải nộp thêm một file evidence:
<ui_id>__<who>.evidence.md
Ghi ngắn gọn (không nhét vào JSONL để diff khỏi rối):
- Nguồn đã đọc: file HTML nào, JS nào (tự resolve từ đâu), dạng UI nhận diện được (config-driven / canvas / form / builder…).
- Marker/config path chính: object gốc tên gì (vd
MASTER_CONFIG), các nhánh chính. - Slot không chắc: slot nào
only/uncertain, vì sao. - Quyết định độ hạt: chỗ nào chọn object-level, chỗ nào leaf-level, và lý do (đây là chỗ hay lệch giữa hai bên nhất).
Evidence là để người soi hiểu vì sao quét ra vậy; JSONL là để máy diff. Hai file, hai việc, không trộn.
9. Validator bắt buộc trước khi nộp
Mỗi bên quét, trước khi báo hoàn thành, phải tự chạy kiểm và chỉ nộp khi qua hết:
- Mỗi dòng parse được JSON.
- Mỗi dòng đủ đúng 9 field, không thiếu, không thừa field tự phát.
- Không có
null; giá trị rỗng chỉ dùng"". slot_key==ui_id+::+location_anchor(ghép đúng, không lệch).anchor_typekhớp tiền tố củalocation_anchor(mục 2c).location_anchorkhông chứa chỉ số mảng[<số>]như[0],[1]; mảng phải là[].- File đã sort theo
slot_keytăng dần. - Không trùng
slot_keytrong cùng file (mỗi slot đúng một dòng). - File nằm đúng path
manual-baseline/<run_id>/<ui_id>/và có file evidence đi kèm.
Riêng UI đã có dữ liệu trong PG (vd master-list-quy-trinh-v1, 30 slot is_current): thêm một kiểm mỏ-neo — đối chiếu bản quét với PG (SELECT read-only) và ghi trong evidence số khớp/lệch. Đây không phải để ép khớp, mà để phát hiện sớm nếu bản quét dựng lại không nổi cái đã biết đáp án — làm trước cả khi so hai agent với nhau.
Thiếu validator này thì agent dễ "tưởng đúng format" nhưng nộp file không so sánh được.