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