@polymorphism-tech/morph-spec 8.45.0 → 8.47.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (146) hide show
  1. package/CLAUDE.md +6 -5
  2. package/README.md +2 -2
  3. package/bin/morph-spec.js +2 -4
  4. package/bin/validate.js +4 -25
  5. package/claude-plugin.json +1 -1
  6. package/docs/CHEATSHEET.md +2 -2
  7. package/docs/QUICKSTART.md +1 -1
  8. package/framework/CLAUDE.md +2 -2
  9. package/framework/MORPH.md +9 -9
  10. package/framework/commands/create-umbraco-lp.md +2 -0
  11. package/framework/commands/migrate-lp-to-umbraco.md +2 -0
  12. package/framework/commands/morph-archive.md +1 -1
  13. package/framework/commands/morph-hotfix.md +1 -1
  14. package/framework/commands/morph-infra.md +17 -203
  15. package/framework/commands/morph-media.md +3 -0
  16. package/framework/commands/morph-preflight.md +5 -28
  17. package/framework/commands/morph-proposal.md +2 -2
  18. package/framework/commands/morph-troubleshoot.md +2 -14
  19. package/framework/docs/CLI.md +8 -7
  20. package/framework/hooks/claude-code/core/state-sync.js +13 -1
  21. package/framework/hooks/shared/e2e-check.js +3 -4
  22. package/framework/hooks/shared/state-writer.js +1 -0
  23. package/framework/rules/ai-agents-standards.md +18 -16
  24. package/framework/rules/asaas-standards.md +3 -1
  25. package/framework/rules/csharp-standards.md +10 -8
  26. package/framework/rules/data-standards.md +5 -5
  27. package/framework/rules/dotnet-templates.md +1 -1
  28. package/framework/rules/frontend-standards.md +4 -2
  29. package/framework/rules/gsap-standards.md +10 -5
  30. package/framework/rules/hangfire-standards.md +3 -1
  31. package/framework/rules/infrastructure-standards.md +4 -2
  32. package/framework/rules/morph-workflow.md +2 -2
  33. package/framework/rules/neon-auth-standards.md +3 -1
  34. package/framework/rules/neon-standards.md +6 -4
  35. package/framework/rules/nextjs-standards.md +13 -11
  36. package/framework/rules/nextjs-templates.md +1 -1
  37. package/framework/rules/resend-standards.md +3 -1
  38. package/framework/schemas/config.schema.json +4 -0
  39. package/framework/schemas/feature.schema.json +4 -4
  40. package/framework/skills/README.md +1 -1
  41. package/framework/skills/core/create-umbraco-lp/SKILL.md +3 -3
  42. package/framework/skills/core/migrate-lp-to-umbraco/SKILL.md +3 -3
  43. package/framework/skills/core/morph-apply/SKILL.md +3 -3
  44. package/framework/skills/core/morph-brainstorming/SKILL.md +3 -3
  45. package/framework/skills/core/morph-hotfix/SKILL.md +4 -4
  46. package/framework/skills/core/morph-implement/SKILL.md +3 -3
  47. package/framework/skills/core/morph-machine/SKILL.md +3 -3
  48. package/framework/skills/core/morph-media/SKILL.md +3 -3
  49. package/framework/skills/core/morph-plan/SKILL.md +3 -3
  50. package/framework/skills/core/morph-proposal/SKILL.md +3 -3
  51. package/framework/skills/core/morph-review/SKILL.md +30 -17
  52. package/framework/skills/core/morph-stack-scan/SKILL.md +3 -3
  53. package/framework/skills/core/morph-standards/SKILL.md +6 -6
  54. package/framework/skills/core/morph-uiux/SKILL.md +3 -3
  55. package/framework/standards/STANDARDS.json +59 -56
  56. package/framework/standards/ai-agents/evals-with-cache.md +1 -1
  57. package/framework/standards/ai-agents/llm-runtime-defaults.md +1 -1
  58. package/framework/standards/ai-agents/modalities-speech-to-text.md +36 -26
  59. package/framework/standards/ai-agents/prompt-sources.md +6 -4
  60. package/framework/standards/ai-agents/providers/model-registry.md +1 -1
  61. package/framework/standards/ai-agents/rag-custom-pgvector.md +67 -39
  62. package/framework/standards/ai-agents/setup.md +1 -1
  63. package/framework/standards/ai-agents/structured-output.md +1 -1
  64. package/framework/standards/ai-agents/sweet-spot.md +1 -1
  65. package/framework/standards/architecture/code-comments.md +2 -2
  66. package/framework/standards/architecture/vertical-slice/vertical-slice.md +18 -9
  67. package/framework/standards/backend/api/validation.md +135 -47
  68. package/framework/standards/backend/database/postgresql/database.md +11 -7
  69. package/framework/standards/backend/database/vector-search-rag.md +207 -324
  70. package/framework/standards/backend/dotnet/fluent-validation-vsa.md +14 -8
  71. package/framework/standards/backend/dotnet/result-pattern.md +40 -6
  72. package/framework/standards/backend/dotnet/scalar-ui.md +13 -7
  73. package/framework/standards/backend/dotnet/scrutor.md +1 -1
  74. package/framework/standards/backend/dotnet/testing.md +1 -1
  75. package/framework/standards/backend/dotnet/vsa-handler-patterns.md +7 -7
  76. package/framework/standards/backend/integrations/asaas/asaas-api.md +309 -165
  77. package/framework/standards/backend/integrations/hangfire/hangfire-jobs.md +134 -154
  78. package/framework/standards/data/nosql/cache/redis.md +120 -44
  79. package/framework/standards/data/nosql/cosmos-db.md +114 -71
  80. package/framework/standards/frontend/design-system/animations.md +1 -1
  81. package/framework/standards/frontend/design-system/naming.md +1 -1
  82. package/framework/standards/frontend/nextjs/motion-patterns.md +45 -29
  83. package/framework/standards/frontend/nextjs/naming-conventions.md +1 -1
  84. package/framework/standards/infrastructure/docker/coolify-deploy.md +8 -6
  85. package/framework/standards/infrastructure/neon/neon-pgvector.md +120 -75
  86. package/framework/standards/infrastructure/neon/neon-setup.md +53 -31
  87. package/framework/templates/code/dotnet/contracts/contracts-vsa.cs +2 -2
  88. package/framework/templates/code/dotnet/extensions/database-extensions.cs +1 -1
  89. package/framework/templates/code/dotnet/extensions/result-extensions.cs +27 -0
  90. package/framework/templates/code/dotnet/feature/endpoint.cs.hbs +2 -2
  91. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/MediaDisclosureTests.cs +1 -1
  92. package/framework/templates/infrastructure/docker/coolify.template.json +3 -3
  93. package/framework/templates/infrastructure/github/README.md +2 -1
  94. package/framework/templates/infrastructure/github/workflows/deploy-coolify.yml.hbs +2 -1
  95. package/package.json +1 -1
  96. package/src/commands/observability/telemetry.js +3 -5
  97. package/src/commands/project/init.js +2 -1
  98. package/src/commands/project/update.js +16 -18
  99. package/src/commands/state/approve.js +17 -0
  100. package/src/commands/state/gate-check.js +4 -14
  101. package/src/commands/validation/verify.js +99 -57
  102. package/src/core/paths/output-schema.js +1 -1
  103. package/src/dashboard/app/App.jsx +0 -2
  104. package/src/dashboard/app/components/CommandPalette.jsx +2 -3
  105. package/src/dashboard/app/components/EntityLink.jsx +0 -1
  106. package/src/dashboard/app/components/Markdown.jsx +1 -1
  107. package/src/dashboard/app/components/Sidebar.jsx +1 -2
  108. package/src/dashboard/app/pages/RulesMap.jsx +3 -3
  109. package/src/dashboard/app/pages/SkillsLibrary.jsx +3 -3
  110. package/src/dashboard/scanner/activity-scanner.js +1 -1
  111. package/src/dashboard/scanner/docs-scanner.js +5 -23
  112. package/src/dashboard/scanner/features-scanner.js +1 -1
  113. package/src/dashboard/scanner/index.js +0 -2
  114. package/src/dashboard/scanner/rules-scanner.js +15 -10
  115. package/src/dashboard/scanner/skills-scanner.js +2 -3
  116. package/src/lib/ci/ci-verify.js +118 -0
  117. package/src/lib/ci/github-ci.js +192 -0
  118. package/src/lib/ci/workflow-scaffold.js +122 -0
  119. package/src/lib/evaluator-report.js +13 -0
  120. package/src/lib/risk-detector.js +8 -0
  121. package/src/lib/stack/stack-profile.js +1 -1
  122. package/src/lib/telemetry/verify-event.js +3 -2
  123. package/src/lib/validators/reuse/reuse-validator.js +2 -3
  124. package/src/lib/validators/reuse/symbol-scanner.js +2 -4
  125. package/src/lib/validators/validation-runner.js +8 -88
  126. package/src/lib/validators/verify-runner.js +22 -72
  127. package/src/scripts/setup-infra.js +33 -22
  128. package/src/utils/agents-installer.js +20 -8
  129. package/src/utils/file-copier.js +7 -5
  130. package/framework/commands/commit.md +0 -166
  131. package/framework/hooks/claude-code/__pycache__/statusline.cpython-313.pyc +0 -0
  132. package/framework/rubrics/backend.json +0 -41
  133. package/framework/rubrics/default.json +0 -36
  134. package/framework/rubrics/frontend.json +0 -36
  135. package/framework/templates/project/validate.js +0 -97
  136. package/src/dashboard/app/pages/Rubrics.jsx +0 -97
  137. package/src/dashboard/scanner/rubrics-scanner.js +0 -12
  138. package/src/lib/validators/architecture/architecture-validator.js +0 -270
  139. package/src/lib/validators/architecture/index.js +0 -7
  140. package/src/lib/validators/css/css-validator.js +0 -340
  141. package/src/lib/validators/css/index.js +0 -7
  142. package/src/lib/validators/design-system/design-system-validator.js +0 -237
  143. package/src/lib/validators/design-system/index.js +0 -7
  144. package/src/lib/validators/score-aggregator.js +0 -244
  145. package/src/lib/validators/ui/index.js +0 -7
  146. package/src/lib/validators/ui/ui-contrast-validator.js +0 -422
