@polymorphism-tech/morph-spec 8.29.0 → 8.32.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 +11 -1
- package/README.md +13 -2
- package/bin/morph-spec.js +67 -1
- package/bin/validate.js +379 -379
- package/claude-plugin.json +1 -1
- package/docs/CHEATSHEET.md +1 -1
- package/docs/QUICKSTART.md +1 -1
- package/framework/CLAUDE.md +4 -1
- package/framework/MORPH.md +4 -3
- package/framework/agents.json +6 -6
- package/framework/commands/morph-status.md +1 -1
- package/framework/docs/CLI.md +68 -45
- package/framework/evals/rubrics/code-quality.md +17 -0
- package/framework/evals/rubrics/condensed.md +3 -1
- package/framework/hooks/claude-code/statusline.sh +11 -11
- package/framework/hooks/git/commit-msg/conventional-commits.sh +33 -33
- package/framework/hooks/git/pre-commit/agents.sh +25 -25
- package/framework/hooks/git/pre-commit/orchestrator.sh +64 -64
- package/framework/hooks/git/pre-commit/specs.sh +50 -50
- package/framework/hooks/git/pre-push/run-tests.sh +44 -44
- package/framework/hooks/shared/phase-utils.js +2 -0
- package/framework/hooks/shared/stdin-reader.js +26 -26
- package/framework/mcp/registry.json +39 -4
- package/framework/rubrics/backend.json +2 -2
- package/framework/rubrics/default.json +2 -2
- package/framework/rubrics/frontend.json +2 -2
- package/framework/schemas/tasks.schema.json +25 -0
- package/framework/skills/core/create-umbraco-lp/SKILL.md +1 -1
- package/framework/skills/core/migrate-lp-to-umbraco/SKILL.md +1 -1
- package/framework/skills/core/morph-apply/SKILL.md +14 -4
- package/framework/skills/core/morph-brainstorming/SKILL.md +1 -1
- package/framework/skills/core/morph-eval/SKILL.md +1 -1
- package/framework/skills/core/morph-hotfix/SKILL.md +2 -2
- package/framework/skills/core/morph-implement/SKILL.md +12 -2
- package/framework/skills/core/morph-plan/SKILL.md +45 -3
- package/framework/skills/core/morph-proposal/SKILL.md +1 -1
- package/framework/skills/core/morph-review/SKILL.md +12 -4
- package/framework/skills/core/morph-stack-scan/SKILL.md +13 -1
- package/framework/skills/core/morph-standards/SKILL.md +1 -1
- package/framework/skills/core/morph-uiux/SKILL.md +1 -1
- package/framework/standards/ai-agents/agent-session.md +160 -160
- package/framework/standards/ai-agents/batching.md +193 -193
- package/framework/standards/ai-agents/context-providers.md +188 -188
- package/framework/standards/ai-agents/mcp-server.md +250 -250
- package/framework/standards/ai-agents/mcp-tools.md +197 -197
- package/framework/standards/ai-agents/middleware-patterns.md +207 -207
- package/framework/standards/ai-agents/modalities-image-gen.md +173 -173
- package/framework/standards/ai-agents/modalities-speech-to-text.md +171 -171
- package/framework/standards/ai-agents/modalities-text-to-speech.md +181 -181
- package/framework/standards/ai-agents/modalities-vision.md +201 -201
- package/framework/standards/ai-agents/multi-agent-patterns.md +155 -155
- package/framework/standards/ai-agents/production.md +231 -231
- package/framework/standards/ai-agents/prompts-in-db.md +214 -214
- package/framework/standards/ai-agents/providers/model-registry.md +158 -158
- package/framework/standards/ai-agents/service-tier-flex.md +135 -135
- package/framework/standards/ai-agents/structured-output.md +249 -249
- package/framework/standards/ai-agents/sweet-spot.md +109 -109
- package/framework/templates/.idea/morph-templates.xml +92 -0
- package/framework/templates/.vscode/morph-templates.code-snippets +186 -0
- package/framework/templates/IDE-SNIPPETS.md +262 -262
- package/framework/templates/code/dotnet/abstractions/api-endpoint.cs +7 -7
- package/framework/templates/code/dotnet/ai-agents/AgentBootstrap.cs.template +39 -39
- package/framework/templates/code/dotnet/ai-agents/EmbeddingService.cs.template +68 -68
- package/framework/templates/code/dotnet/ai-agents/ModelRegistry.cs.template +117 -117
- package/framework/templates/code/dotnet/ai-agents/PromptRepository.cs.template +95 -95
- package/framework/templates/code/dotnet/ai-agents/ResilientToolMiddleware.cs.template +61 -61
- package/framework/templates/code/dotnet/ai-agents/VectorSearchTool.cs.template +107 -107
- package/framework/templates/code/dotnet/ai-agents/model-registry.json.template +58 -58
- package/framework/templates/code/dotnet/contracts/contracts-vsa.cs +281 -281
- package/framework/templates/code/dotnet/database/migration.cs +83 -83
- package/framework/templates/code/dotnet/feature/endpoint.cs.hbs +21 -21
- package/framework/templates/code/dotnet/feature/request.cs.hbs +4 -4
- package/framework/templates/code/dotnet/feature/response.cs.hbs +4 -4
- package/framework/templates/code/dotnet/feature/validator.cs.hbs +15 -15
- package/framework/templates/code/sql/ai-agents/agent-prompts-migration.sql.template +58 -58
- package/framework/templates/code/sql/ai-agents/pgvector-embeddings-migration.sql.template +91 -91
- package/framework/templates/code/typescript/contracts.ts +167 -167
- package/framework/templates/context/ARCHITECTURE.md +20 -0
- package/framework/templates/docs/clarifications.md +253 -253
- package/framework/templates/docs/schema-analysis.md +119 -119
- package/framework/templates/docs/ui-components.md +52 -52
- package/framework/templates/docs/ui-design-system.md +76 -76
- package/framework/templates/docs/ui-flows.md +167 -167
- package/framework/templates/docs/ui-mockups.md +98 -98
- package/framework/templates/feature/decisions.md +122 -122
- package/framework/templates/frontend/nextjs/Dockerfile.nextjs.hbs +43 -43
- package/framework/templates/frontend/nextjs/client-component.tsx.hbs +26 -26
- package/framework/templates/frontend/nextjs/env.mjs.hbs +32 -32
- package/framework/templates/frontend/nextjs/feature-form.tsx.hbs +56 -56
- package/framework/templates/frontend/nextjs/page.tsx.hbs +22 -22
- package/framework/templates/frontend/nextjs/tsconfig.json.hbs +26 -26
- package/framework/templates/frontend/nextjs/use-feature.ts.hbs +54 -54
- package/framework/templates/infrastructure/docker/Dockerfile.template +58 -58
- package/framework/templates/infrastructure/docker/dockerfile-api.dockerfile +38 -38
- package/framework/templates/infrastructure/docker/dockerfile-web.dockerfile +48 -48
- package/framework/templates/infrastructure/github/README.md +56 -56
- package/framework/templates/infrastructure/github/actions/docker-build-push/action.yml.hbs +45 -45
- package/framework/templates/infrastructure/github/actions/health-check/action.yml.hbs +27 -27
- package/framework/templates/infrastructure/github/workflows/docker-build-push.yml.hbs +59 -59
- package/framework/templates/infrastructure/github/workflows/dotnet-build.yml.hbs +39 -39
- package/framework/templates/integrations/asaas-client.cs +387 -387
- package/framework/templates/integrations/asaas-webhook.cs +351 -351
- package/framework/templates/meta-prompts/fusion/fusion-agent.md +76 -76
- package/framework/templates/meta-prompts/fusion/fusion-aggregator.md +100 -100
- package/framework/templates/meta-prompts/validators/pre-commit-validator.md +95 -95
- package/framework/templates/project/validate.js +120 -120
- package/framework/templates/state.template.json +17 -17
- package/framework/templates/ui/design-system.css +226 -226
- package/package.json +1 -1
- package/src/commands/dashboard/dashboard.js +53 -53
- package/src/commands/graph/affected.js +89 -0
- package/src/commands/graph/build.js +94 -0
- package/src/commands/graph/explain.js +96 -0
- package/src/commands/graph/status.js +93 -0
- package/src/commands/index.js +13 -13
- package/src/commands/project/branch.js +170 -0
- package/src/commands/project/checkpoint.js +209 -209
- package/src/commands/project/doctor.js +148 -9
- package/src/commands/project/fleet.js +10 -3
- package/src/commands/project/worktree.js +73 -50
- package/src/commands/state/finish.js +41 -26
- package/src/commands/state/list.js +79 -6
- package/src/core/paths/output-schema.js +7 -0
- package/src/core/state/lifecycle-audit.js +127 -0
- package/src/core/state/phase-state-machine.js +197 -197
- package/src/core/templates/template-renderer.js +477 -477
- package/src/dashboard/app/components/DetailPanel.jsx +11 -11
- package/src/dashboard/app/components/FlowGraph.jsx +17 -17
- package/src/dashboard/app/components/SearchFilter.jsx +22 -22
- package/src/dashboard/app/hooks/useDetailPanel.js +15 -15
- package/src/dashboard/app/main.jsx +13 -13
- package/src/dashboard/app/pages/AgentMap.jsx +370 -370
- package/src/dashboard/app/pages/HookInspector.jsx +418 -418
- package/src/dashboard/app/pages/PhasePipeline.jsx +408 -408
- package/src/dashboard/app/pages/RulesMap.jsx +273 -273
- package/src/dashboard/app/pages/SkillsLibrary.jsx +366 -366
- package/src/dashboard/app/pages/Standards.jsx +332 -332
- package/src/dashboard/app/styles/index.css +47 -47
- package/src/dashboard/index.html +12 -12
- package/src/dashboard/package.json +24 -24
- package/src/dashboard/scanner/phases-scanner.js +15 -15
- package/src/dashboard/scanner/rubrics-scanner.js +12 -12
- package/src/dashboard/scanner/rules-scanner.js +37 -37
- package/src/dashboard/scanner/skills-scanner.js +43 -43
- package/src/dashboard/scanner/squads-scanner.js +13 -13
- package/src/dashboard/vite-plugin-morph-data.js +17 -17
- package/src/dashboard/vite.config.js +24 -24
- package/src/lib/detectors/design-system-detector.js +188 -188
- package/src/lib/detectors/index.js +217 -217
- package/src/lib/generators/architecture-scanner.js +45 -4
- package/src/lib/generators/design-system-generator.js +508 -508
- package/src/lib/generators/settings-generator.js +209 -209
- package/src/lib/git/branch-classifier.js +177 -0
- package/src/lib/git/gh.js +30 -0
- package/src/lib/git/pending-prs.js +89 -0
- package/src/lib/git/pr-index.js +70 -0
- package/src/lib/graph/god-nodes.js +75 -0
- package/src/lib/graph/graph-health.js +61 -0
- package/src/lib/graph/graph-loader.js +86 -0
- package/src/lib/graph/graphify-runtime.js +100 -0
- package/src/lib/graph/graphifyignore.js +86 -0
- package/src/lib/graph/sanitize.js +280 -0
- package/src/lib/hooks/hook-executor.js +167 -167
- package/src/lib/installers/mcp-installer.js +719 -719
- package/src/lib/tasks/task-classifier.js +30 -30
- package/src/lib/tasks/task-parser.js +209 -209
- package/src/lib/tasks/tasks-json-parser.js +26 -0
- package/src/lib/validators/checkpoint-persistence.js +39 -39
- package/src/lib/validators/css/css-validator.js +340 -340
- package/src/lib/validators/reuse/index.js +10 -0
- package/src/lib/validators/reuse/reuse-validator.js +324 -0
- package/src/lib/validators/reuse/symbol-scanner.js +144 -0
- package/src/lib/validators/shared/emit-validator-dispatch.js +14 -14
- package/src/lib/validators/shared/issue-counter.js +14 -14
- package/src/lib/validators/validation-runner.js +40 -1
- package/src/lib/worktree/fleet-scanner.js +51 -21
- package/src/lib/worktree/ignored-triage.js +197 -0
- package/src/lib/worktree/worktree-audit.js +200 -35
- package/src/scripts/setup-infra.js +60 -1
- package/src/utils/agent-id-mapper.js +11 -11
- package/src/utils/claude-settings-manager.js +300 -300
- package/src/utils/file-copier.js +40 -0
- package/src/utils/skills-installer.js +187 -187
|
@@ -4,7 +4,7 @@ description: Avalia a qualidade de uma tarefa ou fase com rubricas graduadas 0-1
|
|
|
4
4
|
version: core
|
|
5
5
|
when-to-invoke: ao completar uma tarefa ou ao finalizar uma fase de implementação
|
|
6
6
|
user-invokable: false
|
|
7
|
-
cliVersion: "8.
|
|
7
|
+
cliVersion: "8.32.0"
|
|
8
8
|
---
|
|
9
9
|
|
|
10
10
|
# morph-eval
|
|
@@ -4,7 +4,7 @@ description: ADW cirúrgico de hotfix — resolve uma queda/bug crítico de prod
|
|
|
4
4
|
version: core
|
|
5
5
|
when-to-invoke: ao receber "/morph-hotfix {feature}", quando a morph-proposal classifica o pedido como workType hotfix (Passo 0), ou o usuário diz "produção caiu / hotfix / urgente"
|
|
6
6
|
user-invokable: true
|
|
7
|
-
cliVersion: "8.
|
|
7
|
+
cliVersion: "8.32.0"
|
|
8
8
|
---
|
|
9
9
|
|
|
10
10
|
# morph-hotfix
|
|
@@ -109,7 +109,7 @@ morph-spec approve {feature} review --approver "<nome de quem aprovou>"
|
|
|
109
109
|
### 8. Pós-gate (o "test loop → ship" de `MORPH.md` §2.1)
|
|
110
110
|
|
|
111
111
|
- Rode a suíte completa uma vez: `morph-spec verify {feature}` (sem escopo de task — cobertura da feature inteira, agora que o Gate 3 está aprovado).
|
|
112
|
-
- Feche com `morph-spec finish {feature} --pr|--merge` — o mesmo encerramento único das demais features (arquiva commitando dentro de `morph/{f}`,
|
|
112
|
+
- Feche com `morph-spec finish {feature} --pr|--merge` — o mesmo encerramento único das demais features (arquiva commitando dentro de `morph/{f}`, desmonta). Num hotfix urgente o `--merge` (local, direto na default, integra na hora) costuma ser o caminho; use `--pr` quando o fix precisa passar por CI/review antes do merge — mas `--pr` **não integra**: sai com `finished: true` e `integrated: false`, `awaiting: "merge"`. Um hotfix "fechado" por `--pr` ainda não chegou na produção — não relate o incidente como resolvido até o merge. Ver `MORPH.md` §2.2.
|
|
113
113
|
- Não integre automaticamente — o usuário decide quando e por qual modo (`finish` toca a default ou o remoto). Feature legada sem `morph/{f}`: `finish` roteia para `morph-spec archive {feature}`.
|
|
114
114
|
|
|
115
115
|
## Anti-padrões
|
|
@@ -4,7 +4,7 @@ description: Guia a execução de cada tarefa de implementação com standard e
|
|
|
4
4
|
version: core
|
|
5
5
|
when-to-invoke: ao iniciar cada task de implementação listada em tasks.json
|
|
6
6
|
user-invokable: false
|
|
7
|
-
cliVersion: "8.
|
|
7
|
+
cliVersion: "8.32.0"
|
|
8
8
|
---
|
|
9
9
|
|
|
10
10
|
# morph-implement
|
|
@@ -60,9 +60,19 @@ Ao iniciar cada task de `tasks.json` — antes de escrever qualquer código. Nor
|
|
|
60
60
|
|
|
61
61
|
Spec: [description completa do tasks.json]
|
|
62
62
|
Standard: [path do .md do standard identificado no passo 2]
|
|
63
|
-
Arquivos relevantes: [
|
|
63
|
+
Arquivos relevantes: [os `target` de task.reuse + o que o scout/grafo apontou
|
|
64
|
+
— este campo não é decorativo: é por ele que o código já existente chega
|
|
65
|
+
ao sub-agente, que trabalha em contexto fresco e não conhece o projeto]
|
|
66
|
+
Reuso: [task.reuse do tasks.json — `CONCEITO: VEREDITO → alvo`, uma linha por
|
|
67
|
+
conceito; ausente numa task que não introduz conceito]
|
|
64
68
|
Done quando: [doneCriteria do tasks.json]
|
|
65
69
|
|
|
70
|
+
Antes de criar tipo, componente, hook ou serviço NOVO, verifique se já
|
|
71
|
+
existe: `morph-spec graph explain <nome>` e `graph affected <conceito>
|
|
72
|
+
--uses-only`. Sem grafo (`no-graph`), caia para `grep -rn "<nome>"` e siga —
|
|
73
|
+
a ausência de grafo nunca bloqueia. Se algo cobre o caso, reuse ou estenda;
|
|
74
|
+
se criar mesmo assim, diga por que em uma linha no relatório.
|
|
75
|
+
|
|
66
76
|
Contrato do relatório final:
|
|
67
77
|
1. GRAVE `.morph/features/{feature}/3-implement/reports/[ID].md` ANTES de
|
|
68
78
|
encerrar — é o retorno autoritativo, não a sua mensagem final (que é
|
|
@@ -4,7 +4,7 @@ description: Guia a produção dos artefatos de planejamento de uma feature (use
|
|
|
4
4
|
version: core
|
|
5
5
|
when-to-invoke: ao iniciar a fase plan de uma feature, após o Gate 1 (proposal aprovado)
|
|
6
6
|
user-invokable: true
|
|
7
|
-
cliVersion: "8.
|
|
7
|
+
cliVersion: "8.32.0"
|
|
8
8
|
---
|
|
9
9
|
|
|
10
10
|
# morph-plan
|
|
@@ -24,6 +24,41 @@ Ao iniciar a fase plan — após o `proposal.md` ser aprovado e antes de qualque
|
|
|
24
24
|
|
|
25
25
|
1. **Garanta que `proposal.md` e `stack-scan.md` estão no seu contexto antes de planejar.** Se não tem o conteúdo atual em contexto (sessão fresca, ou conteúdo possivelmente desatualizado), **use a tool Read para carregá-los** — não confie em cache de turnos anteriores. Na dúvida, Read. `.morph/features/{feature}/0-proposal/proposal.md` (problema, escopo, riscos — o comportamento é destilado aqui no passo 2, não vem pronto do proposal) e `.morph/features/{feature}/0-proposal/stack-scan.md` (contexto técnico detectado: greenfield vs brownfield, stack atual, drift). Em brownfield, o stack-scan informa decisões de spec/mandate — não invente padrões que conflitam com o detectado.
|
|
26
26
|
|
|
27
|
+
1b. **Scout — mapa do território antes de planejar (opcional, mas o default em feature grande ou brownfield).** É o call-site do "scout opcional antes do plan" que a MORPH.md promete. Dispare quando a feature toca área que você não tem em contexto, ou quando o `stack-scan.md` acusou brownfield com desvios.
|
|
28
|
+
|
|
29
|
+
```
|
|
30
|
+
subagent_type: "morph-scout"
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
Peça: os arquivos que a feature vai tocar, o blast radius dos símbolos centrais dessa área e o que quebra junto. O scout começa por `morph-spec graph affected|explain` e **cai para grep sozinho quando não há grafo** — nunca trate a ausência de grafo como impedimento para planejar.
|
|
34
|
+
|
|
35
|
+
**Fallback** (se `morph-scout` não estiver registrado nos subagent types deste projeto): `general-purpose` com o campo `persona` do agente `scout` colado no prompt (Read em `.morph/framework/agents.json`), registrando o fallback no `decisions.md`.
|
|
36
|
+
|
|
37
|
+
Pular o scout é aceitável em feature pequena dentro de domínio já carregado — nesse caso **registre a decisão em uma linha no `decisions.md`**.
|
|
38
|
+
|
|
39
|
+
1c. **Mapa de reuso — o que desta feature JÁ EXISTE no codebase.** Antes de destilar os use cases, responda por conceito: *isto já está implementado?* É o passo que falta hoje e cujo efeito é medido — 17 duplicações intra-feature no ProspectPRO, `SimpleBuilding` redefinido em cinco arquivos da MESMA pasta, um deles já sem um campo. Sem este passo, quem implementa não tem como saber, e o framework nunca pergunta.
|
|
40
|
+
|
|
41
|
+
**Insumos, nesta ordem:**
|
|
42
|
+
1. **O grafo** — para cada conceito central (entidade, DTO, componente, hook, serviço, helper) que a feature vai introduzir: `morph-spec graph explain <nome-candidato>` (existe? onde?) e `morph-spec graph affected <conceito> --uses-only` (quem usa de verdade, sem contar quem só implementa).
|
|
43
|
+
2. **O inventário do projeto** — `.morph/context/README.md` (gerado por `morph-spec context refresh`): entidades, endpoints com método e rota, páginas e **componentes com path**. É a granularidade de *conceito* que o grafo, que casa por símbolo, não dá. **Este passo é o consumidor declarado desse arquivo** — se ele não existir, rode `morph-spec context refresh` ou siga sem ele.
|
|
44
|
+
3. **Sem grafo** (`no-graph`, projeto sem Python) — **caia para `grep -rn "<nome-candidato>"` no diretório de código e siga**; a ausência de grafo nunca bloqueia o plano (ADR-002). Registre a queda no `decisions.md`.
|
|
45
|
+
|
|
46
|
+
**Produza `.morph/features/{feature}/2-plan/reuse-map.md`** — artefato, não prosa solta:
|
|
47
|
+
|
|
48
|
+
```markdown
|
|
49
|
+
| Conceito | Já existe? | Onde | Decisão |
|
|
50
|
+
|---|---|---|---|
|
|
51
|
+
| SimpleBuilding | SIM (5×) | features/roulette/types/roulette.types.ts | REUSAR |
|
|
52
|
+
| FilterValues | SIM (ambíguo) | dashboard + leads + store | ESTENDER o de dashboard |
|
|
53
|
+
| RouletteScore | NÃO | — | CRIAR — nenhum tipo cobre pontuação por sorteio |
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
**`CRIAR` exige justificativa em uma linha, na própria célula** — a mesma forma que `morph-uiux` §"Consistência" já impõe para token de design (*"um token novo precisa de justificativa: o existente não cobre o caso"*). Sem a justificativa, o veredito não vale.
|
|
57
|
+
|
|
58
|
+
**Distinga "extrair para shared" de "importar de outra feature".** Reuso não é desculpa para acoplar slices: se o conceito é de duas features, o alvo é um módulo comum, não um import cruzado — a rubrica `architecture` penaliza dependência cross-feature e continua valendo.
|
|
59
|
+
|
|
60
|
+
O veredito viaja para o implementador pelo campo `reuse` de cada task (passo 5). Quem cobra o resultado é o `morph-spec verify`: o validador `reuse` acusa símbolo novo cujo nome já existe no grafo, na mesma linguagem — **intra-feature reprova**, cross-feature avisa, e um `CRIAR` declarado no plano rebaixa o achado a aviso.
|
|
61
|
+
|
|
27
62
|
2. **Destile os use cases — ANTES de escrever `spec.md` e `tasks.json`.**
|
|
28
63
|
|
|
29
64
|
Um arquivo por use case em `.morph/features/{feature}/2-plan/usecases/UC-{nn}-{slug}.md`:
|
|
@@ -87,7 +122,7 @@ Ao iniciar a fase plan — após o `proposal.md` ser aprovado e antes de qualque
|
|
|
87
122
|
4. Produza `mandate.md` em `.morph/features/{feature}/2-plan/`:
|
|
88
123
|
- Stack exata do projeto (leia de `.morph/config/config.json` → `project.stack` ou do CLAUDE.md).
|
|
89
124
|
- Standards obrigatórios: descubra-os com `morph-spec standards --find "<domínio/tecnologia>"` (top 3 `id · path · digest` — o digest decide) por domínio tocado; liste os IDs escolhidos.
|
|
90
|
-
- Patterns proibidos — **copie por extenso no mandate.md** a lista canônica de `MORPH.md` § "Stack baked-in" (`Guid.NewGuid()`→`Guid.CreateVersion7()`, classes não-sealed sem justificativa, exceptions como fluxo de controle, camada de Service/Manager entre handler e dados, repository genérico `IRepository<T>`/`IUnitOfWork`, SQL cru sem justificativa) mais qualquer proibição específica da feature. Copie o texto completo — o mandate.md é lido por implementadores em contexto fresco, sem `MORPH.md` carregado, então um ponteiro sozinho não basta aqui.
|
|
125
|
+
- Patterns proibidos — **copie por extenso no mandate.md** a lista canônica de `MORPH.md` § "Stack baked-in" (`Guid.NewGuid()`→`Guid.CreateVersion7()`, classes não-sealed sem justificativa, exceptions como fluxo de controle, camada de Service/Manager entre handler e dados, repository genérico `IRepository<T>`/`IUnitOfWork`, SQL cru sem justificativa, redeclarar tipo/componente que já existe na mesma feature) mais qualquer proibição específica da feature. Copie o texto completo — o mandate.md é lido por implementadores em contexto fresco, sem `MORPH.md` carregado, então um ponteiro sozinho não basta aqui.
|
|
91
126
|
- Política de acesso a dados (registre no mandate): `ApplicationDbContext` injetado direto no slice — para CRUD e para queries específicas (joins, projeção, paginação). Para concorrência ou SQL específico, store dedicado hand-written com justificativa em `decisions.md`.
|
|
92
127
|
- **Features com agentes de IA (MAF):** se a feature envolve um agente (Microsoft Agent Framework), **use a tool Read** nos standards relevantes de `.morph/framework/standards/ai-agents/*.md` (ex.: `setup`, `agent-session`, `middleware-patterns`, `mcp-server`, `mcp-tools`, `multi-agent-patterns`, `production`) — filtre pelo que a feature exige via `morph-spec standards --find "<tema>" --category ai-agents`. Esses standards carregam as **convenções de projeto** (single-agent por padrão — Workflows só para fan-in/out real; provider, caching, observability, A2A) que uma fonte externa de docs (ex.: context7) não conhece. Padrão de divisão: **superfície da API** pode vir de docs ao vivo/context7 (mais frescas para um SDK em evolução), mas **as convenções vêm dos standards** — registre ambas no mandate. Não pule os `ai-agents/*.md` só porque o STANDARDS.json index lista que eles existem.
|
|
93
128
|
- **Verificação de runtime** — como se PROVA que a feature funciona rodando, não só compilando.
|
|
@@ -146,7 +181,8 @@ Ao iniciar a fase plan — após o `proposal.md` ser aprovado e antes de qualque
|
|
|
146
181
|
"group": "backend",
|
|
147
182
|
"usecase": "UC-07",
|
|
148
183
|
"agent": "dotnet-senior",
|
|
149
|
-
"standards": ["standards/backend/dotnet/vsa-handler-patterns.md"]
|
|
184
|
+
"standards": ["standards/backend/dotnet/vsa-handler-patterns.md"],
|
|
185
|
+
"reuse": [{ "concept": "SimpleBuilding", "verdict": "REUSAR", "target": "features/roulette/types/roulette.types.ts" }]
|
|
150
186
|
}
|
|
151
187
|
]
|
|
152
188
|
}
|
|
@@ -161,12 +197,15 @@ Ao iniciar a fase plan — após o `proposal.md` ser aprovado e antes de qualque
|
|
|
161
197
|
- `outputs` (opcional): array de paths que serão criados/modificados.
|
|
162
198
|
- `usecase` (opcional): o **ID** do use case que a task realiza — `UC-07`, nunca o nome do arquivo. Task puramente técnica (build, config, migração) não tem.
|
|
163
199
|
- `agent` (opcional): o `id` da persona de `agents.json` responsável pelo domínio da task — **sem** o prefixo `morph-` (ex.: `"dotnet-senior"`, não `"morph-dotnet-senior"`). Carimbado no passo 5a abaixo; ausente numa task sem domínio de agente claro (config/docs) ou num plano legado.
|
|
200
|
+
- `reuse` (opcional): os vereditos do `reuse-map.md` (passo 1c) que ESTA task realiza — `[{concept, verdict: REUSAR|ESTENDER|CRIAR, target}]`. Só nas tasks que tocam o conceito; task que não introduz conceito nenhum não tem. **A justificativa do CRIAR fica no `reuse-map.md`, não aqui** — o mesmo fato escrito em dois lugares diverge. É este campo que o `morph-spec verify` lê para saber se a criação de um símbolo homônimo foi decidida no Gate 2 ou aconteceu por descuido.
|
|
164
201
|
- `standards` (opcional): array de paths relativos de `.morph/framework/standards/` (ex.: `["standards/backend/dotnet/vsa-handler-patterns.md"]`) — os standards da persona resolvida em `agent`, mais qualquer standard específico que a task exija.
|
|
165
202
|
|
|
166
203
|
**5a. Carimbe `agent` e `standards` — uma leitura de `agents.json`, N tasks.** Antes de fechar o array `tasks`, **use a tool Read UMA VEZ** em `.morph/framework/agents.json` (não releia por task — é essa releitura repetida por task, hoje feita em `morph-implement`/`morph-apply` a cada dispatch, que este passo existe para eliminar). Para cada task do plano, resolva:
|
|
167
204
|
- `agent`: o `id` da persona (chave do objeto `agents`, sem prefixo `morph-`) cujo `domains` cobre o `group`/domínio da task — o mesmo casamento que `morph-implement` §3 e `morph-apply` §3b fariam manualmente a cada dispatch; aqui você faz uma vez, no plano.
|
|
168
205
|
- `standards`: o array `standards` dessa persona, mais qualquer standard adicional que a task especificamente exija (some, não substitua).
|
|
169
206
|
|
|
207
|
+
**5b. Carimbe `reuse` — do `reuse-map.md` para a task que realiza o conceito.** Para cada linha do mapa produzido no passo 1c, encontre a task que vai escrever aquele conceito e copie o veredito para `reuse` (`concept` + `verdict` + `target`). Um conceito sem task é sinal de plano incompleto — ou o mapa listou o que a feature não vai tocar, ou falta task. Resolva antes do Gate 2.
|
|
208
|
+
|
|
170
209
|
**Task que cria ou altera superfície de UI** (componente, página, estilo, fluxo de navegação) carimba `agent: "ui-designer"` — inclusive numa feature que não tomou o ramo uiux (MORPH.md §2.1): o critério de agente é por domínio da task, não por fase percorrida.
|
|
171
210
|
|
|
172
211
|
Uma task cujo domínio não bate com nenhuma persona registrada (ex.: task pura de config/docs) fica sem `agent` — `morph-implement` cai no fallback de Read de `agents.json` nesse caso.
|
|
@@ -226,6 +265,7 @@ Ao iniciar a fase plan — após o `proposal.md` ser aprovado e antes de qualque
|
|
|
226
265
|
- `.morph/features/{feature}/2-plan/usecases/UC-{nn}-{slug}.md` — um arquivo por use case, com pós-condições testáveis
|
|
227
266
|
- `.morph/features/{feature}/2-plan/spec.md` — especificação técnica completa
|
|
228
267
|
- `.morph/features/{feature}/2-plan/mandate.md` — contrato de implementação
|
|
268
|
+
- `.morph/features/{feature}/2-plan/reuse-map.md` — veredito REUSAR/ESTENDER/CRIAR por conceito, com justificativa de uma linha em cada CRIAR (passo 1c)
|
|
229
269
|
- `.morph/features/{feature}/2-plan/tasks.json` — task list estruturada válida contra schema, com o `doneCriteria` carregando a pós-condição literal e, por task, `agent`/`standards` carimbados a partir de `agents.json` (passo 5a)
|
|
230
270
|
- Gate 2 registrado aprovado em `.morph/features/{feature}/feature.json` (autoritativo)
|
|
231
271
|
|
|
@@ -237,6 +277,8 @@ Ao registrar a aprovação do Gate 2, atualize o frontmatter de cada use case de
|
|
|
237
277
|
- `morph-spec standards --find "<query>"` — para identificar standards relevantes ao mandate (o índice inteiro só como navegação exploratória; ver `morph-standards`)
|
|
238
278
|
- `.morph/framework/agents.json` — personas por domínio; lido UMA VEZ no passo 5a para carimbar `agent`/`standards` por task
|
|
239
279
|
- `.morph/framework/schemas/tasks.schema.json` — schema obrigatório de tasks.json
|
|
280
|
+
- `morph-spec graph explain|affected --uses-only` — "isto já existe?" e "quem usa de verdade" (passo 1c); **sem grafo, o fallback é `grep -rn`**
|
|
281
|
+
- `.morph/context/README.md` — inventário do projeto (entidades, endpoints, componentes com path); insumo declarado do passo 1c, atualizado por `morph-spec context refresh`
|
|
240
282
|
- `.morph/specs/usecases/` — acervo permanente de use cases do projeto (`usecase reserve` já lê antes de numerar; em `bug`, é onde mora o UC a corrigir)
|
|
241
283
|
- `.morph/features/{feature}/0-proposal/proposal.md` — escopo da feature
|
|
242
284
|
- `.morph/features/{feature}/0-proposal/stack-scan.md` — contexto técnico detectado
|
|
@@ -4,7 +4,7 @@ description: Orquestra a fase de proposta+plano de uma feature morph-spec. Encad
|
|
|
4
4
|
version: core
|
|
5
5
|
when-to-invoke: ao receber instrução "use /morph-proposal" do usuário, ou ao iniciar uma nova feature
|
|
6
6
|
user-invokable: true
|
|
7
|
-
cliVersion: "8.
|
|
7
|
+
cliVersion: "8.32.0"
|
|
8
8
|
---
|
|
9
9
|
|
|
10
10
|
# morph-proposal
|
|
@@ -4,7 +4,7 @@ description: Conduz a fase de revisão e produz o review-report.md para o Gate 3
|
|
|
4
4
|
version: core
|
|
5
5
|
when-to-invoke: ao iniciar a fase review, após todas as tasks de tasks.json estarem com status `done`
|
|
6
6
|
user-invokable: true
|
|
7
|
-
cliVersion: "8.
|
|
7
|
+
cliVersion: "8.32.0"
|
|
8
8
|
---
|
|
9
9
|
|
|
10
10
|
# morph-review
|
|
@@ -72,12 +72,19 @@ e o `recap.md` estar atualizado. Normalmente invocada pela skill orquestradora `
|
|
|
72
72
|
- **Confira também `traceability.warnings`** (advisory, nunca faz `verify` falhar): cada entrada é
|
|
73
73
|
um arquivo citado no `recap.md` sem task dona em `outputs` — repasse ao avaliador como sinal de
|
|
74
74
|
rastreabilidade para a dimensão de qualidade, não como bloqueio automático do gate.
|
|
75
|
+
- **Anexe o `reuse-map.md`** (`.morph/features/{feature}/2-plan/reuse-map.md`), quando existir: é o
|
|
76
|
+
que o plano decidiu por conceito (REUSAR/ESTENDER/CRIAR) e o único insumo que permite ao
|
|
77
|
+
avaliador julgar a dimensão `code-quality` **contra o codebase**, não só contra o diff — símbolo
|
|
78
|
+
recriado não aparece no diff como problema. Junto dele, repasse os achados do validador `reuse`
|
|
79
|
+
do nó `validators` do verify. Sem `reuse-map.md` (plano anterior a este passo), diga isso ao
|
|
80
|
+
avaliador em vez de deixá-lo supor que não havia conceito a reusar.
|
|
75
81
|
- Gere o **diff da implementação** (`git diff` do range da feature, ou a lista de arquivos do recap.md com seus conteúdos atuais).
|
|
82
|
+
- **Blast radius dos símbolos do diff (se houver grafo).** Para cada símbolo público que o diff tocou, rode `morph-spec graph affected <símbolo> --prod --json` e inclua o resultado no contexto do avaliador. É o que permite perguntar *"o que esta mudança pode ter quebrado FORA do diff"* sem que o avaliador leia o repositório inteiro. **Fallback:** `no-graph`, `not-found` ou `ambiguous` — siga sem o blast radius e **registre a ausência na seção de método do review-report**; o gate não depende disso. **Trate o resultado como possivelmente stale:** o rebuild do hook é assíncrono, então logo depois de um commit o grafo ainda é o anterior. E lembre que em frontend ele responde "quem importa", não "quem chama".
|
|
76
83
|
- Despache o subagent registrado `morph-evaluator` (gerado de `agents.json` — a persona do avaliador é o system prompt dele, com Read/Glob/Grep + Bash somente para verificação + Playwright para navegar, sem Write/Edit), com contexto fresco:
|
|
77
84
|
```
|
|
78
85
|
dispatch Agent (subagent_type: "morph-evaluator") with prompt:
|
|
79
|
-
contexto: mandate.md + spec.md + diff da implementação
|
|
80
|
-
+ nó e2e do verify (stack no ar? em qual URL?)
|
|
86
|
+
contexto: mandate.md + spec.md + reuse-map.md + diff da implementação
|
|
87
|
+
+ saída real dos testes + nó e2e do verify (stack no ar? em qual URL?)
|
|
81
88
|
instrução: |
|
|
82
89
|
Você NÃO participou da implementação — avalie o que está no diff,
|
|
83
90
|
contra o mandate. NÃO leia `4-review/evaluator-report.md` neste
|
|
@@ -195,7 +202,7 @@ e o `recap.md` estar atualizado. Normalmente invocada pela skill orquestradora `
|
|
|
195
202
|
|
|
196
203
|
No caminho manual, o hook state-sync detecta a aprovação em answers de AskUserQuestion e grava `approvalGates.review = { approved: true, timestamp, approvedBy: "manual" }` no `feature.json` autoritativo da feature — **a atribuição vem do nome da feature na pergunta** (o template acima o carrega). Pergunta sem nome, com mais de uma feature na árvore: o hook recusa a gravar e avisa; aprove então por `morph-spec approve {feature} review`. Em auto sem AskUserQuestion, aprove via `morph-spec approve {feature} review --mode auto` — nunca via Edit à mão de state.json.
|
|
197
204
|
|
|
198
|
-
**Próximo passo (não é desta skill):** com o Gate 3 aprovado, a feature fecha por `morph-spec finish {feature} --pr|--merge` — a `morph-apply` §6 conduz a escolha PR vs merge. Não arquive nem integre aqui.
|
|
205
|
+
**Próximo passo (não é desta skill):** com o Gate 3 aprovado, a feature fecha por `morph-spec finish {feature} --pr|--merge` — a `morph-apply` §6 conduz a escolha PR vs merge. Não arquive nem integre aqui. **`--pr` não encerra a feature** — arquiva e termina, mas só integra no merge (`integrated: false`, `awaiting: "merge"` no payload); até lá a feature fica `aguardando-merge` em `morph-spec list`/`fleet`. `finished: true` não é sinônimo de entregue.
|
|
199
206
|
|
|
200
207
|
## Output esperado
|
|
201
208
|
- `.morph/features/{feature}/4-review/evaluator-report.md` — relatório bruto e autoritativo do
|
|
@@ -207,6 +214,7 @@ e o `recap.md` estar atualizado. Normalmente invocada pela skill orquestradora `
|
|
|
207
214
|
|
|
208
215
|
## Referências
|
|
209
216
|
- `morph-spec verify {feature} [task] --json` — cadeia determinística build+testes+validadores+e2e anexada ao prompt do avaliador (passo 4)
|
|
217
|
+
- `.morph/features/{feature}/2-plan/reuse-map.md` — veredito de reuso por conceito; insumo do avaliador para julgar `code-quality` contra o codebase
|
|
210
218
|
- `.morph/framework/MORPH.md` — seção 5 (Gate 3 + regras de pausa), seção 1 (papel Evaluator), Trust Mode
|
|
211
219
|
- `.claude/agents/morph-evaluator.md` — subagent do avaliador (gerado de `.morph/framework/agents.json`), despachado no passo 4
|
|
212
220
|
- `.claude/skills/morph-eval/SKILL.md` — rubricas e bandas que o avaliador aplica
|
|
@@ -4,7 +4,7 @@ description: Varre o codebase existente, detecta stack atual, compara com o stac
|
|
|
4
4
|
version: core
|
|
5
5
|
when-to-invoke: ao iniciar uma feature em projeto brownfield (codebase já existente)
|
|
6
6
|
user-invokable: true
|
|
7
|
-
cliVersion: "8.
|
|
7
|
+
cliVersion: "8.32.0"
|
|
8
8
|
---
|
|
9
9
|
|
|
10
10
|
# morph-stack-scan
|
|
@@ -60,6 +60,18 @@ Antes de despachar Explore, verifique se o projeto é greenfield:
|
|
|
60
60
|
|
|
61
61
|
## O que fazer (brownfield)
|
|
62
62
|
|
|
63
|
+
0. **Se houver grafo, ele responde a pergunta do repository genérico melhor que o grep.**
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
morph-spec graph status --json # available:false? pule este passo
|
|
67
|
+
morph-spec graph affected IRepository --uses-only --json
|
|
68
|
+
morph-spec graph explain IRepository --json # o registro no DI aparece aqui
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
A pergunta do passo 1 é *"quantos CONSUMIDORES REAIS"*, e é exatamente aí que o grep erra: no ProspectPRO ele aponta **23 arquivos**, mas 20 são interfaces filhas **herdando** o contrato e só **2** são uso real. Quem lê 23 conclui "está em uso, não é código morto" — e a conclusão certa é a oposta. `--uses-only` exclui `inherits`/`implements`/`extends` e devolve os 2. O `explain` do mesmo símbolo entrega de brinde a outra metade da pergunta: onde ele está registrado no DI.
|
|
72
|
+
|
|
73
|
+
**Fallback:** grafo ausente (`no-graph`), símbolo não encontrado ou ambíguo — siga com o `Explore` e o grep do passo 1, e **registre no `stack-scan.md` que a contagem veio do grep**, que superestima consumidores por não separar herança de uso.
|
|
74
|
+
|
|
63
75
|
1. Despache um `Explore` subagent para varrer o codebase:
|
|
64
76
|
|
|
65
77
|
```
|
|
@@ -4,7 +4,7 @@ description: Localiza um standard específico via busca server-side (morph-spec
|
|
|
4
4
|
version: core
|
|
5
5
|
when-to-invoke: quando precisar consultar um standard específico durante a implementação
|
|
6
6
|
user-invokable: true
|
|
7
|
-
cliVersion: "8.
|
|
7
|
+
cliVersion: "8.32.0"
|
|
8
8
|
---
|
|
9
9
|
|
|
10
10
|
# morph-standards
|
|
@@ -4,7 +4,7 @@ description: Conduz a fase opcional de UI/UX — design system, mockups, compone
|
|
|
4
4
|
version: core
|
|
5
5
|
when-to-invoke: ao tomar o ramo uiux (morph-spec advance --design), depois do Gate 1 e antes do morph-plan
|
|
6
6
|
user-invokable: true
|
|
7
|
-
cliVersion: "8.
|
|
7
|
+
cliVersion: "8.32.0"
|
|
8
8
|
---
|
|
9
9
|
|
|
10
10
|
# morph-uiux
|
|
@@ -1,160 +1,160 @@
|
|
|
1
|
-
# Agent Session — Conversation state and chat reducers
|
|
2
|
-
|
|
3
|
-
> **Scope:** stacks=["dotnet"]
|
|
4
|
-
> **Layer:** 2 (on-keyword)
|
|
5
|
-
> **Keywords:** agent session, chat history, conversation state, chat reducer, history truncation, AgentThread
|
|
6
|
-
> **Read by Claude in:** implement
|
|
7
|
-
|
|
8
|
-
**Verified against:** MAF 1.0 GA. Last-verified: 2026-05-19.
|
|
9
|
-
|
|
10
|
-
---
|
|
11
|
-
|
|
12
|
-
## Quando usar AgentSession
|
|
13
|
-
|
|
14
|
-
Use `AgentSession` quando o agente é **conversacional** — há uma UI de chat que mantém histórico entre turnos do usuário.
|
|
15
|
-
|
|
16
|
-
**Use AgentSession quando:**
|
|
17
|
-
- Existe uma interface de chat (web, mobile, CLI interativo)
|
|
18
|
-
- O agente precisa lembrar o que foi dito nos turnos anteriores da mesma conversa
|
|
19
|
-
- Há perguntas de follow-up ("e agora?", "altere o segundo ponto", "lembra do que disse sobre X?")
|
|
20
|
-
|
|
21
|
-
**NÃO use AgentSession quando:**
|
|
22
|
-
- O agente é one-shot (gerador, executor, classificador) — recebe uma entrada, devolve uma saída, fim
|
|
23
|
-
- Agentes stateless em pipeline multi-agent — cada chamada é independente
|
|
24
|
-
- Jobs de batch/background — não há conversa; histórico não faz sentido
|
|
25
|
-
|
|
26
|
-
> "Add Memory/Session because agents need state" é um anti-pattern de `sweet-spot.md`.
|
|
27
|
-
> Só adicione quando uma UI de chat real existe.
|
|
28
|
-
|
|
29
|
-
---
|
|
30
|
-
|
|
31
|
-
## A API
|
|
32
|
-
|
|
33
|
-
**Pacote:** `Microsoft.Agents.AI` (base; já requerido por `ai-agents-setup`).
|
|
34
|
-
|
|
35
|
-
**Tipos confirmados via context7:**
|
|
36
|
-
- `AgentSession` — container leve de estado de conversa; obtido via `agent.CreateSessionAsync()`
|
|
37
|
-
- `AgentThread` — equivalente para agentes persistentes (PersistentAgentsClient); use `agent.GetNewThread()`
|
|
38
|
-
- `ChatClientAgentOptions` — aceita `ChatHistoryProvider` e `AIContextProviders`
|
|
39
|
-
- `InMemoryChatHistoryProvider` — implementação embutida de histórico em memória
|
|
40
|
-
- `AgentResponse` — tipo retornado por `AIAgent.RunAsync` (non-generic); acesse o texto via `.Text`
|
|
41
|
-
|
|
42
|
-
```csharp
|
|
43
|
-
// Construção do agente com histórico em memória
|
|
44
|
-
AIAgent agent = _modelRegistry.GetChatClient("text-default")
|
|
45
|
-
.AsAIAgent(new ChatClientAgentOptions
|
|
46
|
-
{
|
|
47
|
-
ChatOptions = new() { Instructions = "You are a helpful assistant." },
|
|
48
|
-
ChatHistoryProvider = new InMemoryChatHistoryProvider(),
|
|
49
|
-
});
|
|
50
|
-
```
|
|
51
|
-
|
|
52
|
-
---
|
|
53
|
-
|
|
54
|
-
## Exemplo C# — agente conversacional com session persistente entre turnos
|
|
55
|
-
|
|
56
|
-
```csharp
|
|
57
|
-
using Microsoft.Agents.AI;
|
|
58
|
-
using Microsoft.Extensions.AI;
|
|
59
|
-
|
|
60
|
-
// ── 1. Registrar o agente conversacional no DI ──
|
|
61
|
-
// Program.cs
|
|
62
|
-
builder.Services.AddSingleton<AIAgent>(sp =>
|
|
63
|
-
{
|
|
64
|
-
var registry = sp.GetRequiredService<ModelRegistry>();
|
|
65
|
-
return registry.GetChatClient("text-default")
|
|
66
|
-
.AsAIAgent(new ChatClientAgentOptions
|
|
67
|
-
{
|
|
68
|
-
ChatOptions = new() { Instructions = "You are a helpful customer support assistant." },
|
|
69
|
-
// InMemoryChatHistoryProvider: histórico vive em memória, por session.
|
|
70
|
-
// Seguro registrar como parte do singleton AIAgent: cada AgentSession criada via
|
|
71
|
-
// agent.CreateSessionAsync() isola o histórico de sua conversa — conversas distintas
|
|
72
|
-
// não compartilham estado. O provider em si é stateless; o estado fica na session.
|
|
73
|
-
// Para persistência entre restarts, substitua por um provider customizado (ex: DB).
|
|
74
|
-
ChatHistoryProvider = new InMemoryChatHistoryProvider(),
|
|
75
|
-
});
|
|
76
|
-
});
|
|
77
|
-
|
|
78
|
-
// ── 2. Criar uma session por conversa (ex: por conexão SignalR / por request de "start chat") ──
|
|
79
|
-
// ChatController.cs ou ChatHub.cs
|
|
80
|
-
public class ChatService(AIAgent _agent)
|
|
81
|
-
{
|
|
82
|
-
// Uma session por conversa ativa — armazene no escopo do usuário (cache, memória da conexão)
|
|
83
|
-
public async Task<AgentSession> StartConversationAsync()
|
|
84
|
-
=> await _agent.CreateSessionAsync();
|
|
85
|
-
|
|
86
|
-
// Cada mensagem do usuário passa a mesma session
|
|
87
|
-
public async Task<string> SendMessageAsync(AgentSession session, string userMessage)
|
|
88
|
-
{
|
|
89
|
-
AgentResponse response = await _agent.RunAsync(userMessage, session);
|
|
90
|
-
return response.Text;
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
// ── 3. Uso no controller/hub ──
|
|
95
|
-
// Primeiro turno
|
|
96
|
-
AgentSession session = await chatService.StartConversationAsync();
|
|
97
|
-
string reply1 = await chatService.SendMessageAsync(session, "Meu pedido #1234 está atrasado.");
|
|
98
|
-
// reply1: "Vou verificar o pedido #1234 para você..."
|
|
99
|
-
|
|
100
|
-
// Segundo turno — o agente lembra do contexto anterior
|
|
101
|
-
string reply2 = await chatService.SendMessageAsync(session, "E quando foi despachado?");
|
|
102
|
-
// reply2: "O pedido #1234 foi despachado em..." ← contexto mantido pela session
|
|
103
|
-
```
|
|
104
|
-
|
|
105
|
-
---
|
|
106
|
-
|
|
107
|
-
## History management
|
|
108
|
-
|
|
109
|
-
O histórico cresce a cada turno. Quando estoura o context window do modelo, as chamadas falham ou truncam silenciosamente.
|
|
110
|
-
|
|
111
|
-
### Estratégias
|
|
112
|
-
|
|
113
|
-
| Estratégia | Quando usar | Como implementar |
|
|
114
|
-
|------------|-------------|------------------|
|
|
115
|
-
| **Sliding window** | Conversas longas com contexto recente suficiente | Limitar `InMemoryChatHistoryProvider` a N mensagens mais recentes |
|
|
116
|
-
| **Token budget** | Modelos com context window pequeno | Contar tokens acumulados; truncar quando ultrapassa threshold |
|
|
117
|
-
| **Summarization** | Histórico precisa de contexto antigo comprimido | Agente periódico que resume N turnos anteriores em uma mensagem única |
|
|
118
|
-
|
|
119
|
-
> **Custom history management:** Para o caso comum, `InMemoryChatHistoryProvider` cobre a
|
|
120
|
-
> necessidade de histórico em memória por sessão. Se o projeto exigir truncation customizada
|
|
121
|
-
> (ex: sliding window limitando a N mensagens mais recentes ou por token budget), o mecanismo
|
|
122
|
-
> de extensão correto — seja subclasse de `ChatHistoryProvider`, um `IChatReducer`, ou outro
|
|
123
|
-
> ponto de override — deve ser verificado contra a versão instalada de `Microsoft.Agents.AI`.
|
|
124
|
-
> Consulte a documentação MAF atual antes de implementar um provider customizado, pois a API
|
|
125
|
-
> exata de extensão não foi confirmada na versão GA e pode variar entre releases.
|
|
126
|
-
|
|
127
|
-
---
|
|
128
|
-
|
|
129
|
-
## Anti-patterns
|
|
130
|
-
|
|
131
|
-
| Anti-pattern | Problema |
|
|
132
|
-
|-------------|----------|
|
|
133
|
-
| Adicionar session em agente one-shot (gerador, executor) | Complexidade desnecessária; estado nunca é usado |
|
|
134
|
-
| Histórico ilimitado sem truncation | Context window estoura; custo aumenta linearmente com a conversa |
|
|
135
|
-
| Compartilhar uma `AgentSession` entre múltiplos usuários | Histórico de um usuário vaza para outro |
|
|
136
|
-
| Usar `InMemoryChatHistoryProvider` sem criar uma `AgentSession` por conversa | Sem `AgentSession` por conversa, o histórico é compartilhado entre todos os usuários; a isolation depende de `AgentSession` — sempre crie uma session por conversa via `agent.CreateSessionAsync()` |
|
|
137
|
-
| Usar `AgentThread` (PersistentAgentsClient) sem necessidade | `AgentThread` requer hosted agent service; prefira `AgentSession` com direct model |
|
|
138
|
-
|
|
139
|
-
---
|
|
140
|
-
|
|
141
|
-
## Checklist (verifiable by morph-eval)
|
|
142
|
-
|
|
143
|
-
- [ ] `AgentSession` usada somente em agentes conversacionais com UI de chat real
|
|
144
|
-
- [ ] `InMemoryChatHistoryProvider` (ou equivalente) registrado em `ChatClientAgentOptions`
|
|
145
|
-
- [ ] Uma `AgentSession` por conversa/usuário — nunca compartilhada
|
|
146
|
-
- [ ] Estratégia de truncation definida (sliding window, token budget ou summarization)
|
|
147
|
-
- [ ] Agentes one-shot (gerador, executor, classificador) **não** recebem session
|
|
148
|
-
|
|
149
|
-
---
|
|
150
|
-
|
|
151
|
-
## References
|
|
152
|
-
|
|
153
|
-
- `ai-agents-setup` — packages e DI base
|
|
154
|
-
- `ai-agents-sweet-spot` — decisão de quando adicionar estado ao agente
|
|
155
|
-
- `ai-agents-middleware-patterns` (Onda 2) — pipeline de contexto e middleware
|
|
156
|
-
- MAF Conversations docs: https://learn.microsoft.com/en-us/agent-framework/agents/conversations/
|
|
157
|
-
|
|
158
|
-
---
|
|
159
|
-
|
|
160
|
-
*MORPH-SPEC by Polymorphism Tech — ai-agents/agent-session.md v1.0 (2026-05-19)*
|
|
1
|
+
# Agent Session — Conversation state and chat reducers
|
|
2
|
+
|
|
3
|
+
> **Scope:** stacks=["dotnet"]
|
|
4
|
+
> **Layer:** 2 (on-keyword)
|
|
5
|
+
> **Keywords:** agent session, chat history, conversation state, chat reducer, history truncation, AgentThread
|
|
6
|
+
> **Read by Claude in:** implement
|
|
7
|
+
|
|
8
|
+
**Verified against:** MAF 1.0 GA. Last-verified: 2026-05-19.
|
|
9
|
+
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
## Quando usar AgentSession
|
|
13
|
+
|
|
14
|
+
Use `AgentSession` quando o agente é **conversacional** — há uma UI de chat que mantém histórico entre turnos do usuário.
|
|
15
|
+
|
|
16
|
+
**Use AgentSession quando:**
|
|
17
|
+
- Existe uma interface de chat (web, mobile, CLI interativo)
|
|
18
|
+
- O agente precisa lembrar o que foi dito nos turnos anteriores da mesma conversa
|
|
19
|
+
- Há perguntas de follow-up ("e agora?", "altere o segundo ponto", "lembra do que disse sobre X?")
|
|
20
|
+
|
|
21
|
+
**NÃO use AgentSession quando:**
|
|
22
|
+
- O agente é one-shot (gerador, executor, classificador) — recebe uma entrada, devolve uma saída, fim
|
|
23
|
+
- Agentes stateless em pipeline multi-agent — cada chamada é independente
|
|
24
|
+
- Jobs de batch/background — não há conversa; histórico não faz sentido
|
|
25
|
+
|
|
26
|
+
> "Add Memory/Session because agents need state" é um anti-pattern de `sweet-spot.md`.
|
|
27
|
+
> Só adicione quando uma UI de chat real existe.
|
|
28
|
+
|
|
29
|
+
---
|
|
30
|
+
|
|
31
|
+
## A API
|
|
32
|
+
|
|
33
|
+
**Pacote:** `Microsoft.Agents.AI` (base; já requerido por `ai-agents-setup`).
|
|
34
|
+
|
|
35
|
+
**Tipos confirmados via context7:**
|
|
36
|
+
- `AgentSession` — container leve de estado de conversa; obtido via `agent.CreateSessionAsync()`
|
|
37
|
+
- `AgentThread` — equivalente para agentes persistentes (PersistentAgentsClient); use `agent.GetNewThread()`
|
|
38
|
+
- `ChatClientAgentOptions` — aceita `ChatHistoryProvider` e `AIContextProviders`
|
|
39
|
+
- `InMemoryChatHistoryProvider` — implementação embutida de histórico em memória
|
|
40
|
+
- `AgentResponse` — tipo retornado por `AIAgent.RunAsync` (non-generic); acesse o texto via `.Text`
|
|
41
|
+
|
|
42
|
+
```csharp
|
|
43
|
+
// Construção do agente com histórico em memória
|
|
44
|
+
AIAgent agent = _modelRegistry.GetChatClient("text-default")
|
|
45
|
+
.AsAIAgent(new ChatClientAgentOptions
|
|
46
|
+
{
|
|
47
|
+
ChatOptions = new() { Instructions = "You are a helpful assistant." },
|
|
48
|
+
ChatHistoryProvider = new InMemoryChatHistoryProvider(),
|
|
49
|
+
});
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
---
|
|
53
|
+
|
|
54
|
+
## Exemplo C# — agente conversacional com session persistente entre turnos
|
|
55
|
+
|
|
56
|
+
```csharp
|
|
57
|
+
using Microsoft.Agents.AI;
|
|
58
|
+
using Microsoft.Extensions.AI;
|
|
59
|
+
|
|
60
|
+
// ── 1. Registrar o agente conversacional no DI ──
|
|
61
|
+
// Program.cs
|
|
62
|
+
builder.Services.AddSingleton<AIAgent>(sp =>
|
|
63
|
+
{
|
|
64
|
+
var registry = sp.GetRequiredService<ModelRegistry>();
|
|
65
|
+
return registry.GetChatClient("text-default")
|
|
66
|
+
.AsAIAgent(new ChatClientAgentOptions
|
|
67
|
+
{
|
|
68
|
+
ChatOptions = new() { Instructions = "You are a helpful customer support assistant." },
|
|
69
|
+
// InMemoryChatHistoryProvider: histórico vive em memória, por session.
|
|
70
|
+
// Seguro registrar como parte do singleton AIAgent: cada AgentSession criada via
|
|
71
|
+
// agent.CreateSessionAsync() isola o histórico de sua conversa — conversas distintas
|
|
72
|
+
// não compartilham estado. O provider em si é stateless; o estado fica na session.
|
|
73
|
+
// Para persistência entre restarts, substitua por um provider customizado (ex: DB).
|
|
74
|
+
ChatHistoryProvider = new InMemoryChatHistoryProvider(),
|
|
75
|
+
});
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
// ── 2. Criar uma session por conversa (ex: por conexão SignalR / por request de "start chat") ──
|
|
79
|
+
// ChatController.cs ou ChatHub.cs
|
|
80
|
+
public class ChatService(AIAgent _agent)
|
|
81
|
+
{
|
|
82
|
+
// Uma session por conversa ativa — armazene no escopo do usuário (cache, memória da conexão)
|
|
83
|
+
public async Task<AgentSession> StartConversationAsync()
|
|
84
|
+
=> await _agent.CreateSessionAsync();
|
|
85
|
+
|
|
86
|
+
// Cada mensagem do usuário passa a mesma session
|
|
87
|
+
public async Task<string> SendMessageAsync(AgentSession session, string userMessage)
|
|
88
|
+
{
|
|
89
|
+
AgentResponse response = await _agent.RunAsync(userMessage, session);
|
|
90
|
+
return response.Text;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// ── 3. Uso no controller/hub ──
|
|
95
|
+
// Primeiro turno
|
|
96
|
+
AgentSession session = await chatService.StartConversationAsync();
|
|
97
|
+
string reply1 = await chatService.SendMessageAsync(session, "Meu pedido #1234 está atrasado.");
|
|
98
|
+
// reply1: "Vou verificar o pedido #1234 para você..."
|
|
99
|
+
|
|
100
|
+
// Segundo turno — o agente lembra do contexto anterior
|
|
101
|
+
string reply2 = await chatService.SendMessageAsync(session, "E quando foi despachado?");
|
|
102
|
+
// reply2: "O pedido #1234 foi despachado em..." ← contexto mantido pela session
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
---
|
|
106
|
+
|
|
107
|
+
## History management
|
|
108
|
+
|
|
109
|
+
O histórico cresce a cada turno. Quando estoura o context window do modelo, as chamadas falham ou truncam silenciosamente.
|
|
110
|
+
|
|
111
|
+
### Estratégias
|
|
112
|
+
|
|
113
|
+
| Estratégia | Quando usar | Como implementar |
|
|
114
|
+
|------------|-------------|------------------|
|
|
115
|
+
| **Sliding window** | Conversas longas com contexto recente suficiente | Limitar `InMemoryChatHistoryProvider` a N mensagens mais recentes |
|
|
116
|
+
| **Token budget** | Modelos com context window pequeno | Contar tokens acumulados; truncar quando ultrapassa threshold |
|
|
117
|
+
| **Summarization** | Histórico precisa de contexto antigo comprimido | Agente periódico que resume N turnos anteriores em uma mensagem única |
|
|
118
|
+
|
|
119
|
+
> **Custom history management:** Para o caso comum, `InMemoryChatHistoryProvider` cobre a
|
|
120
|
+
> necessidade de histórico em memória por sessão. Se o projeto exigir truncation customizada
|
|
121
|
+
> (ex: sliding window limitando a N mensagens mais recentes ou por token budget), o mecanismo
|
|
122
|
+
> de extensão correto — seja subclasse de `ChatHistoryProvider`, um `IChatReducer`, ou outro
|
|
123
|
+
> ponto de override — deve ser verificado contra a versão instalada de `Microsoft.Agents.AI`.
|
|
124
|
+
> Consulte a documentação MAF atual antes de implementar um provider customizado, pois a API
|
|
125
|
+
> exata de extensão não foi confirmada na versão GA e pode variar entre releases.
|
|
126
|
+
|
|
127
|
+
---
|
|
128
|
+
|
|
129
|
+
## Anti-patterns
|
|
130
|
+
|
|
131
|
+
| Anti-pattern | Problema |
|
|
132
|
+
|-------------|----------|
|
|
133
|
+
| Adicionar session em agente one-shot (gerador, executor) | Complexidade desnecessária; estado nunca é usado |
|
|
134
|
+
| Histórico ilimitado sem truncation | Context window estoura; custo aumenta linearmente com a conversa |
|
|
135
|
+
| Compartilhar uma `AgentSession` entre múltiplos usuários | Histórico de um usuário vaza para outro |
|
|
136
|
+
| Usar `InMemoryChatHistoryProvider` sem criar uma `AgentSession` por conversa | Sem `AgentSession` por conversa, o histórico é compartilhado entre todos os usuários; a isolation depende de `AgentSession` — sempre crie uma session por conversa via `agent.CreateSessionAsync()` |
|
|
137
|
+
| Usar `AgentThread` (PersistentAgentsClient) sem necessidade | `AgentThread` requer hosted agent service; prefira `AgentSession` com direct model |
|
|
138
|
+
|
|
139
|
+
---
|
|
140
|
+
|
|
141
|
+
## Checklist (verifiable by morph-eval)
|
|
142
|
+
|
|
143
|
+
- [ ] `AgentSession` usada somente em agentes conversacionais com UI de chat real
|
|
144
|
+
- [ ] `InMemoryChatHistoryProvider` (ou equivalente) registrado em `ChatClientAgentOptions`
|
|
145
|
+
- [ ] Uma `AgentSession` por conversa/usuário — nunca compartilhada
|
|
146
|
+
- [ ] Estratégia de truncation definida (sliding window, token budget ou summarization)
|
|
147
|
+
- [ ] Agentes one-shot (gerador, executor, classificador) **não** recebem session
|
|
148
|
+
|
|
149
|
+
---
|
|
150
|
+
|
|
151
|
+
## References
|
|
152
|
+
|
|
153
|
+
- `ai-agents-setup` — packages e DI base
|
|
154
|
+
- `ai-agents-sweet-spot` — decisão de quando adicionar estado ao agente
|
|
155
|
+
- `ai-agents-middleware-patterns` (Onda 2) — pipeline de contexto e middleware
|
|
156
|
+
- MAF Conversations docs: https://learn.microsoft.com/en-us/agent-framework/agents/conversations/
|
|
157
|
+
|
|
158
|
+
---
|
|
159
|
+
|
|
160
|
+
*MORPH-SPEC by Polymorphism Tech — ai-agents/agent-session.md v1.0 (2026-05-19)*
|