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