@anhth2/spec-driven-dev-plugin 0.6.0 → 0.8.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 (86) hide show
  1. package/bin/index.js +285 -11
  2. package/commands/debug.md +233 -11
  3. package/commands/debug.tmpl +170 -6
  4. package/commands/define-product.md +68 -6
  5. package/commands/define-product.tmpl +5 -1
  6. package/commands/fix-bug.md +111 -11
  7. package/commands/fix-bug.tmpl +48 -6
  8. package/commands/generate-bdd.md +86 -9
  9. package/commands/generate-bdd.tmpl +23 -4
  10. package/commands/generate-code.md +146 -19
  11. package/commands/generate-code.tmpl +83 -14
  12. package/commands/generate-design-spec.md +754 -0
  13. package/commands/generate-design-spec.tmpl +399 -0
  14. package/commands/generate-prd.md +91 -7
  15. package/commands/generate-prd.tmpl +28 -2
  16. package/commands/generate-spec-manifest.md +519 -0
  17. package/commands/generate-spec-manifest.tmpl +164 -0
  18. package/commands/generate-tech-docs.md +122 -9
  19. package/commands/generate-tech-docs.tmpl +59 -4
  20. package/commands/generate-tests.md +491 -37
  21. package/commands/generate-tests.tmpl +428 -32
  22. package/commands/refine-prd.md +76 -8
  23. package/commands/refine-prd.tmpl +13 -3
  24. package/commands/review-code.md +94 -6
  25. package/commands/review-code.tmpl +31 -1
  26. package/commands/review-context.md +118 -12
  27. package/commands/review-context.tmpl +55 -7
  28. package/commands/review-tech-docs.md +76 -9
  29. package/commands/review-tech-docs.tmpl +13 -4
  30. package/commands/run-tests.md +196 -18
  31. package/commands/run-tests.tmpl +133 -13
  32. package/commands/setup-ai-first.md +192 -6
  33. package/commands/setup-ai-first.tmpl +136 -5
  34. package/commands/smoke-test.md +228 -22
  35. package/commands/smoke-test.tmpl +165 -17
  36. package/commands/validate-traces.md +77 -8
  37. package/commands/validate-traces.tmpl +14 -3
  38. package/core/FRAMEWORK_VERSION +1 -1
  39. package/core/commands/debug.md +233 -11
  40. package/core/commands/define-product.md +68 -6
  41. package/core/commands/fix-bug.md +111 -11
  42. package/core/commands/generate-bdd.md +86 -9
  43. package/core/commands/generate-code.md +146 -19
  44. package/core/commands/generate-design-spec.md +754 -0
  45. package/core/commands/generate-prd.md +91 -7
  46. package/core/commands/generate-spec-manifest.md +519 -0
  47. package/core/commands/generate-tech-docs.md +122 -9
  48. package/core/commands/generate-tests.md +491 -37
  49. package/core/commands/refine-prd.md +76 -8
  50. package/core/commands/review-code.md +94 -6
  51. package/core/commands/review-context.md +118 -12
  52. package/core/commands/review-tech-docs.md +76 -9
  53. package/core/commands/run-tests.md +196 -18
  54. package/core/commands/setup-ai-first.md +192 -6
  55. package/core/commands/smoke-test.md +228 -22
  56. package/core/commands/validate-traces.md +77 -8
  57. package/core/skills/code/SKILL.md +68 -8
  58. package/core/skills/debug/SKILL.md +72 -10
  59. package/core/skills/design-spec/SKILL.md +450 -0
  60. package/core/skills/discovery/SKILL.md +62 -4
  61. package/core/skills/prd/SKILL.md +12 -8
  62. package/core/skills/setup-ai-first/SKILL.md +5 -3
  63. package/core/skills/spec/SKILL.md +11 -7
  64. package/core/skills/test/SKILL.md +130 -12
  65. package/core/steps/context-loader.md +57 -1
  66. package/core/steps/gate.md +1 -1
  67. package/core/steps/report-footer.md +5 -3
  68. package/core/steps/spawn-agent.md +3 -1
  69. package/core/templates/design-spec.template.md +209 -0
  70. package/core/templates/project-context.yaml +29 -0
  71. package/package.json +1 -1
  72. package/skills/code/SKILL.md +68 -8
  73. package/skills/debug/SKILL.md +72 -10
  74. package/skills/design-spec/SKILL.md +450 -0
  75. package/skills/design-spec/SKILL.tmpl +95 -0
  76. package/skills/discovery/SKILL.md +62 -4
  77. package/skills/prd/SKILL.md +12 -8
  78. package/skills/setup-ai-first/SKILL.md +5 -3
  79. package/skills/spec/SKILL.md +11 -7
  80. package/skills/test/SKILL.md +130 -12
  81. package/steps/context-loader.md +57 -1
  82. package/steps/gate.md +1 -1
  83. package/steps/report-footer.md +5 -3
  84. package/steps/spawn-agent.md +3 -1
  85. package/templates/design-spec.template.md +209 -0
  86. package/templates/project-context.yaml +29 -0
