@educa-corp/sdd-framework 0.4.0 → 0.5.0

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.
Files changed (158) hide show
  1. package/bin/build.js +9 -0
  2. package/bin/index.js +115 -4
  3. package/bin/self-check.js +354 -0
  4. package/bin/trace-schema.json +1199 -0
  5. package/commands/debug.md +19 -12
  6. package/commands/define-product.md +19 -12
  7. package/commands/dev-gen-test.md +53 -19
  8. package/commands/dev-run-test.md +55 -20
  9. package/commands/dev-run-test.tmpl +2 -1
  10. package/commands/dev-smoke-test.md +19 -12
  11. package/commands/extend-prd.md +907 -0
  12. package/commands/extend-prd.tmpl +270 -0
  13. package/commands/fix-bug.md +101 -15
  14. package/commands/fix-bug.tmpl +29 -3
  15. package/commands/generate-architecture.md +19 -12
  16. package/commands/generate-bdd.md +174 -48
  17. package/commands/generate-bdd.tmpl +107 -18
  18. package/commands/generate-code.md +122 -29
  19. package/commands/generate-code.tmpl +69 -10
  20. package/commands/generate-design-spec.md +19 -12
  21. package/commands/generate-prd.md +44 -12
  22. package/commands/generate-prd.tmpl +25 -0
  23. package/commands/generate-spec-manifest.md +19 -12
  24. package/commands/generate-tech-docs.md +22 -15
  25. package/commands/generate-tech-docs.tmpl +2 -2
  26. package/commands/learn.md +19 -12
  27. package/commands/map-testids.md +19 -12
  28. package/commands/propose-scenario.md +91 -15
  29. package/commands/propose-scenario.tmpl +72 -3
  30. package/commands/qc-analyze.md +19 -12
  31. package/commands/qc-design-test.md +20 -12
  32. package/commands/qc-design-test.tmpl +1 -0
  33. package/commands/qc-plan.md +19 -12
  34. package/commands/qc-report.md +19 -12
  35. package/commands/qc-review.md +19 -12
  36. package/commands/qc-run-test.md +88 -22
  37. package/commands/qc-run-test.tmpl +35 -3
  38. package/commands/refine-prd.md +19 -12
  39. package/commands/report-bug.md +19 -12
  40. package/commands/review-code.md +60 -14
  41. package/commands/review-code.tmpl +41 -2
  42. package/commands/review-context.md +62 -16
  43. package/commands/review-context.tmpl +43 -4
  44. package/commands/review-tech-docs.md +50 -14
  45. package/commands/review-tech-docs.tmpl +31 -2
  46. package/commands/setup-ai-first.md +26 -16
  47. package/commands/setup-ai-first.tmpl +7 -4
  48. package/commands/sync.md +43 -18
  49. package/commands/sync.tmpl +37 -14
  50. package/commands/update-framework.md +43 -4
  51. package/commands/update-framework.tmpl +37 -0
  52. package/commands/validate-traces.md +481 -49
  53. package/commands/validate-traces.tmpl +462 -37
  54. package/core/FRAMEWORK_VERSION +1 -1
  55. package/core/README.md +56 -0
  56. package/core/commands/debug.md +19 -12
  57. package/core/commands/define-product.md +19 -12
  58. package/core/commands/dev-gen-test.md +53 -19
  59. package/core/commands/dev-run-test.md +55 -20
  60. package/core/commands/dev-smoke-test.md +19 -12
  61. package/core/commands/extend-prd.md +907 -0
  62. package/core/commands/fix-bug.md +101 -15
  63. package/core/commands/generate-architecture.md +19 -12
  64. package/core/commands/generate-bdd.md +174 -48
  65. package/core/commands/generate-code.md +122 -29
  66. package/core/commands/generate-design-spec.md +19 -12
  67. package/core/commands/generate-prd.md +44 -12
  68. package/core/commands/generate-spec-manifest.md +19 -12
  69. package/core/commands/generate-tech-docs.md +22 -15
  70. package/core/commands/learn.md +19 -12
  71. package/core/commands/map-testids.md +19 -12
  72. package/core/commands/propose-scenario.md +91 -15
  73. package/core/commands/qc-analyze.md +19 -12
  74. package/core/commands/qc-design-test.md +20 -12
  75. package/core/commands/qc-plan.md +19 -12
  76. package/core/commands/qc-report.md +19 -12
  77. package/core/commands/qc-review.md +19 -12
  78. package/core/commands/qc-run-test.md +88 -22
  79. package/core/commands/refine-prd.md +19 -12
  80. package/core/commands/report-bug.md +19 -12
  81. package/core/commands/review-code.md +60 -14
  82. package/core/commands/review-context.md +62 -16
  83. package/core/commands/review-tech-docs.md +50 -14
  84. package/core/commands/setup-ai-first.md +26 -16
  85. package/core/commands/sync.md +43 -18
  86. package/core/commands/update-framework.md +43 -4
  87. package/core/commands/validate-traces.md +481 -49
  88. package/core/modules/android-compose/stack-profile.yaml +1 -1
  89. package/core/modules/flutter/stack-profile.yaml +1 -1
  90. package/core/modules/ios-swiftui/stack-profile.yaml +1 -1
  91. package/core/modules/java-spring/stack-profile.yaml +1 -1
  92. package/core/modules/nextjs/stack-profile.yaml +1 -1
  93. package/core/modules/nuxt/stack-profile.yaml +1 -1
  94. package/core/modules/phaser-game/stack-profile.yaml +1 -1
  95. package/core/modules/php-laravel/stack-profile.yaml +1 -1
  96. package/core/modules/qc-playwright/stack-profile.yaml +1 -1
  97. package/core/modules/react/stack-profile.yaml +1 -1
  98. package/core/modules/react-native/stack-profile.yaml +1 -1
  99. package/core/modules/vue/stack-profile.yaml +1 -1
  100. package/core/rules/workflow.md +29 -0
  101. package/core/steps/gate.md +13 -8
  102. package/core/steps/report-footer.md +6 -4
  103. package/core/steps/trace-mirror.md +34 -7
  104. package/core/templates/README.md +47 -0
  105. package/core/templates/feature.template +14 -11
  106. package/core/templates/project-context.yaml +26 -14
  107. package/core/templates/tech-design.template.md +1 -1
  108. package/docs/01-getting-started/installation.md +18 -1
  109. package/docs/01-getting-started/what-is-sdd.md +4 -2
  110. package/docs/02-concepts/architecture.md +27 -3
  111. package/docs/02-concepts/pipeline-steps/02-specification.md +39 -3
  112. package/docs/02-concepts/pipeline-steps/04-bdd.md +24 -2
  113. package/docs/02-concepts/pipeline-steps/05-tech-docs.md +18 -1
  114. package/docs/02-concepts/pipeline-steps/06-code.md +35 -4
  115. package/docs/02-concepts/pipeline-steps/09-validate-traces.md +137 -12
  116. package/docs/02-concepts/pipeline-steps/10-feedback-loop.md +59 -3
  117. package/docs/02-concepts/roles-and-hitl.md +1 -1
  118. package/docs/02-concepts/traceability.md +126 -94
  119. package/docs/03-guides/developer.md +20 -4
  120. package/docs/03-guides/product-owner.md +72 -68
  121. package/docs/03-guides/tester-qa.md +81 -70
  122. package/docs/04-reference/commands.md +134 -105
  123. package/docs/04-reference/configuration.md +146 -94
  124. package/docs/04-reference/trace-schema.md +145 -37
  125. package/docs/explain/02-generate-prd.md +80 -78
  126. package/docs/explain/02b-extend-prd.md +125 -0
  127. package/docs/explain/03-refine-prd.md +86 -86
  128. package/docs/explain/04-review-context.md +18 -1
  129. package/docs/explain/06-generate-bdd.md +23 -0
  130. package/docs/explain/08-review-tech-docs.md +20 -5
  131. package/docs/explain/10-review-code.md +36 -2
  132. package/docs/explain/19-qc-run-test.md +87 -67
  133. package/docs/explain/21-validate-traces.md +74 -68
  134. package/docs/explain/23-fix-bug.md +19 -3
  135. package/docs/explain/26-propose-scenario.md +70 -63
  136. package/docs/explain/README.md +135 -134
  137. package/modules/android-compose/stack-profile.yaml +1 -1
  138. package/modules/flutter/stack-profile.yaml +1 -1
  139. package/modules/ios-swiftui/stack-profile.yaml +1 -1
  140. package/modules/java-spring/stack-profile.yaml +1 -1
  141. package/modules/nextjs/stack-profile.yaml +1 -1
  142. package/modules/nuxt/stack-profile.yaml +1 -1
  143. package/modules/phaser-game/stack-profile.yaml +1 -1
  144. package/modules/php-laravel/stack-profile.yaml +1 -1
  145. package/modules/qc-playwright/stack-profile.yaml +1 -1
  146. package/modules/react/stack-profile.yaml +1 -1
  147. package/modules/react-native/stack-profile.yaml +1 -1
  148. package/modules/vue/stack-profile.yaml +1 -1
  149. package/package.json +5 -4
  150. package/rules/workflow.md +29 -0
  151. package/scripts/migrate-bdd-platform.js +286 -0
  152. package/steps/gate.md +13 -8
  153. package/steps/report-footer.md +6 -4
  154. package/steps/trace-mirror.md +34 -7
  155. package/templates/README.md +47 -0
  156. package/templates/feature.template +14 -11
  157. package/templates/project-context.yaml +26 -14
  158. package/templates/tech-design.template.md +1 -1
