@educa-corp/sdd-framework 0.2.1 → 0.2.4

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 (65) hide show
  1. package/bin/index.js +59 -29
  2. package/commands/debug.md +21 -8
  3. package/commands/define-product.md +21 -8
  4. package/commands/dev-gen-test.md +21 -8
  5. package/commands/dev-run-test.md +21 -8
  6. package/commands/dev-smoke-test.md +21 -8
  7. package/commands/fix-bug.md +21 -8
  8. package/commands/generate-bdd.md +24 -10
  9. package/commands/generate-bdd.tmpl +3 -2
  10. package/commands/generate-code.md +21 -8
  11. package/commands/generate-design-spec.md +21 -8
  12. package/commands/generate-prd.md +21 -8
  13. package/commands/generate-spec-manifest.md +21 -8
  14. package/commands/generate-tech-docs.md +21 -8
  15. package/commands/learn.md +21 -8
  16. package/commands/map-testids.md +21 -8
  17. package/commands/propose-scenario.md +21 -8
  18. package/commands/qc-analyze.md +21 -8
  19. package/commands/qc-design-test.md +21 -8
  20. package/commands/qc-plan.md +21 -8
  21. package/commands/qc-report.md +21 -8
  22. package/commands/qc-review.md +21 -8
  23. package/commands/qc-run-test.md +21 -8
  24. package/commands/refine-prd.md +21 -8
  25. package/commands/report-bug.md +21 -8
  26. package/commands/review-code.md +21 -8
  27. package/commands/review-context.md +21 -8
  28. package/commands/review-tech-docs.md +21 -8
  29. package/commands/setup-ai-first.md +14 -7
  30. package/commands/setup-ai-first.tmpl +14 -7
  31. package/commands/validate-traces.md +21 -8
  32. package/core/FRAMEWORK_VERSION +1 -1
  33. package/core/commands/debug.md +21 -8
  34. package/core/commands/define-product.md +21 -8
  35. package/core/commands/dev-gen-test.md +21 -8
  36. package/core/commands/dev-run-test.md +21 -8
  37. package/core/commands/dev-smoke-test.md +21 -8
  38. package/core/commands/fix-bug.md +21 -8
  39. package/core/commands/generate-bdd.md +24 -10
  40. package/core/commands/generate-code.md +21 -8
  41. package/core/commands/generate-design-spec.md +21 -8
  42. package/core/commands/generate-prd.md +21 -8
  43. package/core/commands/generate-spec-manifest.md +21 -8
  44. package/core/commands/generate-tech-docs.md +21 -8
  45. package/core/commands/learn.md +21 -8
  46. package/core/commands/map-testids.md +21 -8
  47. package/core/commands/propose-scenario.md +21 -8
  48. package/core/commands/qc-analyze.md +21 -8
  49. package/core/commands/qc-design-test.md +21 -8
  50. package/core/commands/qc-plan.md +21 -8
  51. package/core/commands/qc-report.md +21 -8
  52. package/core/commands/qc-review.md +21 -8
  53. package/core/commands/qc-run-test.md +21 -8
  54. package/core/commands/refine-prd.md +21 -8
  55. package/core/commands/report-bug.md +21 -8
  56. package/core/commands/review-code.md +21 -8
  57. package/core/commands/review-context.md +21 -8
  58. package/core/commands/review-tech-docs.md +21 -8
  59. package/core/commands/setup-ai-first.md +14 -7
  60. package/core/commands/validate-traces.md +21 -8
  61. package/core/steps/context-loader.md +21 -8
  62. package/core/templates/project-context.yaml +23 -10
  63. package/package.json +1 -1
  64. package/steps/context-loader.md +21 -8
  65. package/templates/project-context.yaml +23 -10
@@ -186,16 +186,28 @@ Nếu có section `services`:
186
186
  *(vd: `specs/user/create-account/USR01-create-account.md` **và** `specs/user/create-account/bdd/system/UC1.feature` đều → domain = `user`, prd_slug = `create-account`)*
187
187
  - Nếu `$ARGUMENTS` chứa một path, trích xuất segment domain sau `specs_dir`
188
188
 
189
- **2. Route tới service** nếu active domain khớp với một key trong `services`:
190
- - Override `paths.specs_dir` `services.{domain}.specs_dir` **chỉ khi `setup.spec_source` KHÔNG được đặt.** Khi `spec_source` ĐƯỢC đặt, MỌI BDD (web/app/**system**) artifact dùng chung liên team → để bước 4 route sang spec repo; KHÔNG pin theo service ở đây.
191
- - Override `paths.tech_docs_dir` `services.{domain}.tech_docs_dir` — **chỉ khi `setup.spec_source` KHÔNG được đặt.** Khi `spec_source` ĐƯỢC đặt, tech-design (API contract)artifact liên team và phải nằm trong spec repo dùng chung (xử lý ở bước 4), nên để bước 4 route `tech_docs_dir` KHÔNG pin theo service ở đây.
189
+ **1b. Phát hiện active platform** (chỉ cần khi service dạng map-theo-platform bước 2b):
190
+ - Đọc `@trace.platform` từ header của target `.feature` (`system` | `web` | `app`) Gate đã resolve target trước bước này.
191
+ - Nếu target không mang `@trace.platform` (vd targetPRD `.md`), thử suy từ segment `bdd/{platform}/` trong path target.
192
+ - Nếu vẫn không xác định được → `active_platform = null`.
193
+
194
+ **2. Route tới service** — nếu active domain khớp một key trong `services`. Giá trị `services.{domain}` có **hai dạng**; nhận dạng bằng việc có `path` trực tiếp hay không:
195
+
196
+ **2a. Dạng phẳng** — `services.{domain}` có **trực tiếp** `path`/`module` (một domain ↔ một service, mọi platform về cùng submodule). Route như cũ:
192
197
  - Lưu `active_service` = `services.{domain}.path`
193
198
  - Lưu `active_service_module` = `services.{domain}.module`
194
199
  - Nếu service có `module` riêng → dùng nó làm `active_module` (override `tech_stack.module`)
195
200
 
196
- **3. Fallback** — nếu không phát hiện được domain hoặc không service key khớp:
197
- - Giữ path mặc định từ Bước 1
198
- - Đặt `active_service = unresolved`
201
+ **2b. Dạng map-theo-platform** — `services.{domain}` **KHÔNG** `path` trực tiếp mà chứa các sub-key platform (`system` / `web` / `app`), mỗi cái là `{ path, module }` (một business-domain trải trên nhiều platform/submodule). Route theo `active_platform` (bước 1b):
202
+ - Nếu `active_platform` khớp một sub-key → `entry = services.{domain}.{active_platform}`; lưu `active_service = entry.path`, `active_service_module = entry.module` (→ `active_module`, override `tech_stack.module`).
203
+ - Nếu `active_platform = null` (chưa xác định platform, vd đang thao tác cấp PRD) → **KHÔNG** chốt một service; đặt `active_service = multi` và lưu `service_candidates = services.{domain}` (toàn map platform→{path,module}). Lệnh cần một service cụ thể (`/generate-code`, `/dev-*`, `/fix-bug`) luôn chạy trên target `.feature` có platform nên sẽ resolve được ở lần chạy đó; lệnh cấp PRD (`/generate-prd`, `/refine-prd`) không cần service cụ thể.
204
+ - Nếu `active_platform` xác định nhưng không có sub-key tương ứng → `active_service = unresolved` (xem Fallback) với lý do "domain `{domain}` chưa cấu hình platform `{active_platform}`".
205
+
206
+ *(Cả 2a/2b: override `paths.specs_dir`/`paths.tech_docs_dir` per-service CHỈ khi `setup.spec_source` KHÔNG được đặt. Khi `spec_source` ĐƯỢC đặt, MỌI BDD/tech-doc là artifact liên team → để bước 4 route sang spec repo; KHÔNG pin per-service ở đây.)*
207
+
208
+ **3. Fallback**:
209
+ - Không phát hiện được domain, hoặc domain không khớp key nào trong `services` → giữ path mặc định từ Bước 1, đặt `active_service = unresolved`.
210
+ - Domain khớp một map-theo-platform (2b) nhưng `active_platform` xác định mà thiếu sub-key tương ứng → `active_service = unresolved`, ghi lý do rõ để lệnh DỪNG báo lỗi cấu hình (không tự đoán platform).
199
211
 
200
212
  **4. Tự động override theo spec source** — nếu `setup.spec_source` được đặt VÀ path tương ứng chưa được set tường minh trong `paths:`:
201
213
  - Override `paths.specs_dir` → `{spec_source}/specs` — **luôn khi `spec_source` được đặt.** Mọi spec artifact (PRD, BDD, tech-docs, design-spec) nằm dưới gốc spec thống nhất trong spec repo dùng chung theo bố cục feature-package: `{spec_source}/specs/{domain}/{prd-slug}/`. Mọi umbrella (FE/App/BE) đều đọc từ đây. *(`specs/` theo service chỉ khi không có `spec_source`.)*
@@ -215,7 +227,7 @@ Nếu có section `services`:
215
227
 
216
228
  ## Bước 1.6 — [SERVICE CONVENTIONS] Nạp convention riêng của service (chế độ umbrella)
217
229
 
218
- *Bỏ qua hoàn toàn bước này nếu `active_service` là `"unresolved"` hoặc context ở chế độ single-service.*
230
+ *Bỏ qua hoàn toàn bước này nếu `active_service` là `"unresolved"` hoặc `"multi"` (chưa chốt một service — dạng map-theo-platform ở cấp PRD) hoặc context ở chế độ single-service.*
219
231
 
220
232
  Khi `active_service` đã được phân giải thành một path thật ở Bước 1.5 (vd: `user-service/`):
221
233
 
@@ -391,7 +403,8 @@ Ticket : {ticket_prefix}-
391
403
  Dict : {loaded — N canonical terms, M banned terms | missing}
392
404
  Entities : {loaded — EntityA, EntityB, EntityC | missing}
393
405
  Lessons : {loaded — N guardrails | chưa có}
394
- Service : {active_service} ({active_service_module}) | single-service
406
+ Platform : {active_platform: system | web | app | — nếu chưa xác định}
407
+ Service : {active_service} ({active_service_module}) | multi (map-theo-platform, chốt khi target có platform) | single-service
395
408
  Svc Root : {service_root} — đã nạp conventions + trace_dir từ config service | —
396
409
  Status : {FULL | PARTIAL — thiếu: CLAUDE.md / business-dict / core-entities | MINIMAL}
397
410
  ```
@@ -183,16 +183,28 @@ Nếu có section `services`:
183
183
  *(vd: `specs/user/create-account/USR01-create-account.md` **và** `specs/user/create-account/bdd/system/UC1.feature` đều → domain = `user`, prd_slug = `create-account`)*
184
184
  - Nếu `$ARGUMENTS` chứa một path, trích xuất segment domain sau `specs_dir`
185
185
 
186
- **2. Route tới service** nếu active domain khớp với một key trong `services`:
187
- - Override `paths.specs_dir` `services.{domain}.specs_dir` **chỉ khi `setup.spec_source` KHÔNG được đặt.** Khi `spec_source` ĐƯỢC đặt, MỌI BDD (web/app/**system**) artifact dùng chung liên team → để bước 4 route sang spec repo; KHÔNG pin theo service ở đây.
188
- - Override `paths.tech_docs_dir` `services.{domain}.tech_docs_dir` — **chỉ khi `setup.spec_source` KHÔNG được đặt.** Khi `spec_source` ĐƯỢC đặt, tech-design (API contract)artifact liên team và phải nằm trong spec repo dùng chung (xử lý ở bước 4), nên để bước 4 route `tech_docs_dir` KHÔNG pin theo service ở đây.
186
+ **1b. Phát hiện active platform** (chỉ cần khi service dạng map-theo-platform bước 2b):
187
+ - Đọc `@trace.platform` từ header của target `.feature` (`system` | `web` | `app`) Gate đã resolve target trước bước này.
188
+ - Nếu target không mang `@trace.platform` (vd targetPRD `.md`), thử suy từ segment `bdd/{platform}/` trong path target.
189
+ - Nếu vẫn không xác định được → `active_platform = null`.
190
+
191
+ **2. Route tới service** — nếu active domain khớp một key trong `services`. Giá trị `services.{domain}` có **hai dạng**; nhận dạng bằng việc có `path` trực tiếp hay không:
192
+
193
+ **2a. Dạng phẳng** — `services.{domain}` có **trực tiếp** `path`/`module` (một domain ↔ một service, mọi platform về cùng submodule). Route như cũ:
189
194
  - Lưu `active_service` = `services.{domain}.path`
190
195
  - Lưu `active_service_module` = `services.{domain}.module`
191
196
  - Nếu service có `module` riêng → dùng nó làm `active_module` (override `tech_stack.module`)
192
197
 
193
- **3. Fallback** — nếu không phát hiện được domain hoặc không service key khớp:
194
- - Giữ path mặc định từ Bước 1
195
- - Đặt `active_service = unresolved`
198
+ **2b. Dạng map-theo-platform** — `services.{domain}` **KHÔNG** `path` trực tiếp mà chứa các sub-key platform (`system` / `web` / `app`), mỗi cái là `{ path, module }` (một business-domain trải trên nhiều platform/submodule). Route theo `active_platform` (bước 1b):
199
+ - Nếu `active_platform` khớp một sub-key → `entry = services.{domain}.{active_platform}`; lưu `active_service = entry.path`, `active_service_module = entry.module` (→ `active_module`, override `tech_stack.module`).
200
+ - Nếu `active_platform = null` (chưa xác định platform, vd đang thao tác cấp PRD) → **KHÔNG** chốt một service; đặt `active_service = multi` và lưu `service_candidates = services.{domain}` (toàn map platform→{path,module}). Lệnh cần một service cụ thể (`/generate-code`, `/dev-*`, `/fix-bug`) luôn chạy trên target `.feature` có platform nên sẽ resolve được ở lần chạy đó; lệnh cấp PRD (`/generate-prd`, `/refine-prd`) không cần service cụ thể.
201
+ - Nếu `active_platform` xác định nhưng không có sub-key tương ứng → `active_service = unresolved` (xem Fallback) với lý do "domain `{domain}` chưa cấu hình platform `{active_platform}`".
202
+
203
+ *(Cả 2a/2b: override `paths.specs_dir`/`paths.tech_docs_dir` per-service CHỈ khi `setup.spec_source` KHÔNG được đặt. Khi `spec_source` ĐƯỢC đặt, MỌI BDD/tech-doc là artifact liên team → để bước 4 route sang spec repo; KHÔNG pin per-service ở đây.)*
204
+
205
+ **3. Fallback**:
206
+ - Không phát hiện được domain, hoặc domain không khớp key nào trong `services` → giữ path mặc định từ Bước 1, đặt `active_service = unresolved`.
207
+ - Domain khớp một map-theo-platform (2b) nhưng `active_platform` xác định mà thiếu sub-key tương ứng → `active_service = unresolved`, ghi lý do rõ để lệnh DỪNG báo lỗi cấu hình (không tự đoán platform).
196
208
 
197
209
  **4. Tự động override theo spec source** — nếu `setup.spec_source` được đặt VÀ path tương ứng chưa được set tường minh trong `paths:`:
198
210
  - Override `paths.specs_dir` → `{spec_source}/specs` — **luôn khi `spec_source` được đặt.** Mọi spec artifact (PRD, BDD, tech-docs, design-spec) nằm dưới gốc spec thống nhất trong spec repo dùng chung theo bố cục feature-package: `{spec_source}/specs/{domain}/{prd-slug}/`. Mọi umbrella (FE/App/BE) đều đọc từ đây. *(`specs/` theo service chỉ khi không có `spec_source`.)*
@@ -212,7 +224,7 @@ Nếu có section `services`:
212
224
 
213
225
  ## Bước 1.6 — [SERVICE CONVENTIONS] Nạp convention riêng của service (chế độ umbrella)
214
226
 
215
- *Bỏ qua hoàn toàn bước này nếu `active_service` là `"unresolved"` hoặc context ở chế độ single-service.*
227
+ *Bỏ qua hoàn toàn bước này nếu `active_service` là `"unresolved"` hoặc `"multi"` (chưa chốt một service — dạng map-theo-platform ở cấp PRD) hoặc context ở chế độ single-service.*
216
228
 
217
229
  Khi `active_service` đã được phân giải thành một path thật ở Bước 1.5 (vd: `user-service/`):
218
230
 
@@ -388,7 +400,8 @@ Ticket : {ticket_prefix}-
388
400
  Dict : {loaded — N canonical terms, M banned terms | missing}
389
401
  Entities : {loaded — EntityA, EntityB, EntityC | missing}
390
402
  Lessons : {loaded — N guardrails | chưa có}
391
- Service : {active_service} ({active_service_module}) | single-service
403
+ Platform : {active_platform: system | web | app | — nếu chưa xác định}
404
+ Service : {active_service} ({active_service_module}) | multi (map-theo-platform, chốt khi target có platform) | single-service
392
405
  Svc Root : {service_root} — đã nạp conventions + trace_dir từ config service | —
393
406
  Status : {FULL | PARTIAL — thiếu: CLAUDE.md / business-dict / core-entities | MINIMAL}
394
407
  ```
@@ -122,18 +122,25 @@ Dựa trên câu trả lời:
122
122
 
123
123
  **project_type = 1 (Single-service):** Tiếp tục setup chuẩn bên dưới.
124
124
 
125
- **project_type = 2 (Umbrella):** Hỏi hai câu follow-up:
126
- - "Path tới spec submodule (vd `free-trial-specs`)? Nhấn Enter để skip."
127
- - "Liệt các service dạng cặp `domain:module`, ngăn cách bởi dấu phẩy
128
- (vd `user:java-spring,order:java-spring`). Nhấn Enter để skip."
125
+ **project_type = 2 (Umbrella):** Hỏi các câu follow-up:
126
+ - "Path tới spec submodule (vd `free-trial-spec`)? Nhấn Enter để skip."
127
+ - "Một business-domain được triển khai trên NHIỀU platform (BE + Web + App) không? (Y/N)"
128
+ - **N → dạng phẳng (FORM A):** "Liệt kê service dạng cặp `domain:module`, ngăn cách bởi dấu phẩy
129
+ (vd `user:java-spring,order:java-spring`). Nhấn Enter để skip."
130
+ - **Y → dạng map-theo-platform (FORM B):** "Liệt kê dạng bộ ba `domain:platform:module`
131
+ (platform ∈ system|web|app), ngăn cách bởi dấu phẩy — lặp lại domain cho từng platform
132
+ (vd `onboarding:system:java-spring,onboarding:web:nextjs,onboarding:app:flutter`). Nhấn Enter để skip."
133
+ context-loader route theo `@trace.platform` của target `.feature` → chọn đúng submodule.
134
+ **Giữ `@trace.domain` là business-domain** (KHÔNG bịa `onboarding-web`).
129
135
 
130
136
  Rồi:
131
137
  - Skip tạo bất kỳ artifact `specs/` nào (mọi spec — PRD, BDD, tech-docs, design-spec — sống trong spec submodule theo bố cục feature-package `specs/{domain}/{prd-slug}/`)
132
138
  - Chỉ tạo: `.trace/`, `.agent/review/` ở cấp umbrella
133
139
  *(Trừ khi user yêu cầu rõ tạo cấu trúc đầy đủ)*
134
- - Sinh `.agent/project-context.yaml` ở umbrella mode với services và spec_source đã cung cấp
135
- - Skip tạo `CLAUDE.md` (umbrella không một tech stack đơn)
136
- - Sau setup, nhắc: "Mở từng service submodule riêng trong Claude Code để cài framework đó nếu cần."
140
+ - Sinh `.agent/project-context.yaml` ở umbrella mode với services (FORM A hoặc B) và spec_source đã cung cấp.
141
+ **Sau khi sinh, MỞ file kiểm tra:** mỗi `services.{domain}.path` (hoặc `.{platform}.path`) phải trỏ **đúng tên thư mục submodule thật** — generator để placeholder `TODO-…` vì tên dir thường khác tên domain. Sửa cho khớp trước khi chạy lệnh generate.
142
+ - Skip tạo `CLAUDE.md` root (umbrella không có một tech stack đơn) — nhưng nhắc mỗi submodule code cần overlay `{path}/CLAUDE.md` riêng (thiếu thì code-gen fallback về default + cờ ⚠️, có thể sai coding-standards).
143
+ - Sau setup, nhắc: "Mở từng service submodule riêng trong Claude Code để cài framework/overlay ở đó nếu cần."
137
144
 
138
145
  **project_type = 3 (PO Spec repo):**
139
146
  - Tạo base dir: `specs/product-definition/`, `specs/domain-knowledge/`, `feedback/`, `.agent/review/`
@@ -181,16 +181,28 @@ Nếu có section `services`:
181
181
  *(vd: `specs/user/create-account/USR01-create-account.md` **và** `specs/user/create-account/bdd/system/UC1.feature` đều → domain = `user`, prd_slug = `create-account`)*
182
182
  - Nếu `$ARGUMENTS` chứa một path, trích xuất segment domain sau `specs_dir`
183
183
 
184
- **2. Route tới service** nếu active domain khớp với một key trong `services`:
185
- - Override `paths.specs_dir` `services.{domain}.specs_dir` **chỉ khi `setup.spec_source` KHÔNG được đặt.** Khi `spec_source` ĐƯỢC đặt, MỌI BDD (web/app/**system**) artifact dùng chung liên team → để bước 4 route sang spec repo; KHÔNG pin theo service ở đây.
186
- - Override `paths.tech_docs_dir` `services.{domain}.tech_docs_dir` — **chỉ khi `setup.spec_source` KHÔNG được đặt.** Khi `spec_source` ĐƯỢC đặt, tech-design (API contract)artifact liên team và phải nằm trong spec repo dùng chung (xử lý ở bước 4), nên để bước 4 route `tech_docs_dir` KHÔNG pin theo service ở đây.
184
+ **1b. Phát hiện active platform** (chỉ cần khi service dạng map-theo-platform bước 2b):
185
+ - Đọc `@trace.platform` từ header của target `.feature` (`system` | `web` | `app`) Gate đã resolve target trước bước này.
186
+ - Nếu target không mang `@trace.platform` (vd targetPRD `.md`), thử suy từ segment `bdd/{platform}/` trong path target.
187
+ - Nếu vẫn không xác định được → `active_platform = null`.
188
+
189
+ **2. Route tới service** — nếu active domain khớp một key trong `services`. Giá trị `services.{domain}` có **hai dạng**; nhận dạng bằng việc có `path` trực tiếp hay không:
190
+
191
+ **2a. Dạng phẳng** — `services.{domain}` có **trực tiếp** `path`/`module` (một domain ↔ một service, mọi platform về cùng submodule). Route như cũ:
187
192
  - Lưu `active_service` = `services.{domain}.path`
188
193
  - Lưu `active_service_module` = `services.{domain}.module`
189
194
  - Nếu service có `module` riêng → dùng nó làm `active_module` (override `tech_stack.module`)
190
195
 
191
- **3. Fallback** — nếu không phát hiện được domain hoặc không service key khớp:
192
- - Giữ path mặc định từ Bước 1
193
- - Đặt `active_service = unresolved`
196
+ **2b. Dạng map-theo-platform** — `services.{domain}` **KHÔNG** `path` trực tiếp mà chứa các sub-key platform (`system` / `web` / `app`), mỗi cái là `{ path, module }` (một business-domain trải trên nhiều platform/submodule). Route theo `active_platform` (bước 1b):
197
+ - Nếu `active_platform` khớp một sub-key → `entry = services.{domain}.{active_platform}`; lưu `active_service = entry.path`, `active_service_module = entry.module` (→ `active_module`, override `tech_stack.module`).
198
+ - Nếu `active_platform = null` (chưa xác định platform, vd đang thao tác cấp PRD) → **KHÔNG** chốt một service; đặt `active_service = multi` và lưu `service_candidates = services.{domain}` (toàn map platform→{path,module}). Lệnh cần một service cụ thể (`/generate-code`, `/dev-*`, `/fix-bug`) luôn chạy trên target `.feature` có platform nên sẽ resolve được ở lần chạy đó; lệnh cấp PRD (`/generate-prd`, `/refine-prd`) không cần service cụ thể.
199
+ - Nếu `active_platform` xác định nhưng không có sub-key tương ứng → `active_service = unresolved` (xem Fallback) với lý do "domain `{domain}` chưa cấu hình platform `{active_platform}`".
200
+
201
+ *(Cả 2a/2b: override `paths.specs_dir`/`paths.tech_docs_dir` per-service CHỈ khi `setup.spec_source` KHÔNG được đặt. Khi `spec_source` ĐƯỢC đặt, MỌI BDD/tech-doc là artifact liên team → để bước 4 route sang spec repo; KHÔNG pin per-service ở đây.)*
202
+
203
+ **3. Fallback**:
204
+ - Không phát hiện được domain, hoặc domain không khớp key nào trong `services` → giữ path mặc định từ Bước 1, đặt `active_service = unresolved`.
205
+ - Domain khớp một map-theo-platform (2b) nhưng `active_platform` xác định mà thiếu sub-key tương ứng → `active_service = unresolved`, ghi lý do rõ để lệnh DỪNG báo lỗi cấu hình (không tự đoán platform).
194
206
 
195
207
  **4. Tự động override theo spec source** — nếu `setup.spec_source` được đặt VÀ path tương ứng chưa được set tường minh trong `paths:`:
196
208
  - Override `paths.specs_dir` → `{spec_source}/specs` — **luôn khi `spec_source` được đặt.** Mọi spec artifact (PRD, BDD, tech-docs, design-spec) nằm dưới gốc spec thống nhất trong spec repo dùng chung theo bố cục feature-package: `{spec_source}/specs/{domain}/{prd-slug}/`. Mọi umbrella (FE/App/BE) đều đọc từ đây. *(`specs/` theo service chỉ khi không có `spec_source`.)*
@@ -210,7 +222,7 @@ Nếu có section `services`:
210
222
 
211
223
  ## Bước 1.6 — [SERVICE CONVENTIONS] Nạp convention riêng của service (chế độ umbrella)
212
224
 
213
- *Bỏ qua hoàn toàn bước này nếu `active_service` là `"unresolved"` hoặc context ở chế độ single-service.*
225
+ *Bỏ qua hoàn toàn bước này nếu `active_service` là `"unresolved"` hoặc `"multi"` (chưa chốt một service — dạng map-theo-platform ở cấp PRD) hoặc context ở chế độ single-service.*
214
226
 
215
227
  Khi `active_service` đã được phân giải thành một path thật ở Bước 1.5 (vd: `user-service/`):
216
228
 
@@ -386,7 +398,8 @@ Ticket : {ticket_prefix}-
386
398
  Dict : {loaded — N canonical terms, M banned terms | missing}
387
399
  Entities : {loaded — EntityA, EntityB, EntityC | missing}
388
400
  Lessons : {loaded — N guardrails | chưa có}
389
- Service : {active_service} ({active_service_module}) | single-service
401
+ Platform : {active_platform: system | web | app | — nếu chưa xác định}
402
+ Service : {active_service} ({active_service_module}) | multi (map-theo-platform, chốt khi target có platform) | single-service
390
403
  Svc Root : {service_root} — đã nạp conventions + trace_dir từ config service | —
391
404
  Status : {FULL | PARTIAL — thiếu: CLAUDE.md / business-dict / core-entities | MINIMAL}
392
405
  ```
@@ -83,16 +83,28 @@ Nếu có section `services`:
83
83
  *(vd: `specs/user/create-account/USR01-create-account.md` **và** `specs/user/create-account/bdd/system/UC1.feature` đều → domain = `user`, prd_slug = `create-account`)*
84
84
  - Nếu `$ARGUMENTS` chứa một path, trích xuất segment domain sau `specs_dir`
85
85
 
86
- **2. Route tới service** nếu active domain khớp với một key trong `services`:
87
- - Override `paths.specs_dir` `services.{domain}.specs_dir` **chỉ khi `setup.spec_source` KHÔNG được đặt.** Khi `spec_source` ĐƯỢC đặt, MỌI BDD (web/app/**system**) artifact dùng chung liên team → để bước 4 route sang spec repo; KHÔNG pin theo service ở đây.
88
- - Override `paths.tech_docs_dir` `services.{domain}.tech_docs_dir` — **chỉ khi `setup.spec_source` KHÔNG được đặt.** Khi `spec_source` ĐƯỢC đặt, tech-design (API contract)artifact liên team và phải nằm trong spec repo dùng chung (xử lý ở bước 4), nên để bước 4 route `tech_docs_dir` KHÔNG pin theo service ở đây.
86
+ **1b. Phát hiện active platform** (chỉ cần khi service dạng map-theo-platform bước 2b):
87
+ - Đọc `@trace.platform` từ header của target `.feature` (`system` | `web` | `app`) Gate đã resolve target trước bước này.
88
+ - Nếu target không mang `@trace.platform` (vd targetPRD `.md`), thử suy từ segment `bdd/{platform}/` trong path target.
89
+ - Nếu vẫn không xác định được → `active_platform = null`.
90
+
91
+ **2. Route tới service** — nếu active domain khớp một key trong `services`. Giá trị `services.{domain}` có **hai dạng**; nhận dạng bằng việc có `path` trực tiếp hay không:
92
+
93
+ **2a. Dạng phẳng** — `services.{domain}` có **trực tiếp** `path`/`module` (một domain ↔ một service, mọi platform về cùng submodule). Route như cũ:
89
94
  - Lưu `active_service` = `services.{domain}.path`
90
95
  - Lưu `active_service_module` = `services.{domain}.module`
91
96
  - Nếu service có `module` riêng → dùng nó làm `active_module` (override `tech_stack.module`)
92
97
 
93
- **3. Fallback** — nếu không phát hiện được domain hoặc không service key khớp:
94
- - Giữ path mặc định từ Bước 1
95
- - Đặt `active_service = unresolved`
98
+ **2b. Dạng map-theo-platform** — `services.{domain}` **KHÔNG** `path` trực tiếp mà chứa các sub-key platform (`system` / `web` / `app`), mỗi cái là `{ path, module }` (một business-domain trải trên nhiều platform/submodule). Route theo `active_platform` (bước 1b):
99
+ - Nếu `active_platform` khớp một sub-key → `entry = services.{domain}.{active_platform}`; lưu `active_service = entry.path`, `active_service_module = entry.module` (→ `active_module`, override `tech_stack.module`).
100
+ - Nếu `active_platform = null` (chưa xác định platform, vd đang thao tác cấp PRD) → **KHÔNG** chốt một service; đặt `active_service = multi` và lưu `service_candidates = services.{domain}` (toàn map platform→{path,module}). Lệnh cần một service cụ thể (`/generate-code`, `/dev-*`, `/fix-bug`) luôn chạy trên target `.feature` có platform nên sẽ resolve được ở lần chạy đó; lệnh cấp PRD (`/generate-prd`, `/refine-prd`) không cần service cụ thể.
101
+ - Nếu `active_platform` xác định nhưng không có sub-key tương ứng → `active_service = unresolved` (xem Fallback) với lý do "domain `{domain}` chưa cấu hình platform `{active_platform}`".
102
+
103
+ *(Cả 2a/2b: override `paths.specs_dir`/`paths.tech_docs_dir` per-service CHỈ khi `setup.spec_source` KHÔNG được đặt. Khi `spec_source` ĐƯỢC đặt, MỌI BDD/tech-doc là artifact liên team → để bước 4 route sang spec repo; KHÔNG pin per-service ở đây.)*
104
+
105
+ **3. Fallback**:
106
+ - Không phát hiện được domain, hoặc domain không khớp key nào trong `services` → giữ path mặc định từ Bước 1, đặt `active_service = unresolved`.
107
+ - Domain khớp một map-theo-platform (2b) nhưng `active_platform` xác định mà thiếu sub-key tương ứng → `active_service = unresolved`, ghi lý do rõ để lệnh DỪNG báo lỗi cấu hình (không tự đoán platform).
96
108
 
97
109
  **4. Tự động override theo spec source** — nếu `setup.spec_source` được đặt VÀ path tương ứng chưa được set tường minh trong `paths:`:
98
110
  - Override `paths.specs_dir` → `{spec_source}/specs` — **luôn khi `spec_source` được đặt.** Mọi spec artifact (PRD, BDD, tech-docs, design-spec) nằm dưới gốc spec thống nhất trong spec repo dùng chung theo bố cục feature-package: `{spec_source}/specs/{domain}/{prd-slug}/`. Mọi umbrella (FE/App/BE) đều đọc từ đây. *(`specs/` theo service chỉ khi không có `spec_source`.)*
@@ -112,7 +124,7 @@ Nếu có section `services`:
112
124
 
113
125
  ## Bước 1.6 — [SERVICE CONVENTIONS] Nạp convention riêng của service (chế độ umbrella)
114
126
 
115
- *Bỏ qua hoàn toàn bước này nếu `active_service` là `"unresolved"` hoặc context ở chế độ single-service.*
127
+ *Bỏ qua hoàn toàn bước này nếu `active_service` là `"unresolved"` hoặc `"multi"` (chưa chốt một service — dạng map-theo-platform ở cấp PRD) hoặc context ở chế độ single-service.*
116
128
 
117
129
  Khi `active_service` đã được phân giải thành một path thật ở Bước 1.5 (vd: `user-service/`):
118
130
 
@@ -288,7 +300,8 @@ Ticket : {ticket_prefix}-
288
300
  Dict : {loaded — N canonical terms, M banned terms | missing}
289
301
  Entities : {loaded — EntityA, EntityB, EntityC | missing}
290
302
  Lessons : {loaded — N guardrails | chưa có}
291
- Service : {active_service} ({active_service_module}) | single-service
303
+ Platform : {active_platform: system | web | app | — nếu chưa xác định}
304
+ Service : {active_service} ({active_service_module}) | multi (map-theo-platform, chốt khi target có platform) | single-service
292
305
  Svc Root : {service_root} — đã nạp conventions + trace_dir từ config service | —
293
306
  Status : {FULL | PARTIAL — thiếu: CLAUDE.md / business-dict / core-entities | MINIMAL}
294
307
  ```
@@ -132,12 +132,28 @@ domains:
132
132
  # With spec_source set, only ONE override is needed instead of four separate dir vars.
133
133
  #
134
134
  # services: # domain → service submodule routing
135
- # {{DOMAIN_1}}: # must match the PRD's `Domain` (Metadata row) / folder path segment
135
+ # # Each domain key MUST match the PRD's `Domain` (Metadata row) / folder path segment.
136
+ # # A domain value has TWO forms — context-loader (Step 1.5) auto-detects by whether
137
+ # # `path` is present directly:
138
+ # #
139
+ # # FORM A — FLAT (one domain ↔ one submodule, all platforms):
140
+ # {{DOMAIN_1}}:
136
141
  # path: "{{SERVICE_SUBMODULE_DIR}}" # relative path to service submodule (code + .trace/)
137
142
  # module: "{{STACK_MODULE}}" # e.g., java-spring, nextjs, flutter
138
- # # NOTE: with spec_source set, BDD + tech-docs are cross-team and live in the spec repo —
139
- # # do NOT pin per-service specs_dir / tech_docs_dir here (they would be ignored).
140
- # # Per-service specs_dir / tech_docs_dir apply ONLY when there is no spec_source.
143
+ # #
144
+ # # FORM B PER-PLATFORM MAP (one business-domain implemented on several platforms /
145
+ # # submodules a merged monorepo/workspace). No direct `path`; instead one
146
+ # # sub-key per platform (system | web | app). context-loader routes by the
147
+ # # target .feature's @trace.platform → picks {path, module} for that platform.
148
+ # # The PRD keeps a SINGLE business @trace.domain (do NOT invent onboarding-web).
149
+ # {{DOMAIN_2}}:
150
+ # system: { path: "{{BE_SUBMODULE_DIR}}", module: "java-spring" }
151
+ # web: { path: "{{WEB_SUBMODULE_DIR}}", module: "nextjs" }
152
+ # app: { path: "{{APP_SUBMODULE_DIR}}", module: "flutter" }
153
+ # #
154
+ # # NOTE: with spec_source set, BDD + tech-docs are cross-team and live in the spec repo —
155
+ # # do NOT pin per-service specs_dir / tech_docs_dir here (they would be ignored).
156
+ # # Per-service specs_dir / tech_docs_dir apply ONLY when there is no spec_source.
141
157
  #
142
158
  # IMPORTANT — per-service CLAUDE.md:
143
159
  # Each service submodule should have its OWN CLAUDE.md ({path}/CLAUDE.md) defining its
@@ -145,12 +161,9 @@ domains:
145
161
  # layers: root CLAUDE.md (umbrella-wide shared rules) + {service}/CLAUDE.md (overlay,
146
162
  # wins on conflict for architecture/coding-standards). The agent sits at the umbrella
147
163
  # root, so without a service CLAUDE.md, code generation falls back to umbrella defaults
148
- # (likely the wrong stack). Generate one per service via /setup-ai-first inside each.
149
- # {{DOMAIN_2}}:
150
- # path: "{{SERVICE_2_DIR}}"
151
- # module: "{{STACK_MODULE}}"
152
- # specs_dir: "{{SERVICE_2_DIR}}/specs"
153
- # tech_docs_dir: "{{SERVICE_2_DIR}}/specs"
164
+ # + the module stack-profile (a ⚠️ is flagged in the context recap). Generate one per
165
+ # service via /setup-ai-first inside each. For FORM B, add one CLAUDE.md per platform
166
+ # submodule ({system|web|app}.path/CLAUDE.md).
154
167
 
155
168
  # ----- Architecture -----
156
169
  architecture:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@educa-corp/sdd-framework",
3
- "version": "0.2.1",
3
+ "version": "0.2.4",
4
4
  "description": "Spec Driven Development workflow framework for Claude Code",
5
5
  "bin": {
6
6
  "sdd-framework": "./bin/index.js"
@@ -83,16 +83,28 @@ Nếu có section `services`:
83
83
  *(vd: `specs/user/create-account/USR01-create-account.md` **và** `specs/user/create-account/bdd/system/UC1.feature` đều → domain = `user`, prd_slug = `create-account`)*
84
84
  - Nếu `$ARGUMENTS` chứa một path, trích xuất segment domain sau `specs_dir`
85
85
 
86
- **2. Route tới service** nếu active domain khớp với một key trong `services`:
87
- - Override `paths.specs_dir` `services.{domain}.specs_dir` **chỉ khi `setup.spec_source` KHÔNG được đặt.** Khi `spec_source` ĐƯỢC đặt, MỌI BDD (web/app/**system**) artifact dùng chung liên team → để bước 4 route sang spec repo; KHÔNG pin theo service ở đây.
88
- - Override `paths.tech_docs_dir` `services.{domain}.tech_docs_dir` — **chỉ khi `setup.spec_source` KHÔNG được đặt.** Khi `spec_source` ĐƯỢC đặt, tech-design (API contract)artifact liên team và phải nằm trong spec repo dùng chung (xử lý ở bước 4), nên để bước 4 route `tech_docs_dir` KHÔNG pin theo service ở đây.
86
+ **1b. Phát hiện active platform** (chỉ cần khi service dạng map-theo-platform bước 2b):
87
+ - Đọc `@trace.platform` từ header của target `.feature` (`system` | `web` | `app`) Gate đã resolve target trước bước này.
88
+ - Nếu target không mang `@trace.platform` (vd targetPRD `.md`), thử suy từ segment `bdd/{platform}/` trong path target.
89
+ - Nếu vẫn không xác định được → `active_platform = null`.
90
+
91
+ **2. Route tới service** — nếu active domain khớp một key trong `services`. Giá trị `services.{domain}` có **hai dạng**; nhận dạng bằng việc có `path` trực tiếp hay không:
92
+
93
+ **2a. Dạng phẳng** — `services.{domain}` có **trực tiếp** `path`/`module` (một domain ↔ một service, mọi platform về cùng submodule). Route như cũ:
89
94
  - Lưu `active_service` = `services.{domain}.path`
90
95
  - Lưu `active_service_module` = `services.{domain}.module`
91
96
  - Nếu service có `module` riêng → dùng nó làm `active_module` (override `tech_stack.module`)
92
97
 
93
- **3. Fallback** — nếu không phát hiện được domain hoặc không service key khớp:
94
- - Giữ path mặc định từ Bước 1
95
- - Đặt `active_service = unresolved`
98
+ **2b. Dạng map-theo-platform** — `services.{domain}` **KHÔNG** `path` trực tiếp mà chứa các sub-key platform (`system` / `web` / `app`), mỗi cái là `{ path, module }` (một business-domain trải trên nhiều platform/submodule). Route theo `active_platform` (bước 1b):
99
+ - Nếu `active_platform` khớp một sub-key → `entry = services.{domain}.{active_platform}`; lưu `active_service = entry.path`, `active_service_module = entry.module` (→ `active_module`, override `tech_stack.module`).
100
+ - Nếu `active_platform = null` (chưa xác định platform, vd đang thao tác cấp PRD) → **KHÔNG** chốt một service; đặt `active_service = multi` và lưu `service_candidates = services.{domain}` (toàn map platform→{path,module}). Lệnh cần một service cụ thể (`/generate-code`, `/dev-*`, `/fix-bug`) luôn chạy trên target `.feature` có platform nên sẽ resolve được ở lần chạy đó; lệnh cấp PRD (`/generate-prd`, `/refine-prd`) không cần service cụ thể.
101
+ - Nếu `active_platform` xác định nhưng không có sub-key tương ứng → `active_service = unresolved` (xem Fallback) với lý do "domain `{domain}` chưa cấu hình platform `{active_platform}`".
102
+
103
+ *(Cả 2a/2b: override `paths.specs_dir`/`paths.tech_docs_dir` per-service CHỈ khi `setup.spec_source` KHÔNG được đặt. Khi `spec_source` ĐƯỢC đặt, MỌI BDD/tech-doc là artifact liên team → để bước 4 route sang spec repo; KHÔNG pin per-service ở đây.)*
104
+
105
+ **3. Fallback**:
106
+ - Không phát hiện được domain, hoặc domain không khớp key nào trong `services` → giữ path mặc định từ Bước 1, đặt `active_service = unresolved`.
107
+ - Domain khớp một map-theo-platform (2b) nhưng `active_platform` xác định mà thiếu sub-key tương ứng → `active_service = unresolved`, ghi lý do rõ để lệnh DỪNG báo lỗi cấu hình (không tự đoán platform).
96
108
 
97
109
  **4. Tự động override theo spec source** — nếu `setup.spec_source` được đặt VÀ path tương ứng chưa được set tường minh trong `paths:`:
98
110
  - Override `paths.specs_dir` → `{spec_source}/specs` — **luôn khi `spec_source` được đặt.** Mọi spec artifact (PRD, BDD, tech-docs, design-spec) nằm dưới gốc spec thống nhất trong spec repo dùng chung theo bố cục feature-package: `{spec_source}/specs/{domain}/{prd-slug}/`. Mọi umbrella (FE/App/BE) đều đọc từ đây. *(`specs/` theo service chỉ khi không có `spec_source`.)*
@@ -112,7 +124,7 @@ Nếu có section `services`:
112
124
 
113
125
  ## Bước 1.6 — [SERVICE CONVENTIONS] Nạp convention riêng của service (chế độ umbrella)
114
126
 
115
- *Bỏ qua hoàn toàn bước này nếu `active_service` là `"unresolved"` hoặc context ở chế độ single-service.*
127
+ *Bỏ qua hoàn toàn bước này nếu `active_service` là `"unresolved"` hoặc `"multi"` (chưa chốt một service — dạng map-theo-platform ở cấp PRD) hoặc context ở chế độ single-service.*
116
128
 
117
129
  Khi `active_service` đã được phân giải thành một path thật ở Bước 1.5 (vd: `user-service/`):
118
130
 
@@ -288,7 +300,8 @@ Ticket : {ticket_prefix}-
288
300
  Dict : {loaded — N canonical terms, M banned terms | missing}
289
301
  Entities : {loaded — EntityA, EntityB, EntityC | missing}
290
302
  Lessons : {loaded — N guardrails | chưa có}
291
- Service : {active_service} ({active_service_module}) | single-service
303
+ Platform : {active_platform: system | web | app | — nếu chưa xác định}
304
+ Service : {active_service} ({active_service_module}) | multi (map-theo-platform, chốt khi target có platform) | single-service
292
305
  Svc Root : {service_root} — đã nạp conventions + trace_dir từ config service | —
293
306
  Status : {FULL | PARTIAL — thiếu: CLAUDE.md / business-dict / core-entities | MINIMAL}
294
307
  ```
@@ -132,12 +132,28 @@ domains:
132
132
  # With spec_source set, only ONE override is needed instead of four separate dir vars.
133
133
  #
134
134
  # services: # domain → service submodule routing
135
- # {{DOMAIN_1}}: # must match the PRD's `Domain` (Metadata row) / folder path segment
135
+ # # Each domain key MUST match the PRD's `Domain` (Metadata row) / folder path segment.
136
+ # # A domain value has TWO forms — context-loader (Step 1.5) auto-detects by whether
137
+ # # `path` is present directly:
138
+ # #
139
+ # # FORM A — FLAT (one domain ↔ one submodule, all platforms):
140
+ # {{DOMAIN_1}}:
136
141
  # path: "{{SERVICE_SUBMODULE_DIR}}" # relative path to service submodule (code + .trace/)
137
142
  # module: "{{STACK_MODULE}}" # e.g., java-spring, nextjs, flutter
138
- # # NOTE: with spec_source set, BDD + tech-docs are cross-team and live in the spec repo —
139
- # # do NOT pin per-service specs_dir / tech_docs_dir here (they would be ignored).
140
- # # Per-service specs_dir / tech_docs_dir apply ONLY when there is no spec_source.
143
+ # #
144
+ # # FORM B PER-PLATFORM MAP (one business-domain implemented on several platforms /
145
+ # # submodules a merged monorepo/workspace). No direct `path`; instead one
146
+ # # sub-key per platform (system | web | app). context-loader routes by the
147
+ # # target .feature's @trace.platform → picks {path, module} for that platform.
148
+ # # The PRD keeps a SINGLE business @trace.domain (do NOT invent onboarding-web).
149
+ # {{DOMAIN_2}}:
150
+ # system: { path: "{{BE_SUBMODULE_DIR}}", module: "java-spring" }
151
+ # web: { path: "{{WEB_SUBMODULE_DIR}}", module: "nextjs" }
152
+ # app: { path: "{{APP_SUBMODULE_DIR}}", module: "flutter" }
153
+ # #
154
+ # # NOTE: with spec_source set, BDD + tech-docs are cross-team and live in the spec repo —
155
+ # # do NOT pin per-service specs_dir / tech_docs_dir here (they would be ignored).
156
+ # # Per-service specs_dir / tech_docs_dir apply ONLY when there is no spec_source.
141
157
  #
142
158
  # IMPORTANT — per-service CLAUDE.md:
143
159
  # Each service submodule should have its OWN CLAUDE.md ({path}/CLAUDE.md) defining its
@@ -145,12 +161,9 @@ domains:
145
161
  # layers: root CLAUDE.md (umbrella-wide shared rules) + {service}/CLAUDE.md (overlay,
146
162
  # wins on conflict for architecture/coding-standards). The agent sits at the umbrella
147
163
  # root, so without a service CLAUDE.md, code generation falls back to umbrella defaults
148
- # (likely the wrong stack). Generate one per service via /setup-ai-first inside each.
149
- # {{DOMAIN_2}}:
150
- # path: "{{SERVICE_2_DIR}}"
151
- # module: "{{STACK_MODULE}}"
152
- # specs_dir: "{{SERVICE_2_DIR}}/specs"
153
- # tech_docs_dir: "{{SERVICE_2_DIR}}/specs"
164
+ # + the module stack-profile (a ⚠️ is flagged in the context recap). Generate one per
165
+ # service via /setup-ai-first inside each. For FORM B, add one CLAUDE.md per platform
166
+ # submodule ({system|web|app}.path/CLAUDE.md).
154
167
 
155
168
  # ----- Architecture -----
156
169
  architecture: