@edupia-tutor/spec-driven-docs 0.14.4 → 0.14.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -416,6 +416,47 @@ Sau khi hoàn thành tất cả các bước, bạn đã nạp:
416
416
  Tiếp tục sang bước kế tiếp của lệnh đang gọi.
417
417
 
418
418
 
419
+ ---
420
+
421
+ ## Ngôn ngữ nghiệp vụ *(áp khi viết/áp fix — gồm cả Resume Mode Phase 2)*
422
+ # Business Language Guard — chặn thuật ngữ kỹ thuật rò vào tài liệu nghiệp vụ
423
+
424
+ Tài liệu nghiệp vụ (PRD, product-definition) mô tả **WHAT** — chỉ ngôn ngữ nghiệp vụ. Guard này chạy **mỗi khi viết hoặc sửa** prose (gen mới, áp fix `--resume`, hiệu chỉnh): **quét và xử lý** các thuật ngữ kỹ thuật/UI phổ thông bên dưới **trước khi ghi**.
425
+
426
+ > Guard này là **baseline framework**, chạy **song song** với Banned Terms của `business-dictionary.md` (cơ chế dictionary giữ nguyên; project vẫn bổ sung term đặc thù vào đó). Khi cả hai cùng áp, ưu tiên bản chuẩn của dictionary nếu có.
427
+
428
+ ## Bản đồ xử lý (3 nhóm)
429
+
430
+ **Nhóm 1 — Tương tác/triển khai → DIỄN ĐẠT LẠI sang nghiệp vụ (giữ nguyên nghĩa):**
431
+
432
+ | Kỹ thuật/UI | Cách nói nghiệp vụ |
433
+ |---|---|
434
+ | re-render / render lại / reload / refresh (màn) | "hiển thị lại {tên màn}" |
435
+ | timeout | "quá thời gian chờ" |
436
+ | lỗi mạng / network error | "lỗi kết nối" |
437
+ | UI / giao diện (khi chỉ một màn) | "màn" / "màn hình" |
438
+ | click / tap | "bấm" / "chọn" |
439
+ | popup / modal (nếu chỉ là khái niệm hiển thị) | "hộp thoại" / "thông báo" |
440
+ | disable / enable (nút) | "khoá" / "mở" thao tác |
441
+ | redirect / navigate | "chuyển tới {màn}" |
442
+
443
+ **Nhóm 2 — Visual thuần → CHUYỂN Design Spec (bỏ khỏi PRD, ghi nhận lại):**
444
+ `spinner`, `loading indicator`, `animation`, `fade/slide`, màu sắc, font, layout pixel, micro-interaction → *"Chi tiết visual này thuộc Design Spec — ghi nhận để tạo Design Spec sau."*
445
+
446
+ **Nhóm 3 — Backend/contract thuần → BỎ khỏi PRD (thuộc Tech Docs):**
447
+ `API`, `endpoint`, `token/JWT`, `HTTP status`, tên class/bảng/cột DB, query, payload, header.
448
+ *(Ngoại lệ DUY NHẤT: Appendix "Existing API Contract" khi `API Source: existing` — xem Platform Strategy.)*
449
+
450
+ ## Quy tắc áp dụng
451
+ - Quét toàn bộ text sắp ghi (User Story, AC, BR, Business Logic, Scope, Edge Cases, Assumptions…).
452
+ - Nhóm 1 → thay tại chỗ, giữ nguyên nghĩa nghiệp vụ. **Đồng bộ cách diễn đạt** với chỗ đã có sẵn trong cùng tài liệu (vd nếu "quá thời gian chờ" đã dùng ở một BR → dùng nhất quán ở mọi nơi).
453
+ - Nhóm 2 → gỡ khỏi prose nghiệp vụ + nhắc chuyển Design Spec.
454
+ - Nhóm 3 → gỡ khỏi PRD (trừ ngoại lệ brownfield).
455
+ - Nếu term không có trong bản đồ nhưng rõ ràng là tên kỹ thuật/triển khai → vẫn diễn đạt lại theo tinh thần Nhóm 1, đừng để lọt.
456
+
457
+ **Checklist (dùng ở Quality Checklist của lệnh):** 0 thuật ngữ kỹ thuật/UI (re-render, UI, timeout, spinner, API/endpoint/token…) trong prose nghiệp vụ — đã diễn đạt lại (Nhóm 1) / chuyển Design Spec (Nhóm 2) / bỏ về Tech Docs (Nhóm 3).
458
+
459
+
419
460
  ---
420
461
 
421
462
  ## Quy trình Review
@@ -427,10 +468,12 @@ dừng ở mức "đủ" findings, nên mỗi vòng review sau lại lòi ra v
427
468
  fan out song song theo các chiều review, rồi lặp một critic độ-đầy-đủ cho tới khi một
428
469
  vòng không sinh thêm gì mới, *trước khi* ghi file findings.
429
470
 
430
- Lệnh gọi cung cấp hai thứ:
471
+ Lệnh gọi cung cấp hai thứ bắt buộc + hai tuỳ chọn:
431
472
  - **DIMENSIONS** — danh sách các chiều review để fan out
432
473
  (`/refine-prd` → 4 lăng kính; `/review-context` → các P-check hoặc B-check).
433
474
  - **FINDINGS SCHEMA** — dạng YAML mà mỗi finding phải theo (định nghĩa trong lệnh).
475
+ - **GRANULARITY** *(tuỳ chọn, mặc định `auto`)* — `auto`: chọn độ mịn fan-out theo bảng ngưỡng kích thước ở Phase 1 (hành vi cũ). `per-uc`: **LUÔN** fan-out theo từng UC, **bỏ qua ngưỡng** — dùng cho review cần độ đầy đủ cao (`/refine-prd` truyền cái này để lần đầu đã quét sâu). Lệnh không truyền → `auto` → hành vi không đổi.
476
+ - **CHANGED_SCOPE** *(tuỳ chọn)* — danh sách UC/section đã thay đổi (review **delta**). Nếu được truyền, Phase 1 chỉ fan-out trên các phạm vi này + PRD-global; Phase 2 critic vẫn quét **toàn doc** làm lưới an toàn. Không truyền → quét toàn bộ như thường.
434
477
 
435
478
  > **Bỏ qua ở chế độ sub-agent:** Nếu Gate Bước 0 đã set `_agent_mode: true`, toàn bộ
436
479
  > quy trình này bị **bỏ qua** — orchestrator đã chạy sẵn một dimension/UC cho mỗi
@@ -442,8 +485,11 @@ Lệnh gọi cung cấp hai thứ:
442
485
 
443
486
  **Bao nhiêu sub-agent:** *số lượng* agent không phải là đòn bẩy độ đầy đủ — bề rộng được
444
487
  cố định bởi taxonomy DIMENSION (thêm agent vào cùng một dimension chỉ tìm lại cùng vấn đề),
445
- còn *độ sâu* thuộc về vòng lặp critic ở Phase 2. Chọn **độ mịn fan-out**
446
- theo kích thước target, tái dùng ngưỡng của `steps/spawn-agent.md`:
488
+ còn *độ sâu* thuộc về vòng lặp critic ở Phase 2.
489
+
490
+ **Nếu `GRANULARITY = per-uc`:** **bỏ qua bảng ngưỡng dưới đây**, luôn dùng độ mịn **DIMENSION × phạm vi UC** (kể cả PRD nhỏ) — đảm bảo quét sâu, không bỏ sót ngay lần đầu. (Cái giá: nhiều agent hơn cho PRD nhỏ — chấp nhận để lần đầu đầy đủ.)
491
+
492
+ **Nếu `GRANULARITY = auto`** (mặc định): chọn **độ mịn fan-out** theo kích thước target, tái dùng ngưỡng của `steps/spawn-agent.md`:
447
493
 
448
494
  | Kích thước target | Độ mịn | Số agent |
449
495
  |-------------|-------------|-------------|
@@ -466,6 +512,7 @@ UC duy nhất — chính là điều ngăn bỏ sót trên các PRD lớn.
466
512
  agent). Giới hạn mỗi wave ở **`AGENT_CAP = 12`** agent và gom batch các phạm vi UC cho vừa:
467
513
 
468
514
  1. Dựng danh sách phạm vi = `[UC1, UC2, …, UCn, PRD-global]` (độ dài `UCs + 1`).
515
+ - **Nếu `CHANGED_SCOPE` được truyền (review delta):** danh sách phạm vi = `[các UC trong CHANGED_SCOPE] + [PRD-global]` (chỉ các UC đã đổi + global), KHÔNG phải tất cả UC. Số agent tụt theo đó.
469
516
  2. Tính số-phạm-vi-mỗi-bucket: `groups = max(1, floor(AGENT_CAP / dimensions))`.
470
517
  - Nếu `groups ≥ UCs + 1` → không cần batch, chạy một agent cho mỗi `DIMENSION × scope`.
471
518
  - Else chia danh sách phạm vi thành `groups` bucket liền kề kích thước xấp xỉ bằng nhau
@@ -507,6 +554,8 @@ Gom mảng findings của mọi sub-agent vào một danh sách hợp nhất `AL
507
554
  Đây là bước chống đập-chuột-chũi. Lặp cho tới khi **hai vòng liên tiếp thêm 0 finding
508
555
  mới**, hoặc tới cap cứng **3 vòng**, cái nào đến trước:
509
556
 
557
+ > **Lưu ý delta:** kể cả khi `CHANGED_SCOPE` giới hạn Phase 1 vào các UC đã đổi, completeness-critic ở Phase 2 **vẫn đọc TOÀN bộ doc** — đây là lưới an toàn bắt các vấn đề mà một fix ở UC đã đổi có thể làm lộ ra ở chỗ khác.
558
+
510
559
  1. Spawn một sub-agent **completeness-critic** bằng Agent tool. Cho nó:
511
560
  - toàn bộ target file (`{target_file}`),
512
561
  - danh sách findings đã ghi nhận dưới dạng **slim JSON** — chỉ 3 fields cốt lõi
@@ -571,6 +620,19 @@ Convergence: {convergence_rounds} vòng critic — file findings đã đầy đ
571
620
 
572
621
  Chạy review qua **Quy trình Review** ở trên (`steps/review-fanout.md`).
573
622
 
623
+ **Tham số truyền vào Quy trình Review:**
624
+ - `GRANULARITY = per-uc` — LUÔN fan-out theo từng UC (bỏ ngưỡng cả-file), để **ngay lần đầu đã lòi phần nhiều issue**, không dồn sang lần sau.
625
+ - `CHANGED_SCOPE` — xác định theo chế độ full/delta ngay dưới đây.
626
+
627
+ **Chọn full vs delta** *(mặc định: lần đầu FULL, lần sau DELTA)*:
628
+ 1. Tách `--full` khỏi `$ARGUMENTS` nếu có.
629
+ 2. Kiểm tra `{paths.refinement_dir}/{prd-slug}-findings.yaml`:
630
+ - **Không tồn tại** (lần đầu review PRD này) → **FULL**: KHÔNG truyền `CHANGED_SCOPE`.
631
+ - **Tồn tại** + có `--full` → **FULL**: bỏ qua findings cũ, không truyền `CHANGED_SCOPE` (ép quét lại toàn bộ).
632
+ - **Tồn tại** + KHÔNG có `--full` → so `prd_version` trong findings cũ với `| **Version** |` của PRD hiện tại:
633
+ - **Bằng nhau** (PRD chưa đổi từ lần review trước) → DỪNG, báo: `"PRD chưa đổi từ v{X} (lần review gần nhất). Không có gì để review lại — dùng --full nếu vẫn muốn quét toàn bộ."`
634
+ - **Khác** (đã có `--resume` bump version) → **DELTA**: `CHANGED_SCOPE` = { `uc_id`/`section` của các finding `status: applied` trong findings cũ } ∪ { UC có trong PRD hiện tại nhưng chưa từng xuất hiện ở findings cũ }. Truyền `CHANGED_SCOPE` này vào Quy trình Review.
635
+
574
636
  **DIMENSIONS** = 4 lăng kính dưới đây — fan out một sub-agent cho mỗi lăng kính, mỗi cái quét
575
637
  toàn bộ PRD qua đúng lăng kính của nó:
576
638
 
@@ -600,6 +662,7 @@ Ghi `{paths.refinement_dir}/{prd-slug}-findings.yaml`:
600
662
 
601
663
  ```yaml
602
664
  prd_source: "{paths.specs_dir}/{domain}/{prd-slug}/{TICKET-ID}-{prd-slug}.md"
665
+ prd_version: "{đọc | **Version** | từ metadata PRD lúc sinh findings — dùng để chọn full/delta lần chạy sau}"
603
666
  generated_at: "{ISO datetime}"
604
667
  status: "pending_review"
605
668
 
@@ -612,6 +675,11 @@ findings:
612
675
  quote: "{trích đoạn nguyên văn copy CHÍNH XÁC từ PRD tại vị trí lỗi, ≤120 ký tự}"
613
676
  finding: "{mô tả gap hoặc vấn đề}"
614
677
  suggestion: "{đề xuất cải thiện cụ thể, hành động được}"
678
+ resolution_edge_cases: # CHỈ điền cho critical/major; minor → để [] (bỏ qua)
679
+ # Phân tích bậc-hai (advisory, KHÔNG chặn): nếu áp `suggestion` này thì có thể đẻ ra
680
+ # edge case / side-effect gì — path lỗi mới, va chạm với BR/UC khác, trạng thái biên,
681
+ # hệ luỵ cross-section. PO đọc để cân nhắc trước khi accept; nếu muốn xử lý → tạo finding mới.
682
+ - "{edge case có thể phát sinh nếu chốt phương án này}"
615
683
  auto_fixable: false
616
684
  # true = AI tự tin cao vào suggestion này; Review Board có thể hiển thị nút "quick accept"
617
685
  # false = cần human đọc kỹ và ghi quyết định trước khi accept
@@ -641,6 +709,14 @@ summary:
641
709
  > `uc_id` là Use Case sở hữu (hoặc `""` cho finding PRD-global). Hai field này cho phép reviewer click một
642
710
  > finding trong Review Board và nhảy thẳng tới đúng vị trí trong PRD nguồn.
643
711
 
712
+ > **`resolution_edge_cases` — phân tích bậc-hai (chỉ critical/major).**
713
+ > Với mỗi finding `critical`/`major`, sau khi viết `suggestion`, nghĩ tiếp: *nếu PO chốt phương án này thì
714
+ > đẻ ra edge case / side-effect gì?* (path lỗi mới, va chạm BR/UC khác, trạng thái biên, hệ luỵ cross-section).
715
+ > Ghi vào `resolution_edge_cases` để PO **thấy trước khi accept**. Đây là **advisory** — KHÔNG chặn, KHÔNG tự
716
+ > tạo finding; PO đọc rồi quyết. Finding `minor` → để `[]`.
717
+ > *(Phần phản hồi cho phương án PO **tự sửa** (`modified`) đến ở vòng sau: sau `--resume`, lần `/refine-prd`
718
+ > kế chạy delta sẽ quét lại UC đã đổi + critic toàn-doc → tự lòi edge case mà phương án đó tạo ra.)*
719
+
644
720
  ## Report
645
721
 
646
722
  # Report Footer — Định dạng output chuẩn cho mọi lệnh
@@ -802,6 +878,7 @@ Nếu có finding nào có `status: "needs_discussion"`, thêm warning block sau
802
878
  Với mỗi finding được chấp nhận, theo thứ tự severity (critical → major → minor):
803
879
  - Đi tới section PRD được chỉ định bởi `finding.section`.
804
880
  - Áp dụng `finding.suggestion`. Với finding `status: "modified"`, người đã sửa sẵn `finding.suggestion` trong Review Board — giá trị đã sửa đó CHÍNH LÀ bản fix cần áp dụng.
881
+ - **Chạy Business Language Guard trên text mới TRƯỚC khi ghi** (xem section "Ngôn ngữ nghiệp vụ"): diễn đạt lại / gỡ thuật ngữ kỹ thuật-UI để fix không tự kéo theo term kỹ thuật vào PRD.
805
882
  - Không thay đổi bất kỳ section nào không được tham chiếu bởi một finding được chấp nhận.
806
883
 
807
884
  ### Phase 2.5 — Cập nhật file findings
@@ -824,7 +901,7 @@ Cập nhật `status: "applied"` ở **root level** của file findings (không
824
901
  - `| **Version** | {new_version} |`
825
902
  - `| **Updated** | {today YYYY-MM-DD} |`
826
903
  - `| **Status** | draft |` ← reset về draft, phải được duyệt lại
827
- 5. Thêm một row mới lên đầu `## Changelog` ở cuối PRD:
904
+ 5. Thêm một row mới lên đầu `# Change Log` ở cuối PRD:
828
905
  ```
829
906
  | {new_version} | {today} | {tóm tắt súc tích mọi thay đổi đã áp dụng} |
830
907
  ```
@@ -839,6 +916,8 @@ Changes :
839
916
  - {change 1}
840
917
  - {change 2}
841
918
 
919
+ 💡 Vừa áp {N} phương án (gồm bản PO tự sửa) → chạy lại /refine-prd {prd-file}
920
+ (delta tự động) để lòi edge case mà các phương án vừa áp có thể tạo ra.
842
921
  ⚠️ BDD có thể đã lỗi thời. Chạy:
843
922
  /review-context {prd-file} ← kiểm tra chất lượng PRD trước
844
923
  → /generate-bdd {prd-file}
@@ -23,6 +23,11 @@ Nếu `$ARGUMENTS` không chứa `--resume` → tiếp tục luồng review bìn
23
23
 
24
24
  ---
25
25
 
26
+ ## Ngôn ngữ nghiệp vụ *(áp khi viết/áp fix — gồm cả Resume Mode Phase 2)*
27
+ {{include:steps/business-language.md}}
28
+
29
+ ---
30
+
26
31
  ## Quy trình Review
27
32
  {{include:steps/review-fanout.md}}
28
33
 
@@ -32,6 +37,19 @@ Nếu `$ARGUMENTS` không chứa `--resume` → tiếp tục luồng review bìn
32
37
 
33
38
  Chạy review qua **Quy trình Review** ở trên (`steps/review-fanout.md`).
34
39
 
40
+ **Tham số truyền vào Quy trình Review:**
41
+ - `GRANULARITY = per-uc` — LUÔN fan-out theo từng UC (bỏ ngưỡng cả-file), để **ngay lần đầu đã lòi phần nhiều issue**, không dồn sang lần sau.
42
+ - `CHANGED_SCOPE` — xác định theo chế độ full/delta ngay dưới đây.
43
+
44
+ **Chọn full vs delta** *(mặc định: lần đầu FULL, lần sau DELTA)*:
45
+ 1. Tách `--full` khỏi `$ARGUMENTS` nếu có.
46
+ 2. Kiểm tra `{paths.refinement_dir}/{prd-slug}-findings.yaml`:
47
+ - **Không tồn tại** (lần đầu review PRD này) → **FULL**: KHÔNG truyền `CHANGED_SCOPE`.
48
+ - **Tồn tại** + có `--full` → **FULL**: bỏ qua findings cũ, không truyền `CHANGED_SCOPE` (ép quét lại toàn bộ).
49
+ - **Tồn tại** + KHÔNG có `--full` → so `prd_version` trong findings cũ với `| **Version** |` của PRD hiện tại:
50
+ - **Bằng nhau** (PRD chưa đổi từ lần review trước) → DỪNG, báo: `"PRD chưa đổi từ v{X} (lần review gần nhất). Không có gì để review lại — dùng --full nếu vẫn muốn quét toàn bộ."`
51
+ - **Khác** (đã có `--resume` bump version) → **DELTA**: `CHANGED_SCOPE` = { `uc_id`/`section` của các finding `status: applied` trong findings cũ } ∪ { UC có trong PRD hiện tại nhưng chưa từng xuất hiện ở findings cũ }. Truyền `CHANGED_SCOPE` này vào Quy trình Review.
52
+
35
53
  **DIMENSIONS** = 4 lăng kính dưới đây — fan out một sub-agent cho mỗi lăng kính, mỗi cái quét
36
54
  toàn bộ PRD qua đúng lăng kính của nó:
37
55
 
@@ -61,6 +79,7 @@ Ghi `{paths.refinement_dir}/{prd-slug}-findings.yaml`:
61
79
 
62
80
  ```yaml
63
81
  prd_source: "{paths.specs_dir}/{domain}/{prd-slug}/{TICKET-ID}-{prd-slug}.md"
82
+ prd_version: "{đọc | **Version** | từ metadata PRD lúc sinh findings — dùng để chọn full/delta lần chạy sau}"
64
83
  generated_at: "{ISO datetime}"
65
84
  status: "pending_review"
66
85
 
@@ -73,6 +92,11 @@ findings:
73
92
  quote: "{trích đoạn nguyên văn copy CHÍNH XÁC từ PRD tại vị trí lỗi, ≤120 ký tự}"
74
93
  finding: "{mô tả gap hoặc vấn đề}"
75
94
  suggestion: "{đề xuất cải thiện cụ thể, hành động được}"
95
+ resolution_edge_cases: # CHỈ điền cho critical/major; minor → để [] (bỏ qua)
96
+ # Phân tích bậc-hai (advisory, KHÔNG chặn): nếu áp `suggestion` này thì có thể đẻ ra
97
+ # edge case / side-effect gì — path lỗi mới, va chạm với BR/UC khác, trạng thái biên,
98
+ # hệ luỵ cross-section. PO đọc để cân nhắc trước khi accept; nếu muốn xử lý → tạo finding mới.
99
+ - "{edge case có thể phát sinh nếu chốt phương án này}"
76
100
  auto_fixable: false
77
101
  # true = AI tự tin cao vào suggestion này; Review Board có thể hiển thị nút "quick accept"
78
102
  # false = cần human đọc kỹ và ghi quyết định trước khi accept
@@ -102,6 +126,14 @@ summary:
102
126
  > `uc_id` là Use Case sở hữu (hoặc `""` cho finding PRD-global). Hai field này cho phép reviewer click một
103
127
  > finding trong Review Board và nhảy thẳng tới đúng vị trí trong PRD nguồn.
104
128
 
129
+ > **`resolution_edge_cases` — phân tích bậc-hai (chỉ critical/major).**
130
+ > Với mỗi finding `critical`/`major`, sau khi viết `suggestion`, nghĩ tiếp: *nếu PO chốt phương án này thì
131
+ > đẻ ra edge case / side-effect gì?* (path lỗi mới, va chạm BR/UC khác, trạng thái biên, hệ luỵ cross-section).
132
+ > Ghi vào `resolution_edge_cases` để PO **thấy trước khi accept**. Đây là **advisory** — KHÔNG chặn, KHÔNG tự
133
+ > tạo finding; PO đọc rồi quyết. Finding `minor` → để `[]`.
134
+ > *(Phần phản hồi cho phương án PO **tự sửa** (`modified`) đến ở vòng sau: sau `--resume`, lần `/refine-prd`
135
+ > kế chạy delta sẽ quét lại UC đã đổi + critic toàn-doc → tự lòi edge case mà phương án đó tạo ra.)*
136
+
105
137
  ## Report
106
138
 
107
139
  {{include:steps/report-footer.md}}
@@ -163,6 +195,7 @@ Nếu có finding nào có `status: "needs_discussion"`, thêm warning block sau
163
195
  Với mỗi finding được chấp nhận, theo thứ tự severity (critical → major → minor):
164
196
  - Đi tới section PRD được chỉ định bởi `finding.section`.
165
197
  - Áp dụng `finding.suggestion`. Với finding `status: "modified"`, người đã sửa sẵn `finding.suggestion` trong Review Board — giá trị đã sửa đó CHÍNH LÀ bản fix cần áp dụng.
198
+ - **Chạy Business Language Guard trên text mới TRƯỚC khi ghi** (xem section "Ngôn ngữ nghiệp vụ"): diễn đạt lại / gỡ thuật ngữ kỹ thuật-UI để fix không tự kéo theo term kỹ thuật vào PRD.
166
199
  - Không thay đổi bất kỳ section nào không được tham chiếu bởi một finding được chấp nhận.
167
200
 
168
201
  ### Phase 2.5 — Cập nhật file findings
@@ -185,7 +218,7 @@ Cập nhật `status: "applied"` ở **root level** của file findings (không
185
218
  - `| **Version** | {new_version} |`
186
219
  - `| **Updated** | {today YYYY-MM-DD} |`
187
220
  - `| **Status** | draft |` ← reset về draft, phải được duyệt lại
188
- 5. Thêm một row mới lên đầu `## Changelog` ở cuối PRD:
221
+ 5. Thêm một row mới lên đầu `# Change Log` ở cuối PRD:
189
222
  ```
190
223
  | {new_version} | {today} | {tóm tắt súc tích mọi thay đổi đã áp dụng} |
191
224
  ```
@@ -200,6 +233,8 @@ Changes :
200
233
  - {change 1}
201
234
  - {change 2}
202
235
 
236
+ 💡 Vừa áp {N} phương án (gồm bản PO tự sửa) → chạy lại /refine-prd {prd-file}
237
+ (delta tự động) để lòi edge case mà các phương án vừa áp có thể tạo ra.
203
238
  ⚠️ BDD có thể đã lỗi thời. Chạy:
204
239
  /review-context {prd-file} ← kiểm tra chất lượng PRD trước
205
240
  → /generate-bdd {prd-file}
@@ -410,6 +410,47 @@ Sau khi hoàn thành tất cả các bước, bạn đã nạp:
410
410
  Tiếp tục sang bước kế tiếp của lệnh đang gọi.
411
411
 
412
412
 
413
+ ---
414
+
415
+ ## Ngôn ngữ nghiệp vụ *(áp khi viết — Fix Mode & Resume Mode)*
416
+ # Business Language Guard — chặn thuật ngữ kỹ thuật rò vào tài liệu nghiệp vụ
417
+
418
+ Tài liệu nghiệp vụ (PRD, product-definition) mô tả **WHAT** — chỉ ngôn ngữ nghiệp vụ. Guard này chạy **mỗi khi viết hoặc sửa** prose (gen mới, áp fix `--resume`, hiệu chỉnh): **quét và xử lý** các thuật ngữ kỹ thuật/UI phổ thông bên dưới **trước khi ghi**.
419
+
420
+ > Guard này là **baseline framework**, chạy **song song** với Banned Terms của `business-dictionary.md` (cơ chế dictionary giữ nguyên; project vẫn bổ sung term đặc thù vào đó). Khi cả hai cùng áp, ưu tiên bản chuẩn của dictionary nếu có.
421
+
422
+ ## Bản đồ xử lý (3 nhóm)
423
+
424
+ **Nhóm 1 — Tương tác/triển khai → DIỄN ĐẠT LẠI sang nghiệp vụ (giữ nguyên nghĩa):**
425
+
426
+ | Kỹ thuật/UI | Cách nói nghiệp vụ |
427
+ |---|---|
428
+ | re-render / render lại / reload / refresh (màn) | "hiển thị lại {tên màn}" |
429
+ | timeout | "quá thời gian chờ" |
430
+ | lỗi mạng / network error | "lỗi kết nối" |
431
+ | UI / giao diện (khi chỉ một màn) | "màn" / "màn hình" |
432
+ | click / tap | "bấm" / "chọn" |
433
+ | popup / modal (nếu chỉ là khái niệm hiển thị) | "hộp thoại" / "thông báo" |
434
+ | disable / enable (nút) | "khoá" / "mở" thao tác |
435
+ | redirect / navigate | "chuyển tới {màn}" |
436
+
437
+ **Nhóm 2 — Visual thuần → CHUYỂN Design Spec (bỏ khỏi PRD, ghi nhận lại):**
438
+ `spinner`, `loading indicator`, `animation`, `fade/slide`, màu sắc, font, layout pixel, micro-interaction → *"Chi tiết visual này thuộc Design Spec — ghi nhận để tạo Design Spec sau."*
439
+
440
+ **Nhóm 3 — Backend/contract thuần → BỎ khỏi PRD (thuộc Tech Docs):**
441
+ `API`, `endpoint`, `token/JWT`, `HTTP status`, tên class/bảng/cột DB, query, payload, header.
442
+ *(Ngoại lệ DUY NHẤT: Appendix "Existing API Contract" khi `API Source: existing` — xem Platform Strategy.)*
443
+
444
+ ## Quy tắc áp dụng
445
+ - Quét toàn bộ text sắp ghi (User Story, AC, BR, Business Logic, Scope, Edge Cases, Assumptions…).
446
+ - Nhóm 1 → thay tại chỗ, giữ nguyên nghĩa nghiệp vụ. **Đồng bộ cách diễn đạt** với chỗ đã có sẵn trong cùng tài liệu (vd nếu "quá thời gian chờ" đã dùng ở một BR → dùng nhất quán ở mọi nơi).
447
+ - Nhóm 2 → gỡ khỏi prose nghiệp vụ + nhắc chuyển Design Spec.
448
+ - Nhóm 3 → gỡ khỏi PRD (trừ ngoại lệ brownfield).
449
+ - Nếu term không có trong bản đồ nhưng rõ ràng là tên kỹ thuật/triển khai → vẫn diễn đạt lại theo tinh thần Nhóm 1, đừng để lọt.
450
+
451
+ **Checklist (dùng ở Quality Checklist của lệnh):** 0 thuật ngữ kỹ thuật/UI (re-render, UI, timeout, spinner, API/endpoint/token…) trong prose nghiệp vụ — đã diễn đạt lại (Nhóm 1) / chuyển Design Spec (Nhóm 2) / bỏ về Tech Docs (Nhóm 3).
452
+
453
+
413
454
  ---
414
455
 
415
456
  ## Phát hiện Review Mode
@@ -438,10 +479,12 @@ dừng ở mức "đủ" findings, nên mỗi vòng review sau lại lòi ra v
438
479
  fan out song song theo các chiều review, rồi lặp một critic độ-đầy-đủ cho tới khi một
439
480
  vòng không sinh thêm gì mới, *trước khi* ghi file findings.
440
481
 
441
- Lệnh gọi cung cấp hai thứ:
482
+ Lệnh gọi cung cấp hai thứ bắt buộc + hai tuỳ chọn:
442
483
  - **DIMENSIONS** — danh sách các chiều review để fan out
443
484
  (`/refine-prd` → 4 lăng kính; `/review-context` → các P-check hoặc B-check).
444
485
  - **FINDINGS SCHEMA** — dạng YAML mà mỗi finding phải theo (định nghĩa trong lệnh).
486
+ - **GRANULARITY** *(tuỳ chọn, mặc định `auto`)* — `auto`: chọn độ mịn fan-out theo bảng ngưỡng kích thước ở Phase 1 (hành vi cũ). `per-uc`: **LUÔN** fan-out theo từng UC, **bỏ qua ngưỡng** — dùng cho review cần độ đầy đủ cao (`/refine-prd` truyền cái này để lần đầu đã quét sâu). Lệnh không truyền → `auto` → hành vi không đổi.
487
+ - **CHANGED_SCOPE** *(tuỳ chọn)* — danh sách UC/section đã thay đổi (review **delta**). Nếu được truyền, Phase 1 chỉ fan-out trên các phạm vi này + PRD-global; Phase 2 critic vẫn quét **toàn doc** làm lưới an toàn. Không truyền → quét toàn bộ như thường.
445
488
 
446
489
  > **Bỏ qua ở chế độ sub-agent:** Nếu Gate Bước 0 đã set `_agent_mode: true`, toàn bộ
447
490
  > quy trình này bị **bỏ qua** — orchestrator đã chạy sẵn một dimension/UC cho mỗi
@@ -453,8 +496,11 @@ Lệnh gọi cung cấp hai thứ:
453
496
 
454
497
  **Bao nhiêu sub-agent:** *số lượng* agent không phải là đòn bẩy độ đầy đủ — bề rộng được
455
498
  cố định bởi taxonomy DIMENSION (thêm agent vào cùng một dimension chỉ tìm lại cùng vấn đề),
456
- còn *độ sâu* thuộc về vòng lặp critic ở Phase 2. Chọn **độ mịn fan-out**
457
- theo kích thước target, tái dùng ngưỡng của `steps/spawn-agent.md`:
499
+ còn *độ sâu* thuộc về vòng lặp critic ở Phase 2.
500
+
501
+ **Nếu `GRANULARITY = per-uc`:** **bỏ qua bảng ngưỡng dưới đây**, luôn dùng độ mịn **DIMENSION × phạm vi UC** (kể cả PRD nhỏ) — đảm bảo quét sâu, không bỏ sót ngay lần đầu. (Cái giá: nhiều agent hơn cho PRD nhỏ — chấp nhận để lần đầu đầy đủ.)
502
+
503
+ **Nếu `GRANULARITY = auto`** (mặc định): chọn **độ mịn fan-out** theo kích thước target, tái dùng ngưỡng của `steps/spawn-agent.md`:
458
504
 
459
505
  | Kích thước target | Độ mịn | Số agent |
460
506
  |-------------|-------------|-------------|
@@ -477,6 +523,7 @@ UC duy nhất — chính là điều ngăn bỏ sót trên các PRD lớn.
477
523
  agent). Giới hạn mỗi wave ở **`AGENT_CAP = 12`** agent và gom batch các phạm vi UC cho vừa:
478
524
 
479
525
  1. Dựng danh sách phạm vi = `[UC1, UC2, …, UCn, PRD-global]` (độ dài `UCs + 1`).
526
+ - **Nếu `CHANGED_SCOPE` được truyền (review delta):** danh sách phạm vi = `[các UC trong CHANGED_SCOPE] + [PRD-global]` (chỉ các UC đã đổi + global), KHÔNG phải tất cả UC. Số agent tụt theo đó.
480
527
  2. Tính số-phạm-vi-mỗi-bucket: `groups = max(1, floor(AGENT_CAP / dimensions))`.
481
528
  - Nếu `groups ≥ UCs + 1` → không cần batch, chạy một agent cho mỗi `DIMENSION × scope`.
482
529
  - Else chia danh sách phạm vi thành `groups` bucket liền kề kích thước xấp xỉ bằng nhau
@@ -518,6 +565,8 @@ Gom mảng findings của mọi sub-agent vào một danh sách hợp nhất `AL
518
565
  Đây là bước chống đập-chuột-chũi. Lặp cho tới khi **hai vòng liên tiếp thêm 0 finding
519
566
  mới**, hoặc tới cap cứng **3 vòng**, cái nào đến trước:
520
567
 
568
+ > **Lưu ý delta:** kể cả khi `CHANGED_SCOPE` giới hạn Phase 1 vào các UC đã đổi, completeness-critic ở Phase 2 **vẫn đọc TOÀN bộ doc** — đây là lưới an toàn bắt các vấn đề mà một fix ở UC đã đổi có thể làm lộ ra ở chỗ khác.
569
+
521
570
  1. Spawn một sub-agent **completeness-critic** bằng Agent tool. Cho nó:
522
571
  - toàn bộ target file (`{target_file}`),
523
572
  - danh sách findings đã ghi nhận dưới dạng **slim JSON** — chỉ 3 fields cốt lõi
@@ -581,6 +630,19 @@ Convergence: {convergence_rounds} vòng critic — file findings đã đầy đ
581
630
  - **Check do orchestrator chạy (không fan out):** `P0` (umbrella routing) và `P3` (xung đột cross-PRD) cần config / context của PRD khác — orchestrator tự chạy chúng **trước** fan-out và thêm kết quả vào `ALL_FINDINGS`.
582
631
  - Vòng lặp completeness-critic (Phase 2) đảm bảo file findings đầy đủ trong một lần chạy — chạy lại `/review-context` sẽ lòi ra **0 finding mới**. Map mỗi dimension vào field `check_id` của schema dưới đây.
583
632
 
633
+ **Tham số truyền vào Quy trình Review:**
634
+ - `GRANULARITY = per-uc` — LUÔN fan-out theo từng UC (bỏ ngưỡng cả-file), để **cổng review bắt đủ lỗi ngay lần đầu** (cổng cuối trước khi sinh BDD — sót ở đây thì test sai theo).
635
+ - `CHANGED_SCOPE` — theo full/delta dưới đây.
636
+
637
+ **Chọn full vs delta** *(mặc định: lần đầu FULL, lần sau DELTA)*:
638
+ 1. Tách `--full` khỏi `$ARGUMENTS` nếu có.
639
+ 2. Kiểm tra file findings của target (tên suy ở "Phát hiện Review Mode"):
640
+ - **Không tồn tại** (lần đầu) → **FULL**: KHÔNG truyền `CHANGED_SCOPE`.
641
+ - **Tồn tại** + có `--full` → **FULL**: bỏ qua findings cũ.
642
+ - **Tồn tại** + KHÔNG `--full` → so `source_version` trong findings cũ với version target hiện tại (PRD: Metadata `Version`; BDD: `@trace.bdd_version`):
643
+ - **Bằng nhau** (target chưa đổi từ lần review trước) → DỪNG, báo: `"Target chưa đổi từ v{X} (lần review gần nhất). Không có gì để review lại — dùng --full nếu vẫn muốn quét toàn bộ."`
644
+ - **Khác** → **DELTA**: `CHANGED_SCOPE` = { `uc_id`/`section` của finding đã xử lý (status `accepted`/`modified`/`applied_automatically`) trong findings cũ } ∪ { UC có trong target hiện tại nhưng chưa từng xuất hiện ở findings cũ }.
645
+
584
646
  ---
585
647
 
586
648
  ## PRD Review Mode
@@ -591,33 +653,35 @@ Convergence: {convergence_rounds} vòng critic — file findings đã đầy đ
591
653
 
592
654
  Khi `setup.mode = umbrella`, PRD phải có metadata routing đúng để context-loader Bước 1.5 có thể đưa output sinh ra tới đúng service submodule. Chạy các check này **trước P1–P5**:
593
655
 
594
- **P0.1 — `@trace.domain` mặt**
595
- - Đọc block frontmatter của PRD (các dòng bắt đầu bằng `@trace.` ở đầu file)
596
- - Nếu `@trace.domain` **vắng mặt** finding **critical**:
597
- - `finding`: "`@trace.domain` đang thiếu. Umbrella routing của team dev phụ thuộc field này để đưa BDD và code output tới đúng service submodule."
598
- - `suggestion`: "Thêm `@trace.domain: {domain}` vào frontmatter của PRD. Dùng một trong các domain key được định nghĩa trong section services của `project-context.yaml` của umbrella."
656
+ > **Nguồn đọc:** PRD mang Domain/Status ở **bảng Metadata** (`| **Domain** | … |`, `| **Status** | … |`) **KHÔNG** phải frontmatter `@trace.*` (cái đó dành cho file `.feature`/tech-docs do máy đọc). Routing umbrella dựa trên Domain (bảng) + path, đúng như context-loader Bước 1.5 và `generate-bdd` (đọc `| **Status** |`).
657
+
658
+ **P0.1 Row `Domain` mặt trong Metadata**
659
+ - Đọc bảng Metadata của PRD, lấy row `| **Domain** |`.
660
+ - Nếu **vắng mặt / để trống** finding **critical**:
661
+ - `finding`: "Row `Domain` trong bảng Metadata đang thiếu. Umbrella routing của team dev phụ thuộc Domain (+ path) để đưa BDD và code output tới đúng service submodule."
662
+ - `suggestion`: "Thêm `| **Domain** | {domain} |` vào bảng Metadata. Dùng một trong các domain key được định nghĩa trong section services của `project-context.yaml` của umbrella."
599
663
  - `auto_fixable: false` — PO phải confirm tên domain đúng
600
664
 
601
- **P0.2 — `@trace.domain` khớp một service key**
602
- - Nếu `@trace.domain` có mặt, kiểm tra giá trị của nó có khớp key nào trong section `services` của project-context.yaml không
665
+ **P0.2 — `Domain` khớp một service key**
666
+ - Nếu Domain có mặt, kiểm tra giá trị của nó có khớp key nào trong section `services` của project-context.yaml không.
603
667
  - Nếu **không khớp** → finding **critical**:
604
- - `finding`: "`@trace.domain: {value}` không khớp key nào trong config `services` của umbrella. Routing sẽ fallback về path mặc định và BDD có thể được sinh sai chỗ."
605
- - `suggestion`: "Hoặc cập nhật `@trace.domain` cho khớp một service key có sẵn ({list known keys}), hoặc thêm entry mới vào `services` trong project-context.yaml cho domain `{value}`."
668
+ - `finding`: "Domain `{value}` không khớp key nào trong config `services` của umbrella. Routing sẽ fallback về path mặc định và BDD có thể được sinh sai chỗ."
669
+ - `suggestion`: "Hoặc cập nhật row Domain cho khớp một service key có sẵn ({list known keys}), hoặc thêm entry mới vào `services` trong project-context.yaml cho domain `{value}`."
606
670
  - `auto_fixable: false`
607
671
  - Nếu section `services` chưa được cấu hình (rỗng/placeholder) → finding **major**:
608
672
  - `finding`: "Section `services` của umbrella chưa được cấu hình. Không thể kiểm chứng domain routing."
609
673
  - `suggestion`: "Cập nhật section services trong `.agent/project-context.yaml` với mapping domain-to-submodule trước khi sinh BDD."
610
674
  - `auto_fixable: false`
611
675
 
612
- **P0.3 — Check `@trace.status`**
613
- - Nếu `@trace.status` vắng mặt → **minor**, `auto_fixable: true` (thêm `@trace.status: draft`)
614
- - Nếu `@trace.status: draft` → **major**, `auto_fixable: false`:
676
+ **P0.3 — Check row `Status` trong Metadata**
677
+ - Nếu row `| **Status** |` **vắng mặt** → **minor**, `auto_fixable: true` (thêm `| **Status** | draft |`)
678
+ - Nếu `Status: draft` → **major**, `auto_fixable: false`:
615
679
  - `finding`: "PRD status là `draft`. Team dev không nên sinh BDD từ một PRD chưa được duyệt."
616
- - `suggestion`: "PO/SA nên review và cập nhật status thành `approved` trước khi team dev tiếp tục."
680
+ - `suggestion`: "PO/SA nên review và cập nhật Status thành `approved` trước khi team dev tiếp tục."
617
681
 
618
682
  > **P0 là một gate check:** Nếu P0.1 hoặc P0.2 cho finding critical, hiển thị cảnh báo trước khi tiếp tục:
619
683
  > ```
620
- > ⚠️ ROUTING WARNING: phát hiện vấn đề @trace.domain.
684
+ > ⚠️ ROUTING WARNING: phát hiện vấn đề Domain trong Metadata.
621
685
  > BDD/code sinh từ PRD này có thể rơi vào sai service submodule.
622
686
  > Giải quyết các finding P0 trước khi chạy /generate-bdd.
623
687
  > ```
@@ -637,6 +701,9 @@ Quét toàn bộ PRD tìm vấn đề thuật ngữ:
637
701
  3. **Business term chưa liệt kê** — các term quan trọng vắng trong dictionary:
638
702
  → Severity: **minor**. Đề xuất thêm vào `business-dictionary.md`.
639
703
 
704
+ 4. **Thuật ngữ kỹ thuật/UI lọt vào prose nghiệp vụ** — theo baseline **Business Language Guard** (re-render, UI, timeout, spinner, API/endpoint/token…), không nằm trong dictionary nhưng vẫn là từ kỹ thuật:
705
+ → Severity: **major**, auto-fixable. `suggestion` theo guard: diễn đạt lại (Nhóm 1) / chuyển Design Spec (Nhóm 2) / bỏ về Tech Docs (Nhóm 3).
706
+
640
707
  ### P2 — Ambiguity Check
641
708
 
642
709
  Quét mỗi AC và BR tìm:
@@ -661,13 +728,20 @@ hoặc định nghĩa lại field/status transition của một entity khác đi
661
728
 
662
729
  ### P4 — Structural Completeness
663
730
 
664
- - [ ] Block Metadata: Version, Author, Date, Status
665
- - [ ] Ít nhất 1 UC với heading `#### {TICKET-ID}-UC{N}:`
666
- - [ ] Mỗi UC có: Description, Actors, Preconditions, Acceptance Criteria, Business Rules
667
- - [ ] section `## Changelog`
731
+ Đối chiếu với cấu trúc template PRD (Metadata · §1 Tổng quan · §2 AC · §3 UC · §4 UI/UX · Appendix · Change Log):
732
+
733
+ - [ ] **Metadata** có: Version, Status, Author, Created, Updated, Domain, Ticket (PO nếu có)
734
+ - [ ] **§1c "Phụ thuộc liên service"** có mặt (hoặc ghi rõ "Không có")
735
+ - [ ] **§2 Acceptance Criteria** (global) có mặt; **mỗi AC** kết thúc bằng ref `_(BR: …)_` (≥1 BR)
736
+ - [ ] **≥1 UC** với heading `#### {TICKET-ID}-UC{N}:`
737
+ - [ ] Mỗi UC có: **Actor, Description, Pre-condition, Post-condition, AC liên quan**, bảng Business Rule (AC là §2 global — UC chỉ trỏ qua "AC liên quan", KHÔNG chứa AC đầy đủ)
738
+ - [ ] **Nhất quán 2 chiều**: tập "AC liên quan" của mỗi UC = tập AC §2 có ref BR trỏ về UC đó
739
+ - [ ] **§4 UI/UX**: có User Flow và **Wireframe** (Wireframe lái coverage BDD C.1)
740
+ - [ ] **API Source nhất quán**: nếu Metadata `API Source: existing` → Appendix "Existing API Contract" đủ method/path/request/response (hoặc có block ⛔ PENDING + con trỏ nguồn); nếu greenfield/partner (API Source trống) → section "Existing API Contract" đã bị **xoá hẳn** (không để bảng rỗng)
741
+ - [ ] Có section `# Change Log`
668
742
  - [ ] Không còn giá trị `{{PLACEHOLDER}}` chưa điền
669
743
 
670
- → Section thiếu: **major**. AI có thể thêm skeleton khi `--resume` nếu được chấp nhận.
744
+ → Section/field thiếu hoặc lệch cấu trúc: **major**. AI có thể thêm skeleton khi `--resume` nếu được chấp nhận. Riêng **"Nhất quán 2 chiều"** lệch → **major**, `auto_fixable: false` (người xác nhận AC↔UC, AI không tự đoán). **API Source: existing thiếu contract** → **major**, `auto_fixable: false` (cần nguồn contract); **greenfield còn sót section "Existing API Contract" rỗng** → **minor**, `auto_fixable: true` (xoá section).
671
745
 
672
746
  ### P5 — Custom Criteria (tuỳ chọn)
673
747
 
@@ -751,6 +825,7 @@ Sau khi chạy hết các check, ghi `{paths.refinement_dir}/{slug}-review-*-fin
751
825
 
752
826
  ```yaml
753
827
  source_file: "{absolute path to reviewed file}"
828
+ source_version: "{version target lúc sinh findings — PRD: Metadata Version; BDD: @trace.bdd_version — dùng chọn full/delta lần chạy sau}"
754
829
  generated_at: "{ISO datetime}"
755
830
  review_type: "{prd | bdd}"
756
831
  status: "pending_review"
@@ -937,9 +1012,12 @@ Với mỗi finding có `auto_fixable: true`, theo thứ tự (critical → majo
937
1012
 
938
1013
  | check_id | Áp dụng gì |
939
1014
  |----------|--------------|
940
- | P0.3 (Thiếu status) | Thêm `@trace.status: draft` vào frontmatter |
1015
+ | P0.3 (Thiếu status) | Thêm row `| **Status** | draft |` vào bảng Metadata |
941
1016
  | P1 (Banned term) | Thay mọi lần xuất hiện banned term bằng canonical term |
942
- | P4 (Structure) | Thêm skeleton section/metadata còn thiếu |
1017
+ | P1 (Thuật ngữ kỹ thuật/UI) | Diễn đạt lại theo Business Language Guard (Nhóm 1) / chuyển Design Spec (2) / bỏ về Tech Docs (3) |
1018
+ | P4 (Structure) | Thêm skeleton section/metadata còn thiếu; greenfield → xoá section "Existing API Contract" rỗng |
1019
+
1020
+ > **Chạy Business Language Guard trên text vừa sửa TRƯỚC khi ghi** (xem section "Ngôn ngữ nghiệp vụ") — không để bản auto-fix tự kéo thuật ngữ kỹ thuật vào.
943
1021
 
944
1022
  **Với file BDD:**
945
1023
 
@@ -1011,11 +1089,13 @@ Mở file findings trong Review Board → rồi chạy: /review-context --resume
1011
1089
 
1012
1090
  Áp dụng theo thứ tự: critical → major → minor.
1013
1091
 
1092
+ > **Chạy Business Language Guard trên text vừa sửa TRƯỚC khi ghi** (xem section "Ngôn ngữ nghiệp vụ") — đặc biệt với P2 (sửa câu mơ hồ) / P4 skeleton: không để bản fix tự kéo thuật ngữ kỹ thuật-UI vào PRD.
1093
+
1014
1094
  **Với finding PRD:**
1015
1095
  | check_id | Làm gì |
1016
1096
  |----------|-----------|
1017
- | P0.3 (Thiếu status) | Thêm `@trace.status: draft` vào frontmatter |
1018
- | P1 (Banned term) | Thay mọi lần xuất hiện banned term bằng canonical term |
1097
+ | P0.3 (Thiếu status) | Thêm row `| **Status** | draft |` vào bảng Metadata |
1098
+ | P1 (Banned term) | Thay banned term bằng canonical; thuật ngữ kỹ thuật/UI → diễn đạt lại theo Business Language Guard |
1019
1099
  | P2 (Ambiguity) | Áp dụng fix nêu trong `suggestion` hoặc note `modified` |
1020
1100
  | P3 (Conflict) | Áp dụng cách giải quyết nêu trong note modified |
1021
1101
  | P4 (Structure) | Thêm section/metadata field còn thiếu |