@educa-corp/sdd-framework 0.5.0 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (243) hide show
  1. package/bin/build.js +113 -19
  2. package/bin/gate-trace.js +487 -0
  3. package/bin/index.js +445 -146
  4. package/bin/lint-trace.js +643 -0
  5. package/bin/self-check.js +804 -2
  6. package/bin/trace-schema.json +621 -10
  7. package/core/FRAMEWORK_VERSION +1 -1
  8. package/core/README.md +20 -0
  9. package/core/commands/amend-prd.md +518 -0
  10. package/core/commands/debug.md +123 -511
  11. package/core/commands/define-product.md +86 -510
  12. package/core/commands/dev-gen-test.md +86 -510
  13. package/core/commands/dev-run-test.md +133 -519
  14. package/core/commands/dev-smoke-test.md +86 -510
  15. package/core/commands/extend-prd.md +128 -522
  16. package/core/commands/fix-bug.md +118 -509
  17. package/core/commands/generate-architecture.md +94 -515
  18. package/core/commands/generate-bdd.md +128 -513
  19. package/core/commands/generate-code.md +119 -510
  20. package/core/commands/generate-design-spec.md +86 -510
  21. package/core/commands/generate-prd.md +89 -510
  22. package/core/commands/generate-spec-manifest.md +86 -510
  23. package/core/commands/generate-tech-docs.md +120 -512
  24. package/core/commands/learn.md +172 -496
  25. package/core/commands/map-testids.md +86 -510
  26. package/core/commands/propose-scenario.md +86 -510
  27. package/core/commands/qc-analyze.md +86 -510
  28. package/core/commands/qc-design-test.md +86 -510
  29. package/core/commands/qc-plan.md +86 -510
  30. package/core/commands/qc-report.md +86 -510
  31. package/core/commands/qc-review.md +86 -510
  32. package/core/commands/qc-run-test.md +115 -513
  33. package/core/commands/refine-prd.md +112 -522
  34. package/core/commands/report-bug.md +86 -510
  35. package/core/commands/review-code.md +123 -511
  36. package/core/commands/review-context.md +136 -522
  37. package/core/commands/review-tech-docs.md +90 -511
  38. package/core/commands/setup-ai-first.md +166 -138
  39. package/core/commands/sync.md +155 -107
  40. package/core/commands/update-framework.md +16 -103
  41. package/core/commands/validate-traces.md +426 -511
  42. package/core/hooks/data-guard.js +174 -83
  43. package/core/hooks/settings.json +2 -1
  44. package/core/rules/workflow.md +64 -4
  45. package/core/steps/capture-lesson.md +34 -1
  46. package/core/steps/context-loader.md +50 -8
  47. package/core/steps/gate.md +92 -35
  48. package/core/steps/report-footer.md +23 -0
  49. package/core/templates/README.md +24 -1
  50. package/core/templates/ci/trace-gate.yml +146 -0
  51. package/core/templates/feature.template +1 -1
  52. package/core/templates/hooks/pre-push +61 -0
  53. package/docs/02-concepts/architecture.md +61 -6
  54. package/docs/02-concepts/traceability.md +57 -0
  55. package/docs/03-guides/architect.md +63 -0
  56. package/docs/04-reference/commands.md +148 -134
  57. package/docs/04-reference/model-selection.md +32 -19
  58. package/docs/04-reference/trace-schema.md +39 -0
  59. package/docs/explain/02b-extend-prd.md +1 -1
  60. package/docs/explain/02c-amend-prd.md +152 -0
  61. package/docs/explain/21-validate-traces.md +2 -1
  62. package/docs/explain/27-learn.md +5 -3
  63. package/docs/explain/28-sync.md +25 -0
  64. package/docs/explain/README.md +136 -135
  65. package/package.json +5 -9
  66. package/commands/debug.md +0 -917
  67. package/commands/debug.tmpl +0 -257
  68. package/commands/define-product.md +0 -862
  69. package/commands/define-product.tmpl +0 -225
  70. package/commands/dev-gen-test.md +0 -1124
  71. package/commands/dev-gen-test.tmpl +0 -490
  72. package/commands/dev-run-test.md +0 -859
  73. package/commands/dev-run-test.tmpl +0 -225
  74. package/commands/dev-smoke-test.md +0 -798
  75. package/commands/dev-smoke-test.tmpl +0 -217
  76. package/commands/extend-prd.md +0 -907
  77. package/commands/extend-prd.tmpl +0 -270
  78. package/commands/fix-bug.md +0 -910
  79. package/commands/fix-bug.tmpl +0 -197
  80. package/commands/generate-architecture.md +0 -775
  81. package/commands/generate-architecture.tmpl +0 -194
  82. package/commands/generate-bdd.md +0 -1347
  83. package/commands/generate-bdd.tmpl +0 -590
  84. package/commands/generate-code.md +0 -1283
  85. package/commands/generate-code.tmpl +0 -649
  86. package/commands/generate-design-spec.md +0 -1161
  87. package/commands/generate-design-spec.tmpl +0 -524
  88. package/commands/generate-prd.md +0 -1143
  89. package/commands/generate-prd.tmpl +0 -223
  90. package/commands/generate-spec-manifest.md +0 -745
  91. package/commands/generate-spec-manifest.tmpl +0 -164
  92. package/commands/generate-tech-docs.md +0 -1344
  93. package/commands/generate-tech-docs.tmpl +0 -273
  94. package/commands/learn.md +0 -723
  95. package/commands/learn.tmpl +0 -63
  96. package/commands/map-testids.md +0 -662
  97. package/commands/map-testids.tmpl +0 -81
  98. package/commands/propose-scenario.md +0 -783
  99. package/commands/propose-scenario.tmpl +0 -202
  100. package/commands/qc-analyze.md +0 -693
  101. package/commands/qc-analyze.tmpl +0 -112
  102. package/commands/qc-design-test.md +0 -650
  103. package/commands/qc-design-test.tmpl +0 -69
  104. package/commands/qc-plan.md +0 -630
  105. package/commands/qc-plan.tmpl +0 -49
  106. package/commands/qc-report.md +0 -641
  107. package/commands/qc-report.tmpl +0 -60
  108. package/commands/qc-review.md +0 -634
  109. package/commands/qc-review.tmpl +0 -53
  110. package/commands/qc-run-test.md +0 -750
  111. package/commands/qc-run-test.tmpl +0 -116
  112. package/commands/refine-prd.md +0 -1074
  113. package/commands/refine-prd.tmpl +0 -278
  114. package/commands/report-bug.md +0 -729
  115. package/commands/report-bug.tmpl +0 -148
  116. package/commands/review-code.md +0 -803
  117. package/commands/review-code.tmpl +0 -143
  118. package/commands/review-context.md +0 -1323
  119. package/commands/review-context.tmpl +0 -527
  120. package/commands/review-tech-docs.md +0 -982
  121. package/commands/review-tech-docs.tmpl +0 -401
  122. package/commands/setup-ai-first.md +0 -574
  123. package/commands/setup-ai-first.tmpl +0 -378
  124. package/commands/sync.md +0 -486
  125. package/commands/sync.tmpl +0 -384
  126. package/commands/update-framework.md +0 -290
  127. package/commands/update-framework.tmpl +0 -188
  128. package/commands/validate-traces.md +0 -1435
  129. package/commands/validate-traces.tmpl +0 -854
  130. package/hooks/data-guard.js +0 -141
  131. package/hooks/settings.json +0 -18
  132. package/modules/android-compose/module.yaml +0 -13
  133. package/modules/android-compose/stack-profile.yaml +0 -57
  134. package/modules/angular/architecture-snippets/component-patterns.md +0 -187
  135. package/modules/angular/module.yaml +0 -6
  136. package/modules/angular/stack-profile.yaml +0 -38
  137. package/modules/context-engineering/architecture-snippets/context-design.md +0 -119
  138. package/modules/context-engineering/module.yaml +0 -9
  139. package/modules/context-engineering/stack-profile.yaml +0 -61
  140. package/modules/dotnet/architecture-snippets/clean-arch.md +0 -160
  141. package/modules/dotnet/module.yaml +0 -6
  142. package/modules/dotnet/stack-profile.yaml +0 -50
  143. package/modules/flutter/module.yaml +0 -14
  144. package/modules/flutter/stack-profile.yaml +0 -59
  145. package/modules/golang/architecture-snippets/domain-layout.md +0 -283
  146. package/modules/golang/module.yaml +0 -6
  147. package/modules/golang/stack-profile.yaml +0 -40
  148. package/modules/ios-swiftui/module.yaml +0 -13
  149. package/modules/ios-swiftui/stack-profile.yaml +0 -55
  150. package/modules/java-spring/architecture-snippets/layered-arch.md +0 -201
  151. package/modules/java-spring/module.yaml +0 -15
  152. package/modules/java-spring/stack-profile.yaml +0 -28
  153. package/modules/nextjs/architecture-snippets/app-router-patterns.md +0 -269
  154. package/modules/nextjs/module.yaml +0 -14
  155. package/modules/nextjs/stack-profile.yaml +0 -74
  156. package/modules/nuxt/module.yaml +0 -14
  157. package/modules/nuxt/stack-profile.yaml +0 -58
  158. package/modules/phaser-game/architecture-snippets/phaser-scene-patterns.md +0 -646
  159. package/modules/phaser-game/module.yaml +0 -15
  160. package/modules/phaser-game/stack-profile.yaml +0 -90
  161. package/modules/php-laravel/architecture-snippets/service-repository.md +0 -302
  162. package/modules/php-laravel/module.yaml +0 -15
  163. package/modules/php-laravel/stack-profile.yaml +0 -56
  164. package/modules/qc-playwright/stack-profile.yaml +0 -66
  165. package/modules/react/architecture-snippets/hooks-query-patterns.md +0 -254
  166. package/modules/react/module.yaml +0 -14
  167. package/modules/react/stack-profile.yaml +0 -63
  168. package/modules/react-native/module.yaml +0 -14
  169. package/modules/react-native/stack-profile.yaml +0 -56
  170. package/modules/vue/module.yaml +0 -14
  171. package/modules/vue/stack-profile.yaml +0 -65
  172. package/rules/data-protection.md +0 -80
  173. package/rules/workflow.md +0 -73
  174. package/scripts/init.sh +0 -49
  175. package/scripts/upgrade.sh +0 -94
  176. package/skills/code/SKILL.md +0 -19
  177. package/skills/code/SKILL.tmpl +0 -19
  178. package/skills/debug/SKILL.md +0 -19
  179. package/skills/debug/SKILL.tmpl +0 -19
  180. package/skills/design-spec/SKILL.md +0 -11
  181. package/skills/design-spec/SKILL.tmpl +0 -11
  182. package/skills/discovery/SKILL.md +0 -14
  183. package/skills/discovery/SKILL.tmpl +0 -14
  184. package/skills/prd/SKILL.md +0 -19
  185. package/skills/prd/SKILL.tmpl +0 -19
  186. package/skills/qc/qa-analyst/DOC_GAPS.template.md +0 -63
  187. package/skills/qc/qa-analyst/acceptance-criteria.md +0 -60
  188. package/skills/qc/qa-analyst/business-rules.md +0 -59
  189. package/skills/qc/qa-analyst/data-flow.md +0 -64
  190. package/skills/qc/qa-analyst/spec-breakdown.md +0 -61
  191. package/skills/qc/qa-designer/e2e/journey.md +0 -41
  192. package/skills/qc/qa-designer/exploratory/charter.md +0 -68
  193. package/skills/qc/qa-designer/exploratory/explore-to-functional.md +0 -43
  194. package/skills/qc/qa-designer/functional/api.md +0 -45
  195. package/skills/qc/qa-designer/functional/gui-feature.md +0 -46
  196. package/skills/qc/qa-designer/functional/gui-screen.md +0 -52
  197. package/skills/qc/qa-designer/integration/api.md +0 -42
  198. package/skills/qc/qa-designer/integration/db.md +0 -39
  199. package/skills/qc/qa-designer/integration/gui.md +0 -40
  200. package/skills/qc/qa-designer/integration/kafka.md +0 -40
  201. package/skills/qc/qa-designer/non-functional.md +0 -40
  202. package/skills/qc/qa-planner/test-plan.md +0 -120
  203. package/skills/qc/qa-reviewer/script/e2e.md +0 -87
  204. package/skills/qc/qa-reviewer/script/exploratory.md +0 -45
  205. package/skills/qc/qa-reviewer/script/functional.md +0 -101
  206. package/skills/qc/qa-reviewer/script/integration.md +0 -91
  207. package/skills/qc/qa-reviewer/script/non-functional.md +0 -126
  208. package/skills/qc/qa-reviewer/test-case/e2e.md +0 -73
  209. package/skills/qc/qa-reviewer/test-case/exploratory.md +0 -43
  210. package/skills/qc/qa-reviewer/test-case/functional.md +0 -76
  211. package/skills/qc/qa-reviewer/test-case/integration.md +0 -69
  212. package/skills/qc/qa-reviewer/test-case/non-functional.md +0 -73
  213. package/skills/qc/qa-runner/e2e.md +0 -49
  214. package/skills/qc/qa-runner/exploratory/session.md +0 -36
  215. package/skills/qc/qa-runner/functional/api.md +0 -35
  216. package/skills/qc/qa-runner/functional/gui-feature.md +0 -51
  217. package/skills/qc/qa-runner/functional/gui-screen.md +0 -55
  218. package/skills/qc/qa-runner/integration.md +0 -47
  219. package/skills/qc/qa-runner/non-functional.md +0 -49
  220. package/skills/qc/qa-runner/report/report.md +0 -37
  221. package/skills/setup-ai-first/SKILL.md +0 -19
  222. package/skills/setup-ai-first/SKILL.tmpl +0 -19
  223. package/skills/spec/SKILL.md +0 -19
  224. package/skills/spec/SKILL.tmpl +0 -19
  225. package/skills/test/SKILL.md +0 -18
  226. package/skills/test/SKILL.tmpl +0 -18
  227. package/steps/business-language.md +0 -56
  228. package/steps/capture-lesson.md +0 -79
  229. package/steps/context-loader.md +0 -385
  230. package/steps/gate.md +0 -94
  231. package/steps/report-footer.md +0 -102
  232. package/steps/review-fanout.md +0 -159
  233. package/steps/spawn-agent.md +0 -129
  234. package/steps/trace-mirror.md +0 -53
  235. package/templates/README.md +0 -47
  236. package/templates/architecture.template.md +0 -394
  237. package/templates/design-spec.template.md +0 -217
  238. package/templates/feature.template +0 -123
  239. package/templates/platform-guide.template.md +0 -145
  240. package/templates/prd.template.md +0 -283
  241. package/templates/product-definition.template.md +0 -188
  242. package/templates/project-context.yaml +0 -212
  243. package/templates/tech-design.template.md +0 -490
