@polymorphism-tech/morph-spec 8.46.0 → 8.47.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CLAUDE.md +5 -5
- package/README.md +2 -2
- package/bin/morph-spec.js +1 -4
- package/bin/validate.js +4 -25
- package/claude-plugin.json +1 -1
- package/docs/CHEATSHEET.md +2 -2
- package/docs/QUICKSTART.md +1 -1
- package/framework/CLAUDE.md +1 -1
- package/framework/MORPH.md +8 -8
- package/framework/commands/create-umbraco-lp.md +2 -0
- package/framework/commands/migrate-lp-to-umbraco.md +2 -0
- package/framework/commands/morph-archive.md +1 -1
- package/framework/commands/morph-hotfix.md +1 -1
- package/framework/commands/morph-infra.md +17 -203
- package/framework/commands/morph-media.md +3 -0
- package/framework/commands/morph-preflight.md +5 -28
- package/framework/commands/morph-proposal.md +2 -2
- package/framework/commands/morph-troubleshoot.md +2 -14
- package/framework/docs/CLI.md +3 -3
- package/framework/hooks/claude-code/core/state-sync.js +13 -1
- package/framework/hooks/shared/e2e-check.js +3 -4
- package/framework/hooks/shared/state-writer.js +1 -0
- package/framework/rules/ai-agents-standards.md +18 -16
- package/framework/rules/asaas-standards.md +3 -1
- package/framework/rules/csharp-standards.md +10 -8
- package/framework/rules/data-standards.md +5 -5
- package/framework/rules/dotnet-templates.md +1 -1
- package/framework/rules/frontend-standards.md +4 -2
- package/framework/rules/gsap-standards.md +10 -5
- package/framework/rules/hangfire-standards.md +3 -1
- package/framework/rules/infrastructure-standards.md +4 -2
- package/framework/rules/morph-workflow.md +2 -2
- package/framework/rules/neon-auth-standards.md +3 -1
- package/framework/rules/neon-standards.md +6 -4
- package/framework/rules/nextjs-standards.md +13 -11
- package/framework/rules/nextjs-templates.md +1 -1
- package/framework/rules/resend-standards.md +3 -1
- package/framework/schemas/feature.schema.json +1 -3
- package/framework/skills/README.md +1 -1
- package/framework/skills/core/create-umbraco-lp/SKILL.md +3 -3
- package/framework/skills/core/migrate-lp-to-umbraco/SKILL.md +3 -3
- package/framework/skills/core/morph-apply/SKILL.md +3 -3
- package/framework/skills/core/morph-brainstorming/SKILL.md +3 -3
- package/framework/skills/core/morph-hotfix/SKILL.md +4 -4
- package/framework/skills/core/morph-implement/SKILL.md +3 -3
- package/framework/skills/core/morph-machine/SKILL.md +3 -3
- package/framework/skills/core/morph-media/SKILL.md +3 -3
- package/framework/skills/core/morph-plan/SKILL.md +3 -3
- package/framework/skills/core/morph-proposal/SKILL.md +3 -3
- package/framework/skills/core/morph-review/SKILL.md +3 -3
- package/framework/skills/core/morph-stack-scan/SKILL.md +3 -3
- package/framework/skills/core/morph-standards/SKILL.md +6 -6
- package/framework/skills/core/morph-uiux/SKILL.md +3 -3
- package/framework/standards/STANDARDS.json +59 -56
- package/framework/standards/ai-agents/evals-with-cache.md +1 -1
- package/framework/standards/ai-agents/llm-runtime-defaults.md +1 -1
- package/framework/standards/ai-agents/modalities-speech-to-text.md +36 -26
- package/framework/standards/ai-agents/prompt-sources.md +6 -4
- package/framework/standards/ai-agents/providers/model-registry.md +1 -1
- package/framework/standards/ai-agents/rag-custom-pgvector.md +67 -39
- package/framework/standards/ai-agents/setup.md +1 -1
- package/framework/standards/ai-agents/structured-output.md +1 -1
- package/framework/standards/ai-agents/sweet-spot.md +1 -1
- package/framework/standards/architecture/code-comments.md +2 -2
- package/framework/standards/architecture/vertical-slice/vertical-slice.md +18 -9
- package/framework/standards/backend/api/validation.md +135 -47
- package/framework/standards/backend/database/postgresql/database.md +11 -7
- package/framework/standards/backend/database/vector-search-rag.md +207 -324
- package/framework/standards/backend/dotnet/fluent-validation-vsa.md +14 -8
- package/framework/standards/backend/dotnet/result-pattern.md +40 -6
- package/framework/standards/backend/dotnet/scalar-ui.md +13 -7
- package/framework/standards/backend/dotnet/scrutor.md +1 -1
- package/framework/standards/backend/dotnet/testing.md +1 -1
- package/framework/standards/backend/dotnet/vsa-handler-patterns.md +7 -7
- package/framework/standards/backend/integrations/asaas/asaas-api.md +309 -165
- package/framework/standards/backend/integrations/hangfire/hangfire-jobs.md +134 -154
- package/framework/standards/data/nosql/cache/redis.md +120 -44
- package/framework/standards/data/nosql/cosmos-db.md +114 -71
- package/framework/standards/frontend/design-system/animations.md +1 -1
- package/framework/standards/frontend/design-system/naming.md +1 -1
- package/framework/standards/frontend/nextjs/motion-patterns.md +45 -29
- package/framework/standards/frontend/nextjs/naming-conventions.md +1 -1
- package/framework/standards/infrastructure/docker/coolify-deploy.md +8 -6
- package/framework/standards/infrastructure/neon/neon-pgvector.md +120 -75
- package/framework/standards/infrastructure/neon/neon-setup.md +53 -31
- package/framework/templates/code/dotnet/contracts/contracts-vsa.cs +2 -2
- package/framework/templates/code/dotnet/extensions/database-extensions.cs +1 -1
- package/framework/templates/code/dotnet/extensions/result-extensions.cs +27 -0
- package/framework/templates/code/dotnet/feature/endpoint.cs.hbs +2 -2
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/MediaDisclosureTests.cs +1 -1
- package/framework/templates/infrastructure/docker/coolify.template.json +3 -3
- package/framework/templates/infrastructure/github/README.md +2 -1
- package/framework/templates/infrastructure/github/workflows/deploy-coolify.yml.hbs +2 -1
- package/package.json +1 -1
- package/src/commands/observability/telemetry.js +3 -5
- package/src/commands/project/init.js +2 -1
- package/src/commands/project/update.js +16 -18
- package/src/commands/state/approve.js +17 -0
- package/src/commands/state/gate-check.js +4 -14
- package/src/commands/validation/verify.js +3 -12
- package/src/core/paths/output-schema.js +1 -1
- package/src/dashboard/app/App.jsx +0 -2
- package/src/dashboard/app/components/CommandPalette.jsx +2 -3
- package/src/dashboard/app/components/EntityLink.jsx +0 -1
- package/src/dashboard/app/components/Markdown.jsx +1 -1
- package/src/dashboard/app/components/Sidebar.jsx +1 -2
- package/src/dashboard/app/pages/RulesMap.jsx +3 -3
- package/src/dashboard/app/pages/SkillsLibrary.jsx +3 -3
- package/src/dashboard/scanner/activity-scanner.js +1 -1
- package/src/dashboard/scanner/docs-scanner.js +5 -23
- package/src/dashboard/scanner/features-scanner.js +1 -1
- package/src/dashboard/scanner/index.js +0 -2
- package/src/dashboard/scanner/rules-scanner.js +15 -10
- package/src/dashboard/scanner/skills-scanner.js +2 -3
- package/src/lib/evaluator-report.js +13 -0
- package/src/lib/risk-detector.js +8 -0
- package/src/lib/stack/stack-profile.js +1 -1
- package/src/lib/validators/reuse/reuse-validator.js +2 -3
- package/src/lib/validators/reuse/symbol-scanner.js +2 -4
- package/src/lib/validators/validation-runner.js +8 -88
- package/src/lib/validators/verify-runner.js +22 -72
- package/src/scripts/setup-infra.js +33 -22
- package/src/utils/agents-installer.js +20 -8
- package/src/utils/file-copier.js +7 -5
- package/framework/commands/commit.md +0 -166
- package/framework/rubrics/backend.json +0 -41
- package/framework/rubrics/default.json +0 -36
- package/framework/rubrics/frontend.json +0 -36
- package/framework/templates/project/validate.js +0 -97
- package/src/dashboard/app/pages/Rubrics.jsx +0 -97
- package/src/dashboard/scanner/rubrics-scanner.js +0 -12
- package/src/lib/validators/architecture/architecture-validator.js +0 -270
- package/src/lib/validators/architecture/index.js +0 -7
- package/src/lib/validators/css/css-validator.js +0 -340
- package/src/lib/validators/css/index.js +0 -7
- package/src/lib/validators/design-system/design-system-validator.js +0 -237
- package/src/lib/validators/design-system/index.js +0 -7
- package/src/lib/validators/score-aggregator.js +0 -244
- package/src/lib/validators/ui/index.js +0 -7
- package/src/lib/validators/ui/ui-contrast-validator.js +0 -422
package/CLAUDE.md
CHANGED
|
@@ -48,7 +48,7 @@ No build step — pure ESM JavaScript, runs directly via Node.js 18+.
|
|
|
48
48
|
- `templates/` — Handlebars template registry and renderer
|
|
49
49
|
- `workflows/` — Workflow type detector
|
|
50
50
|
- **`src/lib/`** — Libraries and subsystems:
|
|
51
|
-
- `validators/` —
|
|
51
|
+
- `validators/` — Validators run by the `verify` validators node (code-budget, contracts, nextjs, packages, reuse)
|
|
52
52
|
- `generators/` — Code generation (recap, context, handoff)
|
|
53
53
|
- `stack-filter.js` — Stack-aware asset filtering via YAML frontmatter `stacks:` tags
|
|
54
54
|
- `installers/` — Agents, skills, hooks installers
|
|
@@ -117,7 +117,7 @@ When modifying framework components, verify the full scope of related files.
|
|
|
117
117
|
| When you modify... | Also verify... |
|
|
118
118
|
|----------------------------------|-------------------------------------------------------------------------------------------|
|
|
119
119
|
| `framework/agents.json` | agents-installer.js (test/utils/agents-installer.test.js), doctor.js (REQUIRED_AGENTS). **`maxTurns` (ADR-004):** UMA régua de validade, `maxTurnsError` em `agents-installer.js`, com QUATRO leitores — `installAgents` (recusa antes de escrever), `checkAgentsDrift` (`invalid`), `validateAgentsJson` do `doctor` (error) e o Check 6 do `validate-framework.js` (`test/framework/validate-framework-agents.test.js`); uma segunda noção de "valor válido" faz o mesmo arquivo passar num e ser recusado no outro. Sem o campo, o frontmatter sai byte a byte igual ao de antes (canário COMPAT). O corpo gerado anuncia o teto ao sub-agent, e a cláusula 5 dos templates de dispatch depende disso. O drift lê o registry MCP do PACOTE (`mcpRegistryDir`): a cópia instalada não tem `mcp/`, e ler dela dava toda persona com `mcp` como stale numa instalação limpa. O marcador `PARCIAL` (1ª linha) que a cláusula 5 e o corpo da persona mandam gravar é lido por `report-check.js` (`PARTIAL_MARKER` → `report-incomplete`, ação `resume`): mudar a grafia num lado só devolve o parcial lido como `ok`. **`effort`:** mesma régua única em quatro leitores (`effortError`); ausente, o frontmatter não ganha linha e o sub-agent herda o effort da sessão (COMPAT intacto). Implementadores e `scout` `medium`, `evaluator` sem o campo — o Gate 3 não é barateado. **`surfaces` (ADR-005):** mesma régua única em quatro leitores (`surfacesError`), e `personaStandardIds` é a leitura única de "todo standard da persona" para o Check 1, o drift e os scanners do dashboard (`agents-scanner.js`, `standards-scanner.js`) — iterar só `standards` deixa id de superfície sem validar e a aresta do grafo sumida. Sem o campo, o `.md` sai byte a byte o de antes: `test/fixtures/agents/ui-designer-5.3.0.{json,md}` é a persona do 5.3.0 e o render do installer de main, comparados com `strictEqual` — nunca regere a fixture para fazer o teste passar. A chave da superfície é a que o checklist de tela do `morph-uiux` escreve em `Superfície` e que o `morph-plan` §5a usa para somar a lista em `tasks[].standards` |
|
|
120
|
-
| `framework/standards/**` | STANDARDS.json entry exists (o **digest** e o que `standards --find` devolve — corrigir a prosa e esquecer o digest e o caso mais comum), agents reference standard, .md file exists. **Bloco de codigo `csharp` num standard e afirmacao verificavel:** extraia com `node scripts/extract-doc-csharp.mjs <std.md> "## Secao" out.cs` e compile numa sonda com o pin + `TreatWarningsAsErrors` (sem ele `MEAI001` vira aviso e o build fica verde sobre codigo que o consumidor nao compila). Medido: um exemplo carimbado sem compilar tinha 3 erros independentes |
|
|
120
|
+
| `framework/standards/**` | STANDARDS.json entry exists (o **digest** e o que `standards --find` devolve — corrigir a prosa e esquecer o digest e o caso mais comum), e o bullet do standard na rule `framework/rules/*-standards.md` que o roteia (resumo inline; `@`-import em rule resolve relativo ao arquivo da rule e, quando resolve, carrega em TODA sessão ignorando `paths:` — medido; canário `test/framework/rules-no-imports.test.js`), agents reference standard, .md file exists. **Bloco de codigo `csharp` num standard e afirmacao verificavel:** extraia com `node scripts/extract-doc-csharp.mjs <std.md> "## Secao" out.cs` e compile numa sonda com o pin + `TreatWarningsAsErrors` (sem ele `MEAI001` vira aviso e o build fica verde sobre codigo que o consumidor nao compila). Medido: um exemplo carimbado sem compilar tinha 3 erros independentes |
|
|
121
121
|
| `isImplementer` (Check 6 de `scripts/validate-framework.js`) | toda persona com Write/Edit no escopo efetivo de tools tem de alcançar, pelos `standards`, as âncoras `comment-policy` e `test-budget` do STANDARDS.json (`IMPLEMENTER_ANCHORS`). Renomear a chave da âncora no registry derruba o `validate`, e persona implementadora nova sem os dois standards também. Installer e `doctor` não cobram isso de propósito: um `agents.json` instalado antigo continua valendo |
|
|
122
122
|
| `scripts/generate-standards-registry.js` | `framework/standards/STANDARDS.json` (REGENERE, nunca patch textual — tres features escrevem no mesmo arquivo), `src/lib/standards/verified-against.js` (`parseVerifiedAgainst`/`deriveProvedBy`: `verifiedAgainst` e `provedBy` sao DERIVADOS a cada run e NAO entram em `PRESERVE_FIELDS`), `test/framework/standards-registry.test.js`. **Renomear um standard e uma PARADA, nao um exit 0**: path sumido + path novo faz o gerador recusar nomeando os dois conjuntos e prescrevendo a edicao do campo `path` da entrada antiga — ele nao adivinha o par. `MORPH_STANDARDS_BASE` e o unico seam de teste do script e nada em producao o define |
|
|
123
123
|
| `framework/hooks/claude-code/**` | hooks-installer.js MORPH_HOOKS array, hook follows fail-open pattern. O `timeout` de cada entrada é em SEGUNDOS, a unidade que o Claude Code lê — em milissegundos ele passa do default de 600 e prende a sessão por horas (canário em `test/hooks/hooks-installer.test.js`); binding `async: true` não tem timeout aplicado |
|
|
@@ -162,8 +162,8 @@ When modifying framework components, verify the full scope of related files.
|
|
|
162
162
|
| `framework/hooks/shared/graph-freshness.js` (a regra única de "grafo em dia") | os três lados que perguntam: `graph-refresh.js` (decide se roda e se o disparo solta processo), `graph-loader.js` `getFreshness` (`graph status\|explain\|affected` e o `doctor`) e `graph build\|refresh` (gravam a rodada em `<git-common-dir>/morph-spec/graph-refresh-state.json`). O registro da rodada existe porque o `graphify update` 0.9.50 não recarimba `built_at_commit` quando a topologia não muda: sem ele, o grafo parecia velho para sempre e cada disparo rodava o graphify de novo (~80 s no MORPH_GHLBrain). As regras do `.graphifyignore` entram por CONTEÚDO (hash sem comentário, BOM e CRLF), nunca pelo relógio, e `ignoreTextOf` é a ÚNICA leitura de "as regras desta árvore": numa worktree sem arquivo próprio, as da raiz — as mesmas que o `graph refresh` põe ali durante a rodada (`prepareIgnore` em `build.js`). O arquivo temporário leva `TEMP_IGNORE_MARKER` na 1ª linha; nas duas triagens de sujeira (`finish` e `worktree remove`) `withoutLeftoverIgnore` o tira da conta ANTES das recusas e `dropLeftoverIgnore` o apaga só DEPOIS delas — apagar antes é mutação atrás de uma precondição. Mudar a grafia do marcador num lado só devolve o `worktree-dirty` de uma rodada morta no meio. Mora em `hooks/shared` porque hook não importa `src/` |
|
|
163
163
|
| `framework/hooks/shared/graph-refresh.js` (o refresh em background) | os dois disparos (`graph-refresh-kick`, depois de Bash/PowerShell com `git`/`gh`/`morph-spec`, e o SessionStart de `worktree-context.js`), `graph build\|refresh` (o MESMO lock por repositório) e `worktree.js` (`seedGraphFromRoot`: a worktree nasce com o grafo da raiz e o registro dela — em dia no HEAD só se a raiz estava). **Só a raiz**: o worker destacado nasce com `cwd` no git common dir e nunca recebe o caminho de uma worktree — no win32 um diretório em uso não se apaga, e o `finish` de uma worktree com refresh correndo falhava no meio. A worktree anda por `morph-spec graph refresh`, em primeiro plano. `runGraphify` roda o graphify com `cwd` no git common dir, nunca na árvore (ele lê o alvo, o ignore e o git pelo argumento — medido no 0.9.50). O disparo lê o HEAD pelo disco (`readPrimaryHead`, `git-paths.js`) e não solta processo quando o grafo não pede refresh: ele roda a cada comando git do agente. O step 18b de `setup-infra.js` não instala o hook git do graphify: ele não roda em worktree, não tem `post-merge` e carimba como em dia um grafo sem os arquivos do merge |
|
|
164
164
|
| `framework/hooks/shared/git-exclude.js` | ÚNICO escritor dos blocos do morph-spec no `info/exclude` do COMMON dir — o `.git/worktrees/<id>/info/exclude` o git não lê para worktree vinculada. Leitores que passam o seu marcador: `worktree.js`, `handoff-generator.js` e `graph-refresh.js`. Resolve o common dir pelo disco, sem subprocesso: um `git` gerado por um processo destacado abria janela de console no win32 |
|
|
165
|
-
| `src/lib/validators/reuse/**` | `validation-runner.js` (case + push universal), `
|
|
166
|
-
| `src/lib/validators/code-budget/**` | `validation-runner.js` (case + push universal fora do `light`), `framework/
|
|
165
|
+
| `src/lib/validators/reuse/**` | `validation-runner.js` (case + push universal), `test/fixtures/graph/prospectpro-reuse.graph.json` (recorte do grafo real — regerar com `build-reuse-fixture.mjs`, nunca editar à mão) |
|
|
166
|
+
| `src/lib/validators/code-budget/**` | `validation-runner.js` (case + push universal fora do `light`), `framework/docs/CLI.md` (o que conta como teste e os limites 1,5×/40 linhas) e `test/integration/code-budget-verify.test.js`. Lê `addedLinesAgainst` (`default-branch.js`), `isTestFile` (`symbol-scanner.js`, a mesma régua de teste do `reuse`), `isProjectSource`/`isGenerated` (`hooks/shared/source-files.js`) e `commentLines`/`citesProcess` (`hooks/shared/comment-scanner.js`, sobre `stripComments` com `js`). Sempre `warning`. Não conseguir medir é skip `unavailable` com razão, e o `validation-runner` não põe validador pulado em `results`: pulado não conta como verde |
|
|
167
167
|
| `framework/hooks/shared/comment-rules.js` / `comment-scanner.js` / `source-files.js` (o hook `comment-guard`) | Os dois leitores do MESMO comentário: o hook `comment-guard` (PostToolUse `Write\|Edit`, bloqueia na hora da edição) e o `code-budget` (avisa no `verify`). Moram em `hooks/shared` porque hook não importa `src/`; o `src/` importa daqui, nunca uma cópia. O hook julga só o comentário ADICIONADO (`tool_response.originalFile`, medido: `null` no Write que cria) e só FORMA — processo, banner/`#region`, bloco > 3 linhas, doc-comment fora de pacote publicado —, nunca "repete o código". É o ÚLTIMO elo da chain `Write\|Edit` (um block encerra a chain; canário em `test/hooks/hooks-installer.test.js`). Marcador que template precisa manter (`READS STANDARD(S):`, `VERIFY:`) entra em `ALLOWED`; o canário de template em `test/hooks/comment-rules.test.js` reprova template que dispararia o hook no scaffold |
|
|
168
168
|
| o ciclo de reuso (`tasks[].reuse`) | `framework/schemas/tasks.schema.json` + `tasks-json-parser.js` (campo `reuse`, com `why` no CRIAR — o parser só checa tipo, para não travar a escrita de status de plano antigo), `morph-plan` §1c/§5b, template de dispatch de `morph-implement`/`morph-apply`, personas de `agents.json`, o validador `reuse` (`src/lib/validators/reuse/`) e `test/framework/graph-fallback.test.js` |
|
|
169
169
|
| `framework/mcp/registry.json` | mcp-installer.js, claude-config-detector.js, agents-installer.js (personas' `mcp` field), `scripts/validate-framework.js` Check 8, `test/framework/mcp-registry.test.js` |
|
|
@@ -180,7 +180,7 @@ When modifying framework components, verify the full scope of related files.
|
|
|
180
180
|
| `src/lib/tasks/test-nodes.js` | os DOIS leitores de `validateTestNodes` — `verify-runner.js` (fail-closed: nó inválido avermelha o gate) e `doctor.js` (`doctor --full`, camada 3 do contrato do spec §2.2: WARN, e **pula sem imprimir nada** sem `config.json` ou sem `project.tests`, senão quebra `test/commands/doctor.test.js:46`). Uma segunda noção de "nó válido" faz o mesmo config passar num lugar e falhar no outro. `morphConfigPath` deriva de `CONFIG_FILE_NAME` (`core/state/state-manager.js`), a mesma constante de `readMorphConfig` — o JSDoc promete "one spelling" e agora é literal. O campo `groups` (#87): `TASK_GROUPS` é o MESMO enum do `group` de `tasks.schema.json` e de `config.schema.json` (canário em `test/lib/tasks/test-nodes.test.js`), `RUNNER_DEFAULT_GROUPS` dá o default por runner e `effectiveGroups` é o que `test-scope.js` lê — `null` é nó sem grupo, nunca palpite. `discoverFrontendPackages` (a união do fallback de `resolveTestNodes` e o WARN do `doctor --full`) varre as pastas de `PACKAGE_LOCATIONS`, de `src/lib/detectors/index.js` — uma segunda lista de pastas é como dois detectores começam a discordar |
|
|
181
181
|
| `src/lib/worktree/docker-audit.js` | `worktree-audit.js` (`auditWorktrees` concatena os achados e é quem PASSA as features conhecidas — um segundo scan aqui discordaria sobre o que é órfão), `doctor.js` (a classificação `info`/`warn` = não-falha em `doctorWorktreesCommand`; e `samePathCase`, a regra de caixa de path que o WARN de frontend do `doctor --full` e o `spelled` de `test-scope.js` reusam — uma segunda cópia dela foi removida), `env-allocator.js` (`allocatedFeatures` é read-only de propósito: `allocateEnvBlock` ESCREVE, e uma auditoria que aloca inventa o estado que reporta), `test/lib/worktree/docker-audit.test.js`. Nunca no `doctor --full` (D10): `test/commands/doctor.test.js:46` exige zero `✗` neste repo |
|
|
182
182
|
| `src/lib/risk-detector.js` | os DOIS lados da mesma decisão de gate: `gate-check.js` (`collectSignals` COLETA do disco; nenhum sinal que descreva um FATO pode entrar em `OVERRIDABLE_SIGNAL_KEYS` — `gate`/`workType`/`e2e*` são o precedente), `src/commands/validation/verify.js` (o stamp `verification` é a ÚNICA coisa que o gate lê: campo novo aqui só vira sinal se o stamp o gravar), `framework/schemas/feature.schema.json` (`verification` é `additionalProperties: false`), `framework/MORPH.md` §5 (tabela de sinais — ela se declara fonte única) e `framework/docs/CLI.md`. Política escrita no coletor cria duas fontes para o mesmo fato — foi assim que `gate-check` e `approve` divergiram uma vez sobre o Gate 3. O veredito do avaliador é lido por UM leitor (`evaluatorReportState`, `src/lib/evaluator-report.js`) e decidido por UMA regra (`verdictPause`): `approve` exige `status: ok`, `gate-check` pausa em `FIX`, bloqueador aberto ou sem tipo. **`verificationScope` é sinal, não auditoria**: a dispensa de e2e (`project`/`scope`) só conta vinda de um stamp de escopo `feature` — um `verify {f} {task}` de task `group: docs` carimba a razão de dispensa e compraria o Gate 3 da feature inteira. **E o escopo de task pausa o ship por si só** (#87): no `review`, `verificationScope` string ≠ `'feature'` pausa ANTES do bloco de e2e, mesmo com todos os nós verdes — o recorte automático faz TODO `verify {f} {task}` rodar menos que a suíte; `null` (stamp legado) não pausa por esta regra. O par mora em `verify.js`: `--filter` de escopo de feature não persiste o stamp (`reason: 'filtered'`), nem `--skip-tests`/`--skip-build` (`'skip-tests'`/`'skip-build'`, #113 — nó pulado não reprova o rollup e nenhuma regra do gate lê `tests`/`build` pulados; a lista é `PARTIAL_RUN_FLAGS`) |
|
|
183
|
-
| `src/lib/installers/effects.js` | os NOVE pontos de emissão que atravessam o caminho de `update`: `update.js` (clean, payloads, spawn de `generate-refs.js`, `gitIndex`), `skills-installer.js`, `agents-installer.js`, `setup-infra.js` (`installRulesForStack`, `updateClaudeMdAgentsBlock`), `hooks-installer.js` (`installClaudeHooks`, `installGlobalStatusline`), `file-copier.js` (`updateGitignore`, `ensureGitattributes`), `claude-md-injector.js`, `version-checker.js` (`saveProjectMorphVersion`) e `templates-reconcile.js`. **O caminho real também emite** — o resumo de uma execução real sai de `list()`, e é por isso que um ponto de emissão esquecido some das DUAS saídas ao mesmo tempo. Canários: `test/lib/installers/effects.test.js` (igualdade dry × real do MESMO roteiro) e `test/commands/update-dry-run.test.js` — que tem DOIS canários e precisa dos dois: **igualdade** prova que os dois lados percorrem o mesmo caminho, **diferencial** (mudar `--templates` muda a previsão) prova que o caminho lê a flag. Só igualdade aprova uma previsão e uma execução que ignoram a MESMA flag — foi medido nesta feature, por mutação. Um laço de limpeza de órfão novo tem de nascer calado em `fx.dryRun` (D6): o clean o subsome. E a lista de `frameworkDirsToClean` é pinada **à mão** no teste (`DIRETORIOS_APAGADOS_POR_INTEIRO`, dez caminhos, `deepEqual` ordenado): derivar a expectativa chamando a própria função deixou a suíte inteira cega — medido, dava para remover 2 das 10 entradas sem matar nenhum dos 207 arquivos de teste |
|
|
183
|
+
| `src/lib/installers/effects.js` | os NOVE pontos de emissão que atravessam o caminho de `update`: `update.js` (clean, payloads, spawn de `generate-refs.js`, `gitIndex`), `skills-installer.js`, `agents-installer.js`, `setup-infra.js` (`installRulesForStack`, `installCommandsForStack`, `updateClaudeMdAgentsBlock`), `hooks-installer.js` (`installClaudeHooks`, `installGlobalStatusline`), `file-copier.js` (`updateGitignore`, `ensureGitattributes`), `claude-md-injector.js`, `version-checker.js` (`saveProjectMorphVersion`) e `templates-reconcile.js`. **O caminho real também emite** — o resumo de uma execução real sai de `list()`, e é por isso que um ponto de emissão esquecido some das DUAS saídas ao mesmo tempo. Canários: `test/lib/installers/effects.test.js` (igualdade dry × real do MESMO roteiro) e `test/commands/update-dry-run.test.js` — que tem DOIS canários e precisa dos dois: **igualdade** prova que os dois lados percorrem o mesmo caminho, **diferencial** (mudar `--templates` muda a previsão) prova que o caminho lê a flag. Só igualdade aprova uma previsão e uma execução que ignoram a MESMA flag — foi medido nesta feature, por mutação. Um laço de limpeza de órfão novo tem de nascer calado em `fx.dryRun` (D6): o clean o subsome. E a lista de `frameworkDirsToClean` é pinada **à mão** no teste (`DIRETORIOS_APAGADOS_POR_INTEIRO`, dez caminhos, `deepEqual` ordenado): derivar a expectativa chamando a própria função deixou a suíte inteira cega — medido, dava para remover 2 das 10 entradas sem matar nenhum dos 207 arquivos de teste |
|
|
184
184
|
| `src/lib/ai/**` (`doctor --ai`) | `src/commands/project/doctor.js` (`doctorAiCommand` + a guarda de uso indevido), `bin/morph-spec.js` (`--ai`), `framework/docs/CLI.md`. **`--ai` NUNCA entra no `doctor --full` nem no default** — o renderer do `--full` manda tudo que não é `ok`/`warn` para `✗` e `test/commands/doctor.test.js` exige zero `✗` neste repositório. **Nada alcançável por `--ai` pode escrever**: `test/commands/doctor-ai.test.js` varre o grafo de imports E o corpo de `doctorAiCommand` (com os imports dinâmicos fixados nos dois permitidos), reprovando `writeFileSync`/`spawn`/`fs-extra` em CÓDIGO — comentários apagados antes, porque os módulos EXPLICAM por escrito que não fazem isso e medir a menção seria medir uma cópia do objeto. `null` é DESCONHECIDO e vira `skip` **com razão obrigatória**; `[]` é "perguntei e não achei" e vira `ok` |
|
|
185
185
|
| `src/lib/ai/registry-audit.js` | o leitor de PRODUÇÃO do mesmo documento: `framework/templates/dotnet/ai-kit/src/Morph.AiKit/Providers/ModelRegistry.cs` (`CommentHandling = Skip`, `AllowTrailingCommas`, `AllowedAliasProperties` com `pricing`/`pricingWaiver`) e o exemplar `model-registry.v2.json` que o framework manda COPIAR. A auditoria tem de enxergar a **mesma forma** que o leitor de produção enxerga: com `JSON.parse` estrito, duas das cinco checagens saíam `skip` sobre o exemplar canônico — e `skip` não muda exit code, então o silêncio era total. A tolerância a JSONC vive em `framework/hooks/shared/comment-stripper.js`, ÚNICA (o `code-audit.js` usa o mesmo varredor, e o `code-budget` também, o único com a opção `js` — template e regex literal de TS/JS; sem ela o varredor é o de antes) — e a paridade é nos DOIS sentidos: `jsoncToJson` **lança** no comentário de bloco não fechado, porque o `JsonCommentHandling.Skip` do .NET lança, e ser mais frouxo que o kit é aprovar documento que a app recusa carregar. Ao mascarar strings, **as aspas delimitadoras ficam**: apagá-las junto faz um elemento-string virar espaço puro e o detector de vírgula sobrando comer um SEPARADOR real (`{"tags":["a","b"]}` saía inválido). Canário obrigatório: array terminado em **string**, com e sem vírgula sobrando — uma suíte só com arrays de número passa dos dois lados dessa inversão. A **REGRA DURA** do exemplar — *todo alias declara `pricing` OU `pricingWaiver`, nunca os dois, nunca nenhum* — é do alias, não de um mapa no topo: ler só o mapa dava 6 falsos positivos sobre um documento correto. O canário **copia o exemplar do disco**, nunca uma paráfrase dele |
|
|
186
186
|
| `src/lib/worktree/dirs.js` | ÚNICA fonte do nome do container (`worktrees/`, sem ponto — Jest esconde diretório com ponto e reporta 0 testes sem erro). `worktree.js` re-exporta; `graphifyignore.js`, `file-copier.js`, `architecture-scanner.js`, `test-runner.js`, `worktree-audit.js`, os scanners do dashboard (`features-scanner.js`, `project-api.js`), `tasks/linux-runner.js` (exclusões do `tar` do `--linux`) importam. `test/lib/worktree/dirs.test.js` cobra cada leitor |
|
package/README.md
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
> Spec-driven development framework for multi-stack projects. Turns feature requests into implementation-ready code through structured, AI-orchestrated phases.
|
|
4
4
|
|
|
5
5
|
**Package:** `@polymorphism-tech/morph-spec`
|
|
6
|
-
**Version:** 8.
|
|
6
|
+
**Version:** 8.47.0
|
|
7
7
|
**Requires:** Node.js 22+, Claude Code
|
|
8
8
|
|
|
9
9
|
---
|
|
@@ -379,4 +379,4 @@ Code generated by morph-spec (contracts, templates, implementation output) belon
|
|
|
379
379
|
|
|
380
380
|
---
|
|
381
381
|
|
|
382
|
-
*morph-spec v8.
|
|
382
|
+
*morph-spec v8.47.0 by [Polymorphism Tech](https://polymorphism.tech)*
|
package/bin/morph-spec.js
CHANGED
|
@@ -366,17 +366,15 @@ graphCommand
|
|
|
366
366
|
// ─── Validation ───────────────────────────────────────────────────────────
|
|
367
367
|
program
|
|
368
368
|
.command('validate [validator]')
|
|
369
|
-
.description('Run project validations (all | packages |
|
|
369
|
+
.description('Run project validations (all | packages | next-components | spec-tasks)')
|
|
370
370
|
.option('-v, --verbose', 'Show detailed output')
|
|
371
371
|
.option('--auto-fix, --fix', 'Auto-fix issues where possible')
|
|
372
372
|
.option('--no-fail', 'Don\'t exit with error code')
|
|
373
|
-
.option('--wcag-aaa', 'Use WCAG AAA standard (stricter)')
|
|
374
373
|
.action((validator, options) => {
|
|
375
374
|
const args = validator ? [validator] : ['all'];
|
|
376
375
|
if (options.verbose) args.push('--verbose');
|
|
377
376
|
if (options.autoFix) args.push('--auto-fix');
|
|
378
377
|
if (options.noFail) args.push('--no-fail');
|
|
379
|
-
if (options.wcagAaa) args.push('--wcag-aaa');
|
|
380
378
|
validateCommand(args);
|
|
381
379
|
});
|
|
382
380
|
|
|
@@ -393,7 +391,6 @@ program
|
|
|
393
391
|
.command('verify <feature> [task]')
|
|
394
392
|
.description('Run the deterministic verification chain (build + tests + validators + e2e) — code before the LLM judge')
|
|
395
393
|
.option('--json', 'Output as JSON')
|
|
396
|
-
.option('--rubric <id>', 'Force a rubric (default: by task group → config → default)')
|
|
397
394
|
.option('--timeout <ms>', 'Timeout in ms for the build AND tests nodes (build default 120000; tests default 120000 per test project, or project.testTimeoutMs)', (v) => parseInt(v, 10))
|
|
398
395
|
.option('--filter <ids>', 'Run only the test nodes whose id matches (comma-separated; exact or substring). Unmatched nodes are reported as skip; matching nothing is a usage error (exit 2)')
|
|
399
396
|
.option('--linux', 'Run the test nodes inside a Linux container, on a COPY of the tree (the host tree is mounted read-only and copied in — a bind mount is case-insensitive and cannot reproduce case defects). No Docker = usage error, never a skip')
|
package/bin/validate.js
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
*
|
|
6
6
|
* Runs validators for the current project stack (detected from .morph/config/config.json).
|
|
7
7
|
*
|
|
8
|
-
* .NET
|
|
8
|
+
* .NET stacks: packages, spec-tasks
|
|
9
9
|
* Next.js stack: next-components, spec-tasks
|
|
10
10
|
* Other stacks: spec-tasks
|
|
11
11
|
*
|
|
@@ -23,7 +23,7 @@ import { getStackProfile } from '../src/lib/stack/stack-profile.js';
|
|
|
23
23
|
|
|
24
24
|
/**
|
|
25
25
|
* Build the VALIDATORS registry for the current stack.
|
|
26
|
-
* .NET stacks → packages,
|
|
26
|
+
* .NET stacks → packages, spec-tasks
|
|
27
27
|
* nextjs stack → next-components, spec-tasks
|
|
28
28
|
* other stacks → spec-tasks only
|
|
29
29
|
*/
|
|
@@ -35,24 +35,12 @@ async function buildValidators(stack) {
|
|
|
35
35
|
|
|
36
36
|
if (isDotnet) {
|
|
37
37
|
const { validatePackages } = await import('../src/lib/validators/packages/package-validator.js');
|
|
38
|
-
const { validateArchitecture } = await import('../src/lib/validators/architecture/architecture-validator.js');
|
|
39
|
-
const { validateContrast } = await import('../src/lib/validators/ui/ui-contrast-validator.js');
|
|
40
38
|
|
|
41
39
|
validators.packages = {
|
|
42
40
|
name: 'Package Compatibility',
|
|
43
41
|
description: 'Validates NuGet package versions against .NET compatibility matrix',
|
|
44
42
|
run: validatePackages
|
|
45
43
|
};
|
|
46
|
-
validators.architecture = {
|
|
47
|
-
name: 'Architecture Patterns',
|
|
48
|
-
description: 'Detects DI anti-patterns, middleware issues, and configuration problems in .NET',
|
|
49
|
-
run: validateArchitecture
|
|
50
|
-
};
|
|
51
|
-
validators.contrast = {
|
|
52
|
-
name: 'UI Contrast (WCAG)',
|
|
53
|
-
description: 'Validates color contrast ratios for accessibility',
|
|
54
|
-
run: validateContrast
|
|
55
|
-
};
|
|
56
44
|
}
|
|
57
45
|
|
|
58
46
|
if (isNextjs) {
|
|
@@ -273,8 +261,7 @@ async function runValidator(VALIDATORS, name, options) {
|
|
|
273
261
|
try {
|
|
274
262
|
const result = await validator.run('.', {
|
|
275
263
|
verbose: true, // Always verbose for single validator
|
|
276
|
-
autoFix: options.autoFix
|
|
277
|
-
wcagLevel: options.wcagLevel
|
|
264
|
+
autoFix: options.autoFix
|
|
278
265
|
});
|
|
279
266
|
|
|
280
267
|
if (result.status === 'ok') {
|
|
@@ -302,8 +289,7 @@ function parseOptions(args) {
|
|
|
302
289
|
const options = {
|
|
303
290
|
verbose: false,
|
|
304
291
|
autoFix: false,
|
|
305
|
-
noFail: false
|
|
306
|
-
wcagLevel: 'AA'
|
|
292
|
+
noFail: false
|
|
307
293
|
};
|
|
308
294
|
|
|
309
295
|
for (const arg of args) {
|
|
@@ -313,8 +299,6 @@ function parseOptions(args) {
|
|
|
313
299
|
options.autoFix = true;
|
|
314
300
|
} else if (arg === '--no-fail') {
|
|
315
301
|
options.noFail = true;
|
|
316
|
-
} else if (arg === '--wcag-aaa') {
|
|
317
|
-
options.wcagLevel = 'AAA';
|
|
318
302
|
}
|
|
319
303
|
}
|
|
320
304
|
|
|
@@ -334,20 +318,15 @@ ${chalk.bold('USAGE')}
|
|
|
334
318
|
${chalk.bold('VALIDATORS')}
|
|
335
319
|
all Run all validators (default)
|
|
336
320
|
packages Validate NuGet package compatibility (.NET 10)
|
|
337
|
-
architecture Validate architecture patterns (DI, lifecycle)
|
|
338
|
-
contrast Validate UI contrast ratios (WCAG 2.1)
|
|
339
321
|
|
|
340
322
|
${chalk.bold('OPTIONS')}
|
|
341
323
|
--verbose, -v Show detailed output
|
|
342
324
|
--auto-fix, --fix Auto-fix issues where possible
|
|
343
325
|
--no-fail Don't exit with error code
|
|
344
|
-
--wcag-aaa Use WCAG AAA standard (stricter)
|
|
345
326
|
|
|
346
327
|
${chalk.bold('EXAMPLES')}
|
|
347
328
|
morph-spec validate # Run all validators
|
|
348
329
|
morph-spec validate packages --fix # Validate packages and auto-fix
|
|
349
|
-
morph-spec validate architecture -v # Verbose architecture validation
|
|
350
|
-
morph-spec validate contrast --wcag-aaa # Stricter contrast validation
|
|
351
330
|
morph-spec validate all # Run all validators
|
|
352
331
|
|
|
353
332
|
${chalk.bold('EXIT CODES')}
|
package/claude-plugin.json
CHANGED
package/docs/CHEATSHEET.md
CHANGED
|
@@ -94,7 +94,7 @@ Never hand-edit `gateDecisions`/`approvalGates` in `feature.json` — always go
|
|
|
94
94
|
## CLI — validation
|
|
95
95
|
|
|
96
96
|
```bash
|
|
97
|
-
morph-spec validate [
|
|
97
|
+
morph-spec validate [packages|next-components|spec-tasks]
|
|
98
98
|
morph-spec validate-feature {feature} [--phase {phase}] [--json] # alias: vf
|
|
99
99
|
morph-spec verify {feature} [task] [--json] [--skip-tests] [--skip-build] # deterministic build+test+validator chain, before the LLM judge
|
|
100
100
|
```
|
|
@@ -185,4 +185,4 @@ Never edited directly — go through CLI commands or `morph-spec update` instead
|
|
|
185
185
|
|
|
186
186
|
---
|
|
187
187
|
|
|
188
|
-
*morph-spec v8.
|
|
188
|
+
*morph-spec v8.47.0 by Polymorphism Tech*
|
package/docs/QUICKSTART.md
CHANGED
package/framework/CLAUDE.md
CHANGED
|
@@ -32,7 +32,7 @@ Se não houver feature ativa, pergunte ao usuário qual feature trabalhar.
|
|
|
32
32
|
- Fechar cada task só com `verify {f} {T}` verde e cada `doneCriteria` com a evidência que o prova; vermelho ou critério sem prova dispara o loop de correção autônomo, sem escalar ao humano entre tarefas. No Gate 3, o veredito (`SHIP`/`FIX`) vem do **avaliador independente** despachado pela `morph-review` — nunca de quem implementou.
|
|
33
33
|
- Em `hotfix`, o Gate 3 pausa **sempre** para um humano, mesmo em `trust: auto` — `approve ... review --mode auto` é recusado; aprove com `--approver`.
|
|
34
34
|
- Confirmar antes de `finish`/`archive`/`delete` se o SessionStart avisar que outra sessão tem claim vivo na mesma feature.
|
|
35
|
-
- **
|
|
35
|
+
- **Ao retomar o controle depois de um dispatch, o orquestrador lê o arquivo de relatório, não a mensagem final** — a mensagem pode não chegar ou vir resumida. As cláusulas do prompt vivem só nos templates (`morph-implement` §4, `morph-apply` §3b).
|
|
36
36
|
|
|
37
37
|
## Slash commands
|
|
38
38
|
|
package/framework/MORPH.md
CHANGED
|
@@ -21,7 +21,7 @@ Entre gates, você opera com autonomia total. O dev valida. Você executa.
|
|
|
21
21
|
| Task implementada (código escrito) | `morph-spec verify <feature> <task>` e cada `doneCriteria` com a evidência que o prova |
|
|
22
22
|
| Verify verde e critérios atendidos | Task `done`, próxima tarefa, sem anúncio |
|
|
23
23
|
| Verify vermelho ou critério sem evidência | Loop de correção autônomo com o feedback, até 3 tentativas, sem pausa humana |
|
|
24
|
-
| 3 tentativas sem fechar |
|
|
24
|
+
| 3 tentativas sem fechar | `AskUserQuestion` (aceitar/continuar/pular) — único ponto de pausa do loop; nunca uma 4ª tentativa por conta própria nem re-dispatch: sem informação nova, repete o erro |
|
|
25
25
|
| `verify` exit 3 (fila pesada cheia) | A fila já esperou: no máximo uma chamada nova, depois avise — nunca laço de espera |
|
|
26
26
|
|
|
27
27
|
**Regra de ouro:** dúvida que não bloqueia → resolver e registrar; dúvida que bloqueia → uma pergunta, máximo. Vale **entre gates** (plan/implement/review) — **não** vale na abertura da feature: a fase proposal pode rodar em **modo entrevista** (escolhido na pergunta de abertura, `morph-brainstorming` Parte 2), onde perguntar até fechar o entendimento é o comportamento desejado.
|
|
@@ -64,7 +64,7 @@ O morph-spec opera com três papéis que você assume conforme o contexto:
|
|
|
64
64
|
|
|
65
65
|
**Orchestrator** — ativo na abertura de uma feature e nas transições de fase. Lê o mandato, entende o escopo, propõe o plano de ataque, identifica dependências entre tarefas, garante que gates sejam respeitados antes de avançar. Anti-padrão: não escreve código — se percebe que começou a implementar, pare, você transitou de papel sem intenção.
|
|
66
66
|
|
|
67
|
-
**Specialist** — ativo durante a implementação. Executa tarefas do tasks.json em ordem, consulta standards antes de cada uma, registra decisões/desvios no decisions.md, mantém o recap.md atualizado ao final de cada tarefa. Anti-padrão: não cria novas tarefas —
|
|
67
|
+
**Specialist** — ativo durante a implementação. Executa tarefas do tasks.json em ordem, consulta standards antes de cada uma, registra decisões/desvios no decisions.md, mantém o recap.md atualizado ao final de cada tarefa. Anti-padrão: não cria novas tarefas — escopo descoberto: adição pequena → `notes` da task; erro de fato do spec → `## Errata` (§3); o escopo mudou → `morph-spec unapprove {f} plan` e re-aprovar, porque o contrato do Gate 2 é congelado.
|
|
68
68
|
|
|
69
69
|
**Evaluator** — ativo no Gate 3, sempre um sub-agent independente com contexto fresco (`morph-evaluator`), nunca quem implementou (enforcement: relatório obrigatório, `--self-assessed`, exceção `hotfix` — **§5**). Segue `.morph/framework/evals/review-checklist.md`: veredito `SHIP`/`FIX` e bloqueador só com evidência (uc, teste, regressão, segurança); o resto é nota. Não roda a suíte (a prova é o verify do orquestrador) e não reescreve código.
|
|
70
70
|
|
|
@@ -188,11 +188,11 @@ Cada fase produz um trace JSON em `.morph/traces/YYYY-MM-DD-{feature}-{phase}.js
|
|
|
188
188
|
**Fluxo de consulta:**
|
|
189
189
|
|
|
190
190
|
1. Antes de iniciar uma tarefa, identifique 1-3 standards relevantes — rode `morph-spec standards --find "<domínio/tecnologia da task>"` (devolve o top 3 como `id · path · digest`) em vez de filtrar `id`/`tags` à mão no JSON.
|
|
191
|
-
2.
|
|
191
|
+
2. O `digest` (resumo de ~700 chars) costuma bastar; abra o `.md` (campo `path`) só quando não bastar — exemplo completo, seção que o resumo não cobre.
|
|
192
192
|
3. Aplique durante a implementação. Se precisar de mais contexto, leia mais um standard.
|
|
193
193
|
4. Registre no recap.md qual standard foi consultado e como foi aplicado.
|
|
194
194
|
|
|
195
|
-
**Como identificar o standard certo:** filtre por `category` primeiro (ex: "architecture", "
|
|
195
|
+
**Como identificar o standard certo:** filtre por `category` primeiro (ex: "architecture", "backend", "frontend", "ai-agents"), depois por `tags` (ex: "dotnet", "nextjs", "maf"). Dois standards relevantes em conflito → adote o mais específico ao stack do projeto e registre o conflito no decisions.md.
|
|
196
196
|
|
|
197
197
|
O agente responsável por um domínio está em `.morph/framework/agents.json` (fonte única — os subagents `morph-{id}` são gerados dele). O campo `persona` define o ponto de vista e o nível de rigor a adotar ao aplicar os standards daquele domínio trabalhando inline (um agente de arquitetura é mais rígido que um de suporte mensal).
|
|
198
198
|
|
|
@@ -218,6 +218,7 @@ Gates são pontos de **pausa potencial**. Em `trust: "auto"` (default), auto-pas
|
|
|
218
218
|
| `standardsViolations` | array.length > 0 | Plano ou código viola padrões proibidos |
|
|
219
219
|
| `evaluatorVerdict` | ≠ SHIP, bloqueador, ou ausente | Veredito do `evaluator-report`: `FIX`, bloqueador aberto, bloqueador sem tipo, ou relatório sem `**Verdict:**`. Só no **ship** |
|
|
220
220
|
| `verificationStatus` | === 'fail' | `verify` vermelho — build, testes, validadores **ou e2e**. Vale em **qualquer** gate |
|
|
221
|
+
| `verificationStatus` | ausente, no `review` | nenhum `verify` carimbado: ship sem prova; `approve` auto aplica a mesma régua |
|
|
221
222
|
| `verificationStale` | true | Stamp do `verify` com sha != HEAD: o verde é de outro código. Só no **ship** |
|
|
222
223
|
| `e2eSkipClass` (derivada) | environment/requested/unknown | e2e pulou sem dispensa legítima (só `project`/`scope`). Só no **ship** |
|
|
223
224
|
| `verificationScope` | !== `feature` (presente) | Stamp do `verify` é de escopo parcial (`task:{id}`) — não cobre a feature inteira. Só no **ship** |
|
|
@@ -241,9 +242,9 @@ Modo: `.morph/config/config.json → trust` (`"auto"`, o default, ou `"manual"`)
|
|
|
241
242
|
|
|
242
243
|
**Comportamento padrão fora de gate:** autonomia total — só pausa nestes três pontos. Ambiguidade de escopo fora do plano: registre em decisions.md, resolva conservadoramente, sinalize na entrega da task.
|
|
243
244
|
|
|
244
|
-
**A aprovação de gate é unificada nos comandos da CLI — nunca edite o estado à mão.** Em `trust: "auto"` (sem `AskUserQuestion`): `morph-spec approve <feature> <gate> --mode auto`; `morph-spec advance <feature>` aprova o gate corrente **e** cria a pasta da próxima fase (`--design` = ramo opcional de uiux). **O hook state-sync.js** cobre o caminho manual: `AskUserQuestion` respondida com
|
|
245
|
+
**A aprovação de gate é unificada nos comandos da CLI — nunca edite o estado à mão.** Em `trust: "auto"` (sem `AskUserQuestion`): `morph-spec approve <feature> <gate> --mode auto`; `morph-spec advance <feature>` aprova o gate corrente **e** cria a pasta da próxima fase (`--design` = ramo opcional de uiux). **O hook state-sync.js** cobre o caminho manual: `AskUserQuestion` respondida com frase de aprovação (abaixo) → registra o gate no `feature.json` autoritativo com `approvedBy: "manual"`. **A feature é identificada pelo nome que a pergunta cita** — grava na árvore dona dela, nunca na "feature ativa" da sessão. Pergunta sem nome numa árvore com mais de uma feature: o hook **recusa gravar** e avisa — aprove por `morph-spec approve`.
|
|
245
246
|
|
|
246
|
-
**Registre quem aprovou (`approvedBy`):** objeto `{ approved, timestamp, approvedBy }`. `"auto"` — política `trust: auto` sem decisão humana. `"manual"` — humano aprovou
|
|
247
|
+
**Registre quem aprovou (`approvedBy`):** objeto `{ approved, timestamp, approvedBy }`. `"auto"` — política `trust: auto` sem decisão humana. `"manual"` — humano aprovou — via `AskUserQuestion` ou, em headless, texto; o hook só reconhece resposta que começa com "aprovado" ou contém "pode avançar", "pode implementar" ou "pode prosseguir" (outra frase, ex. "lgtm", não grava: aprove por `morph-spec approve`); em `trust: manual` todo gate é `"manual"` — nunca `"auto"` num gate humano.
|
|
247
248
|
|
|
248
249
|
**O hook gate-guard.js** confere, sem bloquear, se a ação num gate (pausar vs auto-aprovar) bateu com a decisão persistida. **Default:** `gate-check <feature> <gate> [--signals <json>] [--json]` decide via `shouldPauseGate`; `--signals` sobrescreve só o que o disco não expõe (`designSystemDivergence`, `standardsViolations`). **Escape manual** (sinal que `shouldPauseGate` não representa): `gate-decision <feature> <gate> --pause <bool> [--reason <txt>] [--signals <json>]`. Ambos gravam `gateDecisions[gate] = { pause, reason, signals, trust, decidedAt }` server-side (`.morph/framework/schemas/feature.schema.json`). **Nunca edite `gateDecisions` à mão** — colide com state-sync. Divergência → o guard avisa no contexto.
|
|
249
250
|
|
|
@@ -257,7 +258,6 @@ A thread principal é o **Orchestrator**. Trabalho pesado ou fora do domínio co
|
|
|
257
258
|
|
|
258
259
|
- **Domínio diferente do contexto corrente** (ver `agents.json → domains`) — ex: backend agora, próxima task é frontend/UI/dados. Gatilho mais importante: cada domínio tem sua persona e standards, e o sub-agent escopado os carrega sem inchar seu contexto.
|
|
259
260
|
- **Esforço L ou XL** (> 5 minutos).
|
|
260
|
-
- Mais de 3 tentativas de correção falharam no mesmo problema.
|
|
261
261
|
- Análise de arquivo com mais de 500 linhas.
|
|
262
262
|
- Tarefa repetitiva com mais de 3 instâncias do mesmo padrão.
|
|
263
263
|
- Risco de "contaminar" o contexto principal com detalhes de baixo nível irrelevantes.
|
|
@@ -314,7 +314,7 @@ aceito em silêncio. Segunda ausência sem trabalho entregue é falha real: esca
|
|
|
314
314
|
- **Critérios:** cada `doneCriteria`, atendido (com o teste ou `arquivo:linha`) ou não (por quê)
|
|
315
315
|
- Problemas encontrados e como foram resolvidos
|
|
316
316
|
|
|
317
|
-
**Contrato do relatório final:** as oito cláusulas que todo prompt de dispatch inclui vivem nos DOIS templates — `morph-implement` §4 e `morph-apply` §3b — e não são repetidas aqui: duas cópias da mesma regra divergem, e quem manda é a que o sub-agent recebe. **4-8** (escopo, sobrevivência, processos, retorno, mutação) são idênticas **palavra por palavra**, e um canário as compara; **1-3** (relatório, estado, teste) dizem o mesmo na redação de cada template.
|
|
317
|
+
**Contrato do relatório final:** as oito cláusulas que todo prompt de dispatch de implementador inclui vivem nos DOIS templates — `morph-implement` §4 e `morph-apply` §3b — e não são repetidas aqui: duas cópias da mesma regra divergem, e quem manda é a que o sub-agent recebe. **4-8** (escopo, sobrevivência, processos, retorno, mutação) são idênticas **palavra por palavra**, e um canário as compara; **1-3** (relatório, estado, teste) dizem o mesmo na redação de cada template.
|
|
318
318
|
|
|
319
319
|
**Teto de turnos:** `maxTurns` por persona no `agents.json` (sem o campo, 20; implementadores 120), anunciado ao sub-agent no corpo da persona. O corte é cego: a cláusula 5 manda `wip` por marco e, a ~70% do teto, relatório `PARCIAL` sem critério dado como atendido (`report-incomplete`): retome o agente, não carimbe DEGRADADO.
|
|
320
320
|
|
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
description: Cria uma nova landing page ponta-a-ponta a partir do template MORPH Umbraco genérico: coleta decisões, scaffolda com new-lp, sobe e valida, customiza branding e módulos.
|
|
3
3
|
argument-hint: [nome-do-projeto]
|
|
4
4
|
allowed-tools: Read, Write, Edit, Bash, Glob, Grep, AskUserQuestion, Skill
|
|
5
|
+
stacks:
|
|
6
|
+
- umbraco
|
|
5
7
|
---
|
|
6
8
|
|
|
7
9
|
# /create-umbraco-lp — Nova LP Umbraco
|
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
description: Migra uma LP existente (React/Vite, Next.js estático ou HTML) para o template Umbraco-headless MORPH entregando a LP EXATAMENTE igual à origem (mesmo CSS, imagens, fontes e comportamento) + Umbraco — obtenção automática do template, lift-and-rewire verbatim de cada seção para módulo editável, conexão do formulário ao backend de leads e gate de paridade determinístico. Repo-agnóstica.
|
|
3
3
|
argument-hint: <projeto>
|
|
4
4
|
allowed-tools: Read, Write, Edit, Bash, Glob, Grep, Agent, AskUserQuestion, Skill
|
|
5
|
+
stacks:
|
|
6
|
+
- umbraco
|
|
5
7
|
---
|
|
6
8
|
|
|
7
9
|
# /migrate-lp-to-umbraco — Migração de LP para Umbraco Headless
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
---
|
|
2
|
-
description: Archive a
|
|
2
|
+
description: Archive a feature WITHOUT integrating — reserved for legacy features (no morph/{f} branch) and cleanup; the normal close is morph-spec finish
|
|
3
3
|
argument-hint: <feature-name>
|
|
4
4
|
allowed-tools: Read, Bash
|
|
5
5
|
---
|
|
@@ -22,7 +22,7 @@ Resolve uma queda ou bug crítico de produção com prioridade máxima em veloci
|
|
|
22
22
|
4. **Fix cirúrgico:** implementa a mudança mínima que resolve o sintoma — sem refatoração, sem hardening fora de escopo.
|
|
23
23
|
5. **Test loop:** `morph-spec verify {feature-name} {task} --json` focado no blast radius até verde; evidência real capturada no `recap.md`.
|
|
24
24
|
6. **Gate 3 sempre humano:** `morph-spec gate-check {feature-name} review` (força a pausa via `workType: hotfix`, lido do disco) → `4-review/review-report.md` → `AskUserQuestion` sempre. **Nunca** `morph-spec approve {feature-name} review --mode auto` — o CLI recusa essa combinação num hotfix.
|
|
25
|
-
7. **Pós-gate:** `morph-spec verify {feature-name}` (suíte completa da feature)
|
|
25
|
+
7. **Pós-gate:** `morph-spec verify {feature-name}` (suíte completa da feature), depois `morph-spec finish {feature-name} --pr|--merge` — o encerramento único (arquiva, integra, desmonta). O usuário decide o modo, porque `finish` toca a default ou o remoto; `--pr` **não integra** (`awaiting: "merge"`), então o incidente só está resolvido depois do merge.
|
|
26
26
|
|
|
27
27
|
## Anti-padrões
|
|
28
28
|
|
|
@@ -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
|