aw-spec-cli 0.1.4 → 0.1.6

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 (225) hide show
  1. package/assets/.agents/agent.json +28 -0
  2. package/assets/.agents/skills/appweaver/SKILL.md +56 -0
  3. package/assets/.agents/skills/appweaver-demo/SKILL.md +59 -0
  4. package/assets/.claude/agent.json +27 -0
  5. package/assets/.claude/skills/appweaver/SKILL.md +55 -0
  6. package/assets/.claude/skills/appweaver-demo/SKILL.md +59 -0
  7. package/assets/.kilocode/agent.json +28 -0
  8. package/assets/.kilocode/workflows/appweaver-demo.md +59 -0
  9. package/assets/.kilocode/workflows/appweaver.md +55 -0
  10. package/dist/commands/init.d.ts.map +1 -1
  11. package/dist/commands/init.js +7 -54
  12. package/dist/commands/init.js.map +1 -1
  13. package/dist/index.js +0 -2
  14. package/dist/index.js.map +1 -1
  15. package/package.json +1 -1
  16. package/assets/.agents/skills/aw-spec-addcomponent/SKILL.md +0 -129
  17. package/assets/.agents/skills/aw-spec-agent-context-update/SKILL.md +0 -31
  18. package/assets/.agents/skills/aw-spec-analyze/SKILL.md +0 -296
  19. package/assets/.agents/skills/aw-spec-checklist/SKILL.md +0 -374
  20. package/assets/.agents/skills/aw-spec-clarify/SKILL.md +0 -320
  21. package/assets/.agents/skills/aw-spec-constitution/SKILL.md +0 -194
  22. package/assets/.agents/skills/aw-spec-fast-implement/SKILL.md +0 -268
  23. package/assets/.agents/skills/aw-spec-foundation/SKILL.md +0 -358
  24. package/assets/.agents/skills/aw-spec-implement/SKILL.md +0 -279
  25. package/assets/.agents/skills/aw-spec-minimal-implementation/SKILL.md +0 -209
  26. package/assets/.agents/skills/aw-spec-plan/SKILL.md +0 -221
  27. package/assets/.agents/skills/aw-spec-select-domain/SKILL.md +0 -73
  28. package/assets/.agents/skills/aw-spec-specify/SKILL.md +0 -382
  29. package/assets/.agents/skills/aw-spec-tasks/SKILL.md +0 -259
  30. package/assets/.agents/skills/aw-spec-taskstoissues/SKILL.md +0 -107
  31. package/assets/.aw-spec/domain.json +0 -3
  32. package/assets/.aw-spec/extensions/.registry +0 -19
  33. package/assets/.aw-spec/extensions/agent-context/README.md +0 -57
  34. package/assets/.aw-spec/extensions/agent-context/agent-context-config.yml +0 -4
  35. package/assets/.aw-spec/extensions/agent-context/commands/aw-spec.agent-context.update.md +0 -26
  36. package/assets/.aw-spec/extensions/agent-context/extension.yml +0 -34
  37. package/assets/.aw-spec/extensions/agent-context/scripts/bash/update-agent-context.sh +0 -201
  38. package/assets/.aw-spec/extensions/agent-context/scripts/powershell/update-agent-context.ps1 +0 -237
  39. package/assets/.aw-spec/extensions.yml +0 -23
  40. package/assets/.aw-spec/feature.json +0 -3
  41. package/assets/.aw-spec/init-options.json +0 -9
  42. package/assets/.aw-spec/integration.json +0 -15
  43. package/assets/.aw-spec/integrations/aw-spec.manifest.json +0 -17
  44. package/assets/.aw-spec/integrations/claude.manifest.json +0 -17
  45. package/assets/.aw-spec/memory/constitution.md +0 -50
  46. package/assets/.aw-spec/scripts/bash/check-prerequisites.sh +0 -189
  47. package/assets/.aw-spec/scripts/bash/common.sh +0 -582
  48. package/assets/.aw-spec/scripts/bash/create-new-feature.sh +0 -299
  49. package/assets/.aw-spec/scripts/bash/setup-plan.sh +0 -84
  50. package/assets/.aw-spec/scripts/bash/setup-tasks.sh +0 -91
  51. package/assets/.aw-spec/templates/checklist-template.md +0 -40
  52. package/assets/.aw-spec/templates/constitution-template.md +0 -57
  53. package/assets/.aw-spec/templates/plan-template.md +0 -113
  54. package/assets/.aw-spec/templates/spec-template.md +0 -131
  55. package/assets/.aw-spec/templates/tasks-template.md +0 -252
  56. package/assets/.aw-spec/workflows/aw-spec/workflow.yml +0 -77
  57. package/assets/.aw-spec/workflows/workflow-registry.json +0 -13
  58. package/assets/.claude/skills/aw-spec-addcomponent/SKILL.md +0 -129
  59. package/assets/.claude/skills/aw-spec-agent-context-update/SKILL.md +0 -31
  60. package/assets/.claude/skills/aw-spec-analyze/SKILL.md +0 -296
  61. package/assets/.claude/skills/aw-spec-checklist/SKILL.md +0 -374
  62. package/assets/.claude/skills/aw-spec-clarify/SKILL.md +0 -320
  63. package/assets/.claude/skills/aw-spec-constitution/SKILL.md +0 -194
  64. package/assets/.claude/skills/aw-spec-fast-implement/SKILL.md +0 -268
  65. package/assets/.claude/skills/aw-spec-foundation/SKILL.md +0 -358
  66. package/assets/.claude/skills/aw-spec-implement/SKILL.md +0 -279
  67. package/assets/.claude/skills/aw-spec-minimal-implementation/SKILL.md +0 -209
  68. package/assets/.claude/skills/aw-spec-plan/SKILL.md +0 -221
  69. package/assets/.claude/skills/aw-spec-select-domain/SKILL.md +0 -73
  70. package/assets/.claude/skills/aw-spec-specify/SKILL.md +0 -382
  71. package/assets/.claude/skills/aw-spec-tasks/SKILL.md +0 -259
  72. package/assets/.claude/skills/aw-spec-taskstoissues/SKILL.md +0 -107
  73. package/assets/.kilocode/rules/specify-rules.md +0 -4
  74. package/assets/.kilocode/workflows/aw-spec-addcomponent.md +0 -129
  75. package/assets/.kilocode/workflows/aw-spec-agent-context.update.md +0 -31
  76. package/assets/.kilocode/workflows/aw-spec-analyze.md +0 -296
  77. package/assets/.kilocode/workflows/aw-spec-checklist.md +0 -374
  78. package/assets/.kilocode/workflows/aw-spec-clarify.md +0 -320
  79. package/assets/.kilocode/workflows/aw-spec-constitution.md +0 -194
  80. package/assets/.kilocode/workflows/aw-spec-fast-implement.md +0 -263
  81. package/assets/.kilocode/workflows/aw-spec-foundation.md +0 -358
  82. package/assets/.kilocode/workflows/aw-spec-implement.md +0 -279
  83. package/assets/.kilocode/workflows/aw-spec-minimal-implementation.md +0 -204
  84. package/assets/.kilocode/workflows/aw-spec-plan.md +0 -221
  85. package/assets/.kilocode/workflows/aw-spec-select-domain.md +0 -73
  86. package/assets/.kilocode/workflows/aw-spec-specify.md +0 -382
  87. package/assets/.kilocode/workflows/aw-spec-tasks.md +0 -259
  88. package/assets/.kilocode/workflows/aw-spec-taskstoissues.md +0 -107
  89. package/assets/docs/ModularMonolith/ModularMonolith.Reference.pack.txt +0 -25248
  90. package/assets/docs/ModularMonolith/ModularMonolith.pack.txt +0 -5726
  91. package/assets/docs/ModularMonolith/pack-foundation.ps1 +0 -145
  92. package/assets/docs/ModularMonolith/pack-foundation.sh +0 -196
  93. package/assets/docs/ModularMonolith/pack-reference-foundation.ps1 +0 -157
  94. package/assets/docs/ModularMonolith/pack-reference-foundation.sh +0 -209
  95. package/assets/docs/ModularMonolith/unpack-foundation.ps1 +0 -126
  96. package/assets/docs/ModularMonolith/unpack-foundation.sh +0 -217
  97. package/assets/docs/ModularMonolith/unpack-reference-foundation.ps1 +0 -129
  98. package/assets/docs/ModularMonolith/unpack-reference-foundation.sh +0 -218
  99. package/assets/docs/README.md +0 -106
  100. package/assets/docs/UI.Visualization/PACKAGING.md +0 -191
  101. package/assets/docs/UI.Visualization/UI.Visualization.Reference.pack.txt +0 -11768
  102. package/assets/docs/UI.Visualization/UI.Visualization.pack.txt +0 -11214
  103. package/assets/docs/UI.Visualization/pack-foundation.ps1 +0 -165
  104. package/assets/docs/UI.Visualization/pack-foundation.sh +0 -206
  105. package/assets/docs/UI.Visualization/pack-reference-foundation.ps1 +0 -159
  106. package/assets/docs/UI.Visualization/pack-reference-foundation.sh +0 -205
  107. package/assets/docs/UI.Visualization/unpack-foundation.ps1 +0 -126
  108. package/assets/docs/UI.Visualization/unpack-foundation.sh +0 -217
  109. package/assets/docs/UI.Visualization/unpack-reference-foundation.ps1 +0 -129
  110. package/assets/docs/UI.Visualization/unpack-reference-foundation.sh +0 -218
  111. package/assets/docs/VerticalSlice/PACKAGING.md +0 -185
  112. package/assets/docs/VerticalSlice/VerticalSlice.Reference.pack.txt +0 -19318
  113. package/assets/docs/VerticalSlice/VerticalSlice.pack.txt +0 -2833
  114. package/assets/docs/VerticalSlice/pack-foundation.ps1 +0 -145
  115. package/assets/docs/VerticalSlice/pack-foundation.sh +0 -196
  116. package/assets/docs/VerticalSlice/pack-reference-foundation.ps1 +0 -157
  117. package/assets/docs/VerticalSlice/pack-reference-foundation.sh +0 -209
  118. package/assets/docs/VerticalSlice/unpack-foundation.ps1 +0 -126
  119. package/assets/docs/VerticalSlice/unpack-foundation.sh +0 -217
  120. package/assets/docs/VerticalSlice/unpack-reference-foundation.ps1 +0 -129
  121. package/assets/docs/VerticalSlice/unpack-reference-foundation.sh +0 -218
  122. package/assets/docs/architecture/clean-architecture.md +0 -71
  123. package/assets/docs/architecture/cqrs.md +0 -141
  124. package/assets/docs/architecture/eventing.md +0 -83
  125. package/assets/docs/architecture/folder-structure.md +0 -61
  126. package/assets/docs/architecture/modular-monolith.md +0 -47
  127. package/assets/docs/architecture/persistence.md +0 -269
  128. package/assets/docs/architecture/testing.md +0 -55
  129. package/assets/docs/architecture/vertical-slices.md +0 -201
  130. package/assets/docs/command.txt +0 -295
  131. package/assets/docs/components/Email/README.md +0 -119
  132. package/assets/docs/components/Email/ReferenceEndpoints/SendEmailEndpoint.cs +0 -84
  133. package/assets/docs/components/Email/integration.md +0 -300
  134. package/assets/docs/components/Jobs/README.md +0 -113
  135. package/assets/docs/components/Jobs/integration.md +0 -259
  136. package/assets/docs/components/QrGenerator/README.md +0 -85
  137. package/assets/docs/components/QrGenerator/ReferenceEndpoints/GenerateQrEndpoint.cs +0 -57
  138. package/assets/docs/components/QrGenerator/integration.md +0 -230
  139. package/assets/docs/components/README.md +0 -74
  140. package/assets/docs/domain/domain-driven-design.md +0 -212
  141. package/assets/docs/domainspecs/UIVisualization/chart-bubble.md +0 -70
  142. package/assets/docs/domainspecs/UIVisualization/chart-dimred.md +0 -58
  143. package/assets/docs/domainspecs/UIVisualization/chart-heatmap.md +0 -62
  144. package/assets/docs/domainspecs/UIVisualization/chart-network.md +0 -69
  145. package/assets/docs/domainspecs/UIVisualization/chart-parallel.md +0 -75
  146. package/assets/docs/domainspecs/UIVisualization/chart-scurve.md +0 -66
  147. package/assets/docs/domainspecs/UIVisualization/chart-survival.md +0 -69
  148. package/assets/docs/domainspecs/UIVisualization/chart-trellis.md +0 -68
  149. package/assets/docs/domainspecs/UIVisualization/chart-violin.md +0 -61
  150. package/assets/docs/domainspecs/UIVisualization/chart-volcano.md +0 -63
  151. package/assets/docs/domainspecs/UIVisualization/charting-standards.domain.md +0 -80
  152. package/assets/docs/domainspecs/UIVisualization/clean-architecture.domain.md +0 -87
  153. package/assets/docs/domainspecs/UIVisualization/coding-standards.domain.md +0 -47
  154. package/assets/docs/domainspecs/UIVisualization/comandrulemap.md +0 -130
  155. package/assets/docs/domainspecs/UIVisualization/component-contracts.domain.md +0 -67
  156. package/assets/docs/domainspecs/UIVisualization/data-source.domain.md +0 -68
  157. package/assets/docs/domainspecs/UIVisualization/domain-requirements.md +0 -110
  158. package/assets/docs/domainspecs/UIVisualization/folder-structure.domain.md +0 -55
  159. package/assets/docs/domainspecs/UIVisualization/state-management.domain.md +0 -67
  160. package/assets/docs/domainspecs/UIVisualization/testing.domain.md +0 -52
  161. package/assets/docs/domainspecs/UIVisualization/ui-viz-terms.md +0 -39
  162. package/assets/docs/domainspecs/healthcare/ai-rules.domain.md +0 -34
  163. package/assets/docs/domainspecs/healthcare/api-versioning.domain.md +0 -17
  164. package/assets/docs/domainspecs/healthcare/auditing.domain.md +0 -29
  165. package/assets/docs/domainspecs/healthcare/authorization.domain.md +0 -26
  166. package/assets/docs/domainspecs/healthcare/clinical-terms.md +0 -20
  167. package/assets/docs/domainspecs/healthcare/coding-standards.domain.md +0 -26
  168. package/assets/docs/domainspecs/healthcare/comandrulemap.md +0 -166
  169. package/assets/docs/domainspecs/healthcare/consent.md +0 -19
  170. package/assets/docs/domainspecs/healthcare/cqrs.domain.md +0 -12
  171. package/assets/docs/domainspecs/healthcare/data-classification.domain.md +0 -28
  172. package/assets/docs/domainspecs/healthcare/dependency-policy.domain.md +0 -11
  173. package/assets/docs/domainspecs/healthcare/dicom.md +0 -15
  174. package/assets/docs/domainspecs/healthcare/domain-driven-design.domain.md +0 -28
  175. package/assets/docs/domainspecs/healthcare/domain-requirements.md +0 -55
  176. package/assets/docs/domainspecs/healthcare/eventing.domain.md +0 -18
  177. package/assets/docs/domainspecs/healthcare/fhir.md +0 -36
  178. package/assets/docs/domainspecs/healthcare/folder-structure.domain.md +0 -23
  179. package/assets/docs/domainspecs/healthcare/hipaa.md +0 -34
  180. package/assets/docs/domainspecs/healthcare/hl7.md +0 -15
  181. package/assets/docs/domainspecs/healthcare/integration-terms.md +0 -21
  182. package/assets/docs/domainspecs/healthcare/persistence.domain.md +0 -20
  183. package/assets/docs/domainspecs/healthcare/terminology.md +0 -30
  184. package/assets/docs/domainspecs/healthcare/testing.domain.md +0 -15
  185. package/assets/docs/domainspecs/healthcare/vertical-slices.domain.md +0 -18
  186. package/assets/docs/domainspecs/healthcare/zero-trust.domain.md +0 -12
  187. package/assets/docs/domainspecs/pharma/ai-rules.domain.md +0 -32
  188. package/assets/docs/domainspecs/pharma/api-versioning.domain.md +0 -20
  189. package/assets/docs/domainspecs/pharma/atc.md +0 -22
  190. package/assets/docs/domainspecs/pharma/auditing.domain.md +0 -26
  191. package/assets/docs/domainspecs/pharma/authorization.domain.md +0 -25
  192. package/assets/docs/domainspecs/pharma/coding-standards.domain.md +0 -28
  193. package/assets/docs/domainspecs/pharma/comandrulemap.md +0 -186
  194. package/assets/docs/domainspecs/pharma/cqrs.domain.md +0 -16
  195. package/assets/docs/domainspecs/pharma/data-classification.domain.md +0 -24
  196. package/assets/docs/domainspecs/pharma/data-provenance.md +0 -21
  197. package/assets/docs/domainspecs/pharma/dependency-policy.domain.md +0 -11
  198. package/assets/docs/domainspecs/pharma/domain-driven-design.domain.md +0 -36
  199. package/assets/docs/domainspecs/pharma/domain-requirements.md +0 -99
  200. package/assets/docs/domainspecs/pharma/eventing.domain.md +0 -18
  201. package/assets/docs/domainspecs/pharma/fhir.md +0 -31
  202. package/assets/docs/domainspecs/pharma/folder-structure.domain.md +0 -24
  203. package/assets/docs/domainspecs/pharma/hl7.md +0 -17
  204. package/assets/docs/domainspecs/pharma/idmp.md +0 -26
  205. package/assets/docs/domainspecs/pharma/integration-terms.md +0 -21
  206. package/assets/docs/domainspecs/pharma/ndc.md +0 -28
  207. package/assets/docs/domainspecs/pharma/persistence.domain.md +0 -23
  208. package/assets/docs/domainspecs/pharma/pharma-terms.md +0 -27
  209. package/assets/docs/domainspecs/pharma/product-lifecycle.domain.md +0 -30
  210. package/assets/docs/domainspecs/pharma/regulatory-compliance.md +0 -19
  211. package/assets/docs/domainspecs/pharma/terminology.md +0 -32
  212. package/assets/docs/domainspecs/pharma/testing.domain.md +0 -19
  213. package/assets/docs/domainspecs/pharma/vertical-slices.domain.md +0 -17
  214. package/assets/docs/domainspecs/pharma/zero-trust.domain.md +0 -12
  215. package/assets/docs/glossary/ai-rules.md +0 -59
  216. package/assets/docs/governance/api-versioning.md +0 -99
  217. package/assets/docs/governance/coding-standards.md +0 -87
  218. package/assets/docs/governance/dependency-policy.md +0 -40
  219. package/assets/docs/interop/ihe-conformance.md +0 -31
  220. package/assets/docs/security/auditing.md +0 -117
  221. package/assets/docs/security/authorization-matrix.md +0 -14
  222. package/assets/docs/security/authorization.md +0 -87
  223. package/assets/docs/security/data-classification.md +0 -38
  224. package/assets/docs/security/zero-trust.md +0 -76
  225. package/assets/docs/technology-constitution.md +0 -172
