@polymorphism-tech/morph-spec 8.45.0 → 8.47.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CLAUDE.md +6 -5
- package/README.md +2 -2
- package/bin/morph-spec.js +2 -4
- package/bin/validate.js +4 -25
- package/claude-plugin.json +1 -1
- package/docs/CHEATSHEET.md +2 -2
- package/docs/QUICKSTART.md +1 -1
- package/framework/CLAUDE.md +2 -2
- package/framework/MORPH.md +9 -9
- package/framework/commands/create-umbraco-lp.md +2 -0
- package/framework/commands/migrate-lp-to-umbraco.md +2 -0
- package/framework/commands/morph-archive.md +1 -1
- package/framework/commands/morph-hotfix.md +1 -1
- package/framework/commands/morph-infra.md +17 -203
- package/framework/commands/morph-media.md +3 -0
- package/framework/commands/morph-preflight.md +5 -28
- package/framework/commands/morph-proposal.md +2 -2
- package/framework/commands/morph-troubleshoot.md +2 -14
- package/framework/docs/CLI.md +8 -7
- package/framework/hooks/claude-code/core/state-sync.js +13 -1
- package/framework/hooks/shared/e2e-check.js +3 -4
- package/framework/hooks/shared/state-writer.js +1 -0
- package/framework/rules/ai-agents-standards.md +18 -16
- package/framework/rules/asaas-standards.md +3 -1
- package/framework/rules/csharp-standards.md +10 -8
- package/framework/rules/data-standards.md +5 -5
- package/framework/rules/dotnet-templates.md +1 -1
- package/framework/rules/frontend-standards.md +4 -2
- package/framework/rules/gsap-standards.md +10 -5
- package/framework/rules/hangfire-standards.md +3 -1
- package/framework/rules/infrastructure-standards.md +4 -2
- package/framework/rules/morph-workflow.md +2 -2
- package/framework/rules/neon-auth-standards.md +3 -1
- package/framework/rules/neon-standards.md +6 -4
- package/framework/rules/nextjs-standards.md +13 -11
- package/framework/rules/nextjs-templates.md +1 -1
- package/framework/rules/resend-standards.md +3 -1
- package/framework/schemas/config.schema.json +4 -0
- package/framework/schemas/feature.schema.json +4 -4
- package/framework/skills/README.md +1 -1
- package/framework/skills/core/create-umbraco-lp/SKILL.md +3 -3
- package/framework/skills/core/migrate-lp-to-umbraco/SKILL.md +3 -3
- package/framework/skills/core/morph-apply/SKILL.md +3 -3
- package/framework/skills/core/morph-brainstorming/SKILL.md +3 -3
- package/framework/skills/core/morph-hotfix/SKILL.md +4 -4
- package/framework/skills/core/morph-implement/SKILL.md +3 -3
- package/framework/skills/core/morph-machine/SKILL.md +3 -3
- package/framework/skills/core/morph-media/SKILL.md +3 -3
- package/framework/skills/core/morph-plan/SKILL.md +3 -3
- package/framework/skills/core/morph-proposal/SKILL.md +3 -3
- package/framework/skills/core/morph-review/SKILL.md +30 -17
- package/framework/skills/core/morph-stack-scan/SKILL.md +3 -3
- package/framework/skills/core/morph-standards/SKILL.md +6 -6
- package/framework/skills/core/morph-uiux/SKILL.md +3 -3
- package/framework/standards/STANDARDS.json +59 -56
- package/framework/standards/ai-agents/evals-with-cache.md +1 -1
- package/framework/standards/ai-agents/llm-runtime-defaults.md +1 -1
- package/framework/standards/ai-agents/modalities-speech-to-text.md +36 -26
- package/framework/standards/ai-agents/prompt-sources.md +6 -4
- package/framework/standards/ai-agents/providers/model-registry.md +1 -1
- package/framework/standards/ai-agents/rag-custom-pgvector.md +67 -39
- package/framework/standards/ai-agents/setup.md +1 -1
- package/framework/standards/ai-agents/structured-output.md +1 -1
- package/framework/standards/ai-agents/sweet-spot.md +1 -1
- package/framework/standards/architecture/code-comments.md +2 -2
- package/framework/standards/architecture/vertical-slice/vertical-slice.md +18 -9
- package/framework/standards/backend/api/validation.md +135 -47
- package/framework/standards/backend/database/postgresql/database.md +11 -7
- package/framework/standards/backend/database/vector-search-rag.md +207 -324
- package/framework/standards/backend/dotnet/fluent-validation-vsa.md +14 -8
- package/framework/standards/backend/dotnet/result-pattern.md +40 -6
- package/framework/standards/backend/dotnet/scalar-ui.md +13 -7
- package/framework/standards/backend/dotnet/scrutor.md +1 -1
- package/framework/standards/backend/dotnet/testing.md +1 -1
- package/framework/standards/backend/dotnet/vsa-handler-patterns.md +7 -7
- package/framework/standards/backend/integrations/asaas/asaas-api.md +309 -165
- package/framework/standards/backend/integrations/hangfire/hangfire-jobs.md +134 -154
- package/framework/standards/data/nosql/cache/redis.md +120 -44
- package/framework/standards/data/nosql/cosmos-db.md +114 -71
- package/framework/standards/frontend/design-system/animations.md +1 -1
- package/framework/standards/frontend/design-system/naming.md +1 -1
- package/framework/standards/frontend/nextjs/motion-patterns.md +45 -29
- package/framework/standards/frontend/nextjs/naming-conventions.md +1 -1
- package/framework/standards/infrastructure/docker/coolify-deploy.md +8 -6
- package/framework/standards/infrastructure/neon/neon-pgvector.md +120 -75
- package/framework/standards/infrastructure/neon/neon-setup.md +53 -31
- package/framework/templates/code/dotnet/contracts/contracts-vsa.cs +2 -2
- package/framework/templates/code/dotnet/extensions/database-extensions.cs +1 -1
- package/framework/templates/code/dotnet/extensions/result-extensions.cs +27 -0
- package/framework/templates/code/dotnet/feature/endpoint.cs.hbs +2 -2
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/MediaDisclosureTests.cs +1 -1
- package/framework/templates/infrastructure/docker/coolify.template.json +3 -3
- package/framework/templates/infrastructure/github/README.md +2 -1
- package/framework/templates/infrastructure/github/workflows/deploy-coolify.yml.hbs +2 -1
- package/package.json +1 -1
- package/src/commands/observability/telemetry.js +3 -5
- package/src/commands/project/init.js +2 -1
- package/src/commands/project/update.js +16 -18
- package/src/commands/state/approve.js +17 -0
- package/src/commands/state/gate-check.js +4 -14
- package/src/commands/validation/verify.js +99 -57
- package/src/core/paths/output-schema.js +1 -1
- package/src/dashboard/app/App.jsx +0 -2
- package/src/dashboard/app/components/CommandPalette.jsx +2 -3
- package/src/dashboard/app/components/EntityLink.jsx +0 -1
- package/src/dashboard/app/components/Markdown.jsx +1 -1
- package/src/dashboard/app/components/Sidebar.jsx +1 -2
- package/src/dashboard/app/pages/RulesMap.jsx +3 -3
- package/src/dashboard/app/pages/SkillsLibrary.jsx +3 -3
- package/src/dashboard/scanner/activity-scanner.js +1 -1
- package/src/dashboard/scanner/docs-scanner.js +5 -23
- package/src/dashboard/scanner/features-scanner.js +1 -1
- package/src/dashboard/scanner/index.js +0 -2
- package/src/dashboard/scanner/rules-scanner.js +15 -10
- package/src/dashboard/scanner/skills-scanner.js +2 -3
- package/src/lib/ci/ci-verify.js +118 -0
- package/src/lib/ci/github-ci.js +192 -0
- package/src/lib/ci/workflow-scaffold.js +122 -0
- package/src/lib/evaluator-report.js +13 -0
- package/src/lib/risk-detector.js +8 -0
- package/src/lib/stack/stack-profile.js +1 -1
- package/src/lib/telemetry/verify-event.js +3 -2
- package/src/lib/validators/reuse/reuse-validator.js +2 -3
- package/src/lib/validators/reuse/symbol-scanner.js +2 -4
- package/src/lib/validators/validation-runner.js +8 -88
- package/src/lib/validators/verify-runner.js +22 -72
- package/src/scripts/setup-infra.js +33 -22
- package/src/utils/agents-installer.js +20 -8
- package/src/utils/file-copier.js +7 -5
- package/framework/commands/commit.md +0 -166
- package/framework/hooks/claude-code/__pycache__/statusline.cpython-313.pyc +0 -0
- package/framework/rubrics/backend.json +0 -41
- package/framework/rubrics/default.json +0 -36
- package/framework/rubrics/frontend.json +0 -36
- package/framework/templates/project/validate.js +0 -97
- package/src/dashboard/app/pages/Rubrics.jsx +0 -97
- package/src/dashboard/scanner/rubrics-scanner.js +0 -12
- package/src/lib/validators/architecture/architecture-validator.js +0 -270
- package/src/lib/validators/architecture/index.js +0 -7
- package/src/lib/validators/css/css-validator.js +0 -340
- package/src/lib/validators/css/index.js +0 -7
- package/src/lib/validators/design-system/design-system-validator.js +0 -237
- package/src/lib/validators/design-system/index.js +0 -7
- package/src/lib/validators/score-aggregator.js +0 -244
- package/src/lib/validators/ui/index.js +0 -7
- package/src/lib/validators/ui/ui-contrast-validator.js +0 -422
|
@@ -1,215 +1,29 @@
|
|
|
1
1
|
---
|
|
2
|
-
description:
|
|
3
|
-
argument-hint: [
|
|
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
|
|
7
|
+
# /morph-infra — Infraestrutura
|
|
8
8
|
|
|
9
|
-
|
|
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
|
-
##
|
|
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
|
-
|
|
15
|
+
## Deploy — Coolify
|
|
18
16
|
|
|
19
|
-
|
|
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
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
@@ -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
|
-
-
|
|
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
|
|
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,
|
|
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
|
-
|
|
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>`.
|