@polymorphism-tech/morph-spec 8.45.0 → 8.47.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CLAUDE.md +6 -5
- package/README.md +2 -2
- package/bin/morph-spec.js +2 -4
- package/bin/validate.js +4 -25
- package/claude-plugin.json +1 -1
- package/docs/CHEATSHEET.md +2 -2
- package/docs/QUICKSTART.md +1 -1
- package/framework/CLAUDE.md +2 -2
- package/framework/MORPH.md +9 -9
- package/framework/commands/create-umbraco-lp.md +2 -0
- package/framework/commands/migrate-lp-to-umbraco.md +2 -0
- package/framework/commands/morph-archive.md +1 -1
- package/framework/commands/morph-hotfix.md +1 -1
- package/framework/commands/morph-infra.md +17 -203
- package/framework/commands/morph-media.md +3 -0
- package/framework/commands/morph-preflight.md +5 -28
- package/framework/commands/morph-proposal.md +2 -2
- package/framework/commands/morph-troubleshoot.md +2 -14
- package/framework/docs/CLI.md +8 -7
- package/framework/hooks/claude-code/core/state-sync.js +13 -1
- package/framework/hooks/shared/e2e-check.js +3 -4
- package/framework/hooks/shared/state-writer.js +1 -0
- package/framework/rules/ai-agents-standards.md +18 -16
- package/framework/rules/asaas-standards.md +3 -1
- package/framework/rules/csharp-standards.md +10 -8
- package/framework/rules/data-standards.md +5 -5
- package/framework/rules/dotnet-templates.md +1 -1
- package/framework/rules/frontend-standards.md +4 -2
- package/framework/rules/gsap-standards.md +10 -5
- package/framework/rules/hangfire-standards.md +3 -1
- package/framework/rules/infrastructure-standards.md +4 -2
- package/framework/rules/morph-workflow.md +2 -2
- package/framework/rules/neon-auth-standards.md +3 -1
- package/framework/rules/neon-standards.md +6 -4
- package/framework/rules/nextjs-standards.md +13 -11
- package/framework/rules/nextjs-templates.md +1 -1
- package/framework/rules/resend-standards.md +3 -1
- package/framework/schemas/config.schema.json +4 -0
- package/framework/schemas/feature.schema.json +4 -4
- package/framework/skills/README.md +1 -1
- package/framework/skills/core/create-umbraco-lp/SKILL.md +3 -3
- package/framework/skills/core/migrate-lp-to-umbraco/SKILL.md +3 -3
- package/framework/skills/core/morph-apply/SKILL.md +3 -3
- package/framework/skills/core/morph-brainstorming/SKILL.md +3 -3
- package/framework/skills/core/morph-hotfix/SKILL.md +4 -4
- package/framework/skills/core/morph-implement/SKILL.md +3 -3
- package/framework/skills/core/morph-machine/SKILL.md +3 -3
- package/framework/skills/core/morph-media/SKILL.md +3 -3
- package/framework/skills/core/morph-plan/SKILL.md +3 -3
- package/framework/skills/core/morph-proposal/SKILL.md +3 -3
- package/framework/skills/core/morph-review/SKILL.md +30 -17
- package/framework/skills/core/morph-stack-scan/SKILL.md +3 -3
- package/framework/skills/core/morph-standards/SKILL.md +6 -6
- package/framework/skills/core/morph-uiux/SKILL.md +3 -3
- package/framework/standards/STANDARDS.json +59 -56
- package/framework/standards/ai-agents/evals-with-cache.md +1 -1
- package/framework/standards/ai-agents/llm-runtime-defaults.md +1 -1
- package/framework/standards/ai-agents/modalities-speech-to-text.md +36 -26
- package/framework/standards/ai-agents/prompt-sources.md +6 -4
- package/framework/standards/ai-agents/providers/model-registry.md +1 -1
- package/framework/standards/ai-agents/rag-custom-pgvector.md +67 -39
- package/framework/standards/ai-agents/setup.md +1 -1
- package/framework/standards/ai-agents/structured-output.md +1 -1
- package/framework/standards/ai-agents/sweet-spot.md +1 -1
- package/framework/standards/architecture/code-comments.md +2 -2
- package/framework/standards/architecture/vertical-slice/vertical-slice.md +18 -9
- package/framework/standards/backend/api/validation.md +135 -47
- package/framework/standards/backend/database/postgresql/database.md +11 -7
- package/framework/standards/backend/database/vector-search-rag.md +207 -324
- package/framework/standards/backend/dotnet/fluent-validation-vsa.md +14 -8
- package/framework/standards/backend/dotnet/result-pattern.md +40 -6
- package/framework/standards/backend/dotnet/scalar-ui.md +13 -7
- package/framework/standards/backend/dotnet/scrutor.md +1 -1
- package/framework/standards/backend/dotnet/testing.md +1 -1
- package/framework/standards/backend/dotnet/vsa-handler-patterns.md +7 -7
- package/framework/standards/backend/integrations/asaas/asaas-api.md +309 -165
- package/framework/standards/backend/integrations/hangfire/hangfire-jobs.md +134 -154
- package/framework/standards/data/nosql/cache/redis.md +120 -44
- package/framework/standards/data/nosql/cosmos-db.md +114 -71
- package/framework/standards/frontend/design-system/animations.md +1 -1
- package/framework/standards/frontend/design-system/naming.md +1 -1
- package/framework/standards/frontend/nextjs/motion-patterns.md +45 -29
- package/framework/standards/frontend/nextjs/naming-conventions.md +1 -1
- package/framework/standards/infrastructure/docker/coolify-deploy.md +8 -6
- package/framework/standards/infrastructure/neon/neon-pgvector.md +120 -75
- package/framework/standards/infrastructure/neon/neon-setup.md +53 -31
- package/framework/templates/code/dotnet/contracts/contracts-vsa.cs +2 -2
- package/framework/templates/code/dotnet/extensions/database-extensions.cs +1 -1
- package/framework/templates/code/dotnet/extensions/result-extensions.cs +27 -0
- package/framework/templates/code/dotnet/feature/endpoint.cs.hbs +2 -2
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/MediaDisclosureTests.cs +1 -1
- package/framework/templates/infrastructure/docker/coolify.template.json +3 -3
- package/framework/templates/infrastructure/github/README.md +2 -1
- package/framework/templates/infrastructure/github/workflows/deploy-coolify.yml.hbs +2 -1
- package/package.json +1 -1
- package/src/commands/observability/telemetry.js +3 -5
- package/src/commands/project/init.js +2 -1
- package/src/commands/project/update.js +16 -18
- package/src/commands/state/approve.js +17 -0
- package/src/commands/state/gate-check.js +4 -14
- package/src/commands/validation/verify.js +99 -57
- package/src/core/paths/output-schema.js +1 -1
- package/src/dashboard/app/App.jsx +0 -2
- package/src/dashboard/app/components/CommandPalette.jsx +2 -3
- package/src/dashboard/app/components/EntityLink.jsx +0 -1
- package/src/dashboard/app/components/Markdown.jsx +1 -1
- package/src/dashboard/app/components/Sidebar.jsx +1 -2
- package/src/dashboard/app/pages/RulesMap.jsx +3 -3
- package/src/dashboard/app/pages/SkillsLibrary.jsx +3 -3
- package/src/dashboard/scanner/activity-scanner.js +1 -1
- package/src/dashboard/scanner/docs-scanner.js +5 -23
- package/src/dashboard/scanner/features-scanner.js +1 -1
- package/src/dashboard/scanner/index.js +0 -2
- package/src/dashboard/scanner/rules-scanner.js +15 -10
- package/src/dashboard/scanner/skills-scanner.js +2 -3
- package/src/lib/ci/ci-verify.js +118 -0
- package/src/lib/ci/github-ci.js +192 -0
- package/src/lib/ci/workflow-scaffold.js +122 -0
- package/src/lib/evaluator-report.js +13 -0
- package/src/lib/risk-detector.js +8 -0
- package/src/lib/stack/stack-profile.js +1 -1
- package/src/lib/telemetry/verify-event.js +3 -2
- package/src/lib/validators/reuse/reuse-validator.js +2 -3
- package/src/lib/validators/reuse/symbol-scanner.js +2 -4
- package/src/lib/validators/validation-runner.js +8 -88
- package/src/lib/validators/verify-runner.js +22 -72
- package/src/scripts/setup-infra.js +33 -22
- package/src/utils/agents-installer.js +20 -8
- package/src/utils/file-copier.js +7 -5
- package/framework/commands/commit.md +0 -166
- package/framework/hooks/claude-code/__pycache__/statusline.cpython-313.pyc +0 -0
- package/framework/rubrics/backend.json +0 -41
- package/framework/rubrics/default.json +0 -36
- package/framework/rubrics/frontend.json +0 -36
- package/framework/templates/project/validate.js +0 -97
- package/src/dashboard/app/pages/Rubrics.jsx +0 -97
- package/src/dashboard/scanner/rubrics-scanner.js +0 -12
- package/src/lib/validators/architecture/architecture-validator.js +0 -270
- package/src/lib/validators/architecture/index.js +0 -7
- package/src/lib/validators/css/css-validator.js +0 -340
- package/src/lib/validators/css/index.js +0 -7
- package/src/lib/validators/design-system/design-system-validator.js +0 -237
- package/src/lib/validators/design-system/index.js +0 -7
- package/src/lib/validators/score-aggregator.js +0 -244
- package/src/lib/validators/ui/index.js +0 -7
- package/src/lib/validators/ui/ui-contrast-validator.js +0 -422
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
# FluentValidation in VSA Pipelines
|
|
2
2
|
|
|
3
3
|
> **Scope:** dotnet-vsa
|
|
4
|
-
> **Layer:** 0 (
|
|
4
|
+
> **Layer:** 0 (resumo injetado pela rule `csharp-standards` ao editar `**/*.cs`/`**/*.csproj`; texto completo sob demanda via `morph-spec standards --find`)
|
|
5
5
|
> **Keywords:** fluent-validation, validation, decorator, pipeline, vsa
|
|
6
6
|
> **Load When:** dotnet VSA projects
|
|
7
7
|
|
|
8
|
-
**Verified against:** .NET 10 + FluentValidation
|
|
8
|
+
**Verified against:** .NET 10 (SDK 10.0.400-preview) + FluentValidation 12.1.1. The validator block and the decorator (its three blocks assembled into one class) compile with `TreatWarningsAsErrors` over the `Result`/`ValidationError` of `result-pattern.md`, and the validator was run with `FakeTimeProvider` across a year change to show the year is read per validation. Last-verified: 2026-10-03.
|
|
9
9
|
|
|
10
10
|
---
|
|
11
11
|
|
|
@@ -21,11 +21,10 @@ One validator per request DTO. Lives in the same feature folder as the handler:
|
|
|
21
21
|
|
|
22
22
|
```csharp
|
|
23
23
|
// Features/BookFeature/CreateBook/CreateBookValidator.cs
|
|
24
|
-
|
|
24
|
+
using FluentValidation;
|
|
25
|
+
|
|
26
|
+
public sealed class CreateBookValidator : AbstractValidator<CreateBookRequest>
|
|
25
27
|
{
|
|
26
|
-
// TimeProvider injected, never DateTime.UtcNow read directly — see
|
|
27
|
-
// backend-dotnet-testing (Rule 1): a validator that reads the real clock
|
|
28
|
-
// cannot be tested deterministically with FakeTimeProvider.
|
|
29
28
|
public CreateBookValidator(TimeProvider timeProvider)
|
|
30
29
|
{
|
|
31
30
|
RuleFor(c => c.Title)
|
|
@@ -44,12 +43,16 @@ public class CreateBookValidator : AbstractValidator<CreateBookRequest>
|
|
|
44
43
|
|
|
45
44
|
RuleFor(c => c.PublishedYear)
|
|
46
45
|
.GreaterThan(1000).WithMessage("Published year must be a valid year")
|
|
47
|
-
.LessThanOrEqualTo(timeProvider.GetUtcNow().Year)
|
|
46
|
+
.LessThanOrEqualTo(_ => timeProvider.GetUtcNow().Year)
|
|
48
47
|
.WithMessage("Published year cannot be in the future");
|
|
49
48
|
}
|
|
50
49
|
}
|
|
51
50
|
```
|
|
52
51
|
|
|
52
|
+
- `sealed`, like every class in the slice.
|
|
53
|
+
- The clock is `TimeProvider` (never `DateTime.UtcNow`), so `FakeTimeProvider` makes the rule testable (`backend-dotnet-testing`, Rule 1).
|
|
54
|
+
- The year is read **inside the rule** (`_ => timeProvider.GetUtcNow().Year`), on every validation. `AddValidatorsFromAssembly` registers validators as scoped by default, but a validator registered as singleton (or cached) that captured `GetUtcNow().Year` in the constructor would keep rejecting next year's books from 1 January on.
|
|
55
|
+
|
|
53
56
|
---
|
|
54
57
|
|
|
55
58
|
## Auto-Discovery
|
|
@@ -69,6 +72,9 @@ builder.Services.AddHandlersFromAssembly(typeof(Program).Assembly);
|
|
|
69
72
|
The decorator wraps every `IHandler<TRequest, TResponse>` via Scrutor. It collects all `IValidator<TRequest>` from DI, runs them in parallel, and short-circuits on failure:
|
|
70
73
|
|
|
71
74
|
```csharp
|
|
75
|
+
using FluentValidation;
|
|
76
|
+
using FluentValidation.Results;
|
|
77
|
+
|
|
72
78
|
public sealed class ValidationDecorator<TRequest, TResponse>(
|
|
73
79
|
IEnumerable<IValidator<TRequest>> validators,
|
|
74
80
|
IHandler<TRequest, TResponse> innerHandler) : IHandler<TRequest, TResponse>
|
|
@@ -194,7 +200,7 @@ Features/
|
|
|
194
200
|
|
|
195
201
|
| Do NOT | Do Instead |
|
|
196
202
|
|--------|------------|
|
|
197
|
-
| Validate inside the handler | Use `AbstractValidator<T>` + decorator |
|
|
203
|
+
| Validate request shape inside the handler | Use `AbstractValidator<T>` + decorator (state/existence checks do belong in the handler -- `backend/api/validation.md`) |
|
|
198
204
|
| Throw `ValidationException` | Return `Result.Failure(ValidationError)` |
|
|
199
205
|
| Register validators after handlers | Register validators first: `AddValidatorsFromAssembly()` before `AddHandlersFromAssembly()` |
|
|
200
206
|
| Put validation logic in endpoints | Endpoints only do `Match()` — validation is automatic |
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
# Result Pattern — Railway-Oriented Programming
|
|
2
2
|
|
|
3
3
|
> **Scope:** dotnet-vsa
|
|
4
|
-
> **Layer:** 0 (
|
|
4
|
+
> **Layer:** 0 (resumo injetado pela rule `csharp-standards` ao editar `**/*.cs`/`**/*.csproj`; texto completo sob demanda via `morph-spec standards --find`)
|
|
5
5
|
> **Keywords:** result, error-handling, railway, match, monadic
|
|
6
6
|
> **Load When:** dotnet VSA projects
|
|
7
7
|
|
|
8
|
-
**Verified against:** .NET 10 (
|
|
8
|
+
**Verified against:** .NET 10 (SDK 10.0.400-preview) + the shared framework `Microsoft.AspNetCore.App` 10 — the HTTP mapping uses `Results`/`IResult`/`StatusCodes`; the `Error`, `ValidationError`, `ResultExtensions`, `ErrorHttpMapping`, endpoint usage and `BookErrors` blocks compile with `TreatWarningsAsErrors` (the Anatomy blocks carry `{ ... }` placeholders and were compiled through a concrete stand-in). Last-verified: 2026-10-03.
|
|
9
9
|
|
|
10
10
|
---
|
|
11
11
|
|
|
@@ -118,6 +118,7 @@ public enum ErrorType
|
|
|
118
118
|
| `NotFound` | 404 | Entity lookup returned null |
|
|
119
119
|
| `Unauthorized` | 401 | Missing or invalid auth token |
|
|
120
120
|
| `Forbidden` | 403 | Authenticated but lacks permission |
|
|
121
|
+
| `Custom` | 400 | Domain-specific; a feature that needs another status maps it in its own endpoint |
|
|
121
122
|
|
|
122
123
|
---
|
|
123
124
|
|
|
@@ -145,7 +146,7 @@ public sealed record ValidationError : Error
|
|
|
145
146
|
|
|
146
147
|
## Match() Pattern
|
|
147
148
|
|
|
148
|
-
Endpoints use `Match()` to convert `Result<T>` into HTTP responses — never access `.Value` directly:
|
|
149
|
+
Endpoints use `Match()` to convert `Result<T>` into HTTP responses — never access `.Value` directly (the success lambda receives the value; `Match` itself is the only reader of `.Value`):
|
|
149
150
|
|
|
150
151
|
```csharp
|
|
151
152
|
public static class ResultExtensions
|
|
@@ -170,15 +171,48 @@ public static class ResultExtensions
|
|
|
170
171
|
}
|
|
171
172
|
```
|
|
172
173
|
|
|
174
|
+
The failure side maps by `ErrorType`, following the table above — never `Results.BadRequest` for every error, which turns a missing entity into a 400 and a conflict into a 400:
|
|
175
|
+
|
|
176
|
+
```csharp
|
|
177
|
+
public static class ErrorHttpMapping
|
|
178
|
+
{
|
|
179
|
+
public static int StatusCodeFor(ErrorType type) => type switch
|
|
180
|
+
{
|
|
181
|
+
ErrorType.Validation => StatusCodes.Status400BadRequest,
|
|
182
|
+
ErrorType.NotFound => StatusCodes.Status404NotFound,
|
|
183
|
+
ErrorType.Conflict => StatusCodes.Status409Conflict,
|
|
184
|
+
ErrorType.Unauthorized => StatusCodes.Status401Unauthorized,
|
|
185
|
+
ErrorType.Forbidden => StatusCodes.Status403Forbidden,
|
|
186
|
+
ErrorType.Unexpected => StatusCodes.Status500InternalServerError,
|
|
187
|
+
_ => StatusCodes.Status400BadRequest
|
|
188
|
+
};
|
|
189
|
+
|
|
190
|
+
public static IResult ToHttpResult(this Error error)
|
|
191
|
+
{
|
|
192
|
+
Dictionary<string, object?>? extensions = error is ValidationError validation
|
|
193
|
+
? new() { ["errors"] = validation.Errors.Select(e => new { e.Code, e.Description }).ToArray() }
|
|
194
|
+
: null;
|
|
195
|
+
|
|
196
|
+
return Results.Problem(
|
|
197
|
+
title: error.Code,
|
|
198
|
+
detail: error.Description,
|
|
199
|
+
statusCode: StatusCodeFor(error.Type),
|
|
200
|
+
extensions: extensions);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
```
|
|
204
|
+
|
|
173
205
|
Usage in an endpoint:
|
|
174
206
|
|
|
175
207
|
```csharp
|
|
176
|
-
var result = await handler.HandleAsync(
|
|
208
|
+
var result = await handler.HandleAsync(query, cancellationToken);
|
|
177
209
|
return result.Match(
|
|
178
|
-
onSuccess:
|
|
179
|
-
onFailure: error =>
|
|
210
|
+
onSuccess: book => Results.Ok(book),
|
|
211
|
+
onFailure: error => error.ToHttpResult());
|
|
180
212
|
```
|
|
181
213
|
|
|
214
|
+
For a handler that returns `Result` (no value), the success lambda takes no argument: `result.Match(onSuccess: () => Results.NoContent(), onFailure: error => error.ToHttpResult())`.
|
|
215
|
+
|
|
182
216
|
---
|
|
183
217
|
|
|
184
218
|
## Defining Feature Errors
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
# Scalar.AspNetCore — API Documentation UI
|
|
2
2
|
|
|
3
3
|
> **Scope:** dotnet-vsa
|
|
4
|
-
> **Layer:** 0 (
|
|
4
|
+
> **Layer:** 0 (resumo injetado pela rule `csharp-standards` ao editar `**/*.cs`/`**/*.csproj`; texto completo sob demanda via `morph-spec standards --find`)
|
|
5
5
|
> **Keywords:** scalar, openapi, swagger, api-docs, documentation
|
|
6
6
|
> **Load When:** dotnet VSA projects
|
|
7
7
|
|
|
8
|
-
**Verified against:** .NET 10 + Scalar.AspNetCore 2.x + Microsoft.AspNetCore.OpenApi 10. Last-verified: 2026-
|
|
8
|
+
**Verified against:** .NET 10 (SDK 10.0.400-preview) + Scalar.AspNetCore 2.x + Microsoft.AspNetCore.OpenApi 10. The `ApiTags` and tagged-endpoint blocks compile with `TreatWarningsAsErrors` over the `Result`/`ErrorHttpMapping` of `result-pattern.md` (ApiTags placed in `ProjectName.Constants`); the startup and metadata snippets (`async (...) => { ... }`) are fragments, checked against the docs only. Last-verified: 2026-10-03.
|
|
9
9
|
|
|
10
10
|
---
|
|
11
11
|
|
|
@@ -91,6 +91,10 @@ public static class ApiTags
|
|
|
91
91
|
Apply tags in endpoints:
|
|
92
92
|
|
|
93
93
|
```csharp
|
|
94
|
+
using ProjectName.Abstractions;
|
|
95
|
+
using ProjectName.Constants;
|
|
96
|
+
using ProjectName.Extensions;
|
|
97
|
+
|
|
94
98
|
internal sealed class CreateBookEndpoint : IApiEndpoint
|
|
95
99
|
{
|
|
96
100
|
public void MapEndpoint(IEndpointRouteBuilder app)
|
|
@@ -102,16 +106,18 @@ internal sealed class CreateBookEndpoint : IApiEndpoint
|
|
|
102
106
|
{
|
|
103
107
|
var result = await handler.HandleAsync(command, cancellationToken);
|
|
104
108
|
return result.Match(
|
|
105
|
-
onSuccess:
|
|
106
|
-
onFailure: error =>
|
|
109
|
+
onSuccess: response => Results.Ok(response),
|
|
110
|
+
onFailure: error => error.ToHttpResult());
|
|
107
111
|
})
|
|
108
112
|
.WithTags(ApiTags.Books)
|
|
109
113
|
.Produces<CreateBookResponse>(StatusCodes.Status200OK)
|
|
110
|
-
.
|
|
114
|
+
.ProducesProblem(StatusCodes.Status400BadRequest);
|
|
111
115
|
}
|
|
112
116
|
}
|
|
113
117
|
```
|
|
114
118
|
|
|
119
|
+
The failure side is `error.ToHttpResult()` (`Extensions/ErrorHttpMapping.cs`, see `backend/dotnet/result-pattern.md`): the `ErrorType` picks the status and the body is ProblemDetails, so document every failure status the slice can return with `.ProducesProblem(status)` — a 404/409 the endpoint can emit but does not declare is a lie in the Scalar docs.
|
|
120
|
+
|
|
115
121
|
---
|
|
116
122
|
|
|
117
123
|
## Endpoint Metadata
|
|
@@ -124,12 +130,12 @@ app.MapGet("books/{id:guid}", async (...) => { ... })
|
|
|
124
130
|
.WithName("GetBookById")
|
|
125
131
|
.WithSummary("Get a book by its unique identifier")
|
|
126
132
|
.Produces<GetBookResponse>(StatusCodes.Status200OK)
|
|
127
|
-
.
|
|
133
|
+
.ProducesProblem(StatusCodes.Status404NotFound);
|
|
128
134
|
|
|
129
135
|
app.MapDelete("books/{id:guid}", async (...) => { ... })
|
|
130
136
|
.WithTags(ApiTags.Books)
|
|
131
137
|
.Produces(StatusCodes.Status204NoContent)
|
|
132
|
-
.
|
|
138
|
+
.ProducesProblem(StatusCodes.Status404NotFound);
|
|
133
139
|
```
|
|
134
140
|
|
|
135
141
|
### Common Metadata Methods
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# Scrutor 7.0.0 — Auto-Discovery & Decorator Registration
|
|
2
2
|
|
|
3
3
|
> **Scope:** dotnet-vsa
|
|
4
|
-
> **Layer:** 0 (
|
|
4
|
+
> **Layer:** 0 (resumo injetado pela rule `csharp-standards` ao editar `**/*.cs`/`**/*.csproj`; texto completo sob demanda via `morph-spec standards --find`)
|
|
5
5
|
> **Keywords:** scrutor, di, decorators, auto-discovery, assembly-scanning
|
|
6
6
|
> **Load When:** dotnet VSA projects
|
|
7
7
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# .NET Integration Testing — VSA
|
|
2
2
|
|
|
3
3
|
> **Scope:** dotnet-vsa
|
|
4
|
-
> **Layer:**
|
|
4
|
+
> **Layer:** 1 (lista das personas `dotnet-senior`, `ef-modeler`, `maf-expert` e `infra-engineer`; nenhuma rule o resume — fora delas, sob demanda via `morph-spec standards --find`)
|
|
5
5
|
> **Keywords:** testing, xunit, integration test, TimeProvider, FakeTimeProvider, WebApplicationFactory, ProblemDetails, error body assertion
|
|
6
6
|
> **Load When:** writing or reviewing tests for a dotnet VSA project
|
|
7
7
|
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
# VSA Handler Patterns — Complete Feature Slice Guide
|
|
2
2
|
|
|
3
3
|
> **Scope:** dotnet-vsa
|
|
4
|
-
> **Layer:** 0 (
|
|
4
|
+
> **Layer:** 0 (resumo injetado pela rule `csharp-standards` ao editar `**/*.cs`/`**/*.csproj`; texto completo sob demanda via `morph-spec standards --find`)
|
|
5
5
|
> **Keywords:** vsa, handler, feature-slice, vertical-slice, minimal-api, endpoint
|
|
6
6
|
> **Load When:** dotnet VSA projects
|
|
7
7
|
|
|
8
|
-
**Verified against:** .NET 10 (minimal APIs, primary constructors, `Guid.CreateVersion7()`). Last-verified: 2026-
|
|
8
|
+
**Verified against:** .NET 10 (SDK 10.0.400-preview; minimal APIs, primary constructors, `Guid.CreateVersion7()`). The "Complete Endpoint Example" block compiles with `TreatWarningsAsErrors` over the `Result`/`ErrorHttpMapping` of `result-pattern.md` (in `ProjectName.Abstractions`/`ProjectName.Extensions`, reached only through the block's own `using`s); the other blocks are documentary. Last-verified: 2026-10-03.
|
|
9
9
|
|
|
10
10
|
---
|
|
11
11
|
|
|
@@ -137,12 +137,12 @@ internal sealed class CreateBookEndpoint : IApiEndpoint
|
|
|
137
137
|
{
|
|
138
138
|
var result = await handler.HandleAsync(command, cancellationToken);
|
|
139
139
|
return result.Match(
|
|
140
|
-
onSuccess:
|
|
141
|
-
onFailure: error =>
|
|
140
|
+
onSuccess: response => Results.Ok(response),
|
|
141
|
+
onFailure: error => error.ToHttpResult());
|
|
142
142
|
})
|
|
143
143
|
.WithTags(ApiTags.Books)
|
|
144
144
|
.Produces<CreateBookResponse>(StatusCodes.Status200OK)
|
|
145
|
-
.
|
|
145
|
+
.ProducesProblem(StatusCodes.Status400BadRequest);
|
|
146
146
|
}
|
|
147
147
|
}
|
|
148
148
|
```
|
|
@@ -152,9 +152,9 @@ internal sealed class CreateBookEndpoint : IApiEndpoint
|
|
|
152
152
|
- **`internal sealed`** — endpoints are not public API
|
|
153
153
|
- **`IApiEndpoint`** — single interface for auto-discovery
|
|
154
154
|
- **Handler injected via DI** — resolved as `IHandler<TReq, TRes>` parameter
|
|
155
|
-
- **`Match()` for responses** — never
|
|
155
|
+
- **`Match()` for responses** — the success lambda receives the value (never read `.Value` directly); the failure lambda is `error.ToHttpResult()` from `Extensions/ErrorHttpMapping.cs` (`backend/dotnet/result-pattern.md`), which maps `ErrorType` to the status — never `Results.BadRequest` for every error
|
|
156
156
|
- **`.WithTags()`** — always tag for Scalar API docs
|
|
157
|
-
- **`.Produces<T>()`** —
|
|
157
|
+
- **`.Produces<T>()` + `.ProducesProblem(status)`** — document the success type and every failure status the slice can return
|
|
158
158
|
|
|
159
159
|
---
|
|
160
160
|
|