@@ -31,7 +31,7 @@ Display and wait for response:
31
31
  ```
32
32
  ⚙️ MODEL CHECK
33
33
  ──────────────────────────────────────────────────────────────────
34
- Recommended : claude-opus-4-5 (or claude-opus-4)
34
+ Recommended : claude-opus-4 (or latest Opus model)
35
35
  Why needed : Spec analysis, architecture review, code generation
36
36
  require deep reasoning. Smaller models miss edge cases.
37
37
 
@@ -84,7 +84,7 @@ Wait for explicit "Y" or "N" from the user before continuing.
84
84
  - "N" → stop and ask what the user wants to change.
85
85
 
86
86
 
87
- *Note: For this command, the target in Step 1 is a UC-ID or class path. Find files to test: controllers with `@trace.implements={UC-ID}`, service/facade implementations, and the `.feature` file at `{paths.specs_dir}/{domain}/{UC-ID}.feature`.*
87
+ *Note: For this command, the target in Step 1 is a UC-ID or `.feature` file path. Find the feature file at `{paths.specs_dir}/{domain}/{UC-ID}-*.feature` (glob match — filename includes slug suffix) and the implementation files tagged `@trace.implements={UC-ID}`.*
88
88
 
89
89
  ## Context
90
90
  # Context Loader — Load All Project Context
@@ -131,6 +131,7 @@ Read `.agent/project-context.yaml`. Extract and store:
131
131
  - `paths.core_entities` → path to core-entities.md
132
132
  - `paths.tech_docs_dir` → technical documentation root
133
133
  - `paths.trace_dir` → trace state directory
134
+ - `paths.design_spec_dir` → Design Spec documents root (FE/App only)
134
135
 
135
136
  If `paths` section is absent, use these defaults:
136
137
  - `specs_dir` = `specs/bdd`
@@ -140,13 +141,46 @@ If `paths` section is absent, use these defaults:
140
141
  - `domain_knowledge_dir` = `specs/domain-knowledge`
141
142
  - `business_dictionary` = `specs/domain-knowledge/business-dictionary.md`
142
143
  - `core_entities` = `specs/domain-knowledge/core-entities.md`
143
- - `tech_docs_dir` = `tech-docs`
144
+ - `tech_docs_dir` = `specs/tech-docs`
144
145
  - `trace_dir` = `.trace`
146
+ - `design_spec_dir` = `specs/design-spec`
145
147
 
146
148
  If `tech_stack.module` is set, also load `.agent/modules/{module}/stack-profile.yaml` if it exists.
147
149
 
148
150
  ---
149
151
 
152
+ ## Step 1.5 — [SERVICE ROUTING] Resolve service paths (umbrella mode)
153
+
154
+ *Skip this step entirely if `setup.mode` is not `"umbrella"` and `services` section is absent from project-context.yaml.*
155
+
156
+ If `services` section is present:
157
+
158
+ **1. Detect active domain** (in priority order):
159
+ - Read `@trace.domain` from target file frontmatter (if Gate loaded a target file)
160
+ - Extract from target file path: segment immediately after `prd_dir` base path
161
+ *(e.g., `specs/prd/user/FEAT-01.md` → domain = `user`)*
162
+ - If `$ARGUMENTS` contains a path, extract the segment after `prd_dir`
163
+
164
+ **2. Route to service** — if active domain matches a key in `services`:
165
+ - Override `paths.specs_dir` → `services.{domain}.specs_dir`
166
+ - Override `paths.tech_docs_dir` → `services.{domain}.tech_docs_dir`
167
+ - Store `active_service` = `services.{domain}.path`
168
+ - Store `active_service_module` = `services.{domain}.module`
169
+ - If service has its own `module` → use it as `active_module` (overrides `tech_stack.module`)
170
+
171
+ **3. Fallback** — if domain not detected or no matching service key:
172
+ - Keep default paths from Step 1
173
+ - Set `active_service = unresolved`
174
+
175
+ **4. Spec source auto-override** — if `setup.spec_source` is set AND the corresponding path was not already explicitly set in `paths:`:
176
+ - Override `paths.prd_dir` → `{spec_source}/specs/prd`
177
+ - Override `paths.design_spec_dir` → `{spec_source}/specs/design-spec`
178
+ - Override `paths.domain_knowledge_dir` → `{spec_source}/specs/domain-knowledge`
179
+ - Override `paths.business_dictionary` → `{spec_source}/specs/domain-knowledge/business-dictionary.md`
180
+ - Override `paths.core_entities` → `{spec_source}/specs/domain-knowledge/core-entities.md`
181
+
182
+ ---
183
+
150
184
  ## Step 2 — [PROJECT-CONFIG] Load module stack profile (conditional)
151
185
 
152
186
  If `tech_stack.module` is set, read `.agent/modules/{module}/stack-profile.yaml`.
@@ -217,6 +251,26 @@ If the file does not exist → skip silently.
217
251
 
218
252
  ---
219
253
 
254
+ ## Step 6.5 — [PLATFORM] Derive active_module and platform_type
255
+
256
+ Using `tech_stack.module` loaded in Step 1, derive and store two variables for use by all downstream commands:
257
+
258
+ ```
259
+ active_module = tech_stack.module (e.g. "java-spring", "react", "flutter")
260
+ ```
261
+
262
+ | `platform_type` | Modules |
263
+ |---|---|
264
+ | `backend` | `java-spring`, `golang`, `dotnet`, `php-laravel`, `context-engineering` |
265
+ | `web-frontend` | `react`, `nextjs`, `vue`, `nuxt`, `angular` |
266
+ | `mobile` | `flutter`, `react-native`, `ios-swiftui`, `android-compose` |
267
+
268
+ If `tech_stack.module` is blank or not recognized → set `platform_type = "unknown"` and flag as ⚠️ in the Step 7 recap.
269
+
270
+ These two variables (`active_module`, `platform_type`) are the canonical source for all branching logic in commands that need platform-specific behavior (generate-tests, debug, fix-bug, smoke-test).
271
+
272
+ ---
273
+
220
274
  ## Step 7 — [RECAP] Working Memory Recap (anti-lost-in-middle)
221
275
 
222
276
  After loading all context, synthesize and output a compact summary block.
@@ -227,10 +281,12 @@ Output exactly this block:
227
281
  ```
