@anhth2/spec-driven-dev-plugin 0.9.1 → 0.10.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.
- package/ARCHITECTURE.md +20 -9
- package/bin/index.js +1 -2
- package/commands/debug.md +13 -12
- package/commands/define-product.md +12 -11
- package/commands/{generate-tests.md → dev-gen-test.md} +48 -15
- package/commands/{generate-tests.tmpl → dev-gen-test.tmpl} +18 -4
- package/{core/commands/run-tests.md → commands/dev-run-test.md} +62 -13
- package/commands/{run-tests.tmpl → dev-run-test.tmpl} +32 -2
- package/{core/commands/smoke-test.md → commands/dev-smoke-test.md} +17 -16
- package/commands/{smoke-test.tmpl → dev-smoke-test.tmpl} +5 -5
- package/commands/fix-bug.md +13 -12
- package/commands/generate-bdd.md +39 -13
- package/commands/generate-bdd.tmpl +9 -2
- package/commands/generate-code.md +86 -15
- package/commands/generate-code.tmpl +56 -4
- package/commands/generate-design-spec.md +105 -39
- package/commands/generate-design-spec.tmpl +93 -28
- package/commands/generate-prd.md +12 -11
- package/commands/generate-spec-manifest.md +12 -11
- package/commands/generate-tech-docs.md +63 -22
- package/commands/generate-tech-docs.tmpl +51 -11
- package/commands/learn.md +13 -12
- package/commands/propose-scenario.md +13 -12
- package/commands/propose-scenario.tmpl +1 -1
- package/commands/refine-prd.md +166 -16
- package/commands/refine-prd.tmpl +16 -5
- package/commands/report-bug.md +12 -11
- package/commands/review-code.md +14 -13
- package/commands/review-code.tmpl +1 -1
- package/commands/review-context.md +161 -12
- package/commands/review-context.tmpl +11 -1
- package/commands/review-tech-docs.md +13 -11
- package/commands/review-tech-docs.tmpl +1 -0
- package/commands/setup-ai-first.md +7 -7
- package/commands/sync.md +23 -20
- package/commands/sync.tmpl +16 -13
- package/commands/update-framework.md +7 -7
- package/commands/validate-traces.md +57 -37
- package/commands/validate-traces.tmpl +45 -26
- package/core/FRAMEWORK_VERSION +1 -1
- package/core/commands/debug.md +13 -12
- package/core/commands/define-product.md +12 -11
- package/core/commands/{generate-tests.md → dev-gen-test.md} +48 -15
- package/{commands/run-tests.md → core/commands/dev-run-test.md} +62 -13
- package/{commands/smoke-test.md → core/commands/dev-smoke-test.md} +17 -16
- package/core/commands/fix-bug.md +13 -12
- package/core/commands/generate-bdd.md +39 -13
- package/core/commands/generate-code.md +86 -15
- package/core/commands/generate-design-spec.md +105 -39
- package/core/commands/generate-prd.md +12 -11
- package/core/commands/generate-spec-manifest.md +12 -11
- package/core/commands/generate-tech-docs.md +63 -22
- package/core/commands/learn.md +13 -12
- package/core/commands/propose-scenario.md +13 -12
- package/core/commands/refine-prd.md +166 -16
- package/core/commands/report-bug.md +12 -11
- package/core/commands/review-code.md +14 -13
- package/core/commands/review-context.md +161 -12
- package/core/commands/review-tech-docs.md +13 -11
- package/core/commands/setup-ai-first.md +7 -7
- package/core/commands/sync.md +23 -20
- package/core/commands/update-framework.md +7 -7
- package/core/commands/validate-traces.md +57 -37
- package/core/modules/android-compose/module.yaml +13 -0
- package/core/modules/android-compose/stack-profile.yaml +57 -0
- package/core/modules/flutter/module.yaml +14 -0
- package/core/modules/flutter/stack-profile.yaml +59 -0
- package/core/modules/ios-swiftui/module.yaml +13 -0
- package/core/modules/ios-swiftui/stack-profile.yaml +55 -0
- package/core/modules/nuxt/module.yaml +14 -0
- package/core/modules/nuxt/stack-profile.yaml +58 -0
- package/core/modules/react-native/module.yaml +14 -0
- package/core/modules/react-native/stack-profile.yaml +56 -0
- package/core/modules/vue/module.yaml +14 -0
- package/core/modules/vue/stack-profile.yaml +65 -0
- package/core/skills/code/SKILL.md +19 -18
- package/core/skills/debug/SKILL.md +27 -26
- package/core/skills/design-spec/SKILL.md +12 -11
- package/core/skills/discovery/SKILL.md +12 -11
- package/core/skills/prd/SKILL.md +14 -14
- package/core/skills/setup-ai-first/SKILL.md +7 -7
- package/core/skills/spec/SKILL.md +14 -14
- package/core/skills/test/SKILL.md +40 -38
- package/core/steps/capture-lesson.md +1 -1
- package/core/steps/context-loader.md +5 -4
- package/core/steps/report-footer.md +7 -7
- package/core/steps/review-fanout.md +138 -0
- package/core/steps/spawn-agent.md +1 -1
- package/core/steps/trace-mirror.md +18 -0
- package/core/templates/design-spec.template.md +16 -8
- package/core/templates/product-definition.template.md +3 -3
- package/core/templates/project-context.yaml +4 -1
- package/modules/android-compose/module.yaml +13 -0
- package/modules/android-compose/stack-profile.yaml +57 -0
- package/modules/flutter/module.yaml +14 -0
- package/modules/flutter/stack-profile.yaml +59 -0
- package/modules/ios-swiftui/module.yaml +13 -0
- package/modules/ios-swiftui/stack-profile.yaml +55 -0
- package/modules/nuxt/module.yaml +14 -0
- package/modules/nuxt/stack-profile.yaml +58 -0
- package/modules/react-native/module.yaml +14 -0
- package/modules/react-native/stack-profile.yaml +56 -0
- package/modules/vue/module.yaml +14 -0
- package/modules/vue/stack-profile.yaml +65 -0
- package/package.json +1 -1
- package/skills/code/SKILL.md +19 -18
- package/skills/debug/SKILL.md +27 -26
- package/skills/debug/SKILL.tmpl +1 -1
- package/skills/design-spec/SKILL.md +12 -11
- package/skills/discovery/SKILL.md +12 -11
- package/skills/prd/SKILL.md +14 -14
- package/skills/setup-ai-first/SKILL.md +7 -7
- package/skills/spec/SKILL.md +14 -14
- package/skills/test/SKILL.md +40 -38
- package/skills/test/SKILL.tmpl +9 -9
- package/steps/capture-lesson.md +1 -1
- package/steps/context-loader.md +5 -4
- package/steps/report-footer.md +7 -7
- package/steps/review-fanout.md +138 -0
- package/steps/spawn-agent.md +1 -1
- package/steps/trace-mirror.md +18 -0
- package/templates/design-spec.template.md +16 -8
- package/templates/product-definition.template.md +3 -3
- package/templates/project-context.yaml +4 -1
|
@@ -165,7 +165,7 @@ If `services` section is present:
|
|
|
165
165
|
|
|
166
166
|
**2. Route to service** — if active domain matches a key in `services`:
|
|
167
167
|
- Override `paths.specs_dir` → `services.{domain}.specs_dir`
|
|
168
|
-
- Override `paths.tech_docs_dir` → `services.{domain}.tech_docs_dir`
|
|
168
|
+
- Override `paths.tech_docs_dir` → `services.{domain}.tech_docs_dir` — **only if `setup.spec_source` is NOT set.** When `spec_source` IS set, the tech-design (API contract) is a cross-team artifact and must live in the shared spec repo (handled in step 4), so leave `tech_docs_dir` for step 4 to route — do NOT pin it per-service here.
|
|
169
169
|
- Store `active_service` = `services.{domain}.path`
|
|
170
170
|
- Store `active_service_module` = `services.{domain}.module`
|
|
171
171
|
- If service has its own `module` → use it as `active_module` (overrides `tech_stack.module`)
|
|
@@ -177,13 +177,14 @@ If `services` section is present:
|
|
|
177
177
|
**4. Spec source auto-override** — if `setup.spec_source` is set AND the corresponding path was not already explicitly set in `paths:`:
|
|
178
178
|
- Override `paths.prd_dir` → `{spec_source}/specs/prd`
|
|
179
179
|
- Override `paths.design_spec_dir` → `{spec_source}/specs/design-spec`
|
|
180
|
+
- Override `paths.tech_docs_dir` → `{spec_source}/specs/tech-docs` — **always when `spec_source` is set** (step 2 no longer pins tech-docs per-service in this case). The tech-design IS the cross-team API contract: BE authors it here, and FE/App read it from the same spec submodule at `/generate-code --phase=integration`. *(Per-service tech-docs only happen when there is no `spec_source` — a pure multi-service BE repo with no shared spec module.)*
|
|
180
181
|
- Override `paths.domain_knowledge_dir` → `{spec_source}/specs/domain-knowledge`
|
|
181
182
|
- Override `paths.business_dictionary` → `{spec_source}/specs/domain-knowledge/business-dictionary.md`
|
|
182
183
|
- Override `paths.core_entities` → `{spec_source}/specs/domain-knowledge/core-entities.md`
|
|
183
184
|
- Override `paths.bug_reports_dir` → `{spec_source}/feedback/bug-reports`
|
|
184
185
|
- Override `paths.bdd_proposals_dir` → `{spec_source}/feedback/bdd-proposals`
|
|
185
186
|
|
|
186
|
-
> **Why under `spec_source`:**
|
|
187
|
+
> **Why under `spec_source`:** PRD, design-spec, domain knowledge, the **API contract (tech-docs)**, and tester feedback are all **cross-team artifacts** — they must live in the **shared spec repo** so every umbrella (FE/App/BE) reads the same source via `/sync`. Tech-docs specifically: BE authors the tech-design (API contract), commits + pushes it into the spec submodule (2-layer commit), and FE/App pull it on their next `/sync` to wire the real API in `/generate-code --phase=integration`. In single-service mode (no `spec_source`), these default under the repo root — still shared, same repo.
|
|
187
188
|
|
|
188
189
|
---
|
|
189
190
|
|
|
@@ -207,7 +208,7 @@ When `active_service` has been resolved to a real path in Step 1.5 (e.g., `user-
|
|
|
207
208
|
| `paths.specs_dir` | `{active_service}/{service paths.specs_dir}` (if set in service config, else keep Step 1.5 override) |
|
|
208
209
|
|
|
209
210
|
**3. Store** `service_root = {active_service}` as the working directory anchor for all downstream commands:
|
|
210
|
-
- Shell commands (`/run-
|
|
211
|
+
- Shell commands (`/dev-run-test`, `/dev-gen-test`) run **from within** `service_root`
|
|
211
212
|
- File write operations (test files, trace TSVs) use paths **relative to** `service_root`
|
|
212
213
|
|
|
213
214
|
**4. If service config not found** — keep umbrella defaults, still set `service_root = {active_service}` (path anchor is always needed even without a config override).
|
|
@@ -300,7 +301,7 @@ active_module = tech_stack.module (e.g. "java-spring", "react", "flutter")
|
|
|
300
301
|
|
|
301
302
|
If `tech_stack.module` is blank or not recognized → set `platform_type = "unknown"` and flag as ⚠️ in the Step 7 recap.
|
|
302
303
|
|
|
303
|
-
These two variables (`active_module`, `platform_type`) are the canonical source for all branching logic in commands that need platform-specific behavior (
|
|
304
|
+
These two variables (`active_module`, `platform_type`) are the canonical source for all branching logic in commands that need platform-specific behavior (dev-gen-test, debug, fix-bug, dev-smoke-test).
|
|
304
305
|
|
|
305
306
|
---
|
|
306
307
|
|
|
@@ -375,8 +376,12 @@ Check whether `services` array exists in `project-context.yaml`.
|
|
|
375
376
|
- Use `services[N].trace_dir` if explicitly set
|
|
376
377
|
- Otherwise default to `{services[N].path}/.trace`
|
|
377
378
|
- Set `all_trace_dirs = [ dir1, dir2, ... ]` — one per service
|
|
378
|
-
- Set `umbrella_root_trace = ".trace"` (at umbrella workspace root)
|
|
379
379
|
- Step 1 will read TSVs from ALL dirs in `all_trace_dirs`, tagged by service name
|
|
380
|
+
- **Resolve the Living Docs home (canonical report location):**
|
|
381
|
+
- If `setup.spec_source` is set → `living_docs_dir = {spec_source}/.living-docs`
|
|
382
|
+
*(the shared specs module — mounted inside every service/umbrella workspace, so the panel resolves it no matter which submodule the dev is standing in)*
|
|
383
|
+
- Else (umbrella without a separate spec repo) → `living_docs_dir = .living-docs` at umbrella root
|
|
384
|
+
- **Resolve the panel mirror:** `panel_mirror = ./.trace` at the **current workspace root** (wherever this command runs). The VS Code panel reads `.trace/trace-report.json` from the open workspace — writing the merged report here is what makes the view non-empty when a dev opens a service submodule directly.
|
|
380
385
|
|
|
381
386
|
**If no `services` key (single-service mode):**
|
|
382
387
|
- Set `all_trace_dirs = [ {paths.trace_dir} ]`
|
|
@@ -439,6 +444,7 @@ If code was generated from an older revision → flag `TECHDOC_DRIFT`.
|
|
|
439
444
|
*Skip this step if no TSV files existed (handled by Step 1 no-TSV path).*
|
|
440
445
|
|
|
441
446
|
For each `.tsv` file processed: write updated `spec_ver`, `status`, `last_updated` back to disk.
|
|
447
|
+
Do **not** modify `dev_selftest` or `dev_selftest_at` — those are owned by `/dev-run-test`; this command only reads them for the report.
|
|
442
448
|
|
|
443
449
|
### Step 7 — Compute dashboard aggregates
|
|
444
450
|
|
|
@@ -454,6 +460,11 @@ test_coverage = rows where test_count > 0 / total_scs
|
|
|
454
460
|
drift_count = rows where status == DRIFT
|
|
455
461
|
untracked_count = rows where status == UNTRACKED
|
|
456
462
|
gap_count = rows where status == GAP
|
|
463
|
+
dev_selftest_passing = rows where dev_selftest == pass
|
|
464
|
+
dev_selftest_failing = rows where dev_selftest == fail
|
|
465
|
+
dev_selftest_not_run = rows where dev_selftest in (not_run, —)
|
|
466
|
+
# NOTE: this is the DEV self-check signal (did the dev run their own smoke tests),
|
|
467
|
+
# NOT official QC/dev-team coverage — keep it labeled as such on the dashboard.
|
|
457
468
|
tech_docs_count = count .md files in {paths.tech_docs_dir}/{domain}/
|
|
458
469
|
```
|
|
459
470
|
|
|
@@ -481,6 +492,9 @@ Schema:
|
|
|
481
492
|
"drift_count": 0,
|
|
482
493
|
"gap_count": 0,
|
|
483
494
|
"untracked_count": 0,
|
|
495
|
+
"dev_selftest_passing": 0,
|
|
496
|
+
"dev_selftest_failing": 0,
|
|
497
|
+
"dev_selftest_not_run": 0,
|
|
484
498
|
"tech_docs_count": 0
|
|
485
499
|
},
|
|
486
500
|
"prds": [
|
|
@@ -506,6 +520,8 @@ Schema:
|
|
|
506
520
|
"implemented_by": "<ClassName.method or null>",
|
|
507
521
|
"test_count": 0,
|
|
508
522
|
"test_classes": ["<TestClass1>", "<TestClass2>"],
|
|
523
|
+
"dev_selftest": "pass | fail | not_run",
|
|
524
|
+
"dev_selftest_at": "<YYYY-MM-DD or null>",
|
|
509
525
|
"prd_version": "<prd version when BDD was generated>",
|
|
510
526
|
"bdd_version": "<bdd version when code was generated>",
|
|
511
527
|
"tech_doc_revision": 0,
|
|
@@ -532,7 +548,7 @@ Schema:
|
|
|
532
548
|
"sc_id": "<SC-ID>",
|
|
533
549
|
"sc_title": "<title>",
|
|
534
550
|
"implemented_by": "<method>",
|
|
535
|
-
"fix": "/
|
|
551
|
+
"fix": "/dev-gen-test <UC-ID>"
|
|
536
552
|
}
|
|
537
553
|
],
|
|
538
554
|
"untracked": [
|
|
@@ -570,37 +586,40 @@ Schema:
|
|
|
570
586
|
- `tech_doc_revision`: use integer; `0` if not yet generated
|
|
571
587
|
- `code_coverage_pct` / `test_coverage_pct`: round to nearest integer (0–100)
|
|
572
588
|
- Always write to `{paths.trace_dir}/trace-report.json` regardless of domain filter — if a domain filter was applied, include only those PRDs in `prds[]` but note the domain in the `domain` field
|
|
573
|
-
- **TSV `"—"` mapping**: when reading TSV files, map dash values to JSON types: `implemented_by: "—"` → `null`; `test_count: "—"` → `0`; `test_classes: "—"` → `[]`; `tech_doc_revision: "—"` → `0`
|
|
589
|
+
- **TSV `"—"` mapping**: when reading TSV files, map dash values to JSON types: `implemented_by: "—"` → `null`; `test_count: "—"` → `0`; `test_classes: "—"` → `[]`; `tech_doc_revision: "—"` → `0`; `dev_selftest: "—"` → `"not_run"`; `dev_selftest_at: "—"` → `null`
|
|
574
590
|
|
|
575
|
-
### Step 8b —
|
|
591
|
+
### Step 8b — Living Docs Sync *(umbrella mode only)*
|
|
576
592
|
|
|
577
593
|
*Skip this step in single-service mode.*
|
|
578
594
|
|
|
579
|
-
|
|
595
|
+
Authoritative trace state stays in each service submodule's `.trace/` (committed there).
|
|
596
|
+
This step publishes the merged report to the **canonical Living Docs home** (the specs
|
|
597
|
+
module) and drops a local mirror so the panel is never empty.
|
|
580
598
|
|
|
581
|
-
1.
|
|
599
|
+
1. **Write canonical merged report** to `{living_docs_dir}/` (`mkdir -p` it first):
|
|
600
|
+
- `{living_docs_dir}/trace-report.json` — merge every per-service `trace-report.json`
|
|
601
|
+
into one document, add a `"service"` field per scenario row, recalc summary aggregates.
|
|
602
|
+
- `{living_docs_dir}/{service-name}/{UC-ID}.tsv` — copy each service's TSVs, namespaced
|
|
603
|
+
by service. Overwrite; don't delete TSVs whose service source is gone (prior runs).
|
|
582
604
|
|
|
583
|
-
2. **
|
|
584
|
-
|
|
585
|
-
{
|
|
586
|
-
|
|
587
|
-
Overwrite if exists. Do not delete files in `.trace/` that no longer have a service source — they may be from a previous run.
|
|
588
|
-
|
|
589
|
-
3. **Write aggregated `trace-report.json`** to `{umbrella_root_trace}/trace-report.json`:
|
|
590
|
-
- Merge all per-service `trace-report.json` data into one document
|
|
591
|
-
- Add `"service"` field to each scenario row
|
|
592
|
-
- Recalculate summary aggregates across all services
|
|
605
|
+
2. **Mirror to the panel location** `{panel_mirror}` (`./.trace` at the current workspace
|
|
606
|
+
root) so a dev who opened *this* repo (umbrella **or** a single service submodule) sees
|
|
607
|
+
data immediately: copy `{living_docs_dir}/trace-report.json` → `{panel_mirror}/trace-report.json`
|
|
608
|
+
(and the namespaced TSVs). If `panel_mirror` already resolves to `living_docs_dir`, skip.
|
|
593
609
|
|
|
594
|
-
|
|
610
|
+
3. **Print sync summary:**
|
|
595
611
|
```
|
|
596
|
-
|
|
597
|
-
user-service : {N} TSV files
|
|
598
|
-
order-service : {N} TSV files
|
|
612
|
+
Living Docs → {living_docs_dir}/ (canonical, in specs module)
|
|
613
|
+
user-service : {N} TSV files
|
|
614
|
+
order-service : {N} TSV files
|
|
599
615
|
trace-report.json: merged ({total} scenarios across {S} services)
|
|
600
|
-
|
|
616
|
+
Panel mirror → {panel_mirror}/trace-report.json (current workspace)
|
|
601
617
|
```
|
|
602
618
|
|
|
603
|
-
> **Note
|
|
619
|
+
> **Note — both locations are generated, gitignored mirrors.** Add `.living-docs/` to the
|
|
620
|
+
> **specs module's** `.gitignore` and `.trace/` to the current repo's `.gitignore`. The only
|
|
621
|
+
> committed, authoritative trace state is each service submodule's own `.trace/`. The report
|
|
622
|
+
> is regenerated by `/validate-traces` or `/sync` — never commit it.
|
|
604
623
|
|
|
605
624
|
## Output
|
|
606
625
|
|
|
@@ -642,13 +661,13 @@ Suggest the logical next command based on workflow phase:
|
|
|
642
661
|
| /review-context (BDD) | `/generate-tech-docs {UC-ID}` if APPROVED; regenerate if NEEDS_FIX |
|
|
643
662
|
| /generate-tech-docs | `/review-tech-docs {tech-design-file}` |
|
|
644
663
|
| /review-tech-docs | `/generate-code {feature-file}` if APPROVED; fix doc if NEEDS_FIX |
|
|
645
|
-
| /generate-code | First gen → `/review-code {UC-ID}`; re-gen → `/
|
|
646
|
-
| /
|
|
647
|
-
| /run-
|
|
648
|
-
| /run-
|
|
649
|
-
| /review-code | `/smoke-test {UC-ID}` or create PR |
|
|
650
|
-
| /smoke-test | Create PR and link to ticket |
|
|
651
|
-
| /validate-traces | DRIFT/UNTRACKED → `/generate-code {UC-ID}`; GAP → `/
|
|
664
|
+
| /generate-code | First gen → `/review-code {UC-ID}`; re-gen → `/dev-gen-test {UC-ID}` |
|
|
665
|
+
| /dev-gen-test | `/dev-run-test {UC-ID}` |
|
|
666
|
+
| /dev-run-test (passing) | `/review-code {UC-ID}` |
|
|
667
|
+
| /dev-run-test (failing) | `/fix-bug {ticket-id}` or `/debug {error}` |
|
|
668
|
+
| /review-code | `/dev-smoke-test {UC-ID}` or create PR |
|
|
669
|
+
| /dev-smoke-test | Create PR and link to ticket |
|
|
670
|
+
| /validate-traces | DRIFT/UNTRACKED → `/generate-code {UC-ID}`; GAP → `/dev-gen-test {UC-ID}`; all OK → create PR |
|
|
652
671
|
| /fix-bug | Create PR and link to ticket |
|
|
653
672
|
| /debug | `/fix-bug {ticket-id}` if fix needed |
|
|
654
673
|
| /report-bug | Send to dev (`/fix-bug {BUG-ID}`); if coverage gap → `/propose-scenario {UC-ID}` |
|
|
@@ -701,11 +720,12 @@ Tech-Doc Revision Drift:
|
|
|
701
720
|
|
|
702
721
|
Recommendations:
|
|
703
722
|
- /generate-code {UC-ID} for DRIFT and UNTRACKED scenarios
|
|
704
|
-
- /
|
|
723
|
+
- /dev-gen-test {UC-ID} for GAP (missing tests)
|
|
705
724
|
- /generate-bdd {prd-file} for PRD version drift
|
|
706
725
|
|
|
707
726
|
[Umbrella mode only]
|
|
708
|
-
Living Docs
|
|
709
|
-
|
|
710
|
-
|
|
727
|
+
Living Docs canonical → {living_docs_dir}/ (specs module — shared, gitignored)
|
|
728
|
+
Panel mirror → {panel_mirror}/trace-report.json (current workspace)
|
|
729
|
+
Tip: run /validate-traces (or /sync) after each codegen session to refresh the panel.
|
|
730
|
+
Both are generated mirrors — do not commit (.living-docs/ + .trace/ in .gitignore).
|
|
711
731
|
```
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
name: "Android (Jetpack Compose)"
|
|
2
|
+
version: "1.0.0"
|
|
3
|
+
description: "Native Android development with Jetpack Compose + Kotlin"
|
|
4
|
+
language: "Kotlin"
|
|
5
|
+
framework: "Jetpack Compose"
|
|
6
|
+
stack_type: "mobile"
|
|
7
|
+
default_layer_order:
|
|
8
|
+
- Domain models
|
|
9
|
+
- Repository (data layer)
|
|
10
|
+
- UseCase (domain logic)
|
|
11
|
+
- ViewModel (StateFlow<UiState>)
|
|
12
|
+
- Composable screens (presentation)
|
|
13
|
+
test_framework: "JUnit5 + MockK + Turbine"
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
build:
|
|
2
|
+
compile: "./gradlew assembleRelease"
|
|
3
|
+
test: "./gradlew test"
|
|
4
|
+
run: "./gradlew installDebug / Open in Android Studio → Run"
|
|
5
|
+
lint: "./gradlew lint"
|
|
6
|
+
|
|
7
|
+
architecture:
|
|
8
|
+
style: "MVVM + Clean Architecture (Composable → ViewModel → UseCase → Repository)"
|
|
9
|
+
key_rules:
|
|
10
|
+
- "Composables are pure UI functions — no business logic, no direct suspend calls"
|
|
11
|
+
- "ViewModels expose StateFlow<UiState>; Composables collect via collectAsStateWithLifecycle()"
|
|
12
|
+
- "UseCases are single-responsibility classes injected into ViewModels"
|
|
13
|
+
- "Repositories are interfaces; implementations live in the data layer"
|
|
14
|
+
- "Dependency injection via Hilt"
|
|
15
|
+
- "Never launch coroutines from a Composable directly — use LaunchedEffect or ViewModel"
|
|
16
|
+
folder_structure: |
|
|
17
|
+
src/main/java/{package}/
|
|
18
|
+
├── core/
|
|
19
|
+
│ ├── network/ ← Retrofit instance, interceptors
|
|
20
|
+
│ ├── theme/ ← MaterialTheme, Color, Type, Shape
|
|
21
|
+
│ └── utils/
|
|
22
|
+
├── shared/
|
|
23
|
+
│ └── components/ ← reusable Composables (AppButton, AppTextField...)
|
|
24
|
+
├── features/
|
|
25
|
+
│ └── {domain}/
|
|
26
|
+
│ ├── ui/ ← Composable screens + components
|
|
27
|
+
│ ├── viewmodel/ ← ViewModel + UiState
|
|
28
|
+
│ ├── domain/ ← UseCases, domain models
|
|
29
|
+
│ └── data/ ← Repository impl, data sources
|
|
30
|
+
|
|
31
|
+
coding_standards:
|
|
32
|
+
naming:
|
|
33
|
+
composables: "PascalCase (e.g., OrderListScreen, CreateOrderForm)"
|
|
34
|
+
viewmodels: "PascalCase + ViewModel suffix (e.g., OrderListViewModel)"
|
|
35
|
+
usecases: "PascalCase + UseCase suffix (e.g., CreateOrderUseCase)"
|
|
36
|
+
files:
|
|
37
|
+
screen: "{Feature}Screen.kt"
|
|
38
|
+
viewmodel: "{Feature}ViewModel.kt"
|
|
39
|
+
usecase: "{Feature}UseCase.kt"
|
|
40
|
+
patterns:
|
|
41
|
+
state_management: "StateFlow<UiState> in ViewModel"
|
|
42
|
+
navigation: "Jetpack Navigation Compose"
|
|
43
|
+
networking: "Retrofit + OkHttp"
|
|
44
|
+
serialization: "Kotlinx Serialization or Gson"
|
|
45
|
+
di: "Hilt"
|
|
46
|
+
async: "Kotlin Coroutines + Flow"
|
|
47
|
+
|
|
48
|
+
testing:
|
|
49
|
+
unit: "JUnit5 + MockK + Turbine (Flow testing)"
|
|
50
|
+
ui: "Compose UI Test (composeTestRule)"
|
|
51
|
+
patterns:
|
|
52
|
+
- "Test ViewModel with fake repositories and TestCoroutineDispatcher"
|
|
53
|
+
- "UI tests use semantics matchers — set Modifier.testTag() on Composables"
|
|
54
|
+
|
|
55
|
+
trace_tags:
|
|
56
|
+
implements: "// @trace.implements={UC-ID}-SC{N}"
|
|
57
|
+
source: "// @trace.source=specs/bdd/{domain}/{UC-ID}.feature"
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
name: "Flutter"
|
|
2
|
+
version: "1.0.0"
|
|
3
|
+
description: "Cross-platform mobile (iOS + Android) with Flutter/Dart"
|
|
4
|
+
language: "Dart"
|
|
5
|
+
framework: "Flutter"
|
|
6
|
+
stack_type: "mobile"
|
|
7
|
+
default_layer_order:
|
|
8
|
+
- Models / entities (domain)
|
|
9
|
+
- Repositories (data layer)
|
|
10
|
+
- Use cases (domain logic)
|
|
11
|
+
- BLoC / Cubit or Riverpod providers (state)
|
|
12
|
+
- Widgets (presentation)
|
|
13
|
+
- Pages / screens
|
|
14
|
+
test_framework: "flutter_test + mocktail"
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
build:
|
|
2
|
+
compile: "flutter build apk / flutter build ios"
|
|
3
|
+
test: "flutter test"
|
|
4
|
+
run: "flutter run"
|
|
5
|
+
lint: "flutter analyze"
|
|
6
|
+
|
|
7
|
+
architecture:
|
|
8
|
+
style: "Feature-based (presentation → domain → data)"
|
|
9
|
+
key_rules:
|
|
10
|
+
- "Widget tree is pure UI — business logic lives in BLoC/Cubit or Riverpod providers"
|
|
11
|
+
- "Repositories abstract data sources; services contain domain logic"
|
|
12
|
+
- "State management: BLoC (complex flows) or Riverpod (simple/medium)"
|
|
13
|
+
- "Never call API directly inside a Widget build() method"
|
|
14
|
+
- "Shared UI widgets in lib/shared/widgets/, feature widgets in lib/features/{name}/presentation/"
|
|
15
|
+
- "Use const constructors wherever possible for performance"
|
|
16
|
+
folder_structure: |
|
|
17
|
+
lib/
|
|
18
|
+
├── core/
|
|
19
|
+
│ ├── theme/ ← AppTheme, colors, text styles
|
|
20
|
+
│ ├── router/ ← GoRouter or auto_route
|
|
21
|
+
│ └── utils/
|
|
22
|
+
├── shared/
|
|
23
|
+
│ └── widgets/ ← reusable UI widgets (AppButton, AppInput...)
|
|
24
|
+
├── features/
|
|
25
|
+
│ └── {domain}/
|
|
26
|
+
│ ├── data/ ← repositories, data sources, models
|
|
27
|
+
│ ├── domain/ ← entities, use cases, repo interfaces
|
|
28
|
+
│ └── presentation/ ← pages, widgets, BLoC/Cubit
|
|
29
|
+
└── main.dart
|
|
30
|
+
|
|
31
|
+
coding_standards:
|
|
32
|
+
naming:
|
|
33
|
+
widgets: "PascalCase (e.g., OrderListPage, CreateOrderForm)"
|
|
34
|
+
blocs: "PascalCase + Bloc/Cubit suffix (e.g., OrderListCubit)"
|
|
35
|
+
repositories: "PascalCase + Repository suffix (e.g., OrderRepository)"
|
|
36
|
+
files:
|
|
37
|
+
page: "{feature}_page.dart"
|
|
38
|
+
widget: "{feature}_widget.dart"
|
|
39
|
+
cubit: "{feature}_cubit.dart"
|
|
40
|
+
repository: "{feature}_repository.dart"
|
|
41
|
+
model: "{feature}_model.dart"
|
|
42
|
+
patterns:
|
|
43
|
+
state_management: "BLoC / Cubit (flutter_bloc) or Riverpod"
|
|
44
|
+
navigation: "GoRouter or auto_route"
|
|
45
|
+
networking: "Dio with interceptors"
|
|
46
|
+
serialization: "json_serializable / freezed"
|
|
47
|
+
|
|
48
|
+
testing:
|
|
49
|
+
unit: "flutter_test + mocktail"
|
|
50
|
+
widget: "flutter_test WidgetTester"
|
|
51
|
+
integration: "integration_test package"
|
|
52
|
+
patterns:
|
|
53
|
+
- "Test BLoC/Cubit in isolation with fake repositories"
|
|
54
|
+
- "Widget tests pump the widget tree and find by key or semantics label"
|
|
55
|
+
- "Use goldenFileComparator for snapshot tests of complex widgets"
|
|
56
|
+
|
|
57
|
+
trace_tags:
|
|
58
|
+
implements: "// @trace.implements={UC-ID}-SC{N}"
|
|
59
|
+
source: "// @trace.source=specs/bdd/{domain}/{UC-ID}.feature"
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
name: "iOS (SwiftUI)"
|
|
2
|
+
version: "1.0.0"
|
|
3
|
+
description: "Native iOS development with SwiftUI + Swift"
|
|
4
|
+
language: "Swift"
|
|
5
|
+
framework: "SwiftUI"
|
|
6
|
+
stack_type: "mobile"
|
|
7
|
+
default_layer_order:
|
|
8
|
+
- Models / entities
|
|
9
|
+
- Repository (data sources)
|
|
10
|
+
- UseCase (business operations)
|
|
11
|
+
- ViewModel (ObservableObject state)
|
|
12
|
+
- View (SwiftUI presentation)
|
|
13
|
+
test_framework: "XCTest + Quick/Nimble"
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
build:
|
|
2
|
+
compile: "xcodebuild -scheme {AppName} -configuration Release"
|
|
3
|
+
test: "xcodebuild test -scheme {AppName}"
|
|
4
|
+
run: "Open in Xcode → Run (⌘R)"
|
|
5
|
+
lint: "swiftlint"
|
|
6
|
+
|
|
7
|
+
architecture:
|
|
8
|
+
style: "MVVM + Clean Architecture (View → ViewModel → UseCase → Repository)"
|
|
9
|
+
key_rules:
|
|
10
|
+
- "Views are pure SwiftUI — no business logic, no direct API calls"
|
|
11
|
+
- "ViewModels are ObservableObject classes; bind via @StateObject / @ObservedObject"
|
|
12
|
+
- "UseCases encapsulate single business operations"
|
|
13
|
+
- "Repositories abstract data sources (network, cache, local DB)"
|
|
14
|
+
- "Dependency injection via environment or constructor injection"
|
|
15
|
+
- "Use Combine or async/await for async flows — never DispatchQueue in ViewModel"
|
|
16
|
+
folder_structure: |
|
|
17
|
+
Sources/
|
|
18
|
+
├── Core/
|
|
19
|
+
│ ├── Network/ ← URLSession wrapper, interceptors
|
|
20
|
+
│ ├── Theme/ ← Color, Font, Spacing tokens
|
|
21
|
+
│ └── Utils/
|
|
22
|
+
├── Shared/
|
|
23
|
+
│ └── Components/ ← reusable SwiftUI views (AppButton, AppTextField...)
|
|
24
|
+
├── Features/
|
|
25
|
+
│ └── {Domain}/
|
|
26
|
+
│ ├── Views/
|
|
27
|
+
│ ├── ViewModels/
|
|
28
|
+
│ ├── UseCases/
|
|
29
|
+
│ └── Repository/
|
|
30
|
+
|
|
31
|
+
coding_standards:
|
|
32
|
+
naming:
|
|
33
|
+
views: "PascalCase + View suffix (e.g., OrderListView, CreateOrderView)"
|
|
34
|
+
viewmodels: "PascalCase + ViewModel suffix (e.g., OrderListViewModel)"
|
|
35
|
+
usecases: "PascalCase + UseCase suffix (e.g., CreateOrderUseCase)"
|
|
36
|
+
files:
|
|
37
|
+
view: "{Feature}View.swift"
|
|
38
|
+
viewmodel: "{Feature}ViewModel.swift"
|
|
39
|
+
usecase: "{Feature}UseCase.swift"
|
|
40
|
+
patterns:
|
|
41
|
+
async: "async/await + Combine"
|
|
42
|
+
navigation: "NavigationStack (iOS 16+)"
|
|
43
|
+
persistence: "Core Data or SwiftData"
|
|
44
|
+
networking: "URLSession or Alamofire"
|
|
45
|
+
|
|
46
|
+
testing:
|
|
47
|
+
unit: "XCTest + Quick/Nimble"
|
|
48
|
+
ui: "XCUITest"
|
|
49
|
+
patterns:
|
|
50
|
+
- "Test ViewModel by injecting mock repositories"
|
|
51
|
+
- "UI tests use accessibility identifiers — set .accessibilityIdentifier on Views"
|
|
52
|
+
|
|
53
|
+
trace_tags:
|
|
54
|
+
implements: "// @trace.implements={UC-ID}-SC{N}"
|
|
55
|
+
source: "// @trace.source=specs/bdd/{domain}/{UC-ID}.feature"
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
name: "Nuxt"
|
|
2
|
+
version: "1.0.0"
|
|
3
|
+
description: "Nuxt 3 with SSR/SSG, Composition API, Pinia, and useFetch"
|
|
4
|
+
language: "TypeScript"
|
|
5
|
+
framework: "Nuxt 3"
|
|
6
|
+
stack_type: "frontend"
|
|
7
|
+
default_layer_order:
|
|
8
|
+
- Types / interfaces
|
|
9
|
+
- Server API routes (server/api/)
|
|
10
|
+
- Composables (useFetch / useAsyncData)
|
|
11
|
+
- State store (Pinia)
|
|
12
|
+
- UI components (presentational)
|
|
13
|
+
- Page components (pages/)
|
|
14
|
+
test_framework: "Vitest + Vue Test Utils + @nuxt/test-utils"
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
build:
|
|
2
|
+
compile: "npm run build"
|
|
3
|
+
test: "npm run test"
|
|
4
|
+
run: "npm run dev"
|
|
5
|
+
lint: "npm run lint"
|
|
6
|
+
|
|
7
|
+
architecture:
|
|
8
|
+
style: "Pages + Composables + Server routes (SSR/SSG)"
|
|
9
|
+
key_rules:
|
|
10
|
+
- "Pages in pages/ are route-level containers — no business logic"
|
|
11
|
+
- "Business logic in composables/ using useAsyncData or useFetch"
|
|
12
|
+
- "Server-side logic in server/api/ routes (nitro)"
|
|
13
|
+
- "Client-only state in Pinia stores; server state via useAsyncData"
|
|
14
|
+
- "Shared UI primitives in components/ui/, auto-imported by Nuxt"
|
|
15
|
+
- "Use useRoute/useRouter instead of $route/$router in Composition API"
|
|
16
|
+
- "'use client' equivalent: wrap client-only code in onMounted or <ClientOnly>"
|
|
17
|
+
folder_structure: |
|
|
18
|
+
├── components/
|
|
19
|
+
│ └── ui/ ← auto-imported UI primitives (BaseButton, BaseInput...)
|
|
20
|
+
├── composables/ ← auto-imported composables (useOrderList...)
|
|
21
|
+
├── pages/ ← file-based routing
|
|
22
|
+
├── server/
|
|
23
|
+
│ └── api/ ← nitro server routes (GET /api/orders.ts)
|
|
24
|
+
├── stores/ ← Pinia stores (auto-imported)
|
|
25
|
+
├── plugins/ ← Nuxt plugins
|
|
26
|
+
└── utils/ ← auto-imported utilities
|
|
27
|
+
|
|
28
|
+
coding_standards:
|
|
29
|
+
naming:
|
|
30
|
+
components: "PascalCase (auto-imported, e.g., OrderList, BaseButton)"
|
|
31
|
+
composables: "camelCase with 'use' prefix (auto-imported, e.g., useOrderList)"
|
|
32
|
+
stores: "camelCase + Store suffix via defineStore()"
|
|
33
|
+
server_routes: "method.path.ts (e.g., get.orders.ts or [id].get.ts)"
|
|
34
|
+
files:
|
|
35
|
+
component: "{Feature}.vue"
|
|
36
|
+
composable: "use{Feature}.ts"
|
|
37
|
+
store: "{feature}.store.ts"
|
|
38
|
+
server_route: "{resource}.get.ts / {resource}.post.ts"
|
|
39
|
+
patterns:
|
|
40
|
+
data_fetching: "useAsyncData() for SSR data, useFetch() for client fetches"
|
|
41
|
+
global_state: "Pinia stores"
|
|
42
|
+
forms: "VeeValidate + Zod"
|
|
43
|
+
api_client: "$fetch (ofetch) — built-in to Nuxt, SSR-compatible"
|
|
44
|
+
ssr_hydration: "useState() for shared SSR/client state to avoid hydration mismatch"
|
|
45
|
+
|
|
46
|
+
testing:
|
|
47
|
+
unit: "Vitest + @nuxt/test-utils"
|
|
48
|
+
e2e: "Playwright via @nuxt/test-utils/e2e"
|
|
49
|
+
patterns:
|
|
50
|
+
- "Use mountSuspended() for components that need Nuxt context"
|
|
51
|
+
- "Use registerEndpoint() to mock server routes in tests"
|
|
52
|
+
- "Test pages with renderSuspended() from @nuxt/test-utils"
|
|
53
|
+
|
|
54
|
+
trace_tags:
|
|
55
|
+
implements: "// @trace.implements={UC-ID}-SC{N}"
|
|
56
|
+
source: "// @trace.source=specs/bdd/{domain}/{UC-ID}.feature"
|
|
57
|
+
verifies: "// @trace.verifies={UC-ID}"
|
|
58
|
+
test_type: "// @trace.test_type=unit|integration"
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
name: "React Native"
|
|
2
|
+
version: "1.0.0"
|
|
3
|
+
description: "Cross-platform mobile (iOS + Android) with React Native / Expo"
|
|
4
|
+
language: "TypeScript"
|
|
5
|
+
framework: "React Native"
|
|
6
|
+
stack_type: "mobile"
|
|
7
|
+
default_layer_order:
|
|
8
|
+
- Types / interfaces
|
|
9
|
+
- API client (React Query hooks)
|
|
10
|
+
- State store (Zustand)
|
|
11
|
+
- Custom hooks (business logic)
|
|
12
|
+
- UI components (presentational)
|
|
13
|
+
- Screen / feature components (container)
|
|
14
|
+
test_framework: "Jest + React Native Testing Library"
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
build:
|
|
2
|
+
compile: "npx expo export / npx react-native build-android"
|
|
3
|
+
test: "jest"
|
|
4
|
+
run: "npx expo start / npx react-native run-ios"
|
|
5
|
+
lint: "eslint . && tsc --noEmit"
|
|
6
|
+
|
|
7
|
+
architecture:
|
|
8
|
+
style: "Feature-based (screens → hooks → queries → store)"
|
|
9
|
+
key_rules:
|
|
10
|
+
- "Business logic lives in custom hooks, not in screen components"
|
|
11
|
+
- "Server state managed by React Query (TanStack Query)"
|
|
12
|
+
- "Navigation state managed by React Navigation"
|
|
13
|
+
- "Native modules wrapped in a service layer — never called directly in components"
|
|
14
|
+
- "Shared UI primitives in src/components/ui/, feature screens in src/features/{name}/"
|
|
15
|
+
folder_structure: |
|
|
16
|
+
src/
|
|
17
|
+
├── api/ ← axios instance, interceptors
|
|
18
|
+
├── components/
|
|
19
|
+
│ └── ui/ ← reusable primitives (AppButton, AppInput...)
|
|
20
|
+
├── features/
|
|
21
|
+
│ └── {domain}/
|
|
22
|
+
│ ├── components/
|
|
23
|
+
│ ├── hooks/
|
|
24
|
+
│ ├── queries/
|
|
25
|
+
│ └── screens/
|
|
26
|
+
├── navigation/ ← React Navigation stacks/tabs
|
|
27
|
+
├── store/ ← Zustand slices
|
|
28
|
+
└── lib/ ← utils, formatters, constants
|
|
29
|
+
|
|
30
|
+
coding_standards:
|
|
31
|
+
naming:
|
|
32
|
+
components: "PascalCase (e.g., OrderListScreen, CreateOrderForm)"
|
|
33
|
+
hooks: "camelCase with 'use' prefix (e.g., useOrderList)"
|
|
34
|
+
screens: "PascalCase + Screen suffix (e.g., OrderDetailScreen)"
|
|
35
|
+
files:
|
|
36
|
+
screen: "{Feature}Screen.tsx"
|
|
37
|
+
component: "{Feature}.tsx"
|
|
38
|
+
hook: "use{Feature}.ts"
|
|
39
|
+
patterns:
|
|
40
|
+
state_management: "Zustand + React Query"
|
|
41
|
+
navigation: "React Navigation v6 (Stack + Tab + Drawer)"
|
|
42
|
+
networking: "Axios"
|
|
43
|
+
forms: "React Hook Form + Zod"
|
|
44
|
+
storage: "MMKV or AsyncStorage"
|
|
45
|
+
|
|
46
|
+
testing:
|
|
47
|
+
unit: "Jest + React Native Testing Library"
|
|
48
|
+
e2e: "Detox or Maestro"
|
|
49
|
+
patterns:
|
|
50
|
+
- "Test by accessibility label or testID — never by style/className"
|
|
51
|
+
- "Mock native modules in jest setup"
|
|
52
|
+
- "Use renderWithProviders() with NavigationContainer + QueryClient"
|
|
53
|
+
|
|
54
|
+
trace_tags:
|
|
55
|
+
implements: "// @trace.implements={UC-ID}-SC{N}"
|
|
56
|
+
source: "// @trace.source=specs/bdd/{domain}/{UC-ID}.feature"
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
name: "Vue"
|
|
2
|
+
version: "1.0.0"
|
|
3
|
+
description: "Vue 3 SPA with Composition API, Pinia, and Vue Query"
|
|
4
|
+
language: "TypeScript"
|
|
5
|
+
framework: "Vue 3"
|
|
6
|
+
stack_type: "frontend"
|
|
7
|
+
default_layer_order:
|
|
8
|
+
- Types / interfaces
|
|
9
|
+
- API client (Vue Query / composables)
|
|
10
|
+
- State store (Pinia)
|
|
11
|
+
- Composables (business logic)
|
|
12
|
+
- UI components (presentational)
|
|
13
|
+
- Page / feature components (container)
|
|
14
|
+
test_framework: "Vitest + Vue Test Utils"
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
build:
|
|
2
|
+
compile: "npm run build"
|
|
3
|
+
test: "npm run test"
|
|
4
|
+
run: "npm run dev"
|
|
5
|
+
lint: "npm run lint"
|
|
6
|
+
|
|
7
|
+
architecture:
|
|
8
|
+
style: "Feature-based (components → composables → queries → store)"
|
|
9
|
+
key_rules:
|
|
10
|
+
- "Business logic lives in composables, not in component setup()"
|
|
11
|
+
- "Server state managed by Vue Query (@tanstack/vue-query)"
|
|
12
|
+
- "Client-only UI state managed by Pinia stores"
|
|
13
|
+
- "Components must be pure — no direct API calls inside <template> or setup()"
|
|
14
|
+
- "Shared UI primitives in components/ui/, feature logic in features/{name}/"
|
|
15
|
+
- "Never fetch data directly in a component — use a composable"
|
|
16
|
+
- "Props defined with defineProps<{}>(), emits with defineEmits<{}>()"
|
|
17
|
+
folder_structure: |
|
|
18
|
+
src/
|
|
19
|
+
├── api/ ← axios instance, interceptors
|
|
20
|
+
├── components/
|
|
21
|
+
│ └── ui/ ← reusable primitives (BaseButton, BaseInput...)
|
|
22
|
+
├── features/
|
|
23
|
+
│ └── {domain}/
|
|
24
|
+
│ ├── components/ ← feature-specific UI
|
|
25
|
+
│ ├── composables/ ← useOrderList, useCreateOrder...
|
|
26
|
+
│ ├── queries/ ← Vue Query definitions
|
|
27
|
+
│ ├── store/ ← Pinia slice (if needed)
|
|
28
|
+
│ └── types.ts
|
|
29
|
+
├── pages/ ← route-level page components (Vue Router)
|
|
30
|
+
├── router/ ← route definitions
|
|
31
|
+
└── lib/ ← utilities, formatters, constants
|
|
32
|
+
|
|
33
|
+
coding_standards:
|
|
34
|
+
naming:
|
|
35
|
+
components: "PascalCase (e.g., OrderList, CreateOrderModal)"
|
|
36
|
+
composables: "camelCase with 'use' prefix (e.g., useOrderList, useCreateOrder)"
|
|
37
|
+
stores: "camelCase + Store suffix (e.g., useCartStore) — defined with defineStore()"
|
|
38
|
+
query_keys: "SCREAMING_SNAKE_CASE string/array (e.g., ['ORDER_LIST', customerId])"
|
|
39
|
+
files:
|
|
40
|
+
component: "{Feature}.vue"
|
|
41
|
+
composable: "use{Feature}.ts"
|
|
42
|
+
query: "{feature}.queries.ts"
|
|
43
|
+
store: "{feature}.store.ts"
|
|
44
|
+
types: "{feature}.types.ts"
|
|
45
|
+
patterns:
|
|
46
|
+
data_fetching: "Vue Query (TanStack Query v5 for Vue)"
|
|
47
|
+
global_state: "Pinia stores"
|
|
48
|
+
forms: "VeeValidate + Zod"
|
|
49
|
+
api_client: "Axios with interceptors for auth + error handling"
|
|
50
|
+
reactivity: "ref() for primitives, reactive() for objects, computed() for derived state"
|
|
51
|
+
|
|
52
|
+
testing:
|
|
53
|
+
unit: "Vitest + Vue Test Utils"
|
|
54
|
+
e2e: "Playwright or Cypress"
|
|
55
|
+
patterns:
|
|
56
|
+
- "Test behavior, not implementation — query by role/label via @testing-library/vue"
|
|
57
|
+
- "Mock Vue Query with VueQueryPlugin wrapper in test setup"
|
|
58
|
+
- "Use MSW (Mock Service Worker) to mock API calls in tests"
|
|
59
|
+
- "mountWithProviders() helper wraps component with Pinia + VueQuery + Router"
|
|
60
|
+
|
|
61
|
+
trace_tags:
|
|
62
|
+
implements: "// @trace.implements={UC-ID}-SC{N}"
|
|
63
|
+
source: "// @trace.source=specs/bdd/{domain}/{UC-ID}.feature"
|
|
64
|
+
verifies: "// @trace.verifies={UC-ID}"
|
|
65
|
+
test_type: "// @trace.test_type=unit|integration"
|