@educa-corp/sdd-framework 0.4.0 → 0.4.2
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.
- package/bin/build.js +9 -0
- package/bin/index.js +115 -4
- package/bin/self-check.js +236 -0
- package/bin/trace-schema.json +692 -0
- package/commands/debug.md +16 -10
- package/commands/define-product.md +16 -10
- package/commands/dev-gen-test.md +16 -10
- package/commands/dev-run-test.md +18 -11
- package/commands/dev-run-test.tmpl +2 -1
- package/commands/dev-smoke-test.md +16 -10
- package/commands/fix-bug.md +71 -13
- package/commands/fix-bug.tmpl +29 -3
- package/commands/generate-architecture.md +16 -10
- package/commands/generate-bdd.md +118 -35
- package/commands/generate-bdd.tmpl +89 -15
- package/commands/generate-code.md +49 -13
- package/commands/generate-code.tmpl +33 -3
- package/commands/generate-design-spec.md +16 -10
- package/commands/generate-prd.md +16 -10
- package/commands/generate-spec-manifest.md +16 -10
- package/commands/generate-tech-docs.md +19 -13
- package/commands/generate-tech-docs.tmpl +2 -2
- package/commands/learn.md +16 -10
- package/commands/map-testids.md +16 -10
- package/commands/propose-scenario.md +36 -12
- package/commands/propose-scenario.tmpl +20 -2
- package/commands/qc-analyze.md +16 -10
- package/commands/qc-design-test.md +16 -10
- package/commands/qc-plan.md +16 -10
- package/commands/qc-report.md +16 -10
- package/commands/qc-review.md +16 -10
- package/commands/qc-run-test.md +38 -12
- package/commands/qc-run-test.tmpl +22 -2
- package/commands/refine-prd.md +16 -10
- package/commands/report-bug.md +16 -10
- package/commands/review-code.md +56 -12
- package/commands/review-code.tmpl +40 -2
- package/commands/review-context.md +58 -14
- package/commands/review-context.tmpl +42 -4
- package/commands/review-tech-docs.md +47 -12
- package/commands/review-tech-docs.tmpl +31 -2
- package/commands/setup-ai-first.md +23 -14
- package/commands/setup-ai-first.tmpl +7 -4
- package/commands/sync.md +3 -2
- package/commands/update-framework.md +40 -2
- package/commands/update-framework.tmpl +37 -0
- package/commands/validate-traces.md +165 -18
- package/commands/validate-traces.tmpl +149 -8
- package/core/FRAMEWORK_VERSION +1 -1
- package/core/README.md +56 -0
- package/core/commands/debug.md +16 -10
- package/core/commands/define-product.md +16 -10
- package/core/commands/dev-gen-test.md +16 -10
- package/core/commands/dev-run-test.md +18 -11
- package/core/commands/dev-smoke-test.md +16 -10
- package/core/commands/fix-bug.md +71 -13
- package/core/commands/generate-architecture.md +16 -10
- package/core/commands/generate-bdd.md +118 -35
- package/core/commands/generate-code.md +49 -13
- package/core/commands/generate-design-spec.md +16 -10
- package/core/commands/generate-prd.md +16 -10
- package/core/commands/generate-spec-manifest.md +16 -10
- package/core/commands/generate-tech-docs.md +19 -13
- package/core/commands/learn.md +16 -10
- package/core/commands/map-testids.md +16 -10
- package/core/commands/propose-scenario.md +36 -12
- package/core/commands/qc-analyze.md +16 -10
- package/core/commands/qc-design-test.md +16 -10
- package/core/commands/qc-plan.md +16 -10
- package/core/commands/qc-report.md +16 -10
- package/core/commands/qc-review.md +16 -10
- package/core/commands/qc-run-test.md +38 -12
- package/core/commands/refine-prd.md +16 -10
- package/core/commands/report-bug.md +16 -10
- package/core/commands/review-code.md +56 -12
- package/core/commands/review-context.md +58 -14
- package/core/commands/review-tech-docs.md +47 -12
- package/core/commands/setup-ai-first.md +23 -14
- package/core/commands/sync.md +3 -2
- package/core/commands/update-framework.md +40 -2
- package/core/commands/validate-traces.md +165 -18
- package/core/modules/android-compose/stack-profile.yaml +1 -1
- package/core/modules/flutter/stack-profile.yaml +1 -1
- package/core/modules/ios-swiftui/stack-profile.yaml +1 -1
- package/core/modules/java-spring/stack-profile.yaml +1 -1
- package/core/modules/nextjs/stack-profile.yaml +1 -1
- package/core/modules/nuxt/stack-profile.yaml +1 -1
- package/core/modules/phaser-game/stack-profile.yaml +1 -1
- package/core/modules/php-laravel/stack-profile.yaml +1 -1
- package/core/modules/qc-playwright/stack-profile.yaml +1 -1
- package/core/modules/react/stack-profile.yaml +1 -1
- package/core/modules/react-native/stack-profile.yaml +1 -1
- package/core/modules/vue/stack-profile.yaml +1 -1
- package/core/rules/workflow.md +11 -0
- package/core/steps/gate.md +13 -8
- package/core/steps/report-footer.md +3 -2
- package/core/templates/README.md +47 -0
- package/core/templates/feature.template +13 -10
- package/core/templates/project-context.yaml +26 -14
- package/core/templates/tech-design.template.md +1 -1
- package/docs/02-concepts/traceability.md +29 -6
- package/docs/04-reference/trace-schema.md +128 -37
- package/modules/android-compose/stack-profile.yaml +1 -1
- package/modules/flutter/stack-profile.yaml +1 -1
- package/modules/ios-swiftui/stack-profile.yaml +1 -1
- package/modules/java-spring/stack-profile.yaml +1 -1
- package/modules/nextjs/stack-profile.yaml +1 -1
- package/modules/nuxt/stack-profile.yaml +1 -1
- package/modules/phaser-game/stack-profile.yaml +1 -1
- package/modules/php-laravel/stack-profile.yaml +1 -1
- package/modules/qc-playwright/stack-profile.yaml +1 -1
- package/modules/react/stack-profile.yaml +1 -1
- package/modules/react-native/stack-profile.yaml +1 -1
- package/modules/vue/stack-profile.yaml +1 -1
- package/package.json +50 -49
- package/rules/workflow.md +11 -0
- package/scripts/migrate-bdd-platform.js +286 -0
- package/steps/gate.md +13 -8
- package/steps/report-footer.md +3 -2
- package/templates/README.md +47 -0
- package/templates/feature.template +13 -10
- package/templates/project-context.yaml +26 -14
- package/templates/tech-design.template.md +1 -1
|
@@ -45,23 +45,23 @@ Hiển thị và chờ phản hồi:
|
|
|
45
45
|
```
|
|
46
46
|
⚙️ MODEL CHECK
|
|
47
47
|
──────────────────────────────────────────────────────────────────
|
|
48
|
-
Recommended :
|
|
48
|
+
Recommended : model Opus mới nhất
|
|
49
49
|
Why needed : Phân tích spec, review kiến trúc, sinh code đòi hỏi
|
|
50
|
-
suy luận sâu. Model nhỏ hơn dễ bỏ sót edge case.
|
|
50
|
+
suy luận sâu. Model nhỏ hơn (Haiku/Sonnet) dễ bỏ sót edge case.
|
|
51
51
|
|
|
52
52
|
Cách đổi trong Claude Code:
|
|
53
|
-
•
|
|
54
|
-
• hoặc:
|
|
53
|
+
• /model → chọn model Opus
|
|
54
|
+
• hoặc: Settings → Model
|
|
55
55
|
|
|
56
|
-
Đang chạy
|
|
57
|
-
Y — đúng
|
|
56
|
+
Đang chạy một model Opus?
|
|
57
|
+
Y — đúng → tiếp tục
|
|
58
58
|
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)
|
|
59
59
|
──────────────────────────────────────────────────────────────────
|
|
60
60
|
```
|
|
61
61
|
|
|
62
62
|
- "Y" → tiếp tục sang Bước 1.
|
|
63
63
|
- "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).
|
|
64
|
-
- "N" hoặc bất kỳ giá trị nào khác → **DỪNG.** Xuất: "Vui lòng chuyển sang
|
|
64
|
+
- "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."
|
|
65
65
|
|
|
66
66
|
## Bước 1 — Xác định Target File
|
|
67
67
|
|
|
@@ -70,7 +70,12 @@ Hiển thị và chờ phản hồi:
|
|
|
70
70
|
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/`):
|
|
71
71
|
- **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 đó.
|
|
72
72
|
- **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.)*
|
|
73
|
-
- **Lệnh tech-docs
|
|
73
|
+
- **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:
|
|
74
|
+
- `$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`.
|
|
75
|
+
- `$ARGUMENTS` là **TICKET-ID** → glob trực tiếp như trên.
|
|
76
|
+
- Chưa biết domain → `{specs_dir}/*/*/tech-docs/{TICKET-ID}-tech-design.md`.
|
|
77
|
+
- 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.
|
|
78
|
+
*(Đừ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`.)*
|
|
74
79
|
- **Lệnh design-spec**: `{specs_dir}/{domain}/*/design-spec/{TICKET-ID}*.md`.
|
|
75
80
|
|
|
76
81
|
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.
|
|
@@ -738,6 +743,7 @@ Gợi ý lệnh kế tiếp hợp lý theo phase của workflow:
|
|
|
738
743
|
| /qc-run-test | `/qc-report {UC-ID}` rồi `/qc-review {UC-ID}` (review script) |
|
|
739
744
|
| /qc-review (script) | `/qc-report {UC-ID}` rồi tạo PR nếu APPROVED |
|
|
740
745
|
| /qc-report | `/validate-traces {UC-ID}` để làm mới Living Docs (qc_status) |
|
|
746
|
+
| /map-testids | `/qc-design-test {UC-ID}` (QC dựng Page Object từ contract §4.5.6 vừa ghi) |
|
|
741
747
|
| /generate-tech-docs | `/review-tech-docs {tech-design-file}` |
|
|
742
748
|
| /review-tech-docs | `/generate-code {feature-file}` nếu APPROVED; sửa doc nếu NEEDS_FIX |
|
|
743
749
|
| /generate-code | Lần gen đầu → `/review-code {UC-ID}`; gen lại → `/dev-gen-test {UC-ID}` |
|
|
@@ -746,8 +752,8 @@ Gợi ý lệnh kế tiếp hợp lý theo phase của workflow:
|
|
|
746
752
|
| /dev-run-test (failing) | `/fix-bug {ticket-id}` hoặc `/debug {error}` |
|
|
747
753
|
| /review-code | `/dev-smoke-test {UC-ID}` hoặc tạo PR |
|
|
748
754
|
| /dev-smoke-test | Tạo PR và link tới ticket |
|
|
749
|
-
| /validate-traces | DRIFT/UNTRACKED → `/generate-code {UC-ID}
|
|
750
|
-
| /fix-bug |
|
|
755
|
+
| /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** |
|
|
756
|
+
| /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 |
|
|
751
757
|
| /debug | `/fix-bug {ticket-id}` nếu cần sửa |
|
|
752
758
|
| /report-bug | Gửi cho dev (`/fix-bug {BUG-ID}`); nếu thiếu coverage → `/propose-scenario {UC-ID}` |
|
|
753
759
|
| /propose-scenario | Báo PO/Dev review proposal trong `feedback/bdd-proposals/` |
|
package/commands/generate-bdd.md
CHANGED
|
@@ -32,23 +32,23 @@ Hiển thị và chờ phản hồi:
|
|
|
32
32
|
```
|
|
33
33
|
⚙️ MODEL CHECK
|
|
34
34
|
──────────────────────────────────────────────────────────────────
|
|
35
|
-
Recommended :
|
|
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
|
-
•
|
|
41
|
-
• hoặc:
|
|
40
|
+
• /model → chọn model Opus
|
|
41
|
+
• hoặc: Settings → Model
|
|
42
42
|
|
|
43
|
-
Đang chạy
|
|
44
|
-
Y — đúng
|
|
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
|
|
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
|
|
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.
|
|
@@ -481,7 +486,16 @@ Tiếp tục sang bước kế tiếp của lệnh đang gọi.
|
|
|
481
486
|
|
|
482
487
|
|
|
483
488
|
> **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:
|
|
484
|
-
> - `Status: accepted` (PO/Dev đã duyệt) → chèn scenario vào `.feature` của UC (
|
|
489
|
+
> - `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.
|
|
490
|
+
>
|
|
491
|
+
> **Normalize — bắt buộc, nếu không scenario sẽ vô hình với trace:**
|
|
492
|
+
> 1. Gán `# @trace.scenario: {UC-ID}-SC{N}` với `{N}` = số SC **kế tiếp** trong file đó (thay placeholder `SC?`).
|
|
493
|
+
> 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.
|
|
494
|
+
> 3. **Strip** tag `@proposed` / `@from-test` — chúng là nhãn vòng đời proposal, không thuộc BDD canonical.
|
|
495
|
+
> 4. Đặt scenario vào **đúng NHÓM** theo business theme (C.5), không nối vào cuối file.
|
|
496
|
+
> 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`).
|
|
497
|
+
>
|
|
498
|
+
> **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.
|
|
485
499
|
> - `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).
|
|
486
500
|
> Bỏ qua sạch nếu folder rỗng.
|
|
487
501
|
|
|
@@ -687,9 +701,24 @@ Chỉ cần kiểm tra trạng thái đã phân giải:
|
|
|
687
701
|
| `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.) |
|
|
688
702
|
| Single-service (không có section `services`) | `active_module = tech_stack.module` (đã set ở Bước 6.5). Tiếp tục. |
|
|
689
703
|
|
|
690
|
-
|
|
704
|
+
### Phân giải `active_platform` (umbrella mode)
|
|
691
705
|
|
|
692
|
-
|
|
706
|
+
Umbrella mode không hỏi platform (khác spec repo mode) — nó **suy** từ module của service. Bắt buộc phải có giá trị: `active_platform` đi vào **path file**, vào **header `@trace.platform`**, và vào **tên sổ trace** `{UC-ID}-{platform}.tsv`.
|
|
707
|
+
|
|
708
|
+
| `active_module` | → `active_platform` |
|
|
709
|
+
|---|---|
|
|
710
|
+
| react · nextjs · vue · nuxt · angular | `web` |
|
|
711
|
+
| flutter · react-native · ios-swiftui · android-compose | `app` |
|
|
712
|
+
| java-spring · golang · dotnet · php-laravel | `system` |
|
|
713
|
+
| context-engineering · phaser-game | theo `platform_type` của stack-profile (`backend` → `system`, còn lại → `web`) |
|
|
714
|
+
|
|
715
|
+
- `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`.
|
|
716
|
+
- 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.
|
|
717
|
+
|
|
718
|
+
**Output path (umbrella mode):** `{paths.specs_dir}/{domain}/{prd-slug}/bdd/{active_platform}/{TICKET-ID}-UC{N}-{slug}.feature`
|
|
719
|
+
|
|
720
|
+
*(**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.*
|
|
721
|
+
*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.)*
|
|
693
722
|
|
|
694
723
|
**Từ vựng theo platform** — điều chỉnh cách viết step BDD theo `active_module`:
|
|
695
724
|
|
|
@@ -772,9 +801,9 @@ Sau khi sinh tất cả file `.feature` và `.tsv` cho UC được giao, trả v
|
|
|
772
801
|
|
|
773
802
|
Trước khi sinh, kiểm tra các file `.feature` có sẵn cho PRD này:
|
|
774
803
|
|
|
775
|
-
1.
|
|
776
|
-
|
|
777
|
-
|
|
804
|
+
1. Search path (**giống nhau ở cả hai mode** — bố cục `bdd/{platform}/` là chuẩn duy nhất):
|
|
805
|
+
`{paths.specs_dir}/{domain}/{prd-slug}/bdd/{active_platform}/{TICKET-ID}-UC*.feature`
|
|
806
|
+
> **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.
|
|
778
807
|
2. Đọc `| **Version** |` hiện tại của PRD từ metadata (vd: `1.2`).
|
|
779
808
|
|
|
780
809
|
**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`.
|
|
@@ -823,7 +852,7 @@ Trước khi sinh, kiểm tra các file `.feature` có sẵn cho PRD này:
|
|
|
823
852
|
| Check | Rule |
|
|
824
853
|
|-------|------|
|
|
825
854
|
| 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. |
|
|
826
|
-
| C.2 PRD Traceability | Mỗi AC và mỗi BR
|
|
855
|
+
| 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ả.)* |
|
|
827
856
|
| C.3 Business Dictionary | Dùng đúng canonical term từ business-dictionary.md. |
|
|
828
857
|
| C.4 Banned Terms | 0 banned term trong file — grep trước khi gen. |
|
|
829
858
|
| C.5 NHÓM Grouping | Feature ≥3 SC → PHẢI có NHÓM grouping theo business theme. |
|
|
@@ -879,19 +908,23 @@ CHECKPOINT: "Outline này đúng chưa? Bạn muốn thêm hay bớt SC nào kh
|
|
|
879
908
|
|
|
880
909
|
## Generate
|
|
881
910
|
|
|
882
|
-
**Output path
|
|
883
|
-
- **Spec repo mode**: `{paths.specs_dir}/{domain}/{prd-slug}/bdd/{active_platform}/{TICKET-ID}-UC{N}-{slug}.feature`
|
|
884
|
-
- **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)*
|
|
911
|
+
**Output path — MỘT bố cục duy nhất cho cả hai mode:**
|
|
885
912
|
|
|
886
|
-
|
|
913
|
+
```
|
|
914
|
+
{paths.specs_dir}/{domain}/{prd-slug}/bdd/{active_platform}/{TICKET-ID}-UC{N}-{slug}.feature
|
|
915
|
+
```
|
|
916
|
+
|
|
917
|
+
`{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.**
|
|
918
|
+
|
|
919
|
+
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.
|
|
887
920
|
|
|
888
921
|
```gherkin
|
|
889
922
|
# ============================================================
|
|
890
923
|
# @trace.id: {TICKET-ID}-UC{N}
|
|
891
924
|
# @trace.title: <Feature name>
|
|
892
|
-
# @trace.revision: 1 ← field tĩnh;
|
|
925
|
+
# @trace.revision: 1 ← field tĩnh; version theo dõi bằng @trace.bdd_version
|
|
893
926
|
# @trace.domain: <domain>
|
|
894
|
-
# @trace.platform: {active_platform — web | app | system
|
|
927
|
+
# @trace.platform: {active_platform — web | app | system} ← BẮT BUỘC mọi mode; phải khớp segment bdd/{platform}/ của path
|
|
895
928
|
# @trace.service: {active_service — bỏ trong spec repo mode}
|
|
896
929
|
# @trace.module: {active_module trong umbrella mode; "unknown" trong spec repo mode}
|
|
897
930
|
# @trace.status: draft
|
|
@@ -899,8 +932,8 @@ Với mỗi UC, ghi vào path đã phân giải ở trên. Dùng từ vựng cho
|
|
|
899
932
|
# @trace.created_at: {YYYY-MM-DD}
|
|
900
933
|
# @trace.prd: {TICKET-ID}
|
|
901
934
|
# @trace.prd_version: {đọc từ metadata PRD "| **Version** |"}
|
|
902
|
-
# @trace.bdd_version: {1.0 nếu gen mới; tăng 0.1 khi gen lại
|
|
903
|
-
# @trace.business_rules: {TICKET-ID}-UC{N}-
|
|
935
|
+
# @trace.bdd_version: {cấp FILE — 1.0 nếu gen mới; tăng 0.1 khi gen lại. Khác @trace.sc_version (cấp từng SC) bên dưới}
|
|
936
|
+
# @trace.business_rules: {TICKET-ID}-UC{N}-BR{m}, {TICKET-ID}-UC{N}-BR{m+1} ← {m} lấy NGUYÊN từ PRD §3: BR đánh số LIÊN TỤC toàn PRD, KHÔNG reset theo UC
|
|
904
937
|
# @trace.dataset: {domain}.testdata.yaml
|
|
905
938
|
# ============================================================
|
|
906
939
|
|
|
@@ -947,8 +980,8 @@ Feature: <Feature name>
|
|
|
947
980
|
|
|
948
981
|
# Side-effects: <liệt kê ngắn các Then side-effect cần verify>
|
|
949
982
|
# @trace.scenario: {TICKET-ID}-UC{N}-SC1
|
|
950
|
-
# @trace.sc_version: 1.0
|
|
951
|
-
# @trace.business_rules: {TICKET-ID}-UC{N}-
|
|
983
|
+
# @trace.sc_version: 1.0 ← cấp SCENARIO. Sửa thân SC này (tên/step/table/side-effect) thì +0.1, nếu không code cũ mãi hiện OK
|
|
984
|
+
# @trace.business_rules: {TICKET-ID}-UC{N}-BR{m}
|
|
952
985
|
@happy
|
|
953
986
|
Scenario: <mô tả business outcome — dùng động từ chính xác: create/receive/assign/block>
|
|
954
987
|
Given <input state — alias từ dataset>
|
|
@@ -959,7 +992,7 @@ Feature: <Feature name>
|
|
|
959
992
|
# Side-effects: <...>
|
|
960
993
|
# @trace.scenario: {TICKET-ID}-UC{N}-SC2
|
|
961
994
|
# @trace.sc_version: 1.0
|
|
962
|
-
# @trace.business_rules: {TICKET-ID}-UC{N}-
|
|
995
|
+
# @trace.business_rules: {TICKET-ID}-UC{N}-BR{m}
|
|
963
996
|
@happy @alternative
|
|
964
997
|
Scenario: <cùng theme NHÓM 1 nhưng path khác — vd: giá trị enum khác>
|
|
965
998
|
Given <state>
|
|
@@ -973,7 +1006,7 @@ Feature: <Feature name>
|
|
|
973
1006
|
# Side-effects: <...>
|
|
974
1007
|
# @trace.scenario: {TICKET-ID}-UC{N}-SC3
|
|
975
1008
|
# @trace.sc_version: 1.0
|
|
976
|
-
# @trace.business_rules: {TICKET-ID}-UC{N}-
|
|
1009
|
+
# @trace.business_rules: {TICKET-ID}-UC{N}-BR{m+2}
|
|
977
1010
|
@edge
|
|
978
1011
|
Scenario: <scenario boundary / error>
|
|
979
1012
|
Given <state>
|
|
@@ -985,8 +1018,8 @@ Feature: <Feature name>
|
|
|
985
1018
|
# AC1 (...) → SC1, SC2
|
|
986
1019
|
# AC2 (...) → SC3
|
|
987
1020
|
# BR mapping (mỗi bullet PHẢI có ≥1 SC — C.2):
|
|
988
|
-
# {TICKET-ID}-UC{N}-
|
|
989
|
-
# {TICKET-ID}-UC{N}-
|
|
1021
|
+
# {TICKET-ID}-UC{N}-BR{m} (...) → SC1, SC2
|
|
1022
|
+
# {TICKET-ID}-UC{N}-BR{m+2} (...) → SC3
|
|
990
1023
|
# Wireframe mapping (mỗi component/action ≥1 SC — C.1):
|
|
991
1024
|
# Screen "<screen name>":
|
|
992
1025
|
# [x] <action 1> → SC1
|
|
@@ -999,6 +1032,9 @@ Feature: <Feature name>
|
|
|
999
1032
|
|
|
1000
1033
|
# === PRE-MERGE CHECKLIST ===
|
|
1001
1034
|
# - [ ] Mỗi SC có Side-effects + @trace.scenario + @trace.sc_version + @trace.business_rules
|
|
1035
|
+
# - [ ] SỬA nội dung một SC (tên / step / data table / side-effect) → đã bump @trace.sc_version của
|
|
1036
|
+
# CHÍNH SC đó (+0.1). Quên bump = code sinh từ SC cũ vẫn hiện OK, không ai biết phải regen.
|
|
1037
|
+
# (Đổi @trace.business_rules / tag / comment → KHÔNG bump: không đổi hành vi cần implement.)
|
|
1002
1038
|
# - [ ] Coverage Matrix: 0 dòng MISSING (C.1)
|
|
1003
1039
|
# - [ ] FE/App: mỗi Screen State (≠default) + AC-UI behavioral của design-spec có ≥1 SC (C.1 mở rộng)
|
|
1004
1040
|
# - [ ] Mỗi AC/BR map tới ≥1 SC (C.2)
|
|
@@ -1009,7 +1045,35 @@ Feature: <Feature name>
|
|
|
1009
1045
|
|
|
1010
1046
|
```
|
|
1011
1047
|
|
|
1012
|
-
|
|
1048
|
+
> **Template này đến từ đâu — đọc trước khi định "customize":**
|
|
1049
|
+
> 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.
|
|
1050
|
+
>
|
|
1051
|
+
> **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`).
|
|
1052
|
+
>
|
|
1053
|
+
> Coverage Matrix + Pre-merge Checklist nằm ở **cuối** template, thêm vào cuối mỗi file.
|
|
1054
|
+
|
|
1055
|
+
### Bump `@trace.sc_version` *(CHỈ khi gen lại — file `.feature` đã tồn tại)*
|
|
1056
|
+
|
|
1057
|
+
*Bỏ qua hoàn toàn khi gen mới: mọi SC nhận `1.0`.*
|
|
1058
|
+
|
|
1059
|
+
`@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).
|
|
1060
|
+
|
|
1061
|
+
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:
|
|
1062
|
+
|
|
1063
|
+
1. dòng `Scenario:` (tên)
|
|
1064
|
+
2. chuỗi step `Given` / `When` / `Then` / `And` (nội dung + thứ tự)
|
|
1065
|
+
3. nội dung data table (nếu có)
|
|
1066
|
+
4. dòng `# Side-effects:`
|
|
1067
|
+
|
|
1068
|
+
| Kết quả so | Hành động |
|
|
1069
|
+
|---|---|
|
|
1070
|
+
| Khác ở **bất kỳ** thành phần nào | `@trace.sc_version` += `0.1` (vd `1.0` → `1.1`) |
|
|
1071
|
+
| Giống hoàn toàn | **GIỮ NGUYÊN** — bump vô cớ sẽ tạo `DRIFT` giả, làm cờ mất giá trị |
|
|
1072
|
+
| SC mới (chưa có trong bản cũ) | `1.0` |
|
|
1073
|
+
|
|
1074
|
+
*(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.)*
|
|
1075
|
+
|
|
1076
|
+
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`.
|
|
1013
1077
|
|
|
1014
1078
|
---
|
|
1015
1079
|
|
|
@@ -1030,7 +1094,19 @@ sc_id\tsc_title\tspec_ver\tgen_ver\timplemented_by\ttest_count\ttest_classes\tde
|
|
|
1030
1094
|
- 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.
|
|
1031
1095
|
- 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`.
|
|
1032
1096
|
- 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 `—`.
|
|
1033
|
-
- SC không còn trong `.feature` (bị xoá
|
|
1097
|
+
- SC không còn trong `.feature` (bị xoá / gộp / đổi số) → **phụ thuộc SC đó đã có code chưa:**
|
|
1098
|
+
- `implemented_by == —` (**chưa** có code) → **xoá row**. Không có gì mồ côi.
|
|
1099
|
+
- `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.
|
|
1100
|
+
In cảnh báo nổi bật ở report cuối:
|
|
1101
|
+
```
|
|
1102
|
+
⚠️ ORPHANED — {UC-ID}-SC{N} "{sc_title}" đã bị xoá khỏi .feature nhưng còn code:
|
|
1103
|
+
{implemented_by} (+ {test_count} test: {test_classes})
|
|
1104
|
+
Không tự hết — chọn MỘT:
|
|
1105
|
+
(a) behavior không còn cần → xoá method + test, rồi xoá row khỏi .tsv
|
|
1106
|
+
(b) SC bị xoá do nhầm → đưa scenario trở lại .feature (row về DRIFT/OK bình thường)
|
|
1107
|
+
(/validate-traces giữ cờ ORPHANED 🔴 và chặn "pass" tới khi xử lý xong.)
|
|
1108
|
+
```
|
|
1109
|
+
*(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.)*
|
|
1034
1110
|
|
|
1035
1111
|
**Giá trị ghi cho mỗi scenario:**
|
|
1036
1112
|
|
|
@@ -1163,6 +1239,7 @@ Gợi ý lệnh kế tiếp hợp lý theo phase của workflow:
|
|
|
1163
1239
|
| /qc-run-test | `/qc-report {UC-ID}` rồi `/qc-review {UC-ID}` (review script) |
|
|
1164
1240
|
| /qc-review (script) | `/qc-report {UC-ID}` rồi tạo PR nếu APPROVED |
|
|
1165
1241
|
| /qc-report | `/validate-traces {UC-ID}` để làm mới Living Docs (qc_status) |
|
|
1242
|
+
| /map-testids | `/qc-design-test {UC-ID}` (QC dựng Page Object từ contract §4.5.6 vừa ghi) |
|
|
1166
1243
|
| /generate-tech-docs | `/review-tech-docs {tech-design-file}` |
|
|
1167
1244
|
| /review-tech-docs | `/generate-code {feature-file}` nếu APPROVED; sửa doc nếu NEEDS_FIX |
|
|
1168
1245
|
| /generate-code | Lần gen đầu → `/review-code {UC-ID}`; gen lại → `/dev-gen-test {UC-ID}` |
|
|
@@ -1171,8 +1248,8 @@ Gợi ý lệnh kế tiếp hợp lý theo phase của workflow:
|
|
|
1171
1248
|
| /dev-run-test (failing) | `/fix-bug {ticket-id}` hoặc `/debug {error}` |
|
|
1172
1249
|
| /review-code | `/dev-smoke-test {UC-ID}` hoặc tạo PR |
|
|
1173
1250
|
| /dev-smoke-test | Tạo PR và link tới ticket |
|
|
1174
|
-
| /validate-traces | DRIFT/UNTRACKED → `/generate-code {UC-ID}
|
|
1175
|
-
| /fix-bug |
|
|
1251
|
+
| /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** |
|
|
1252
|
+
| /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 |
|
|
1176
1253
|
| /debug | `/fix-bug {ticket-id}` nếu cần sửa |
|
|
1177
1254
|
| /report-bug | Gửi cho dev (`/fix-bug {BUG-ID}`); nếu thiếu coverage → `/propose-scenario {UC-ID}` |
|
|
1178
1255
|
| /propose-scenario | Báo PO/Dev review proposal trong `feedback/bdd-proposals/` |
|
|
@@ -1207,9 +1284,9 @@ Next (spec repo):
|
|
|
1207
1284
|
→ Sau khi gen hết platform: commit + push + báo team dev
|
|
1208
1285
|
→ Team dev đọc BDD từ spec submodule — không chạy /generate-bdd ở phía họ
|
|
1209
1286
|
|
|
1210
|
-
[Umbrella mode — service: {active_service}]
|
|
1287
|
+
[Umbrella mode — service: {active_service} · platform: {active_platform} (suy từ module {active_module})]
|
|
1211
1288
|
Files:
|
|
1212
|
-
{paths.specs_dir}/{domain}/{prd-slug}/bdd/{TICKET-ID}-UC1-{slug}.feature ({N} scenarios)
|
|
1289
|
+
{paths.specs_dir}/{domain}/{prd-slug}/bdd/{active_platform}/{TICKET-ID}-UC1-{slug}.feature ({N} scenarios)
|
|
1213
1290
|
Trace:
|
|
1214
1291
|
{paths.trace_dir}/{domain}/{prd-slug}/{TICKET-ID}-UC1-{active_platform}.tsv ({N} rows)
|
|
1215
1292
|
Next (umbrella):
|
|
@@ -1217,5 +1294,11 @@ Next (umbrella):
|
|
|
1217
1294
|
→ /generate-tech-docs {feature-file}
|
|
1218
1295
|
→ /generate-code {feature-file}
|
|
1219
1296
|
|
|
1297
|
+
{chỉ khi gen lại VÀ có ≥1 SC bị bump — ngược lại bỏ cả khối}
|
|
1298
|
+
🔄 sc_version đã bump (scenario đổi nội dung → code cũ lỗi thời):
|
|
1299
|
+
{UC-ID}-SC2 1.0 → 1.1 {sc_title}
|
|
1300
|
+
{UC-ID}-SC5 1.2 → 1.3 {sc_title}
|
|
1301
|
+
→ {n} SC này sẽ hiện DRIFT ở /validate-traces. Sinh lại code: /generate-code {feature-file}
|
|
1302
|
+
|
|
1220
1303
|
📊 Living Docs: chạy /validate-traces (hoặc /sync) để push trace này lên dashboard spec-module.
|
|
1221
1304
|
```
|
|
@@ -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 (
|
|
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
|
-
|
|
225
|
+
### Phân giải `active_platform` (umbrella mode)
|
|
217
226
|
|
|
218
|
-
|
|
227
|
+
Umbrella mode không hỏi platform (khác spec repo mode) — nó **suy** từ module của service. Bắt buộc phải có giá trị: `active_platform` đi vào **path file**, vào **header `@trace.platform`**, và 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.
|
|
302
|
-
|
|
303
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
|
@@ -436,7 +492,19 @@ sc_id\tsc_title\tspec_ver\tgen_ver\timplemented_by\ttest_count\ttest_classes\tde
|
|
|
436
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`, `last_updated`. Giữ nguyên các cột khác.
|
|
437
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`, `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`.
|
|
438
494
|
- 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á
|
|
495
|
+
- SC không còn trong `.feature` (bị xoá / gộp / đổi số) → **phụ thuộc SC đó đã có code chưa:**
|
|
496
|
+
- `implemented_by == —` (**chưa** có code) → **xoá row**. Không có gì mồ côi.
|
|
497
|
+
- `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.
|
|
498
|
+
In cảnh báo nổi bật ở report cuối:
|
|
499
|
+
```
|
|
500
|
+
⚠️ ORPHANED — {UC-ID}-SC{N} "{sc_title}" đã bị xoá khỏi .feature nhưng còn code:
|
|
501
|
+
{implemented_by} (+ {test_count} test: {test_classes})
|
|
502
|
+
Không tự hết — chọn MỘT:
|
|
503
|
+
(a) behavior không còn cần → xoá method + test, rồi xoá row khỏi .tsv
|
|
504
|
+
(b) SC bị xoá do nhầm → đưa scenario trở lại .feature (row về DRIFT/OK bình thường)
|
|
505
|
+
(/validate-traces giữ cờ ORPHANED 🔴 và chặn "pass" tới khi xử lý xong.)
|
|
506
|
+
```
|
|
507
|
+
*(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
508
|
|
|
441
509
|
**Giá trị ghi cho mỗi scenario:**
|
|
442
510
|
|
|
@@ -487,9 +555,9 @@ Next (spec repo):
|
|
|
487
555
|
→ Sau khi gen hết platform: commit + push + báo team dev
|
|
488
556
|
→ Team dev đọc BDD từ spec submodule — không chạy /generate-bdd ở phía họ
|
|
489
557
|
|
|
490
|
-
[Umbrella mode — service: {active_service}]
|
|
558
|
+
[Umbrella mode — service: {active_service} · platform: {active_platform} (suy từ module {active_module})]
|
|
491
559
|
Files:
|
|
492
|
-
{paths.specs_dir}/{domain}/{prd-slug}/bdd/{TICKET-ID}-UC1-{slug}.feature ({N} scenarios)
|
|
560
|
+
{paths.specs_dir}/{domain}/{prd-slug}/bdd/{active_platform}/{TICKET-ID}-UC1-{slug}.feature ({N} scenarios)
|
|
493
561
|
Trace:
|
|
494
562
|
{paths.trace_dir}/{domain}/{prd-slug}/{TICKET-ID}-UC1-{active_platform}.tsv ({N} rows)
|
|
495
563
|
Next (umbrella):
|
|
@@ -497,5 +565,11 @@ Next (umbrella):
|
|
|
497
565
|
→ /generate-tech-docs {feature-file}
|
|
498
566
|
→ /generate-code {feature-file}
|
|
499
567
|
|
|
568
|
+
{chỉ khi gen lại VÀ có ≥1 SC bị bump — ngược lại bỏ cả khối}
|
|
569
|
+
🔄 sc_version đã bump (scenario đổi nội dung → code cũ lỗi thời):
|
|
570
|
+
{UC-ID}-SC2 1.0 → 1.1 {sc_title}
|
|
571
|
+
{UC-ID}-SC5 1.2 → 1.3 {sc_title}
|
|
572
|
+
→ {n} SC này sẽ hiện DRIFT ở /validate-traces. Sinh lại code: /generate-code {feature-file}
|
|
573
|
+
|
|
500
574
|
📊 Living Docs: chạy /validate-traces (hoặc /sync) để push trace này lên dashboard spec-module.
|
|
501
575
|
```
|