228
282
  [CTX LOADED]
229
283
  Stack : {language} / {framework} / {database}
284
+ Platform : {active_module} ({platform_type})
230
285
  Layers : {layer order from CLAUDE.md §2, e.g., Controller → Facade → Service → Repository}
231
286
  Ticket : {ticket_prefix}-
232
287
  Dict : {loaded — N canonical terms, M banned terms | missing}
233
288
  Entities : {loaded — EntityA, EntityB, EntityC | missing}
289
+ Service : {active_service} ({active_service_module}) | single-service
234
290
  Status : {FULL | PARTIAL — missing: CLAUDE.md / business-dict / core-entities | MINIMAL}
235
291
  ```
236
292
 
@@ -252,50 +308,446 @@ After completing all steps, you have loaded:
252
308
  Proceed to the next step of the calling command.
253
309
 
254
310
 
311
+ ---
312
+
313
+ ## Service Detection
314
+
315
+ Read `@trace.service` and `@trace.module` from the feature file header.
316
+
317
+ | Condition | Action |
318
+ |---|---|
319
+ | `@trace.module` present | Use as `active_module` |
320
+ | `@trace.module` absent | Use `tech_stack.module` from project-context.yaml |
321
+ | `@trace.service` present | Store as `active_service` |
322
+ | `@trace.service` absent | Use `{UC-ID}` domain as fallback |
323
+
324
+ **Platform type classification:**
325
+
326
+ | Platform | Modules |
327
+ |---|---|
328
+ | `backend` | `java-spring`, `golang`, `dotnet`, `php-laravel`, `context-engineering` |
329
+ | `web-frontend` | `react`, `nextjs`, `vue`, `nuxt`, `angular` |
330
+ | `mobile` | `flutter`, `react-native`, `ios-swiftui`, `android-compose` |
331
+
255
332
  ---
256
333
 
257
334
  ## CHECKPOINT — Test Plan
335
+
336
+ Before generating, scan the feature file for scenarios and the implementation files for classes/functions. Display:
337
+
258
338
  ```
