@polymorphism-tech/morph-spec 8.45.0 → 8.47.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (146) hide show
  1. package/CLAUDE.md +6 -5
  2. package/README.md +2 -2
  3. package/bin/morph-spec.js +2 -4
  4. package/bin/validate.js +4 -25
  5. package/claude-plugin.json +1 -1
  6. package/docs/CHEATSHEET.md +2 -2
  7. package/docs/QUICKSTART.md +1 -1
  8. package/framework/CLAUDE.md +2 -2
  9. package/framework/MORPH.md +9 -9
  10. package/framework/commands/create-umbraco-lp.md +2 -0
  11. package/framework/commands/migrate-lp-to-umbraco.md +2 -0
  12. package/framework/commands/morph-archive.md +1 -1
  13. package/framework/commands/morph-hotfix.md +1 -1
  14. package/framework/commands/morph-infra.md +17 -203
  15. package/framework/commands/morph-media.md +3 -0
  16. package/framework/commands/morph-preflight.md +5 -28
  17. package/framework/commands/morph-proposal.md +2 -2
  18. package/framework/commands/morph-troubleshoot.md +2 -14
  19. package/framework/docs/CLI.md +8 -7
  20. package/framework/hooks/claude-code/core/state-sync.js +13 -1
  21. package/framework/hooks/shared/e2e-check.js +3 -4
  22. package/framework/hooks/shared/state-writer.js +1 -0
  23. package/framework/rules/ai-agents-standards.md +18 -16
  24. package/framework/rules/asaas-standards.md +3 -1
  25. package/framework/rules/csharp-standards.md +10 -8
  26. package/framework/rules/data-standards.md +5 -5
  27. package/framework/rules/dotnet-templates.md +1 -1
  28. package/framework/rules/frontend-standards.md +4 -2
  29. package/framework/rules/gsap-standards.md +10 -5
  30. package/framework/rules/hangfire-standards.md +3 -1
  31. package/framework/rules/infrastructure-standards.md +4 -2
  32. package/framework/rules/morph-workflow.md +2 -2
  33. package/framework/rules/neon-auth-standards.md +3 -1
  34. package/framework/rules/neon-standards.md +6 -4
  35. package/framework/rules/nextjs-standards.md +13 -11
  36. package/framework/rules/nextjs-templates.md +1 -1
  37. package/framework/rules/resend-standards.md +3 -1
  38. package/framework/schemas/config.schema.json +4 -0
  39. package/framework/schemas/feature.schema.json +4 -4
  40. package/framework/skills/README.md +1 -1
  41. package/framework/skills/core/create-umbraco-lp/SKILL.md +3 -3
  42. package/framework/skills/core/migrate-lp-to-umbraco/SKILL.md +3 -3
  43. package/framework/skills/core/morph-apply/SKILL.md +3 -3
  44. package/framework/skills/core/morph-brainstorming/SKILL.md +3 -3
  45. package/framework/skills/core/morph-hotfix/SKILL.md +4 -4
  46. package/framework/skills/core/morph-implement/SKILL.md +3 -3
  47. package/framework/skills/core/morph-machine/SKILL.md +3 -3
  48. package/framework/skills/core/morph-media/SKILL.md +3 -3
  49. package/framework/skills/core/morph-plan/SKILL.md +3 -3
  50. package/framework/skills/core/morph-proposal/SKILL.md +3 -3
  51. package/framework/skills/core/morph-review/SKILL.md +30 -17
  52. package/framework/skills/core/morph-stack-scan/SKILL.md +3 -3
  53. package/framework/skills/core/morph-standards/SKILL.md +6 -6
  54. package/framework/skills/core/morph-uiux/SKILL.md +3 -3
  55. package/framework/standards/STANDARDS.json +59 -56
  56. package/framework/standards/ai-agents/evals-with-cache.md +1 -1
  57. package/framework/standards/ai-agents/llm-runtime-defaults.md +1 -1
  58. package/framework/standards/ai-agents/modalities-speech-to-text.md +36 -26
  59. package/framework/standards/ai-agents/prompt-sources.md +6 -4
  60. package/framework/standards/ai-agents/providers/model-registry.md +1 -1
  61. package/framework/standards/ai-agents/rag-custom-pgvector.md +67 -39
  62. package/framework/standards/ai-agents/setup.md +1 -1
  63. package/framework/standards/ai-agents/structured-output.md +1 -1
  64. package/framework/standards/ai-agents/sweet-spot.md +1 -1
  65. package/framework/standards/architecture/code-comments.md +2 -2
  66. package/framework/standards/architecture/vertical-slice/vertical-slice.md +18 -9
  67. package/framework/standards/backend/api/validation.md +135 -47
  68. package/framework/standards/backend/database/postgresql/database.md +11 -7
  69. package/framework/standards/backend/database/vector-search-rag.md +207 -324
  70. package/framework/standards/backend/dotnet/fluent-validation-vsa.md +14 -8
  71. package/framework/standards/backend/dotnet/result-pattern.md +40 -6
  72. package/framework/standards/backend/dotnet/scalar-ui.md +13 -7
  73. package/framework/standards/backend/dotnet/scrutor.md +1 -1
  74. package/framework/standards/backend/dotnet/testing.md +1 -1
  75. package/framework/standards/backend/dotnet/vsa-handler-patterns.md +7 -7
  76. package/framework/standards/backend/integrations/asaas/asaas-api.md +309 -165
  77. package/framework/standards/backend/integrations/hangfire/hangfire-jobs.md +134 -154
  78. package/framework/standards/data/nosql/cache/redis.md +120 -44
  79. package/framework/standards/data/nosql/cosmos-db.md +114 -71
  80. package/framework/standards/frontend/design-system/animations.md +1 -1
  81. package/framework/standards/frontend/design-system/naming.md +1 -1
  82. package/framework/standards/frontend/nextjs/motion-patterns.md +45 -29
  83. package/framework/standards/frontend/nextjs/naming-conventions.md +1 -1
  84. package/framework/standards/infrastructure/docker/coolify-deploy.md +8 -6
  85. package/framework/standards/infrastructure/neon/neon-pgvector.md +120 -75
  86. package/framework/standards/infrastructure/neon/neon-setup.md +53 -31
  87. package/framework/templates/code/dotnet/contracts/contracts-vsa.cs +2 -2
  88. package/framework/templates/code/dotnet/extensions/database-extensions.cs +1 -1
  89. package/framework/templates/code/dotnet/extensions/result-extensions.cs +27 -0
  90. package/framework/templates/code/dotnet/feature/endpoint.cs.hbs +2 -2
  91. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/MediaDisclosureTests.cs +1 -1
  92. package/framework/templates/infrastructure/docker/coolify.template.json +3 -3
  93. package/framework/templates/infrastructure/github/README.md +2 -1
  94. package/framework/templates/infrastructure/github/workflows/deploy-coolify.yml.hbs +2 -1
  95. package/package.json +1 -1
  96. package/src/commands/observability/telemetry.js +3 -5
  97. package/src/commands/project/init.js +2 -1
  98. package/src/commands/project/update.js +16 -18
  99. package/src/commands/state/approve.js +17 -0
  100. package/src/commands/state/gate-check.js +4 -14
  101. package/src/commands/validation/verify.js +99 -57
  102. package/src/core/paths/output-schema.js +1 -1
  103. package/src/dashboard/app/App.jsx +0 -2
  104. package/src/dashboard/app/components/CommandPalette.jsx +2 -3
  105. package/src/dashboard/app/components/EntityLink.jsx +0 -1
  106. package/src/dashboard/app/components/Markdown.jsx +1 -1
  107. package/src/dashboard/app/components/Sidebar.jsx +1 -2
  108. package/src/dashboard/app/pages/RulesMap.jsx +3 -3
  109. package/src/dashboard/app/pages/SkillsLibrary.jsx +3 -3
  110. package/src/dashboard/scanner/activity-scanner.js +1 -1
  111. package/src/dashboard/scanner/docs-scanner.js +5 -23
  112. package/src/dashboard/scanner/features-scanner.js +1 -1
  113. package/src/dashboard/scanner/index.js +0 -2
  114. package/src/dashboard/scanner/rules-scanner.js +15 -10
  115. package/src/dashboard/scanner/skills-scanner.js +2 -3
  116. package/src/lib/ci/ci-verify.js +118 -0
  117. package/src/lib/ci/github-ci.js +192 -0
  118. package/src/lib/ci/workflow-scaffold.js +122 -0
  119. package/src/lib/evaluator-report.js +13 -0
  120. package/src/lib/risk-detector.js +8 -0
  121. package/src/lib/stack/stack-profile.js +1 -1
  122. package/src/lib/telemetry/verify-event.js +3 -2
  123. package/src/lib/validators/reuse/reuse-validator.js +2 -3
  124. package/src/lib/validators/reuse/symbol-scanner.js +2 -4
  125. package/src/lib/validators/validation-runner.js +8 -88
  126. package/src/lib/validators/verify-runner.js +22 -72
  127. package/src/scripts/setup-infra.js +33 -22
  128. package/src/utils/agents-installer.js +20 -8
  129. package/src/utils/file-copier.js +7 -5
  130. package/framework/commands/commit.md +0 -166
  131. package/framework/hooks/claude-code/__pycache__/statusline.cpython-313.pyc +0 -0
  132. package/framework/rubrics/backend.json +0 -41
  133. package/framework/rubrics/default.json +0 -36
  134. package/framework/rubrics/frontend.json +0 -36
  135. package/framework/templates/project/validate.js +0 -97
  136. package/src/dashboard/app/pages/Rubrics.jsx +0 -97
  137. package/src/dashboard/scanner/rubrics-scanner.js +0 -12
  138. package/src/lib/validators/architecture/architecture-validator.js +0 -270
  139. package/src/lib/validators/architecture/index.js +0 -7
  140. package/src/lib/validators/css/css-validator.js +0 -340
  141. package/src/lib/validators/css/index.js +0 -7
  142. package/src/lib/validators/design-system/design-system-validator.js +0 -237
  143. package/src/lib/validators/design-system/index.js +0 -7
  144. package/src/lib/validators/score-aggregator.js +0 -244
  145. package/src/lib/validators/ui/index.js +0 -7
  146. package/src/lib/validators/ui/ui-contrast-validator.js +0 -422
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/` — Data-driven validators (architecture, css, design-system, nextjs)
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 |
@@ -156,13 +156,14 @@ When modifying framework components, verify the full scope of related files.
156
156
  | `src/lib/mutation/**` (`morph-spec mutate`, a mutação dirigida do Gate 3) | `src/commands/validation/mutate.js` (fina), `bin/morph-spec.js`, o bullet `mutate` de `framework/docs/CLI.md`, a seção "Prova por mutação" de `framework/evals/review-checklist.md` e o prompt do avaliador em `morph-review` §4, com os canários `test/lib/mutation/*` e `test/commands/mutate.test.js`. O mundo falso `test/helpers/mutation-world.js` reproduz o build INCREMENTAL do MSBuild (sem essa propriedade o teste da armadilha do mtime não morde). Invariantes: **no máximo 3 mutantes, cada um com `expect`** (`manifest.js` recusa com exit 2 — mutante cujo efeito o avaliador não prevê tende a ser equivalente); **com subconjunto utilizável, ele decide** (sobrevivente fica `survived` com escopo `subset`, sem escalar à suíte inteira — o subconjunto é escolhido pela ferramenta, nunca pelo avaliador); sem subconjunto, a suíte inteira roda e a morte tem de se repetir; restaurar é **escrita nova**, nunca `os.utime`; `killed` exige falha NOVA contra a rodada-base do MESMO escopo, e `inconclusive`/`build-error` nunca contam como morte; `proveBinary` compara a DLL final com a construída da árvore limpa. `dotnetBuildCommandFor` é a única grafia de "dotnet build" e `csharpClasses` a única leitura de classe de teste; o filtro do subconjunto é composto por `withFilter` (`narrowNode` recusa comando que já declara `--filter`). Dois parsers de TRX, ou um segundo "dotnet build" digitado, divergem |
157
157
  | `--record-baseline` (`recordTestBaselines` em `src/lib/tasks/test-nodes.js`) | `verify-runner.js` (`baselineTargetProblem` recusa ANTES do build; `recordBaselineFrom` só grava com todos os `runs[]` verdes), `src/commands/validation/verify.js` (`baselineStampBlock`: só a rodada que GRAVOU a baseline persiste o stamp — sem gravação, o `reason` nomeia o nó), `framework/schemas/config.schema.json` (`expectedCount`) e `atomicWriteJson` de `state-manager.js` — o número é MEDIDO por comando, nunca digitado (D8). Com `{task}` é erro de uso ANTES do build (`baselineTargetProblem`, #87), e `recordTestBaselines` recusa de novo qualquer medição com `selection` — a segunda barreira mora no gravador |
158
158
  | `src/lib/tasks/eval-node.js` | os leitores do MESMO nó `runner: "evals"`: `test-nodes.js` (o enum de `TEST_RUNNERS` e a validação fail-closed do bloco `evals`), `verify-runner.js` (`preDecideRun` faz o pré-voo — os três estados de cache reprovam ANTES de `dotnet test`; `judgeEvalRun` lê o sumário depois), `verify.js` (`evalsStatusOf` deriva `nodes.evals` do stamp) e `framework/schemas/config.schema.json`. O pin se resolve em UM lugar, `resolveAiPin` (`src/lib/ai/pin-drift.js`, o mesmo do `doctor --ai`): `.morph/framework/ai-pin.json` do projeto e, sem ele, o do pacote da CLI. O `update` nunca copia o pin, então o fallback é o caso comum — um leitor próprio do eval deixou `MORPH_EVAL_PIN` sem ser injetado em todo projeto instalado, e com ele a régua do carimbo desligada sem aviso. Subir o pin do pacote reprova (`eval-cache-stale-pin`) toda cache carimbada no anterior: é o efeito, não um defeito. **`null` é DESCONHECIDO, nunca 0**: sumário ilegível não pode fabricar queda contra `expectedCount`, e miss de cache é `fail` nomeado, JAMAIS `skip`. A unidade do nó é o CENÁRIO: só `judgeEvalRun` compara com `expectedCount` — `judgeRun` passa baseline `null` para `runner: "evals"` (#110 parte 2: os `[Fact]` do extrator contra a baseline de cenários reprovavam um 24/24), e sem sumário legível a contagem é `null`, nunca a do extrator — nem compara nem vira baseline. O sumário (`morph-eval-{id}-{pid}.json` no temp) é APAGADO antes e depois da rodada: um arquivo herdado sob o mesmo pid era lido como desta. `--linux` com nó de eval é erro de uso: o canal `MORPH_EVAL_*` carrega caminhos absolutos do host e o container roda uma cópia em `/work` |
159
+ | `src/lib/ci/**` (`verify {f} --ci`, a prova do Gate 3 no CI) | O CI é **outra fonte do MESMO stamp**, nunca uma regra de gate: `verify.js` grava o `verify --json` que o run publicou (artefato `morph-verify`, `CI_RESULT_FILE`) pelo mesmo caminho do local, com `source: 'ci'`, `ciRun` e `sha` = `headSha` do run — `gate-check`/`risk-detector`/`approve` não mudam. Só o ARTEFATO vira prova, nunca a `conclusion` do run (workflow de lint verde não compra o Gate 3), e `findCiWorkflow` só aceita workflow que chama `morph-spec verify` E publica `morph-verify.json` — o `renderCiWorkflow` (`workflow-scaffold.js`) tem de continuar passando nele (canário em `test/lib/ci/workflow-scaffold.test.js`). O run vale por ÁRVORE, não por SHA (`treeMovedSince`): commit só de `.morph/` não invalida, e o gatilho do workflow exclui `.morph/**` menos o `config.json` pela mesma razão (senão `cancel-in-progress` cancela o run do código). `cancelled`/`skipped`/`startup_failure` não são prova. Todo "não dá" é `mode: 'local'` com razão (fallback, nunca pausa); código fora de commit é exit 2; run pendente no prazo é exit 3 sem stamp. A URL do origin é lida CRUA (`remote.origin.url`): `git remote get-url` aplica `insteadOf`, que é como os testes apontam o push para um bare local. Canários: `test/commands/verify-ci.test.js` (stub `test/helpers/gh-ci-stub.js`), `test/lib/ci/*` |
159
160
  | `nodeStatuses()` em `src/commands/validation/verify.js` | os TRÊS blocos `nodes` de `framework/schemas/feature.schema.json`, todos `additionalProperties: false` — `verification.nodes`, `verifications.feature.nodes` e `taskVerifications.*.nodes`. A função é escritora ÚNICA dos três: um campo novo aqui sem os três no schema faz o histórico violar o próprio contrato. Canário: "os DOIS escritores do mapa de nós concordam" em `test/commands/verify.test.js` |
160
161
  | `src/lib/graph/**` | `test/lib/graph/*.test.js`, the committed fixture `test/fixtures/graph/namespace-hierarchy.graph.json` (ADR-003 regression — never hand-edit it), `doctor.js` (`codebase graph` check), `architecture-scanner.js` (centrality section), `setup-infra.js` step 18b. `isTestPath` (`sanitize.js`) é o predicado ÚNICO de teste: `god-nodes.js`, `recoverDispatch`, `graph status\|explain` e `symbol-scanner.js` (`isTestFile`, que o `code-budget` também lê) o importam, e uma segunda régua faz o grafo e o validador discordarem sobre o mesmo arquivo. A fixture `test-doubles.graph.json` saiu do graphify real (passo a passo em `docs/specs/graphify-integration/fixture-test-doubles/`) — regerar, nunca editar à mão |
161
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/` |
162
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 |
163
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 |
164
- | `src/lib/validators/reuse/**` | `validation-runner.js` (case + push universal), `framework/rubrics/*.json` (o id `reuse` mapeado numa categoria, senão o achado não pesa no score), `test/fixtures/graph/prospectpro-reuse.graph.json` (recorte do grafo real — regerar com `build-reuse-fixture.mjs`, nunca editar à mão) |
165
- | `src/lib/validators/code-budget/**` | `validation-runner.js` (case + push universal fora do `light`), `framework/rubrics/*.json` (o id `code-budget` em `code_quality` nas TRÊS, senão o aviso não pesa), `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 |
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 |
166
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 |
167
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` |
168
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` |
@@ -179,7 +180,7 @@ When modifying framework components, verify the full scope of related files.
179
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 |
180
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 |
181
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`) |
182
- | `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 |
183
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` |
184
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 |
185
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.45.0
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.45.0 by [Polymorphism Tech](https://polymorphism.tech)*
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 | architecture | contrast)')
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')
@@ -406,6 +403,7 @@ program
406
403
  .option('--skip-e2e', 'Skip the e2e node (Docker bring-up + Playwright)')
407
404
  .option('--fresh-e2e', 'Recreate the e2e stack from scratch: down -v (named volumes included), drop the freshness stamp, rebuild, re-seed auth')
408
405
  .option('--no-persist', 'Do not persist the result to feature.json')
406
+ .option('--ci', 'Feature scope only: push morph/<feature> and take the proof from the project CI (creates .github/workflows/morph-verify.yml when no workflow runs verify). Same stamp, source "ci". No GitHub/gh/workflow = local verify with the reason; uncommitted code = exit 2; CI still running after 50 min = exit 3, no stamp')
409
407
  .action((feature, task, options) =>
410
408
  verifyCommand(feature, task, { ...options, noPersist: options.persist === false })
411
409
  );
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/Blazor stacks: packages, architecture, contrast, spec-tasks
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, architecture, contrast, spec-tasks
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')}
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "morph-spec",
3
- "version": "8.45.0",
3
+ "version": "8.47.0",
4
4
  "displayName": "MORPH-SPEC Framework",
5
5
  "description": "Spec-driven development with 38 agents and 8-phase workflow for .NET/Blazor/Next.js/Azure",
6
6
  "publisher": "polymorphism-tech",
@@ -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 [architecture|packages|contrast] [--wcag-aaa]
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.45.0 by Polymorphism Tech*
188
+ *morph-spec v8.47.0 by Polymorphism Tech*
@@ -249,4 +249,4 @@ morph-spec doctor
249
249
 
250
250
  ---
251
251
 
252
- *morph-spec v8.45.0 by Polymorphism Tech*
252
+ *morph-spec v8.47.0 by Polymorphism Tech*
@@ -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
- - **Contrato do relatório final** em todo dispatch de sub-agent: (1) grava o relatório em arquivo canônico ANTES de encerrar — `.morph/features/{f}/3-implement/reports/{taskId}.md` (avaliador: `4-review/evaluator-report.md`) — com o quê / **Arquivos:** / **Critérios:** (cada doneCriteria com evidência) / problemas; é o retorno autoritativo, nunca chamar `SendMessage`; (2) não tocar `tasks.json`/`feature.json`/`state.json` nem o board de sessão; (3) testes pelo orçamento, só o próprio filtro uma vez, sem red→green (a prova é o `verify` do orquestrador); (8) nunca mutar produção. Racional completo: `MORPH.md` §6. Templates: `morph-implement` §4, `morph-apply` §3b. **O orquestrador lê o arquivo ao retomar controle** — nunca confia só na mensagem de retorno da tool.
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
 
@@ -68,7 +68,7 @@ Se não houver feature ativa, pergunte ao usuário qual feature trabalhar.
68
68
  | `dag <f>` | DAG de `tasks.json`: sequencial/paralelo, recomenda isolamento |
69
69
  | `graph status\|build\|refresh` | Grafo de codebase. `status` reporta frescor e se há refresh em background |
70
70
  | `graph affected\|explain <sym>` | Blast radius e vizinhança saneados. Sem grafo, caia para grep — nunca é erro |
71
- | `verify <f> [task] [--json]` | Build+testes+validadores+e2e — a prova determinística da task e do Gate 3 |
71
+ | `verify <f> [task] [--ci]` | Build+testes+validadores+e2e — prova da task; a do Gate 3 com `--ci` (no CI) |
72
72
  | `e2e up\|status\|down <f>` | Stack de verificação (docker compose) |
73
73
  | `doctor --worktrees [--prune]` | Reconcilia git × disco × features; só `--prune` remove algo |
74
74
  | `report-check <f> [task...] [--json]` | Audita relatório de sub-agent vs. trabalho entregue |
@@ -21,12 +21,12 @@ 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 | Escalar ao usuário (aceitar/continuar/pular) — único ponto de pausa do loop |
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.
28
28
 
29
- **A prova é o código:** `morph-spec verify` (build+testes+validadores) prova o que é determinístico; o `doneCriteria` prova o comportamento. Não há nota por task — estilo e estrutura são nota do avaliador do Gate 3, nunca bloqueio.
29
+ **A prova é o código:** `morph-spec verify` (build+testes+validadores) prova o que é determinístico; o `doneCriteria` prova o comportamento. A suíte do Gate 3 roda no CI do projeto (`verify {f} --ci`: mesmo stamp, a máquina fica livre; sem CI, local). Não há nota por task — estilo e estrutura são nota do avaliador do Gate 3, nunca bloqueio.
30
30
 
31
31
  ---
32
32
 
@@ -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 — descoberta de escopo vai para decisions.md com flag "escopo expandido", o Orchestrator avalia se o tasks.json precisa mudar.
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. Leia apenas o arquivo `.md` daquele standard (campo `path`).
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", "contracts", "testing"), depois por `tags` (ex: "dotnet", "nextjs", "blazor"). Dois standards relevantes em conflito → adote o mais específico ao stack do projeto e registre o conflito no decisions.md.
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 label "aprovado" → 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
+ **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 (via `AskUserQuestion` ou, em headless, texto iniciado em "aprovado"); em `trust: manual` todo gate é `"manual"` — nunca `"auto"` num gate humano.
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 completed MORPH-SPEC feature after Gate 3 approval
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) e sinaliza commit/archive — não arquiva automaticamente.
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