@@ -1,215 +1,29 @@
1
1
  ---
2
- description: Manage Azure infrastructure with Bicep templates — provision, update, and monitor cloud resources
3
- argument-hint: [up|down|plan|status]
4
- allowed-tools: Read, Write, Edit, Bash, Glob, AskUserQuestion
2
+ description: Infra do projeto — Docker Compose local e deploy no Coolify (API via SSH), com política de custo
3
+ argument-hint: [compose|deploy|status]
4
+ allowed-tools: Read, Write, Edit, Bash, Glob, Grep, AskUserQuestion, Agent
5
5
  ---
6
6
 
7
- # /morph-infra - Infrastructure Management
7
+ # /morph-infra — Infraestrutura
8
8
 
9
- Manage Azure infrastructure using Bicep templates.
9
+ Antes de agir, leia os standards: `morph-spec standards --find infra` (deploy no Coolify e isolamento do compose local). Trabalho de infra vai para a persona `morph-infra-engineer` quando ela está instalada; sem ela, siga os standards direto.
10
10
 
11
- ## Usage
11
+ ## Local — Docker Compose
12
12
 
13
- ```
14
- /morph-infra [action] [options]
15
- ```
13
+ Cada worktree sobe o próprio stack: portas do bloco do worktree (`${VAR:-default}`), nunca `container_name`, healthcheck em todo serviço. Subir e provar: `morph-spec e2e up {feature}` e o nó e2e do `morph-spec verify {feature}`.
16
14
 
