aw-spec-cli 0.1.0 → 0.1.2
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/assets/.agents/skills/aw-spec-addcomponent/SKILL.md +129 -0
- package/assets/.agents/skills/aw-spec-agent-context-update/SKILL.md +31 -0
- package/assets/.agents/skills/aw-spec-analyze/SKILL.md +296 -0
- package/assets/.agents/skills/aw-spec-checklist/SKILL.md +374 -0
- package/assets/.agents/skills/aw-spec-clarify/SKILL.md +320 -0
- package/assets/.agents/skills/aw-spec-constitution/SKILL.md +194 -0
- package/assets/.agents/skills/aw-spec-fast-implement/SKILL.md +268 -0
- package/assets/.agents/skills/aw-spec-foundation/SKILL.md +358 -0
- package/assets/.agents/skills/aw-spec-implement/SKILL.md +279 -0
- package/assets/.agents/skills/aw-spec-minimal-implementation/SKILL.md +209 -0
- package/assets/.agents/skills/aw-spec-plan/SKILL.md +221 -0
- package/assets/.agents/skills/aw-spec-select-domain/SKILL.md +73 -0
- package/assets/.agents/skills/aw-spec-specify/SKILL.md +382 -0
- package/assets/.agents/skills/aw-spec-tasks/SKILL.md +259 -0
- package/assets/.agents/skills/aw-spec-taskstoissues/SKILL.md +107 -0
- package/assets/.aw-spec/domain.json +3 -0
- package/assets/.aw-spec/extensions/.registry +19 -0
- package/assets/.aw-spec/extensions/agent-context/README.md +57 -0
- package/assets/.aw-spec/extensions/agent-context/agent-context-config.yml +4 -0
- package/assets/.aw-spec/extensions/agent-context/commands/aw-spec.agent-context.update.md +26 -0
- package/assets/.aw-spec/extensions/agent-context/extension.yml +34 -0
- package/assets/.aw-spec/extensions/agent-context/scripts/bash/update-agent-context.sh +201 -0
- package/assets/.aw-spec/extensions/agent-context/scripts/powershell/update-agent-context.ps1 +237 -0
- package/assets/.aw-spec/extensions.yml +23 -0
- package/assets/.aw-spec/feature.json +3 -0
- package/assets/.aw-spec/init-options.json +9 -0
- package/assets/.aw-spec/integration.json +15 -0
- package/assets/.aw-spec/integrations/aw-spec.manifest.json +17 -0
- package/assets/.aw-spec/integrations/claude.manifest.json +17 -0
- package/assets/.aw-spec/memory/constitution.md +50 -0
- package/assets/.aw-spec/scripts/bash/check-prerequisites.sh +189 -0
- package/assets/.aw-spec/scripts/bash/common.sh +582 -0
- package/assets/.aw-spec/scripts/bash/create-new-feature.sh +299 -0
- package/assets/.aw-spec/scripts/bash/setup-plan.sh +84 -0
- package/assets/.aw-spec/scripts/bash/setup-tasks.sh +91 -0
- package/assets/.aw-spec/templates/checklist-template.md +40 -0
- package/assets/.aw-spec/templates/constitution-template.md +57 -0
- package/assets/.aw-spec/templates/plan-template.md +113 -0
- package/assets/.aw-spec/templates/spec-template.md +131 -0
- package/assets/.aw-spec/templates/tasks-template.md +252 -0
- package/assets/.aw-spec/workflows/aw-spec/workflow.yml +77 -0
- package/assets/.aw-spec/workflows/workflow-registry.json +13 -0
- package/assets/.claude/skills/aw-spec-addcomponent/SKILL.md +129 -0
- package/assets/.claude/skills/aw-spec-agent-context-update/SKILL.md +31 -0
- package/assets/.claude/skills/aw-spec-analyze/SKILL.md +296 -0
- package/assets/.claude/skills/aw-spec-checklist/SKILL.md +374 -0
- package/assets/.claude/skills/aw-spec-clarify/SKILL.md +320 -0
- package/assets/.claude/skills/aw-spec-constitution/SKILL.md +194 -0
- package/assets/.claude/skills/aw-spec-fast-implement/SKILL.md +268 -0
- package/assets/.claude/skills/aw-spec-foundation/SKILL.md +358 -0
- package/assets/.claude/skills/aw-spec-implement/SKILL.md +279 -0
- package/assets/.claude/skills/aw-spec-minimal-implementation/SKILL.md +209 -0
- package/assets/.claude/skills/aw-spec-plan/SKILL.md +221 -0
- package/assets/.claude/skills/aw-spec-select-domain/SKILL.md +73 -0
- package/assets/.claude/skills/aw-spec-specify/SKILL.md +382 -0
- package/assets/.claude/skills/aw-spec-tasks/SKILL.md +259 -0
- package/assets/.claude/skills/aw-spec-taskstoissues/SKILL.md +107 -0
- package/assets/.kilocode/rules/specify-rules.md +4 -0
- package/assets/.kilocode/workflows/aw-spec-addcomponent.md +129 -0
- package/assets/.kilocode/workflows/aw-spec-agent-context.update.md +31 -0
- package/assets/.kilocode/workflows/aw-spec-analyze.md +296 -0
- package/assets/.kilocode/workflows/aw-spec-checklist.md +374 -0
- package/assets/.kilocode/workflows/aw-spec-clarify.md +320 -0
- package/assets/.kilocode/workflows/aw-spec-constitution.md +194 -0
- package/assets/.kilocode/workflows/aw-spec-fast-implement.md +263 -0
- package/assets/.kilocode/workflows/aw-spec-foundation.md +358 -0
- package/assets/.kilocode/workflows/aw-spec-implement.md +279 -0
- package/assets/.kilocode/workflows/aw-spec-minimal-implementation.md +204 -0
- package/assets/.kilocode/workflows/aw-spec-plan.md +221 -0
- package/assets/.kilocode/workflows/aw-spec-select-domain.md +73 -0
- package/assets/.kilocode/workflows/aw-spec-specify.md +382 -0
- package/assets/.kilocode/workflows/aw-spec-tasks.md +259 -0
- package/assets/.kilocode/workflows/aw-spec-taskstoissues.md +107 -0
- package/assets/docs/README.md +106 -0
- package/assets/docs/VerticalSlice/PACKAGING.md +185 -0
- package/assets/docs/VerticalSlice/VerticalSlice.pack.txt +2833 -0
- package/assets/docs/VerticalSlice/pack-foundation.ps1 +145 -0
- package/assets/docs/VerticalSlice/pack-foundation.sh +196 -0
- package/assets/docs/VerticalSlice/unpack-foundation.ps1 +126 -0
- package/assets/docs/VerticalSlice/unpack-foundation.sh +217 -0
- package/assets/docs/architecture/clean-architecture.md +71 -0
- package/assets/docs/architecture/cqrs.md +141 -0
- package/assets/docs/architecture/eventing.md +83 -0
- package/assets/docs/architecture/folder-structure.md +61 -0
- package/assets/docs/architecture/modular-monolith.md +47 -0
- package/assets/docs/architecture/persistence.md +269 -0
- package/assets/docs/architecture/testing.md +55 -0
- package/assets/docs/architecture/vertical-slices.md +201 -0
- package/assets/docs/command.txt +295 -0
- package/assets/docs/components/Email/README.md +112 -0
- package/assets/docs/components/Email/integration.md +260 -0
- package/assets/docs/components/Jobs/README.md +113 -0
- package/assets/docs/components/Jobs/integration.md +245 -0
- package/assets/docs/components/QrGenerator/README.md +78 -0
- package/assets/docs/components/QrGenerator/integration.md +185 -0
- package/assets/docs/components/README.md +57 -0
- package/assets/docs/domain/domain-driven-design.md +212 -0
- package/assets/docs/domainspecs/healthcare/ai-rules.domain.md +34 -0
- package/assets/docs/domainspecs/healthcare/api-versioning.domain.md +17 -0
- package/assets/docs/domainspecs/healthcare/auditing.domain.md +29 -0
- package/assets/docs/domainspecs/healthcare/authorization.domain.md +26 -0
- package/assets/docs/domainspecs/healthcare/clinical-terms.md +20 -0
- package/assets/docs/domainspecs/healthcare/coding-standards.domain.md +26 -0
- package/assets/docs/domainspecs/healthcare/comandrulemap.md +119 -0
- package/assets/docs/domainspecs/healthcare/consent.md +19 -0
- package/assets/docs/domainspecs/healthcare/cqrs.domain.md +12 -0
- package/assets/docs/domainspecs/healthcare/data-classification.domain.md +28 -0
- package/assets/docs/domainspecs/healthcare/dependency-policy.domain.md +11 -0
- package/assets/docs/domainspecs/healthcare/dicom.md +15 -0
- package/assets/docs/domainspecs/healthcare/domain-driven-design.domain.md +28 -0
- package/assets/docs/domainspecs/healthcare/domain-requirements.md +55 -0
- package/assets/docs/domainspecs/healthcare/eventing.domain.md +18 -0
- package/assets/docs/domainspecs/healthcare/fhir.md +36 -0
- package/assets/docs/domainspecs/healthcare/folder-structure.domain.md +23 -0
- package/assets/docs/domainspecs/healthcare/hipaa.md +34 -0
- package/assets/docs/domainspecs/healthcare/hl7.md +15 -0
- package/assets/docs/domainspecs/healthcare/integration-terms.md +21 -0
- package/assets/docs/domainspecs/healthcare/persistence.domain.md +20 -0
- package/assets/docs/domainspecs/healthcare/terminology.md +30 -0
- package/assets/docs/domainspecs/healthcare/testing.domain.md +15 -0
- package/assets/docs/domainspecs/healthcare/vertical-slices.domain.md +18 -0
- package/assets/docs/domainspecs/healthcare/zero-trust.domain.md +12 -0
- package/assets/docs/domainspecs/pharma/ai-rules.domain.md +32 -0
- package/assets/docs/domainspecs/pharma/api-versioning.domain.md +20 -0
- package/assets/docs/domainspecs/pharma/atc.md +22 -0
- package/assets/docs/domainspecs/pharma/auditing.domain.md +26 -0
- package/assets/docs/domainspecs/pharma/authorization.domain.md +25 -0
- package/assets/docs/domainspecs/pharma/coding-standards.domain.md +28 -0
- package/assets/docs/domainspecs/pharma/comandrulemap.md +136 -0
- package/assets/docs/domainspecs/pharma/cqrs.domain.md +16 -0
- package/assets/docs/domainspecs/pharma/data-classification.domain.md +24 -0
- package/assets/docs/domainspecs/pharma/data-provenance.md +21 -0
- package/assets/docs/domainspecs/pharma/dependency-policy.domain.md +11 -0
- package/assets/docs/domainspecs/pharma/domain-driven-design.domain.md +36 -0
- package/assets/docs/domainspecs/pharma/domain-requirements.md +99 -0
- package/assets/docs/domainspecs/pharma/eventing.domain.md +18 -0
- package/assets/docs/domainspecs/pharma/fhir.md +31 -0
- package/assets/docs/domainspecs/pharma/folder-structure.domain.md +24 -0
- package/assets/docs/domainspecs/pharma/hl7.md +17 -0
- package/assets/docs/domainspecs/pharma/idmp.md +26 -0
- package/assets/docs/domainspecs/pharma/integration-terms.md +21 -0
- package/assets/docs/domainspecs/pharma/ndc.md +28 -0
- package/assets/docs/domainspecs/pharma/persistence.domain.md +23 -0
- package/assets/docs/domainspecs/pharma/pharma-terms.md +27 -0
- package/assets/docs/domainspecs/pharma/product-lifecycle.domain.md +30 -0
- package/assets/docs/domainspecs/pharma/regulatory-compliance.md +19 -0
- package/assets/docs/domainspecs/pharma/terminology.md +32 -0
- package/assets/docs/domainspecs/pharma/testing.domain.md +19 -0
- package/assets/docs/domainspecs/pharma/vertical-slices.domain.md +17 -0
- package/assets/docs/domainspecs/pharma/zero-trust.domain.md +12 -0
- package/assets/docs/glossary/ai-rules.md +59 -0
- package/assets/docs/governance/api-versioning.md +99 -0
- package/assets/docs/governance/coding-standards.md +87 -0
- package/assets/docs/governance/dependency-policy.md +40 -0
- package/assets/docs/interop/ihe-conformance.md +31 -0
- package/assets/docs/pharma-constitution.my-ref.md +86 -0
- package/assets/docs/requirement-spec.txt +15 -0
- package/assets/docs/security/auditing.md +117 -0
- package/assets/docs/security/authorization-matrix.md +14 -0
- package/assets/docs/security/authorization.md +87 -0
- package/assets/docs/security/data-classification.md +38 -0
- package/assets/docs/security/zero-trust.md +76 -0
- package/assets/docs/technology-constitution.md +172 -0
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +3 -2
- package/dist/commands/init.js.map +1 -1
- package/dist/utils/copy-assets.d.ts +8 -1
- package/dist/utils/copy-assets.d.ts.map +1 -1
- package/dist/utils/copy-assets.js +1 -0
- package/dist/utils/copy-assets.js.map +1 -1
- package/dist/utils/paths.js +1 -1
- package/dist/utils/paths.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
# AI Operational Rules
|
|
2
|
+
|
|
3
|
+
> Pure technology (domain-agnostic) — domain AI rules in [[ai-rules.domain]]. Applies to all AI agents (Claude Code, Augment, Copilot, etc.). Commands: **ALL**.
|
|
4
|
+
|
|
5
|
+
AI agents MUST treat the constitution as **ground truth**, overriding pre-trained habits and
|
|
6
|
+
any contradictory documentation found inside the repo.
|
|
7
|
+
|
|
8
|
+
## Context loading rules / retrieval expectations
|
|
9
|
+
|
|
10
|
+
- Before generating or editing code, **inspect the relevant vertical slice**
|
|
11
|
+
(`<Module>/Features/<Feature>/`) and the aggregate it mutates (see [[vertical-slices]]).
|
|
12
|
+
- **Confirm symbol signatures via repository search** rather than inferring from generic
|
|
13
|
+
framework knowledge — `IRepository<T>` here is AppWeaver's, not a generic one
|
|
14
|
+
(see [[cqrs]], [[persistence]]).
|
|
15
|
+
|
|
16
|
+
## AI generation rules
|
|
17
|
+
|
|
18
|
+
- New features are generated as **complete vertical slices**: Command/Query + Handler +
|
|
19
|
+
Endpoint + Validator + (when applicable) Specification + DTO + mapping extension + Unit test
|
|
20
|
+
+ Integration test.
|
|
21
|
+
- Do **NOT** introduce new top-level dependencies (see [[dependency-policy]]).
|
|
22
|
+
- Do **NOT** create speculative abstractions (single-implementation interfaces, generic
|
|
23
|
+
wrappers around existing framework types) — YAGNI.
|
|
24
|
+
- Generated tests follow Test-First Engineering and **MUST fail before** the production change
|
|
25
|
+
is in place (see [[testing]]).
|
|
26
|
+
|
|
27
|
+
## Output formatting conventions
|
|
28
|
+
|
|
29
|
+
- Do **NOT** write inline rationale comments explaining why a pattern was chosen — the
|
|
30
|
+
constitution is the rationale (see [[coding-standards]]).
|
|
31
|
+
|
|
32
|
+
## Migration generation policy
|
|
33
|
+
|
|
34
|
+
- AI MAY scaffold EF Core migrations but **MUST NOT run them** against shared environments.
|
|
35
|
+
Production migrations require DBA sign-off and an SRE-executed rollout (see [[persistence]]).
|
|
36
|
+
|
|
37
|
+
## Operating envelope
|
|
38
|
+
|
|
39
|
+
- Edits limited to the scope explicitly requested. Drive-by refactors of unrelated files are
|
|
40
|
+
forbidden.
|
|
41
|
+
- File creation constrained to the Mandatory Folder Structure; new folders require an amendment.
|
|
42
|
+
- All AI-authored commits are self-contained: source change + tests + affected
|
|
43
|
+
DTOs/mappings + migration (when schema changes).
|
|
44
|
+
|
|
45
|
+
## Refusal conditions (generic) — refuse and ask for clarification when a request would:
|
|
46
|
+
|
|
47
|
+
- Violate a NON-NEGOTIABLE principle.
|
|
48
|
+
- Introduce a Forbidden Pattern (see [[coding-standards]]).
|
|
49
|
+
- Require a cross-aggregate transaction (see [[cqrs]], [[domain-driven-design]]).
|
|
50
|
+
- Embed a secret, connection string, or production hostname in code or config.
|
|
51
|
+
- Disable a soft-delete filter, tenancy filter, audit interceptor, scope check, or auth policy
|
|
52
|
+
globally.
|
|
53
|
+
|
|
54
|
+
> Domain-specific generation rules, data masking, terminology codes, and refusal conditions are in [[ai-rules.domain]].
|
|
55
|
+
|
|
56
|
+
## Related
|
|
57
|
+
|
|
58
|
+
- [[coding-standards]] · [[dependency-policy]] · [[testing]] · [[persistence]] ·
|
|
59
|
+
[[vertical-slices]] · [[ai-rules.domain]]
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
# API Versioning Policy
|
|
2
|
+
|
|
3
|
+
> Pure technology (domain-agnostic) — domain (e.g. FHIR) versioning in [[api-versioning.domain]]. Commands: `/aw-spec-plan`, `/aw-spec-tasks`, `/aw-spec-implement`.
|
|
4
|
+
|
|
5
|
+
## REST versioning
|
|
6
|
+
|
|
7
|
+
- Internal REST (FastEndpoints) serves first-party apps and operational tooling.
|
|
8
|
+
- List endpoints accept `pageNumber` (1-based) + `pageSize` and return `PagedResult<TDto>`
|
|
9
|
+
(see [[vertical-slices]]).
|
|
10
|
+
- Breaking changes to internal REST require a **versioned route prefix** (`v2/...`).
|
|
11
|
+
- Endpoints MUST NOT swallow exceptions; the central exception handler owns the contract.
|
|
12
|
+
|
|
13
|
+
## ETag / concurrency at the edge
|
|
14
|
+
|
|
15
|
+
- The server sets the ETag header on every create/read/update via `SetETagFromBase64(...)`.
|
|
16
|
+
- Update/Delete extract `If-Match` via `GetIfMatchBase64Token()`; mismatch -> `409
|
|
17
|
+
concurrency.conflict`. ETag tokens are strong; `W/` weak prefixes stripped on read.
|
|
18
|
+
See [[persistence]].
|
|
19
|
+
|
|
20
|
+
## Machine-readable schemas
|
|
21
|
+
|
|
22
|
+
- The service publishes versioned, machine-readable schemas:
|
|
23
|
+
- **OpenAPI** for REST,
|
|
24
|
+
- **AsyncAPI** for events (see [[eventing]]).
|
|
25
|
+
- All schemas regenerate cleanly in CI; diffs are reviewed for breaking changes
|
|
26
|
+
(see [[coding-standards]]).
|
|
27
|
+
- (FHIR CapabilityStatement — see [[api-versioning.domain]].)
|
|
28
|
+
|
|
29
|
+
## Deprecation lifecycle & sunset policy
|
|
30
|
+
|
|
31
|
+
- **Breaking changes are MAJOR version bumps** (per the constitution's governance SemVer).
|
|
32
|
+
- A superseded REST version remains available through a documented deprecation window before
|
|
33
|
+
sunset; the deprecation and sunset are announced via the published schema diffs and the
|
|
34
|
+
governance amendment process.
|
|
35
|
+
|
|
36
|
+
## Canonical example (agent reference — `<SampleProjectName>` sample)
|
|
37
|
+
|
|
38
|
+
ETag round-trip: the endpoint reads `If-Match` on update and sets the `ETag` on success;
|
|
39
|
+
the handler compares the token and translates an EF conflict to `409 concurrency.conflict`:
|
|
40
|
+
|
|
41
|
+
```csharp
|
|
42
|
+
// Endpoint (PUT /projects/{id:guid})
|
|
43
|
+
var id = Route<Guid>("id");
|
|
44
|
+
var token = HttpContext.Request.GetIfMatchBase64Token(); // extract If-Match
|
|
45
|
+
|
|
46
|
+
var result = await _mediator.Send(new UpdateProjectCommand(
|
|
47
|
+
id, req.Name, req.Description, req.StartDate, req.EndDate, req.OwnerId, token), ct);
|
|
48
|
+
|
|
49
|
+
if (result.IsSuccess && !string.IsNullOrWhiteSpace(result.Value?.RowVersionBase64))
|
|
50
|
+
HttpContext.Response.SetETagFromBase64(result.Value!.RowVersionBase64); // set ETag
|
|
51
|
+
return result;
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
```csharp
|
|
55
|
+
// Handler — compare the supplied token, and catch EF's concurrency exception
|
|
56
|
+
var current = entity.RowVersion is { Length: > 0 } rv ? Convert.ToBase64String(rv) : string.Empty;
|
|
57
|
+
if (string.IsNullOrWhiteSpace(request.RowVersionBase64) ||
|
|
58
|
+
!string.Equals(current, request.RowVersionBase64, StringComparison.Ordinal))
|
|
59
|
+
return Result<ProjectDto>.Failure(new Error(
|
|
60
|
+
"concurrency.conflict", "Resource was modified by another process. Reload and retry.",
|
|
61
|
+
409, "projects"));
|
|
62
|
+
|
|
63
|
+
entity.Update(request.Name, request.Description, request.StartDate, request.EndDate, ownerId);
|
|
64
|
+
await _repo.UpdateAsync(entity, ct);
|
|
65
|
+
|
|
66
|
+
try { await _uow.SaveChangesAsync(ct); }
|
|
67
|
+
catch (DbUpdateConcurrencyException)
|
|
68
|
+
{
|
|
69
|
+
return Result<ProjectDto>.Failure(new Error(
|
|
70
|
+
"concurrency.conflict", "Resource was modified by another process. Reload and retry.",
|
|
71
|
+
409, "projects"));
|
|
72
|
+
}
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
ETag token helpers — strong tokens; strip `W/` weak prefix and quotes on read:
|
|
76
|
+
|
|
77
|
+
```csharp
|
|
78
|
+
// <SampleProjectName>.Service/Utilities/HttpRequestETagExtensions.cs
|
|
79
|
+
public static string GetIfMatchBase64Token(this HttpRequest request, string? fallback = null)
|
|
80
|
+
{
|
|
81
|
+
var token = request.Headers.IfMatch.FirstOrDefault();
|
|
82
|
+
return !string.IsNullOrWhiteSpace(token) ? SanitizeETagToken(token) : (fallback ?? string.Empty);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
public static string SanitizeETagToken(string? raw)
|
|
86
|
+
{
|
|
87
|
+
if (string.IsNullOrWhiteSpace(raw)) return string.Empty;
|
|
88
|
+
var token = raw.Trim();
|
|
89
|
+
if (token.StartsWith("W/")) token = token[2..].Trim(); // strip weak prefix
|
|
90
|
+
return token.Trim('"'); // strip quotes
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// On success the endpoint sets the strong ETag from the DTO's RowVersionBase64:
|
|
94
|
+
HttpContext.Response.SetETagFromBase64(result.Value!.RowVersionBase64);
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
## Related
|
|
98
|
+
|
|
99
|
+
- [[vertical-slices]] · [[persistence]] · [[eventing]] · [[coding-standards]] · [[api-versioning.domain]]
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
# Coding Standards
|
|
2
|
+
|
|
3
|
+
> Pure technology (domain-agnostic) — domain forbidden patterns & CI gates in [[coding-standards.domain]]. Commands: `/aw-spec-plan`, `/aw-spec-tasks`, `/aw-spec-implement`.
|
|
4
|
+
|
|
5
|
+
## Naming conventions
|
|
6
|
+
|
|
7
|
+
- Solution and projects: `<SampleProjectName>.<Role>` (e.g. `<SampleProjectName>.Api`,
|
|
8
|
+
`<SampleProjectName>.AppHost`, `<SampleProjectName>.ServiceDefaults`).
|
|
9
|
+
- Namespaces mirror the folder path exactly; **file-scoped namespaces only**.
|
|
10
|
+
- Aggregates: `class <Aggregate> : EntityBase<<Aggregate>Id>, IAggregateRoot,
|
|
11
|
+
IFullAuditedObject, ISoftDeletable, IConcurrencyTracked, IHasDomainEvents,
|
|
12
|
+
INamedEntity<<Aggregate>Id>`.
|
|
13
|
+
- Commands/Queries: `record <Verb><Aggregate>Command(...) : ICommand<Result<TDto>>`.
|
|
14
|
+
- DTOs: `record <Aggregate>Dto { public required Guid Id { get; init; } ... }` — no setters;
|
|
15
|
+
required init for non-nullable members.
|
|
16
|
+
|
|
17
|
+
## Nullable & language rules
|
|
18
|
+
|
|
19
|
+
- C# language version: latest LTS; **nullable reference types ENABLED**; implicit usings
|
|
20
|
+
ENABLED; **`TreatWarningsAsErrors` for Release**.
|
|
21
|
+
|
|
22
|
+
## Async conventions
|
|
23
|
+
|
|
24
|
+
- Every I/O-bound method ends with `Async` and accepts a `CancellationToken` as the **last
|
|
25
|
+
parameter**.
|
|
26
|
+
- `async void` is forbidden outside event handlers.
|
|
27
|
+
- Blocking calls (`.Result`, `.Wait()`, `Task.Run` to fake async) in request-handling paths
|
|
28
|
+
are FORBIDDEN.
|
|
29
|
+
|
|
30
|
+
## Error / exception conventions
|
|
31
|
+
|
|
32
|
+
- Exceptions are thrown by the **Domain only** (`DomainException`,
|
|
33
|
+
`InvariantViolationException`). Application code returns `Result<T>` (see [[cqrs]]).
|
|
34
|
+
- Never catch-all `catch (Exception)` that swallows or rewraps without rethrow, except in the
|
|
35
|
+
central exception handler.
|
|
36
|
+
- Never return HTTP 200 with an error body — use correct status codes (4xx client, 5xx server,
|
|
37
|
+
409 concurrency).
|
|
38
|
+
|
|
39
|
+
## Documentation expectations
|
|
40
|
+
|
|
41
|
+
- XML doc comments on public types and aggregate methods.
|
|
42
|
+
- Inline comments only where intent is non-obvious. Commented-out code MUST be removed before
|
|
43
|
+
merge.
|
|
44
|
+
- Do not write inline rationale comments explaining pattern choices — the constitution is the
|
|
45
|
+
rationale (see [[ai-rules]]).
|
|
46
|
+
|
|
47
|
+
## Forbidden patterns (generic; rejected at review, build-warning where statically detectable)
|
|
48
|
+
|
|
49
|
+
1. MediatR — use `AppWeaver.Mediator` (see [[dependency-policy]]).
|
|
50
|
+
2. AutoMapper / Mapster reflection mapping — hand-written `ToDto()` only.
|
|
51
|
+
3. Anemic domain models (see [[domain-driven-design]]).
|
|
52
|
+
4. Repository as data-access god object (see [[persistence]]).
|
|
53
|
+
5. Cross-aggregate EF navigation properties — strongly-typed ID only.
|
|
54
|
+
6. Hard-coded connection strings, secrets, ports, or issuer URLs in source (see [[zero-trust]]).
|
|
55
|
+
7. Static service locator — use constructor DI.
|
|
56
|
+
8. Direct `DbContext` access from Endpoints or Features outside repo/spec surface.
|
|
57
|
+
9. Blocking calls in request paths.
|
|
58
|
+
10. Catch-all that swallows/rewraps without rethrow (outside central handler).
|
|
59
|
+
11. `AllowAnonymous()` on sensitive-data/mutating endpoints in a CI-protected branch.
|
|
60
|
+
12. Logging/emitting secrets, tokens, keys, or payment-card data (see [[data-classification]]).
|
|
61
|
+
13. Multiple aggregate mutations in one transaction (see [[cqrs]], [[eventing]]).
|
|
62
|
+
14. `DateTime.UtcNow` / `Guid.NewGuid()` in aggregates/handlers/SUT — use `IClock` /
|
|
63
|
+
`IGuidGenerator` (see [[testing]]).
|
|
64
|
+
15. HTTP 200 with an error body.
|
|
65
|
+
16. Analytics/reporting by querying the OLTP DB directly — emit events (see [[eventing]]).
|
|
66
|
+
|
|
67
|
+
> Domain-specific forbidden patterns are in [[coding-standards.domain]].
|
|
68
|
+
|
|
69
|
+
## Quality gates (CI) — generic, must pass before merge
|
|
70
|
+
|
|
71
|
+
1. `dotnet build` succeeds with `TreatWarningsAsErrors=true` for Release.
|
|
72
|
+
2. `dotnet test` passes for UnitTests + IntegrationTests.
|
|
73
|
+
3. Code coverage on changed files meets the Testing Profile threshold (see [[testing]]).
|
|
74
|
+
4. EF Core migrations apply cleanly to a fresh PostgreSQL instance (see [[persistence]]).
|
|
75
|
+
5. OpenAPI document generation succeeds; diff reviewed for breaking changes.
|
|
76
|
+
6. AsyncAPI regenerates cleanly for any domain-event contract change (see [[eventing]]).
|
|
77
|
+
7. Static analysis (Roslyn analyzers, nullable warnings, banned-API analyzer) — zero errors.
|
|
78
|
+
8. Grep checks: no `AllowAnonymous()` on production endpoints, no hard-coded secrets, no
|
|
79
|
+
MediatR/AutoMapper references.
|
|
80
|
+
9. Constitution compliance review checked off in the PR template.
|
|
81
|
+
|
|
82
|
+
> Domain-specific CI gates are in [[coding-standards.domain]].
|
|
83
|
+
|
|
84
|
+
## Related
|
|
85
|
+
|
|
86
|
+
- [[dependency-policy]] · [[api-versioning]] · [[domain-driven-design]] · [[cqrs]] ·
|
|
87
|
+
[[persistence]] · [[testing]] · [[ai-rules]] · [[coding-standards.domain]]
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# Dependency Policy
|
|
2
|
+
|
|
3
|
+
> Pure technology (domain-agnostic) — domain package rules in [[dependency-policy.domain]]. Commands: `/aw-spec-plan`, `/aw-spec-implement`.
|
|
4
|
+
|
|
5
|
+
## Package governance
|
|
6
|
+
|
|
7
|
+
- **All package versions are governed by `Directory.Packages.props`** (Central Package
|
|
8
|
+
Management). `PackageReference` entries in `.csproj` MUST NOT specify a `Version`.
|
|
9
|
+
- New dependencies are added via `dotnet add package`.
|
|
10
|
+
|
|
11
|
+
## Approved / preferred packages
|
|
12
|
+
|
|
13
|
+
- Mediator: **`AppWeaver.Mediator`**.
|
|
14
|
+
- Mapping: **hand-written `ToDto()` extension methods** (no reflection mapper).
|
|
15
|
+
- Observability: **`AppWeaver.ServiceDefaults`** (OpenTelemetry + Serilog + resilience +
|
|
16
|
+
discovery) — see [[auditing]].
|
|
17
|
+
- Validation: FluentValidation (via the mediator pipeline behavior; see [[cqrs]]).
|
|
18
|
+
|
|
19
|
+
## Banned packages
|
|
20
|
+
|
|
21
|
+
- **MediatR** — MUST NOT appear in any `Directory.Packages.props` entry (CI grep gate).
|
|
22
|
+
- **AutoMapper / Mapster** (reflection mapping) — forbidden (see [[coding-standards]]).
|
|
23
|
+
- Any logging package that ships analytics/telemetry exfiltration by default — rejected.
|
|
24
|
+
|
|
25
|
+
## Supply-chain rules
|
|
26
|
+
|
|
27
|
+
- New dependencies are reviewed for **license and supply-chain risk** before adoption.
|
|
28
|
+
- AI agents **MUST NOT** introduce new top-level dependencies; new framework capabilities are
|
|
29
|
+
requested explicitly by the developer (see [[ai-rules]]).
|
|
30
|
+
- Do not create speculative abstractions (single-implementation interfaces, generic wrappers
|
|
31
|
+
around existing framework types) — YAGNI is enforced.
|
|
32
|
+
|
|
33
|
+
## Licensing expectations
|
|
34
|
+
|
|
35
|
+
- Dependencies must carry a license compatible with the intended commercial deployment;
|
|
36
|
+
copyleft licenses that would force source disclosure of the service are rejected.
|
|
37
|
+
|
|
38
|
+
## Related
|
|
39
|
+
|
|
40
|
+
- [[coding-standards]] · [[cqrs]] · [[auditing]] · [[ai-rules]] · [[dependency-policy.domain]]
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# IHE Conformance Notes — Patients / RegisterPatient
|
|
2
|
+
|
|
3
|
+
Tracks IHE-profile conformance and deliberate scope decisions for the RegisterPatient feature.
|
|
4
|
+
|
|
5
|
+
## PIX / PDQ (Patient Identifier Cross-referencing / Patient Demographics Query)
|
|
6
|
+
|
|
7
|
+
- **In scope (delivered):** populate-and-link. Each registered patient carries a system-assigned
|
|
8
|
+
tenant MRN plus retained external identifiers (HL7 v2 PID-3) with their assigning authorities, so
|
|
9
|
+
the patient is cross-referenceable. `ResolvePatientIdentifiersQuery` resolves a patient's full
|
|
10
|
+
identifier set from any one known identifier (`Patients/Interop/Pix/`).
|
|
11
|
+
- **Deferred (follow-up feature):** standing up full **PIX Manager** / **PDQ Supplier** query-responder
|
|
12
|
+
endpoints, and outbound notification to an external/central MPI. Decision recorded per
|
|
13
|
+
`/aw-spec-clarify` Q3 and `research.md` R8.
|
|
14
|
+
|
|
15
|
+
## ATNA (Audit Trail and Node Authentication)
|
|
16
|
+
|
|
17
|
+
- Every PHI command/query emits a PHI-free, ATNA-shaped access record via the
|
|
18
|
+
`PhiAccessAuditBehavior` mediator behavior; entity writes are additionally captured by the
|
|
19
|
+
framework's advanced auditing. Node authentication (mTLS between nodes) is a deployment concern.
|
|
20
|
+
|
|
21
|
+
## HL7 v2 ADT
|
|
22
|
+
|
|
23
|
+
- Inbound ADT^A04 over MLLP with accept/receipt ACK; outbound ADT^A04 on `PatientRegisteredEvent`.
|
|
24
|
+
Conformance tested against an internal corpus (`AppWeaver.Health.TestFixtures`).
|
|
25
|
+
|
|
26
|
+
## Known follow-ups
|
|
27
|
+
|
|
28
|
+
- **Tenant-scoped MRN uniqueness:** the generated migration currently creates a *global* unique index
|
|
29
|
+
`UX_Patients_Mrn`. Confirm the framework tenancy discriminator column and change to a composite
|
|
30
|
+
unique index `(TenantId, Mrn)` so the "same MRN in a different tenant" edge case is permitted.
|
|
31
|
+
- **FHIR CapabilityStatement / StructureDefinition** packaging and validator CI gate (HG1/HG2).
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
# Pharma Constitution — Domain Addenda
|
|
2
|
+
|
|
3
|
+
> **Domain addenda to the technology constitution.** Not ratified on its own — compose it with
|
|
4
|
+
> the technology base.
|
|
5
|
+
|
|
6
|
+
## How to use (compose-at-ratification)
|
|
7
|
+
|
|
8
|
+
The technology base (`docs/technology-constitution.md`) **auto-loads** for `/aw-spec-constitution`,
|
|
9
|
+
so you pass only this domain addenda:
|
|
10
|
+
|
|
11
|
+
```
|
|
12
|
+
/aw-spec-constitution @docs/pharma-constitution.md
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
This file supplies **only** the pharma deltas: project identity, regulatory posture, the pharma
|
|
16
|
+
domain principles (which **continue** the technology numbering at **XVI**), folder additions,
|
|
17
|
+
governance overrides, and the active-domain pointer. The reusable principles I–XV live in the
|
|
18
|
+
technology base and are not repeated here.
|
|
19
|
+
|
|
20
|
+
Active domain folder: `docs/domainspecs/pharma/` (set via `/aw-spec-select-domain pharma`).
|
|
21
|
+
|
|
22
|
+
## Project Identity (fills the technology base)
|
|
23
|
+
|
|
24
|
+
- **Project name:** `<SampleProjectName>` — Medicinal Product Registry (Headless API).
|
|
25
|
+
- **Mission:** maintain a centralized, authoritative registry of medicinal products in compliance with pharmaceutical industry standards, regulatory requirements, and healthcare interoperability specifications; exposed as a headless HTTP + FHIR API ready for integration with prescribing, dispensing, manufacturing, pharmacovigilance, and clinical systems.
|
|
26
|
+
- **Bounded contexts (modules):** enumerated in `docs/domainspecs/pharma/domain-requirements.md` (project-specific).
|
|
27
|
+
- **Architecture posture:** Modular Monolith with Clean Architecture + DDD; `MedicinalProduct` is the Aggregate Root; each module designed for clean extraction into an independent microservice. **HEADLESS.**
|
|
28
|
+
|
|
29
|
+
## Regulatory Posture
|
|
30
|
+
|
|
31
|
+
Built for regulatory compliance **readiness** — auditability, traceability, data integrity, and
|
|
32
|
+
provenance are designed in. Data-integrity expectations follow **ALCOA+** for GxP-relevant
|
|
33
|
+
product records. Identifier standards (NDC), substance identity (UNII), classification (WHO
|
|
34
|
+
ATC), and ISO IDMP concept alignment are first-class. Data residency configurable per deployment.
|
|
35
|
+
|
|
36
|
+
## Domain Principles
|
|
37
|
+
|
|
38
|
+
> Extend the technology Core Principles; numbering **continues from XV**.
|
|
39
|
+
|
|
40
|
+
### XVI — Pharmaceutical Product Identification (NON-NEGOTIABLE)
|
|
41
|
+
NDC modeled as a Value Object with discrete Labeler/Product/Package segments; NDC uniqueness enforced (aggregate + persistence constraint); NDC validation rules configurable; no invented NDC/Labeler codes. The domain model aligns with ISO IDMP concepts (Medicinal Product, Manufactured Item, Pharmaceutical Product, Substance, MAH, Packaging, Strength, Route of Administration).
|
|
42
|
+
→ `docs/domainspecs/pharma/ndc.md`, `idmp.md`
|
|
43
|
+
|
|
44
|
+
### XVII — Pharma Terminology & Classification (NON-NEGOTIABLE)
|
|
45
|
+
Coded data uses standard terminologies — UNII (substances), RxNorm (clinical drug), SNOMED CT (dose form / route / classification), UCUM (units), and WHO ATC (therapeutic classification). Every coded field is a Codeable value object; lookup/validation via the Terminology module; ATC codes validated against configured code sets (multiple ATC per product allowed); no invented or free-text codes.
|
|
46
|
+
→ `docs/domainspecs/pharma/terminology.md`, `atc.md`
|
|
47
|
+
|
|
48
|
+
### XVIII — Regulatory Compliance: Audit, Provenance & Versioning (NON-NEGOTIABLE)
|
|
49
|
+
Immutable audit records (User, Timestamp, Action, Previous Value, New Value) for product creation, modification, retirement, identifier changes, and classification changes; `AuditRecord` is append-only. Provenance captured for externally sourced product data (source, method, agent, timestamp). Full version history of product information; historical versions remain queryable. Extends technology **X** and **XIV** with pharma compliance controls.
|
|
50
|
+
→ `docs/domainspecs/pharma/regulatory-compliance.md`, `data-provenance.md`, `auditing.domain.md`, `persistence.domain.md`
|
|
51
|
+
|
|
52
|
+
### XIX — Medicinal Product Lifecycle Integrity (NON-NEGOTIABLE)
|
|
53
|
+
MedicinalProduct lifecycle (Draft → Under Review → Approved → Active → Discontinued/Withdrawn) is a guarded state machine; only valid transitions are permitted; registration completeness (required attributes, ≥ 1 active ingredient, ≥ 1 packaging, valid NDC/dosage-form/route/ATC) is enforced before leaving Draft; retirement is a logical state change, never a hard delete. Each transition emits a domain event and is audit-mandatory.
|
|
54
|
+
→ `docs/domainspecs/pharma/product-lifecycle.domain.md`, `domain-driven-design.domain.md`, `eventing.domain.md`
|
|
55
|
+
|
|
56
|
+
### XX — Healthcare Interoperability Readiness
|
|
57
|
+
The model maps to FHIR Medication / MedicationKnowledge / Substance / Organization (projections, not the system of record); product identifiers map to FHIR Identifier; coded fields map to CodeableConcept/Coding; the solution supports future HL7-based integration via Interop adapters. Extends technology **XII** and **XIV** with the FHIR contract.
|
|
58
|
+
→ `docs/domainspecs/pharma/fhir.md`, `hl7.md`
|
|
59
|
+
|
|
60
|
+
## Folder Structure Additions
|
|
61
|
+
|
|
62
|
+
- Pharma additions (Interop/Fhir + Hl7v2 adapters, InteropTests, terminology seed fixtures, provenance/version-history stores): `docs/domainspecs/pharma/folder-structure.domain.md`
|
|
63
|
+
|
|
64
|
+
## Security & Governance Addenda
|
|
65
|
+
|
|
66
|
+
- **Change authority** (who may register / approve / retire products, and change identifiers or classification): `docs/domainspecs/pharma/authorization.domain.md`
|
|
67
|
+
- **Data classification** (public vs commercially-sensitive / pre-market / externally-sourced; GxP/ALCOA+ integrity): `docs/domainspecs/pharma/data-classification.domain.md`
|
|
68
|
+
- **Zero-trust posture** (transport, scope/trace propagation, change-authority enforcement): `docs/domainspecs/pharma/zero-trust.domain.md`
|
|
69
|
+
- **Coding standards** (forbidden patterns, quality gates): `docs/domainspecs/pharma/coding-standards.domain.md`
|
|
70
|
+
- **API/contract versioning** (FHIR + NDC identifier stability): `docs/domainspecs/pharma/api-versioning.domain.md`
|
|
71
|
+
- **Dependency policy** (FHIR SDK, terminology libraries, regulatory suitability): `docs/domainspecs/pharma/dependency-policy.domain.md`
|
|
72
|
+
|
|
73
|
+
## AI Rules Addenda
|
|
74
|
+
|
|
75
|
+
- Identifier/code safety (no invented NDC/UNII/RxNorm/SNOMED/ATC), projection discipline, lifecycle/audit edit limits, pharma refusal conditions: `docs/domainspecs/pharma/ai-rules.domain.md` (extends `docs/glossary/ai-rules.md`)
|
|
76
|
+
|
|
77
|
+
## Governance Overrides
|
|
78
|
+
|
|
79
|
+
- Mandatory approver for any identifier (NDC), classification (ATC), or lifecycle-rule change: Regulatory/Compliance Officer (in addition to a Principal Engineer and a Domain Owner). Change-authority roles are defined in `docs/domainspecs/pharma/authorization.domain.md`.
|
|
80
|
+
- Additional review required after every relevant regulatory or terminology-release update (e.g. WHO ATC annual update, FDA NDC directory changes).
|
|
81
|
+
- **Versioning note:** any change to NDC identity rules, the lifecycle state machine, the audit-record shape, or the canonical FHIR interoperability surface is a MAJOR bump.
|
|
82
|
+
|
|
83
|
+
## Domain Requirements Pointer
|
|
84
|
+
|
|
85
|
+
→ `docs/domainspecs/pharma/domain-requirements.md`
|
|
86
|
+
(`[DOMAIN: Bounded Contexts / Modules]`, per-subsection pointers, `[DOMAIN: Platform Topology]`, `[DOMAIN: Governance Overrides]`).
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/aw-spec-specify MedicinalProduct/RegisterMedicinalProduct
|
|
2
|
+
|
|
3
|
+
Allow an authorized user to register a new medicinal product in the registry.
|
|
4
|
+
|
|
5
|
+
On registration the user provides: product name, generic name, NDC, dosage form, strength, route of administration, manufacturer, one or more active ingredients with details, one or more packaging configurations and one or more ATC classification codes.
|
|
6
|
+
|
|
7
|
+
The system validates the submission (required attributes, duplicate NDC, valid dosage form and route of administration) and creates the product in the Draft state. A successful registration is auditable and emits a registration event.
|
|
8
|
+
|
|
9
|
+
Acceptance:
|
|
10
|
+
- A valid submission creates a Draft MedicinalProduct and returns its identifier.
|
|
11
|
+
- A malformed or duplicate NDC is rejected with a clear validation error.
|
|
12
|
+
- A product missing a required attribute, an active ingredient, or a packaging configuration is rejected.
|
|
13
|
+
- The creation is recorded in the audit trail.
|
|
14
|
+
|
|
15
|
+
==============================================================================================
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
# Auditing & Observability Standards
|
|
2
|
+
|
|
3
|
+
> Pure technology (domain-agnostic) — domain audit additions in [[auditing.domain]]. NON-NEGOTIABLE. Commands: `/aw-spec-plan`, `/aw-spec-tasks`, `/aw-spec-implement`.
|
|
4
|
+
|
|
5
|
+
## Observability baseline
|
|
6
|
+
|
|
7
|
+
- All services consume `AppWeaver.ServiceDefaults` (`builder.AddServiceDefaults()`):
|
|
8
|
+
OpenTelemetry (ASP.NET Core, HttpClient, EF Core, runtime), Serilog structured logging with
|
|
9
|
+
enrichment (environment/machine/thread/correlation IDs), health checks at `/health` and
|
|
10
|
+
`/alive`, standard HTTP resilience handlers, and Aspire service discovery.
|
|
11
|
+
|
|
12
|
+
## Structured logging
|
|
13
|
+
|
|
14
|
+
- All logging goes through `ILogger<T>` with **message templates** (no string interpolation in
|
|
15
|
+
log calls).
|
|
16
|
+
- `Console.WriteLine`, `Debug.WriteLine`, and `System.Diagnostics.Trace` are **forbidden** in
|
|
17
|
+
production code.
|
|
18
|
+
- Log levels: Information (state transitions), Warning (recoverable anomalies), Error
|
|
19
|
+
(handled failures), Critical (invariant breaches). Debug/Trace MUST be safe to enable in
|
|
20
|
+
production.
|
|
21
|
+
|
|
22
|
+
## Metrics & tracing
|
|
23
|
+
|
|
24
|
+
- Custom metrics follow OTel semantic conventions, registered via a single `Meter` per module.
|
|
25
|
+
- Trace context MUST propagate across `IMediator.Send`, `HttpClient`, and EF Core; bypassing
|
|
26
|
+
the framework's `HttpClientFactory` is forbidden.
|
|
27
|
+
|
|
28
|
+
## Secrets never in telemetry
|
|
29
|
+
|
|
30
|
+
- Authentication tokens, password material, private keys, and full payment-card numbers MUST
|
|
31
|
+
NEVER appear in logs, metrics labels, exception messages, or trace attributes.
|
|
32
|
+
(Domain-sensitive data — PHI, SSN, government IDs — see [[data-classification.domain]] and
|
|
33
|
+
[[auditing.domain]].)
|
|
34
|
+
|
|
35
|
+
## Immutable audit strategy (generic)
|
|
36
|
+
|
|
37
|
+
- Audit records are **append-only**: no update, no delete (no hard-delete path).
|
|
38
|
+
- An audit-access pipeline behavior runs on every mediator request (see [[cqrs]]).
|
|
39
|
+
- Audit records are retained per the applicable retention policy (see [[persistence]];
|
|
40
|
+
regulatory retention in [[auditing.domain]]).
|
|
41
|
+
|
|
42
|
+
## Canonical example (agent reference — `<SampleProjectName>` sample)
|
|
43
|
+
|
|
44
|
+
Aggregates opt into auditing by implementing the audit interfaces; the framework populates the
|
|
45
|
+
fields automatically (no manual `CreatedOn`/`ModifiedBy` assignment in handlers):
|
|
46
|
+
|
|
47
|
+
```csharp
|
|
48
|
+
public class Project :
|
|
49
|
+
EntityBase<ProjectId>, IAggregateRoot,
|
|
50
|
+
IFullAuditedObject, // ICreationAudited + IModificationAudited + IDeletionAudited
|
|
51
|
+
IFieldAudited, // opt-in to field-level (before/after) auditing
|
|
52
|
+
ISoftDeletable, IConcurrencyTracked, IHasDomainEvents
|
|
53
|
+
{
|
|
54
|
+
public DateTimeOffset CreatedOn { get; set; }
|
|
55
|
+
public string CreatedBy { get; set; } = default!;
|
|
56
|
+
public DateTimeOffset? ModifiedOn { get; set; }
|
|
57
|
+
public string? ModifiedBy { get; set; }
|
|
58
|
+
public bool IsDeleted { get; set; }
|
|
59
|
+
public DateTimeOffset? DeletedOn { get; set; }
|
|
60
|
+
public string? DeletedBy { get; set; }
|
|
61
|
+
// ...
|
|
62
|
+
}
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
The DbContext applies audit (plus soft-delete/tenant) behaviors centrally:
|
|
66
|
+
|
|
67
|
+
```csharp
|
|
68
|
+
// OnModelCreating — see [[persistence]] for the full DbContext
|
|
69
|
+
modelBuilder.ApplyCrossCuttingBehaviors(this); // wires audit interceptors + filters
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
Composition — enable auditing on the repository stack and route audit records to a
|
|
73
|
+
separate, append-only store (schema `Auditing`, prefix `Audit_`):
|
|
74
|
+
|
|
75
|
+
```csharp
|
|
76
|
+
// <SampleProjectName>.Service/Extensions/InfrastructureServiceRegistration.cs
|
|
77
|
+
services.AddRepositories(builder => builder
|
|
78
|
+
.UseSingleDbContextEFRepositories<ApplicationDbContext>(/* ... */)
|
|
79
|
+
.EnableAuditing()
|
|
80
|
+
.EnableAdvancedAuditing()
|
|
81
|
+
.Done(), o => o.Provider = "EfCore");
|
|
82
|
+
|
|
83
|
+
services.AddDataStore(b => b.UseEfCoreDataStore(/* ... */), o =>
|
|
84
|
+
{
|
|
85
|
+
o.Strategy = DataStoreStrategy.Shared; // infra tables (audit, tenancy) in one store
|
|
86
|
+
o.TablePrefix = "AppWeaver_";
|
|
87
|
+
o.AutoMigrate = true;
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
services.AddAuditing(b => b
|
|
91
|
+
.UseEfCoreAuditing(o =>
|
|
92
|
+
{
|
|
93
|
+
o.TablePrefix = "Audit_";
|
|
94
|
+
o.SchemaName = "Auditing";
|
|
95
|
+
o.ExcludeShadowProperties = true;
|
|
96
|
+
})
|
|
97
|
+
.UseAppWeaverDataStore(),
|
|
98
|
+
o => { o.IsEnabled = true; o.EnableFieldLevelAuditing = true; });
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
Observability baseline — `ServiceDefaults` (OTel + Serilog + health) wired once in `Program.cs`:
|
|
102
|
+
|
|
103
|
+
```csharp
|
|
104
|
+
// <SampleProjectName>.Service/Program.cs
|
|
105
|
+
builder.AddServiceDefaults(); // OTel, resilience, service discovery, health
|
|
106
|
+
Log.Logger = new LoggerConfiguration()
|
|
107
|
+
.ReadFrom.Configuration(builder.Configuration)
|
|
108
|
+
.Enrich.FromLogContext()
|
|
109
|
+
.CreateLogger();
|
|
110
|
+
builder.Host.UseSerilog();
|
|
111
|
+
// ...
|
|
112
|
+
app.MapDefaultEndpoints(); // /health, /alive
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
## Related
|
|
116
|
+
|
|
117
|
+
- [[data-classification]] · [[cqrs]] · [[persistence]] · [[auditing.domain]]
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# Authorization Matrix
|
|
2
|
+
|
|
3
|
+
Per-route role + SMART-scope authorization (authorization.domain). PHI endpoints are
|
|
4
|
+
`SecurityLevel.Restricted`, `CachePolicy.NoStore`, and emit ATNA audit records.
|
|
5
|
+
|
|
6
|
+
| Route | Method | Roles | SMART scope | Security level | Notes |
|
|
7
|
+
|---|---|---|---|---|---|
|
|
8
|
+
| `/patients` | POST | `FrontDesk`, `SystemAdmin`, `ExternalSystem` | `system/Patient.write` or `user/Patient.write` | Restricted | Register patient (PHI write). 400/409/422 per spec. |
|
|
9
|
+
| `/fhir/R4/Patient/{id}` | GET | `Physician`, `Nurse`, `SystemAdmin`, `ExternalSystem` | `patient/Patient.read`, `user/Patient.read`, or `patient/Patient.read.full` for un-redacted demographics | Restricted | Minimum-necessary redaction in the projection extension. |
|
|
10
|
+
| `/fhir/metadata` | GET | — (anonymous) | — | Public | CapabilityStatement (infra/discovery allowlist). |
|
|
11
|
+
| HL7 v2 MLLP ingress (ADT^A04) | MLLP | `ExternalSystem` (node-authenticated) | n/a | Restricted | Receipt ACK; async registration via canonical command. |
|
|
12
|
+
|
|
13
|
+
> The `patient/Patient.read.full` scope gates full demographics (telecom/address/secondary identifiers);
|
|
14
|
+
> without it the FHIR read returns identity essentials only.
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
# Authorization Standards
|
|
2
|
+
|
|
3
|
+
> Pure technology (domain-agnostic) — domain roles & matrix in [[authorization.domain]]. NON-NEGOTIABLE. Commands: `/aw-spec-plan`, `/aw-spec-tasks`, `/aw-spec-implement`.
|
|
4
|
+
|
|
5
|
+
## Authorization model
|
|
6
|
+
|
|
7
|
+
- Authorization is **role-based AND scope-based** — both MUST pass.
|
|
8
|
+
- Per-route authorization is declared per endpoint via `Roles(...)` + scope evaluation,
|
|
9
|
+
documented in an authorization matrix.
|
|
10
|
+
- Identity comes from an external IdP via `.AddAppWeaverSecurity(...)` — local identity stores
|
|
11
|
+
forbidden (see [[zero-trust]]).
|
|
12
|
+
- (The concrete role set and scope syntax are domain-specific — see [[authorization.domain]].)
|
|
13
|
+
|
|
14
|
+
## Policy evaluation
|
|
15
|
+
|
|
16
|
+
- Endpoints inherit `SecureFastEndpoint<TRequest, TResponse>` and MUST override
|
|
17
|
+
`GetSecurityCachePolicy()` to declare `SecurityLevel` and `CachePolicy`.
|
|
18
|
+
- Mutating endpoints MUST use `CachePolicy.NoStore`.
|
|
19
|
+
- Sensitive-data read endpoints MUST use `CachePolicy.NoStore`.
|
|
20
|
+
- Field-level redaction (minimum-necessary) lives in **projection extensions**, not
|
|
21
|
+
controllers (see [[data-classification]]; domain specifics in [[authorization.domain]]).
|
|
22
|
+
|
|
23
|
+
## Endpoint authorization patterns
|
|
24
|
+
|
|
25
|
+
- `AllowAnonymous()` is permitted **only** for infrastructure/discovery endpoints
|
|
26
|
+
(`/.well-known`, `/health`, `/alive`, service metadata) and during initial scaffolding
|
|
27
|
+
before merge to a protected branch.
|
|
28
|
+
- `AllowAnonymous()` on sensitive-data or mutating endpoints in any CI-protected branch is a
|
|
29
|
+
Forbidden Pattern (CI grep gate; see [[coding-standards]]).
|
|
30
|
+
|
|
31
|
+
## Canonical example (agent reference — `<SampleProjectName>` sample)
|
|
32
|
+
|
|
33
|
+
Secure endpoint — declare auth scheme + roles in `Configure()`, and the security/cache policy
|
|
34
|
+
in `GetSecurityCachePolicy()`:
|
|
35
|
+
|
|
36
|
+
```csharp
|
|
37
|
+
public override void Configure()
|
|
38
|
+
{
|
|
39
|
+
Post("/projects");
|
|
40
|
+
AuthSchemes("Bearer"); // JWT from the external IdP
|
|
41
|
+
Roles("Admin"); // role-based gate (combine with scope checks for FHIR — see .domain)
|
|
42
|
+
Summary(s => s.Summary = "Create a new project");
|
|
43
|
+
Options(o => o.WithTags("02 - Project - (FastEndpoints)"));
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
protected override SecurityCachePolicy GetSecurityCachePolicy() => new()
|
|
47
|
+
{
|
|
48
|
+
SecurityLevel = SecurityLevel.Internal, // Internal | Restricted (admin/sensitive)
|
|
49
|
+
CachePolicy = CachePolicy.NoStore // NoStore for mutating/sensitive; PrivateCache for cacheable reads
|
|
50
|
+
};
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
> Mutating endpoints use `CachePolicy.NoStore`. Admin/audit endpoints (e.g. list-including-deleted)
|
|
54
|
+
> use `SecurityLevel.Restricted`. `AllowAnonymous()` is only for infrastructure/discovery routes
|
|
55
|
+
> or pre-merge scaffolding (the template ships endpoints scaffolded with `AllowAnonymous()` and
|
|
56
|
+
> the `AuthSchemes`/`Roles` lines commented — replace them before merging to a protected branch).
|
|
57
|
+
|
|
58
|
+
Composition — external IdP only (no local identity store), role-based authorization:
|
|
59
|
+
|
|
60
|
+
```csharp
|
|
61
|
+
// <SampleProjectName>.Service/Program.cs
|
|
62
|
+
builder.Services.AddAppWeaverSecurity(security =>
|
|
63
|
+
{
|
|
64
|
+
security
|
|
65
|
+
.AddAuthentication(auth =>
|
|
66
|
+
{
|
|
67
|
+
if (authProvider == "EntraExternalID")
|
|
68
|
+
auth.UseEntraExternalID(o => { o.TenantId = cfg["EntraExternalID:TenantId"]!;
|
|
69
|
+
o.ClientId = cfg["EntraExternalID:ClientId"]!; }, cfg);
|
|
70
|
+
else
|
|
71
|
+
auth.UseAzureAdB2C(o => { o.Domain = cfg["AzureAdB2C:Domain"]!;
|
|
72
|
+
o.ClientId = cfg["AzureAdB2C:ClientId"]!;
|
|
73
|
+
o.SignUpSignInPolicyId = cfg["AzureAdB2C:SignUpSignInPolicyId"]!; }, cfg);
|
|
74
|
+
})
|
|
75
|
+
.AddAuthorization(authz => authz.UseRoleBased(o => o.AdminRole = "Admin"));
|
|
76
|
+
// NOTE: no .AddIdentity(...) — the IdP is the source of truth (see [[zero-trust]])
|
|
77
|
+
}, options => { }, configuration: cfg);
|
|
78
|
+
|
|
79
|
+
// ... pipeline order ...
|
|
80
|
+
app.UseAuthentication();
|
|
81
|
+
app.UseAuthorization();
|
|
82
|
+
app.UseFastEndpoints();
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
## Related
|
|
86
|
+
|
|
87
|
+
- [[zero-trust]] · [[auditing]] · [[data-classification]] · [[authorization.domain]]
|