@polymorphism-tech/morph-spec 8.46.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 +5 -5
- package/README.md +2 -2
- package/bin/morph-spec.js +1 -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 +1 -1
- package/framework/MORPH.md +8 -8
- 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 +3 -3
- 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/feature.schema.json +1 -3
- 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 +3 -3
- 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 +3 -12
- 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/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/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/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
|
@@ -28,14 +28,14 @@ These checks verify that the morph-spec workflow is complete for the feature bei
|
|
|
28
28
|
# Verify implementation is complete
|
|
29
29
|
morph-spec status {feature}
|
|
30
30
|
morph-spec approval-status {feature}
|
|
31
|
-
morph-spec
|
|
31
|
+
morph-spec verify {feature}
|
|
32
32
|
```
|
|
33
33
|
|
|
34
34
|
| Check | Command | Pass Criteria |
|
|
35
35
|
|-------|---------|---------------|
|
|
36
36
|
| Phase is implement or later | `status` | phase derived as `implement` or `review` |
|
|
37
37
|
| All gates approved | `approval-status` | proposal, plan, review gates all approved |
|
|
38
|
-
|
|
|
38
|
+
| Build, tests and validators pass | `verify` | every node green |
|
|
39
39
|
| All tasks complete | `status` → tasks | 0 tasks in `pending` or `in_progress` |
|
|
40
40
|
| Recap generated | Read `3-implement/recap.md` | File exists and is non-empty |
|
|
41
41
|
|
|
@@ -43,13 +43,7 @@ morph-spec validate {feature}
|
|
|
43
43
|
|
|
44
44
|
---
|
|
45
45
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
```bash
|
|
49
|
-
dotnet test --verbosity normal
|
|
50
|
-
```
|
|
51
|
-
|
|
52
|
-
**Pass criteria:** 100% test pass rate. Zero tolerance — any failing test blocks deployment.
|
|
46
|
+
**Tests:** run them only through `morph-spec verify {feature}` — never a bare `dotnet test`/`npm test`, which bypasses the heavy-job queue and leaves no proof the gates read. Any red node blocks deployment.
|
|
53
47
|
|
|
54
48
|
---
|
|
55
49
|
|
|
@@ -109,6 +103,8 @@ dotnet ef migrations add <migration-name>
|
|
|
109
103
|
- .dockerignore present
|
|
110
104
|
|
|
111
105
|
**How to check:**
|
|
106
|
+
`Dockerfile.api` and `Dockerfile.web` are the names the `docker-dockerfile-api`/`docker-dockerfile-web` templates emit (`outputName` in REGISTRY.json); check the project's real names first.
|
|
107
|
+
|
|
112
108
|
```bash
|
|
113
109
|
# Validate API Dockerfile
|
|
114
110
|
docker build --check -f Dockerfile.api .
|
|
@@ -173,25 +169,6 @@ grep -E "REPLACE_WITH_" coolify.json 2>/dev/null && echo "✗ coolify.json still
|
|
|
173
169
|
|
|
174
170
|
---
|
|
175
171
|
|
|
176
|
-
## Example Output
|
|
177
|
-
|
|
178
|
-
```
|
|
179
|
-
MORPH Pre-Flight Check: Deployment
|
|
180
|
-
|
|
181
|
-
Packages: No version conflicts detected
|
|
182
|
-
Migrations: No pending changes
|
|
183
|
-
Dockerfile: Valid multi-stage build (API + Web)
|
|
184
|
-
Docker Compose: Valid configuration
|
|
185
|
-
Secrets: No hardcoded credentials found
|
|
186
|
-
Coolify: Health endpoint found at /healthz
|
|
187
|
-
|
|
188
|
-
Summary: 6 passed, 0 failed, 0 warnings
|
|
189
|
-
|
|
190
|
-
READY TO DEPLOY
|
|
191
|
-
```
|
|
192
|
-
|
|
193
|
-
---
|
|
194
|
-
|
|
195
172
|
## Usage Workflow
|
|
196
173
|
|
|
197
174
|
### Before creating PR for production
|
|
@@ -28,7 +28,7 @@ Conduz a feature do request inicial até o Gate 2 (plano aprovado), sem interven
|
|
|
28
28
|
- Apresenta o **Gate 1** (que pausa sempre no modo entrevista).
|
|
29
29
|
|
|
30
30
|
3. **Aguardar aprovação do Gate 1.**
|
|
31
|
-
- Resposta válida: "aprovado"
|
|
31
|
+
- Resposta válida (o que o hook `state-sync` reconhece): começa com "aprovado" ou contém "pode avançar", "pode implementar" ou "pode prosseguir". Outra frase não grava o gate.
|
|
32
32
|
- Se ajustes solicitados: aplicar e reapresentar o gate.
|
|
33
33
|
|
|
34
34
|
4. **Após Gate 1, invocar `morph-plan`** (skill em `.claude/skills/morph-plan/`):
|
|
@@ -37,7 +37,7 @@ Conduz a feature do request inicial até o Gate 2 (plano aprovado), sem interven
|
|
|
37
37
|
- Apresenta o **Gate 2** com template fixo (ver SKILL.md passo 5).
|
|
38
38
|
|
|
39
39
|
5. **Aguardar aprovação do Gate 2.**
|
|
40
|
-
- Resposta válida:
|
|
40
|
+
- Resposta válida: as mesmas do Gate 1 (ex.: "aprovado", "pode implementar").
|
|
41
41
|
- Se ajustes solicitados: aplicar e reapresentar o gate.
|
|
42
42
|
|
|
43
43
|
6. **Após Gate 2:** sinalizar ao usuário que `/morph-apply {feature-name}` está pronto para iniciar a implementação.
|
|
@@ -27,16 +27,13 @@ Find solutions for common .NET/Next.js development errors and problems.
|
|
|
27
27
|
|
|
28
28
|
### Level 1: Search Standards (Primary)
|
|
29
29
|
|
|
30
|
-
|
|
30
|
+
Find the standard that covers the error, then read its matching section:
|
|
31
31
|
|
|
32
32
|
```bash
|
|
33
|
-
|
|
34
|
-
Grep: "{keywords}" in framework/standards/ (glob: "*.md")
|
|
33
|
+
morph-spec standards --find "{keywords}"
|
|
35
34
|
Grep: "{keywords}" in .morph/framework/standards/ (glob: "*.md")
|
|
36
35
|
```
|
|
37
36
|
|
|
38
|
-
Read matching files and extract the relevant solution sections.
|
|
39
|
-
|
|
40
37
|
### Level 2: Search Codebase (Automatic)
|
|
41
38
|
|
|
42
39
|
If standards don't cover the issue, search the codebase for the error pattern:
|
|
@@ -73,15 +70,6 @@ If the user asks about a SignalR error:
|
|
|
73
70
|
2. If not found: WebSearch with `site:github.com/dotnet/aspnetcore SignalR connection closed`
|
|
74
71
|
3. Return formatted relevant links
|
|
75
72
|
|
|
76
|
-
## Available Categories
|
|
77
|
-
|
|
78
|
-
| Category | Description |
|
|
79
|
-
|-----------|-----------|
|
|
80
|
-
| `efcore` | Entity Framework Core, DbContext, migrations, queries |
|
|
81
|
-
| `nextjs` | Next.js App Router, pages, components, routing |
|
|
82
|
-
| `auth` | Authentication, authorization, tokens, claims |
|
|
83
|
-
| `deploy` | Deployment, CI/CD, pipelines, containers |
|
|
84
|
-
|
|
85
73
|
## Output Format
|
|
86
74
|
|
|
87
75
|
When finding a solution, present in the following format:
|
package/framework/docs/CLI.md
CHANGED
|
@@ -46,7 +46,7 @@
|
|
|
46
46
|
- **`morph-spec dag <feature>`** — análise do DAG de tasks.json com decisão sequencial vs paralelo **ponderada por effort** (raízes S vão inline, nunca ganham agente próprio). Também cruza `tasks.json` com os use cases: `usecase` apontando para um `UC-{nn}` inexistente é **erro** (exit 1, não despacha); pós-condição órfã e `doneCriteria` que parafraseou em vez de copiar são avisos. UC com nome fora do padrão `UC-{nn}-{slug}.md` (slug com maiúscula ou `_`) continua encontrado, com aviso: o `finish` não o promove ao acervo. `doneCriteria` com vocabulário de artefato (`grep`, "contém a string", "nenhum arquivo", glob `**/`…) sem `Por que artefato:` também é aviso, sob `⚠ Critérios de artefato` — o exit não muda, e o `--json` traz a chave irmã `artifactCriteria`. Os achados de ciclo de vida do `usecase check` (task sobre UC `superseded`, revogação que o `finish` não aplicaria) saem sob `⚠ Ciclo de vida do acervo`, também só aviso, na chave `lifecycle`. **Tamanho de task:** task com mais de 8 `outputs` (`MAX_TASK_OUTPUTS`) é **erro** (exit 1) enquanto o Gate 2 não foi aprovado — fatie por pós-condição — e só aviso depois dele (chore/hotfix nascem com o plano aprovado por política); o `--json` traz `taskSize: {max, oversized, blocking}`. `--mermaid` nunca é bloqueado.
|
|
47
47
|
- **`morph-spec verify <feature> [task] [--json] [--timeout <ms>] [--filter <ids>] [--full] [--record-baseline] [--refresh-cache] [--linux] [--linux-image <img>] [--skip-tests] [--skip-build] [--skip-e2e] [--fresh-e2e] [--no-persist] [--ci]`** — cadeia determinística build+testes+validadores+**e2e** (código antes do juiz LLM); escopo de task quando informado, senão a feature inteira. O nó `e2e` é o único que **executa** a aplicação: sobe o `docker compose` do projeto, espera todo serviço ficar saudável e roda as specs Playwright quando existem (sem specs, ainda vale como smoke — "a app sobe" já pega o serviço não registrado que derruba o startup e passa batido no build). Aplicabilidade: escopo de feature (Gate 3) **sempre** roda; escopo de task roda para `group` `frontend`/`backend`/`infra` e dispensa `tests`/`docs`. **Dispensa legítima × setup faltando:** projeto sem compose e sem a integração `docker` é `skip` com razão; projeto que **declara** `docker` e não tem compose é `fail` — é verificação que nunca foi montada, não projeto que não containeriza. **Descoberta de testes e relógio:** a varredura por `*Tests.csproj` **não enxerga worktrees** — nem os convencionais (`worktrees/`, `.worktrees/`) nem os registrados em `git worktree list` fora da convenção. Um worktree é o checkout de OUTRA feature do mesmo repo: incluí-lo dobrava o relógio e reportava sobre código que não está sob verificação (em campo isso deu `tests: fail` no timeout exato, com o sumário da 1ª execução dizendo "Com falha: 0"). O timeout do nó `tests` deixou de ser fixo: `--timeout` (que agora cobre build **e** tests) > `project.testTimeoutMs` no `config.json` > **120 s por projeto de teste encadeado** — um encadeamento nunca nasce condenado pelo tamanho. Quando o relógio estoura, o nó diz isso com nome (`timeout após N ms na execução X de Y — nenhum teste falhou`) em vez de devolver log truncado. **`--fresh-e2e` é destrutivo por definição:** derruba a stack anterior **com os volumes nomeados** (`down -v`), apaga o carimbo de frescor, sobe com `--build` e força o seed de auth a rodar de novo. Sem o `down -v`, "fresco" reconstruiria a imagem e preservaria o banco velho — e um `storageState` capturado contra aquele banco continuaria autenticando um usuário que não existe mais (403 que parece app quebrada). **No escopo de task o recorte é automático:** `verify {f} {task}` deixa o `group` da task escolher os nós e os arquivos de teste de `outputs` estreitarem o comando dentro de cada nó — o relógio de uma task não paga a suíte inteira (regras, veredito e limites em *O recorte por task*, na seção **Suítes de teste**). **`--filter <ids>` força a escolha de nós** (lista separada por vírgula, casando `id` exato ou substring; desliga a escolha pelo `group`, e o estreitamento dentro dos nós continua) e **`--full` desliga o recorte** (o `group` não escolhe nós e nenhum comando é estreitado; os nós com `e2e: true` e os de `runner: "evals"` continuam só no escopo de feature; no escopo de feature `--full` é no-op, porque ali nada é recortado). O Gate 3 continua sendo `verify {f} --json` **sem filtro**, porque a prova que o avaliador lê tem de cobrir todos os nós (o CI idem) — e um `verify {f} --filter …` de escopo de **feature** não persiste o stamp (`persisted: false`, `reason: 'filtered'`), nem um de escopo de feature com `--skip-tests` (`reason: 'skip-tests'`) ou `--skip-build` (`reason: 'skip-build'`): um nó pulado não reprova o rollup, e rodada parcial nunca fica no lugar da prova do Gate 3. No escopo de task as três gravam normalmente — o stamp já diz `task:{id}`, e o Gate 3 pausa nele. O nó não casado **aparece** em `runs[]` como `skip` nomeando o filtro, em vez de sumir — suíte que some do relatório é verde sobre código não testado. E `--filter` que não casa nenhum nó é **erro de uso (exit 2)**, checado antes do build: filtrar tudo e reportar verde seria a forma mais barata de recriar o defeito que a lista de nós existe para matar. **`--record-baseline` é a ÚNICA via de escrita da baseline:** mede cada nó numa execução real e grava `project.tests[].expectedCount` de volta no `config.json` (escrita atômica `tmp.{pid}`+rename, preservando todo o resto do arquivo). A regra do `morph-plan` — *baseline é um COMANDO, nunca um número* — sobrevive porque ninguém digita o número: um valor escrito à mão descreve o que alguém acreditou, e a divergência entre a crença e a suíte é o defeito que a regra existe para matar. Recusa alto e claro em vez de improvisar: execução **vermelha** não grava (congelaria a regressão), execução **parcial** não grava (com `--filter` alguns nós ficam sem medida, e baseline de meia frota é afirmação sobre a outra metade que ninguém fez), contagem **desconhecida** não grava (`null` nunca vira `0`), e **sem `config.json` é erro de uso (exit 2)** — o arquivo nunca é criado implicitamente. **Com `{task}` também é erro de uso (exit 2)**, recusado antes do build: no escopo de task o verify recorta nós e testes, e baseline de recorte não é baseline — o gravador ainda recusa, uma segunda vez, qualquer medição que carregue `selection`. A execução que gravou a baseline persiste o stamp; a que não gravou nada, não. **`--linux` roda as suítes dentro de um container Linux** (`mcr.microsoft.com/dotnet/sdk:10.0` por default; sobrescrevível por `--linux-image` ou por `project.tests[].linuxImage`), e a árvore sob teste é **copiada** para o filesystem do container, não montada: o bind mount do Docker Desktop no Windows atravessa 9p/gRPC-FUSE, que é **case-insensitive** — rodar em cima do mount devolveria o mesmo verde do Windows e não provaria nada sobre defeitos de caixa (`Link=`, `Uri.TryCreate`), que são exatamente os que só aparecem no Linux. A raiz é montada **read-only** em `/src` só como origem do `tar` (com exclusão de `node_modules`, `bin`, `obj`, `.git` e os diretórios-contêiner de worktree), a cópia vive em `/work`, e cada nó roda de `/work/{cwd}`. Sem Docker, **erro de uso (exit 2)**, jamais um `skip`: "não deu para checar no Linux" lido como verde é o próprio defeito. `--linux` com `--skip-tests` se anulam (erro de uso), e um nó `e2e: true` é pulado no container com motivo (docker-em-docker fora de escopo). O resultado marca `nodes.tests.environment = { kind: 'linux-container', image }` — sem isso, um verde de container e um verde de host viram indistinguíveis no `feature.json` e a próxima pessoa não sabe o que foi provado. **`--no-persist` não grava nada no `feature.json`** — nem o histórico `verifications.*` nem o stamp `verification` que o gate lê: a execução vira sonda, nunca prova para gate algum, qualquer que seja a regra de carimbo do escopo. E o `--filter` só estreita o nó de testes (build, validadores e e2e rodam igual). Por isso a execução de evidência de eval do `morph-implement` §5 é `verify {f} --filter {id-do-nó-evals} --skip-e2e --no-persist`: só o eval, sem stack, sem escrever no `feature.json`. **Onde grava:** só na árvore DONA da feature (`resolveFeatureTree`, a regra do guard dos mutadores, perguntada da raiz) — o worktree com `morph/{f}` checada, mesmo sem ponteiro `worktree.path` na raiz, ou a raiz quando nenhum worktree vivo a possui. Fora dela, `persisted: false` com `reason: 'unregistered-worktree'` (outro worktree, como o `morph-task/{n}` de um sub-agent) ou `'owner-tree-elsewhere'` (a raiz, com o dono vivo em outro lugar). **Rodada repetida:** no escopo de feature, se nada mudou fora de `.morph/` desde o verify de feature anterior (HEAD, diff, arquivos não rastreados e o `config.json`), o `--json` traz `repeatOf: { ts, status, sha }` e a saída humana avisa — é aviso, a rodada executa e carimba igual, porque só o código sabe que a árvore não mudou, não que o ambiente (Docker, banco) também não. Forma de `project.tests[]`, prefixo `[tests/{id}]` do `feedback[]`, regra de contagem/baseline e a divisão loop × suíte: seção **Suítes de teste** abaixo. **Validador que não pôde checar:** o `reuse` sem grafo não acha nada, e um `validators: pass` dos outros validadores o esconderia — então o `feedback[]` diz `[validators/reuse] não rodou: …`, e o nó leva `skipped: [{ validator, reason, unavailable }]` (quando o nó inteiro pula, a própria linha dele já dá o motivo). **Nós pesados (build, testes, e2e) esperam por slot** (`morph-spec heavy`): estouro da espera sai **exit 3** com `status: "busy"`, `persisted: false`, sem stamp e sem evento — não é vermelho, e o orquestrador repete a chamada.
|
|
48
48
|
- **`morph-spec verify <feature> --ci [--json]`** — a prova de escopo de feature tirada do **CI do projeto** (GitHub Actions), não da máquina: faz `git push -u origin morph/<feature>`, espera o run do commit e transforma o `verify --json` que o run publicou (artefato `morph-verify`) no **mesmo stamp** de uma rodada local, com `source: "ci"` e `ciRun` (URL do run); `sha` é o `headSha` do run, então o `verificationStale` compara o código que o CI viu com o HEAD. Nenhuma regra de gate muda: e2e pulado, escopo, evals e stale valem igual. Só o **artefato** vira prova, nunca o "verde" do run: workflow de lint, deploy ou de terceiro não compra o Gate 3. **Sem workflow** que rode `morph-spec verify` publicando `morph-verify.json`, cria e commita `.github/workflows/morph-verify.yml` na branch da feature (gatilho `push` em `morph/**`, `.morph/` fora do gatilho exceto o `config.json`, `cancel-in-progress`, setup de .NET por `global.json`/stack, install por lockfile, browsers do Playwright onde há config, e o `morph-spec` na versão de `.morph/.morphversion`). Um run sobre um commit anterior vale quando só `.morph/` andou desde ele; run `cancelled`/`skipped`/`startup_failure` não é prova. **Sem CI cai para o verify local, com a razão** (`project.ci: false`, origin fora do GitHub, `gh` ausente ou sem login, branch que não é `morph/<feature>`, token HTTPS sem o escopo `workflow` para criar o arquivo, push recusado, workflow que não disparou nem com `gh workflow run`, run sem o artefato). Código não commitado é **erro de uso (exit 2)**: o CI só vê commits. CI rodando depois de 50 min é **exit 3** (`status: "pending"`), sem stamp — não é vermelho. Com task, `--filter`, `--skip-*`, `--fresh-e2e`, `--linux`, `--record-baseline`, `--refresh-cache` ou `--no-persist`, erro de uso. Vermelho imprime `gh run view <id> --log-failed`. **O runner só tem o que está commitado:** o que testes e e2e leem de arquivo gitignored (`.env`, `appsettings.*.json` local, connection string do Neon, `storageState`) não existe lá, e o run sai vermelho sem defeito de código. Declare o valor como secret do repositório e exponha-o no `env:` do job em `morph-verify.yml` (o arquivo é do projeto: editar à mão é o esperado, e o `findCiWorkflow` continua aceitando enquanto ele chamar `morph-spec verify` publicando `morph-verify.json`), ou desligue com `project.ci: false`.
|
|
49
|
-
- **Validador `code-budget` (nó `validators` do `verify`)** — mede o que a árvore **adicionou** contra a branch default: `git diff -U0` a partir do merge-base com `origin/{default}` (ou `{default}` local), arquivo novo não rastreado conta inteiro e arquivo movido sem edição não conta. Lê `.cs`/`.ts`/`.tsx`/`.js`/`.jsx`/`.mjs`/`.cjs`, fora de `.d.ts`, de `node_modules`/`bin`/`obj`/`dist`/`.morph` e de código gerado (`*.Designer.cs`, `*.g.cs`, `*ModelSnapshot.cs`). Três medidas, cada uma **um aviso, nunca erro
|
|
49
|
+
- **Validador `code-budget` (nó `validators` do `verify`)** — mede o que a árvore **adicionou** contra a branch default: `git diff -U0` a partir do merge-base com `origin/{default}` (ou `{default}` local), arquivo novo não rastreado conta inteiro e arquivo movido sem edição não conta. Lê `.cs`/`.ts`/`.tsx`/`.js`/`.jsx`/`.mjs`/`.cjs`, fora de `.d.ts`, de `node_modules`/`bin`/`obj`/`dist`/`.morph` e de código gerado (`*.Designer.cs`, `*.g.cs`, `*ModelSnapshot.cs`). Três medidas, cada uma **um aviso, nunca erro**: (a) **linhas de comentário adicionadas que citam processo** — `UC-nn`, `ADR-nn`, `FR-nn`, `T{n}`, `Gate N`, `spec.md`, mockup — avisa a partir de uma, com a contagem sobre o total de linhas de comentário adicionadas e até 4 `arquivo:linha` (standard `architecture-code-comments`); comentário é o que o varredor de `src/lib/ai/comment-stripper.js` separa de string, com template e regex literal de TS/JS; (b) **linhas não vazias de teste ÷ de produção** — avisa acima de **1,5×**, e só quando o teste adicionado passa de **40 linhas** (a regressão de um bug de duas linhas cabe abaixo disso); sem linha de produção, o aviso dá as duas contagens sem razão. (c) **densidade de comentário** — linhas de comentário adicionadas ÷ linhas de código adicionadas (as não vazias menos as de comentário), doc-comment (`///`, JSDoc) incluído — avisa acima de **1 a cada 10**, e só a partir de **40 linhas** de código, com até 4 arquivos mais comentados e suas contagens (o padrão do standard `architecture-code-comments` é nenhum comentário). Teste é o que o validador `reuse` já trata como teste: arquivo `.test.` ou `.spec.` com extensão `js`/`jsx`/`ts`/`tsx`, e tudo sob `__tests__/`, `test/`, `tests/` ou pasta terminada em `Test`/`Tests` (`Foo.Tests/`, `FooTests/`). O resto conta como produção, inclusive `a.test.mjs`, `a.test.cjs` e `*.e2e.ts` fora dessas pastas. Sem base git, ou sem linha de código adicionada, pula com razão; qualquer falha de medição também vira skip, nunca erro. Fica fora do modo `light`. Excesso justificado não silencia o aviso: a justificativa mora na task (orçamento do `morph-plan` passo 5), e é lá que o avaliador a lê.
|
|
50
50
|
- **`morph-spec mutate [feature] [--plan <arquivo>] [--out <arquivo>] [--no-subset] [--allow-dirty] [--jobs <n>] [--timeout <ms>] [--restore] [--json]`** — mutação **dirigida** para o avaliador do Gate 3: no máximo **3** mutantes, só quando o diff tem uma guarda que um UC exige e o avaliador não achou o teste que a morde. **Manifesto** (`--plan`, default `4-review/mutants.json`): `{"mutants":[{"id":"M1","file":"src/x.cs","line":42,"find":"a && b","replace":"a","expect":"OrdersTests.RecusaSemItens deve falhar"}]}` — `expect` é obrigatório (qual teste deve falhar e por quê, dito antes de rodar), `find` começa na `line` e é único nela; barra invertida do código vai como `{{BS}}` (o transporte do Bash a reduz). Mais de 3 mutantes ou `expect` ausente: exit 2. **Método:** recusa arquivo não commitado (`--allow-dirty` assume o risco); snapshot dos bytes fora da árvore (`<git-common-dir>/morph-spec/mutation/<run>/`) com journal — rodada que morreu se desfaz por `mutate --restore`; em .NET, uma chave `MORPH_MUTANT` por mutante de expressão e um build só (o que a chave não compila cai para rebuild real, e falha de build é `build-error`, nunca morte); depois **o subconjunto decide**: os testes que a feature alterou mais os que citam os tipos mutados (vitest/jest usam `related`) — a ferramenta escolhe, não o avaliador. `killed` = falha **nova** contra a rodada-base do mesmo escopo, com o motivo literal do TRX; `survived` no subconjunto é o veredito, com o `expect` ao lado; sem subconjunto utilizável, a suíte inteira roda e a morte tem de se repetir. **Reversão:** bytes do snapshot com md5 conferido, por escrita nova (nunca `os.utime`), rebuild forçado e DLL igual à de antes do 1º mutante. **Exit:** `0` rodou (sobrevivente é achado), `1` a árvore já não compilava, `2` uso, `3` semáforo ocupado, `4` **inseguro** (arquivo não restaurado ou binário que não bate). Segura **um** slot `heavy`.
|
|
51
51
|
- **`morph-spec e2e up|status|down <feature>`** — ciclo de vida do stack de verificação, compartilhando toda a lógica com o nó `e2e` do verify. `up [--fresh]` reusa um stack quente **só** quando ele provadamente serve o código atual: o carimbo compara `HEAD` + `git status --porcelain` + `git diff HEAD`, então qualquer edição, commitada ou não, invalida o reuso (a exceção é o serviço com bind mount do código, que serve o disco por construção). Na dúvida, rebuild — o layer cache do Docker faz disso barato. O motivo impresso diz **o que** decidiu o reuso, e as duas procedências nunca se confundem: `source unchanged since build` é o carimbo, `source is bind-mounted` é o disco. E a segunda só sai quando **todo** serviço com seção `build:` monta a raiz do projeto — um sidecar Playwright montando `./e2e` ao lado de uma app compilada em imagem não fala pela stack inteira; nesse caso quem decide é o carimbo. `--fresh` derruba com volumes (`down -v`), apaga o carimbo e re-roda o seed. `status [--json]` responde a pergunta que decide se um verde vale alguma coisa (no ar? saudável? construído do código atual?) e sai 0 só com os três; o `baseUrl` do payload é o endereço real da app — é dele que a `morph-review` tira a URL que vai no prompt do avaliador, em vez de deixá-lo adivinhar `localhost:3000`. `down [--volumes]` é best-effort e nunca bloqueia. Isolamento entre worktrees: `COMPOSE_PROJECT_NAME=morph-{feature}` separa os containers e o bloco de portas do fleet (`env-allocator`) separa as portas — um compose com porta fixa não pode ser isolado, e isso é avisado. O bloco vale nos **dois** caminhos (este comando e o nó do `verify`): `PORT`, `MORPH_PORT_BASE`, `ASPNETCORE_URLS` em dotnet, mais `PLAYWRIGHT_BASE_URL` apontando para a porta alocada. Por isso o `playwright.config.ts` precisa ler a base URL do ambiente em vez de cravá-la — com a URL cravada, a suíte bate numa porta vazia e o `ECONNREFUSED` se disfarça de app quebrada.
|
|
52
52
|
- **`morph-spec heavy status [--json]`** e **`morph-spec heavy run [--label <x>] -- <comando…>`** — semáforo de nós pesados **por usuário**: os slots vivem em `%LOCALAPPDATA%morph-specheavy` (Windows) ou `$XDG_STATE_HOME/morph-spec/heavy` (`~/.local/state/…`), compartilhados por **todas** as sessões da máquina, com `heavy.slots` (2) vagas. **Quem toma slot:** build, testes (inclusive `--linux`) e e2e do `verify` — um slot por `verify`, adquirido só imediatamente antes do primeiro spawn real (nó pulado não adquire e a espera fica fora do `durationMs`) —, o `e2e up` (só durante a subida da stack) e o `heavy run`, que roda qualquer comando sob slot e repassa o exit code (`127` se não conseguiu executá-lo; `npm`/`npx`/`pnpm`/`yarn` viram `.cmd` no Windows). `heavy status` é **somente leitura**: quem segura cada slot (pid, label, feature, nó, idade), a limpeza exclusiva do build-server se houver e a RAM livre. **Espera:** slot cheio, ou RAM livre (`os.freemem()`) abaixo de `heavy.minFreeMemGb` (4), espera até `heavy.waitTimeoutSec` (600) avisando em **stderr** a cada 30 s e nomeando quem segura (o `--json` mantém o stdout limpo). RAM baixa espera **com ou sem** holder de slot: a pressão costuma vir de fora (vmmem do WSL, IDE), e "sem holder → roda" reproduziria o "roda e morre". **Estouro = exit 3 (`busy`), nunca vermelho:** `verify --json` sai `{ status: "busy", persisted: false, busy: { reason, holders, freeGb, waitedMs } }`, **sem stamp e sem evento `Verify`** (nada foi verificado, e fila não é gate verde nem vermelho); `e2e up` e `heavy run` também saem 3. Quem orquestra repete a chamada — nas skills, exit 3 não conta como tentativa e não dispara o loop de correção. **Slot vago:** pid morto, ou idade acima de `heavy.maxHoldMinutes` (90, contra reuso de PID); Ctrl-C deixa o arquivo, que o pid morto cobre. Um filho herda o slot por `MORPH_HEAVY_LEASE` e nunca pede um segundo. **Config** (`.morph/config/config.json`, bloco `heavy`, schema em `config.schema.json`): `enabled`, `slots`, `minFreeMemGb`, `waitTimeoutSec`, `maxHoldMinutes`, `shutdownBuildServer`, `disableNodeReuse`, `gateBash`; valor ilegível cai no default **com aviso** — o bloco nunca bloqueia trabalho por estar errado. Env: `MORPH_HEAVY=off` desliga o semáforo (a suíte do próprio framework roda assim, para não enfileirar nos slots reais de quem a executa), `MORPH_HEAVY_SLOTS`, `MORPH_HEAVY_DIR`. **Fim do trabalho pesado:** no fim do `verify` de escopo **feature** (nunca no de task: cada task pagaria compilador frio), no `e2e down` e no `doctor --procs --prune`, roda `dotnet build-server shutdown` **só** se o projeto é .NET, `heavy.shutdownBuildServer` está ligado, nenhum slot está preso **e** nenhum `dotnet build|test|publish|msbuild` está vivo — sub-agents e IDE não passam pelo semáforo, então semáforo vazio não basta. Com `heavy.enabled: false` nunca roda: sem semáforo, nada prova que outra sessão não está compilando. Fail-open, timeout de 30 s, resultado em `cleanup` no `--json`. `heavy.disableNodeReuse` põe `MSBUILDDISABLENODEREUSE=1` enquanto o nó pesado roda e restaura depois. O `--linux` rotula o container (`morph-spec.verify=<runId>`, `morph-spec.root=<hash da raiz>`) e o remove **por label** no fim, nunca por nome; o `e2e down` usa `--remove-orphans`. O `verify` **não** derruba a stack e2e (`keepUp` é por desenho). **Bash cru (`heavy.gateBash`, opt-in, padrão `false`):** o `dotnet build`/`dotnet test`/`npm test` de um sub-agent pelo Bash não passa pelo `verify`, então dois elos das chains de hooks fazem o papel dele. `heavy-gate` (`PreToolUse` Bash, depois de todos os guards e antes do `worktree-env-inject`) reconhece o comando pesado — `dotnet build|test|publish|pack|msbuild|vstest`, `msbuild`, `npm|pnpm|yarn|bun test|build` e `run test|build[:x]`, `npx vitest|jest|playwright test|next build`, `vitest`, `jest`, `playwright test`, `next|vite build`, `docker build|buildx`, `docker compose|docker-compose up|build` — olhando o executável de cada segmento (`;`, `&&`, `||`, `|`, quebra de linha, fora de aspas): `git commit -m "fix build && dotnet test"` e `morph-spec heavy run -- dotnet test` não são pesados. Toma um slot e espera até `min(waitTimeoutSec, 540)` s — abaixo dos 600 s de timeout do hook, porque um `PreToolUse` que estoura o timeout **deixa a chamada rodar**; sem vaga no prazo a chamada é **recusada** (`deny`) com a causa e a instrução de repetir o mesmo comando, e não roda. `heavy-release` (`PostToolUse` e `PostToolUseFailure`) devolve a vaga pelo `tool_use_id`. **Não reescreve o comando:** a doc do Claude Code avalia as regras de permissão contra o comando **reescrito**, e o `allow` que acompanha um `updatedInput` aprovaria o que o prompt perguntaria; por isso não há reescritor (o `allow` não atropela regra `deny`/`ask`, mas a reescrita esconderia o comando delas). **O que a vaga não cobre:** ela não tem processo vivo para vigiar (o hook sai na hora), então vence por **TTL** — o `timeout` da chamada, no máximo o da plataforma, mais 30 s — e uma chamada recusada por regra de permissão ou interrompida pelo usuário, que não dispara `Post*`, segura o slot até lá. O TTL conta desde o `PreToolUse`, que roda **antes** do prompt de permissão: se a aprovação demora mais que o `timeout` + 30 s, a vaga vence com o comando ainda por rodar e outra chamada pode tomá-la (sobreassinatura, do lado seguro). Chamadas de Bash de um mesmo lote paralelo rodam **em série** no Claude Code (medido na 2.1.285: o `Pre` da 2ª só rodou depois do `Post` da 1ª), então um agente não fica esperando uma vaga que só a própria chamada anterior liberaria. `run_in_background`, PowerShell e comando fora da lista não são cobertos, e a cláusula 6 dos templates de dispatch (`heavy run`) segue valendo para eles. **Medido** com `claude -p` real e a chain instalada: fila cheia recusa após a espera e não mexe na vaga alheia; a vaga está presa durante a chamada e volta vazia depois, com exit 0 e com exit 1. Custo num Bash **leve**: ~1,5 ms na chain Pre (mediana 61,4 → 62,9 ms, medido intercalado) e ~0 na Post; os módulos do semáforo só carregam quando o comando é pesado. O núcleo (`heavy-config`, `heavy-holders`, `heavy-slots`) mora em `framework/hooks/shared`, porque hook não importa `src/`.
|
|
@@ -475,12 +475,12 @@ A saída de todos é a mesma: `--full`.
|
|
|
475
475
|
|
|
476
476
|
## Instalação e atualização
|
|
477
477
|
|
|
478
|
-
- **`morph-spec update [--templates] [--standards] [--dry-run] [--force]`** — reinstala os payloads do framework no projeto (standards, hooks, schemas,
|
|
478
|
+
- **`morph-spec update [--templates] [--standards] [--dry-run] [--force]`** — reinstala os payloads do framework no projeto (standards, hooks, schemas, evals, `.claude/{commands,rules,skills,agents}`, `.claude/CLAUDE.md`, `.gitignore`/`.gitattributes`, statusline global).
|
|
479
479
|
|
|
480
480
|
**`--dry-run` prevê sem tocar em nada.** Previsão e execução são o **mesmo percurso**, com os **mesmos filtros** e o **mesmo coletor de efeitos**: em modo normal cada verbo executa *e* registra, em dry apenas registra. Não há travessia paralela — o resumo de uma execução real também sai do coletor, de modo que um ponto de emissão esquecido some das duas saídas ao mesmo tempo, em vez de mentir só no `--dry-run`.
|
|
481
481
|
|
|
482
482
|
O relatório agrupa por natureza do efeito (apagar, criar diretório, copiar, escrever, mesclar, executar, índice do git) e **nomeia separadamente três grupos que ninguém adivinha da palavra "update"**:
|
|
483
|
-
- **apagados por inteiro antes da reinstalação** — `.claude/{commands,rules,skills,agents}` e `.morph/{framework/standards,framework/hooks,framework/schemas,framework/rubrics,framework/evals,config}`, com a contagem de arquivos que hoje moram em cada um;
|
|
483
|
+
- **apagados por inteiro antes da reinstalação** — `.claude/{commands,rules,skills,agents}` e `.morph/{framework/standards,framework/hooks,framework/schemas,framework/rubrics,framework/evals,config}`, com a contagem de arquivos que hoje moram em cada um (`framework/rubrics` não é mais instalado: entra na lista só para sumir de instalações antigas);
|
|
484
484
|
- **fora do projeto** — `~/.claude/statusline.sh`, `~/.claude/statusline.py` e a mesclagem de `~/.claude/settings.json`;
|
|
485
485
|
- **dentro do pacote da CLI** — `generate-refs.js` escrevendo `framework/hooks/shared/phase-utils.js` do pacote, que num `npm link` de desenvolvimento é o próprio repositório do framework. Em dry roda a variante `--check`, que verifica e não escreve.
|
|
486
486
|
|
|
@@ -11,6 +11,8 @@
|
|
|
11
11
|
* - AskUserQuestion answered with option starting "aprovado" → advance gate
|
|
12
12
|
*/
|
|
13
13
|
|
|
14
|
+
import { existsSync } from 'fs';
|
|
15
|
+
import { join } from 'path';
|
|
14
16
|
import { loadState, stateExists } from '../../shared/state-reader.js';
|
|
15
17
|
import { persistFeature, setGateApproved, createDefaultFeature, readFeatureFile } from '../../shared/state-writer.js';
|
|
16
18
|
import { resolveMorphRoot } from '../../shared/payload-utils.js';
|
|
@@ -216,7 +218,17 @@ function handleAskUserQuestion(input, sessionRoot) {
|
|
|
216
218
|
// (`morph-spec approve`) produce one consistent shape
|
|
217
219
|
// ({ approved, timestamp, approvedBy }) in the authoritative feature.json —
|
|
218
220
|
// and a manual approval stays distinguishable from a policy auto-approval.
|
|
219
|
-
|
|
221
|
+
// Same contract as `approve`'s --self-assessed: a human may ship Gate 3 without the
|
|
222
|
+
// independent evaluator, but the degradation is stamped, never silent.
|
|
223
|
+
const selfAssessed = gate === 'review' && feature?.workType !== 'hotfix'
|
|
224
|
+
&& !existsSync(join(root, '.morph', 'features', featureName, '4-review', 'evaluator-report.md'));
|
|
225
|
+
if (selfAssessed) {
|
|
226
|
+
warnings.push(
|
|
227
|
+
`⚠️ [morph-spec] Gate 3 de "${featureName}" aprovado SEM avaliador independente — ` +
|
|
228
|
+
`registrado como selfAssessed (aparece no list/fleet).`
|
|
229
|
+
);
|
|
230
|
+
}
|
|
231
|
+
setGateApproved(featureName, gate, { approvedBy: 'manual', ...(selfAssessed ? { selfAssessed: true } : {}) }, root);
|
|
220
232
|
}
|
|
221
233
|
return warnings.length ? warnings.join('\n') : null;
|
|
222
234
|
}
|
|
@@ -3,10 +3,9 @@
|
|
|
3
3
|
*
|
|
4
4
|
* The sibling of build-check.js, one layer out: where `runBuildCheck()` proves
|
|
5
5
|
* the code COMPILES, `runE2eCheck()` proves the application RUNS. That gap is
|
|
6
|
-
* not academic
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
* and it is invisible to `dotnet build`. Only a real bring-up catches it.
|
|
6
|
+
* not academic: a service injected into a Minimal API endpoint without a
|
|
7
|
+
* matching `AddScoped` takes the whole app down at startup, and it is
|
|
8
|
+
* invisible to `dotnet build`. Only a real bring-up catches it.
|
|
10
9
|
*
|
|
11
10
|
* Two levels of proof, in increasing strength:
|
|
12
11
|
* 1. SMOKE — the stack comes up and every service reports healthy.
|
|
@@ -114,6 +114,7 @@ export function setGateApproved(featureName, gate, meta = {}, projectPath) {
|
|
|
114
114
|
approved: true,
|
|
115
115
|
timestamp: meta.timestamp || now,
|
|
116
116
|
approvedBy: meta.approvedBy || 'manual',
|
|
117
|
+
...(meta.selfAssessed ? { selfAssessed: true } : {}),
|
|
117
118
|
};
|
|
118
119
|
feature.updatedAt = now;
|
|
119
120
|
return persistFeature(featureName, feature, projectPath);
|
|
@@ -9,26 +9,28 @@ stacks:
|
|
|
9
9
|
|
|
10
10
|
# Microsoft Agent Framework Standards
|
|
11
11
|
|
|
12
|
-
> **Layer 0/1 —
|
|
13
|
-
> agente MAF.
|
|
12
|
+
> **Layer 0/1 — resumo injetado pela rule.** São os standards core: aplicam-se a praticamente todo
|
|
13
|
+
> agente MAF. O resumo de cada um entra no contexto quando você toca código de agentes.
|
|
14
14
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
15
|
+
- **ai-agents-setup** (`ai-agents/setup.md`) — Versões exatas: Microsoft.Agents.AI 1.20.0, Microsoft.Extensions.AI 10.9.0, OpenAI 2.13.0. OpenAI pelo Responses API (`GetResponsesClient().AsAIAgent(...)`); Chat Completions só com `"api": "chat"`; `store: false`. PROIBIDO: orquestração Semantic Kernel, `AgentOpenTelemetryConsts` (não existe).
|
|
16
|
+
- **ai-agents-sweet-spot** (`ai-agents/sweet-spot.md`) — Chat + Structured Output + Tools + RAG custom cobre 95%. Single-agent é o padrão; multi-agent/workflow só com fan-in/out ou handoff real. Teto prático de ~12 tools por agente (podar, depois allow-list por fase, depois agent-as-tool). Leia no plan, antes de decidir a arquitetura.
|
|
17
|
+
- **ai-agents-providers-model-registry** (`ai-agents/providers/model-registry.md`) — `model-registry.json` v2 mapeia alias → { provider, model, api, options, constraints }; agente usa alias, nunca string de modelo. Todo alias declara `api` (`responses` padrão, `chat` opt-in). `temperature` e `reasoningEffort` são mutuamente exclusivos por alias. Sem range flutuante em PackageReference.
|
|
18
|
+
- **ai-agents-structured-output** (`ai-agents/structured-output.md`) — Saída que alimenta outro sistema é record tipado (`RunAsync<T>` ou `ChatResponseFormat.ForJsonSchema<T>()`): `sealed record` com `[Description]` e `[JsonPropertyName]`. Raiz nunca primitivo/array (use wrapper); em streaming, `ToAgentResponseAsync()` antes de desserializar. Regra de negócio se valida depois da chamada.
|
|
19
|
+
- **ai-agents-multi-agent-patterns** (`ai-agents/multi-agent-patterns.md`) — Do mais barato: agent-as-tool (`AsAIFunction()`, aninhamento ≤ 2) → sequencial → workflow; handoff vem depois de agent-as-tool, com Id estável e `WithAutonomousMode(turnLimit:)`; group chat ≤ 3 agentes. PROIBIDO: agente auxiliar DENTRO de uma tool (dois loops de function calling no mesmo turno).
|
|
20
|
+
- **ai-agents-prompt-sources** (`ai-agents/prompt-sources.md`) — Prompt fixo: tabela versionada `agent_prompts` (INSERT + flip de `is_active` numa transação, nunca UPDATE; id `Guid.CreateVersion7()`). Prompt por tenant: YAML no repo → compositor determinístico → spec renderizada em jsonb. O registro de execução grava a versão EFETIVA (e o hash do blob no caminho composto).
|
|
21
|
+
- **ai-agents-rag-custom-pgvector** (`ai-agents/rag-custom-pgvector.md`) — RAG em pgvector via EF Core: `IEmbeddingGenerator` injetado direto (sem EmbeddingService), alias `embeddings` do registry, `CosineDistance` LINQ, HNSW `vector_cosine_ops`, `tenant_id` filtrado em toda query e nunca exposto ao LLM. Ingestão delta por lookup-key: Unchanged nunca re-embeda, Removed só depois das escritas.
|
|
22
|
+
- **ai-agents-middleware-patterns** (`ai-agents/middleware-patterns.md`) — Registro sempre `.AsBuilder().Use(...).Build()`; o PRIMEIRO registrado é o mais externo. Ordem medida: timeout → usage-cost → FunctionInvokingChatClient → chat telemetry → provider. Só o delegate não-streaming rebaixa streaming em silêncio: use `Use(sharedFunc)`.
|
|
23
|
+
- **ai-agents-observability-patterns** (`ai-agents/observability-patterns.md`) — Registre os DOIS ActivitySources (`Experimental.Microsoft.Agents.AI` e `Experimental.Microsoft.Extensions.AI`); a API é `builder.UseOpenTelemetry(...)` (`agent.WithOpenTelemetry()` não existe). Instrumentar os dois níveis é normal; o mesmo nível duas vezes duplica. Prompt cru só em dev com opt-in, hash em produção; turno cancelado não grava nada.
|
|
24
|
+
- **ai-agents-workflows** (`ai-agents/workflows.md`) — Raro: só para fan-in/out real ou HITL longo; `foreach` + LINQ costuma bastar. `AgentWorkflowBuilder`/`WorkflowBuilder`/`InProcessExecution` vivem no pacote SEPARADO `Microsoft.Agents.AI.Workflows` 1.20.0. Agente declarativo (`Microsoft.Agents.AI.Declarative`) ainda pede `--prerelease`.
|
|
25
|
+
- **ai-agents-production** (`ai-agents/production.md`) — A2A (`Microsoft.Agents.AI.A2A`) é PRERELEASE. Isolamento multi-tenant (`AgentIsolationKeyProvider`, `UseClaimsBasedAgentIsolation`) com a chave vinda da credencial, nunca do payload. `AllowConcurrentInvocation` é opt-in e inseguro com DbContext scoped. Cache: Redis semântico + HybridCache.
|
|
26
|
+
|
|
27
|
+
> Resumo injetado quando você lê ou edita um arquivo casado por `paths:`. Texto completo: `morph-spec standards --find "<id>"` e leia só a seção necessária de `.morph/framework/standards/<path>`.
|
|
26
28
|
|
|
27
29
|
---
|
|
28
30
|
|
|
29
|
-
> **Layer 2 — sob demanda (
|
|
30
|
-
> MCP: relevantes apenas quando a feature usa especificamente esse recurso.
|
|
31
|
-
> `morph-standards` quando uma keyword corresponder — manter fora do
|
|
31
|
+
> **Layer 2 — sob demanda (sem resumo aqui).** São standards de modalidade, otimização e
|
|
32
|
+
> MCP: relevantes apenas quando a feature usa especificamente esse recurso. Busque via
|
|
33
|
+
> `morph-spec standards --find` (skill `morph-standards`) quando uma keyword corresponder — manter fora do resumo preserva o
|
|
32
34
|
> context window para o trabalho que importa.
|
|
33
35
|
|
|
34
36
|
| Standard | Carregue quando a feature envolve |
|
|
@@ -10,4 +10,6 @@ stacks:
|
|
|
10
10
|
|
|
11
11
|
# Asaas Payment Integration Standards
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
- **backend-integrations-asaas-asaas-api** (`backend/integrations/asaas/asaas-api.md`) — Só o backend .NET chama o Asaas (o Next passa pelo proxy): `AsaasClient` HttpClient tipado, base URL com barra final e paths relativos, headers `access_token` + `User-Agent`; respostas viram `Result`. Sem cobrança duplicada: `externalReference` + GET antes de criar, nunca retry automático em cartão. Webhook: `asaas-access-token` comparado com `FixedTimeEquals`, id de evento deduplicado. Nunca guarde dado de cartão.
|
|
14
|
+
|
|
15
|
+
> Resumo injetado quando você lê ou edita um arquivo casado por `paths:`. Texto completo: `morph-spec standards --find "<id>"` e leia só a seção necessária de `.morph/framework/standards/<path>`.
|
|
@@ -8,11 +8,13 @@ stacks:
|
|
|
8
8
|
|
|
9
9
|
# C# and .NET VSA Standards
|
|
10
10
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
11
|
+
- **architecture-vertical-slice-vertical-slice** (`architecture/vertical-slice/vertical-slice.md`) — Uma slice por operação em `Features/{Entity}Feature/{Verb}{Entity}/`: Request/Response `sealed record`, Handler, Validator e Endpoint `IApiEndpoint` `internal sealed`. Decorators Scrutor: Logging → Validation → Handler. PROIBIDO: MediatR/`IRequest`, AggregateRoot, DomainEvent, CQRS formal, camada Application Service, DTO compartilhado entre features, `IRepository<T>`/`IUnitOfWork`.
|
|
12
|
+
- **backend-dotnet-vsa-handler-patterns** (`backend/dotnet/vsa-handler-patterns.md`) — Handler `sealed class` com primary constructor implementando `IHandler<TRequest, TResponse>` (não MediatR): injeta `ApplicationDbContext` direto, usa `Guid.CreateVersion7()` (nunca `Guid.NewGuid()`), passa `CancellationToken` em tudo e devolve `Result<T>`. Erros em `{Feature}Errors.cs`. SQL cru ou store dedicado só com justificativa em `decisions.md`.
|
|
13
|
+
- **backend-dotnet-result-pattern** (`backend/dotnet/result-pattern.md`) — Falha esperada é `Result`/`Result<T>`, nunca exceção. Endpoint: `result.Match(value => Results.Ok(value), error => error.ToHttpResult())`; nunca leia `.Value` fora do `Match`. Status pelo `ErrorType` (Validation 400, NotFound 404, Conflict 409, Unauthorized 401, Forbidden 403, Unexpected 500), nunca BadRequest para tudo. PROIBIDO: try/catch no handler, retornar null, erro string sem código.
|
|
14
|
+
- **backend-dotnet-fluent-validation-vsa** (`backend/dotnet/fluent-validation-vsa.md`) — Um `sealed AbstractValidator<TRequest>` (FluentValidation 12) por request, na pasta da slice; o `ValidationDecorator` roda antes do handler e devolve `Result.Failure(ValidationError)`. `AddValidatorsFromAssembly()` ANTES de `AddHandlersFromAssembly()`. Sempre `.WithMessage()`; tempo via `TimeProvider` lido DENTRO da regra. Nunca lance `ValidationException`.
|
|
15
|
+
- **backend-dotnet-scrutor** (`backend/dotnet/scrutor.md`) — `AddHandlersFromAssembly` registra cada `IHandler<,>` como Scoped (filtro `!t.ContainsGenericParameters`) e depois `Decorate` Validation e então Logging (o último registrado é o mais externo). Endpoints `IApiEndpoint` via `TryAddEnumerable` Transient + `MapApiEndpoints`. Nunca registre handler um a um.
|
|
16
|
+
- **backend-dotnet-scalar-ui** (`backend/dotnet/scalar-ui.md`) — Scalar no lugar do Swagger: `Microsoft.AspNetCore.OpenApi` 10.0.0 + `Scalar.AspNetCore` 2.12.40, nunca Swashbuckle. `MapOpenApi()` só em `IsDevelopment()` e antes de `MapScalarApiReference`. Todo endpoint com `.WithTags(ApiTags.X)` (constantes), `.Produces<T>` e `.ProducesProblem(status)` por falha possível.
|
|
17
|
+
- **backend-api-validation** (`backend/api/validation.md`) — O validator checa só FORMA (sem DbContext); existência, unicidade e estado ficam no handler com `{Entity}Errors` tipados. Checagem de estado valida o INVÁLIDO (enum ordenado, estados de erro em 100+). PROIBIDO: lançar exceção, validar na lambda do endpoint, misturar DataAnnotations, `if (status != Expected)`, switch exaustivo sobre estados válidos.
|
|
18
|
+
- **architecture-code-comments** (`architecture/code-comments.md`) — Padrão é nenhum comentário: renomeie, extraia, tipe ou teste antes. Só o porquê que o código não mostra (invariante, ordem, quirk externo com link), até ~3 linhas. PROIBIDO: história de processo (task, UC, ADR, Gate), repetir o código, XML doc/JSDoc que repete a assinatura, banner, `#region`, código comentado, TODO solto. O hook `comment-guard` bloqueia.
|
|
19
|
+
|
|
20
|
+
> Resumo injetado quando você lê ou edita um arquivo casado por `paths:`. Texto completo: `morph-spec standards --find "<id>"` e leia só a seção necessária de `.morph/framework/standards/<path>`.
|
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
paths:
|
|
3
3
|
- "**/*Cache*.cs"
|
|
4
4
|
- "**/*Redis*.cs"
|
|
5
|
-
- "**/*Cosmos*.cs"
|
|
6
5
|
- "**/*Vector*.cs"
|
|
7
6
|
- "**/*Embedding*.cs"
|
|
8
7
|
- "**/*Rag*.cs"
|
|
@@ -12,7 +11,8 @@ stacks:
|
|
|
12
11
|
|
|
13
12
|
# Data & Cache Standards
|
|
14
13
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
14
|
+
- **data-nosql-cache-redis** (`data/nosql/cache/redis.md`) — Cache distribuído, nunca store primário. `Microsoft.Extensions.Caching.StackExchangeRedis` 10.0.12; `IConnectionMultiplexer` singleton LAZY (nunca conectar no registro). Cache-aside no handler com `IDistributedCache` (sem wrapper); invalide a chave exata depois do `SaveChangesAsync`. PROIBIDO: cachear segredo/PII sem criptografia, omitir TTL, engolir `RedisConnectionException`.
|
|
15
|
+
- **data-vector-search-rag-chunking** (`data/vector-search/rag-chunking.md`) — Prosa: sentence-aware, 256 tokens; doc técnico: semântico por headers; Q&A inteiro; código: a função inteira. Overlap 10–20%, mínimo 50 e máximo ~512 tokens. Sempre grave metadados do chunk (DocumentId, ChunkIndex, Section, SourceUrl) e reindexe quando o hash do conteúdo mudar.
|
|
16
|
+
- **backend-database-vector-search-rag** (`backend/database/vector-search-rag.md`) — Slices VSA sobre EF Core 10 + pgvector: o handler injeta `ApplicationDbContext` e `IEmbeddingGenerator` direto (sem EmbeddingService/SearchService). Busca por `d.Embedding!.CosineDistance(query)` com a distância em ordem ASC. Sem documento relevante, `NotFound` antes de chamar o LLM. Indexação em lote: um `GenerateAsync` e um `SaveChanges`.
|
|
17
|
+
|
|
18
|
+
> Resumo injetado quando você lê ou edita um arquivo casado por `paths:`. Texto completo: `morph-spec standards --find "<id>"` e leia só a seção necessária de `.morph/framework/standards/<path>`.
|
|
@@ -30,7 +30,7 @@ morph-spec scaffold dotnet book get-all-books
|
|
|
30
30
|
|
|
31
31
|
Generates 7 files: endpoint, handler, request, response, validator + entity, errors (shared at feature level). Entity/errors auto-skipped if they already exist.
|
|
32
32
|
|
|
33
|
-
**
|
|
33
|
+
**Adding a single file to an existing slice:** read its `.hbs` template (table below) and write the file following it — `scaffold` generates whole slices only, and there is no single-file render command.
|
|
34
34
|
|
|
35
35
|
## VSA Feature Templates (Vertical Slice Architecture)
|
|
36
36
|
|
|
@@ -9,5 +9,7 @@ stacks:
|
|
|
9
9
|
|
|
10
10
|
# Frontend Standards
|
|
11
11
|
|
|
12
|
-
|
|
13
|
-
|
|
12
|
+
- **frontend-design-system-naming** (`frontend/design-system/naming.md`) — BEM para componente reutilizável (`.card`, `.card__header`, `.card--featured`); prefixo de página para elemento específico (`.home-hero`). Custom properties em `:root` com namespace `--app-`. Estado por sufixo fixo: `--active`, `--disabled`, `--loading`, `--error`.
|
|
13
|
+
- **frontend-design-system-animations** (`frontend/design-system/animations.md`) — Classes `.animate-*`, `.duration-fast/normal/slow`, `.stagger-1..10`. Lista escalonada exige `animation-delay` E `animation-fill-mode: backwards`. Sempre o bloco `prefers-reduced-motion: reduce`. Além do CSS: Motion (`motion/react`) para enter/exit/layout; GSAP só para ScrollTrigger, SVG morph e timelines.
|
|
14
|
+
|
|
15
|
+
> Resumo injetado quando você lê ou edita um arquivo casado por `paths:`. Texto completo: `morph-spec standards --find "<id>"` e leia só a seção necessária de `.morph/framework/standards/<path>`.
|
|
@@ -1,13 +1,18 @@
|
|
|
1
1
|
---
|
|
2
2
|
paths:
|
|
3
|
-
- "
|
|
4
|
-
- "
|
|
3
|
+
- "**/animations/**"
|
|
4
|
+
- "**/*gsap*"
|
|
5
|
+
- "**/*Gsap*"
|
|
6
|
+
- "**/*scroll-trigger*"
|
|
7
|
+
- "**/*ScrollTrigger*"
|
|
5
8
|
stacks:
|
|
6
9
|
- nextjs
|
|
7
10
|
---
|
|
8
11
|
|
|
9
12
|
# GSAP Animation Standards
|
|
10
13
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
+
- **frontend-gsap-core** (`frontend/gsap/gsap-core.md`) — GSAP 3.13 + `@gsap/react` só para ScrollTrigger, SVG morph/draw, timelines complexas e SplitText; o resto é Motion, e NUNCA os dois no mesmo elemento. Código em `'use client'` dentro de `useGSAP()` com `{ scope: containerRef }` (nunca `useEffect`). Só transform/opacity; `prefers-reduced-motion` via `gsap.matchMedia()` é obrigatório; durations/easings vêm de `@/lib/design-tokens`.
|
|
15
|
+
- **frontend-gsap-scrolltrigger** (`frontend/gsap/gsap-scrolltrigger.md`) — `gsap.registerPlugin(ScrollTrigger)` uma vez no init, nunca em `useEffect`/loop, e nunca `ScrollTrigger.create()` no escopo do módulo. Scrub com ease `'none'` (0.3–1 em produção). `pin: true` + `anticipatePin: 1`; nunca pin aninhado. `ScrollTrigger.batch` em grids de 20+. `ScrollTrigger.refresh()` após conteúdo dinâmico, troca de rota e `onLoad` de imagem.
|
|
16
|
+
- **frontend-gsap-plugins** (`frontend/gsap/gsap-plugins.md`) — Todo plugin é gratuito no pacote público `gsap` (`npm install gsap @gsap/react`); o registry privado `@gsap/shockingly` é obsoleto. Flip: `getState()` → muda o DOM → `Flip.from(...)`. SplitText com `autoSplit: true`, animando em `onSplit`. MorphSVG exige `<path>`; DrawSVG exige stroke visível; Draggable com inércia exige InertiaPlugin.
|
|
17
|
+
|
|
18
|
+
> Resumo injetado quando você lê ou edita um arquivo casado por `paths:`. Texto completo: `morph-spec standards --find "<id>"` e leia só a seção necessária de `.morph/framework/standards/<path>`.
|
|
@@ -10,4 +10,6 @@ stacks:
|
|
|
10
10
|
|
|
11
11
|
# Hangfire Background Jobs Standards
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
- **backend-integrations-hangfire-hangfire-jobs** (`backend/integrations/hangfire/hangfire-jobs.md`) — Hangfire 1.8.25 só com razão real em `decisions.md`; o que cabe no request fica no handler. Storage `Hangfire.PostgreSql` 1.21.1 com `UseNpgsqlConnection` no endpoint DIRETO do Neon; referencie `Newtonsoft.Json` 13.0.4. Job é classe fina e idempotente que chama o handler; `[AutomaticRetry]`, sem try/catch nem retry manual. Dashboard exige Admin autenticado.
|
|
14
|
+
|
|
15
|
+
> Resumo injetado quando você lê ou edita um arquivo casado por `paths:`. Texto completo: `morph-spec standards --find "<id>"` e leia só a seção necessária de `.morph/framework/standards/<path>`.
|
|
@@ -10,8 +10,10 @@ stacks:
|
|
|
10
10
|
|
|
11
11
|
# Infrastructure Standards
|
|
12
12
|
|
|
13
|
-
|
|
14
|
-
|
|
13
|
+
- **infrastructure-docker-coolify-deploy** (`infrastructure/docker/coolify-deploy.md`) — Acesso só por SSH na VPS, API do Coolify em `localhost:8000/api/v1` com token server-side (nunca impresso). Env vars no Coolify, nunca no Dockerfile. Endpoint `/health`. Redeploy por `POST /api/v1/deploy?uuid=` (GET não deploya); sem `deployment_uuid` na resposta, nada foi deployado.
|
|
14
|
+
- **infrastructure-docker-local-compose-isolation** (`infrastructure/docker/local-compose-isolation.md`) — Cada worktree roda o próprio stack compose; portas do bloco da worktree (`PORT`, `API_PORT`, `POSTGRES_PORT`, `REDIS_PORT`) publicadas como `${VAR:-default}:container`. PROIBIDO: porta fixa no host, `container_name`. Healthcheck em todo serviço. Compose que lê a porta por outro nome: declare em `e2e.portEnvNames`.
|
|
15
|
+
|
|
16
|
+
> Resumo injetado quando você lê ou edita um arquivo casado por `paths:`. Texto completo: `morph-spec standards --find "<id>"` e leia só a seção necessária de `.morph/framework/standards/<path>`.
|
|
15
17
|
|
|
16
18
|
## Infrastructure Templates
|
|
17
19
|
|
|
@@ -48,7 +48,7 @@ The full slash-command and CLI surface lives in the resident `.claude/CLAUDE.md`
|
|
|
48
48
|
|
|
49
49
|
| Path | Notes |
|
|
50
50
|
|------|-------|
|
|
51
|
-
| `.morph/features/{feature}/feature.json` | **Authoritative + committed** feature state. Write it through the CLI (`create`, `
|
|
51
|
+
| `.morph/features/{feature}/feature.json` | **Authoritative + committed** feature state. Write it through the CLI (`create`, `gate-decision`, `gate-check`, `approve`) — a hand Edit collides with the `state-sync` hook |
|
|
52
52
|
| `.morph/state.json` | **READ-ONLY** — thin, gitignored index rebuilt from `feature.json` on load; kept in sync by the `state-sync` hook |
|
|
53
53
|
| `.morph/features/{feature}/{phase}/` | Feature outputs organized by phase |
|
|
54
54
|
| `.morph/framework/` | **READ-ONLY** — framework files managed by morph-spec |
|
|
@@ -198,7 +198,7 @@ fix** — never a patch on the symptom. This is MANDATORY:
|
|
|
198
198
|
1. Reproduce it and read the full error, not the first line.
|
|
199
199
|
2. For a known .NET/Next.js error, run `/morph-troubleshoot <keywords>` first.
|
|
200
200
|
3. One hypothesis at a time, each tested by the smallest check that could refute it.
|
|
201
|
-
4. After three failed fixes,
|
|
201
|
+
4. After three failed fixes, never try a fourth on your own: escalate via `AskUserQuestion` (accept / keep trying / skip), bringing the design question the failures raise — a fourth attempt with no new information repeats the error (same rule as MORPH.md §0).
|
|
202
202
|
5. Confirm the fix with the same check that reproduced the bug; add a test only when no existing one covers that behavior.
|
|
203
203
|
|
|
204
204
|
Everything else that a rule like this might list is owned by the flow itself, and duplicating it
|
|
@@ -10,4 +10,6 @@ stacks:
|
|
|
10
10
|
|
|
11
11
|
# Neon Auth Standards
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
- **backend-integrations-neon-auth-neon-auth** (`backend/integrations/neon-auth/neon-auth.md`) — SDK `@neondatabase/auth`: `createNeonAuth()` no server, `createAuthClient()` no client, `auth.middleware()` com `runtime='nodejs'`. Duas constantes de prefixo (`BACKEND_API_PREFIX`/`PROXY_API_PREFIX`), nunca uma só. O front não consulta o Neon para dado de negócio. .NET: token EdDSA/Ed25519, verificado pelo JWKS com BouncyCastle + `SignatureValidator` devolvendo `JsonWebToken`.
|
|
14
|
+
|
|
15
|
+
> Resumo injetado quando você lê ou edita um arquivo casado por `paths:`. Texto completo: `morph-spec standards --find "<id>"` e leia só a seção necessária de `.morph/framework/standards/<path>`.
|
|
@@ -10,10 +10,12 @@ stacks:
|
|
|
10
10
|
|
|
11
11
|
# Neon PostgreSQL Standards
|
|
12
12
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
13
|
+
- **infrastructure-neon-setup** (`infrastructure/neon/neon-setup.md`) — Duas connection strings: `DefaultConnection` (pooled `-pooler`) para a app e `MigrationConnection` (direta) para migrations e advisory locks; nunca commitadas (user-secrets / env do Coolify). Formato keyword (`Host=...;SSL Mode=Require`), não URI. `EnableRetryOnFailure(5, 10s)` por causa do scale-to-zero. Branch sempre com `--expires-at`.
|
|
14
|
+
- **infrastructure-neon-pgvector** (`infrastructure/neon/neon-pgvector.md`) — `Npgsql.EntityFrameworkCore.PostgreSQL` 10.0.3 + `Pgvector.EntityFrameworkCore` 0.3.0; coluna `Vector` (`vector(1536)`) e `UseVector()` no `UseNpgsql`. SEMPRE índice HNSW com operator class casando a função LINQ (senão seq scan); distância em ordem ASC. PROIBIDO: DocumentRepository, SQL cru do C# sem `decisions.md`, default `gen_random_uuid()`.
|
|
15
|
+
- **infrastructure-neon-pgrag** (`infrastructure/neon/neon-pgrag.md`) — A extensão `rag` é EXPERIMENTAL: protótipo e RAG só-SQL, não produção sem teste. Nunca misture modelos de embedding numa coluna (bge-small 384 × text-embedding-3-small 1536). Índice HNSW depois da carga inicial. Produção .NET com lógica própria: tool MAF tipada sobre pgvector.
|
|
16
|
+
- **backend-integrations-neon-auth-neon-auth** (`backend/integrations/neon-auth/neon-auth.md`) — SDK `@neondatabase/auth`: `createNeonAuth()` no server, `createAuthClient()` no client, `auth.middleware()` com `runtime='nodejs'`. Duas constantes de prefixo (`BACKEND_API_PREFIX`/`PROXY_API_PREFIX`), nunca uma só. O front não consulta o Neon para dado de negócio. .NET: token EdDSA/Ed25519, verificado pelo JWKS com BouncyCastle + `SignatureValidator` devolvendo `JsonWebToken`.
|
|
17
|
+
|
|
18
|
+
> Resumo injetado quando você lê ou edita um arquivo casado por `paths:`. Texto completo: `morph-spec standards --find "<id>"` e leia só a seção necessária de `.morph/framework/standards/<path>`.
|
|
17
19
|
|
|
18
20
|
## SQL Templates
|
|
19
21
|
|
|
@@ -8,14 +8,16 @@ stacks:
|
|
|
8
8
|
|
|
9
9
|
# Next.js Standards
|
|
10
10
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
11
|
+
- **frontend-nextjs-naming-conventions** (`frontend/nextjs/naming-conventions.md`) — Arquivo SEMPRE kebab-case (`user-card.tsx`); export PascalCase para componente e `useX` para hook. Zod em `.schemas.ts`, tipos em `.types.ts` via `z.infer`. Named export (só `page.tsx` usa `default`). Nunca misture casos na mesma categoria.
|
|
12
|
+
- **frontend-nextjs-project-structure** (`frontend/nextjs/project-structure.md`) — `src/app/` só roteia e importa de `features/`. Cada `src/features/{f}/` tem `components/`, `hooks/`, `types/` e `index.ts` como API pública (import de `@/features/users`, nunca deep path). NUNCA uma feature importa outra; `components/ui/` é shadcn e não se edita.
|
|
13
|
+
- **frontend-nextjs-app-router** (`frontend/nextjs/app-router.md`) — Server Component por padrão; `'use client'` só para interação, browser API ou TanStack Query. Nunca fetch em `useEffect`. `loading.tsx`/`error.tsx` ao lado do `page.tsx` (`error.tsx` é client). Server Component chama o .NET com `BACKEND_API_PREFIX`; evite `route.ts`.
|
|
14
|
+
- **frontend-nextjs-components** (`frontend/nextjs/components.md`) — `components/ui/` é saída do shadcn CLI (nunca editar; envolva em `components/`); `components/` sem regra de negócio e sem importar `features/`; `features/*/components/` conhece o domínio. Props tipadas e validadas com Zod, sempre com `className` opcional.
|
|
15
|
+
- **frontend-nextjs-data-fetching** (`frontend/nextjs/data-fetching.md`) — Server Component com `BACKEND_API_PREFIX`; Client Component só pelo proxy same-origin com `PROXY_API_PREFIX`. TanStack Query 5 com key factory por feature e `invalidateQueries({ queryKey })`; toda resposta passa por Zod. PROIBIDO: `useEffect(() => fetch…)` e paginar/filtrar a coleção inteira no browser.
|
|
16
|
+
- **frontend-nextjs-forms** (`frontend/nextjs/forms.md`) — react-hook-form + Zod (`zodResolver`) + Field do shadcn via `Controller`; tipo por `z.infer`; nunca `useState` por campo. Submit por `useMutation`, erro em `root`. Edição reusa o schema de criação (`.partial()`). Zod 4: `z.email()`.
|
|
17
|
+
- **frontend-nextjs-state-management** (`frontend/nextjs/state-management.md`) — Estado de servidor no TanStack Query, nunca copiado para `useState`. Página, ordenação, filtros e aba ativa na URL (`searchParams`). Context só para tema, usuário ou locale. NUNCA Zustand/Redux antes de esgotar Server Components + TanStack Query.
|
|
18
|
+
- **frontend-nextjs-testing** (`frontend/nextjs/testing.md`) — Vitest + Testing Library; MSW para API; Playwright para e2e. Teste comportamento, não implementação. Orçamento: um teste por pós-condição + um de regressão por bug; cenários do mesmo comportamento em `it.each`; nada de teste que só confere string de copy.
|
|
19
|
+
- **frontend-nextjs-nextjs-patterns** (`frontend/nextjs/nextjs-patterns.md`) — Route Handler é BFF: `auth.getSession()` (401 sem sessão) e delega ao .NET; banco NUNCA em route handler. Zod no client e no server (`safeParse`, 400). `strict: true` + `noUncheckedIndexedAccess`; sem `any` em resposta de API. shadcn via `npx shadcn@latest add`.
|
|
20
|
+
- **frontend-nextjs-motion-patterns** (`frontend/nextjs/motion-patterns.md`) — Motion via `motion/react` (nunca `framer-motion` junto). Variants em `motion/variants.ts` com durations/easings de `@/lib/design-tokens`. `useReducedMotion()` obrigatório; o `layout.tsx` raiz segue Server Component. 3–5 momentos de motion por página; cena guiada por scroll é do GSAP, nunca no mesmo elemento.
|
|
21
|
+
- **architecture-code-comments** (`architecture/code-comments.md`) — Padrão é nenhum comentário: renomeie, extraia, tipe ou teste antes. Só o porquê que o código não mostra (invariante, ordem, quirk externo com link), até ~3 linhas. PROIBIDO: história de processo (task, UC, ADR, Gate), repetir o código, XML doc/JSDoc que repete a assinatura, banner, `#region`, código comentado, TODO solto. O hook `comment-guard` bloqueia.
|
|
22
|
+
|
|
23
|
+
> Resumo injetado quando você lê ou edita um arquivo casado por `paths:`. Texto completo: `morph-spec standards --find "<id>"` e leia só a seção necessária de `.morph/framework/standards/<path>`.
|
|
@@ -27,7 +27,7 @@ morph-spec scaffold nextjs user-profile
|
|
|
27
27
|
|
|
28
28
|
Generates 4 files: page (Server Component), list component, form component, TanStack Query hook.
|
|
29
29
|
|
|
30
|
-
**
|
|
30
|
+
**Adding a single file to an existing feature:** read its `.hbs` template (table below) and write the file following it — `scaffold` generates whole features only, and there is no single-file render command.
|
|
31
31
|
|
|
32
32
|
| Template | Path | Purpose |
|
|
33
33
|
|----------|------|---------|
|
|
@@ -9,4 +9,6 @@ stacks:
|
|
|
9
9
|
|
|
10
10
|
# Resend Email Standards
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
- **backend-integrations-resend-resend-email** (`backend/integrations/resend/resend-email.md`) — REST API, não SMTP. Next: `resend` + React Email, um cliente em `lib/resend.ts`; .NET: pacote `Resend` com `AddHttpClient<ResendClient>()`. Envio sempre assíncrono (Hangfire), retry com Polly. Webhook Svix: verifique o corpo CRU com `resend.webhooks.verify()`, nunca HMAC na mão. Domínio verificado (SPF, DKIM, DMARC) antes do primeiro envio.
|
|
13
|
+
|
|
14
|
+
> Resumo injetado quando você lê ou edita um arquivo casado por `paths:`. Texto completo: `morph-spec standards --find "<id>"` e leia só a seção necessária de `.morph/framework/standards/<path>`.
|
|
@@ -90,7 +90,6 @@
|
|
|
90
90
|
},
|
|
91
91
|
"additionalProperties": false
|
|
92
92
|
},
|
|
93
|
-
"score": { "type": ["number", "null"], "minimum": 0, "maximum": 10 },
|
|
94
93
|
"attempts": { "type": "integer", "minimum": 1 },
|
|
95
94
|
"updatedAt": { "type": "string", "format": "date-time" }
|
|
96
95
|
},
|
|
@@ -118,7 +117,6 @@
|
|
|
118
117
|
},
|
|
119
118
|
"additionalProperties": false
|
|
120
119
|
},
|
|
121
|
-
"score": { "type": ["number", "null"], "minimum": 0, "maximum": 10 },
|
|
122
120
|
"attempts": { "type": "integer", "minimum": 1 },
|
|
123
121
|
"updatedAt": { "type": "string", "format": "date-time" }
|
|
124
122
|
},
|
|
@@ -236,7 +234,7 @@
|
|
|
236
234
|
"approved": { "type": "boolean" },
|
|
237
235
|
"timestamp": { "type": ["string", "null"], "description": "ISO timestamp of the approval/revocation, null while never evaluated." },
|
|
238
236
|
"approvedBy": { "type": ["string", "null"], "description": "\"auto\" (approve/advance --mode auto), \"manual\" (human in the loop), \"policy:{workType}\" (gate stamped at birth by `morph-spec create` per the work type's skippedGates — see src/core/work-types.js; the gate-guard hook only special-cases \"auto\", so policy-stamped gates stay silent to it), a --approver name, or null while never evaluated." },
|
|
239
|
-
"selfAssessed": { "type": "boolean", "description": "Gate `review` SOMENTE. `true` quando o Gate 3 foi aprovado sem `4-review/evaluator-report.md`, via `morph-spec approve <f> review --self-assessed`. O
|
|
237
|
+
"selfAssessed": { "type": "boolean", "description": "Gate `review` SOMENTE. `true` quando o Gate 3 foi aprovado sem `4-review/evaluator-report.md`, via `morph-spec approve <f> review --self-assessed`. O veredito deveria vir do avaliador independente; esta flag registra a degradação em vez de deixá-la silenciosa, e aparece no `morph-spec list`." }
|
|
240
238
|
},
|
|
241
239
|
"additionalProperties": true
|
|
242
240
|
}
|
|
@@ -16,7 +16,7 @@ These are the only skills the harness needs. MORPH.md references them by path.
|
|
|
16
16
|
| `morph-stack-scan` | Detect stack drift in existing projects, report in stack-scan.md | yes |
|
|
17
17
|
| `morph-plan` | Produce use cases + spec.md + tasks.json (Gate 2) | yes |
|
|
18
18
|
| `morph-implement` | Execute each task with standard + persona, auto-advance between tasks| no |
|
|
19
|
-
| `morph-review` | Final review report with
|
|
19
|
+
| `morph-review` | Final review report with SHIP/FIX verdict (Gate 3) | yes |
|
|
20
20
|
| `morph-standards` | Locate and load a specific standard on demand | no |
|
|
21
21
|
|
|
22
22
|
The legacy `level-0-meta/` and `level-1-workflows/` skill trees were removed
|