259
- Test Plan — {UC-ID}:
260
- Unit Tests:
261
- - {Service}ImplTest: {method} {scenario} {expected}
262
- Integration Tests:
263
- - {Controller}Test: {endpoint} {scenario} HTTP {status}
264
- Total: {N} classes, ~{M} test cases. Proceed? (Y/N)
339
+ Test Plan — {UC-ID} ({active_module})
340
+ ──────────────────────────────────────
341
+ Platform : {backend | web-frontend | mobile}
342
+ Scenarios : {N} scenarios from .feature file
343
+ Impl files : {list of files tagged @trace.implements={UC-ID}}
344
+
345
+ Tests to generate:
346
+ {platform-specific list — see templates below}
347
+
348
+ Proceed? (Y/N)
265
349
  ```
266
350
 
351
+ Wait for explicit "Y" before generating.
352
+
353
+ ---
354
+
267
355
  ## Generate
268
356
 
269
- ### Unit Test Template
270
- ```
357
+ ### If `platform_type = backend`
358
+
359
+ #### java-spring
360
+
361
+ ```java
271
362
  // @trace.verifies={UC-ID}
363
+ // @trace.service={active_service}
272
364
  // @trace.test_type=unit
273
365
  class {Resource}ServiceImplTest {
274
- // Mock dependencies
275
- // Test: methodName_whenValid_shouldReturnExpected()
276
- // Given — set up mocks | When — call method | Then — assert
277
- // Test: methodName_whenNotFound_shouldThrowException()
278
- // Given — mock returns empty | When & Then — assert exception
366
+ @Mock {Repository} {repository};
367
+ @InjectMocks {Resource}ServiceImpl service;
368
+
369
+ // methodName_whenValid_shouldReturnExpected()
370
+ // Given — mock repository returns data
371
+ // When — call service method
372
+ // Then — assert result matches expected
373
+
374
+ // methodName_whenNotFound_shouldThrowException()
375
+ // Given — mock returns Optional.empty()
376
+ // When & Then — assertThrows({NotFoundException}.class, ...)
377
+ }
378
+
379
+ // @trace.verifies={UC-ID}
380
+ // @trace.service={active_service}
381
+ // @trace.test_type=integration
382
+ @WebMvcTest({Resource}Controller.class)
383
+ class {Resource}ControllerTest {
384
+ @MockBean {Facade | Service} facade;
385
+
386
+ // endpoint_shouldReturn200WhenValid()
387
+ // endpoint_shouldReturn400WhenInvalid()
388
+ // endpoint_shouldReturn404WhenNotFound()
389
+ // endpoint_shouldReturn401WhenUnauthenticated()
390
+ }
391
+ ```
392
+
393
+ Rules:
394
+ - Unit tests: mock at Repository layer, test Service logic
395
+ - Integration tests: mock at Facade/Service layer, test HTTP contract only
396
+ - Never mock the class under test
397
+ - Follow naming: `methodName_whenCondition_shouldOutcome` (from CLAUDE.md §6)
398
+
399
+ #### golang
400
+
401
+ ```go
402
+ // @trace.verifies={UC-ID}
403
+ // Unit: table-driven tests for service layer
404
+ func Test{Resource}Service_{Method}(t *testing.T) {
405
+ tests := []struct {
406
+ name string
407
+ input {InputType}
408
+ want {OutputType}
409
+ wantErr bool
410
+ }{
411
+ {"valid input", ..., ..., false},
412
+ {"not found", ..., nil, true},
413
+ }
414
+ for _, tt := range tests {
415
+ t.Run(tt.name, func(t *testing.T) { ... })
416
+ }
417
+ }
418
+
419
+ // @trace.verifies={UC-ID}
420
+ // Integration: HTTP handler tests using httptest
421
+ func Test{Resource}Handler_{Endpoint}(t *testing.T) {
422
+ // setup router, mock service, fire request
423
+ // assert status code and response body
424
+ }
425
+ ```
426
+
427
+ #### dotnet
428
+
429
+ ```csharp
430
+ // @trace.verifies={UC-ID}
431
+ // Unit: xUnit + Moq
432
+ public class {Resource}ServiceTests {
433
+ private readonly Mock<I{Repository}> _repoMock = new();
434
+ private readonly {Resource}Service _sut;
435
+
436
+ [Fact]
437
+ public async Task {Method}_WhenValid_Returns{Expected}() { }
438
+
439
+ [Fact]
440
+ public async Task {Method}_WhenNotFound_ThrowsNotFoundException() { }
441
+ }
442
+
443
+ // @trace.verifies={UC-ID}
444
+ // Integration: WebApplicationFactory
445
+ public class {Resource}ControllerTests : IClassFixture<WebApplicationFactory<Program>> {
446
+ [Fact]
447
+ public async Task {Endpoint}_Returns200_WhenValid() { }
448
+
449
+ [Fact]
450
+ public async Task {Endpoint}_Returns400_WhenInvalid() { }
451
+ }
452
+ ```
453
+
454
+ #### php-laravel
455
+
456
+ ```php
457
+ // @trace.verifies={UC-ID}
458
+ // Unit: PHPUnit
459
+ class {Resource}ServiceTest extends TestCase {
460
+ public function test_{method}_when_valid_should_return_expected(): void { }
461
+ public function test_{method}_when_not_found_should_throw(): void { }
462
+ }
463
+
464
+ // @trace.verifies={UC-ID}
465
+ // Feature: Laravel HTTP tests
466
+ class {Resource}ControllerTest extends TestCase {
467
+ use RefreshDatabase;
468
+ public function test_{endpoint}_returns_200_when_valid(): void {
469
+ $response = $this->getJson('/api/{resource}');
470
+ $response->assertStatus(200)->assertJsonStructure([...]);
471
+ }
279
472
  }
280
473
  ```
