@polymorphism-tech/morph-spec 9.2.0 → 9.4.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 +14 -78
- package/README.md +2 -2
- package/bin/morph-spec.js +7 -0
- package/claude-plugin.json +1 -1
- package/docs/CHEATSHEET.md +1 -1
- package/docs/QUICKSTART.md +1 -1
- package/framework/CLAUDE.md +3 -3
- package/framework/MORPH.md +5 -8
- package/framework/agents.json +14 -9
- package/framework/commands/morph-status.md +2 -2
- package/framework/docs/CLI.md +8 -2
- package/framework/evals/review-checklist.md +4 -1
- package/framework/hooks/README.md +1 -0
- package/framework/hooks/claude-code/__pycache__/statusline.cpython-313.pyc +0 -0
- package/framework/hooks/claude-code/core/block-raw-worktree-add.js +2 -15
- package/framework/hooks/claude-code/core/conventional-commit.js +57 -0
- package/framework/hooks/claude-code/core/worktree-context.js +23 -0
- package/framework/hooks/claude-code/statusline.py +3 -2
- package/framework/hooks/shared/command-tokens.js +17 -0
- package/framework/hooks/shared/gate-event.js +18 -0
- package/framework/hooks/shared/next-step.js +107 -0
- package/framework/hooks/shared/slice-progress.js +18 -5
- package/framework/hooks/shared/state-writer.js +6 -1
- package/framework/mcp/registry.json +31 -1
- package/framework/rules/csharp-standards.md +5 -3
- package/framework/rules/dotnet-templates.md +2 -2
- package/framework/rules/nextjs-standards.md +1 -0
- package/framework/schemas/feature.schema.json +1 -0
- package/framework/skills/core/create-umbraco-lp/SKILL.md +1 -1
- package/framework/skills/core/migrate-lp-to-umbraco/SKILL.md +1 -1
- package/framework/skills/core/morph-apply/SKILL.md +4 -2
- package/framework/skills/core/morph-hotfix/SKILL.md +1 -1
- package/framework/skills/core/morph-machine/SKILL.md +1 -1
- package/framework/skills/core/morph-media/SKILL.md +1 -1
- package/framework/skills/core/morph-plan/SKILL.md +3 -3
- package/framework/skills/core/morph-review/SKILL.md +1 -1
- package/framework/skills/core/morph-standards/SKILL.md +1 -1
- package/framework/skills/core/morph-uiux/SKILL.md +2 -1
- package/framework/standards/STANDARDS.json +55 -8
- package/framework/standards/STANDARDS.meta.json +29 -1
- package/framework/standards/ai-agents/setup.md +2 -0
- package/framework/standards/architecture/engineering-lens.md +100 -0
- package/framework/standards/architecture/vertical-slice/vertical-slice.md +7 -9
- package/framework/standards/backend/dotnet/analyzers.md +59 -0
- package/framework/standards/backend/dotnet/vsa-handler-patterns.md +5 -12
- package/framework/standards/integration/mcp/mcp-tools.md +3 -1
- package/framework/templates/README.md +0 -1
- package/framework/templates/REGISTRY.json +0 -207
- package/package.json +1 -1
- package/src/commands/observability/cost.js +12 -0
- package/src/commands/project/status.js +6 -68
- package/src/commands/project/worktree.js +1 -1
- package/src/commands/state/advance.js +1 -1
- package/src/commands/state/archive.js +1 -1
- package/src/commands/state/finish.js +1 -1
- package/src/commands/state/gate-check.js +2 -2
- package/src/commands/state/gate-decision.js +4 -1
- package/src/commands/state/next.js +24 -0
- package/src/commands/state/report-check.js +3 -1
- package/src/commands/validation/verify.js +6 -1
- package/src/core/state/state-manager.js +4 -0
- package/src/core/templates/template-registry.js +1 -43
- package/src/dashboard/app/pages/FeatureDetail.jsx +6 -2
- package/src/dashboard/scanner/features-scanner.js +5 -2
- package/src/lib/generators/handoff-generator.js +11 -25
- package/src/lib/next-step.js +10 -0
- package/src/lib/risk-detector.js +18 -7
- package/src/lib/stack/stack-scan.js +17 -7
- package/src/lib/telemetry/metrics.js +46 -0
- package/src/lib/validators/verify-runner.js +1 -1
- package/src/utils/agents-installer.js +24 -1
- package/src/utils/hooks-installer.js +1 -1
- package/framework/templates/meta-prompts/fusion/fusion-agent.md +0 -76
- package/framework/templates/meta-prompts/fusion/fusion-aggregator.md +0 -100
- package/framework/templates/meta-prompts/squad-leaders/backend-squad.md +0 -90
- package/framework/templates/meta-prompts/squad-leaders/frontend-squad.md +0 -84
- package/framework/templates/meta-prompts/squad-leaders/squad-leader.md +0 -44
- package/framework/templates/meta-prompts/validators/checkpoint-validator.md +0 -107
- package/framework/templates/meta-prompts/validators/pre-commit-validator.md +0 -94
|
@@ -4,7 +4,7 @@ description: Leva uma feature do pedido ao plano aprovado — roteia o tipo de t
|
|
|
4
4
|
version: core
|
|
5
5
|
when_to_use: Use quando uma feature, bug ou chore começar ("/morph-plan {feature}", "vamos planejar X"), ou ao retomar uma feature cujo gate plan ainda não foi aprovado
|
|
6
6
|
user-invocable: true
|
|
7
|
-
cliVersion: "9.
|
|
7
|
+
cliVersion: "9.4.0"
|
|
8
8
|
---
|
|
9
9
|
|
|
10
10
|
# morph-plan
|
|
@@ -95,11 +95,11 @@ O `create` já escreveu o esqueleto em `2-plan/plan.md` (leia e preencha; `chore
|
|
|
95
95
|
|
|
96
96
|
**Arquitetura**: componentes, fluxo de dados, edge cases **técnicos** e integrações; não repita os cenários.
|
|
97
97
|
|
|
98
|
-
**Reuso**: tabela `| conceito | veredito | alvo | why |`, veredito `REUSAR` | `ESTENDER` | `CRIAR`. **`CRIAR` exige `why`** — a consulta que respondeu "não existe" (`graph explain X → not-found; grep -rn X → 0`). Conceito de duas features vai para um módulo comum, nunca import cruzado entre slices. O validador `reuse` do `verify` cobra: símbolo novo homônimo de um existente reprova na mesma feature e avisa entre features; `CRIAR` declarado rebaixa a aviso. Conceito sem fatia que o escreva: falta fatia ou o conceito não é desta feature — resolva antes do gate.
|
|
98
|
+
**Reuso**: tabela `| conceito | veredito | alvo | why |`, veredito `REUSAR` | `ESTENDER` | `CRIAR`. **`CRIAR` exige `why`** — a consulta que respondeu "não existe" (`graph explain X → not-found; grep -rn X → 0`). Abstração nova (interface, base, factory, camada) soma ao `why` o segundo uso ou a próxima adição que ela barateia; sem isso é excesso (`architecture-engineering-lens#excess`). Conceito de duas features vai para um módulo comum, nunca import cruzado entre slices. O validador `reuse` do `verify` cobra: símbolo novo homônimo de um existente reprova na mesma feature e avisa entre features; `CRIAR` declarado rebaixa a aviso. Conceito sem fatia que o escreva: falta fatia ou o conceito não é desta feature — resolva antes do gate.
|
|
99
99
|
|
|
100
100
|
**Fatias** (`### Fn — título`): a unidade de entrega e de relatório (`3-implement/reports/Fn.md`).
|
|
101
101
|
- `persona:` obrigatório — o id sem `morph-`, cujo `domains` cobre a fatia (uma leitura de `agents.json`). Fatia que cria ou altera UI é `ui-designer`, mesmo sem o ramo de design; com `superfície:`, os standards são os daquela chave de `surfaces`.
|
|
102
|
-
- `standards:` — `morph-spec standards --find "<domínio>"` por domínio (o digest decide): os da persona mais o que a fatia exige, sempre `architecture-code-comments` e o de teste do stack; agente MAF soma `--category ai-agents`.
|
|
102
|
+
- `standards:` — `morph-spec standards --find "<domínio>"` por domínio (o digest decide): os da persona mais o que a fatia exige, sempre `architecture-code-comments`, `architecture-engineering-lens` e o de teste do stack; agente MAF soma `--category ai-agents`.
|
|
103
103
|
- `arquivos:` — os paths planejados, testes incluídos, **no máximo 8**: acima disso o sub-agente bate no teto de turnos.
|
|
104
104
|
- `depende:` opcional; `cobre:` lista os Cn — todo cenário é coberto por alguma fatia. Fatias sem `depende:` comum e sem arquivo em comum rodam em paralelo; o `plan-check` imprime as ondas.
|
|
105
105
|
- Cenário que se observa por um artefato de outra fatia (rota, endpoint, componente que ela cria) faz da fatia que o cobre um `depende:` daquela — senão as duas caem na mesma onda e o teste do cenário não tem por onde provar. O `plan-check` não vê de quem é a rota: a regra é sua.
|
|
@@ -4,7 +4,7 @@ description: Conduz o Gate 3 — despacha verificador ∥ revisor (∥ seguranç
|
|
|
4
4
|
version: core
|
|
5
5
|
when_to_use: Use quando a fase review começar, com todas as fatias com relatório final e o verify da feature verde
|
|
6
6
|
user-invocable: true
|
|
7
|
-
cliVersion: "9.
|
|
7
|
+
cliVersion: "9.4.0"
|
|
8
8
|
---
|
|
9
9
|
|
|
10
10
|
# morph-review
|
|
@@ -4,7 +4,7 @@ description: Localiza um standard específico via busca server-side (morph-spec
|
|
|
4
4
|
version: core
|
|
5
5
|
when_to_use: Use quando precisar consultar um standard específico durante a implementação
|
|
6
6
|
user-invocable: true
|
|
7
|
-
cliVersion: "9.
|
|
7
|
+
cliVersion: "9.4.0"
|
|
8
8
|
---
|
|
9
9
|
|
|
10
10
|
# morph-standards
|
|
@@ -4,7 +4,7 @@ description: Conduz a fase opcional de UI/UX de uma feature com plano aprovado
|
|
|
4
4
|
version: core
|
|
5
5
|
when_to_use: Use quando o ramo de design for tomado (morph-spec advance {feature} --design, depois do gate do plano) e antes de /morph-apply
|
|
6
6
|
user-invocable: true
|
|
7
|
-
cliVersion: "9.
|
|
7
|
+
cliVersion: "9.4.0"
|
|
8
8
|
---
|
|
9
9
|
|
|
10
10
|
# morph-uiux
|
|
@@ -22,6 +22,7 @@ Entra depois do gate do plano, quando a `morph-plan` §6 decidiu que a feature c
|
|
|
22
22
|
|
|
23
23
|
- WCAG AA e todos os estados de componente interativo (default/hover/focus/disabled/loading/error), não só o caminho feliz.
|
|
24
24
|
- Reusa token e componente existentes antes de criar; token novo precisa de justificativa. Sistemas, não telas isoladas.
|
|
25
|
+
- Tarefa principal da tela em até 3 interações, uma ação primária por tela, voltar/cancelar sempre à vista. Erro diz o que fazer a seguir; conteúdo gerado por IA aparece marcado como tal.
|
|
25
26
|
- **Toda tela sai com o checklist de tela preenchido.** O `advance --design` já escreveu `1-design/mockups.md` a partir do template do pacote, a fonte única dos dez itens (tabela `### Checklist de tela` dentro de cada `## Tela:`). "Não se aplica" vale se escrito; `…`, vazio ou "a definir" reprovam. Design sem tela nova ou alterada (só componente ou estilo): apague o `1-design/mockups.md` semeado. É o checklist que faz a lista de 2 000 linhas paginar: o verificador do Gate 3 cobra cada item decidido em `## Checklist de tela`. Tela de dados segue os standards de `admin-app` (lista/tabela, formulário, feedback, estrutura de página); login, onboarding, primeira tela, estado vazio de destaque e sucesso são `brand-moment`.
|
|
26
27
|
- **Conformidade não basta: a régua estética vale junto.** Direção escrita antes de componente (`1-design/direcao.md`: conceito, metáfora, o que torna a tela memorável, o que ela não pode parecer; 2 ou 3 direções para tela nova ou momento de marca), celular desenhado e não derivado, movimento com versão estática digna. A régua é o standard `frontend-design-system-art-direction`.
|
|
27
28
|
- **Toda tela nova ou alterada tem protótipo renderizado e conferido por print.** `1-design/prototipos/{tela}.html` autossuficiente, com os tokens reais do projeto (lidos do CSS de tokens), desktop e celular lado a lado, controles animado/estático/pausar; prints `{tela}-1280.png` e `{tela}-390.png` ao lado, saídos do laço de autocrítica. A `## Tela:` aponta o protótipo na linha `**Protótipo:**`.
|
|
@@ -496,7 +496,7 @@
|
|
|
496
496
|
"example": "#exemplo-c-completo",
|
|
497
497
|
"content-types": "#datacontent-vs-uricontent"
|
|
498
498
|
},
|
|
499
|
-
"digest": "Vision em MAF usa ChatMessage multimodal (Microsoft.Extensions.AI): new ChatMessage(ChatRole.User, [new TextContent(pergunta), imagem]) passado a agent.RunAsync. Imagem em URL pública: UriContent(uri, mediaType); arquivo local: DataContent.LoadFromAsync(path), o helper canônico e preferido; bytes já em memória: new DataContent(BinaryData, mediaType). Upload de usuário ou imagem privada usa DataContent, nunca URL temporária (pode expirar); CDN estável usa UriContent. O modelo vem do alias vision no Model Registry, pois nem todo provider suporta visão. Dado tipado: RunAsync<T> com
|
|
499
|
+
"digest": "Vision em MAF usa ChatMessage multimodal (Microsoft.Extensions.AI): new ChatMessage(ChatRole.User, [new TextContent(pergunta), imagem]) passado a agent.RunAsync. Imagem em URL pública: UriContent(uri, mediaType); arquivo local: DataContent.LoadFromAsync(path), o helper canônico e preferido; bytes já em memória: new DataContent(BinaryData, mediaType). Upload de usuário ou imagem privada usa DataContent, nunca URL temporária (pode expirar); CDN estável usa UriContent. O modelo vem do alias vision no Model Registry, pois nem todo provider suporta visão. Dado tipado: RunAsync<T> com record, nunca regex em response.Text. Proibido: base64 manual, imagem 4K sem resize (tokens cobrados por resolução; ex.: 800px para OCR), vision quando já há texto extraído ou um parser/OCR determinístico basta. PDF: confirme application/pdf no provider."
|
|
500
500
|
},
|
|
501
501
|
{
|
|
502
502
|
"id": "ai-agents-multi-agent-patterns",
|
|
@@ -692,7 +692,7 @@
|
|
|
692
692
|
"type": "#definindo-o-tipo-de-saída",
|
|
693
693
|
"validation": "#validação-pós-call"
|
|
694
694
|
},
|
|
695
|
-
"digest": "Every agent whose output feeds another system returns a typed record, never free text. Form 1 is RunAsync<T>; Form 2 is ResponseFormat = ChatResponseFormat.ForJsonSchema<T>() in ChatOptions. Type is a
|
|
695
|
+
"digest": "Every agent whose output feeds another system returns a typed record, never free text. Form 1 is RunAsync<T>; Form 2 is ResponseFormat = ChatResponseFormat.ForJsonSchema<T>() in ChatOptions. Type is a record with [Description] on the type and [JsonPropertyName] on every property. Documented limits: ResponseFormat does not accept a primitive or an array at the root (use a wrapper), and in streaming you must build the whole response with ToAgentResponseAsync() before deserialising. Schema validates shape, not semantics — always validate business rules after the call. Do not use temperature as the example of a normal ChatOption."
|
|
696
696
|
},
|
|
697
697
|
{
|
|
698
698
|
"id": "ai-agents-sweet-spot",
|
|
@@ -801,7 +801,7 @@
|
|
|
801
801
|
"rules": "#padrões-obrigatórios",
|
|
802
802
|
"cqrs": "#anti-patterns-nunca-fazer-em-vsa"
|
|
803
803
|
},
|
|
804
|
-
"digest": "Uma slice por operação em Features/{Entity}Feature/{Verb}{Entity}/: Request e Response (
|
|
804
|
+
"digest": "Uma slice por operação em Features/{Entity}Feature/{Verb}{Entity}/: Request e Response (records) no mesmo arquivo do Handler, Validator FluentValidation (nenhum em GetAll) e Endpoint `IApiEndpoint` `internal`. O handler implementa o `IHandler<TRequest, TResponse>` próprio, devolve `Result<T>` (erros em `{Entity}Errors`, 1 por feature), injeta `ApplicationDbContext` direto e chama `SaveChangesAsync` nas escritas; o endpoint usa `result.Match()`: o sucesso recebe o valor no lambda (nunca `result.Value`) e a falha vira `error.ToHttpResult()` (status pelo `ErrorType`, ProblemDetails), nunca `Results.BadRequest` para todo erro, declarando cada status com `.ProducesProblem`. Decorators Scrutor: Logging -> Validation -> Handler. PROIBIDO: MediatR/IRequest, AggregateRoot, DomainEvent, CQRS formal, Application Service layer, DTOs compartilhados entre features e `IRepository<T>`/`IUnitOfWork` (DbSet já é repository, vira código morto); store dedicado só com justificativa em `plan.md` (`## Decisões`). Não misture VSA com DDD."
|
|
805
805
|
},
|
|
806
806
|
{
|
|
807
807
|
"id": "backend-api-validation",
|
|
@@ -821,7 +821,7 @@
|
|
|
821
821
|
"rules": "#validation-patterns",
|
|
822
822
|
"state": "#state-checks-in-the-handler"
|
|
823
823
|
},
|
|
824
|
-
"digest": "Request/DTO validation in the VSA slice: one
|
|
824
|
+
"digest": "Request/DTO validation in the VSA slice: one `AbstractValidator<TRequest>` (FluentValidation) next to the handler, checking SHAPE only (required, length, range, format, child items via RuleForEach/ChildRules; no DbContext; time rules read an injected TimeProvider inside the rule). The ValidationDecorator runs it before the handler and short-circuits with `Result.Failure(new ValidationError([...]))`; the endpoint maps it with `error.ToHttpResult()` to 400 ProblemDetails with `errors[]` (decorator and conversion in fluent-validation-vsa.md, ValidationError/ErrorHttpMapping in result-pattern.md). Never throw (ValidationException/InvalidOperationException), never validate in the endpoint lambda, never mix DataAnnotations with FluentValidation (minimal APIs have no ModelState). Existence/uniqueness/state checks live in the handler and return typed `{Entity}Errors` (NotFound 404, Conflict 409, Failure 400), never `Result.Failure(\"string\")`. State checks validate what is INVALID, not what is valid, so a new flow (e.g. a free flow that skips PendingPayment) does not break: ordered status enum (Created=0 ... EmailSent=4) with error states at 100+ (Failed=100, Cancelled=101, Refunded=102), blocked by `status >= Completed`, an error-state list, or `status >= Failed`. FORBIDDEN: `if (status != Expected)`, exhaustive switches over valid states, business rules mixed into the state check (keep `RequiresPayment && !IsPaid` separate). Document all flows in plan.md (## Decisões) and test each."
|
|
825
825
|
},
|
|
826
826
|
{
|
|
827
827
|
"id": "backend-database-postgresql-database",
|
|
@@ -883,7 +883,7 @@
|
|
|
883
883
|
"decorator": "#validationdecorator-pattern",
|
|
884
884
|
"conversion": "#fluentvalidation-to-validationerror-conversion"
|
|
885
885
|
},
|
|
886
|
-
"digest": "One
|
|
886
|
+
"digest": "One `AbstractValidator<TRequest>` per request DTO (FluentValidation 12), in the same feature folder as the handler; no `RuleSet` grouping and no `IValidateOptions<T>`. Never validate request shape inside the handler or endpoint (existence/state checks do belong in the handler): `ValidationDecorator<TRequest, TResponse>` wraps every `IHandler` via Scrutor, collects all `IValidator<TRequest>`, runs them in parallel (`Task.WhenAll`) and on failure short-circuits to `Result.Failure(ValidationError)` (an array of `Error.Validation(code, message)`), supporting both `Result` and `Result<T>`. Never throw `ValidationException`. In Program.cs call `AddValidatorsFromAssembly()` BEFORE `AddHandlersFromAssembly()` so the decorator can resolve validators. Always give user-facing `.WithMessage()` text. Inject `TimeProvider` instead of reading `DateTime.UtcNow` and read it INSIDE the rule (`LessThanOrEqualTo(_ => timeProvider.GetUtcNow().Year)`), so the year is evaluated per validation (a value captured in the constructor goes stale in a singleton/cached validator; AddValidatorsFromAssembly defaults to Scoped) and stays testable with `FakeTimeProvider`."
|
|
887
887
|
},
|
|
888
888
|
{
|
|
889
889
|
"id": "backend-dotnet-result-pattern",
|
|
@@ -988,7 +988,7 @@
|
|
|
988
988
|
"endpoint": "#complete-endpoint-example",
|
|
989
989
|
"registration": "#programcs-di-registration-order"
|
|
990
990
|
},
|
|
991
|
-
"digest": "Each VSA feature is a slice in `Features/{Feature}/{UseCase}/` with 5 files: Request, Response (
|
|
991
|
+
"digest": "Each VSA feature is a slice in `Features/{Feature}/{UseCase}/` with 5 files: Request, Response (records, may be inline), Handler, Validator, Endpoint; feature errors in `{Feature}Errors.cs` (`Error.NotFound/Conflict/Failure`). Handlers implement `IHandler<TRequest, TResponse>` (resolved from DI; NOT MediatR, decorators via Scrutor), inject `ApplicationDbContext` directly (`DbSet<T>` is the repository, `DbContext` the unit of work: no service layer, no `IRepository<T>`/`IUnitOfWork`), return `Result<T>` instead of throwing for expected failures. Endpoints are `internal` `IApiEndpoint`, only map HTTP and call `Match()`: success lambda receives the value (never read `.Value`), failure is `error.ToHttpResult()` (status by `ErrorType`, never `Results.BadRequest` for every error), with `.ProducesProblem` per failure status. Raw SQL or a dedicated hand-written store (outbox, lease) only with a `plan.md` (`## Decisões`) justification."
|
|
992
992
|
},
|
|
993
993
|
{
|
|
994
994
|
"id": "backend-integrations-asaas-asaas-api",
|
|
@@ -1028,7 +1028,7 @@
|
|
|
1028
1028
|
"jobs": "#job-types",
|
|
1029
1029
|
"retry": "#retry--error-handling"
|
|
1030
1030
|
},
|
|
1031
|
-
"digest": "Hangfire 1.8.25 on .NET 10, only with a real reason (survive restarts, schedules, deferred retries) recorded in plan.md (## Decisões); work that fits in the request stays in the slice handler. Storage is Neon Postgres via Hangfire.PostgreSql 1.21.1 with `UsePostgreSqlStorage(o => o.UseNpgsqlConnection(cs))` (the string overloads are [Obsolete]) and `HangfireConnection` on the direct/unpooled endpoint (schema install at startup is a migration, which Neon routes to the direct string; EnableLongPolling uses LISTEN/NOTIFY, unsupported by Neon's PgBouncer transaction mode); reference Newtonsoft.Json 13.0.4 explicitly or restore fails with NU1903 (Hangfire.Core pulls 11.0.1). A job is a thin
|
|
1031
|
+
"digest": "Hangfire 1.8.25 on .NET 10, only with a real reason (survive restarts, schedules, deferred retries) recorded in plan.md (## Decisões); work that fits in the request stays in the slice handler. Storage is Neon Postgres via Hangfire.PostgreSql 1.21.1 with `UsePostgreSqlStorage(o => o.UseNpgsqlConnection(cs))` (the string overloads are [Obsolete]) and `HangfireConnection` on the direct/unpooled endpoint (schema install at startup is a migration, which Neon routes to the direct string; EnableLongPolling uses LISTEN/NOTIFY, unsupported by Neon's PgBouncer transaction mode); reference Newtonsoft.Json 13.0.4 explicitly or restore fails with NU1903 (Hangfire.Core pulls 11.0.1). A job is a thin class in the slice that calls the slice handler (`IHandler<TRequest, Result>`); no XService layer. Hangfire.AspNetCore's AspNetCoreJobActivator opens a DI scope per job, so inject the ApplicationDbContext/handler directly (IDbContextFactory only for parallel work inside one job); never capture request-scoped services into Enqueue. Jobs are idempotent (check state, return). Expected failures come back as Result and the job logs and completes; unexpected ones are the exceptions EF/HttpClient already throw, uncaught, which `[AutomaticRetry(Attempts = 3, DelaysInSeconds = [60, 300, 900])]` retries; no try/catch, no manual retry loops. API: `BackgroundJob.Enqueue<TJob>(j => j.RunAsync(id, CancellationToken.None))`, `Schedule`, `RecurringJob.AddOrUpdate` with a stable id and cron (UTC), `ContinueJobWith`. Dashboard: IDashboardAuthorizationFilter requiring authenticated Admin, dev bypass only via IHostEnvironment.IsDevelopment() (never Request.Host, which the client controls)."
|
|
1032
1032
|
},
|
|
1033
1033
|
{
|
|
1034
1034
|
"id": "backend-integrations-neon-auth-neon-auth",
|
|
@@ -1857,7 +1857,7 @@
|
|
|
1857
1857
|
"reais": "#exemplos-reais",
|
|
1858
1858
|
"regras": "#regras"
|
|
1859
1859
|
},
|
|
1860
|
-
"digest": "A tool that carries business rule keeps apart three things that change for different reasons: the RULE (
|
|
1860
|
+
"digest": "A tool that carries business rule keeps apart three things that change for different reasons: the RULE (class XOps(...), explicit AddScoped; never *Service, which MORPH.md bans as a layer between handler and data), the sentence the MODEL reads, and the EFFECT a human sees. Each Ops method returns a typed outcome (abstract record XDesfecho, one record per case); the model-facing text (description, refusal, self-correction, success, the ToolUnavailable decision) lives in the adapter, in public static object Render(XDesfecho), while human text delivered by code (task body, note, ack) stays in Ops because it is an effect. Checkable by grep: Ops names no tool and instructs no model. The adapter (AIFunctionFactory.Create) only builds the typed request, calls Ops and returns Render: zero rule, not a line count, and a long dynamic description stays byte for byte. Two test layers: Ops tests assert outcome, calls and order on the fake, never text; contract tests assert Render's exact literal and .Description, with a golden of name, description and schema per real tenant. A neutral refactor is proven before any test moves: a proof commit with the old suite untouched and green (offline evals included), then the migration, with mutation run only against the new tests. \"Already covered\" needs proof assertion by assertion (11 of 22 such claims had lost one); the tests left at the origin must be identical to the base; mutation on the new tests found rules the old suite never protected. The C# blocks were compiled against the pin and the three xunit tests run."
|
|
1861
1861
|
},
|
|
1862
1862
|
{
|
|
1863
1863
|
"id": "frontend-design-system-art-direction",
|
|
@@ -1879,6 +1879,53 @@
|
|
|
1879
1879
|
"prototype": "#section-7-prototype-and-self-review"
|
|
1880
1880
|
},
|
|
1881
1881
|
"digest": "Order beats talent: before any component of a new or changed screen, write 1-design/direcao.md with the concept in one sentence, the visual metaphor, what makes it memorable, and what it must NOT look like. New screens and brand moments (login, onboarding, first screen, empty state, success) explore 2-3 DISTINCT directions and recommend one with a product reason. Avoid template tells (raw shadcn look, white card centered on gray, generic icon list, default fonts, flat backgrounds, mobile as stacked desktop, decorative fade-ins, made-up numbers); three or more means raise it. Mobile is composed at 390px on its own (bottom sheet, thumb zone, 44px targets). Motion tells something with the lightest tool (CSS keyframes > scroll-driven CSS > Framer Motion > GSAP), transform/opacity only, and a composed reduced-motion version. Prove it in 1-design/prototipos/{screen}.html with the project REAL tokens, screenshotted at 1280 and 390 and revised until it passes."
|
|
1882
|
+
},
|
|
1883
|
+
{
|
|
1884
|
+
"id": "architecture-engineering-lens",
|
|
1885
|
+
"name": "engineering lens",
|
|
1886
|
+
"path": "architecture/engineering-lens.md",
|
|
1887
|
+
"category": "architecture",
|
|
1888
|
+
"subcategory": null,
|
|
1889
|
+
"tags": [
|
|
1890
|
+
"architecture",
|
|
1891
|
+
"clean-code",
|
|
1892
|
+
"review"
|
|
1893
|
+
],
|
|
1894
|
+
"stacks": [
|
|
1895
|
+
"*"
|
|
1896
|
+
],
|
|
1897
|
+
"anchors": {
|
|
1898
|
+
"fragility": "#fragilidade",
|
|
1899
|
+
"dead-code": "#código-morto",
|
|
1900
|
+
"excess": "#excesso",
|
|
1901
|
+
"readability": "#legibilidade",
|
|
1902
|
+
"extensibility": "#extensibilidade",
|
|
1903
|
+
"ai-vs-code": "#ia-e-código",
|
|
1904
|
+
"git": "#git",
|
|
1905
|
+
"review-blockers": "#o-que-bloqueia-no-gate-3"
|
|
1906
|
+
},
|
|
1907
|
+
"digest": "Write for whoever inherits the repo tomorrow: the project convention beats taste, the simplest thing that works beats the general one, fix the root cause. Fragility: no swallowed catch, handle null/absence from boundaries (HTTP, DB, file, LLM), no hidden order dependency, no strict parser on real data, no blind guard, no test that passes without proving the rule. Dead code: a symbol, endpoint, flag, config or table without a real consumer goes, and so does commented-out code; two paths doing the same thing become one. Excess: no abstraction without a second use today, no pass-through layer, no speculative config, no reinvented stdlib; a plan CREATE of an abstraction names the second use or the next addition it makes cheaper. Readability: descriptive searchable domain names, nouns for classes and verbs for methods, small single-purpose functions, at most 3 parameters, no boolean flag that forks the method (two methods), no hidden side effect, positive conditionals, repeated literals become constants. Extensibility: the next likely addition touches few files and no scattered switch. AI: an unbreakable rule stays in code (ai-agents-guardrails); hand-written NL classification may become structured output only when measured by eval. Git: branch morph/{f}; Conventional Commits <type>(<scope>): <subject>, enforced by the conventional-commit hook. Gate 3 blocks only what the DIFF introduced: swallowed exception, boolean flag parameter, hidden side effect, symbol/flag/config without consumer or commented-out code, abstraction without second use nor plan why, repeated magic literal in a comparison; everything else is a note."
|
|
1908
|
+
},
|
|
1909
|
+
{
|
|
1910
|
+
"id": "backend-dotnet-analyzers",
|
|
1911
|
+
"name": "analyzers",
|
|
1912
|
+
"path": "backend/dotnet/analyzers.md",
|
|
1913
|
+
"category": "backend",
|
|
1914
|
+
"subcategory": "dotnet",
|
|
1915
|
+
"tags": [
|
|
1916
|
+
"backend",
|
|
1917
|
+
"dotnet",
|
|
1918
|
+
"analyzers"
|
|
1919
|
+
],
|
|
1920
|
+
"stacks": [
|
|
1921
|
+
"dotnet"
|
|
1922
|
+
],
|
|
1923
|
+
"anchors": {
|
|
1924
|
+
"files": "#arquivos",
|
|
1925
|
+
"rules": "#o-que-cada-regra-compra",
|
|
1926
|
+
"not-a-rule": "#o-que-não-é-regra"
|
|
1927
|
+
},
|
|
1928
|
+
"digest": "Rules with a reason that must never break are build errors, not prose: three files at the solution root, no .csproj change. Directory.Build.props adds Microsoft.CodeAnalysis.BannedApiAnalyzers 5.6.0 (PrivateAssets=all), BannedSymbols.txt as AdditionalFiles and morph.globalconfig as GlobalAnalyzerConfigFiles (with Central Package Management the version goes to Directory.Packages.props). BannedSymbols.txt bans M:System.Guid.NewGuid (use Guid.CreateVersion7(): time-ordered, does not fragment the Postgres PK index). morph.globalconfig sets RS0030 and CA2016 (a CancellationToken received and not forwarded) to error. A new banned API is one BannedSymbols.txt line with the reason after the semicolon, never a prose rule. sealed, primary constructors, var, expression bodies and using order are taste: neither prose nor analyzer; follow the neighbouring file."
|
|
1882
1929
|
}
|
|
1883
1930
|
]
|
|
1884
1931
|
}
|
|
@@ -1123,7 +1123,7 @@
|
|
|
1123
1123
|
"MCP server",
|
|
1124
1124
|
"MCP client"
|
|
1125
1125
|
],
|
|
1126
|
-
"verifiedAgainst": "Claude Code 2.1.282 MCP tooling — context7, Playwright (as plugin and as a project MCP server); GitHub via `gh` CLI; Neon via its MCP or `neonctl
|
|
1126
|
+
"verifiedAgainst": "Claude Code 2.1.282 MCP tooling — context7, Playwright (as plugin and as a project MCP server); GitHub via `gh` CLI; Neon via its MCP or `neonctl`; Microsoft Learn MCP (`https://learn.microsoft.com/api/mcp`, tools listed by `tools/list` on 2026-10-17).",
|
|
1127
1127
|
"provedBy": []
|
|
1128
1128
|
},
|
|
1129
1129
|
"frontend-admin-app-data-table": {
|
|
@@ -1223,6 +1223,34 @@
|
|
|
1223
1223
|
],
|
|
1224
1224
|
"verifiedAgainst": "the `login-ciclo-do-produto` design round (2026-10-06): a correct, plain design was rejected; a second round with a written direction, an HTML prototype and a screenshot self-review was approved on first sight.",
|
|
1225
1225
|
"provedBy": []
|
|
1226
|
+
},
|
|
1227
|
+
"architecture-engineering-lens": {
|
|
1228
|
+
"aliases": [
|
|
1229
|
+
"clean code",
|
|
1230
|
+
"código limpo",
|
|
1231
|
+
"código morto",
|
|
1232
|
+
"dead code",
|
|
1233
|
+
"fragilidade",
|
|
1234
|
+
"excesso",
|
|
1235
|
+
"extensibilidade",
|
|
1236
|
+
"flag argument",
|
|
1237
|
+
"conventional commits"
|
|
1238
|
+
],
|
|
1239
|
+
"verifiedAgainst": "C#, TypeScript e JavaScript (convenção de linguagem, sem pacote externo); Conventional Commits 1.0.0.",
|
|
1240
|
+
"provedBy": []
|
|
1241
|
+
},
|
|
1242
|
+
"backend-dotnet-analyzers": {
|
|
1243
|
+
"aliases": [
|
|
1244
|
+
"analyzer",
|
|
1245
|
+
"roslyn analyzer",
|
|
1246
|
+
"banned api",
|
|
1247
|
+
"BannedSymbols",
|
|
1248
|
+
"Guid.NewGuid",
|
|
1249
|
+
"CreateVersion7",
|
|
1250
|
+
"CA2016"
|
|
1251
|
+
],
|
|
1252
|
+
"verifiedAgainst": ".NET SDK 10.0.402, Microsoft.CodeAnalysis.BannedApiAnalyzers 5.6.0 (sonda: `Guid.NewGuid()` → erro RS0030, token não repassado → erro CA2016, código conforme → 0 erros).",
|
|
1253
|
+
"provedBy": []
|
|
1226
1254
|
}
|
|
1227
1255
|
},
|
|
1228
1256
|
"sharedAliases": {
|
|
@@ -331,6 +331,8 @@ AIAgent instrumented = agent.AsBuilder()
|
|
|
331
331
|
|
|
332
332
|
## 7. What NOT to do (anti-patterns)
|
|
333
333
|
|
|
334
|
+
`morph-spec stack-scan` cobra três linhas desta tabela no código: Semantic Kernel, hosted agent service e versão flutuante.
|
|
335
|
+
|
|
334
336
|
| Wrong | Right |
|
|
335
337
|
|-------|-------|
|
|
336
338
|
| `using Microsoft.SemanticKernel` | `using Microsoft.Agents.AI` |
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
# Lente de engenharia — código para quem herda amanhã
|
|
2
|
+
|
|
3
|
+
> **Scope:** universal
|
|
4
|
+
> **Layer:** 0 (resumo injetado pelas rules `csharp-standards` (`**/*.cs`, `**/*.csproj`) e `nextjs-standards` (`**/*.ts`, `**/*.tsx`); texto completo sob demanda via `morph-spec standards --find`)
|
|
5
|
+
> **Keywords:** clean code, código limpo, fragilidade, código morto, excesso, legibilidade, extensibilidade, flag booleana, efeito colateral, conventional commits
|
|
6
|
+
> **Load When:** escrevendo, planejando ou revisando código em qualquer stack
|
|
7
|
+
|
|
8
|
+
**Verified against:** C#, TypeScript e JavaScript (convenção de linguagem, sem pacote externo); Conventional Commits 1.0.0. Last-verified: 2026-10-18.
|
|
9
|
+
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
## Quem herda amanhã
|
|
13
|
+
|
|
14
|
+
Escreva para quem abre o repo amanhã e precisa entregar uma feature na semana seguinte. A
|
|
15
|
+
convenção que o projeto já usa vence o seu gosto. O mais simples que resolve vence o mais geral.
|
|
16
|
+
Bug se corrige na causa raiz, nunca no sintoma.
|
|
17
|
+
|
|
18
|
+
## Fragilidade
|
|
19
|
+
|
|
20
|
+
Código que quebra fácil:
|
|
21
|
+
|
|
22
|
+
- `catch` vazio, ou que só loga e segue como se nada tivesse acontecido.
|
|
23
|
+
- Null ou ausência vindo de fronteira (HTTP, banco, arquivo, LLM) sem tratamento.
|
|
24
|
+
- Dependência de ordem implícita: método que só funciona se outro rodou antes.
|
|
25
|
+
- Parser estrito diante de dado real (campo opcional tratado como obrigatório, enum fechado para
|
|
26
|
+
valor externo).
|
|
27
|
+
- Guard cego: a checagem e o dado saem do mesmo serializador, então ela nunca falha.
|
|
28
|
+
- Teste que passa sem provar a regra (ver `backend-dotnet-testing` / `frontend-nextjs-testing`).
|
|
29
|
+
|
|
30
|
+
## Código morto
|
|
31
|
+
|
|
32
|
+
Símbolo, endpoint, flag, config ou tabela sem consumidor real sai. Feature começada e largada
|
|
33
|
+
sai. Dois caminhos que fazem a mesma coisa viram um. Quando "X só é usado por Y", pergunte para
|
|
34
|
+
quê Y existe: muitas vezes os dois saem. Código comentado sai, porque o git guarda o antigo.
|
|
35
|
+
|
|
36
|
+
## Excesso
|
|
37
|
+
|
|
38
|
+
- Abstração (interface, factory, base class, strategy) sem um segundo uso hoje.
|
|
39
|
+
- Camada que só repassa a chamada.
|
|
40
|
+
- Config ou parâmetro para um caso que ninguém pediu (o "e se um dia eu quiser voar com o carro":
|
|
41
|
+
construa as asas quando for voar).
|
|
42
|
+
- Stdlib, plataforma ou dependência já instalada reinventada à mão.
|
|
43
|
+
|
|
44
|
+
No plano, todo CRIAR de abstração diz no `why` o segundo uso ou a próxima adição que ela barateia.
|
|
45
|
+
|
|
46
|
+
## Legibilidade
|
|
47
|
+
|
|
48
|
+
- **Nomes:** descritivos, pronunciáveis, buscáveis, na linguagem do domínio. Distinção com
|
|
49
|
+
significado (`sourceAccount`/`targetAccount`, nunca `account1`/`account2`). Sem prefixo de tipo
|
|
50
|
+
nem caractere especial. Classe é substantivo, método é verbo, e um padrão aparece no nome
|
|
51
|
+
(`InvoiceFactory`). Um idioma por identificador.
|
|
52
|
+
- **Funções:** pequenas, com um objetivo só. Poucos parâmetros (passou de 3, agrupe num tipo).
|
|
53
|
+
Sem efeito colateral fora do próprio objeto que o nome não anuncie.
|
|
54
|
+
- **Sem flag booleana que bifurca o método** — são dois métodos:
|
|
55
|
+
|
|
56
|
+
```ts
|
|
57
|
+
// ruim
|
|
58
|
+
sendInvoice(invoice, true);
|
|
59
|
+
// bom
|
|
60
|
+
sendInvoiceByEmail(invoice);
|
|
61
|
+
sendInvoiceByWhatsApp(invoice);
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
- **Condicional positiva:** `if (order.isEmpty)` em vez de `if (!order.hasItems)`; o `!` some na leitura.
|
|
65
|
+
- **Literal repetido vira constante** (detalhe em `architecture-code-comments`).
|
|
66
|
+
- **Estrutura:** por feature/slice (`architecture-vertical-slice-vertical-slice`); variável declarada
|
|
67
|
+
perto do uso; funções de cima para baixo, a chamadora antes da chamada; funções afins juntas; sem
|
|
68
|
+
alinhamento horizontal; formatação pelo formatter do projeto.
|
|
69
|
+
- **Comentários:** `architecture-code-comments` é a regra única.
|
|
70
|
+
|
|
71
|
+
## Extensibilidade
|
|
72
|
+
|
|
73
|
+
A próxima adição provável (uma skill nova, um campo novo de config refletido na tela, um provider
|
|
74
|
+
novo) deve tocar poucos arquivos e nenhum `switch` espalhado. Se exige editar N lugares que precisam
|
|
75
|
+
concordar, o atrito é o registro que falta, não a falta de uma camada genérica.
|
|
76
|
+
|
|
77
|
+
## IA e código
|
|
78
|
+
|
|
79
|
+
Regra que nunca pode ser quebrada fica em código, nunca vira prompt (`ai-agents-guardrails`). O
|
|
80
|
+
inverso também vale: regex, heurística ou árvore de `if` que classifica linguagem natural é candidata
|
|
81
|
+
a structured output, e a troca só entra medida por eval (`ai-agents-evals-with-cache`).
|
|
82
|
+
|
|
83
|
+
## Git
|
|
84
|
+
|
|
85
|
+
A branch é `morph/{f}`, criada pelo CLI. Commits seguem Conventional Commits:
|
|
86
|
+
`<tipo>(<escopo>): <assunto>`, com o tipo em `feat`, `fix`, `refactor`, `test`, `chore`, `docs`,
|
|
87
|
+
`perf`, `ci`, `build`, `style` ou `revert`. O hook `conventional-commit` recusa outra forma.
|
|
88
|
+
|
|
89
|
+
## O que bloqueia no Gate 3
|
|
90
|
+
|
|
91
|
+
Só o que o **diff introduziu**. O que já existia no arquivo vira nota.
|
|
92
|
+
|
|
93
|
+
1. `catch` que engole a exceção.
|
|
94
|
+
2. Flag booleana que bifurca o método.
|
|
95
|
+
3. Efeito colateral escondido: o método muda estado alheio e o nome não diz.
|
|
96
|
+
4. Símbolo, flag ou config sem consumidor, ou código comentado.
|
|
97
|
+
5. Abstração ou camada sem segundo uso e sem `why` no plano.
|
|
98
|
+
6. Literal mágico repetido em comparação.
|
|
99
|
+
|
|
100
|
+
O resto (nome, tamanho, ordem, "eu faria diferente") é nota.
|
|
@@ -380,20 +380,18 @@ app.MapApiEndpoints();
|
|
|
380
380
|
| Errors class | `{Entity}Errors` | `BookErrors` (static, shared por feature) |
|
|
381
381
|
| DTO record (GetAll) | `{Entity}Dto` | `BookDto` |
|
|
382
382
|
| API tag constant | `ApiTags.{Entities}` | `ApiTags.Books` |
|
|
383
|
-
| Entity class | `{Entity}`
|
|
383
|
+
| Entity class | `{Entity}` | `Book` |
|
|
384
384
|
|
|
385
385
|
---
|
|
386
386
|
|
|
387
387
|
## Padrões Obrigatórios
|
|
388
388
|
|
|
389
389
|
1. **Handler records no mesmo arquivo**: `Request` e `Response` são declarados no mesmo arquivo `.cs` que o `Handler`.
|
|
390
|
-
2. **`
|
|
391
|
-
3.
|
|
392
|
-
4. **`
|
|
393
|
-
5.
|
|
394
|
-
6. **`
|
|
395
|
-
7. **`ApplicationDbContext` direto no slice**: CRUD por chave via `DbSet<T>`; queries específicas (joins, projeção, paginação) via LINQ no handler. Nunca uma Application Service layer entre handler e dados; nunca repository genérico (`IRepository<T>`/`IUnitOfWork`).
|
|
396
|
-
8. **`SaveChangesAsync()`** explícito em operações de escrita (Create, Update, Delete).
|
|
390
|
+
2. **`result.Match()`** nos endpoints — o sucesso recebe o valor no lambda (nunca ler `result.Value`) e a falha vira `error.ToHttpResult()` (status pelo `ErrorType`, nunca `Results.BadRequest` para todo erro).
|
|
391
|
+
3. **IApiEndpoint `internal`** — endpoints não são públicos.
|
|
392
|
+
4. **`{Entity}Errors` compartilhado** por todas as operações da feature (1 arquivo por feature, não por slice).
|
|
393
|
+
5. **`ApplicationDbContext` direto no slice**: CRUD por chave via `DbSet<T>`; queries específicas (joins, projeção, paginação) via LINQ no handler. Nunca uma Application Service layer entre handler e dados; nunca repository genérico (`IRepository<T>`/`IUnitOfWork`).
|
|
394
|
+
6. **`SaveChangesAsync()`** explícito em operações de escrita (Create, Update, Delete).
|
|
397
395
|
9. **Sem validator em GetAll** — GetAllBooksHandler não tem validator (sem parâmetros para validar).
|
|
398
396
|
|
|
399
397
|
---
|
|
@@ -402,7 +400,7 @@ app.MapApiEndpoints();
|
|
|
402
400
|
|
|
403
401
|
| Anti-pattern | Por quê não |
|
|
404
402
|
|--------------|-------------|
|
|
405
|
-
| `AggregateRoot` | VSA não usa DDD — entidade simples
|
|
403
|
+
| `AggregateRoot` | VSA não usa DDD — entidade simples |
|
|
406
404
|
| `DomainEvent` | VSA não tem Domain Events — reações são handlers separados |
|
|
407
405
|
| `MediatR` / `IRequest` / `IRequestHandler` | Substituído por `IHandler<TRequest, TResponse>` próprio |
|
|
408
406
|
| DTOs compartilhados entre features | Cada slice define seus próprios records |
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
# Analyzers .NET — o build cobra, a prosa não repete
|
|
2
|
+
|
|
3
|
+
> **Scope:** dotnet
|
|
4
|
+
> **Layer:** 0 (resumo injetado pela rule `csharp-standards` (`**/*.cs`, `**/*.csproj`); o `stack-scan` acusa o projeto sem estes arquivos)
|
|
5
|
+
> **Keywords:** analyzer, roslyn, BannedApiAnalyzers, BannedSymbols, globalconfig, Guid.NewGuid, CreateVersion7, CancellationToken, CA2016, RS0030
|
|
6
|
+
> **Load When:** projeto .NET sem `BannedSymbols.txt`, ou o build acusou RS0030/CA2016
|
|
7
|
+
|
|
8
|
+
**Verified against:** .NET SDK 10.0.402, Microsoft.CodeAnalysis.BannedApiAnalyzers 5.6.0 (sonda: `Guid.NewGuid()` → erro RS0030, token não repassado → erro CA2016, código conforme → 0 erros). Last-verified: 2026-10-18.
|
|
9
|
+
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
Regra de código que tem motivo e nunca pode ser quebrada vira **erro de build**, não frase que o
|
|
13
|
+
agente relê a cada sessão. Estes três arquivos na raiz da solution bastam; nenhum `.csproj` muda.
|
|
14
|
+
|
|
15
|
+
## Arquivos
|
|
16
|
+
|
|
17
|
+
`Directory.Build.props` (se já existe, some o `ItemGroup` ao dele):
|
|
18
|
+
|
|
19
|
+
```xml
|
|
20
|
+
<Project>
|
|
21
|
+
<ItemGroup>
|
|
22
|
+
<PackageReference Include="Microsoft.CodeAnalysis.BannedApiAnalyzers" Version="5.6.0" PrivateAssets="all" />
|
|
23
|
+
<AdditionalFiles Include="$(MSBuildThisFileDirectory)BannedSymbols.txt" />
|
|
24
|
+
<GlobalAnalyzerConfigFiles Include="$(MSBuildThisFileDirectory)morph.globalconfig" />
|
|
25
|
+
</ItemGroup>
|
|
26
|
+
</Project>
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
Com Central Package Management (`Directory.Packages.props`), a versão vai lá e o
|
|
30
|
+
`PackageReference` fica sem `Version`.
|
|
31
|
+
|
|
32
|
+
`BannedSymbols.txt`:
|
|
33
|
+
|
|
34
|
+
```text
|
|
35
|
+
M:System.Guid.NewGuid;Use Guid.CreateVersion7(): ordenado, nao fragmenta o indice da PK
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
`morph.globalconfig`:
|
|
39
|
+
|
|
40
|
+
```ini
|
|
41
|
+
is_global = true
|
|
42
|
+
dotnet_diagnostic.RS0030.severity = error
|
|
43
|
+
dotnet_diagnostic.CA2016.severity = error
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## O que cada regra compra
|
|
47
|
+
|
|
48
|
+
| Diagnóstico | Bloqueia | Por quê |
|
|
49
|
+
|---|---|---|
|
|
50
|
+
| RS0030 | `Guid.NewGuid()` | UUID v4 é aleatório: PK nele fragmenta o índice B-tree do Postgres a cada insert; `Guid.CreateVersion7()` é ordenado no tempo |
|
|
51
|
+
| CA2016 | método que recebe `CancellationToken` e não o repassa | requisição cancelada continua segurando conexão do banco e chamada ao LLM |
|
|
52
|
+
|
|
53
|
+
API nova para banir entra como linha no `BannedSymbols.txt` (formato `M:`/`T:`/`P:` com a
|
|
54
|
+
mensagem depois do `;`), com o porquê na mensagem. Nunca uma regra nova em prosa.
|
|
55
|
+
|
|
56
|
+
## O que não é regra
|
|
57
|
+
|
|
58
|
+
`sealed`, primary constructor, `var`, expression-bodied member e ordem de `using` são gosto: nem
|
|
59
|
+
prosa nem analyzer. Siga o arquivo vizinho, e o formatter do projeto decide a forma.
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
|
|
12
12
|
## Overview
|
|
13
13
|
|
|
14
|
-
Each feature in VSA is a self-contained slice with 5 files in a feature folder. Handlers implement `IHandler<TRequest, TResponse>` (not MediatR).
|
|
14
|
+
Each feature in VSA is a self-contained slice with 5 files in a feature folder. Handlers implement `IHandler<TRequest, TResponse>` (not MediatR). The data path is `Handler -> ApplicationDbContext` — no service layer, no generic repository in between.
|
|
15
15
|
|
|
16
16
|
---
|
|
17
17
|
|
|
@@ -108,11 +108,8 @@ public sealed class CreateBookHandler(
|
|
|
108
108
|
|
|
109
109
|
### Key Patterns
|
|
110
110
|
|
|
111
|
-
- **`
|
|
112
|
-
- **`
|
|
113
|
-
- **Primary constructors** — `(ApplicationDbContext db)` replaces field + constructor boilerplate
|
|
114
|
-
- **`Guid.CreateVersion7()`** — time-sortable, index-friendly UUID (not `Guid.NewGuid()`)
|
|
115
|
-
- **`CancellationToken`** — passed through every async call
|
|
111
|
+
- **`record`** — DTOs are immutable value types
|
|
112
|
+
- **`Guid.CreateVersion7()` and a forwarded `CancellationToken`** — enforced by the build (`backend-dotnet-analyzers`), not by review
|
|
116
113
|
- **`Result<T>` return** — never throw for expected failures
|
|
117
114
|
|
|
118
115
|
---
|
|
@@ -149,7 +146,7 @@ internal sealed class CreateBookEndpoint : IApiEndpoint
|
|
|
149
146
|
|
|
150
147
|
### Endpoint Conventions
|
|
151
148
|
|
|
152
|
-
- **`internal
|
|
149
|
+
- **`internal`** — endpoints are not public API
|
|
153
150
|
- **`IApiEndpoint`** — single interface for auto-discovery
|
|
154
151
|
- **Handler injected via DI** — resolved as `IHandler<TReq, TRes>` parameter
|
|
155
152
|
- **`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
|
|
@@ -299,13 +296,9 @@ builder.Services.AddExceptionHandler<CustomExceptionHandler>()
|
|
|
299
296
|
| Use MediatR `IRequest`/`Send()` | Use `IHandler<TReq, TRes>` resolved directly from DI |
|
|
300
297
|
| Create a service/manager layer | Handler uses `ApplicationDbContext` directly |
|
|
301
298
|
| Add a generic repository (`IRepository<T>`/`IUnitOfWork`) | `DbSet<T>` is the repository; `DbContext` is the unit of work |
|
|
302
|
-
| Use
|
|
303
|
-
| Make handler classes non-sealed | Always `sealed class` |
|
|
304
|
-
| Use mutable DTOs (class with setters) | Always `sealed record` with positional parameters |
|
|
305
|
-
| Omit `CancellationToken` from async methods | Pass `CancellationToken` through every async call |
|
|
299
|
+
| Use mutable DTOs (class with setters) | Always `record` with positional parameters |
|
|
306
300
|
| Put business logic in endpoints | Endpoints only map HTTP and call `Match()` |
|
|
307
301
|
| Share handlers across features | Each feature has its own handler, even if similar |
|
|
308
|
-
| Use constructor + field for DI | Use primary constructors: `class Handler(IDep dep)` |
|
|
309
302
|
| Group by technical concern (Controllers/, Services/) | Group by feature: `Features/BookFeature/CreateBook/` |
|
|
310
303
|
| Route data access through any indirection layer | Inject `DbContext` and write LINQ in the slice; dedicated store only with `plan.md` (`## Decisões`) justification |
|
|
311
304
|
| Use raw SQL (`FromSqlRaw` / `ExecuteQuery(string)`) by default | Use LINQ; raw SQL only with a `plan.md` (`## Decisões`) justification |
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
> **Keywords:** mcp, model-context-protocol, mcp-tools, context7, playwright, mcp-client
|
|
6
6
|
> **Load When:** an MCP tool is used in any morph-spec phase
|
|
7
7
|
|
|
8
|
-
**Verified against:** Claude Code 2.1.282 MCP tooling — context7, Playwright (as plugin and as a project MCP server); GitHub via `gh` CLI; Neon via its MCP or `neonctl
|
|
8
|
+
**Verified against:** Claude Code 2.1.282 MCP tooling — context7, Playwright (as plugin and as a project MCP server); GitHub via `gh` CLI; Neon via its MCP or `neonctl`; Microsoft Learn MCP (`https://learn.microsoft.com/api/mcp`, tools listed by `tools/list` on 2026-10-17). Last-verified: 2026-10-17.
|
|
9
9
|
|
|
10
10
|
---
|
|
11
11
|
|
|
@@ -138,6 +138,7 @@ Fallback: manual image creation, stock photos, or placeholder divs.
|
|
|
138
138
|
|------|----------|---------|
|
|
139
139
|
| **Neon CLI (`neonctl`) / psql** | Database schema, tables, relationships, RLS | `psql <DB_URL> -c "SELECT ..."` |
|
|
140
140
|
| **Context7** | Library docs for architecture decisions | `mcp__context7__query-docs()` |
|
|
141
|
+
| **Microsoft Learn** (stack .NET) | Official docs for a .NET/EF Core/MAF decision (limits, what is new, migration) | `mcp__microsoft-learn__microsoft_docs_search({ query })` |
|
|
141
142
|
| **GitHub CLI (`gh`)** | Check existing code patterns, PRs | `gh search code` (Bash) |
|
|
142
143
|
|
|
143
144
|
```bash
|
|
@@ -213,6 +214,7 @@ gh issue create --title "F1: Create Entity Lead" \
|
|
|
213
214
|
| **Neon CLI (`neonctl`) / psql** | Run migrations, create RLS policies | `dotnet ef database update` |
|
|
214
215
|
| **GitHub CLI (`gh`)** | Create PR, push branches | `gh pr create` (Bash) |
|
|
215
216
|
| **Context7** | Look up API usage during coding | `mcp__context7__query-docs()` |
|
|
217
|
+
| **Microsoft Learn** (stack .NET) | Current .NET 10 / EF Core / ASP.NET Core / MAF API: search, then fetch the page when the excerpt is cut | `mcp__microsoft-learn__microsoft_docs_search({ query })` → `microsoft_docs_fetch({ url })`; `microsoft_code_sample_search` for snippets |
|
|
216
218
|
| **Playwright** | Smoke test deployed features, verify UI | `mcp__playwright__browser_navigate()` |
|
|
217
219
|
| **Vercel** | Deploy, check logs, manage env vars | `mcp__vercel__list_projects()` |
|
|
218
220
|
| **Azure CLI** | Resource management, deployments | `az` commands |
|
|
@@ -36,7 +36,6 @@ framework/templates/
|
|
|
36
36
|
│
|
|
37
37
|
├── integrations/ # asaas-client, asaas-webhook, neon-auth-config
|
|
38
38
|
├── ui/ # design-system.css
|
|
39
|
-
├── meta-prompts/ # fusion, squad-leaders, validators
|
|
40
39
|
├── examples/ # design-system-examples.md
|
|
41
40
|
└── REGISTRY.json # template metadata — the index every consumer reads
|
|
42
41
|
```
|