17
- ### Actions
15
+ ## Deploy — Coolify
18
16
 
19
- | Action | Description |
20
- |--------|-------------|
21
- | `init` | Initialize IaC structure in the project |
22
- | `validate` | Validate Bicep templates |
23
- | `plan` | Show what-if of changes |
24
- | `deploy` | Execute resource deployment |
25
- | `destroy` | Remove all resources |
17
+ Não existe MCP nem CLI do Coolify: o acesso é por SSH ao servidor (alias configurado pelo usuário) e `curl` na API em `localhost`, com o token lido do lado do servidor e nunca impresso. Variáveis de ambiente ficam no Coolify, nunca no Dockerfile. Repositório e PRs pelo `gh`.
26
18
 
27
- ---
28
-
29
- ## Workflow
30
-
31
- ### 1. INIT - Initialize IaC
32
-
33
- When the user requests `/morph-infra init`:
34
-
35
- 1. Create the `infra/` structure in the project:
36
- ```
37
- infra/
38
- ├── main.bicep
39
- ├── parameters.dev.json
40
- ├── parameters.prod.json
41
- └── modules/
42
- ├── container-app.bicep
43
- ├── container-app-env.bicep
44
- ├── sql-database.bicep
45
- ├── storage.bicep
46
- ├── key-vault.bicep
47
- └── app-insights.bicep
48
- ```
49
-
50
- 2. Copy templates from `.morph/framework/templates/infrastructure/...`
51
-
52
- 3. Replace placeholders:
53
- - `{{APP_NAME}}` → project name
54
- - `{{SUBSCRIPTION_ID}}` → request from user
55
- - `{{RESOURCE_GROUP}}` → suggest default `rg-{app}-{env}`
56
-
57
- 4. Document the created structure in `decisions.md`
58
-
59
- ---
60
-
61
- ### 2. VALIDATE - Validate Templates
62
-
63
- When the user requests `/morph-infra validate`:
64
-
65
- 1. Run Bicep validation:
66
- ```bash
67
- az bicep build --file infra/main.bicep
68
- ```
69
-
70
- 2. Check required parameters
71
-
72
- 3. Report errors or success
73
-
74
- ---
75
-
76
- ### 3. PLAN - Preview Changes
77
-
78
- When the user requests `/morph-infra plan [env]`:
79
-
80
- 1. Check if Azure CLI is authenticated:
81
- ```bash
82
- az account show
83
- ```
84
-
85
- 2. Run what-if:
86
- ```bash
87
- az deployment group what-if \
88
- --resource-group rg-{app}-{env} \
89
- --template-file infra/main.bicep \
90
- --parameters @infra/parameters.{env}.json
91
- ```
92
-
93
- 3. Present change summary:
94
- - Resources to create
95
- - Resources to modify
96
- - Resources to delete
97
- - Estimated costs
98
-
99
- 4. **STOP and wait for approval** before deploy
100
-
101
- ---
102
-
103
- ### 4. DEPLOY - Execute Deploy
104
-
105
- When the user requests `/morph-infra deploy [env]`:
106
-
107
- 1. Verify plan was approved
108
-
109
- 2. Create resource group if it doesn't exist:
110
- ```bash
111
- az group create --name rg-{app}-{env} --location brazilsouth
112
- ```
113
-
114
- 3. Execute deploy:
115
- ```bash
116
- az deployment group create \
117
- --resource-group rg-{app}-{env} \
118
- --template-file infra/main.bicep \
119
- --parameters @infra/parameters.{env}.json
120
- ```
19
+ ## Custo
121
20
 
122
- 4. Capture outputs:
123
- - Container App URL
124
- - SQL Connection String
125
- - Key Vault URI
21
+ | Custo mensal | Ação |
22
+ |---|---|
23
+ | grátis | segue |
24
+ | até US$ 10 | confirme com o usuário |
25
+ | acima de US$ 10 | ADR em `decisions.md` antes de criar |
126
26
 
127
- 5. Update documentation with deploy information
128
-
129
- ---
130
-
131
- ### 5. DESTROY - Remove Resources
132
-
133
- When the user requests `/morph-infra destroy [env]`:
134
-
135
- 1. **WARN** that this action is irreversible
136
-
137
- 2. List resources that will be deleted
138
-
139
- 3. **STOP and wait for explicit confirmation** ("yes, delete")
140
-
141
- 4. Execute:
142
- ```bash
143
- az group delete --name rg-{app}-{env} --yes --no-wait
144
- ```
145
-
146
- ---
147
-
148
- ## Security Rules
149
-
150
- ### NEVER:
151
- - Expose secrets in logs
152
- - Commit files with secrets
153
- - Delete production resources without explicit confirmation
154
- - Create resources outside Bicep (zero portal)
155
-
156
- ### ALWAYS:
157
- - Use Key Vault for secrets
158
- - Validate Bicep before deploy
159
- - Run what-if before deploy
160
- - Document estimated costs
161
- - Keep parameters.json separated by environment
162
-
163
- ---
164
-
165
- ## Cost Guardian Integration
166
-
167
- Before any deploy, check with Cost Guardian:
168
-
169
- 1. Estimate monthly resource costs
170
- 2. Verify it's within approved limits:
171
- - Free tier: no approval needed
172
- - Up to $10/month: requires confirmation
173
- - Above $10: requires ADR
174
-
175
- ---
176
-
177
- ## Usage Example
178
-
179
- ```
180
- User: /morph-infra init
181
- Claude: Creating IaC structure...
182
- - infra/main.bicep
183
- - infra/parameters.dev.json
184
- - infra/modules/*.bicep
185
- Done! Run /morph-infra validate to verify.
186
-
187
- User: /morph-infra plan dev
188
- Claude: Analyzing changes for dev environment...
189
-
190
- CREATE:
191
- + Container App Environment
192
- + Container App
193
- + SQL Server (Free)
194
- + Storage Account (LRS)
195
- + Key Vault
196
- + Application Insights
197
-
198
- ESTIMATED COST: ~$0/month (Free tier)
199
-
200
- Approve deploy? (yes/no)
201
-
202
- User: yes
203
- Claude: Executing deploy...
204
- ✓ Resource Group created
205
- ✓ Deploy completed in 3m 45s
206
-
207
- OUTPUTS:
208
- - URL: https://myapp-dev.azurecontainerapps.io
209
- - SQL: tcp:myapp-dev-sql.database.windows.net
210
- - Key Vault: https://myapp-dev-kv.vault.azure.net
211
- ```
212
-
213
- ---
27
+ Nunca apague recurso de produção sem confirmação explícita.
214
28
 
215
- *MORPH-SPEC by Polymorphism Tech*
29
+ **Input:** $ARGUMENTS
@@ -2,6 +2,9 @@
2
2
  description: Conduz a autoria de mídia gerada por IA — escolher o modelo pelo trabalho, montar o prompt como spec versionada, encadear a partir de um herói aprovado, subir a escada de custo e conduzir o gate humano de escolha entre N candidatas.
3
3
  argument-hint: [o que gerar]
4
4
  allowed-tools: Read, Write, Edit, Bash, Glob, Grep, AskUserQuestion, Skill
5
+ stacks:
6
+ - dotnet
7
+ - nextjs
5
8
  ---
6
9
 
7
10
  # /morph-media — Autoria de mídia gerada
@@ -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 validate {feature}
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
- | Feature validation passes | `validate` | 100% pass rate |
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
- ## Test Suite (run before infrastructure checks)
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", "pode avançar", "lgtm".
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: "pode implementar", "aprovado".
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
- Search the known problems and standards files for matching patterns:
30
+ Find the standard that covers the error, then read its matching section:
31
31
 
32
32
  ```bash
33
- # Search in framework standards
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:
@@ -44,8 +44,9 @@
44
44
  ## Validação
45
45
 
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
- - **`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]`** — 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
- - **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**, mapeado em `code_quality` da rubrica: (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ê.
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
+ - **`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**: (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ê.
49
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`.
50
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.
51
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/`.
@@ -275,8 +276,8 @@ passa com aviso de alta, e o comando grava.
275
276
  | --- | --- | --- |
276
277
  | Sub-agent implementador | só o filtro dos próprios testes, **uma vez**, antes do relatório (nunca `morph-spec verify`, nunca red→green) | A prova é do orquestrador; cada rodada extra dentro do sub-agent custa turnos que o teto corta |
277
278
  | Loop por task | `verify {f} {task}` (recorte automático: o `group` escolhe os nós, os `outputs` estreitam os testes — ver *O recorte por task*; `--filter {stack-da-task}` só para forçar os nós, `--full` para desligar) | O relógio de uma task não paga a suíte inteira; a task tocou **um** stack |
278
- | Gate 3 | `verify {f} --json` (sem filtro) | A prova que o avaliador lê tem de cobrir **todos** os nós |
279
- | CI | a suíte inteira, todos os nós | idem |
279
+ | Gate 3 | `verify {f} --ci --json` (sem filtro; sem CI disponível, roda local) | A prova tem de cobrir **todos** os nós; no CI ela não ocupa a máquina e roda em paralelo com o avaliador |
280
+ | CI | a suíte inteira, todos os nós (`morph-verify.yml` roda o mesmo `verify {f}`) | idem — e o artefato dele é o stamp do Gate 3 |
280
281
  | Evidência de eval (task com `evidencias/` em `outputs`) | `verify {f} --filter {id-do-nó-evals} --skip-e2e --no-persist` | Só o nó `evals` (pulado no escopo de task), sem stack e sem carimbo — não é a verificação da task, é a prova que o `morph-implement` §5 grava |
281
282
 
282
283
  > **Não existe constante de 600 s neste CLI** (`grep -rn "600000" src/ framework/` → zero). O teto que
@@ -474,12 +475,12 @@ A saída de todos é a mesma: `--full`.
474
475
 
475
476
  ## Instalação e atualização
476
477
 
477
- - **`morph-spec update [--templates] [--standards] [--dry-run] [--force]`** — reinstala os payloads do framework no projeto (standards, hooks, schemas, rubrics, evals, `.claude/{commands,rules,skills,agents}`, `.claude/CLAUDE.md`, `.gitignore`/`.gitattributes`, statusline global).
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).
478
479
 