281
474
 
282
- ### Integration Test Template (HTTP layer)
475
+ #### context-engineering
476
+
477
+ Check `tech_stack.language` from project-context.yaml to select the correct test syntax:
478
+
479
+ **If language = Python** (default):
480
+
481
+ ```python
482
+ # @trace.verifies={UC-ID}
483
+ # @trace.test_type=unit
484
+ # Unit: test prompt orchestration functions
485
+
486
+ import pytest
487
+ from unittest.mock import patch, MagicMock
488
+
489
+ class Test{Resource}Prompt:
490
+ # test_{function}_when_valid_input_should_return_expected_output()
491
+ # Given — mock LLM client returns controlled response
492
+ # When — call prompt function with valid input
493
+ # Then — assert output matches expected structure/content
494
+
495
+ # test_{function}_when_llm_unavailable_should_raise()
496
+ # Given — mock LLM client raises connection error
497
+ # When & Then — assert specific exception is raised
498
+
499
+ # test_{function}_trace_assertions()
500
+ # Given — run function with trace capture enabled
501
+ # Then — assert @trace.implements tag present in function definition
502
+ # assert output conforms to expected schema
503
+
504
+ def test_{function}_when_valid_should_return_expected(self, mock_llm):
505
+ # Arrange
506
+ mock_llm.complete.return_value = "{expected response}"
507
+ # Act
508
+ result = {function}(input={test_input})
509
+ # Assert
510
+ assert result == {expected_output}
511
+ mock_llm.complete.assert_called_once_with(...)
512
+
513
+ def test_{function}_when_invalid_input_should_raise(self):
514
+ with pytest.raises({ExpectedError}):
515
+ {function}(input=None)
516
+ ```
517
+
518
+ **If language = TypeScript / JavaScript** (Node.js LangChain.js, etc.):
519
+
520
+ ```typescript
521
+ // @trace.verifies={UC-ID}
522
+ // @trace.test_type=unit
523
+ import { jest } from '@jest/globals'
524
+
525
+ describe('{Resource}Prompt', () => {
526
+ const mockLlm = { complete: jest.fn() }
527
+
528
+ it('{scenario description}', async () => {
529
+ mockLlm.complete.mockResolvedValue('{expected response}')
530
+ const result = await {function}({ input: '{test_input}', llm: mockLlm })
531
+ expect(result).toEqual({expected_output})
532
+ expect(mockLlm.complete).toHaveBeenCalledWith(expect.objectContaining({ ... }))
533
+ })
534
+
535
+ it('throws when input is invalid', async () => {
536
+ await expect({function}({ input: null, llm: mockLlm })).rejects.toThrow('{ExpectedError}')
537
+ })
538
+ })
539
+ ```
540
+
541
+ **If language = Java** (LangChain4j, etc.): use JUnit 5 + Mockito, same patterns as java-spring unit tests above — mock the `ChatLanguageModel` interface.
542
+
543
+ Rules:
544
+ - Mock the LLM client at the boundary — never call real LLM in unit tests
545
+ - Validate input schema and output schema separately
546
+ - Each scenario in `.feature` maps to one test function
547
+ - Use parameterized tests for multiple input variants
548
+
549
+ ---
550
+
551
+ ### If `platform_type = web-frontend`
552
+
553
+ #### react / nextjs / vue / nuxt / angular
554
+
555
+ ```typescript
556
+ // @trace.verifies={UC-ID}
557
+ // @trace.service={active_service}
558
+ // @trace.test_type=component
559
+
560
+ // Component tests (Vitest + Testing Library)
561
+ describe('{ComponentName}', () => {
562
+ it('renders correctly when data is loaded', () => {
563
+ render(<{ComponentName} {...props} />)
564
+ expect(screen.getByText('...')).toBeInTheDocument()
565
+ })
566
+
567
+ it('shows loading state while fetching', () => { })
568
+
569
+ it('shows error message on API failure', () => { })
570
+
571
+ it('calls handler when user interacts', async () => {
572
+ await userEvent.click(screen.getByRole('button', { name: '...' }))
573
+ expect(mockHandler).toHaveBeenCalledWith(...)
574
+ })
575
+ })
576
+
577
+ // @trace.verifies={UC-ID}
578
+ // @trace.test_type=e2e
579
+
580
+ // E2E tests (Playwright or Cypress — use whichever is in project)
581
+ test('{scenario from .feature}', async ({ page }) => {
582
+ await page.goto('/{route}')
583
+ await page.getByRole('button', { name: '...' }).click()
584
+ await expect(page.getByText('...')).toBeVisible()
585
+ })
283
586
  ```