@@ -1,269 +0,0 @@
1
- # Persistence Standards
2
-
3
- > Pure technology (domain-agnostic) — domain additions in [[persistence.domain]]. NON-NEGOTIABLE where marked. Commands: `/aw-spec-plan`, `/aw-spec-tasks`, `/aw-spec-implement`.
4
-
5
- ## EF Core conventions
6
-
7
- - The `DbContext` is a `ContextAwareDbContext<TContext>` and is **provider-aware**.
8
- - EF Core mapping lives **only** in `{Module}/Persistence/<Aggregate>Configuration.cs`,
9
- applied via `ApplyConfigurationsFromAssembly`.
10
- - `DateTimeOffset` columns **MUST** be re-mapped to `timestamptz` when the provider is Npgsql.
11
- - Cross-cutting behaviors are applied via `modelBuilder.ApplyCrossCuttingBehaviors(this)`:
12
- soft-delete global filters, tenant filters, base concurrency-token configuration.
13
-
14
- ## Repository rules
15
-
16
- - **Read handlers** inject `IReadRepository<TAggregate>`; **write handlers** inject
17
- `IRepository<TAggregate>` + `IUnitOfWork`.
18
- - No "repository as data-access god object" — **no custom methods** beyond
19
- `IRepository<T>` / `IReadRepository<T>` + `Specification<T>`.
20
- - Direct `DbContext.Set<T>()` LINQ inside Application Features is FORBIDDEN — use
21
- `IReadRepository<T>` + a `Specification<T>` only.
22
-
23
- ## Specification pattern
24
-
25
- - Every non-trivial query is encapsulated in a `Specification<TAggregate>` under
26
- `{Module}/Domain/Specifications/`.
27
- - Specifications **MUST be composable**; paging via `WithPaging(pageNumber, pageSize)`.
28
- - Ordering uses shared generic helpers (`OrderByIdAsc`, `OrderByIdDesc`, `OrderByNameAsc`);
29
- ad-hoc `OrderBy` in handlers is forbidden when a typed helper exists.
30
- - **Cross-aggregate joins via EF navigation properties are FORBIDDEN** — reference parent
31
- aggregates by strongly-typed ID and project client-side.
32
-
33
- ## Concurrency handling
34
-
35
- - Every mutable aggregate **MUST** implement `IConcurrencyTracked`.
36
- - Concurrency tokens are **client-managed**: `byte[] RowVersion`,
37
- `IsConcurrencyToken().ValueGeneratedNever()`, refreshed by a `SaveChanges` interceptor.
38
- - SQL Server `IsRowVersion()` semantics **MUST NOT** leak into entity configurations.
39
- - ETag / `If-Match` at the edge: see [[api-versioning]].
40
- - Server sets the ETag header on every create/read/update via `SetETagFromBase64(...)`.
41
- - Update/Delete endpoints extract `If-Match` via `GetIfMatchBase64Token()` and pass it
42
- to the command; mismatch returns `409 concurrency.conflict`.
43
- - `DbUpdateConcurrencyException` is translated to the same `409 concurrency.conflict`.
44
-
45
- ## Soft-delete rules
46
-
47
- - Soft delete is enabled via `EnableSoftDelete()`.
48
- - **Hard deletes are FORBIDDEN** for any aggregate flagged as retention-protected / regulated;
49
- deletion is logical via `MarkAsDeleted(...)`. (See [[persistence.domain]] for which data
50
- classes are retention-protected.)
51
- - Append-only aggregates have no delete path at all.
52
-
53
- ## Tenancy patterns
54
-
55
- - Tenancy is enabled via `EnableTenancy()` and `RegisterDefaultContexts(...)`.
56
- - Tenant resolution **MUST be transparent** to handlers.
57
- - Default model: **tenant-per-row** with tenant-aware global filters; deployments MAY opt
58
- into tenant-per-database via configuration.
59
-
60
- ## Migrations policy
61
-
62
- - Migrations are authored against **PostgreSQL** (production provider). Development MAY run
63
- on SQLite (non-sensitive development data only).
64
- - Multi-provider warnings (`PendingModelChangesWarning`) **MUST** be suppressed only at the
65
- runtime registration site, **never** in migration code.
66
- - Migrations **MUST** apply cleanly to a fresh PostgreSQL instance (CI gate).
67
-
68
- ## Provider profile
69
-
70
- - Production: PostgreSQL 16+ (encryption-at-rest profile in [[persistence.domain]]);
71
- per-module schemas where reasonable.
72
- - Development: PostgreSQL via Aspire containers; SQLite only for in-memory unit-test fixtures.
73
-
74
- ## Canonical example (agent reference — `<SampleProjectName>` sample)
75
-
76
- EF Core configuration (Vogen ID + value object conversions, unique business key,
77
- cross-aggregate FK stored as a raw `Guid` with no navigation):
78
-
79
- ```csharp
80
- // <SampleProjectName>.Service/Projects/Persistence/ProjectConfiguration.cs
81
- public class ProjectConfiguration : IEntityTypeConfiguration<Project>
82
- {
83
- public void Configure(EntityTypeBuilder<Project> builder)
84
- {
85
- builder.ToTable("Projects");
86
- builder.HasKey(e => e.Id);
87
-
88
- // Strongly-typed Vogen ID -> Guid, never DB-generated
89
- builder.Property(e => e.Id)
90
- .HasConversion(id => id.Value, v => ProjectId.From(v))
91
- .ValueGeneratedNever();
92
-
93
- // Value object -> column + unique business key
94
- builder.Property(e => e.Code)
95
- .HasConversion(c => c.Value, v => ProjectCode.From(v))
96
- .HasColumnName("Code").HasMaxLength(20).IsRequired();
97
- builder.HasIndex(e => e.Code).IsUnique();
98
-
99
- builder.Property(e => e.Status).HasConversion<string>().HasMaxLength(30).IsRequired();
100
-
101
- // Cross-aggregate FK as nullable Guid — NO EF navigation to TaskUser
102
- builder.Property(e => e.OwnerId)
103
- .HasConversion(
104
- id => id.HasValue ? id.Value.Value : (Guid?)null,
105
- v => v.HasValue ? TaskUserId.From(v.Value) : (TaskUserId?)null)
106
- .HasColumnName("OwnerId");
107
- }
108
- }
109
- ```
110
-
111
- Composable specification (filter + include + paging):
112
-
113
- ```csharp
114
- // {Module}/Domain/Specifications/ProjectSearchSpec.cs
115
- public sealed class ProjectSearchSpec : Specification<Project>
116
- {
117
- public ProjectSearchSpec(string? search, ProjectStatus? status = null)
118
- {
119
- Query.Include(p => p.Tasks);
120
- if (!string.IsNullOrWhiteSpace(search))
121
- {
122
- var s = search.Trim().ToLowerInvariant();
123
- Query.Where(p => p.Code.Value.ToLower().Contains(s) || p.Name.ToLower().Contains(s));
124
- }
125
- if (status.HasValue) Query.Where(p => p.Status == status.Value);
126
- }
127
-
128
- public ProjectSearchSpec WithPaging(int pageNumber, int pageSize)
129
- {
130
- Query.Skip(Math.Max(0, (pageNumber - 1) * pageSize)).Take(Math.Max(1, pageSize));
131
- return this;
132
- }
133
- }
134
- ```
135
-
136
- Read handler using the spec + typed ordering helper, returning `PagedResult<T>`:
137
-
138
- ```csharp
139
- var spec = new ProjectSearchSpec(request.Search, request.Status)
140
- .OrderByIdDesc<ProjectSearchSpec, Project, ProjectId>(); // typed helper, not ad-hoc OrderBy
141
-
142
- var total = await _repo.CountAsync(spec, ct);
143
- var items = await _repo.ListAsync(spec.WithPaging(request.PageNumber, request.PageSize), ct);
144
-
145
- return Result<PagedResult<ProjectDto>>.Ok(new PagedResult<ProjectDto>
146
- {
147
- Items = items.ToDtoList(), TotalCount = total,
148
- PageNumber = request.PageNumber, PageSize = request.PageSize
149
- });
150
- ```
151
-
152
- Owned child-entity configuration (separate table, FK + cascade, soft-delete mirror filter,
153
- no EF navigation to other aggregates):
154
-
155
- ```csharp
156
- // {Module}/Persistence/ProjectTaskConfiguration.cs
157
- public class ProjectTaskConfiguration : IEntityTypeConfiguration<ProjectTask>
158
- {
159
- public void Configure(EntityTypeBuilder<ProjectTask> builder)
160
- {
161
- builder.ToTable("ProjectTasks");
162
- builder.HasKey(e => e.Id);
163
- builder.Property(e => e.Id)
164
- .HasConversion(id => id.Value, v => ProjectTaskId.From(v)).ValueGeneratedNever();
165
-
166
- builder.Property(e => e.ProjectId) // FK back to the root
167
- .HasConversion(id => id.Value, v => ProjectId.From(v)).IsRequired();
168
-
169
- builder.Property(e => e.Title) // Vogen value object
170
- .HasConversion(t => t.Value, v => TaskTitle.From(v)).HasMaxLength(300).IsRequired();
171
- builder.Property(e => e.Status).HasConversion<string>().HasMaxLength(30).IsRequired();
172
-
173
- // Cross-aggregate ref stored as raw Guid — NO navigation to TaskUser
174
- builder.Property(e => e.AssigneeId)
175
- .HasConversion(
176
- id => id.HasValue ? id.Value.Value : (Guid?)null,
177
- v => v.HasValue ? TaskUserId.From(v.Value) : (TaskUserId?)null);
178
-
179
- builder.HasOne(e => e.Project).WithMany(p => p.Tasks) // owned collection
180
- .HasForeignKey(e => e.ProjectId).OnDelete(DeleteBehavior.Cascade);
181
-
182
- // Mirror the parent's ISoftDeletable filter onto the required dependent
183
- builder.HasQueryFilter(t => !t.Project.IsDeleted);
184
- builder.HasIndex(e => e.ProjectId);
185
- builder.HasIndex(e => e.AssigneeId);
186
- }
187
- }
188
- ```
189
-
190
- DbContext — `ContextAwareDbContext`, assembly-scanned configs, provider-aware `timestamptz`,
191
- cross-cutting behaviors, and the client-managed concurrency token override:
192
-
193
- ```csharp
194
- // <SampleProjectName>.Service/Infrastructure/ApplicationDbContext.cs
195
- public class ApplicationDbContext(
196
- DbContextOptions<ApplicationDbContext> options, ICurrentTenant? tenant = null)
197
- : ContextAwareDbContext<ApplicationDbContext>(options, tenant)
198
- {
199
- protected override void OnModelCreating(ModelBuilder modelBuilder)
200
- {
201
- modelBuilder.ApplyConfigurationsFromAssembly(typeof(ApplicationDbContext).Assembly);
202
-
203
- if (Database.ProviderName == "Npgsql.EntityFrameworkCore.PostgreSQL")
204
- foreach (var et in modelBuilder.Model.GetEntityTypes())
205
- foreach (var p in et.GetProperties()
206
- .Where(p => p.ClrType == typeof(DateTimeOffset) || p.ClrType == typeof(DateTimeOffset?)))
207
- p.SetColumnType("timestamptz");
208
-
209
- modelBuilder.ApplyCrossCuttingBehaviors(this); // soft-delete + tenant filters + audit
210
-
211
- // Client-managed concurrency token (works on SQLite + PostgreSQL; not SQL Server IsRowVersion)
212
- foreach (var et in modelBuilder.Model.GetEntityTypes())
213
- if (typeof(IConcurrencyTracked).IsAssignableFrom(et.ClrType))
214
- modelBuilder.Entity(et.ClrType).Property<byte[]>("RowVersion")
215
- .IsConcurrencyToken().ValueGeneratedNever();
216
-
217
- base.OnModelCreating(modelBuilder);
218
- }
219
-
220
- public DbSet<Project> Projects => Set<Project>();
221
- public DbSet<ProjectTask> ProjectTasks => Set<ProjectTask>();
222
- }
223
- ```
224
-
225
- Soft-delete: logical delete via `MarkAsDeleted(...)` + `DeleteAsync` (never a hard delete),
226
- and a separate `IgnoreQueryFilters()` spec for admin/audit reads:
227
-
228
- ```csharp
229
- // Delete handler — soft delete + concurrency guard (returns Result, no DTO)
230
- var entity = await _repo.GetByIdAsync(ProjectId.From(request.Id), ct);
231
- if (entity is null)
232
- return Result.Failure(new Error("project.not_found", $"Project {request.Id} not found", 404, "projects"));
233
- // ... concurrency token check (see api-versioning) ...
234
- entity.MarkAsDeleted(); // sets IsDeleted/DeletedOn + raises ProjectDeletedEvent
235
- await _repo.DeleteAsync(entity, ct); // framework translates to UPDATE, not DELETE
236
-
237
- // Admin/audit spec that bypasses the global soft-delete filter
238
- public sealed class ProjectSearchIncludeDeletedSpec : Specification<Project>
239
- {
240
- public ProjectSearchIncludeDeletedSpec(string? search, ProjectStatus? status = null)
241
- {
242
- Query.Include(p => p.Tasks).IgnoreQueryFilters(); // include soft-deleted rows
243
- // ... same Where filters as ProjectSearchSpec ...
244
- }
245
- }
246
- ```
247
-
248
- Composition — repository + cross-cutting behaviors registered once at startup:
249
-
250
- ```csharp
251
- // <SampleProjectName>.Service/Extensions/InfrastructureServiceRegistration.cs
252
- services.AddRepositories(builder =>
253
- {
254
- builder
255
- .UseSingleDbContextEFRepositories<ApplicationDbContext>(db => db.UseSqlite(/* or UseNpgsql */)
256
- .ConfigureWarnings(w => w.Ignore(RelationalEventId.PendingModelChangesWarning)))
257
- .EnableTenancy()
258
- .EnableSoftDelete()
259
- .EnableClientManagedConcurrency()
260
- .EnableAuditing().EnableAdvancedAuditing() // see [[auditing]]
261
- .Done();
262
- }, options => options.Provider = "EfCore");
263
-
264
- services.TryAddScoped(typeof(IUnitOfWork), typeof(EfUnitOfWork<ApplicationDbContext>));
265
- ```
266
-
267
- ## Related
268
-
269
- - [[clean-architecture]] · [[cqrs]] · [[domain-driven-design]] · [[api-versioning]] · [[persistence.domain]]
@@ -1,55 +0,0 @@
1
- # Testing Standards
2
-
3
- > Pure technology (domain-agnostic) — domain additions in [[testing.domain]]. Commands: `/aw-spec-tasks`, `/aw-spec-implement`.
4
-
5
- ## Test-first engineering
6
-
7
- - Generated tests follow Test-First Engineering and **MUST fail before** the production
8
- change is in place.
9
- - Any change to an aggregate invariant, endpoint contract, or error code **MUST** include
10
- the corresponding test update **in the same commit**.
11
-
12
- ## Unit test conventions
13
-
14
- - Unit tests are required for **every** aggregate behavior, value object, and specification.
15
- - Tests are **deterministic** — time, randomness, and IDs are injected
16
- (use the framework-provided `IClock` / `IGuidGenerator`). Never `DateTime.UtcNow` /
17
- `Guid.NewGuid()` in the system under test.
18
- - Tests **MUST NOT** reach into module internals; they exercise the public endpoint or
19
- `IMediator` surface.
20
-
21
- ## Handler tests
22
-
23
- Required for **every** Command/Query handler and MUST cover:
24
-
25
- - happy path
26
- - validation failure
27
- - not-found
28
- - (for writes) concurrency conflict
29
-
30
- ## Integration testing
31
-
32
- - Required for **every** endpoint; run against **PostgreSQL via Testcontainers** or Aspire.
33
- - Test database engine: Testcontainers PostgreSQL for integration; in-memory fixtures for
34
- unit tests.
35
-
36
- ## Testcontainers usage
37
-
38
- - Integration and contract tests spin up real PostgreSQL (and other backing services)
39
- via Testcontainers / Aspire — no faked persistence in integration tests.
40
-
41
- ## Test data hygiene
42
-
43
- - Tests use a **shared fixture library** rather than inlining fabricated data.
44
- - Production data MUST NOT be used in development, test, demo, or shared CI.
45
- (Domain specifics — synthetic fixtures + terminology sentinels — in [[testing.domain]].)
46
-
47
- ## Coverage rules
48
-
49
- - Minimum **85% line coverage** on Application + Domain.
50
- - **100%** on aggregate behaviors and value objects.
51
- - Code coverage on changed files must meet the threshold (CI gate).
52
-
53
- ## Related
54
-
55
- - [[domain-driven-design]] · [[persistence]] · [[testing.domain]]
@@ -1,201 +0,0 @@
1
- # Vertical Slice Standards
2
-
3
- > Pure technology (domain-agnostic) — domain additions in [[vertical-slices.domain]]. Commands: `/aw-spec-plan`, `/aw-spec-tasks`, `/aw-spec-implement`.
4
-
5
- ## Feature organization
6
-
7
- - Each feature is a **self-contained vertical slice**: Command/Query, Handler, Endpoint,
8
- Validator co-located in **one folder**.
9
- - No horizontal "Services/Managers" layers spanning slices.
10
-
11
- ## Folder structure (per module)
12
-
13
- ```
14
- <Module>/
15
- Domain/
16
- <Aggregate>.cs
17
- <ChildEntity>.cs
18
- <ValueObject>.cs
19
- <Aggregate>Id.cs [ValueObject<Guid>] partial struct
20
- Events/ DomainEventBase descendants
21
- Specifications/
22
- Persistence/<Aggregate>Configuration.cs
23
- Features/<FeatureName>/ Command/Query + Handler + Endpoint + Validator
24
- Interop/ external-protocol adapters (see [[vertical-slices.domain]])
25
- Shared/ module-public DTOs + ToDto extensions
26
- ```
27
-
28
- ## Endpoint conventions
29
-
30
- - One endpoint **per file**. Endpoints inherit `SecureFastEndpoint<TRequest, TResponse>`.
31
- - Kebab-case routes; resources are plural nouns; verbs map to intent
32
- (POST create, GET list/fetch, PUT full update, PATCH partial update, DELETE logical delete).
33
- - GUID route ids **MUST** use the `:guid` constraint.
34
- - Tagged via `Options(o => o.WithTags("NN - <Module> - (FastEndpoints)"))` and **MUST**
35
- provide a `Summary`.
36
- - HTTP request classes are declared **inside the same file as the Endpoint** and contain
37
- **primitive types only**.
38
- - Endpoints **MUST NOT** swallow exceptions — the central exception handler owns the contract.
39
- - See [[authorization]] for security cache policy on endpoints.
40
-
41
- ## Validator placement
42
-
43
- - Validators live in the **feature folder** alongside the Command/Query they validate.
44
- - Validators check **request shape only**; aggregate invariants are enforced in the Domain.
45
-
46
- ## DTO placement (immutable contracts)
47
-
48
- - DTOs live in `{Module}/Shared/` and are mapped with **hand-written** `ToDto()` /
49
- `ToDtoList()` extension methods. AutoMapper-style reflection mappers are FORBIDDEN.
50
- - `record EntityDto { public required Guid Id { get; init; } ... }` — no setters;
51
- required init for non-nullable members.
52
- - Every DTO **MUST** include a base64 `RowVersionBase64` when the aggregate implements
53
- `IConcurrencyTracked`.
54
- - Paged results **MUST** use the standard `PagedResult<T>` shape
55
- (`Items, TotalCount, PageNumber, PageSize`, computed `TotalPages`) with required init props.
56
-
57
- ## Specification placement
58
-
59
- - Query composition lives in the **Domain**, under `{Module}/Domain/Specifications/`.
60
- See [[persistence]] for specification rules.
61
-
62
- ## Canonical example (agent reference — `<SampleProjectName>` sample)
63
-
64
- A complete slice: Endpoint (+ request class in the same file), Validator, DTO, and mapping.
65
-
66
- ```csharp
67
- // <SampleProjectName>.Service/Projects/Features/CreateProject/CreateProjectEndpoint.cs
68
- public class CreateProjectEndpoint(IMediator _mediator)
69
- : SecureFastEndpoint<CreateProjectRequest, ProjectDto>
70
- {
71
- public override void Configure()
72
- {
73
- Post("/projects"); // kebab-case, plural noun
74
- Summary(s => s.Summary = "Create a new project");
75
- Options(o => o.WithTags("02 - Project - (FastEndpoints)"));
76
- }
77
-
78
- protected override SecurityCachePolicy GetSecurityCachePolicy() => new()
79
- {
80
- SecurityLevel = SecurityLevel.Internal,
81
- CachePolicy = CachePolicy.NoStore // mutating endpoint
82
- };
83
-
84
- protected override async Task<Result<ProjectDto>> ExecuteAsync(
85
- CreateProjectRequest req, CancellationToken ct)
86
- {
87
- var result = await _mediator.Send(new CreateProjectCommand( // IMediator only
88
- req.Code, req.Name, req.Description, req.StartDate, req.EndDate, req.OwnerId), ct);
89
-
90
- if (result.IsSuccess && !string.IsNullOrWhiteSpace(result.Value?.RowVersionBase64))
91
- HttpContext.Response.SetETagFromBase64(result.Value!.RowVersionBase64);
92
- return result;
93
- }
94
- }
95
-
96
- // Request lives in the SAME file — primitive types only
97
- public class CreateProjectRequest
98
- {
99
- public string Code { get; set; } = string.Empty;
100
- public string Name { get; set; } = string.Empty;
101
- public string? Description { get; set; }
102
- public DateOnly StartDate { get; set; }
103
- public DateOnly? EndDate { get; set; }
104
- public Guid? OwnerId { get; set; }
105
- }
106
- ```
107
-
108
- ```csharp
109
- // .../CreateProject/CreateProjectValidator.cs — request shape only
110
- public class CreateProjectValidator : AbstractValidator<CreateProjectCommand>
111
- {
112
- public CreateProjectValidator()
113
- {
114
- RuleFor(x => x.Code).NotEmpty().MaximumLength(20).Matches(@"^[A-Z0-9\-]+$");
115
- RuleFor(x => x.Name).NotEmpty().MaximumLength(200);
116
- RuleFor(x => x.EndDate)
117
- .GreaterThanOrEqualTo(x => x.StartDate).When(x => x.EndDate.HasValue);
118
- }
119
- }
120
- ```
121
-
122
- ```csharp
123
- // {Module}/Shared/ProjectDto.cs — includes RowVersionBase64 (IConcurrencyTracked)
124
- public record ProjectDto
125
- {
126
- public Guid Id { get; set; }
127
- public string Code { get; set; } = string.Empty;
128
- public string Name { get; set; } = string.Empty;
129
- public string Status { get; set; } = string.Empty;
130
- public Guid? OwnerId { get; set; }
131
- public List<ProjectTaskDto> Tasks { get; set; } = new();
132
- public string RowVersionBase64 { get; set; } = string.Empty;
133
- }
134
-
135
- // {Module}/Shared/ProjectMappingExtensions.cs — hand-written, zero reflection
136
- public static class ProjectMappingExtensions
137
- {
138
- public static ProjectDto ToDto(this Project e) => new()
139
- {
140
- Id = e.Id.Value, Code = e.Code.Value, Name = e.Name,
141
- Status = e.Status.ToString(), OwnerId = e.OwnerId?.Value,
142
- Tasks = e.Tasks.Select(t => t.ToDto()).ToList(),
143
- RowVersionBase64 = e.RowVersion is { Length: > 0 } rv
144
- ? Convert.ToBase64String(rv) : string.Empty
145
- };
146
-
147
- public static List<ProjectDto> ToDtoList(this IEnumerable<Project> es)
148
- => es.Select(e => e.ToDto()).ToList();
149
- }
150
- ```
151
-
152
- Sub-resource endpoint for a distinct operation on a child — nested route, multiple route
153
- params, `EmptyResponse` when there is no body to return:
154
-
155
- ```csharp
156
- // .../Features/AssignTask/AssignTaskEndpoint.cs
157
- public class AssignTaskEndpoint(IMediator _mediator)
158
- : SecureFastEndpoint<AssignTaskRequest, EmptyResponse>
159
- {
160
- public override void Configure()
161
- {
162
- Put("/projects/{projectId:guid}/tasks/{taskId:guid}/assignee"); // sub-resource
163
- Summary(s => s.Summary = "Assign a task to a TaskUser");
164
- Options(o => o.WithTags("02 - Project - (FastEndpoints)"));
165
- }
166
-
167
- protected override SecurityCachePolicy GetSecurityCachePolicy() => new()
168
- { SecurityLevel = SecurityLevel.Internal, CachePolicy = CachePolicy.NoStore };
169
-
170
- protected override async Task<Result<EmptyResponse>> ExecuteAsync(
171
- AssignTaskRequest req, CancellationToken ct)
172
- {
173
- var projectId = Route<Guid>("projectId");
174
- var taskId = Route<Guid>("taskId");
175
- var result = await _mediator.Send(new AssignTaskCommand(projectId, taskId, req.AssigneeId), ct);
176
- return result.IsSuccess
177
- ? Result<EmptyResponse>.Ok(new EmptyResponse())
178
- : Result<EmptyResponse>.Failure([.. result.Errors]);
179
- }
180
- }
181
-
182
- public class AssignTaskRequest { public Guid AssigneeId { get; set; } }
183
- ```
184
-
185
- Standard `PagedResult<T>` contract (required init props, computed `TotalPages`):
186
-
187
- ```csharp
188
- // {Module}/Common/PagedResult.cs
189
- public sealed class PagedResult<T>
190
- {
191
- public required IReadOnlyList<T> Items { get; init; }
192
- public required int TotalCount { get; init; }
193
- public required int PageNumber { get; init; }
194
- public required int PageSize { get; init; }
195
- public int TotalPages => (int)Math.Ceiling((double)TotalCount / PageSize);
196
- }
197
- ```
198
-
199
- ## Related
200
-
201
- - [[clean-architecture]] · [[cqrs]] · [[persistence]] · [[authorization]] · [[vertical-slices.domain]]