479
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`.
480
481
 
481
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"**:
482
- - **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);
483
484
  - **fora do projeto** — `~/.claude/statusline.sh`, `~/.claude/statusline.py` e a mesclagem de `~/.claude/settings.json`;
484
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.
485
486
 
@@ -539,6 +540,6 @@ O servidor MCP (`graphify-mcp`, 10 tools, todas aceitando `project_path`) está
539
540
  | `PostToolUse` / `PostToolUseFailure` | `tool`, `toolUseId`, `durationMs`; Bash/PowerShell: `command` (redigido, ≤2000 chars) + `commandChars`, `exitCode`, `outcome` (`ok`/`error`/`timeout`/`interrupted`/`background`), `exitMasked` quando o exit gravado não é o do trabalho (pipe sem `pipefail`, `|| …`, `&` em background, ou `;` seguido de `echo`/`true`/`:` no fim); Read: `target`, `kind`, `bytes` (tamanho do arquivo), `offset`, `limit`, `bytesRead` — um Read que falhou não grava bytes, porque não trouxe nada; Agent: `persona`, `desc`, `prompt` + `promptChars`; SendMessage: `target`, `messageChars` |
540
541
  | `ToolBatch` | `size`, `tools` (contagem por ferramenta) — um por turno do modelo |
541
542
  | `SessionUsage` / `AgentUsage` | totais acumulados do transcript + `delta` desde a amostra anterior |
542
- | `Verify` | `scope`, `task`, `status`, `sha`, `fingerprint` (a árvore fora de `.morph/`), `tree` (`root`/`worktree`), `persisted`, `reason`, `nodes.{build,tests,validators,e2e}.{status,durationMs}`, `nodes.validators.{ran,skipped}` |
543
+ | `Verify` | `scope`, `task`, `status`, `sha`, `fingerprint` (a árvore fora de `.morph/`), `tree` (`root`/`worktree`), `persisted`, `reason`, `source` (`local`/`ci`: a rodada do `--ci` registra os nós e durações do run), `nodes.{build,tests,validators,e2e}.{status,durationMs}`, `nodes.validators.{ran,skipped}` |
543
544
 
544
545
  Nada da saída de um comando é gravado — nem o corpo de erro do `PostToolUseFailure`, onde um valor de `.env` pode aparecer sem nome ao lado. `command`, `prompt`, `desc` e `target` passam por redação de segredos óbvios antes de gravar: valor ao lado de um nome de segredo (`API_KEY=…`, `*_KEY`/`*_PASS=…`, `--password …`, `user-secrets set <k> <v>`, `AccountKey=…`), `Authorization: …`, `user:pass@` (senha com `/` inclusive) e `?sig=`/`?access_token=` em URL, e formatos de token (GitHub, `sk-`, `sk_live_`, `npm_`, Slack, AWS, Google, JWT). É por padrão, não por garantia: um valor sem nome ao lado passa.
@@ -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
- setGateApproved(featureName, gate, { approvedBy: 'manual' }, root);
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 — `framework/evals/rubrics/architecture.md` calls out the
7
- * canonical case in its own words: a service injected into a Minimal API
8
- * endpoint without a matching `AddScoped` "derruba a app inteira no startup",
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 — auto-carregados.** São os standards core: aplicam-se a praticamente todo
13
- > agente MAF. Carregue-os sempre que tocar código de agentes.
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
- @.morph/framework/standards/ai-agents/setup.md
16
- @.morph/framework/standards/ai-agents/sweet-spot.md
17
- @.morph/framework/standards/ai-agents/providers/model-registry.md
18
- @.morph/framework/standards/ai-agents/structured-output.md
19
- @.morph/framework/standards/ai-agents/multi-agent-patterns.md
20
- @.morph/framework/standards/ai-agents/prompt-sources.md
21
- @.morph/framework/standards/ai-agents/rag-custom-pgvector.md
22
- @.morph/framework/standards/ai-agents/middleware-patterns.md
23
- @.morph/framework/standards/ai-agents/observability-patterns.md
24
- @.morph/framework/standards/ai-agents/workflows.md
25
- @.morph/framework/standards/ai-agents/production.md
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 (NÃO auto-carregados).** São standards de modalidade, otimização e
30
- > MCP: relevantes apenas quando a feature usa especificamente esse recurso. Carregue via
31
- > `morph-standards` quando uma keyword corresponder — manter fora do auto-load preserva o
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
- @.morph/framework/standards/backend/integrations/asaas/asaas-api.md
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>`.