587
+
588
+ Rules:
589
+ - One component test file per component involved in the UC
590
+ - One E2E test file per UC covering happy path + key error scenarios
591
+ - Mock API calls at the network layer (MSW or cy.intercept), not at component props
592
+ - Use accessible queries (`getByRole`, `getByLabelText`) — avoid `getByTestId` unless necessary
593
+ - Each test maps to exactly one scenario in the `.feature` file
594
+
595
+ ---
596
+
597
+ ### If `platform_type = mobile`
598
+
599
+ #### flutter
600
+
601
+ ```dart
602
+ // @trace.verifies={UC-ID}
603
+ // @trace.service={active_service}
604
+ // @trace.test_type=widget
605
+
606
+ // Widget tests
607
+ group('{FeatureName} widget tests', () {
608
+ testWidgets('renders correctly when state is loaded', (tester) async {
609
+ await tester.pumpWidget(MaterialApp(home: {Widget}()));
610
+ await tester.pumpAndSettle();
611
+ expect(find.text('...'), findsOneWidget);
612
+ });
613
+
614
+ testWidgets('shows loading indicator while fetching', (tester) async { });
615
+ testWidgets('shows error widget on failure', (tester) async { });
616
+ testWidgets('calls handler on tap', (tester) async {
617
+ await tester.tap(find.byType(ElevatedButton));
618
+ await tester.pumpAndSettle();
619
+ verify(() => mockBloc.add({Event}())).called(1);
620
+ });
621
+ });
622
+
284
623
  // @trace.verifies={UC-ID}
285
624
  // @trace.test_type=integration
286
- class {Resource}ControllerTest {
287
- // Mock Facade/Service
288
- // Test: filter_shouldReturn200()
289
- // Test: create_shouldReturn201()
290
- // Test: create_shouldReturn400WhenInvalid()
625
+ // Integration test (flutter_test / integration_test package)
626
+ void main() {
627
+ IntegrationTestWidgetsFlutterBinding.ensureInitialized();
628
+ testWidgets('{scenario from .feature}', (tester) async {
629
+ app.main();
630
+ await tester.pumpAndSettle();
631
+ // Navigate, interact, assert
632
+ });
633
+ }
634
+ ```
635
+
636
+ #### react-native
637
+
638
+ ```typescript
639
+ // @trace.verifies={UC-ID}
640
+ // @trace.service={active_service}
641
+ // @trace.test_type=component
642
+
643
+ // Jest + React Native Testing Library
644
+ describe('{ComponentName}', () => {
645
+ it('{scenario description}', () => {
646
+ const { getByText, getByRole } = render(<{ComponentName} {...props} />)
647
+ expect(getByText('...')).toBeTruthy()
648
+ })
649
+
650
+ it('calls navigation on button press', () => {
651
+ const mockNavigate = jest.fn()
652
+ const { getByRole } = render(<{ComponentName} navigation={{ navigate: mockNavigate }} />)
653
+ fireEvent.press(getByRole('button'))
654
+ expect(mockNavigate).toHaveBeenCalledWith('...')
655
+ })
656
+ })
657
+ ```
658
+
659
+ #### ios-swiftui
660
+
661
+ ```swift
662
+ // @trace.verifies={UC-ID}
663
+ // @trace.service={active_service}
664
+ // @trace.test_type=unit
665
+
666
+ // XCTest — ViewModel unit tests
667
+ @MainActor
668
+ final class {Feature}ViewModelTests: XCTestCase {
669
+ var sut: {Feature}ViewModel!
670
+ var mockRepo: Mock{Repository}!
671
+
672
+ override func setUp() async throws {
673
+ mockRepo = Mock{Repository}()
674
+ sut = {Feature}ViewModel(repository: mockRepo)
675
+ }
676
+
677
+ func test_{method}_whenValid_shouldUpdate{State}() async throws {
678
+ // Given
679
+ mockRepo.stub{Method}Result = {expected}
680
+ // When
681
+ await sut.{method}()
682
+ // Then
683
+ XCTAssertEqual(sut.{state}, {expected})
684
+ }
685
+
686
+ func test_{method}_whenError_shouldSetErrorState() async throws { }
687
+ }
688
+ ```
689
+
690
+ #### android-compose
691
+
692
+ ```kotlin
693
+ // @trace.verifies={UC-ID}
694
+ // @trace.service={active_service}
695
+ // @trace.test_type=unit
696
+
697
+ // Unit test — ViewModel
698
+ class {Feature}ViewModelTest {
699
+ @get:Rule val mainDispatcherRule = MainDispatcherRule()
700
+ private val mockRepo: {Repository} = mockk()
701
+ private lateinit var sut: {Feature}ViewModel
702
+
703
+ @Before fun setup() { sut = {Feature}ViewModel(mockRepo) }
704
+
705
+ @Test fun `{method} when valid should emit success state`() = runTest {
706
+ coEvery { mockRepo.{method}(any()) } returns Result.success({data})
707
+ sut.{method}({input})
708
+ assertEquals(UiState.Success({data}), sut.uiState.value)
709
+ }
710
+ }
711
+
712
+ // @trace.verifies={UC-ID}
713
+ // @trace.test_type=ui
714
+
715
+ // UI test — Compose
716
+ @HiltAndroidTest
717
+ class {Feature}ScreenTest {
718
+ @get:Rule(order = 0) val hiltRule = HiltAndroidRule(this)
719
+ @get:Rule(order = 1) val composeRule = createAndroidComposeRule<MainActivity>()
720
+
721
+ @Test fun {scenario}_displaysExpectedUi() {
722
+ composeRule.onNodeWithText("...").assertIsDisplayed()
723
+ composeRule.onNodeWithContentDescription("...").performClick()
724
+ composeRule.onNodeWithText("...").assertIsDisplayed()
725
+ }
291
726
  }
292
727
  ```
293
728
 
729
+ ---
730
+
294
731
  ## Checklist
295
- - [ ] `@trace.verifies` on every test class
296
- - [ ] Every .feature scenario has a test
297
- - [ ] Correct layer mocked (no repo mocks in controller tests)
298
- - [ ] Test names follow pattern from CLAUDE.md §6 (e.g., `methodName_whenCondition_shouldOutcome`)
732
+
733
+ **All platforms:**
734
+ - [ ] `@trace.verifies` on every test class / test group
735
+ - [ ] `@trace.service` on every test class / test group
736
+ - [ ] Every scenario in `.feature` file has ≥ 1 corresponding test
737
+ - [ ] Happy path covered
738
+ - [ ] Key error / edge case scenarios covered
739
+
740
+ **Backend only:**
741
+ - [ ] Correct layer mocked (Repository in unit tests, Facade/Service in controller tests)
742
+ - [ ] No real DB calls in unit tests
743
+ - [ ] Test naming follows `methodName_whenCondition_shouldOutcome` (CLAUDE.md §6)
744
+
745
+ **Frontend / Mobile only:**
746
+ - [ ] No hardcoded delays (`sleep`, `setTimeout`) in tests — use `waitFor` / `pumpAndSettle`
747
+ - [ ] Accessible queries used (role, label) not implementation-specific selectors
748
+ - [ ] API calls mocked at network layer, not at component level
749
+
750
+ ---
299
751
 
300
752
  ## Write Trace State
301
753
 
@@ -303,14 +755,14 @@ After generating all test files, update `{paths.trace_dir}/{UC-ID}.tsv` — for
303
755
 
304
756
  | Column | Value |
305
757
  |--------|-------|
306
- | `test_count` | number of test methods covering this SC across all generated test classes |
307
- | `test_classes` | comma-separated list of test class names (e.g., `OrderServiceImplTest,OrderControllerTest`) |
758
+ | `test_count` | number of test methods covering this SC |
759
+ | `test_classes` | comma-separated test class / describe-block names |
308
760
  | `last_updated` | today `YYYY-MM-DD` |
309
761
 
310
- Count `test_count` by scanning generated test files for methods that correspond to this SC (methods whose name or comment references the SC-ID, or all methods in a test class dedicated to this UC).
311
-
312
762
  Leave all other columns unchanged.
313
763
 
764
+ ---
765
+
314
766
  ## Output
315
767
 
316
768
  # Report Footer — Standard Command Output Format
@@ -341,21 +793,23 @@ Suggest the logical next command based on workflow phase:
341
793
 
342
794
  | Current command | Suggest next |
343
795
  |-------------------------|-----------------------------------------------|
796
+ | /setup-ai-first | `/define-product` to start your first feature |
344
797
  | /define-product | `/generate-prd {product-definition-file}` |
345
798
  | /generate-prd | `/refine-prd {prd-file}` then `/review-context {prd-file}` |
346
799
  | /refine-prd | Open Review Board → update PRD → `/review-context {prd-file}` |
347
- | /review-context (PRD) | `/generate-bdd {prd-file}` if APPROVED; fix PRD if NEEDS_FIX |
800
+ | /review-context (PRD) | FE/App: `/generate-design-spec {prd-file}` (then BDD after sign-off); BE: `/generate-bdd {prd-file}` directly; fix PRD if NEEDS_FIX |
801
+ | /generate-design-spec | Designer review → Figma links confirmed → PO + Designer sign-off → `/generate-bdd {prd-file}` |
348
802
  | /generate-bdd | `/review-context {feature-file}` to verify coverage |
349
803
  | /review-context (BDD) | `/generate-tech-docs {UC-ID}` if APPROVED; regenerate if NEEDS_FIX |
350
804
  | /generate-tech-docs | `/review-tech-docs {tech-design-file}` |
351
805
  | /review-tech-docs | `/generate-code {feature-file}` if APPROVED; fix doc if NEEDS_FIX |
352
- | /generate-code | `/generate-tests {UC-ID}` |
806
+ | /generate-code | First gen → `/review-code {UC-ID}`; re-gen → `/generate-tests {UC-ID}` |
353
807
  | /generate-tests | `/run-tests {UC-ID}` |
354
808
  | /run-tests (passing) | `/review-code {UC-ID}` |
355
809
  | /run-tests (failing) | `/fix-bug {ticket-id}` or `/debug {error}` |
356
810
  | /review-code | `/smoke-test {UC-ID}` or create PR |
357
811
  | /smoke-test | Create PR and link to ticket |
358
- | /validate-traces | `/generate-code {UC-ID}` for gaps |
812
+ | /validate-traces | DRIFT/UNTRACKED → `/generate-code {UC-ID}`; GAP → `/generate-tests {UC-ID}`; all OK → create PR |
359
813
  | /fix-bug | Create PR and link to ticket |
360
814
  | /debug | `/fix-bug {ticket-id}` if fix needed |
361
815
 
@@ -369,9 +823,9 @@ Next : {suggested command with example arguments}
369
823
 
370
824
 
371
825
  ```
372
- /generate-tests Complete — {UC-ID}
373
- ✅ {Service}Test ({M} tests)
374
- ✅ {Controller}Test ({M} tests)
375
- Trace: {paths.trace_dir}/{UC-ID}.tsv updated (test_count, test_classes)
826
+ /generate-tests Complete — {UC-ID} ({active_module})
827
+ ✅ {TestClass1} ({N} tests)
828
+ ✅ {TestClass2} ({N} tests)
829
+ Trace: {paths.trace_dir}/{UC-ID}.tsv updated
376
830
  Next: /run-tests {UC-ID}
377
831
  ```