aw-spec-cli 0.1.20 → 0.1.21
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/.appweaver/ORCH-DIALECT.md +8 -3
- package/assets/.appweaver/orchestrator/modules/delta-spec.orch.yaml +22 -1
- package/assets/.appweaver/orchestrator/orchestrator.py +65 -3
- package/assets/.appweaver/orchestrator/workflows/aw-spec-analyze-plan-delta.orch.yaml +6 -6
- package/assets/.appweaver/orchestrator/workflows/aw-spec-analyze-plan.orch.yaml +107 -48
- package/assets/.appweaver/orchestrator/workflows/aw-spec-analyze-slice-delta.orch.yaml +17 -9
- package/assets/.appweaver/orchestrator/workflows/aw-spec-analyze-slice.orch.yaml +265 -20
- package/assets/.appweaver/orchestrator/workflows/aw-spec-constitution.orch.yaml +4 -4
- package/assets/.appweaver/orchestrator/workflows/aw-spec-finalize-delta.orch.yaml +89 -15
- package/assets/.appweaver/orchestrator/workflows/aw-spec-foundation.orch.yaml +140 -36
- package/assets/.appweaver/orchestrator/workflows/aw-spec-implement.orch.yaml +354 -52
- package/assets/.appweaver/orchestrator/workflows/aw-spec-onboard.orch.yaml +3 -3
- package/assets/.appweaver/orchestrator/workflows/aw-spec-select-stack.orch.yaml +47 -23
- package/assets/.appweaver/orchestrator/workflows/aw-spec-specify.orch.yaml +1 -1
- package/assets/.appweaver/scripts/bash/aw-spec-actions.sh +651 -76
- package/assets/.appweaver/scripts/powershell/aw-spec-actions.ps1 +600 -59
- package/assets/.appweaver/scripts/python/aw-crstate.py +194 -0
- package/assets/.appweaver/scripts/python/aw-foundation.py +151 -0
- package/assets/.appweaver/scripts/python/aw-payload-parity.py +3 -0
- package/assets/.appweaver/scripts/python/aw-plan-coverage.py +423 -0
- package/assets/.appweaver/scripts/python/aw-stack.py +251 -28
- package/assets/.appweaver/scripts/python/aw-testlog.py +148 -0
- package/assets/.appweaver/stack-profiles/dotnet-webapi.json +1 -0
- package/assets/.appweaver/stack-profiles/java-spring.json +1 -0
- package/assets/.appweaver/stack-profiles/node-express.json +1 -0
- package/assets/.appweaver/stack-profiles/python-fastapi.json +1 -0
- package/assets/.appweaver/templates/constitution-domain-template.md +17 -2
- package/assets/guardrails/projects/dotnet-default-project/domain-rule-bindings.md +10 -6
- package/assets/guardrails/projects/dotnet-default-project/tech-rule-bindings.md +4 -0
- package/assets/guardrails/technology/dotnet-webapi/auditing.md +3 -1
- package/assets/guardrails/technology/dotnet-webapi/coding-standards.md +5 -3
- package/assets/guardrails/technology/dotnet-webapi/cqrs.md +5 -1
- package/assets/guardrails/technology/dotnet-webapi/domain-driven-design.md +13 -5
- package/assets/guardrails/technology/dotnet-webapi/examples/cqrs.md +2 -0
- package/assets/guardrails/technology/dotnet-webapi/examples/domain-driven-design.md +6 -2
- package/assets/guardrails/technology/dotnet-webapi/framework-api-map.md +70 -0
- package/assets/guardrails/technology/dotnet-webapi/technology-constitution.md +3 -0
- package/assets/guardrails/technology/dotnet-webapi/testing.md +10 -0
- package/assets/guardrails/technology/nextjs/ui-constitution.md +8 -1
- package/package.json +1 -1
- package/assets/.appweaver/orchestrator/.state/smoke.state.json +0 -32
|
@@ -26,13 +26,16 @@ through the spec/design (as CN-/DC-/IC- ids).
|
|
|
26
26
|
|
|
27
27
|
## /aw-spec-constitution
|
|
28
28
|
<!-- What the DOMAIN half ratifies into constitution.domain.md as domain principles XVI+.
|
|
29
|
-
The industry constitution first, then the STANDARD industry rules —
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
29
|
+
The industry constitution first, then the STANDARD industry rules — the modelling
|
|
30
|
+
overlay the Adopted Modelling Standard section cites, identity, regulatory, interop,
|
|
31
|
+
security overlays, and the folder overlay the Domain Folder Additions section cites —
|
|
32
|
+
which every project on this corpus shares, and LAST the PROJECT-SUPPLIED
|
|
33
|
+
domain-requirements.md the project team authors. Only that last file is expected to
|
|
34
|
+
differ between two projects on the same corpus, and its mapping is OPTIONAL: the project
|
|
35
|
+
owner supplies the file, and where it is absent the set loads without it rather than
|
|
36
|
+
failing. -->
|
|
35
37
|
- guardrails/domain/healthcare/domain-constitution.md
|
|
38
|
+
- guardrails/domain/healthcare/domain-driven-design.domain.md
|
|
36
39
|
- guardrails/domain/healthcare/clinical-terms.md
|
|
37
40
|
- guardrails/domain/healthcare/terminology.md
|
|
38
41
|
- guardrails/domain/healthcare/consent.md
|
|
@@ -45,6 +48,7 @@ through the spec/design (as CN-/DC-/IC- ids).
|
|
|
45
48
|
- guardrails/domain/healthcare/authorization.domain.md
|
|
46
49
|
- guardrails/domain/healthcare/zero-trust.domain.md
|
|
47
50
|
- guardrails/domain/healthcare/ai-rules.domain.md
|
|
51
|
+
- guardrails/domain/healthcare/folder-structure.domain.md
|
|
48
52
|
- guardrails/projects/dotnet-default-project/domain/domain-requirements.md
|
|
49
53
|
|
|
50
54
|
## /aw-spec-specify
|
|
@@ -141,6 +141,7 @@ NOT listed here — every consumer refers to it directly, as-is.
|
|
|
141
141
|
- guardrails/technology/dotnet-webapi/coding-standards.md
|
|
142
142
|
- guardrails/technology/dotnet-webapi/dependency-policy.md
|
|
143
143
|
- guardrails/technology/dotnet-webapi/folder-structure.md
|
|
144
|
+
- guardrails/technology/dotnet-webapi/framework-api-map.md
|
|
144
145
|
- guardrails/technology/dotnet-webapi/testing.md
|
|
145
146
|
- guardrails/technology/dotnet-webapi/PACKAGING.md
|
|
146
147
|
|
|
@@ -181,6 +182,7 @@ NOT listed here — every consumer refers to it directly, as-is.
|
|
|
181
182
|
- guardrails/technology/dotnet-webapi/coding-standards.md
|
|
182
183
|
- guardrails/technology/dotnet-webapi/dependency-policy.md
|
|
183
184
|
- guardrails/technology/dotnet-webapi/folder-structure.md
|
|
185
|
+
- guardrails/technology/dotnet-webapi/framework-api-map.md
|
|
184
186
|
- guardrails/technology/dotnet-webapi/testing.md
|
|
185
187
|
- guardrails/technology/dotnet-webapi/PACKAGING.md
|
|
186
188
|
|
|
@@ -199,6 +201,7 @@ NOT listed here — every consumer refers to it directly, as-is.
|
|
|
199
201
|
resolve-domain `## /aw-spec-gate`). Validation input only — never authoring. -->
|
|
200
202
|
- guardrails/technology/dotnet-webapi/testing.md
|
|
201
203
|
- guardrails/technology/dotnet-webapi/coding-standards.md
|
|
204
|
+
- guardrails/technology/dotnet-webapi/framework-api-map.md
|
|
202
205
|
|
|
203
206
|
## /aw-spec-constitution
|
|
204
207
|
<!-- What the TECHNICAL half of the split constitution ratifies into
|
|
@@ -214,6 +217,7 @@ NOT listed here — every consumer refers to it directly, as-is.
|
|
|
214
217
|
- guardrails/technology/dotnet-webapi/clean-architecture.md
|
|
215
218
|
- guardrails/technology/dotnet-webapi/modular-monolith.md
|
|
216
219
|
- guardrails/technology/dotnet-webapi/vertical-slices.md
|
|
220
|
+
- guardrails/technology/dotnet-webapi/domain-driven-design.md
|
|
217
221
|
- guardrails/technology/dotnet-webapi/cqrs.md
|
|
218
222
|
- guardrails/technology/dotnet-webapi/persistence.md
|
|
219
223
|
- guardrails/technology/dotnet-webapi/eventing.md
|
|
@@ -51,7 +51,9 @@ implements. The interceptor populates every audit field automatically (no manual
|
|
|
51
51
|
| **Advanced** | Basic markers **+ `IFieldAudited`** | Basic + field-level before/after (`PropertyChange`: original → new) | entity + property-level previous/new |
|
|
52
52
|
|
|
53
53
|
Field-level before/after values exist **only** at the Advanced tier. Children marked
|
|
54
|
-
`IAggregateChild`
|
|
54
|
+
`IAggregateChild` (`AppWeaver.Auditing.Interfaces`, package `AppWeaver.Auditing` —
|
|
55
|
+
[[framework-api-map]]) fold into the root's `AuditLog`; an unmarked child gets a standalone
|
|
56
|
+
entry instead, which is the silent failure this marker exists to prevent.
|
|
55
57
|
|
|
56
58
|
## Canonical example
|
|
57
59
|
|
|
@@ -7,9 +7,11 @@
|
|
|
7
7
|
- Solution and projects: `<SampleProjectName>.<Role>` (e.g. `<SampleProjectName>.Api`,
|
|
8
8
|
`<SampleProjectName>.AppHost`, `<SampleProjectName>.ServiceDefaults`).
|
|
9
9
|
- Namespaces mirror the folder path exactly; **file-scoped namespaces only**.
|
|
10
|
-
- Aggregates: `class <Aggregate> :
|
|
11
|
-
|
|
12
|
-
|
|
10
|
+
- Aggregates: `class <Aggregate> : AggregateRootBase<<Aggregate>Id>, IFullAuditedObject,
|
|
11
|
+
ISoftDeletable, IConcurrencyTracked, INamedEntity<<Aggregate>Id>` —
|
|
12
|
+
`AggregateRootBase<TId>` already carries `EntityBase<TId>`, `IAggregateRoot` and
|
|
13
|
+
`IHasDomainEvents`. Child entities take `EntityBase<TId>` plus `IAggregateChild`.
|
|
14
|
+
Packages and namespaces: [[framework-api-map]].
|
|
13
15
|
- Commands/Queries: `record <Verb><Aggregate>Command(...) : ICommand<Result<TDto>>`.
|
|
14
16
|
- DTOs: `record <Aggregate>Dto { public required Guid Id { get; init; } ... }` — no setters;
|
|
15
17
|
required init for non-nullable members.
|
|
@@ -47,7 +47,11 @@ Enabled at startup, in order:
|
|
|
47
47
|
|
|
48
48
|
- Write handlers **MUST** wrap mutating work in
|
|
49
49
|
`IUnitOfWork.ExecuteInTransactionAsync(..., IsolationLevel.ReadCommitted, ct)` and call
|
|
50
|
-
`SaveChangesAsync` **inside** the lambda.
|
|
50
|
+
`SaveChangesAsync` **inside** the lambda. That method is an extension in
|
|
51
|
+
`AppWeaver.Repository.Extensions` over `AppWeaver.Repository.Abstractions.IUnitOfWork` —
|
|
52
|
+
add both usings. It does NOT extend the `AppWeaver.SharedKernel` interface of the same
|
|
53
|
+
name, and injecting that one is why the method can appear not to exist
|
|
54
|
+
([[framework-api-map]]).
|
|
51
55
|
- They **MUST** publish domain events and call `ClearDomainEvents()` before returning.
|
|
52
56
|
- **One command mutates one aggregate per transaction** — cross-aggregate effects flow
|
|
53
57
|
through domain events or follow-up commands (see [[eventing]]).
|
|
@@ -15,10 +15,14 @@
|
|
|
15
15
|
## Aggregate design
|
|
16
16
|
|
|
17
17
|
- Every entity exposed to the Application layer is reached through an **Aggregate Root**.
|
|
18
|
-
- Aggregate roots
|
|
19
|
-
|
|
20
|
-
`
|
|
21
|
-
|
|
18
|
+
- Aggregate roots derive from `AggregateRootBase<TId>`
|
|
19
|
+
(`AppWeaver.DomainAbstraction.Entities`), which already carries `EntityBase<TId>`,
|
|
20
|
+
`IAggregateRoot` and domain-event support — do not reassemble those by hand. They add the
|
|
21
|
+
audit/soft-delete/concurrency interfaces they need: `IFullAuditedObject`
|
|
22
|
+
(`AppWeaver.Auditing.Interfaces`) or `IAuditableEntityBase`, `ISoftDeletable`,
|
|
23
|
+
`IConcurrencyTracked` (`AppWeaver.DomainAbstraction.Entities`), and `INamedEntity<TId>`
|
|
24
|
+
when listable. Every package and namespace is in [[framework-api-map]]; a type that seems
|
|
25
|
+
to be missing is a missing package reference, not an absent API.
|
|
22
26
|
|
|
23
27
|
### Aggregate root responsibilities
|
|
24
28
|
|
|
@@ -28,7 +32,11 @@
|
|
|
28
32
|
|
|
29
33
|
### Child entity ownership
|
|
30
34
|
|
|
31
|
-
- Child entities
|
|
35
|
+
- Child entities derive from `EntityBase<TId>` (`AppWeaver.DomainAbstraction.Entities`) and
|
|
36
|
+
implement `IAggregateChild` (`AppWeaver.Auditing.Interfaces` — a DIFFERENT package from the
|
|
37
|
+
aggregate types, see [[framework-api-map]]), returning the in-memory root instance from
|
|
38
|
+
`GetAggregateRoot()`. That linkage is what folds the child's changes into the root's audit
|
|
39
|
+
entry instead of leaving a standalone one.
|
|
32
40
|
- They are mutated **only** through methods on the aggregate root.
|
|
33
41
|
|
|
34
42
|
## Transactional / consistency boundaries
|
|
@@ -21,6 +21,8 @@ public record CreateProjectCommand(
|
|
|
21
21
|
) : ICommand<Result<ProjectDto>>;
|
|
22
22
|
|
|
23
23
|
// Write handler — primary-ctor DI, transaction, publish + clear events, Result<T>
|
|
24
|
+
// using AppWeaver.Repository.Abstractions; // IRepository<T>, IUnitOfWork
|
|
25
|
+
// using AppWeaver.Repository.Extensions; // ExecuteInTransactionAsync — the extension below
|
|
24
26
|
public class CreateProjectCommandHandler(
|
|
25
27
|
IRepository<Project> _repo, IUnitOfWork _uow, IPublisher _eventBus
|
|
26
28
|
) : ICommandHandler<CreateProjectCommand, Result<ProjectDto>>
|
|
@@ -19,9 +19,11 @@ domain events, and an invariant enforced **inside** the aggregate:
|
|
|
19
19
|
[ValueObject<Guid>]
|
|
20
20
|
public partial struct ProjectId { }
|
|
21
21
|
|
|
22
|
+
// using AppWeaver.DomainAbstraction.Entities; // AggregateRootBase, ISoftDeletable, IConcurrencyTracked
|
|
23
|
+
// using AppWeaver.Auditing.Interfaces; // IFullAuditedObject, IFieldAudited (package AppWeaver.Auditing)
|
|
22
24
|
public class Project :
|
|
23
|
-
|
|
24
|
-
ISoftDeletable, IConcurrencyTracked,
|
|
25
|
+
AggregateRootBase<ProjectId>, IFullAuditedObject, IFieldAudited,
|
|
26
|
+
ISoftDeletable, IConcurrencyTracked, INamedEntity<ProjectId>
|
|
25
27
|
{
|
|
26
28
|
private readonly List<ProjectTask> _tasks = new();
|
|
27
29
|
|
|
@@ -100,6 +102,8 @@ setters, and is mutated **only** through methods the parent aggregate calls:
|
|
|
100
102
|
[ValueObject<Guid>]
|
|
101
103
|
public partial struct ProjectTaskId { }
|
|
102
104
|
|
|
105
|
+
// using AppWeaver.DomainAbstraction.Entities; // EntityBase
|
|
106
|
+
// using AppWeaver.Auditing.Interfaces; // IAggregateChild (package AppWeaver.Auditing)
|
|
103
107
|
public class ProjectTask : EntityBase<ProjectTaskId>, IAggregateChild
|
|
104
108
|
{
|
|
105
109
|
public ProjectId ProjectId { get; private set; } // FK back to the root
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
# Framework API Map — which package and namespace each cited type comes from
|
|
2
|
+
|
|
3
|
+
> Pure technology (domain-agnostic). NON-NEGOTIABLE where it names a package.
|
|
4
|
+
> Commands: `/aw-spec-implement`, `/aw-spec-implement-delta`, `/aw-spec-analyze-slice`.
|
|
5
|
+
>
|
|
6
|
+
> The rules in this corpus name framework types — `AggregateRootBase<TId>`,
|
|
7
|
+
> `IAggregateChild`, `IUnitOfWork`. This file says where each one LIVES. Without it a
|
|
8
|
+
> generator searches for a bare type name, finds a same-named type in another package or
|
|
9
|
+
> none at all, and concludes the API does not exist — then hand-rolls a substitute or
|
|
10
|
+
> silently drops the rule that required it.
|
|
11
|
+
|
|
12
|
+
## The trap this file exists for
|
|
13
|
+
|
|
14
|
+
**Several of these names exist TWICE**, once in `AppWeaver.SharedKernel` and once in the
|
|
15
|
+
split packages (`AppWeaver.DomainAbstraction`, `AppWeaver.Repository`, `AppWeaver.Auditing`).
|
|
16
|
+
The two sets are not interchangeable:
|
|
17
|
+
|
|
18
|
+
- `AppWeaver.Repository.Abstractions.IUnitOfWork` takes an isolation level on
|
|
19
|
+
`BeginTransactionAsync` and is what `ExecuteInTransactionAsync` extends.
|
|
20
|
+
`AppWeaver.SharedKernel.UnitOfWork.IUnitOfWork` does neither — inject that one and the
|
|
21
|
+
transaction helper is not in scope, which reads exactly like "the method does not exist".
|
|
22
|
+
- `EntityBase<TId>`, `IAggregateRoot`, `DomainEventBase`, `ISoftDeletable` and the other
|
|
23
|
+
entity interfaces have SharedKernel twins in the same shape.
|
|
24
|
+
|
|
25
|
+
**Use the split packages.** They are what the shipped foundation references; SharedKernel is
|
|
26
|
+
not referenced by it. Never import a SharedKernel namespace into generated code, and never
|
|
27
|
+
mix the two sets in one type.
|
|
28
|
+
|
|
29
|
+
## The map
|
|
30
|
+
|
|
31
|
+
| Type | Package | Namespace |
|
|
32
|
+
|---|---|---|
|
|
33
|
+
| `AggregateRootBase<TId>` | `AppWeaver.DomainAbstraction` | `AppWeaver.DomainAbstraction.Entities` |
|
|
34
|
+
| `EntityBase<TId>` | `AppWeaver.DomainAbstraction` | `AppWeaver.DomainAbstraction.Entities` |
|
|
35
|
+
| `IEntity<TId>` | `AppWeaver.DomainAbstraction` | `AppWeaver.DomainAbstraction.Entities` |
|
|
36
|
+
| `IAuditableEntityBase`, `ISoftDeletable`, `IConcurrencyTracked` | `AppWeaver.DomainAbstraction` | `AppWeaver.DomainAbstraction.Entities` |
|
|
37
|
+
| `IAggregateRoot` | `AppWeaver.DomainAbstraction` | `AppWeaver.DomainAbstraction.Aggregates` |
|
|
38
|
+
| `DomainEventBase`, `IHasDomainEvents` | `AppWeaver.DomainAbstraction` | `AppWeaver.DomainAbstraction.DomainEvents` |
|
|
39
|
+
| `IAggregateChild`, `IFullAuditedObject`, `IFieldAudited` | **`AppWeaver.Auditing`** | `AppWeaver.Auditing.Interfaces` |
|
|
40
|
+
| `IUnitOfWork`, `IRepository<T>`, `IReadRepository<T>` | `AppWeaver.Repository` | `AppWeaver.Repository.Abstractions` |
|
|
41
|
+
| `ExecuteInTransactionAsync(...)` (extension on `IUnitOfWork`) | `AppWeaver.Repository` | `AppWeaver.Repository.Extensions` |
|
|
42
|
+
| `ContextAwareDbContext<T>`, `EfRepository<T>` | `AppWeaver.Repository.EfCore` | `AppWeaver.Repository.EfCore` |
|
|
43
|
+
| `INamedEntity`, `INamedEntity<TId>` | `AppWeaver.DomainAbstraction` | `AppWeaver.DomainAbstraction.Entities` |
|
|
44
|
+
| `IAuditedObject` | `AppWeaver.Auditing` | `AppWeaver.Auditing.Interfaces` |
|
|
45
|
+
| `Result`, `Result<T>` | `AppWeaver.Results` | `AppWeaver.Results` |
|
|
46
|
+
| `ICommand<T>`, `ICommandHandler<TCommand, TResponse>` | `AppWeaver.Mediator` | `AppWeaver.Mediator.Interfaces` |
|
|
47
|
+
| `IPublisher` | `AppWeaver.EventBus` | `AppWeaver.EventBus` |
|
|
48
|
+
|
|
49
|
+
The auditing interfaces are the ones most often reported missing: they are in a DIFFERENT
|
|
50
|
+
package from the aggregate types, so a project that references `AppWeaver.DomainAbstraction`
|
|
51
|
+
alone cannot see `IAggregateChild` at all. A rule that requires one requires its package.
|
|
52
|
+
|
|
53
|
+
## Rules
|
|
54
|
+
|
|
55
|
+
- **Cite a framework type with its namespace** in plans and generated code, and add the
|
|
56
|
+
`using` for the namespace in the table above — not a guess from the type's name.
|
|
57
|
+
- **A missing type is a MISSING PACKAGE REFERENCE, not an absent API.** Before concluding
|
|
58
|
+
a rule cannot be followed, check this table and the project's package references. Say so
|
|
59
|
+
in the run's summary rather than hand-rolling a substitute — a hand-rolled
|
|
60
|
+
`IAggregateChild` gives the auditing interceptor nothing to hook.
|
|
61
|
+
- **`AggregateRootBase<TId>` is the base for an aggregate root.** It already combines
|
|
62
|
+
`EntityBase<TId>`, `IAggregateRoot` and domain-event support
|
|
63
|
+
(`RegisterDomainEvent` / `DomainEvents` / `ClearDomainEvents`). Assembling those by hand
|
|
64
|
+
duplicates what the package ships. `TId` is `where TId : struct, IEquatable<TId>`, which
|
|
65
|
+
the `[ValueObject<Guid>]` partial-struct id satisfies.
|
|
66
|
+
- **`EntityBase<TId>` is the base for a CHILD entity**, which adds `IAggregateChild`.
|
|
67
|
+
|
|
68
|
+
## Related
|
|
69
|
+
|
|
70
|
+
- [[domain-driven-design]] · [[coding-standards]] · [[cqrs]] · [[auditing]] · [[persistence]]
|
|
@@ -124,6 +124,9 @@ Governed in full by the technical rule files; this file does not duplicate them.
|
|
|
124
124
|
`guardrails/technology/dotnet-webapi/coding-standards.md`
|
|
125
125
|
- Package governance (approved/banned packages, supply-chain, licensing):
|
|
126
126
|
`guardrails/technology/dotnet-webapi/dependency-policy.md`
|
|
127
|
+
- Which package and namespace each framework type cited by these rules comes from, and the
|
|
128
|
+
same-named types that must NOT be mixed:
|
|
129
|
+
`guardrails/technology/dotnet-webapi/framework-api-map.md`
|
|
127
130
|
- AI operational rules (generation, context loading, refusal conditions):
|
|
128
131
|
`.appweaver/ai-rules.md` — the domain half extends this with domain-specific masking,
|
|
129
132
|
code-set and refusal rules
|
|
@@ -38,6 +38,16 @@ Required for **every** Command/Query handler and MUST cover:
|
|
|
38
38
|
- Integration and contract tests spin up real PostgreSQL (and other backing services)
|
|
39
39
|
via Testcontainers / Aspire — no faked persistence in integration tests.
|
|
40
40
|
|
|
41
|
+
## Container validation
|
|
42
|
+
|
|
43
|
+
- One test per service project builds the application's service provider with
|
|
44
|
+
`ValidateOnBuild` and `ValidateScopes` enabled and asserts it succeeds — the registrations
|
|
45
|
+
the app runs with, not a subset re-declared in the test.
|
|
46
|
+
- Every handler, endpoint and adapter the app registers MUST resolve from it. A dependency
|
|
47
|
+
that is injected and never bound compiles green, because a container graph is not
|
|
48
|
+
compile-checked, and fails on the first request that needs it. This test is what turns
|
|
49
|
+
that into a red `dotnet test`.
|
|
50
|
+
|
|
41
51
|
## Test data hygiene
|
|
42
52
|
|
|
43
53
|
- Tests use a **shared fixture library** rather than inlining fabricated data.
|
|
@@ -83,6 +83,13 @@ Every route, form and interaction state ships with tests: component behaviour, c
|
|
|
83
83
|
integration against the generated types, and an accessibility assertion per interactive surface.
|
|
84
84
|
Snapshot-only tests do not count as coverage.
|
|
85
85
|
|
|
86
|
+
**The runner is wired up, not assumed.** The lane's `package.json` defines the `test` script
|
|
87
|
+
its declared command runs (`npm test` unless `.appweaver/stacks.json` says otherwise), pointing
|
|
88
|
+
at the runner the tests are written for. A scaffold ships without one — `create-next-app`
|
|
89
|
+
defines no `test` script — so the first feature that writes a test installs the runner and adds
|
|
90
|
+
the script in the same pass. A lane whose declared test command has nothing to run is not a
|
|
91
|
+
passing lane: `analyze-slice` reports it as untested and fails the gate.
|
|
92
|
+
|
|
86
93
|
## Where Each Principle Is Detailed
|
|
87
94
|
|
|
88
95
|
This file is the thin authority; the enforceable rules live in the corpus beside it. Each
|
|
@@ -125,7 +132,7 @@ web/
|
|
|
125
132
|
lib/api/ # the GENERATED contract client + typed wrappers (never hand-edited)
|
|
126
133
|
lib/ # cross-feature client utilities
|
|
127
134
|
locales/ # locale catalogues
|
|
128
|
-
tests/ # component, integration and accessibility tests
|
|
135
|
+
tests/ # component, integration and accessibility tests — see "Test runner"
|
|
129
136
|
```
|
|
130
137
|
|
|
131
138
|
Generated contract clients land in `lib/api/` and are regenerated, never patched. A feature
|
package/package.json
CHANGED
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"vars": {},
|
|
3
|
-
"strategy": "agent",
|
|
4
|
-
"model": "",
|
|
5
|
-
"input": "",
|
|
6
|
-
"answers": {},
|
|
7
|
-
"selects": {},
|
|
8
|
-
"agent_profile": "",
|
|
9
|
-
"isolation": "bare",
|
|
10
|
-
"os": "posix",
|
|
11
|
-
"cache": {
|
|
12
|
-
"script_ro": {
|
|
13
|
-
"domains": [
|
|
14
|
-
"dotnet-default-project"
|
|
15
|
-
]
|
|
16
|
-
}
|
|
17
|
-
},
|
|
18
|
-
"pending": {
|
|
19
|
-
"kind": "ask",
|
|
20
|
-
"key": "pick_mode",
|
|
21
|
-
"step": "pick_mode",
|
|
22
|
-
"prompt": "Smoke mode?",
|
|
23
|
-
"options": [
|
|
24
|
-
"fast",
|
|
25
|
-
"slow"
|
|
26
|
-
],
|
|
27
|
-
"multi": false,
|
|
28
|
-
"instructions": "Ask the USER this question (the options are the allowed choices). Write the answer as PLAIN TEXT to response_path, then re-run the resume command.",
|
|
29
|
-
"response_path": ".appweaver/orchestrator/.state/smoke.state.json.response.md",
|
|
30
|
-
"resume": "python3 .appweaver/orchestrator/orchestrator.py .appweaver/orchestrator/workflows/_smoke.orch.yaml --checkpoint .appweaver/orchestrator/.state/smoke.state.json --json"
|
|
31
|
-
}
|
|
32
|
-
}
|