@@ -7,7 +7,16 @@
7
7
  {{include:steps/context-loader.md}}
8
8
 
9
9
  > **Proposal của tester (input tuỳ chọn):** trước khi sinh, quét `{paths.bdd_proposals_dir}/` (mặc định `{spec_source}/feedback/bdd-proposals/`) tìm `{UC-ID}-*.md`. Với mỗi proposal:
10
- > - `Status: accepted` (PO/Dev đã duyệt) → chèn scenario vào `.feature` của UC (giữ `@trace`), rồi **lưu trữ**: chuyển file sang `{paths.bdd_proposals_dir}/archived/` + đặt `Status: incorporated`, và **commit + push** spec repo để gỡ khỏi feedback chung.
10
+ > - `Status: accepted` (PO/Dev đã duyệt) → chèn scenario vào `.feature` của UC, **normalize khi chèn** (xem dưới), rồi **lưu trữ**: chuyển file sang `{paths.bdd_proposals_dir}/archived/` + đặt `Status: incorporated`, và **commit + push** spec repo để gỡ khỏi feedback chung.
11
+ >
12
+ > **Normalize — bắt buộc, nếu không scenario sẽ vô hình với trace:**
13
+ > 1. Gán `# @trace.scenario: {UC-ID}-SC{N}` với `{N}` = số SC **kế tiếp** trong file đó (thay placeholder `SC?`).
14
+ > 2. Giữ `# @trace.sc_version: 1.0`. Bổ sung `# @trace.business_rules` nếu proposal để `—` (suy từ AC mà dòng `# Covers:` trỏ tới); không suy được → để `—` và nêu trong report.
15
+ > 3. **Strip** tag `@proposed` / `@from-test` — chúng là nhãn vòng đời proposal, không thuộc BDD canonical.
16
+ > 4. Đặt scenario vào **đúng NHÓM** theo business theme (C.5), không nối vào cuối file.
17
+ > 5. **Append row TSV** cho SC mới (như nhánh "SC mới" ở Write Trace State: `spec_ver = 1.0`, mọi cột gen/test/qc = `—`, `status = UNTRACKED`).
18
+ >
19
+ > **Backward-compat:** proposal cũ mang `@trace.uc=` / `@trace.ac=` (vocabulary trước đây, không thuộc contract `.feature`) → tự map sang canonical (`@trace.uc` bỏ — số UC đã có trong `sc_id`; `@trace.ac` → dòng `# Covers:`) và in một dòng cảnh báo khuyến nghị proposal sau viết theo format mới.
11
20
  > - `Status: proposed`/`rejected` (hoặc thiếu `Status`) → **bỏ qua**, để nguyên cho PO/Dev xử lý (KHÔNG tự đoán, KHÔNG tự đưa vào).
12
21
  > Bỏ qua sạch nếu folder rỗng.
13
22
 
@@ -213,9 +222,24 @@ Chỉ cần kiểm tra trạng thái đã phân giải:
213
222
  | `active_service = "unresolved"` (có section `services` nhưng domain PRD không khớp entry nào) | **DỪNG**, báo: "Domain `{domain}` của PRD không khớp service nào trong `services:` của project-context.yaml — bổ sung mapping rồi chạy lại." (Không đoán/hỏi tay — domain là khoá định danh, lệch là lỗi cấu hình cần sửa ở SoT.) |
214
223
  | Single-service (không có section `services`) | `active_module = tech_stack.module` (đã set ở Bước 6.5). Tiếp tục. |
215
224
 
216
- **Output path (umbrella mode):** `{paths.specs_dir}/{domain}/{prd-slug}/bdd/{TICKET-ID}-UC{N}-{slug}.feature`
225
+ ### Phân giải `active_platform` (umbrella mode)
217
226
 
218
- *(Không thêm subfolder theo service: feature-package đã domain-scoped sẵn `{domain}/`, service route 1-1 theo domain nên thêm subfolder service sẽ chỉ lặp lại domain. `active_service` chỉ dùng cho `service_root`/từ vựng, KHÔNG vào path spec.)*
227
+ Umbrella mode không hỏi platform (khác spec repo mode) **suy** từ module của service. Bắt buộc phải giá trị: `active_platform` đi vào **path file**, vào **header `@trace.platform`**, vào **tên sổ trace** `{UC-ID}-{platform}.tsv`.
228
+
229
+ | `active_module` | → `active_platform` |
230
+ |---|---|
231
+ | react · nextjs · vue · nuxt · angular | `web` |
232
+ | flutter · react-native · ios-swiftui · android-compose | `app` |
233
+ | java-spring · golang · dotnet · php-laravel | `system` |
234
+ | context-engineering · phaser-game | theo `platform_type` của stack-profile (`backend` → `system`, còn lại → `web`) |
235
+
236
+ - `active_service = "multi"` + `service_candidates_kind = platform` → **nhiều** `active_platform` (một cho mỗi platform trong `service_candidates`); sinh một file `.feature` cho mỗi platform, module lấy theo `service_candidates.{platform}.module`.
237
+ - Không suy được (module lạ, không có trong bảng và không có `platform_type`) → **DỪNG**, hỏi người dùng chọn `web`/`app`/`system`. **KHÔNG** ghi file khi chưa có `active_platform` — file thiếu platform sẽ vô hình với `/validate-traces` và va chạm tên với platform khác.
238
+
239
+ **Output path (umbrella mode):** `{paths.specs_dir}/{domain}/{prd-slug}/bdd/{active_platform}/{TICKET-ID}-UC{N}-{slug}.feature`
240
+
241
+ *(**Subfolder `{platform}/` LUÔN có — mọi mode.** `web` và `system` của cùng một UC là hai file khác nhau: nếu bỏ subfolder, chúng ra cùng filename và **ghi đè nhau**; ngoài ra trace tách theo platform (`{UC-ID}-{platform}.tsv`) nên bố cục spec phải tách tương ứng.*
242
+ *Cái KHÔNG thêm là subfolder theo **service**: feature-package đã domain-scoped sẵn ở `{domain}/`, mà service route 1-1 theo domain — thêm subfolder service chỉ lặp lại domain. `active_service` chỉ dùng cho `service_root`/từ vựng, KHÔNG vào path spec.)*
219
243
 
220
244
  **Từ vựng theo platform** — điều chỉnh cách viết step BDD theo `active_module`:
221
245
 
@@ -298,9 +322,9 @@ Sau khi sinh tất cả file `.feature` và `.tsv` cho UC được giao, trả v
298
322
 
299
323
  Trước khi sinh, kiểm tra các file `.feature` có sẵn cho PRD này:
300
324
 
301
- 1. Phân giải search path theo mode:
302
- - **Spec repo mode**: `{paths.specs_dir}/{domain}/{prd-slug}/bdd/{active_platform}/{TICKET-ID}-UC*.feature`
303
- - **Umbrella mode**: `{paths.specs_dir}/{domain}/{prd-slug}/bdd/{TICKET-ID}-UC*.feature`
325
+ 1. Search path (**giống nhau cả hai mode** — bố cục `bdd/{platform}/` là chuẩn duy nhất):
326
+ `{paths.specs_dir}/{domain}/{prd-slug}/bdd/{active_platform}/{TICKET-ID}-UC*.feature`
327
+ > **Legacy:** nếu không khớp gì, thử thêm một lần ở bố cục phẳng cũ `…/bdd/{TICKET-ID}-UC*.feature`. Khớp → xử lý như file có sẵn **và** in cảnh báo: `⚠️ File .feature đang ở bố cục phẳng (trước v0.4.1). Chạy: npx sdd-framework --migrate-bdd-platform để chuyển sang bdd/{platform}/.` Đừng tự di chuyển file trong lệnh này.
304
328
  2. Đọc `| **Version** |` hiện tại của PRD từ metadata (vd: `1.2`).
305
329
 
306
330
  **Nếu không có file feature nào** → gen mới, tiếp tục bình thường. Dùng version PRD làm `@trace.prd_version`.
@@ -349,7 +373,7 @@ Trước khi sinh, kiểm tra các file `.feature` có sẵn cho PRD này:
349
373
  | Check | Rule |
350
374
  |-------|------|
351
375
  | C.1 Wireframe Coverage | Mỗi component/action trong Wireframe (PRD §4b) có ≥1 SC. **FE/App: mỗi Screen State (≠default) và mỗi AC-UI behavioral của design-spec (`design_coverage`) cũng phải có ≥1 SC** — dedup với AC nghiệp vụ PRD; bỏ AC-UI visual thuần. |
