@educa-corp/sdd-framework 0.2.6 → 0.2.8

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 (77) hide show
  1. package/commands/debug.md +4 -1
  2. package/commands/define-product.md +38 -1
  3. package/commands/define-product.tmpl +34 -0
  4. package/commands/dev-gen-test.md +4 -1
  5. package/commands/dev-run-test.md +4 -1
  6. package/commands/dev-smoke-test.md +4 -1
  7. package/commands/fix-bug.md +4 -1
  8. package/commands/generate-architecture.md +4 -1
  9. package/commands/generate-bdd.md +4 -1
  10. package/commands/generate-code.md +80 -26
  11. package/commands/generate-code.tmpl +76 -25
  12. package/commands/generate-design-spec.md +4 -1
  13. package/commands/generate-prd.md +4 -1
  14. package/commands/generate-spec-manifest.md +4 -1
  15. package/commands/generate-tech-docs.md +4 -1
  16. package/commands/learn.md +4 -1
  17. package/commands/map-testids.md +4 -1
  18. package/commands/propose-scenario.md +4 -1
  19. package/commands/qc-analyze.md +4 -1
  20. package/commands/qc-design-test.md +4 -1
  21. package/commands/qc-plan.md +4 -1
  22. package/commands/qc-report.md +4 -1
  23. package/commands/qc-review.md +4 -1
  24. package/commands/qc-run-test.md +4 -1
  25. package/commands/refine-prd.md +4 -1
  26. package/commands/report-bug.md +4 -1
  27. package/commands/review-code.md +4 -1
  28. package/commands/review-context.md +4 -1
  29. package/commands/review-tech-docs.md +4 -1
  30. package/commands/setup-ai-first.md +2 -1
  31. package/commands/validate-traces.md +4 -1
  32. package/core/FRAMEWORK_VERSION +1 -1
  33. package/core/commands/debug.md +4 -1
  34. package/core/commands/define-product.md +38 -1
  35. package/core/commands/dev-gen-test.md +4 -1
  36. package/core/commands/dev-run-test.md +4 -1
  37. package/core/commands/dev-smoke-test.md +4 -1
  38. package/core/commands/fix-bug.md +4 -1
  39. package/core/commands/generate-architecture.md +4 -1
  40. package/core/commands/generate-bdd.md +4 -1
  41. package/core/commands/generate-code.md +80 -26
  42. package/core/commands/generate-design-spec.md +4 -1
  43. package/core/commands/generate-prd.md +4 -1
  44. package/core/commands/generate-spec-manifest.md +4 -1
  45. package/core/commands/generate-tech-docs.md +4 -1
  46. package/core/commands/learn.md +4 -1
  47. package/core/commands/map-testids.md +4 -1
  48. package/core/commands/propose-scenario.md +4 -1
  49. package/core/commands/qc-analyze.md +4 -1
  50. package/core/commands/qc-design-test.md +4 -1
  51. package/core/commands/qc-plan.md +4 -1
  52. package/core/commands/qc-report.md +4 -1
  53. package/core/commands/qc-review.md +4 -1
  54. package/core/commands/qc-run-test.md +4 -1
  55. package/core/commands/refine-prd.md +4 -1
  56. package/core/commands/report-bug.md +4 -1
  57. package/core/commands/review-code.md +4 -1
  58. package/core/commands/review-context.md +4 -1
  59. package/core/commands/review-tech-docs.md +4 -1
  60. package/core/commands/setup-ai-first.md +2 -1
  61. package/core/commands/validate-traces.md +4 -1
  62. package/core/modules/phaser-game/architecture-snippets/phaser-scene-patterns.md +646 -0
  63. package/core/modules/phaser-game/module.yaml +15 -0
  64. package/core/modules/phaser-game/stack-profile.yaml +90 -0
  65. package/core/steps/context-loader.md +2 -0
  66. package/core/steps/gate.md +2 -1
  67. package/core/templates/project-context.yaml +6 -0
  68. package/docs/02-concepts/pipeline-steps/06-code.md +7 -4
  69. package/docs/03-guides/developer.md +6 -3
  70. package/docs/explain/09-generate-code.md +41 -3
  71. package/modules/phaser-game/architecture-snippets/phaser-scene-patterns.md +646 -0
  72. package/modules/phaser-game/module.yaml +15 -0
  73. package/modules/phaser-game/stack-profile.yaml +90 -0
  74. package/package.json +1 -1
  75. package/steps/context-loader.md +2 -0
  76. package/steps/gate.md +2 -1
  77. package/templates/project-context.yaml +6 -0
@@ -0,0 +1,90 @@
1
+ build:
2
+ compile: "tsc --noEmit && vite build" # type-check, then bundle to dist/ (base './')
3
+ test: "vitest run"
4
+ run: "vite" # dev server with HMR
5
+ lint: "eslint . --ext .ts"
6
+
7
+ architecture:
8
+ style: "Phaser 3 scene state machine + framework-free domain logic + direct backend API (token injected by host) + thin native window bridge"
9
+ key_rules:
10
+ - "Keep GAME LOGIC that can be pure (scoring, spawn tables, weight/physics math) in framework-free TS modules under src/domain/ — no Phaser imports there, so it unit-tests in Node"
11
+ - "Scenes orchestrate; they call domain functions and drive Phaser objects — they do NOT hold complex business math inline"
12
+ - "One responsibility per scene: BootScene (read host config), PreloadScene (load assets + game data), MenuScene, GameScene, GameOverScene, UIScene (HUD overlay run in parallel)"
13
+ - "Game objects (Miner, Hook, Gold) are Phaser.GameObjects subclasses (prefabs) — reusable, self-contained"
14
+ - "APPROACH A: the game calls the backend DIRECTLY (fetch). Host (Flutter) injects a HostConfig (apiBase, token, userId, sessionId) into the WebView BEFORE load — the game never hard-codes URLs or tokens"
15
+ - "All backend I/O goes through ONE API client (src/api/) that reads the token from HostConfig and sets the Authorization header — never scatter raw fetch() across scenes"
16
+ - "The native bridge (GameContract) is thin: window CONTROL only (onReady, onClose/back). Data does NOT go through native in Approach A — it goes over HTTP"
17
+ - "Tuning numbers live in src/config/constants.ts, never hard-coded in scenes"
18
+ - "Vite base MUST be './' so the built bundle works from file:// inside a WebView"
19
+ - "Use Phaser Arcade Physics for collision/overlap; avoid hand-rolled loops unless the mechanic needs it"
20
+ - "Cross-scene / game↔UI communication uses an event emitter (this.game.events or a shared EventBus), not direct scene references"
21
+ - "Debug console (eruda) is loaded ONLY when the URL has ?debug=1 — never bundled into the default runtime path"
22
+ - "Never trust HostConfig blindly — validate it at boot (BootScene) and fail loudly (call onClose) if apiBase/token is missing"
23
+ folder_structure: |
24
+ .
25
+ ├── index.html ← <div id="game"> + <script type="module" src="/src/main.ts">
26
+ ├── vite.config.ts ← base: './', @vitejs/plugin-legacy for old WebView
27
+ ├── tsconfig.json
28
+ ├── public/assets/ ← images, audio, atlases, tilemaps (copied as-is)
29
+ ├── src/
30
+ │ ├── main.ts ← Phaser.Game config + scene list, mount debug bridge
31
+ │ ├── contracts/
32
+ │ │ ├── game-contract.ts ← GameContract (window control) + HostConfig shape
33
+ │ │ └── api.dto.ts ← backend request/response DTOs (GameDataDto, ResultDto)
34
+ │ ├── config/
35
+ │ │ ├── constants.ts ← GRAVITY, HOOK_SPEED, SPAWN_TABLE...
36
+ │ │ ├── host-config.ts ← read + validate window.GAME_CONFIG injected by Flutter
37
+ │ │ └── game-config.ts ← Phaser.Types.Core.GameConfig
38
+ │ ├── api/ ← client.ts (fetch + auth header), game.api.ts (loadData, submitResult)
39
+ │ ├── domain/ ← PURE TS, no Phaser: scoring.ts, spawn.ts, hook-physics.ts
40
+ │ ├── scenes/ ← BootScene.ts, PreloadScene.ts, MenuScene.ts, GameScene.ts, GameOverScene.ts, UIScene.ts
41
+ │ ├── objects/ ← Miner.ts, Hook.ts, Gold.ts (Phaser.GameObjects prefabs)
42
+ │ ├── bridge/ ← native-bridge.ts (onReady/onClose), event-bus.ts
43
+ │ └── debug/ ← eruda.ts (lazy-loaded when ?debug=1)
44
+ └── tests/ ← *.test.ts (Vitest → domain/) + e2e/*.spec.ts (Playwright)
45
+
46
+ coding_standards:
47
+ naming:
48
+ scenes: "PascalCase + 'Scene' suffix (BootScene, GameScene, UIScene)"
49
+ game_objects: "PascalCase prefab class (Miner, Hook, Gold)"
50
+ domain_functions: "camelCase pure functions (calcScore, rollSpawn, retractSpeed)"
51
+ contracts: "PascalCase interface, 'Contract'/'Dto' suffix (GameContract, ScoreDto)"
52
+ constants: "SCREAMING_SNAKE_CASE (GRAVITY, HOOK_SPEED, SPAWN_TABLE)"
53
+ scene_keys: "kebab or PascalCase string key matching class (scene.start('GameScene'))"
54
+ files:
55
+ scene: "{Name}Scene.ts"
56
+ object: "{Object}.ts"
57
+ domain: "{topic}.ts"
58
+ contract: "game-contract.ts / {topic}.dto.ts"
59
+ test: "{unit}.test.ts"
60
+ patterns:
61
+ scenes: "Phaser scene lifecycle: init(data) → preload() → create() → update(t, dt); transition via this.scene.start('Key', data)"
62
+ hud: "UIScene launched in parallel (this.scene.launch('UIScene')) and rendered above GameScene"
63
+ physics: "Arcade Physics — this.physics.add.overlap(hook, goldGroup, onGrab)"
64
+ assets: "Load everything in PreloadScene with a progress bar; reference by key afterwards"
65
+ animation: "Tweens (this.tweens.add) for hook swing/retract easing; sprite sheet anims for characters"
66
+ state_transfer: "Pass data between scenes via scene.start('Key', payload); load game data once in PreloadScene via the API client, then hand it to GameScene"
67
+ host_config: "Flutter injects window.GAME_CONFIG = { apiBase, token, userId, sessionId } via initialUserScripts/evaluateJavascript BEFORE the page loads; read + validate it once in host-config.ts"
68
+ api_client: "APPROACH A — game calls backend directly. One fetch wrapper sets Authorization: Bearer <token> from HostConfig; game.api.ts exposes loadData()/submitResult() returning typed DTOs; handle 401/timeout and surface to the player"
69
+ native_bridge: "Thin GameContract = window control only. onReady() when boot ok; onClose() on back/X button → Flutter pops the WebView. NO data over the bridge in Approach A"
70
+ events: "EventBus (Phaser.Events.EventEmitter) for game→HUD updates (score changed, time left)"
71
+ webview: "Vite base './' + @vitejs/plugin-legacy + core-js for ES5 WebViews; toggle legacy per Android min-SDK"
72
+ debug: "eruda (or vConsole) lazy-imported only when new URLSearchParams(location.search).get('debug') === '1'"
73
+
74
+ testing:
75
+ unit: "Vitest — target src/domain/ pure functions (scoring, spawn rolls, retract speed by weight). No Phaser, no DOM, no canvas mock needed"
76
+ e2e: "Playwright — run vite preview, load the page, drive canvas via keyboard/pointer, assert on the HUD DOM overlay or exposed window.__gameState"
77
+ patterns:
78
+ - "Split logic so the testable part imports zero Phaser — Phaser scenes stay thin wrappers"
79
+ - "Inject a seeded RNG into spawn functions so tests are deterministic"
80
+ - "Expose a minimal window.__gameState (score, scene) in dev/e2e builds for Playwright assertions"
81
+ - "Mock the native bridge with a fake GameContract in tests; assert onReady/onClose are called"
82
+ - "Mock fetch (or the API client) in unit tests; assert loadData()/submitResult() send the right URL, auth header, and payload"
83
+ - "In e2e, stub window.GAME_CONFIG and intercept backend routes with Playwright page.route() so tests need no real server"
84
+ - "Do not pixel-diff the canvas — assert on state and HUD text instead"
85
+
86
+ trace_tags:
87
+ implements: "// @trace.implements={UC-ID}-SC{N}"
88
+ source: "// @trace.source=specs/{domain}/{prd-slug}/bdd/{UC-ID}.feature"
89
+ verifies: "// @trace.verifies={UC-ID}"
90
+ test_type: "// @trace.test_type=unit|integration"
@@ -42,6 +42,7 @@ Thực hiện các bước theo đúng thứ tự. Lưu mọi thứ vào bộ nh
42
42
  - `paths.business_dictionary` → path tới business-dictionary.md
43
43
  - `paths.core_entities` → path tới core-entities.md
44
44
  - `paths.tech_docs_dir` → gốc tài liệu kỹ thuật (gộp với specs_dir trong bố cục feature-package — tech-docs nằm dưới `{specs_dir}/{domain}/{prd-slug}/tech-docs/`)
45
+ - `paths.src_dir` → gốc mã nguồn (nơi generate-code đặt & quét code; nguồn chính cho FE + phạm vi reuse-scan của DS5)
45
46
  - `paths.trace_dir` → thư mục trạng thái trace; cấu trúc: `.trace/{domain}/{prd-slug}/{UC-ID}-{platform}.tsv` (mỗi UC × platform một sổ)
46
47
 
47
48
  Nếu không có section `paths`, dùng các giá trị mặc định:
@@ -54,6 +55,7 @@ Nếu không có section `paths`, dùng các giá trị mặc định:
54
55
  - `business_dictionary` = `specs/domain-knowledge/business-dictionary.md`
55
56
  - `core_entities` = `specs/domain-knowledge/core-entities.md`
56
57
  - `tech_docs_dir` = `specs`
58
+ - `src_dir` = `src`
57
59
  - `trace_dir` = `.trace`
58
60
 
59
61
  Lưu ý: Trong bố cục feature-package, `specs_dir` là gốc thống nhất. Mọi loại spec artifact (PRD, BDD, tech-docs, design-spec) đều nằm dưới `{specs_dir}/{domain}/{prd-slug}/`. `prd-slug` là tên folder feature-package, không phải một biến config riêng.
@@ -49,7 +49,8 @@ Hiển thị và chờ phản hồi:
49
49
 
50
50
  ## Bước 1 — Xác định Target File
51
51
 
52
- 1. Nếu `$ARGUMENTS` được cung cấp trỏ tới một file tồn tại dùng trực tiếp làm target.
52
+ 0. **Tách cờ trước khi resolve target.** `$ARGUMENTS` thể lẫn các `--flag` (vd `--phase=integration`, `--comment`, `--fix`). **Loại bỏ mọi token bắt đầu bằng `--`** ra khỏi phần dùng để tìm target — chỉ giữ phần path/UC-ID/ticket. (Các flag đó do phần logic riêng của lệnh parse ở bước sau, KHÔNG phải tên file.)
53
+ 1. Nếu `$ARGUMENTS` (đã tách cờ) được cung cấp và trỏ tới một file tồn tại → dùng trực tiếp làm target.
53
54
  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/`):
54
55
  - **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 đó.
55
56
  - **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.)*
@@ -35,6 +35,12 @@ paths:
35
35
  # prd-slug is derived from the PRD folder path — not a separate config variable.
36
36
  specs_dir: "specs"
37
37
  templates_dir: "specs/templates"
38
+
39
+ # Source code root — gốc mã nguồn của service/app này (nơi generate-code đặt & quét code).
40
+ # BE thường suy vị trí từ CLAUDE.md §2 (code_base_package + layer); src_dir là gốc chung,
41
+ # và là NGUỒN CHÍNH cho FE (component/hook/service/adapter) + phạm vi quét reuse (DS5).
42
+ # Ví dụ: "src" (mono FE), "app/src", "src/main/java" (BE). Chỉnh theo layout thật của repo.
43
+ src_dir: "src"
38
44
  feature_template: ".agent/templates/feature.template" # SoT skeleton .feature (dùng bởi /generate-bdd qua {{include}})
39
45
  bdd_writing_guide: "specs/templates/bdd-writing-guide.md"
40
46
  trace_report: "specs/.trace/trace-report.md"
@@ -70,12 +70,15 @@ Code là **hệ quả của spec, không phải nguồn**. Bước này biến s
70
70
  5. **Build verify** — chạy `{conventions.build_command}`, ≤3 retry.
71
71
  6. **Ghi trace row** vào `.tsv` (trong spec repo nếu umbrella — thao tác ghi liên-repo).
72
72
 
73
- **Phase cho Frontend:**
74
- | Phase | Ý nghĩa |
73
+ **Mode theo phase × platform:**
74
+ | Mode | Ý nghĩa |
75
75
  |-------|---------|
76
76
  | `--phase=ui` | FE Phase 1 — sinh UI + layer **mock API** từ System BDD contract |
77
- | `--phase=integration` | FE nối vào **BE contract thật** (đọc tech-design từ spec repo) |
78
- | *(default)* | Backend — sinh từ tech-design §4 |
77
+ | `--phase=integration` | FE Phase 2 **thay** mock bằng **BE contract thật** (đọc tech-design từ spec repo); UI đã có |
78
+ | *(default, `web`/`app`)* | **`fe_full`** — sinh UI **+** wire API thật một lần, bỏ lớp mock |
79
+ | *(default, `system`)* | Backend — sinh từ tech-design §4 |
80
+
81
+ > Nhánh wire API thật (`integration` / `fe_full`) đi qua **DS4** (kiểm §4.5.4 đủ, vét nguồn rồi gộp-hỏi) và **DS5** (phát hiện component/service FE đang chạy → hỏi reuse/new, không dựng song song). Bảng hành vi đầy đủ từng bước → [Explain · generate-code](../../explain/09-generate-code.md#hành-vi-theo-mode-phase--platform).
79
82
 
80
83
  **`/review-code`** — read-only, chỉ báo findings (không tự sửa: "AI tự fix tự review" = lặp lỗi).
81
84
  **`/fix-bug`** — sửa lỗi có root-cause + regression test; xem [Feedback Loop](10-feedback-loop.md).
@@ -42,12 +42,15 @@ flowchart LR
42
42
 
43
43
  ---
44
44
 
45
- ## Frontend: hai phase
45
+ ## Frontend: hai phase, hoặc một lần (full)
46
46
 
47
- | Phase | Khi nào | Việc |
47
+ | Mode | Khi nào | Việc |
48
48
  |-------|---------|------|
49
49
  | `--phase=ui` | BE contract chưa sẵn | Sinh UI + layer **mock API** từ System BDD |
50
- | `--phase=integration` | BE contract `approved` | Nối vào contract thật (đọc tech-design từ spec repo) |
50
+ | `--phase=integration` | BE contract `approved`, UI đã có | Nối vào contract thật (đọc tech-design từ spec repo) |
51
+ | **không flag** (`fe_full`) | contract sẵn, muốn làm dứt điểm | Sinh UI **+** wire API thật một lần, bỏ lớp mock |
52
+
53
+ > Cả `integration` lẫn `fe_full` đều qua **DS4** (kiểm §4.5.4 đủ trước khi wire; vét nguồn rồi gộp-hỏi thay vì hỏi live) và **DS5** (thấy component/service FE đang chạy thì **hỏi reuse/new**, không im lặng dựng mới). Bảng hành vi đầy đủ theo từng mode → [Explain · generate-code](../explain/09-generate-code.md#hành-vi-theo-mode-phase--platform).
51
54
 
52
55
  ---
53
56
 
@@ -15,7 +15,7 @@ Code là **hệ quả của spec**. Command biến scenario thành code sao cho:
15
15
  ## Vị trí & tiền đề
16
16
 
17
17
  - **Vị trí:** Phase Implementation, sau BDD + tech-docs.
18
- - **Gate vào:** cảnh báo mềm DS1 (BDD chưa approved), DS2 (design-spec), DS3 (tech-doc contract).
18
+ - **Gate vào:** cảnh báo mềm DS1 (BDD chưa approved), DS2 (design-spec), DS3 (tech-doc contract BE), **DS4** (client contract §4.5.4) + **DS5** (FE reuse gate) — DS4/DS5 chỉ khi wire API thật (`integration`/`fe_full`). Xem [Hành vi theo mode](#hành-vi-theo-mode-phase--platform).
19
19
 
20
20
  ---
21
21
 
@@ -30,7 +30,7 @@ Code là **hệ quả của spec**. Command biến scenario thành code sao cho:
30
30
  ## Các bước xử lý (chi tiết)
31
31
 
32
32
  1. **Guard BDD & Design Spec** — DS1/DS2/DS3 cảnh báo mềm nếu nguồn chưa chốt.
33
- 2. **Phase Detection** — `--phase=ui` (FE Phase 1: UI + mock API; **mock source hybrid** = ưu tiên tech-doc §4 contract, fallback System BDD) · `--phase=integration` (thay mock bằng API thật) · default (BE/full-stack). `system` bỏ flag.
33
+ 2. **Phase Detection** — `--phase=ui` (FE Phase 1: UI + mock API; **mock source hybrid** = ưu tiên tech-doc §4 contract, fallback System BDD) · `--phase=integration` (thay mock bằng API thật) · **`fe_full`** (default trên `web`/`app`: UI + real API một lần, bỏ mock) · default BE (`system`, full backend). Chi tiết: [Hành vi theo mode](#hành-vi-theo-mode-phase--platform).
34
34
  3. **Figma Dev Mode MCP Check** (sinh UI) — đọc frame Figma độ trung thực cao cho codegen.
35
35
  4. **Read Trace State** — so `bdd_version`/spec với `.tsv` → phân loại **new / drifted / synced-skip**.
36
36
  5. **Package Placement** — đặt code đúng `code_base_package` + strategy (by-layer/by-feature), chống phân mảnh.
@@ -42,11 +42,47 @@ Code là **hệ quả của spec**. Command biến scenario thành code sao cho:
42
42
  8. **CHECKPOINT — Code Generation Plan** — 🛑 trình: scenarios (X new, Y drifted, Z synced-skip) · CREATE (mới) · **EXTEND (ADD-ONLY, cấm full Write)** · **FILL** (lấp stub tại chỗ, không đẻ method song song) · SKIP + danh sách member GIỮ NGUYÊN (UC khác). **Scope Lock**: chỉ đọc/implement UC target.
43
43
  9. **Branch** `feature/{TICKET}-{slug}`.
44
44
  10. **Generate** theo **thứ tự layer từ CLAUDE.md §2**; tag `@trace.implements/source` ở boundary.
45
- 11. **Mock/Integration** — layer mock (`--phase=ui`) hoặc thay adapter thật (`--phase=integration`).
45
+ 11. **Mock/Integration** — layer mock (`--phase=ui`) · thay adapter thật (`--phase=integration`) · wire real thẳng (`fe_full`). Nhánh wire API thật đi qua **DS4** (kiểm §4.5.4 đủ + **SRC-CHAIN** vét nguồn, gộp-hỏi thay vì hỏi live).
46
46
  12. **Self-Review (3 vòng)** → **Build Verify** (`{build_command}`, ≤3 retry) → **Write Trace State** → **Refresh Panel Mirror** (Living Docs umbrella) → **Commit** (sau khi duyệt).
47
47
 
48
48
  ---
49
49
 
50
+ ## Hành vi theo mode (Phase × Platform)
51
+
52
+ `generate-code` rẽ nhánh theo `--phase` và platform của `.feature`. Bốn mode:
53
+
54
+ - **`--phase=ui`** — FE Phase 1: chỉ UI + mock, không chạm API thật (prototype nhanh).
55
+ - **`--phase=integration`** — FE Phase 2: **không** sinh lại UI, chỉ **thay** mock bằng real adapter.
56
+ - **`fe_full`** — default (không flag) trên feature `web`/`app`: UI **+** wire API thật trong một lần, bỏ lớp mock trung gian.
57
+ - **default BE** — không flag trên `system`: full backend.
58
+
59
+ | Bước / Gate | `--phase=ui` | `--phase=integration` | `fe_full` (default web/app) | default BE (`system`) |
60
+ |---|:--:|:--:|:--:|:--:|
61
+ | **DS1** — BDD `approved` guard | ✅ | ✅ | ✅ | ✅ |
62
+ | **DS2** — design-spec guard (FE) | ✅ | ✅ | ✅ | — |
63
+ | **DS3** — BE contract gate (§4) | — | — | — | ✅ |
64
+ | **DS4** — client contract gate (§4.5.4) | ⏭️ cố ý | ✅ | ✅ | — |
65
+ | **DS5** — FE component/service reuse gate | ⏭️ | ✅ | ✅ | — |
66
+ | **Sinh UI** (Figma MCP, component, state) | ✅ | ⏭️ giữ UI cũ | ✅ | — |
67
+ | **Test Selectors** (emit test-id) | ✅ | ⏭️ | ✅ | — |
68
+ | **Mock API Layer** | ✅ | ⏭️ | ⏭️ | — |
69
+ | **Integration Phase** (real API adapter) | ⏭️ | ✅ | ✅ | — |
70
+ | **Wire-up API** | dùng **mock** | **lật** mock→real | wire **thẳng** real | — (BE là API) |
71
+ | **SRC-CHAIN** (vét nguồn trước khi hỏi) | ✅¹ | ✅ | ✅ | ✅ |
72
+ | **Nguồn shape/contract** | §4 nếu có, else System BDD | §4.5.4 → §4.1–4.3 | §4.5.4 → §4.1–4.3 (+ design-spec) | §4 (DS3, nguyên văn) |
73
+ | **Output adapter** | `{UC}MockApiAdapter` | `{UC}ApiAdapter` (giữ mock) | `{UC}ApiAdapter` (không mock) | — |
74
+ | **Trace `fe_phase`** | `ui` | `integrated` | `integrated` | `—` |
75
+
76
+ <sub>✅ chạy · ⏭️ bỏ qua (có chủ đích) · — không áp dụng. ¹SRC-CHAIN là quy tắc chung, active mọi mode; chỉ được **gom-hỏi qua DS4** ở integration/fe_full.</sub>
77
+
78
+ **Bất đối xứng đáng nhớ:**
79
+ - **DS4** có ở mọi nhánh wire API thật (integration + fe_full), **trừ** `ui` (cố ý) và BE (BE dùng DS3).
80
+ - **Mock API Layer** là thứ **duy nhất** riêng `--phase=ui`.
81
+ - **Sinh UI** có ở `ui` và `fe_full`, **không** ở `integration` (giả định UI đã có từ pha ui).
82
+ - **`fe_full` = `ui` + `integration`** gộp một lần (bỏ mock), nhưng **vẫn giữ safety gate DS4** — không phải đường tắt kém an toàn.
83
+
84
+ ---
85
+
50
86
  ## Checkpoint & Gate
51
87
 
52
88
  - 🛑 **Comprehension checkpoint** (Code Generation Plan) — điểm dừng chính; Dev xác nhận drift + scope đúng.
@@ -59,6 +95,8 @@ Code là **hệ quả của spec**. Command biến scenario thành code sao cho:
59
95
  - **Seam & Stub Ledger** — cơ chế tinh vi nhất: giải quyết vấn đề "sinh code từng BDD rời tạo hàm mồ côi". `READY` là cờ 🔴 để lần sau nối hàng thật vào.
60
96
  - **Scope Lock + ADD-ONLY + FILL** — bảo toàn code UC khác trong file dùng chung; EXTEND chỉ Edit thêm, FILL lấp stub tại chỗ.
61
97
  - **Mock source hybrid** — mock shape ưu tiên contract thật để đỡ rework lúc integration.
98
+ - **SRC-CHAIN + DS4** — "vét cạn nguồn trước khi hỏi": khi §4.5.4 thiếu, quét lần lượt PRD/BDD/design-spec/core-entities/mock adapter (skip-if-answered) rồi **gộp mọi gap vào một checkpoint** thay vì hỏi live từng câu. DS4 là cổng đối xứng với DS3 của BE cho nhánh FE wire API thật (integration/fe_full).
99
+ - **DS5 — FE reuse gate** ("discover & reuse trước khi dựng mới"): ở nhánh wire API thật, nếu **không có mock adapter** (chạy `--phase=integration` một mình trên app brownfield) nhưng **quét thấy component/service FE đang chạy** → **hỏi reuse/new** thay vì im lặng dựng adapter song song, mồ côi. Chọn *reuse* → EXTEND/wire vào code có sẵn.
62
100
  - **3 vòng self-review + build ≤3 retry** — hai lớp tự kiểm trước khi giao.
63
101
 
64
102
  ---