@@ -1,90 +0,0 @@
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/{platform}/{UC-ID}-{slug}.feature"
89
- verifies: "// @trace.verifies={UC-ID}"
90
- test_type: "// @trace.test_type=unit|integration"
@@ -1,302 +0,0 @@
1
- # PHP Laravel — Service-Repository Architecture Patterns
2
-
3
- ## Form Request (Validation Layer)
4
-
5
- ```php
6
- <?php
7
- // app/Http/Requests/Order/CreateOrderRequest.php
8
-
9
- namespace App\Http\Requests\Order;
10
-
11
- use Illuminate\Foundation\Http\FormRequest;
12
-
13
- class CreateOrderRequest extends FormRequest
14
- {
15
- public function authorize(): bool
16
- {
17
- return true; // auth handled by middleware
18
- }
19
-
20
- public function rules(): array
21
- {
22
- return [
23
- 'customer_id' => ['required', 'integer', 'exists:customers,id'],
24
- 'items' => ['required', 'array', 'min:1'],
25
- 'items.*.product_id' => ['required', 'integer', 'exists:products,id'],
26
- 'items.*.quantity' => ['required', 'integer', 'min:1'],
27
- ];
28
- }
29
-
30
- public function messages(): array
31
- {
32
- return [
33
- 'items.required' => 'At least one order item is required.',
34
- ];
35
- }
36
- }
37
- ```
38
-
39
- ## Repository Interface + Eloquent Implementation
40
-
41
- ```php
42
- <?php
43
- // app/Repositories/OrderRepositoryInterface.php
44
-
45
- namespace App\Repositories;
46
-
47
- use App\Models\Order;
48
- use Illuminate\Contracts\Pagination\LengthAwarePaginator;
49
-
50
- interface OrderRepositoryInterface
51
- {
52
- public function findById(int $id): ?Order;
53
- public function findByCustomer(int $customerId, int $perPage = 15): LengthAwarePaginator;
54
- public function create(array $data): Order;
55
- public function updateStatus(int $id, string $status): bool;
56
- }
57
- ```
58
-
59
- ```php
60
- <?php
61
- // app/Repositories/Eloquent/OrderRepository.php
62
-
63
- namespace App\Repositories\Eloquent;
64
-
65
- use App\Models\Order;
66
- use App\Repositories\OrderRepositoryInterface;
67
- use Illuminate\Contracts\Pagination\LengthAwarePaginator;
68
-
69
- class OrderRepository implements OrderRepositoryInterface
70
- {
71
- public function findById(int $id): ?Order
72
- {
73
- return Order::with(['items.product', 'customer'])->find($id);
74
- }
75
-
76
- public function findByCustomer(int $customerId, int $perPage = 15): LengthAwarePaginator
77
- {
78
- return Order::where('customer_id', $customerId)
79
- ->with('items')
80
- ->latest()
81
- ->paginate($perPage);
82
- }
83
-
84
- public function create(array $data): Order
85
- {
86
- return Order::create($data);
87
- }
88
-
89
- public function updateStatus(int $id, string $status): bool
90
- {
91
- return Order::where('id', $id)->update(['status' => $status]) > 0;
92
- }
93
- }
94
- ```
95
-
96
- ## Service Layer
97
-
98
- ```php
99
- <?php
100
- // app/Services/Order/OrderService.php
101
-
102
- namespace App\Services\Order;
103
-
104
- use App\Exceptions\BusinessException;
105
- use App\Models\Order;
106
- use App\Repositories\OrderRepositoryInterface;
107
- use Illuminate\Support\Facades\DB;
108
-
109
- class OrderService
110
- {
111
- // @trace.implements=ORD-UC1-SC1
112
- // @trace.source=specs/order/order-management/bdd/ORD-UC1.feature
113
- public function __construct(
114
- private readonly OrderRepositoryInterface $orderRepository
115
- ) {}
116
-
117
- public function createOrder(int $customerId, array $items): Order
118
- {
119
- return DB::transaction(function () use ($customerId, $items) {
120
- $order = $this->orderRepository->create([
121
- 'customer_id' => $customerId,
122
- 'status' => 'pending',
123
- ]);
124
-
125
- foreach ($items as $item) {
126
- $order->items()->create([
127
- 'product_id' => $item['product_id'],
128
- 'quantity' => $item['quantity'],
129
- ]);
130
- }
131
-
132
- return $order->load('items.product');
133
- });
134
- }
135
-
136
- public function cancelOrder(int $orderId): void
137
- {
138
- $order = $this->orderRepository->findById($orderId);
139
-
140
- if (!$order) {
141
- throw new BusinessException("Order #{$orderId} not found.", 404);
142
- }
143
-
144
- if ($order->status !== 'pending') {
145
- throw new BusinessException("Only pending orders can be cancelled.", 422);
146
- }
147
-
148
- $this->orderRepository->updateStatus($orderId, 'cancelled');
149
- }
150
- }
151
- ```
152
-
153
- ## Resource Controller
154
-
155
- ```php
156
- <?php
157
- // app/Http/Controllers/Order/OrderController.php
158
-
159
- namespace App\Http\Controllers\Order;
160
-
161
- use App\Http\Controllers\Controller;
162
- use App\Http\Requests\Order\CreateOrderRequest;
163
- use App\Http\Resources\Order\OrderResource;
164
- use App\Services\Order\OrderService;
165
- use Illuminate\Http\JsonResponse;
166
- use Illuminate\Http\Request;
167
-
168
- class OrderController extends Controller
169
- {
170
- public function __construct(
171
- private readonly OrderService $orderService
172
- ) {}
173
-
174
- // @trace.implements=ORD-UC1-SC1
175
- // @trace.source=specs/order/order-management/bdd/ORD-UC1.feature
176
- public function store(CreateOrderRequest $request): JsonResponse
177
- {
178
- $order = $this->orderService->createOrder(
179
- customerId: $request->validated('customer_id'),
180
- items: $request->validated('items')
181
- );
182
-
183
- return response()->json([
184
- 'status' => 'success',
185
- 'message' => 'Order created successfully.',
186
- 'data' => new OrderResource($order),
187
- ], 201);
188
- }
189
-
190
- // @trace.implements=ORD-UC2-SC1
191
- // @trace.source=specs/order/order-management/bdd/ORD-UC2.feature
192
- public function destroy(int $id): JsonResponse
193
- {
194
- $this->orderService->cancelOrder($id);
195
-
196
- return response()->json([
197
- 'status' => 'success',
198
- 'message' => 'Order cancelled.',
199
- ]);
200
- }
201
- }
202
- ```
203
-
204
- ## API Resource (Response Transformer)
205
-
206
- ```php
207
- <?php
208
- // app/Http/Resources/Order/OrderResource.php
209
-
210
- namespace App\Http\Resources\Order;
211
-
212
- use Illuminate\Http\Request;
213
- use Illuminate\Http\Resources\Json\JsonResource;
214
-
215
- class OrderResource extends JsonResource
216
- {
217
- public function toArray(Request $request): array
218
- {
219
- return [
220
- 'id' => $this->id,
221
- 'status' => $this->status,
222
- 'customer_id' => $this->customer_id,
223
- 'items' => OrderItemResource::collection($this->whenLoaded('items')),
224
- 'created_at' => $this->created_at->toISOString(),
225
- ];
226
- }
227
- }
228
- ```
229
-
230
- ## Feature Test (HTTP Layer)
231
-
232
- ```php
233
- <?php
234
- // tests/Feature/Order/CreateOrderTest.php
235
-
236
- namespace Tests\Feature\Order;
237
-
238
- use App\Models\Customer;
239
- use App\Models\Product;
240
- use Illuminate\Foundation\Testing\RefreshDatabase;
241
- use Tests\TestCase;
242
-
243
- // @trace.verifies=ORD-UC1
244
- // @trace.test_type=feature
245
- class CreateOrderTest extends TestCase
246
- {
247
- use RefreshDatabase;
248
-
249
- /** @test */
250
- public function it_creates_order_successfully(): void
251
- {
252
- $customer = Customer::factory()->create();
253
- $product = Product::factory()->create(['stock' => 10]);
254
-
255
- $response = $this->actingAs($customer)
256
- ->postJson('/api/v1/orders', [
257
- 'customer_id' => $customer->id,
258
- 'items' => [
259
- ['product_id' => $product->id, 'quantity' => 2],
260
- ],
261
- ]);
262
-
263
- $response->assertStatus(201)
264
- ->assertJsonStructure(['status', 'data' => ['id', 'status', 'items']]);
265
-
266
- $this->assertDatabaseHas('orders', ['customer_id' => $customer->id, 'status' => 'pending']);
267
- }
268
-
269
- /** @test */
270
- public function it_returns_422_when_items_are_empty(): void
271
- {
272
- $customer = Customer::factory()->create();
273
-
274
- $response = $this->actingAs($customer)
275
- ->postJson('/api/v1/orders', ['customer_id' => $customer->id, 'items' => []]);
276
-
277
- $response->assertStatus(422)
278
- ->assertJsonValidationErrors(['items']);
279
- }
280
- }
281
- ```
282
-
283
- ## Service Provider Binding
284
-
285
- ```php
286
- <?php
287
- // app/Providers/RepositoryServiceProvider.php
288
-
289
- namespace App\Providers;
290
-
291
- use App\Repositories\Eloquent\OrderRepository;
292
- use App\Repositories\OrderRepositoryInterface;
293
- use Illuminate\Support\ServiceProvider;
294
-
295
- class RepositoryServiceProvider extends ServiceProvider
296
- {
297
- public function register(): void
298
- {
299
- $this->app->bind(OrderRepositoryInterface::class, OrderRepository::class);
300
- }
301
- }
302
- ```
@@ -1,15 +0,0 @@
1
- name: "PHP Laravel"
2
- version: "1.0.0"
3
- description: "Laravel 11 backend with service-repository pattern"
4
- language: "PHP"
5
- framework: "Laravel"
6
- stack_type: "backend"
7
- default_layer_order:
8
- - Form Request (validation)
9
- - Model / Eloquent
10
- - Repository interface
11
- - Repository impl
12
- - Service
13
- - Controller (Resource Controller)
14
- - Resource (API response transformer)
15
- test_framework: "PHPUnit + Pest"
@@ -1,56 +0,0 @@
1
- build:
2
- compile: "composer install"
3
- test: "php artisan test"
4
- run: "php artisan serve"
5
- migrate: "php artisan migrate"
6
- seed: "php artisan db:seed"
7
-
8
- architecture:
9
- style: "Service-Repository (Controller → Service → Repository → Model)"
10
- key_rules:
11
- - "Controllers must not contain business logic — delegate to Services"
12
- - "Services own business logic and transaction boundaries"
13
- - "Repositories abstract all Eloquent/DB queries"
14
- - "Form Requests handle all input validation (never validate in controller)"
15
- - "API Resources transform Model output (never return raw Model/array)"
16
- - "Use dependency injection via constructor, not Facades in classes"
17
-
18
- coding_standards:
19
- naming:
20
- controllers: "PascalCase + Controller suffix — Resource style (e.g., OrderController)"
21
- services: "PascalCase + Service suffix (e.g., OrderService)"
22
- repositories:
23
- interface: "PascalCase + RepositoryInterface (e.g., OrderRepositoryInterface)"
24
- impl: "PascalCase + Repository (e.g., EloquentOrderRepository)"
25
- models: "PascalCase singular (e.g., Order)"
26
- form_requests: "PascalCase + Request suffix (e.g., CreateOrderRequest)"
27
- resources: "PascalCase + Resource suffix (e.g., OrderResource)"
28
- files:
29
- controller: "app/Http/Controllers/{Domain}/{Name}Controller.php"
30
- service: "app/Services/{Domain}/{Name}Service.php"
31
- repository_interface: "app/Repositories/{Name}RepositoryInterface.php"
32
- repository_impl: "app/Repositories/Eloquent/{Name}Repository.php"
33
- model: "app/Models/{Name}.php"
34
- form_request: "app/Http/Requests/{Domain}/{Action}{Name}Request.php"
35
- resource: "app/Http/Resources/{Domain}/{Name}Resource.php"
36
- patterns:
37
- response_wrapper: "JsonResponse with status/data/message structure"
38
- pagination: "Laravel paginate() — returns LengthAwarePaginator"
39
- exception_base: "App\\Exceptions\\BusinessException"
40
- transactions: "DB::transaction(fn () => ...) in Service layer"
41
- events: "Laravel Events + Listeners for domain events"
42
-
43
- testing:
44
- unit: "PHPUnit or Pest"
45
- feature: "Laravel Feature Tests (HTTP layer with database)"
46
- patterns:
47
- - "Use RefreshDatabase or DatabaseTransactions trait"
48
- - "Fake external services with Http::fake() or mock()"
49
- - "Use actingAs(\$user) for auth context"
50
- - "Assert JSON structure with assertJson() / assertJsonStructure()"
51
-
52
- trace_tags:
53
- implements: "// @trace.implements={UC-ID}-SC{N}"
54
- source: "// @trace.source=specs/{domain}/{prd-slug}/bdd/{platform}/{UC-ID}-{slug}.feature"
55
- verifies: "// @trace.verifies={UC-ID}"
56
- test_type: "// @trace.test_type=unit|feature"
@@ -1,66 +0,0 @@
1
- # QC automation module — Python + pytest-playwright + Page Object
2
- # Used by the /qc-* commands (the official QC automation pipeline ported from the QC team).
3
- # This is the QC test-authoring/execution stack, independent of the dev implementation
4
- # module (java-spring, react, flutter, …). Selected via tech_stack.qc_module or per /qc-* run.
5
-
6
- build:
7
- test: "python3 -m pytest"
8
- e2e: "python3 -m pytest -m e2e"
9
- report: "python3 -m pytest --html=reports/<feature>/report.html --self-contained-html"
10
- show_trace: "python3 -m playwright show-trace <test-results/<nodeid>/trace.zip>"
11
-
12
- architecture:
13
- style: "Page Object Model over pytest-playwright — Markdown test-case first, Python second"
14
- key_rules:
15
- - "Markdown-first: never generate Python until a reviewed .Test.md exists for the feature"
16
- - "No Allure, no hand-written dashboard, no record_video — use Playwright Trace + pytest-html"
17
- - "No hard-coded URL/credential/timeout — read from Env.* and CONFIG[...]"
18
- - "No time.sleep() — use Playwright auto-wait / expect()"
19
- - "Each test independent via pytest-playwright fixtures (page / logged_in_page / …)"
20
- - "Page Object extends slim BasePage; split 3 layers: locators _x(), actions verb_noun(), assertions assert_x() using expect()"
21
- - "Locator priority: data-testid → role → label/text → CSS → avoid XPath"
22
- - "test-id values come from the FE tech-design §2b Test Selectors contract ({UC-ID}-tech-design-{platform}.md) — prefer them (no runtime scan); fall back to role/text only when an actionable element has no test-id there, and note the gap"
23
- - "Group tests by (role, account) so login/logout never interleaves across roles"
24
- - "Cover 100% of TCs in the .Test.md — every TC ends Pass/Fail/Skip, none left Draft"
25
- folder_structure: |
26
- {paths.qc_dir}/{UC-ID}/test-cases/ ← test-case Markdown (.Test.md) — source of truth (mặc định docs/, lộ ra ngoài)
27
- pages/ ← Page Object Model
28
- │ ├── base_page.py ← slim BasePage (click/fill/wait/screenshot)
29
- │ └── <feature>_page.py
30
- tests/ ← pytest scripts, 1-1 with test-cases/
31
- │ ├── conftest.py ← fixtures: browser, page, logged_in_page, tracing
32
- │ └── <project>/test_<feature>.py
33
- utils/ ← config_loader, logger, steps, test_ordering, report helpers
34
- test_data/ ← JSON datasets
35
- config/config.yaml ← browser, timeout, video/screenshot/trace toggles
36
- reports/ test-results/ ← generated (gitignored): html report, trace.zip, screenshots
37
-
38
- coding_standards:
39
- naming:
40
- test_case_id: "TC_<FEATURE>_<NNN>"
41
- test_class: "TestFeatureHappyCase"
42
- test_function: "test_TC<NNN>_<snake_case>"
43
- page_object: "<feature>_page.py with <Feature>Page class extending BasePage"
44
- files:
45
- test_case_md: "{paths.qc_dir}/{UC-ID}/test-cases/TC_<FEATURE>.Test.md"
46
- page_object: "pages/<feature>_page.py"
47
- test_script: "tests/<project>/test_<feature>.py"
48
- patterns:
49
- steps: "wrap steps with `with step(\"…\")` (from utils.steps import step)"
50
- assertions: "Playwright expect() — never bare assert on dynamic UI"
51
- fixtures: "auth fixtures register via register_auth_fixtures([...]) in project conftest"
52
- fail_triage: "classify each FAIL as script-bug (fix selector/logic) vs product-gap (keep FAIL + evidence, never fake-pass)"
53
-
54
- testing:
55
- layers: "functional (gui-screen / gui-feature / api), integration (api/db/gui/kafka), e2e (journey), non-functional, exploratory"
56
- runner: "pytest-playwright; trace via context.tracing.start in conftest"
57
- report: "pytest-html (--html ... --self-contained-html) + Playwright Trace viewer"
58
-
59
- trace_tags:
60
- # QC tests map back to the framework's scenarios — drives qc_status in the trace TSV.
61
- verifies: "# @trace.verifies={UC-ID}-SC{N}"
62
- source: "# @trace.source=specs/{domain}/{prd-slug}/bdd/{platform}/{UC-ID}-{slug}.feature"
63
- test_type: "# @trace.test_type=functional|integration|e2e|non-functional"
64
-
65
- # qc_status: /qc-run-test writes pass|fail|skip|not_run + qc_run_at into {trace_dir}/{UC-ID}.tsv
66
- # (parallel to dev_selftest), surfaced in Living Docs as the OFFICIAL QC automation result.