352
- | C.2 PRD Traceability | Mỗi AC và mỗi BR (gồm từng bullet logic) map tới ≥1 SC. |
376
+ | C.2 PRD Traceability | Mỗi AC **thuộc UC này** (đúng tập ở `**AC liên quan:**` của UC trong PRD §3) và mỗi BR trong bảng Business Rule của UC này map tới ≥1 SC. **KHÔNG** phủ AC của UC khác — đó là việc của `.feature` UC đó. *(AC ở PRD là global cấp PRD, còn `.feature` là per-UC; enforce theo nghĩa "mọi AC của PRD" sẽ bắt AI bịa scenario ngoài scope hoặc báo MISSING giả.)* |
353
377
  | C.3 Business Dictionary | Dùng đúng canonical term từ business-dictionary.md. |
354
378
  | C.4 Banned Terms | 0 banned term trong file — grep trước khi gen. |
355
379
  | C.5 NHÓM Grouping | Feature ≥3 SC → PHẢI có NHÓM grouping theo business theme. |
@@ -405,17 +429,49 @@ CHECKPOINT: "Outline này đúng chưa? Bạn muốn thêm hay bớt SC nào kh
405
429
 
406
430
  ## Generate
407
431
 
408
- **Output path theo mode:**
409
- - **Spec repo mode**: `{paths.specs_dir}/{domain}/{prd-slug}/bdd/{active_platform}/{TICKET-ID}-UC{N}-{slug}.feature`
410
- - **Umbrella mode**: `{paths.specs_dir}/{domain}/{prd-slug}/bdd/{TICKET-ID}-UC{N}-{slug}.feature` *(service route 1-1 theo domain → không thêm subfolder service)*
432
+ **Output path MỘT bố cục duy nhất cho cả hai mode:**
411
433
 
412
- Với mỗi UC, ghi vào path đã phân giải ở trên. Dùng từ vựng cho active platform (từ Platform Selection hoặc Service Detection).
434
+ ```
435
+ {paths.specs_dir}/{domain}/{prd-slug}/bdd/{active_platform}/{TICKET-ID}-UC{N}-{slug}.feature
436
+ ```
437
+
438
+ `{active_platform}` ∈ `web` | `app` | `system` — từ Platform Selection (spec repo mode) hoặc suy từ `active_module` (umbrella mode, xem §Service Detection). **Không có `active_platform` thì không ghi file.**
439
+
440
+ Với mỗi UC, ghi vào path trên và set `# @trace.platform: {active_platform}` trong header (**bắt buộc, mọi mode** — `/generate-code` dùng nó để quyết BE/FE và để định vị sổ trace; `/generate-tech-docs` và `context-loader` cũng đọc nó). Dùng từ vựng cho active platform.
413
441
 
414
442
  ```gherkin
415
443
  {{include:templates/feature.template}}
416
444
  ```
417
445
 
418
- *(Template `.feature` **single-source** `templates/feature.template` sửa file đó để đổi cấu trúc mọi `.feature` sinh ra. Coverage Matrix + Pre-merge Checklist nằm ở **cuối** template, thêm vào cuối mỗi file.)*
446
+ > **Template này đến từ đâuđọc trước khi định "customize":**
447
+ > Skeleton trên là **single-source** ở `templates/feature.template` **của repo framework**, được `{{include}}` **nướng cứng vào lệnh này lúc `npm run build`**. Muốn đổi cấu trúc mọi `.feature` sinh ra: sửa file đó **trong repo framework** rồi build lại + phát hành.
448
+ >
449
+ > **Sửa `.agent/templates/feature.template` trong project KHÔNG có tác dụng** — không lệnh nào đọc file đó; nó chỉ là bản tham khảo. Và nó **sẽ bị ghi đè im lặng** ở lần `/update-framework` kế tiếp (`--init` copy `core/` → `.agent/` vô điều kiện; file duy nhất được giữ là `.agent/project-context.yaml`).
450
+ >
451
+ > Coverage Matrix + Pre-merge Checklist nằm ở **cuối** template, thêm vào cuối mỗi file.
452
+
453
+ ### Bump `@trace.sc_version` *(CHỈ khi gen lại — file `.feature` đã tồn tại)*
454
+
455
+ *Bỏ qua hoàn toàn khi gen mới: mọi SC nhận `1.0`.*
456
+
457
+ `@trace.sc_version` là version **của từng scenario** — nó là tín hiệu DUY NHẤT cho `/validate-traces` biết code của SC đó đã lỗi thời (`spec_ver != gen_ver` → `DRIFT`). Không bump = code sinh từ scenario cũ mãi mãi hiện `OK`. Phân biệt với `@trace.bdd_version` (version **cả file**, không đủ phân giải để biết SC nào cần regen).
458
+
459
+ Trước khi ghi file, với **mỗi** SC, so **thân scenario** bản mới vs bản trên disk theo 4 thành phần:
460
+
461
+ 1. dòng `Scenario:` (tên)
462
+ 2. chuỗi step `Given` / `When` / `Then` / `And` (nội dung + thứ tự)
463
+ 3. nội dung data table (nếu có)
464
+ 4. dòng `# Side-effects:`
465
+
466
+ | Kết quả so | Hành động |
467
+ |---|---|
468
+ | Khác ở **bất kỳ** thành phần nào | `@trace.sc_version` += `0.1` (vd `1.0` → `1.1`) |
469
+ | Giống hoàn toàn | **GIỮ NGUYÊN** — bump vô cớ sẽ tạo `DRIFT` giả, làm cờ mất giá trị |
470
+ | SC mới (chưa có trong bản cũ) | `1.0` |
471
+
472
+ *(Thay đổi ngoài 4 thành phần trên — `@trace.business_rules`, tag `@happy`/`@edge`, comment — KHÔNG bump: chúng không đổi hành vi mà code phải implement.)*
473
+
474
+ In danh sách SC được bump vào report cuối để người dùng biết cái nào sẽ hiện `DRIFT`.
419
475
 
420
476
  ---
421
477
 
@@ -427,16 +483,33 @@ Sau khi sinh tất cả file `.feature`, tạo hoặc cập nhật **sổ trace
427
483
 
428
484
  **Cột TSV (tab-separated, một header row + một data row cho mỗi scenario):**
429
485
  ```
430
- sc_id\tsc_title\tspec_ver\tgen_ver\timplemented_by\ttest_count\ttest_classes\tdev_selftest\tdev_selftest_at\tqc_status\tqc_run_at\tqc_owner\tqc_blocked_by\tprd_version\tbdd_version\ttech_doc_revision\tfe_tech_doc_revision\tprd_status\tuc_status\tfe_phase\tstatus\tlast_updated
486
+ sc_id\tsc_title\tspec_ver\tgen_ver\timplemented_by\ttest_count\ttest_classes\tdev_selftest\tdev_selftest_at\tqc_status\tqc_run_at\tqc_owner\tqc_blocked_by\tprd_version\tbdd_version\ttech_doc_revision\tfe_tech_doc_revision\tprd_status\tuc_status\tfe_phase\tstatus\tlast_updated\tservice\tdesign_spec_version
431
487
  ```
432
488
 
433
489
  **Rules:**
434
490
  - Nếu file chưa tồn tại → tạo với header row + tất cả scenario row.
435
491
  - Nếu file tồn tại (gen lại) → với mỗi SC trong `.feature` mới:
436
- - SC đã có trong `.tsv` VÀ `spec_ver` không đổi → chỉ cập nhật: `sc_title`, `prd_version`, `bdd_version`, `prd_status`, `uc_status`, `last_updated`. Giữ nguyên các cột khác.
437
- - SC đã có trong `.tsv` VÀ `spec_ver` đổi (scenario bị sửa) → cập nhật: `sc_title`, `spec_ver`, `prd_version`, `bdd_version`, `prd_status`, `uc_status`, `last_updated` VÀ set `status = DRIFT` ngay (để TSV phản ánh drift mà không cần đợi `/validate-traces`). Giữ nguyên `gen_ver`, `implemented_by`, `test_count`, `test_classes`, `tech_doc_revision`, `fe_tech_doc_revision`.
492
+ - SC đã có trong `.tsv` VÀ `spec_ver` không đổi → chỉ cập nhật: `sc_title`, `prd_version`, `bdd_version`, `prd_status`, `uc_status`, `service`, `design_spec_version`, `last_updated`. Giữ nguyên các cột khác. *(`service` + `design_spec_version` là sự thật cấp-file, làm mới theo `.feature`/design-spec hiện tại — chúng KHÔNG phải tín hiệu nghiệm thu nên làm mới chúng không che giấu gì.)*
493
+ - SC đã có trong `.tsv` VÀ `spec_ver` đổi (scenario bị sửa) → cập nhật: `sc_title`, `spec_ver`, `prd_version`, `bdd_version`, `prd_status`, `uc_status`, `service`, `design_spec_version`, `last_updated` VÀ set `status = DRIFT` ngay (để TSV phản ánh drift mà không cần đợi `/validate-traces`). Giữ nguyên `gen_ver`, `implemented_by`, `test_count`, `test_classes`, `tech_doc_revision`, `fe_tech_doc_revision`.
494
+ **VÀ hạ hiệu lực tín hiệu kiểm thử của đúng SC đó** — spec vừa đổi nên test/QC cũ đang nghiệm thu một hành vi **không còn tồn tại**:
495
+ `dev_selftest → not_run` · `dev_selftest_at → —` · `qc_status → not_run` · `qc_run_at → —`.
496
+ > **Vì sao bắt buộc** *(luật "Làm mất hiệu lực ≠ ghi đè", `rules/workflow.md`)*: không hạ thì chuỗi sau báo xanh sai — spec đổi → `DRIFT` → `/generate-code` sửa method → `gen_ver = spec_ver` → `/validate-traces` Rule 4 cho `OK` (vì `test_count` vẫn > 0) → dashboard hiện `OK · ✅ 10 tests · qc pass` trong khi hành vi mới **chưa được test lần nào**. Đây là lớp lỗi nguy hiểm hơn G1: G1 làm cờ im lặng, cái này làm cờ **nói dối**.
497
+ > **KHÔNG** đụng `test_count`/`test_classes` (test vẫn nằm trên đĩa — số lượng không sai, chỉ nội dung cũ; hạ số sẽ làm tỷ lệ coverage nhảy loạn) và **KHÔNG** đụng `qc_owner`/`qc_blocked_by` (con trỏ tới bug — spec đổi không làm bug biến mất).
498
+ In cảnh báo kèm: `⚠️ {test_count} test của {sc_id} viết cho spec cũ — /dev-gen-test rà lại trước khi chạy`.
438
499
  - SC mới (thêm trong lần gen lại này) → append row mới với `gen_ver`, `implemented_by`, `test_count`, `test_classes`, `dev_selftest`, `dev_selftest_at`, `qc_status`, `qc_run_at`, `qc_owner`, `qc_blocked_by`, `tech_doc_revision`, `fe_tech_doc_revision` đều set `—`.
439
- - SC không còn trong `.feature` (bị xoá) xoá row của nó. *(An toàn: sổ này chỉ chứa scenario của `{active_platform}`, so với `.feature` của chính platform đó không bao giờ đụng scenario platform khác.)*
500
+ - SC không còn trong `.feature` (bị xoá / gộp / đổi số) **phụ thuộc SC đó đã code chưa:**
501
+ - `implemented_by == —` (**chưa** có code) → **xoá row**. Không có gì mồ côi.
502
+ - `implemented_by != —` (**ĐÃ** có code) → **GIỮ row**, set `status = ORPHANED`, giữ nguyên `implemented_by` / `test_count` / `test_classes` / các cột qc, cập nhật `last_updated`. **KHÔNG xoá** — xoá row thì method đó thành vô hình: không `UNTRACKED`, không `GAP`, không `DRIFT`, không xuất hiện ở report nào, mà vẫn nằm trong code và vẫn được caller gọi. Coverage còn *đẹp hơn* thực tế vì mẫu số nhỏ đi.
503
+ In cảnh báo nổi bật ở report cuối:
504
+ ```
505
+ ⚠️ ORPHANED — {UC-ID}-SC{N} "{sc_title}" đã bị xoá khỏi .feature nhưng còn code:
506
+ {implemented_by} (+ {test_count} test: {test_classes})
507
+ Không tự hết — chọn MỘT:
508
+ (a) behavior không còn cần → xoá method + test, rồi xoá row khỏi .tsv
509
+ (b) SC bị xoá do nhầm → đưa scenario trở lại .feature (row về DRIFT/OK bình thường)
510
+ (/validate-traces giữ cờ ORPHANED 🔴 và chặn "pass" tới khi xử lý xong.)
511
+ ```
512
+ *(An toàn: sổ này chỉ chứa scenario của `{active_platform}`, so với `.feature` của chính platform đó — không bao giờ đụng scenario platform khác.)*
440
513
 
441
514
  **Giá trị ghi cho mỗi scenario:**
442
515
 
@@ -464,6 +537,8 @@ sc_id\tsc_title\tspec_ver\tgen_ver\timplemented_by\ttest_count\ttest_classes\tde
464
537
  | `fe_phase` | `—` (set bởi `/generate-code --phase` khi FE implement) |
465
538
  | `status` | `UNTRACKED` |
466
539
  | `last_updated` | hôm nay `YYYY-MM-DD` |
540
+ | `service` | `@trace.service` từ header `.feature` — đội/submodule sở hữu scenario này. `multi` nếu chưa chốt (map-theo-platform ở cấp PRD), `unresolved` nếu domain không khớp entry nào, `—` ở single-service mode. **Đừng bỏ trống** — trace gộp không tách theo service nên đây là chỗ DUY NHẤT mang thông tin sở hữu ở cấp row. |
541
+ | `design_spec_version` | `\| **Version** \|` của design-spec đã nạp ở §Design Spec — Gate & Load. `—` cho `system`/backend (không có design-spec), và `—` khi người dùng chọn "Y — vẫn sinh BDD" mà không có design-spec. |
467
542
 
468
543
  ## Refresh Panel Mirror
469
544
  {{include:steps/trace-mirror.md}}
@@ -487,9 +562,9 @@ Next (spec repo):
487
562
  → Sau khi gen hết platform: commit + push + báo team dev
488
563
  → Team dev đọc BDD từ spec submodule — không chạy /generate-bdd ở phía họ
489
564
 
490
- [Umbrella mode — service: {active_service}]
565
+ [Umbrella mode — service: {active_service} · platform: {active_platform} (suy từ module {active_module})]
491
566
  Files:
492
- {paths.specs_dir}/{domain}/{prd-slug}/bdd/{TICKET-ID}-UC1-{slug}.feature ({N} scenarios)
567
+ {paths.specs_dir}/{domain}/{prd-slug}/bdd/{active_platform}/{TICKET-ID}-UC1-{slug}.feature ({N} scenarios)
493
568
  Trace:
494
569
  {paths.trace_dir}/{domain}/{prd-slug}/{TICKET-ID}-UC1-{active_platform}.tsv ({N} rows)
495
570
  Next (umbrella):
@@ -497,5 +572,19 @@ Next (umbrella):
497
572
  → /generate-tech-docs {feature-file}
498
573
  → /generate-code {feature-file}
499
574
 
575
+ {chỉ khi gen lại VÀ có ≥1 SC bị bump — ngược lại bỏ cả khối}
576
+ 🔄 sc_version đã bump (scenario đổi nội dung → code cũ lỗi thời):
577
+ {UC-ID}-SC2 1.0 → 1.1 {sc_title}
578
+ {UC-ID}-SC5 1.2 → 1.3 {sc_title}
579
+ → {n} SC này sẽ hiện DRIFT ở /validate-traces. Sinh lại code: /generate-code {feature-file}
580
+
581
+ {cùng điều kiện — chỉ in các SC bump mà TRƯỚC ĐÓ có dev_selftest/qc_status khác "—"}
582
+ 🔻 Tín hiệu kiểm thử bị hạ (spec vừa đổi — nghiệm thu cũ hết hiệu lực):
583
+ {UC-ID}-SC2 dev_selftest pass→not_run · qc_status pass→not_run
584
+ ⚠️ {n} test của các SC này viết cho spec CŨ — rà lại nội dung, đừng chỉ chạy lại.
585
+ → sau khi /generate-code: /dev-gen-test (rà test) → /dev-run-test → QC /qc-run-test
586
+ ℹ️ Coverage "đã kiểm đạt" sẽ TỤT trên dashboard — đó là số đúng; số cũ mới là số sai.
587
+ (Tỷ lệ phủ code/test KHÔNG đổi — test_count giữ nguyên vì test vẫn nằm trên đĩa.)
588
+
500
589
  📊 Living Docs: chạy /validate-traces (hoặc /sync) để push trace này lên dashboard spec-module.
501
590
  ```
@@ -32,23 +32,23 @@ Hiển thị và chờ phản hồi:
32
32
  ```
33
33
  ⚙️ MODEL CHECK
34
34
  ──────────────────────────────────────────────────────────────────
35
- Recommended : claude-opus-4 (hoặc model Opus mới nhất)
35
+ Recommended : model Opus mới nhất
36
36
  Why needed : Phân tích spec, review kiến trúc, sinh code đòi hỏi
37
- suy luận sâu. Model nhỏ hơn dễ bỏ sót edge case.
37
+ suy luận sâu. Model nhỏ hơn (Haiku/Sonnet) dễ bỏ sót edge case.
38
38
 
39
39
  Cách đổi trong Claude Code:
40
- SettingsModel chọn "claude-opus"
41
- • hoặc: /modelchọn claude-opus
40
+ /modelchọn model Opus
41
+ • hoặc: SettingsModel
42
42
 
43
- Đang chạy claude-opus?
44
- Y — đúng, đang dùng claude-opus → tiếp tục
43
+ Đang chạy một model Opus?
44
+ Y — đúng → tiếp tục
45
45
  S — bỏ qua kiểm tra (tôi chấp nhận rủi ro chất lượng thấp hơn với model hiện tại)
46
46
  ──────────────────────────────────────────────────────────────────
47
47
  ```
48
48
 
49
49
  - "Y" → tiếp tục sang Bước 1.
50
50
  - "S" → tiếp tục sang Bước 1 (người dùng chấp nhận rủi ro, thêm ⚠️ vào report cuối).
51
- - "N" hoặc bất kỳ giá trị nào khác → **DỪNG.** Xuất: "Vui lòng chuyển sang claude-opus rồi chạy lại lệnh này."
51
+ - "N" hoặc bất kỳ giá trị nào khác → **DỪNG.** Xuất: "Vui lòng chuyển sang một model Opus (`/model`) rồi chạy lại lệnh này."
52
52
 
53
53
  ## Bước 1 — Xác định Target File
54
54
 
@@ -57,7 +57,12 @@ Hiển thị và chờ phản hồi:
57
57
  2. Nếu `$ARGUMENTS` là một **UC-ID / ticket ID / tên rút gọn** (không có path) → phân giải thành file bằng cách glob theo bố cục feature-package. `{prd-slug}` lúc này **chưa biết**, nên dùng wildcard `*` cho segment đó, và `**` đệ quy dưới `bdd/` để phủ hết các thư mục con theo platform (`bdd/web/`, `bdd/app/`, `bdd/system/`):
58
58
  - **Lệnh BDD** (target là `.feature`): `{specs_dir}/{domain}/*/bdd/**/{UC-ID}*.feature` — hoặc `{specs_dir}/*/*/bdd/**/{UC-ID}*.feature` nếu domain cũng chưa biết. Nếu lệnh ngụ ý một platform/scope cụ thể (vd: system tech-doc cần BDD `system/`), ưu tiên kết quả trong thư mục con platform đó.
59
59
  - **Lệnh PRD** (target là file PRD `{TICKET-ID}-{prd-slug}.md` — file `.md` duy nhất ở gốc feature folder, cạnh `bdd/`): `{specs_dir}/{domain}/*/{TICKET-ID}*.md` nếu biết TICKET-ID; nếu không, `{specs_dir}/{domain}/*/*.md` (khớp feature folder có id tương ứng), hoặc `{specs_dir}/*/*/*.md` nếu domain cũng chưa biết. *(Glob `*/*.md` ở cấp gốc folder chỉ khớp PRD — tech-docs/design-spec `.md` nằm sâu hơn trong thư mục con.)*
60
- - **Lệnh tech-docs**: `{specs_dir}/{domain}/*/tech-docs/{UC-ID}*-tech-design*.md`.
60
+ - **Lệnh tech-docs** — target là tech-doc **gộp cấp PRD** `{TICKET-ID}-tech-design.md` (MỘT doc phủ nhiều UC; danh sách UC nằm ở `@trace.ucs`). Vì tên file mang `{TICKET-ID}` chứ **không** mang `{UC-ID}`, phải tách trước khi glob:
61
+ - `$ARGUMENTS` là **UC-ID** (`{TICKET-ID}-UC{N}`) → lấy `{TICKET-ID}` = phần **trước** `-UC`, rồi glob `{specs_dir}/{domain}/*/tech-docs/{TICKET-ID}-tech-design.md`.
62
+ - `$ARGUMENTS` là **TICKET-ID** → glob trực tiếp như trên.
63
+ - Chưa biết domain → `{specs_dir}/*/*/tech-docs/{TICKET-ID}-tech-design.md`.
64
+ - Vẫn không khớp → glob rộng `{specs_dir}/*/*/tech-docs/*tech-design*.md` rồi liệt kê để người dùng chọn.
65
+ *(Đừng glob `{UC-ID}*-tech-design*.md` — nó nở thành `FT-001-UC1*-tech-design*.md` và **không bao giờ** khớp `FT-001-tech-design.md`.)*
61
66
  - **Lệnh design-spec**: `{specs_dir}/{domain}/*/design-spec/{TICKET-ID}*.md`.
62
67
 
63
68
  Khi một file khớp: đặt nó làm target **và** ghi lại `domain` + `prd_slug` từ path của nó (theo quy tắc trích xuất trong `context-loader.md` Bước 1 — `prd_slug` = segment đầu tiên sau `{specs_dir}/{domain}/`). Mọi path mà lệnh đọc/ghi về sau (BDD/tech-docs/design-spec/trace cùng cấp) đều dùng **`prd_slug` đã phân giải đó**, nên tất cả artifact nằm chung một feature package. Nếu nhiều file khớp (vd: nhiều platform), chọn theo platform/scope của lệnh hoặc liệt kê ra và hỏi.
@@ -537,6 +542,14 @@ Lệnh này giới hạn nghiêm ngặt trong **một file feature** được tr
537
542
  ```
538
543
  Chỉ tiếp khi Y. *(Khác FE: FE degrade êm để prototype qua mock; BE thì contract là sản phẩm chính → chặn mềm.)*
539
544
  - **Có §4 nhưng `@trace.status = draft/in-review`, HOẶC §12 GAP Register còn 🔴 blocker `open` chạm UC này** → WARN (không chặn): "contract chưa chốt / còn {n} blocker-GAP open — có thể phải rework khi contract đổi."
545
+ - **Tech-doc lỗi thời so với BDD** — so entry `{@trace.platform}` trong map `@trace.bdd_versions` của tech-doc vs `@trace.bdd_version` của `.feature` target. Tech-doc **cũ hơn** → WARN (không chặn), kể cả khi `@trace.status: approved`:
546
+ ```
547
+ ⚠️ §4 contract dựng từ BDD v{old}, .feature này giờ v{new}.
548
+ Doc vẫn 'approved' nên shape dưới đây được lấy nguyên văn — nhưng nó phản ánh
549
+ behavior CŨ. Nếu BDD đổi request/response/error thì code sinh ra sẽ sai từ nguồn.
550
+ Khuyến nghị: /generate-tech-docs {feature-file} → /review-tech-docs (cổng T3b) trước.
551
+ ```
552
+ *(Chỉ WARN chứ không chặn: BDD hay bump vì lý do không chạm contract — sửa từ ngữ step, thêm side-effect assertion. Người đọc warning là người biết. `/validate-traces` giữ cờ `TECHDOC_STALE_VS_BDD` song song.)*
540
553
  - **Có §4 + `@trace.status: approved` + 0 blocker-GAP** → dùng §4 làm nguồn contract (shape DTO/endpoint/error lấy nguyên văn từ đây, KHÔNG tự chế).
541
554
 
542
555
  ---
@@ -545,14 +558,21 @@ Lệnh này giới hạn nghiêm ngặt trong **một file feature** được tr
545
558
 
546
559
  > **Nguồn chuẩn quyết BE/FE = `@trace.platform` của FILE FEATURE** (`system` → BE · `web`/`app` → FE). KHÔNG dùng `platform_type` (suy từ module) để quyết BE/FE — nó chỉ dùng cho **idiom stack/module** (cú pháp, layer, thư viện). Lý do: repo fullstack một-module (vd Next.js có API route) có `platform_type` cố định một giá trị, nhưng vẫn có cả feature `system` (BE) lẫn `web` (FE) — chỉ tag của chính feature mới đúng.
547
560
 
548
- Parse `$ARGUMENTS` tìm flag `--phase`:
561
+ Parse `$ARGUMENTS` tìm flag `--phase` và `--force`:
549
562
 
550
563
  | Flag | Ý nghĩa |
551
564
  |---|---|
552
565
  | `--phase=ui` | FE Phase 1 — sinh UI + layer mock API từ System BDD contract |
553
566
  | `--phase=integration` | FE Phase 2 — thay mock adapter bằng lời gọi API thật từ tech docs |
567
+ | `--force` | "Gen lại tường minh" — **CHỈ** bỏ qua guard status ở §Read Trace State (không skip row đang `OK`). Xem định nghĩa hẹp bên dưới. |
554
568
  | *(không có)* | Default — full: **BE/`system`** → full backend; **FE (`web`/`app`)** → **FE full** (sinh UI + wire API thật trong một lần, không qua bước mock) |
555
569
 
570
+ > **`--force` có phạm vi HẸP — đây là ranh giới cứng, không phải khuyến nghị.**
571
+ > Nó bỏ qua **đúng một** thứ: luật "row `OK` thì skip" ở §Read Trace State. **Mọi guard khác giữ nguyên hiệu lực:** Scope Lock (cấm implement scenario của `.feature` khác) · quy tắc EXTEND phi-phá-huỷ (đọc lại trước khi ghi · CẤM full Write trên file đã tồn tại · output phải là superset chặt) · Guard sau-ghi · Fill-before-create · Build Verify.
572
+ > `--force` **KHÔNG** phải "ghi đè tất cả". Không có cờ nào trong lệnh này cho phép điều đó — mất member/tag của UC khác luôn là lỗi chặn, kể cả với `--force`.
573
+ >
574
+ > Dùng khi: tech-doc bump revision có đụng thật phần điều khiển UC này, hoặc cần dựng lại code cho một scenario đang `OK`. **Đọc diff của nguồn TRƯỚC** — nếu revision bump không đụng UC này (vd chỉ thêm UC khác vào doc gộp) thì sinh lại code chỉ để đồng bộ một dòng nhãn là rủi ro không đáng.
575
+
556
576
  **Xác định `fe_full`:** khi **KHÔNG** có `--phase` VÀ `@trace.platform` là `web`/`app` → đây là **FE full mode**. Sinh UI **và** wire API thật trong cùng một lần chạy, **bỏ qua** layer mock. Cụ thể: các section **sinh UI** chạy · **Mock API Layer** bị bỏ (chỉ dành `--phase=ui`) · **DS4** và **Integration Phase** VẪN chạy (xem điều kiện của từng section). BE/`system` ở default vẫn là full backend như trước.
557
577
 
558
578
  **Nếu `--phase` được set — xác nhận platform:**
@@ -677,8 +697,9 @@ Phân giải design điều khiển adapter từ **tech-doc gộp của PRD** `{
677
697
  |--------|---------|-------------------|
678
698
  | `UNTRACKED` | `implemented_by == —` | Generate — scenario chưa có code |
679
699
  | `DRIFT` | `spec_ver != gen_ver` | Sửa **tại chỗ đúng method** của scenario đó (Edit) — KHÔNG viết lại cả file (file chung sẽ mất method UC khác) |
680
- | `OK` | đã implement + test | Skip trừ khi gen lại tường minh |
700
+ | `OK` | đã implement + test | **Skip** trừ khi `--force` (xem §Phase Detection). Sinh lại thì sửa **tại chỗ đúng method** (Edit), như hàng `DRIFT`.<br/>*(Tới đây vì cờ ⓘ `PRD_STALE_REF`/`TECHDOC_STALE_REF`? **Sai lệnh.** Hai cờ đó nghĩa là version bump KHÔNG đụng UC này — dùng `/validate-traces --realign-prd-version {UC-ID}` (chỉ sửa dòng nhãn, không đụng logic). Chỉ dùng `--force` khi cờ là 🟠 `PRD_DRIFT`/`TECHDOC_DRIFT` — nội dung đổi thật.)* |
681
701
  | `GAP` | đã implement, chưa test | Skip codegen — đã code rồi; chạy `/dev-gen-test` thay vì |
702
+ | `ORPHANED` | SC không còn trong `.feature` nhưng code còn | **Skip codegen** — không có scenario nào để implement. **KHÔNG xoá** code/row (cần người quyết định behavior đó còn cần hay không). Nêu ở report cuối: `⚠️ {sc_id} ORPHANED — code {implemented_by} còn tồn tại nhưng scenario đã bị xoá khỏi .feature. Xử: xoá code+test, hoặc đưa scenario trở lại. (/validate-traces giữ cờ 🔴.)` |
682
703
 
683
704
  Dùng các status này để điền số **Scenarios** trong plan CHECKPOINT (`{X} new, {Y} drifted, {Z} synced-skip`).
684
705
  Nếu `.tsv` không tồn tại → coi mọi scenario là `UNTRACKED`.
@@ -874,16 +895,39 @@ DTOs → Entity/Model → Repository → Service interface → Service impl →
874
895
  @trace.prd_version={đọc @trace.prd_version từ header file .feature}
875
896
  @trace.bdd_version={đọc @trace.bdd_version từ header file .feature}
876
897
  @trace.tech_doc_revision={đọc @trace.revision từ header tech-doc, hoặc bỏ nếu không có tech-doc}
877
- @trace.source={paths.specs_dir}/{domain}/{prd-slug}/bdd/{UC-ID}-{slug}.feature
898
+ @trace.design_spec_version={CHỈ FE/App (@trace.platform = web|app): đọc | **Version** | từ Metadata design-spec đã nạp. BỎ HẲN dòng này với system/backend}
899
+ @trace.source={paths.specs_dir}/{domain}/{prd-slug}/bdd/{@trace.platform}/{UC-ID}-{slug}.feature
878
900
  ```
879
901
 
880
902
  `@trace.prd_version` ghi code này được viết theo version PRD nào.
881
903
  `@trace.bdd_version` ghi code này được sinh từ version BDD nào.
882
904
  `@trace.tech_doc_revision` ghi code này theo revision tech-design nào.
905
+ `@trace.design_spec_version` *(chỉ FE/App)* ghi code này dựng theo version design-spec nào — nguồn của `DESIGNSPEC_DRIFT`. **Vì sao cần:** design-spec là input BẮT BUỘC của code FE (màn hình, component inventory, link Figma frame) và của cả BDD FE/App, nhưng trước đây nó là artifact upstream **DUY NHẤT** không có cột TSV, không có tag trong code, không có cờ drift — designer sửa design-spec sau khi code đã sinh thì không gì phát hiện được.
883
906
  `/validate-traces` sẽ gắn cờ drift nếu bất kỳ artifact upstream nào được cập nhật lên version mới hơn.
884
907
 
885
908
  > **Quy tắc entry-point:** `@trace.implements` phải xuất hiện ở **layer entry-point** như định nghĩa trong `CLAUDE.md §2`. Với REST API → Controller. Với module event-driven → event handler / consumer class. Với context-engineering → hàm orchestration prompt. Không bao giờ chỉ đặt ở layer trong.
886
909
 
910
+ > **File phủ NHIỀU UC → lặp CẢ BLOCK 5 tag, đặt trên method của từng UC. CẤM trỏ thư mục, CẤM gộp về một header file.**
911
+ >
912
+ > Đây là hình dạng đúng:
913
+ > ```
914
+ > // @trace.implements=USR-UC1-SC3
915
+ > // @trace.prd_version=1.2 @trace.bdd_version=1.4 @trace.tech_doc_revision=3
916
+ > // @trace.source=specs/user/create-account/bdd/system/USR-UC1-create-account.feature
917
+ > public AccountDto createAccount(...) { }
918
+ >
919
+ > // @trace.implements=USR-UC3-SC1
920
+ > // @trace.prd_version=2.0 @trace.bdd_version=2.1 @trace.tech_doc_revision=5
921
+ > // @trace.source=specs/user/create-account/bdd/system/USR-UC3-verify-email.feature
922
+ > public void verifyEmail(...) { }
923
+ > ```
924
+ >
925
+ > **Vì sao không được gộp:** 3 tag version là **scalar theo từng UC**. Một file phủ UC1 + UC3 mà chỉ có một header thì không diễn đạt được "UC1 ở bdd v1.4, UC3 ở v2.1" → `/validate-traces` Step 4/5/5c báo drift oan hoặc **mù** drift thật. Version phải nằm cạnh member nó mô tả.
926
+ >
927
+ > **Vì sao không được trỏ thư mục** (`@trace.source=…/bdd/system/`): độ phân giải của trace là `UC × SC`, thư mục làm mất cả hai bậc. Và các lệnh tra tag bằng **khớp chuỗi chính xác** (`/dev-gen-test`, `/dev-smoke-test`, `/review-code` đều tìm "file gắn `@trace.implements={UC-ID}`") → tag trỏ folder ra 0 kết quả, UC rơi về `UNTRACKED` dù code đã có.
928
+ >
929
+ > Quy tắc EXTEND ở §File Scan vốn đã yêu cầu giữ **nguyên si** mọi `@trace.implements` cũ *kể cả của UC khác* — tức là thiết kế vốn là **tích luỹ nhiều block**, không phải gộp lại.
930
+
887
931
  > **Quy tắc nguồn giá trị (chống hard-code):** MỌI giá trị cụ thể (endpoint path, error code, tên field/DTO, enum, limit/timeout, header) phải lấy từ **nguồn đã chốt** — **KHÔNG bịa inline**. Nếu một hằng số nghiệp vụ lặp lại hoặc mang ý nghĩa (retry count, ngưỡng, key) → **đặt tên hằng số** (constant/config), không rải magic number/string trong code.
888
932
  >
889
933
  > **VÉT CẠN NGUỒN TRƯỚC KHI HỎI (SRC-CHAIN) — bắt buộc.** Khi một giá trị chưa thấy ở nguồn chính, PHẢI quét lần lượt các nguồn đã có trong context/spec-package theo thứ tự sau, **dừng ngay khi tìm thấy** (skip-if-answered), KHÔNG hỏi người ngay:
@@ -1009,22 +1053,66 @@ Cập nhật `{paths.trace_dir}/{domain}/{prd-slug}/{UC-ID}-{@trace.platform}.ts
1009
1053
  | `bdd_version` | `@trace.bdd_version` từ header `.feature` |
1010
1054
  | `tech_doc_revision` | `@trace.revision` từ tech-doc gộp `{TICKET-ID}-tech-design.md` (§4 backend đã điều khiển codegen của UC này), hoặc `—` nếu chưa có doc |
1011
1055
  | `fe_tech_doc_revision` | `@trace.revision` của cùng tech-doc gộp, ghi khi sinh FE có wire adapter theo §4.5.4 (`--phase=integration` **hoặc** `fe_full`); `—` cho BE, hoặc cho FE `--phase=ui` / chưa có §4.5.4 |
1012
- | `fe_phase` | `ui` nếu `--phase=ui` \| `integrated` nếu `--phase=integration` **hoặc** `fe_full` (đều đã wire real adapter) \| `—` cho BE |
1056
+ | `fe_phase` | `ui` nếu `--phase=ui` \| `integration` nếu `--phase=integration` **hoặc** `fe_full` (đều đã wire real adapter) \| `—` cho BE |
1013
1057
  | `last_updated` | hôm nay `YYYY-MM-DD` |
1014
1058
 
1015
- Giữ nguyên mọi cột khác (`sc_title`, `spec_ver`, `prd_version`, `prd_status`, `uc_status`, `test_count`, `test_classes`, `dev_selftest`, `dev_selftest_at`, `qc_status`, `qc_run_at`, `qc_owner`, `qc_blocked_by`).
1059
+ Giữ nguyên mọi cột khác (`sc_title`, `spec_ver`, `prd_version`, `prd_status`, `uc_status`, `test_count`, `test_classes`, `dev_selftest`, `dev_selftest_at`, `qc_status`, `qc_run_at`, `qc_owner`, `qc_blocked_by`) — **trừ ngoại lệ có kiểm soát ngay dưới đây**: khi logic vừa đổi thật (lấp stub, hoặc sửa method vì `DRIFT`), 4 cột nghiệm thu `dev_selftest`/`dev_selftest_at`/`qc_status`/`qc_run_at` **phải bị hạ** về "chưa biết". Giữ một `pass` đã hết hiệu lực là báo cáo sai, không phải tôn trọng quyền sở hữu cột.
1016
1060
  `status` được tính bởi `/validate-traces` — không set ở đây.
1017
1061
 
1018
- **Reset test khi lấp stub (Fill-before-create).** Nếu lần gen này **lấp** một/nhiều method stub (dòng sổ `→ RESOLVED`): logic vừa đổi thật test cũ viết trên hàm trắng đã cũ (nó có thể "xanh" chỉ vì hàm trắng `throw`/trả rỗng). Với **mọi scenario chạy qua method vừa lấp** gồm cả scenario của **consumer_uc** (UC đã để trắng, thường nằm file TSV khác `{consumer_uc}-{platform}.tsv`):
1019
- - `dev_selftest → not_run`, `dev_selftest_at → —` (ép chạy lại self-test).
1020
- - **CHỈ** đụng 2 cột test này ngoại lệ có kiểm soát của luật "giữ nguyên cột khác"; là thao tác an-toàn (không sửa code UC khác, chỉ hạ cờ test đã cũ).
1021
- - Gom danh sách `{consumer_uc}` bị ảnh hưởng để in ở "Next".
1062
+ **Hạ hiệu lực tín hiệu kiểm thử khi logic vừa đổi thật.** Áp cho **HAI** trường hợp cùng một do, cùng một tập cột *(luật "Làm mất hiệu lực ghi đè", `rules/workflow.md`)*:
1063
+
1064
+ | Trường hợp | Phạm vi scenario bị ảnh hưởng |
1065
+ |---|---|
1066
+ | **A. Lấp stub** (Fill-before-create — dòng sổ `→ RESOLVED`) | **Mọi** scenario chạy qua method vừa lấp — gồm cả scenario của **consumer_uc** (UC đã để trắng, thường nằm ở file TSV khác `{consumer_uc}-{platform}.tsv`) |
1067
+ | **B. Sửa method vì row đang `DRIFT`** (spec đổi sau lần gen trước) | Đúng các SC vừa được sửa method trong lần chạy này |
1068
+
1069
+ Với mỗi scenario trong phạm vi:
1070
+ - `dev_selftest → not_run` · `dev_selftest_at → —` · `qc_status → not_run` · `qc_run_at → —`.
1071
+ - **CHỈ** đụng 4 cột này — ngoại lệ có kiểm soát của luật "giữ nguyên cột khác" ở trên; là thao tác an-toàn (không sửa code UC khác, chỉ hạ cờ nghiệm thu đã hết hiệu lực).
1072
+ - **KHÔNG** đụng `test_count`/`test_classes` (test vẫn tồn tại — số lượng không sai, chỉ nội dung cũ; hạ số sẽ làm tỷ lệ coverage nhảy loạn) và **KHÔNG** đụng `qc_owner`/`qc_blocked_by` (con trỏ tới bug — code đổi không làm bug biến mất).
1073
+ - Gom danh sách `{consumer_uc}` bị ảnh hưởng (trường hợp A) để in ở "Next".
1074
+
1075
+ > **Vì sao trường hợp B cũng phải hạ:** lý do giống hệt A — logic vừa đổi thật, nên test cũ đang nghiệm thu một hành vi không còn tồn tại. Trước đây chỉ A được xử lý, nên chuỗi "spec đổi → `DRIFT` → sửa code → `OK`" kết thúc với `qc_status = pass` từ lần QC chạy trên **spec cũ**, và dashboard hiện xanh hoàn toàn. `/fix-bug` đã làm đúng việc này từ trước với chính lời giải thích đó: *"code vừa đổi nên tín hiệu self-test cũ hết hiệu lực"*.
1076
+
1077
+ Bất kể trường hợp nào, in khối này ở report cuối để dev không tưởng là hệ thống hỏng:
1078
+ ```
1079
+ 🔻 Tín hiệu kiểm thử bị hạ ({spec vừa đổi | vừa lấp stub} — nghiệm thu cũ hết hiệu lực):
1080
+ {sc_id}: dev_selftest pass→not_run · qc_status pass→not_run
1081
+ ⚠️ {n} test của các SC này viết cho bản cũ — rà lại nội dung, đừng chỉ chạy lại.
1082
+ → /dev-run-test {UC-ID} rồi QC chạy /qc-run-test {UC-ID}
1083
+ ℹ️ Coverage "đã kiểm đạt" trên dashboard sẽ TỤT sau lần này — đó là số đúng;
1084
+ số cũ mới là số sai. (Tỷ lệ phủ code/test không đổi — test_count giữ nguyên.)
1085
+ ```
1022
1086
 
1023
1087
  ## Refresh Panel Mirror
1024
- # Làm mới panel mirror của Living Docs *(local, chế độ umbrella)*
1088
+ # Làm mới panel mirror của Living Docs *(local)*
1089
+
1090
+ > **Hai vị trí, HAI TÊN KHÁC NHAU — đọc trước khi sửa gì ở đây.**
1091
+ >
1092
+ > | Đường dẫn | Vai trò | Git |
1093
+ > |---|---|---|
1094
+ > | `{paths.trace_dir}` (`.trace/` hoặc `{spec_source}/.trace/`) | **AUTHORITATIVE** — TSV + `trace-history.jsonl`. Không regenerate được. | **PHẢI commit** |
1095
+ > | `./.trace-mirror/` ở gốc workspace hiện tại | **MIRROR** — bản sao tiện cho panel VS Code. Sinh lại được bất cứ lúc nào. | **Luôn gitignore** |
1096
+ >
1097
+ > Trước v0.4.3 cả hai đều tên `.trace`, nên một luật gitignore theo tên có thể **xoá sạch sổ gốc**
1098
+ > khi dev mở thẳng spec repo làm workspace (lúc đó hai path bằng nhau). Hai tên khác nhau làm
1099
+ > luật git đọc được bằng mắt và **không còn ca nhập nhằng nào**: `.trace-mirror/` không bao giờ
1100
+ > commit, `.trace/` không bao giờ gitignore.
1101
+
1102
+ ## Khi nào CÓ mirror
1025
1103
 
1026
- *Bỏ qua hoàn toàn chế độ single-service (không `services` không `setup.spec_source`) đó
1027
- `.trace/` của chính repo CHÍNH LÀ vị trí panel, nên không có gì để mirror.*
1104
+ Mirror chỉ tồn tại khi **`{paths.trace_dir}` nằm NGOÀI workspace hiện tại** panel đọc từ workspace đang mở nên cần một bản sao đây.
1105
+
1106
+ | Tình huống | `{paths.trace_dir}` | Có mirror? |
1107
+ |---|---|---|
1108
+ | Single-service | `./.trace` — **trong** workspace | ❌ Không. Panel đọc thẳng `.trace/trace-report.json`. Bỏ qua cả file này. |
1109
+ | Dev mở thẳng **spec repo** | `./.trace` — **trong** workspace | ❌ Không. Như trên. |
1110
+ | Umbrella + `spec_source`, dev đứng ở umbrella hoặc service submodule | `{spec_source}/.trace` — **ngoài** workspace | ✅ Có |
1111
+ | Umbrella legacy (không `spec_source`) | `.trace` theo từng service | ✅ Có |
1112
+
1113
+ Quy tắc một dòng: **phân giải `panel_mirror = ./.trace-mirror` ở gốc workspace hiện tại; nếu `{paths.trace_dir}` đã nằm trong workspace này thì bỏ qua toàn bộ bước mirror.**
1114
+
1115
+ ---
1028
1116
 
1029
1117
  Sau khi cập nhật TSV authoritative tại `{paths.trace_dir}`:
1030
1118
 
@@ -1032,11 +1120,14 @@ Sau khi cập nhật TSV authoritative tại `{paths.trace_dir}`:
1032
1120
  `{paths.trace_dir}` phân giải về `{spec_source}/.trace` — vị trí authoritative duy nhất.
1033
1121
  Lệnh này chạy từ `service_root`, nên thao tác ghi là **liên-repo vào spec submodule**;
1034
1122
  commit/push spec submodule cho lần cập nhật trace (giống như `feedback/`).
1035
- 1. Phân giải `panel_mirror = ./.trace` tại **gốc workspace hiện tại**.
1036
- 2. Nếu `panel_mirror` phân giải ra path khác với `{paths.trace_dir}`, copy mỗi
1123
+
1124
+ 1. Phân giải `panel_mirror = ./.trace-mirror` tại **gốc workspace hiện tại**.
1125
+ 2. Nếu `{paths.trace_dir}` **không** nằm trong workspace hiện tại, copy mỗi
1037
1126
  `{UC-ID}-{platform}.tsv` vừa cập nhật → `{panel_mirror}/{UC-ID}-{platform}.tsv` (tạo thư mục; ghi đè).
1038
- Không namespace theo service — chỉ có một bộ trace; service sở hữu được mang trong
1039
- `@trace.service` của từng row.
1127
+ Không namespace theo service — chỉ có một bộ trace; service sở hữu được mang
1128
+ **cột `service` (cột 23)** của chính từng row, do `/generate-bdd` ghi từ `@trace.service`.
1129
+ 3. **KHÔNG copy `trace-history.jsonl`.** Nó là dữ liệu tích luỹ, không phải thứ sinh lại được —
1130
+ nhân bản nó ra một thư mục gitignore là tạo hai lịch sử lệch nhau rồi mất bản thật.
1040
1131
 
1041
1132
  **Legacy (không có `spec_source` — trace theo service):**
1042
1133
  Copy mỗi `{UC-ID}-{platform}.tsv` vừa cập nhật → `{panel_mirror}/{service-name}/{UC-ID}-{platform}.tsv`
@@ -1093,7 +1184,7 @@ Tìm lệnh hiện tại trong bảng phase dưới đây và đánh dấu **pha
1093
1184
  | Phase | Commands |
1094
1185
  |-------|----------|
1095
1186
  | Discovery | `/define-product` |
1096
- | PRD | `/generate-prd` · `/refine-prd` · `/review-context` (PRD) |
1187
+ | PRD | `/generate-prd` · `/extend-prd` · `/refine-prd` · `/review-context` (PRD) |
1097
1188
  | Design Spec | `/generate-design-spec` |
1098
1189
  | BDD | `/generate-bdd` · `/review-context` (BDD) |
1099
1190
  | Tech Design | `/generate-tech-docs` · `/map-testids` · `/review-tech-docs` |
@@ -1118,6 +1209,7 @@ Gợi ý lệnh kế tiếp hợp lý theo phase của workflow:
1118
1209
  | /setup-ai-first | `/define-product` để bắt đầu feature đầu tiên |
1119
1210
  | /define-product | `/generate-prd {product-definition-file}` |
1120
1211
  | /generate-prd | `/refine-prd {prd-file}` rồi `/review-context {prd-file}` |
1212
+ | /extend-prd | `/refine-prd {prd-file}` (soi phần vừa thêm) rồi `/review-context {prd-file}` → PO duyệt → `/generate-bdd` **chỉ cho UC MỚI**; UC cũ dùng `/validate-traces --realign-prd-version {UC-ID}` |
1121
1213
  | /refine-prd | Mở Review Board → cập nhật PRD → `/review-context {prd-file}` |
1122
1214
  | /review-context (PRD) | Khi 0 critical → PO đặt `Status: approved`, rồi FE/App: `/generate-design-spec {prd-file}` (→ design sign-off → BDD); BE: `/generate-bdd {prd-file}`. Còn critical/NEEDS_FIX → sửa PRD (giữ draft) |
1123
1215
  | /generate-design-spec | Designer review → xác nhận link Figma → PO + Designer sign-off → `/generate-bdd {prd-file}` |
@@ -1130,6 +1222,7 @@ Gợi ý lệnh kế tiếp hợp lý theo phase của workflow:
1130
1222
  | /qc-run-test | `/qc-report {UC-ID}` rồi `/qc-review {UC-ID}` (review script) |
1131
1223
  | /qc-review (script) | `/qc-report {UC-ID}` rồi tạo PR nếu APPROVED |
1132
1224
  | /qc-report | `/validate-traces {UC-ID}` để làm mới Living Docs (qc_status) |
1225
+ | /map-testids | `/qc-design-test {UC-ID}` (QC dựng Page Object từ contract §4.5.6 vừa ghi) |
1133
1226
  | /generate-tech-docs | `/review-tech-docs {tech-design-file}` |
1134
1227
  | /review-tech-docs | `/generate-code {feature-file}` nếu APPROVED; sửa doc nếu NEEDS_FIX |
1135
1228
  | /generate-code | Lần gen đầu → `/review-code {UC-ID}`; gen lại → `/dev-gen-test {UC-ID}` |
@@ -1138,11 +1231,11 @@ Gợi ý lệnh kế tiếp hợp lý theo phase của workflow:
1138
1231
  | /dev-run-test (failing) | `/fix-bug {ticket-id}` hoặc `/debug {error}` |
1139
1232
  | /review-code | `/dev-smoke-test {UC-ID}` hoặc tạo PR |
1140
1233
  | /dev-smoke-test | Tạo PR và link tới ticket |
1141
- | /validate-traces | DRIFT/UNTRACKED → `/generate-code {UC-ID}`; GAP → `/dev-gen-test {UC-ID}`; tất cả OK tạo PR |
1142
- | /fix-bug | Tạo PR link tới ticket |
1234
+ | /validate-traces | **Cờ 🔴 trước (chặn PR):** SEAM_UNWIRED → nối binding sang class thật, xoá/thay stub · STUB_UNRESOLVED → `/generate-code {owner_uc}` (lấp logic tại chỗ + xoá hàm song song) · ORPHANED/TRACE_ORPHAN → quyết định thủ công (xoá code+test, đưa scenario trở lại `.feature`, hoặc sửa `sc_id` của tag). **Rồi:** DRIFT/UNTRACKED → `/generate-code {UC-ID}` · BDD_DRIFT → `/generate-code {feature-file}` · tech-doc lỗi thời vs BDD → `/generate-tech-docs` → `/review-tech-docs` · PRD drift → `/generate-bdd {prd-file}` · GAP → `/dev-gen-test {UC-ID}`. **Chỉ tạo PR khi mọi cờ 🔴 = 0** |
1235
+ | /fix-bug | `/dev-run-test {UC-ID}` (dev_selftest vừa reset về not_run) → tạo PR; nếu fix một `{BUG-ID}` → QC chạy `/qc-run-test {UC-ID}` để verify + đóng bug |
1143
1236
  | /debug | `/fix-bug {ticket-id}` nếu cần sửa |
1144
1237
  | /report-bug | Gửi cho dev (`/fix-bug {BUG-ID}`); nếu thiếu coverage → `/propose-scenario {UC-ID}` |
1145
- | /propose-scenario | Báo PO/Dev review proposal trong `feedback/bdd-proposals/` |
1238
+ | /propose-scenario | **Case A** (thiếu scenario cho AC có sẵn) → báo PO/Dev review trong `feedback/bdd-proposals/`; `/generate-bdd` tự chèn khi `Status: accepted`. **Case B** (requirement mới) → `feedback/prd-change-requests/` — PO phải đưa vào PRD trước, KHÔNG tự vào BDD được; `/validate-traces` nhắc lại kèm số ngày chờ chừng nào `Status: Open` |
1146
1239
  | /learn | Tiếp tục làm việc — lesson áp dụng ở lệnh kế tiếp |
1147
1240
  | /sync | `/validate-traces` để xem độ phủ đầy đủ; xử lý mọi `📥 tester feedback` được nêu |
1148
1241
  | /update-framework | Review `git diff .agent/`, commit; `/sync` để đồng bộ nội dung dự án |
@@ -1164,7 +1257,7 @@ Next : {lệnh gợi ý kèm ví dụ tham số}
1164
1257
  Files: created={N}, extended={M}, filled={F} stub, skipped={K} | Build: SUCCESS
1165
1258
  Branch: feature/{TICKET_ID}-{slug}
1166
1259
  Phase : {UI (mock layer) | Integration (real API) | FE full (UI + real API) | BE full}
1167
- fe_phase : {ui | integrated (—phase=integration | fe_full) | —}
1260
+ fe_phase : {ui | integration (—phase=integration | fe_full) | —}
1168
1261
  Figma : {Dev Mode MCP local (grounded) | ⚠️ chỉ link web + text spec (không có MCP local) | n/a cho BE} ← chỉ UI FE/App
1169
1262
 
1170
1263
  Next: