@polymorphism-tech/morph-spec 8.43.0 β 8.45.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 +16 -13
- package/README.md +10 -10
- package/bin/morph-spec.js +10 -13
- package/bin/validate.js +379 -379
- package/claude-plugin.json +1 -1
- package/docs/CHEATSHEET.md +2 -4
- package/docs/QUICKSTART.md +6 -6
- package/framework/CLAUDE.md +6 -8
- package/framework/MORPH.md +39 -45
- package/framework/agents.json +8 -9
- package/framework/commands/morph-apply.md +1 -3
- package/framework/commands/morph-hotfix.md +1 -1
- package/framework/commands/morph-proposal.md +2 -2
- package/framework/docs/CLI.md +22 -17
- package/framework/evals/review-checklist.md +90 -0
- package/framework/evals/trace-schema.md +2 -12
- package/framework/hooks/README.md +2 -3
- package/framework/hooks/claude-code/core/heavy-gate.js +1 -1
- package/framework/hooks/claude-code/core/protect-spec-files.js +2 -2
- package/framework/hooks/claude-code/core/telemetry-log.js +2 -2
- package/framework/hooks/claude-code/core/trace-autogen.js +1 -22
- package/framework/hooks/claude-code/core/validate-completion.js +3 -56
- package/framework/hooks/claude-code/core/worktree-env-inject.js +1 -4
- package/framework/hooks/claude-code/statusline.sh +11 -11
- package/framework/hooks/git/commit-msg/conventional-commits.sh +33 -33
- package/framework/hooks/git/pre-commit/agents.sh +25 -25
- package/framework/hooks/git/pre-commit/orchestrator.sh +64 -64
- package/framework/hooks/git/pre-commit/specs.sh +50 -50
- package/framework/hooks/git/pre-push/run-tests.sh +44 -44
- package/framework/hooks/shared/build-check.js +1 -1
- package/framework/hooks/shared/comment-scanner.js +1 -1
- package/framework/hooks/shared/phase-utils.js +0 -5
- package/framework/hooks/shared/stdin-reader.js +26 -26
- package/framework/phases.json +4 -6
- package/framework/rubrics/backend.json +1 -1
- package/framework/rubrics/default.json +1 -1
- package/framework/rubrics/frontend.json +1 -1
- package/framework/rules/ai-agents-standards.md +1 -0
- package/framework/rules/morph-workflow.md +6 -6
- package/framework/schemas/feature.schema.json +2 -28
- package/framework/schemas/tasks.schema.json +6 -2
- package/framework/skills/README.md +1 -2
- package/framework/skills/core/create-umbraco-lp/SKILL.md +1 -1
- package/framework/skills/core/migrate-lp-to-umbraco/SKILL.md +1 -1
- package/framework/skills/core/morph-apply/SKILL.md +145 -246
- package/framework/skills/core/morph-brainstorming/SKILL.md +1 -1
- package/framework/skills/core/morph-hotfix/SKILL.md +5 -3
- package/framework/skills/core/morph-implement/SKILL.md +101 -201
- package/framework/skills/core/morph-machine/SKILL.md +3 -3
- package/framework/skills/core/morph-media/SKILL.md +1 -1
- package/framework/skills/core/morph-plan/SKILL.md +171 -287
- package/framework/skills/core/morph-proposal/SKILL.md +4 -5
- package/framework/skills/core/morph-review/SKILL.md +109 -430
- package/framework/skills/core/morph-stack-scan/SKILL.md +1 -1
- package/framework/skills/core/morph-standards/SKILL.md +2 -2
- package/framework/skills/core/morph-uiux/SKILL.md +2 -3
- package/framework/standards/STANDARDS.json +54 -10
- package/framework/standards/ai-agents/agent-archetypes.md +1 -1
- package/framework/standards/ai-agents/agent-session.md +1 -1
- package/framework/standards/ai-agents/agent-spec.md +1 -1
- package/framework/standards/ai-agents/batching.md +1 -1
- package/framework/standards/ai-agents/context-providers.md +1 -1
- package/framework/standards/ai-agents/conversational-agent-with-phases.md +1 -1
- package/framework/standards/ai-agents/cost-and-budget.md +1 -1
- package/framework/standards/ai-agents/durable-workflows-hitl.md +1 -1
- package/framework/standards/ai-agents/guardrails.md +1 -1
- package/framework/standards/ai-agents/llm-runtime-defaults.md +1 -1
- package/framework/standards/ai-agents/mcp-server.md +1 -1
- package/framework/standards/ai-agents/mcp-tools.md +1 -1
- package/framework/standards/ai-agents/media-pipeline.md +1 -1
- package/framework/standards/ai-agents/media-video.md +1 -1
- package/framework/standards/ai-agents/middleware-patterns.md +1 -1
- package/framework/standards/ai-agents/modalities-image-gen.md +1 -1
- package/framework/standards/ai-agents/modalities-speech-to-text.md +1 -1
- package/framework/standards/ai-agents/modalities-text-to-speech.md +1 -1
- package/framework/standards/ai-agents/modalities-vision.md +1 -1
- package/framework/standards/ai-agents/multi-agent-patterns.md +1 -1
- package/framework/standards/ai-agents/observability-patterns.md +3 -3
- package/framework/standards/ai-agents/production.md +2 -2
- package/framework/standards/ai-agents/prompt-sources.md +1 -1
- package/framework/standards/ai-agents/providers/model-registry.md +1 -1
- package/framework/standards/ai-agents/rag-custom-pgvector.md +1 -1
- package/framework/standards/ai-agents/service-tier-flex.md +1 -1
- 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/ai-agents/testing-ai.md +1 -1
- package/framework/standards/ai-agents/tool-adapters.md +226 -0
- package/framework/standards/ai-agents/vector-data-extensions.md +1 -1
- package/framework/standards/ai-agents/workflows.md +1 -1
- package/framework/standards/infrastructure/docker/local-compose-isolation.md +11 -1
- package/framework/templates/CLAUDE.md.core.template +1 -1
- package/framework/templates/IDE-SNIPPETS.md +262 -262
- package/framework/templates/README.md +1 -1
- package/framework/templates/REGISTRY.json +0 -81
- package/framework/templates/code/dotnet/abstractions/api-endpoint.cs +7 -7
- package/framework/templates/code/dotnet/feature/endpoint.cs.hbs +21 -21
- package/framework/templates/code/dotnet/feature/request.cs.hbs +4 -4
- package/framework/templates/code/dotnet/feature/response.cs.hbs +4 -4
- package/framework/templates/code/sql/ai-agents/pgvector-embeddings-migration.sql.template +91 -91
- package/framework/templates/docs/spec.md +19 -205
- package/framework/templates/docs/ui-components.md +52 -52
- package/framework/templates/docs/ui-design-system.md +76 -76
- package/framework/templates/docs/ui-flows.md +167 -167
- package/framework/templates/frontend/nextjs/client-component.tsx.hbs +26 -26
- package/framework/templates/frontend/nextjs/feature-form.tsx.hbs +56 -56
- package/framework/templates/frontend/nextjs/tsconfig.json.hbs +26 -26
- package/framework/templates/infrastructure/docker/Dockerfile.template +58 -58
- package/framework/templates/infrastructure/docker/dockerfile-api.dockerfile +38 -38
- package/framework/templates/infrastructure/docker/dockerfile-web.dockerfile +48 -48
- package/framework/templates/infrastructure/github/README.md +56 -56
- package/framework/templates/infrastructure/github/actions/docker-build-push/action.yml.hbs +45 -45
- package/framework/templates/infrastructure/github/actions/health-check/action.yml.hbs +27 -27
- package/framework/templates/infrastructure/github/workflows/docker-build-push.yml.hbs +59 -59
- package/framework/templates/infrastructure/github/workflows/dotnet-build.yml.hbs +39 -39
- package/framework/templates/meta-prompts/fusion/fusion-agent.md +76 -76
- package/framework/templates/meta-prompts/fusion/fusion-aggregator.md +100 -100
- package/framework/templates/meta-prompts/validators/pre-commit-validator.md +95 -95
- package/framework/templates/state.template.json +17 -17
- package/framework/templates/ui/design-system.css +226 -226
- package/package.json +2 -2
- package/src/commands/e2e/e2e.js +1 -1
- package/src/commands/project/heavy.js +1 -1
- package/src/commands/project/status.js +3 -3
- package/src/commands/project/worktree.js +34 -16
- package/src/commands/state/approve.js +9 -50
- package/src/commands/state/finish.js +4 -0
- package/src/commands/state/gate-check.js +18 -107
- package/src/commands/state/gate-decision.js +1 -1
- package/src/commands/state/report-check.js +42 -17
- package/src/commands/validation/mutate.js +1 -1
- package/src/commands/validation/scout-check.js +28 -0
- package/src/commands/validation/validate-feature.js +5 -32
- package/src/commands/validation/verify.js +16 -25
- package/src/core/paths/output-schema.js +0 -2
- package/src/core/state/state-manager.js +1 -2
- package/src/core/templates/template-renderer.js +477 -477
- package/src/dashboard/app/lib/format.js +5 -0
- package/src/dashboard/app/main.jsx +13 -13
- package/src/dashboard/app/pages/FeatureDetail.jsx +16 -22
- package/src/dashboard/app/pages/Features.jsx +7 -7
- package/src/dashboard/app/pages/PhasePipeline.jsx +1 -1
- package/src/dashboard/app/pages/Rubrics.jsx +7 -11
- package/src/dashboard/scanner/docs-scanner.js +1 -2
- package/src/dashboard/scanner/features-scanner.js +3 -4
- package/src/dashboard/scanner/rubrics-scanner.js +12 -12
- package/src/dashboard/vite.config.js +24 -24
- package/src/lib/agents/output-collector.js +0 -1
- package/src/lib/detectors/design-system-detector.js +188 -188
- package/src/lib/e2e/compose-env.js +5 -2
- package/src/lib/evaluator-report.js +52 -334
- package/src/lib/generators/design-system-generator.js +508 -508
- package/src/lib/generators/handoff-generator.js +4 -5
- package/src/lib/git/evidence-staleness.js +39 -0
- package/src/lib/installers/memory-installer.js +2 -2
- package/src/lib/mutation/manifest.js +8 -2
- package/src/lib/mutation/report.js +5 -3
- package/src/lib/mutation/runner.js +9 -7
- package/src/lib/risk-detector.js +63 -125
- package/src/lib/scout/scout-check.js +293 -0
- package/src/lib/tasks/eval-node.js +4 -34
- package/src/lib/tasks/task-classifier.js +30 -30
- package/src/lib/tasks/task-parser.js +209 -209
- package/src/lib/tasks/tasks-json-parser.js +7 -5
- package/src/lib/tasks/test-nodes.js +1 -1
- package/src/lib/tasks/test-runner.js +1 -1
- package/src/lib/validators/contracts/contract-compliance-validator.js +4 -42
- package/src/lib/validators/css/css-validator.js +340 -340
- package/src/lib/validators/reuse/reuse-validator.js +6 -8
- package/src/lib/validators/verify-runner.js +1 -1
- package/src/lib/worktree/env-allocator.js +35 -5
- package/src/scripts/setup-infra.js +1 -1
- package/src/utils/agent-id-mapper.js +11 -11
- package/src/utils/agents-installer.js +5 -9
- package/src/utils/claude-settings-manager.js +300 -300
- package/src/utils/hooks-installer.js +3 -10
- package/src/utils/skills-installer.js +1 -1
- package/framework/evals/proof-rubrics/README.md +0 -76
- package/framework/evals/proof-rubrics/eval-de-modelo.md +0 -80
- package/framework/evals/proof-rubrics/fiacao.md +0 -54
- package/framework/evals/proof-rubrics/guard-cego.md +0 -52
- package/framework/evals/proof-rubrics/media-claims.md +0 -109
- package/framework/evals/proof-rubrics/mutation-proof.md +0 -107
- package/framework/evals/proof-rubrics/observabilidade-genai.md +0 -119
- package/framework/evals/proof-rubrics/verde-nao-e-coberto.md +0 -48
- package/framework/evals/rubrics/README.md +0 -45
- package/framework/evals/rubrics/architecture.md +0 -103
- package/framework/evals/rubrics/code-quality.md +0 -98
- package/framework/evals/rubrics/condensed.md +0 -57
- package/framework/evals/rubrics/contracts.md +0 -67
- package/framework/evals/rubrics/test-coverage.md +0 -99
- package/framework/evals/scenarios/dotnet-crud.yaml +0 -42
- package/framework/evals/scenarios/nextjs-auth.yaml +0 -38
- package/framework/evals/suites/standard.yaml +0 -5
- package/framework/hooks/claude-code/core/post-edit-typecheck.js +0 -127
- package/framework/skills/core/morph-eval/SKILL.md +0 -64
- package/framework/templates/.idea/morph-templates.xml +0 -92
- package/framework/templates/.vscode/morph-templates.code-snippets +0 -186
- package/framework/templates/docs/clarifications.md +0 -253
- package/framework/templates/docs/onboarding.md +0 -122
- package/framework/templates/docs/schema-analysis.md +0 -119
- package/framework/templates/docs/user-stories.md +0 -34
- package/src/commands/state/score.js +0 -147
package/CLAUDE.md
CHANGED
|
@@ -49,7 +49,7 @@ No build step β pure ESM JavaScript, runs directly via Node.js 18+.
|
|
|
49
49
|
- `workflows/` β Workflow type detector
|
|
50
50
|
- **`src/lib/`** β Libraries and subsystems:
|
|
51
51
|
- `validators/` β Data-driven validators (architecture, css, design-system, nextjs)
|
|
52
|
-
- `generators/` β Code generation (
|
|
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
|
|
55
55
|
- **`src/utils/`** β Shared utilities (logger, banner, file-copier, claude-settings-manager, agents-installer, skills-installer, hooks-installer)
|
|
@@ -63,7 +63,7 @@ No build step β pure ESM JavaScript, runs directly via Node.js 18+.
|
|
|
63
63
|
- `skills/core/` β 7 core NLH skills (single source of truth)
|
|
64
64
|
- `templates/` β Handlebars templates for code generation
|
|
65
65
|
- `mcp/registry.json` β MCP server registry
|
|
66
|
-
- `evals/
|
|
66
|
+
- `evals/review-checklist.md` β o que o avaliador do Gate 3 procura (veredito SHIP/FIX + bloqueador com evidΓͺncia)
|
|
67
67
|
|
|
68
68
|
### Key Architectural Patterns
|
|
69
69
|
|
|
@@ -77,7 +77,7 @@ No build step β pure ESM JavaScript, runs directly via Node.js 18+.
|
|
|
77
77
|
|
|
78
78
|
**Stack-aware filtering:** Assets declare `stacks: ['dotnet']` in YAML frontmatter or `stack: ['dotnet']` in `agents.json`. `setup-infra.js` reads `config.json β project.stack`, splits into tags, and installs only matching assets.
|
|
79
79
|
|
|
80
|
-
**Personas, not tiers:** `agents.json` has 8 flat personas β 7 domain personas (dotnet-senior, ef-modeler, maf-expert, nextjs-expert, ui-designer, infra-engineer, evaluator) plus 1 stage persona (`scout`, read-only). `model` is sized per stage: `sonnet` for implementers
|
|
80
|
+
**Personas, not tiers:** `agents.json` has 8 flat personas β 7 domain personas (dotnet-senior, ef-modeler, maf-expert, nextjs-expert, ui-designer, infra-engineer, evaluator) plus 1 stage persona (`scout`, read-only). `model` is sized per stage: `sonnet` for implementers and `scout`; `evaluator` is `sonnet` in agents.json and the dispatch routes it to `opus` when `evaluatorModelFor` (risk-detector.js, read by `report-check --before-eval`) flags the diff (measured against haiku in `docs/specs/scout-evidence/decisions.md`). No tier system β Claude assumes the 3 NLH roles (Orchestrator/Specialist/Evaluator) per MORPH.md.
|
|
81
81
|
|
|
82
82
|
### Phase Sequence
|
|
83
83
|
|
|
@@ -116,7 +116,7 @@ When modifying framework components, verify the full scope of related files.
|
|
|
116
116
|
|
|
117
117
|
| When you modify... | Also verify... |
|
|
118
118
|
|----------------------------------|-------------------------------------------------------------------------------------------|
|
|
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
|
|
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
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 |
|
|
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 |
|
|
@@ -137,22 +137,25 @@ When modifying framework components, verify the full scope of related files.
|
|
|
137
137
|
| `framework/MORPH.md` | All core skills (cross-references), CLAUDE.md (slash command list), e o **teto de 38 400 chars** (`test/framework/resident-size.test.js`). MORPH.md vive colado no teto: linha nova ali sΓ³ entra liberando pelo menos o mesmo nΓΊmero de caracteres **na mesma seΓ§Γ£o**, com o antes/depois medido β `MORPH_MD_LIMIT` nΓ£o sobe |
|
|
138
138
|
| a divisΓ£o loop Γ suΓte (`verify {f} {task}` recorta sozinho; `--filter` forΓ§a nΓ³s, `--full` desliga) | os QUATRO textos que a declaram e nΓ£o podem discordar: `framework/docs/CLI.md` (seΓ§Γ£o "SuΓtes de teste" β a tabela canΓ΄nica, a forma de `project.tests[]` e a subseΓ§Γ£o *O recorte por task*, com os limites), `morph-implement` Β§9, `morph-apply` Β§3a (o `verify` do loop e os `outputs` gravados ANTES dele β o recorte os lΓͺ) e `morph-review` (o Gate 3 roda **sem** filtro e `verify {feature}` nunca recorta). Uma superfΓcie que ensine filtrar no Gate 3 devolve o F30 pela porta da frente; canΓ‘rio: `test/framework/verify-stack-docs-prose.test.js` |
|
|
139
139
|
| `src/lib/worktree/ignored-triage.js` | `worktree.js` (`classifyIgnoredLeftovers`), `test/lib/worktree/ignored-triage.test.js`, e os canΓ‘rios de `test/commands/finish/merge-worktree.test.js` (e o prΓ©-voo de `finish/dry-run.test.js`) que pinam "o exemplar ΓΊnico nunca morre" β mudar um bucket muda o que o `finish` apaga |
|
|
140
|
-
| `src/lib/worktree/finish-backup.js` (backups do finish e a retenΓ§Γ£o) | os QUATRO pontos que escrevem ou leem `.git/morph-spec/finish-backup/`: `finish.js` (o shed da raiz e a poda do fim, `enforceBackupRetention`), `worktree.js` (o backup do desmonte e a poda do fim do `worktree remove`), `worktree-audit.js` (`auditFinishBackups` β `doctor --worktrees`) e `doctor.js` (a linha `.git/morph-spec` do padrΓ£o, nunca `β`, e o `--prune`, a ΓNICA poda que inclui legado). `resolveBackupRetention` Γ© a autoridade do `finishBackup.retention` de `framework/schemas/config.schema.json`: polΓtica que nΓ£o se lΓͺ PULA a poda, nunca cai no padrΓ£o, porque a operaΓ§Γ£o apaga. Backup sem manifesto Γ© legado, e a poda automΓ‘tica nunca o apaga nem o conta. Um finish grava as duas pastas com o MESMO carimbo, e o carimbo da execuΓ§Γ£o vai em `protectStamps`. SΓ³ `task/` Γ© namespace. Apagar Γ© PROVE-or-DECLINE, como a triagem de `ignored-triage.js` (que decide o que entra no backup) |
|
|
140
|
+
| `src/lib/worktree/finish-backup.js` (backups do finish e a retenΓ§Γ£o) | os QUATRO pontos que escrevem ou leem `.git/morph-spec/finish-backup/`: `finish.js` (o shed da raiz e a poda do fim, `enforceBackupRetention`), `worktree.js` (o backup do desmonte, o shed do esqueleto da raiz de feature jΓ‘ arquivada β `clearRootWorktreeRegistration`, que sΓ³ commita registro RASTREADO β e a poda do fim do `worktree remove`), `worktree-audit.js` (`auditFinishBackups` β `doctor --worktrees`) e `doctor.js` (a linha `.git/morph-spec` do padrΓ£o, nunca `β`, e o `--prune`, a ΓNICA poda que inclui legado). `resolveBackupRetention` Γ© a autoridade do `finishBackup.retention` de `framework/schemas/config.schema.json`: polΓtica que nΓ£o se lΓͺ PULA a poda, nunca cai no padrΓ£o, porque a operaΓ§Γ£o apaga. Backup sem manifesto Γ© legado, e a poda automΓ‘tica nunca o apaga nem o conta. Um finish grava as duas pastas com o MESMO carimbo, e o carimbo da execuΓ§Γ£o vai em `protectStamps`. SΓ³ `task/` Γ© namespace. Apagar Γ© PROVE-or-DECLINE, como a triagem de `ignored-triage.js` (que decide o que entra no backup) |
|
|
141
|
+
| a nota `memoryNote` do `finish` (`MEMORY_NOTE` em `finish.js`) | os DOIS leitores da mesma frase: `framework/docs/CLI.md` (entrada do `finish`) e `test/commands/finish/memory-note.test.js`. A nota Γ© carimbada no **`emit`**, ponto ΓΊnico por onde passa todo `finished: true`; o canΓ‘rio do `--pr` existe por isso: aquele payload Γ© montado Γ MΓO, fora do `mergeOutput`, entΓ£o mover a nota para o construtor do merge deixa o teste do `--merge` verde e sΓ³ ESSE morde (medido por mutaΓ§Γ£o). A frase nomeia o que o repositΓ³rio NΓO guarda; medir sΓ³ a presenΓ§a do campo aprovaria uma nota vazia |
|
|
141
142
|
| `src/lib/git/pr-index.js` | os QUATRO leitores do mesmo fato "esta branch tem PR": `fleet-scanner.js` (coluna PR), `branch-classifier.js` (o que NΓO apagar), `worktree-audit.js` (o que NΓO sugerir remover) e `pending-prs.js` (a sΓntese "aguardando merge"). `null` Γ© UNKNOWN, nunca "sem PR" |
|
|
142
143
|
| `src/lib/git/branch-classifier.js` | `src/commands/project/branch.js`, `worktree-audit.js` (`unintegratedWork` reusa `classifyBranch` β o `doctor` e o `branch prune` nΓ£o podem discordar sobre a mesma branch), `test/commands/branch-prune.test.js`, e `lifecycle-audit.js` (`abandoned-scaffold` conta commit prΓ³prio pelo mesmo `resolveBaseRef` + `commitsAhead`) |
|
|
143
144
|
| `src/lib/git/pending-prs.js` | os TRΓS consumidores da mesma sΓntese "aguardando merge": `list.js` (linha sintetizada), `fleet-scanner.js` (entrada `pr-pending`) e `doctor.js` (`auditPullRequests`). Uma feature visΓvel numa superfΓcie e invisΓvel na outra Γ© o bug que este mΓ³dulo existe para impedir |
|
|
144
145
|
| `src/lib/git/gh.js` | `pr-index.js`, `pr-files.js` e `finish.js` β as DUAS formas de invocar o `gh` viraram uma; uma segunda cΓ³pia de `resolveGh` volta a tornar o caminho de sucesso do Γndice intestΓ‘vel |
|
|
146
|
+
| `src/lib/git/evidence-staleness.js` (`treeMovedSince`, "a Γ‘rvore andou desde o commit da prova") | os DOIS leitores do mesmo fato: `gate-check.js` (`verificationStale`, o carimbo do `verify`) e `approve.js` (`reviewStaleness`, o `Reviewed-SHA` do review-report). Uma rΓ©gua sΓ³: comparar SHA com HEAD fazia todo commit de estado (o `feature.json` do prΓ³prio verify, `tasks.json`, `recap.md`) invalidar a prova sem nenhum byte de cΓ³digo diferente. Conta tudo fora de `.morph/` e, dentro dele, sΓ³ o `config.json` β sem `expectedCount`, que a rodada de baseline escreve e nΓ£o Γ© evidΓͺncia; um comando de teste trocado conta. `--no-renames` Γ© load-bearing: um arquivo movido para dentro de `.morph/` aparece como criaΓ§Γ£o lΓ‘ e a deleΓ§Γ£o fora some. SHA que o git nΓ£o resolve = andou (a resposta de antes); sem SHA ou sem HEAD = nΓ£o andou (fail-open). **NΓ£o reuse `treeFingerprint` para isto:** ele hasheia o `HEAD`, entΓ£o um commit sΓ³ de `.morph/` o muda. CanΓ‘rios: `test/lib/git/evidence-staleness.test.js` (mutaΓ§Γ£o: sem a normalizaΓ§Γ£o e sem `--no-renames` cada um derruba um teste) e os de `gate-check.test.js`/`approve.test.js` |
|
|
145
147
|
| `src/lib/git/tree-fingerprint.js` (a impressΓ£o da Γ‘rvore) | `verify.js` (grava `fingerprint` no evento `Verify`; `findRepeatedVerify`, de `verify-event.js`, compara com o ΓΊltimo verify de feature COMPLETO β rodada com `persisted: false` nunca Γ© a prova que uma completa substitui) e `metrics.js` (`verify.unchangedRepeats`, pela mesma regra). `.morph/` fica fora, exceto `config/config.json`: o prΓ³prio verify escreve feature.json e logs a cada rodada, e contΓ‘-los faria duas rodadas sobre o mesmo cΓ³digo parecerem diferentes. `repeatOf` Γ© informaΓ§Γ£o e nenhum gate o lΓͺ: ligΓ‘-lo ao Gate 3 Γ© aceitar a prova de uma rodada anterior no lugar da atual |
|
|
146
148
|
| `src/lib/git/tree-state.js` (o bloco `tree` do `report-check`) | `report-check.js` (toda saΓda, inclusive o batch `report-check <f> T1 T2`, e a foto `--before-eval` em `4-review/tree-before-eval.json`) e as skills que leem os campos: `morph-implement`/`morph-apply` (pΓ³s-dispatch e retomada do PARCIAL) e `morph-review` (higiene contra `tree.sinceBeforeEval`). Campo renomeado aqui sem as skills Γ© instruΓ§Γ£o que lΓͺ `undefined`: `test/framework/report-check-wiring.test.js` confere que todo `tree.*` citado existe na saΓda real. A foto compara CONTEΓDO (blob por arquivo), nΓ£o o `git status`: a mutaΓ§Γ£o que o avaliador commitou tambΓ©m aparece. Fora de git o bloco Γ© `null`, nunca Γ‘rvore vazia. `MUTATION_MARKER` tem a mesma grafia que as skills mandam gravar |
|
|
147
|
-
| `src/lib/worktree/feature-truth.js` | The six readers of the same "which tree owns this feature" predicate: `fleet-scanner.js`, `list.js`, `handoff.js`, the mutator guard (`approve.js`/`advance.js`/`
|
|
149
|
+
| `src/lib/worktree/feature-truth.js` | The six readers of the same "which tree owns this feature" predicate: `fleet-scanner.js`, `list.js`, `handoff.js`, the mutator guard (`approve.js`/`advance.js`/`gate-decision.js`/`gate-check.js`), `verify.js` (`decidePersistence`, asked from the PRIMARY ROOT β asked from cwd, step 4 would make any scratch tree carrying a `feature.json` persist) and the dashboard's `features-scanner.js` (only for ACTIVE features β it spawns git β and a worktree owner without the feature files is shown as `rootStale`, never silently replaced by the root skeleton) |
|
|
148
150
|
| `src/lib/tasks/test-runner.js` | `verify-runner.js` (o nΓ³ `tests` repassa o `--timeout` e lΓͺ `timedOut`), `src/commands/project/worktree.js` (as constantes `WORKTREES_DIR`/`LEGACY_WORKTREES_DIR` que a descoberta ignora β duplicar as strings aqui Γ© como o bug volta), `test/lib/test-runner.test.js` (o canΓ‘rio do ignore de worktrees e o do timeout escalado). Um worktree Γ© o checkout de OUTRA feature: varrΓͺ-lo dobra o relΓ³gio e reporta sobre cΓ³digo fora da Γ‘rvore avaliada. `stripAnsi` roda antes de TODO extrator de contagem (ponto ΓΊnico em `parseTestCount`) e das strings de `isZeroTestRun` β uma cor SGR no meio do sumΓ‘rio derrotava os regexes ancorados (#110). O extrator `dotnet` lΓͺ os DOIS sumΓ‘rios (#115): a linha VSTest e o bloco MTP (cabeΓ§alho `Test run summary:`/`Resumo da execuΓ§Γ£o de teste:` na coluna 0, `total:` indentado), em pt-BR e en-US β UM bloco por invocaΓ§Γ£o de `dotnet test` (uma soluΓ§Γ£o de dois projetos dΓ‘ um bloco agregado, medido), entΓ£o somar blocos Γ© somar links `&&`; um terceiro locale Γ© `null`. `mtpSummaryTotals` (um total por bloco) Γ© o que `judgeNarrowedRun` lΓͺ para achar, numa cadeia MTP recortada, o link que rodou 0 (exit 8 ignorado) escondido pela soma. `ZERO_RUN_SIGNATURES` mistura o medido e o documentado β medidos (capturas em `test/fixtures/test-output/`): o exit 8 do MTP e a string pt-BR do VSTest; sΓ³ documentados, sem captura: a string en-US do VSTest e as de vitest/jest. O exit 9 fica FORA de propΓ³sito: Γ© piso de projeto, nΓ£o zero testes. Leitor: `judgeNarrowedRun` (`verify-runner.js`). `extractCsprojTargets`, `isInside` e `canonicalPath` tambΓ©m sΓ£o lidos por `test-scope.js` (`canonicalPath` dΓ‘ a todo path de `outputs` a grafia do DISCO β no win32 um `Foo.test.ts` mal grafado passa no `isFile`, e sob `--linux`, case-sensitive, o runner nΓ£o o acha enquanto um irmΓ£o mantΓ©m a contagem acima de zero); `dotnet-test-introspection.js` lΓͺ sΓ³ `isInside`. Um segundo canonicalizador Γ© como os dois lados passam a discordar sobre o mesmo path |
|
|
149
|
-
| `src/lib/specs/screen-checklist.js` (checklist de tela, #129) | o sinal mora aqui (`collectScreenChecklist`) e tem QUATRO leitores: `gate-check.js` na `uiux` (`problems` β pausa) e no `review` (`uncovered`: item decidido sem linha na seΓ§Γ£o `## Checklist de tela` que `evaluator-report.js` lΓͺ β ΓΊltima ocorrΓͺncia; tela toda "nΓ£o se aplica" nΓ£o deve nada), e `approve.js`/`advance.js` com assinatura `auto` (`uiuxAutoApprovalBlock`). PolΓtica sΓ³ em `risk-detector.js`: `uiuxChecklistPause` Γ© o predicado ΓNICO do gate-check pausar e do auto recusar β uma segunda regra no advance fez approve e advance discordarem da mesma feature β e `gateDecisions.uiux.pause === false` destrava os dois sΓ³ para o mockup que a decisΓ£o viu: `persistGateDecision` (`gate-decision.js`, o escritor ΓΊnico de `gateDecisions`) grava `mockupsHash` e `uiuxAutoApprovalBlock` o compara com o arquivo atual β os dois leem `mockupsHash` daqui, e um segundo cΓ‘lculo do hash (outra normalizaΓ§Γ£o de BOM/CRLF) faz toda decisΓ£o parecer de outro mockup. `--force` nΓ£o destrava em nenhum dos dois. Mockup legado (`## S\d+:` e nenhuma `## Tela:`, o template anterior) Γ© isento nas duas fases com `exempt` = `LEGACY_MOCKUP_EXEMPTION` Γ vista no `getRiskSignals`; qualquer outro heading Γ© "nenhuma tela declarada", senΓ£o trocar o heading pularia o checklist (fixture: `test/fixtures/mockups/legacy-ui-mockups.md`, o template de main). Os rΓ³tulos dos itens casam com DOIS textos que o parser nΓ£o vΓͺ, os dois com canΓ‘rio em `test/lib/specs/screen-checklist.test.js`: o template do prompt da `morph-uiux` Β§3 e `framework/templates/docs/ui-mockups.md`; mais o formato de linha que o prompt do avaliador (`morph-review` Β§4) e
|
|
151
|
+
| `src/lib/specs/screen-checklist.js` (checklist de tela, #129) | o sinal mora aqui (`collectScreenChecklist`) e tem QUATRO leitores: `gate-check.js` na `uiux` (`problems` β pausa) e no `review` (`uncovered`: item decidido sem linha na seΓ§Γ£o `## Checklist de tela` que `evaluator-report.js` lΓͺ β ΓΊltima ocorrΓͺncia; tela toda "nΓ£o se aplica" nΓ£o deve nada), e `approve.js`/`advance.js` com assinatura `auto` (`uiuxAutoApprovalBlock`). PolΓtica sΓ³ em `risk-detector.js`: `uiuxChecklistPause` Γ© o predicado ΓNICO do gate-check pausar e do auto recusar β uma segunda regra no advance fez approve e advance discordarem da mesma feature β e `gateDecisions.uiux.pause === false` destrava os dois sΓ³ para o mockup que a decisΓ£o viu: `persistGateDecision` (`gate-decision.js`, o escritor ΓΊnico de `gateDecisions`) grava `mockupsHash` e `uiuxAutoApprovalBlock` o compara com o arquivo atual β os dois leem `mockupsHash` daqui, e um segundo cΓ‘lculo do hash (outra normalizaΓ§Γ£o de BOM/CRLF) faz toda decisΓ£o parecer de outro mockup. `--force` nΓ£o destrava em nenhum dos dois. Mockup legado (`## S\d+:` e nenhuma `## Tela:`, o template anterior) Γ© isento nas duas fases com `exempt` = `LEGACY_MOCKUP_EXEMPTION` Γ vista no `getRiskSignals`; qualquer outro heading Γ© "nenhuma tela declarada", senΓ£o trocar o heading pularia o checklist (fixture: `test/fixtures/mockups/legacy-ui-mockups.md`, o template de main). Os rΓ³tulos dos itens casam com DOIS textos que o parser nΓ£o vΓͺ, os dois com canΓ‘rio em `test/lib/specs/screen-checklist.test.js`: o template do prompt da `morph-uiux` Β§3 e `framework/templates/docs/ui-mockups.md`; mais o formato de linha que o prompt do avaliador (`morph-review` Β§4) e `framework/evals/review-checklist.md` ("Contrato de tela") prescrevem. A `SuperfΓcie` Γ© validada contra as chaves de `surfaces` da `ui-designer` (`knownSurfaces`) β Γ© a chave que o `morph-plan` Β§5a usa para somar standards |
|
|
152
|
+
| `src/lib/scout/scout-check.js` (o mapa do scout) | os DOIS escritores do mesmo formato e os que mandam rodΓ‘-lo: a persona `scout` do `framework/agents.json` (o CONTRATO DE EVIDΓNCIA e seus exemplos `Formato:`), MORPH.md Β§6 ("Mapa do scout"), `morph-plan` Β§1b, `morph-hotfix` Β§2 (+ `framework/commands/morph-hotfix.md`) e a entrada do `framework/docs/CLI.md`. Persona que ensina uma forma que o checker reprova faz TODO mapa voltar: `test/framework/scout-persona-format.test.js` passa os exemplos da persona pelo checker. Os relaxamentos (linha jΓ‘ citada, bloco cercado logo depois, listagem sob comando de busca, subseΓ§Γ£o de Inferido, menΓ§Γ£o entre aspas) tΓͺm cada um o caso parecido que continua reprovando em `test/lib/scout/scout-check.test.js`; crase descritiva depois de citaΓ§Γ£o reprova de propΓ³sito, Γ© a forma do erro de campo. Raiz = `--root` ou topo da worktree, nunca a raiz primΓ‘ria. MediΓ§Γ£o de modelo/effort: `docs/specs/scout-evidence/decisions.md` |
|
|
150
153
|
| `src/lib/tasks/test-scope.js` (o recorte por task, #87) | Este DECIDE, `verify-runner.js` executa: `applyTaskScope` roda UMA vez em `runVerify`, antes do build, e `preDecideRun` (pula `out-of-scope`) e `judgeRun` β `judgeNarrowedRun` (nunca `compareTestCount`; zero ou desconhecido Γ© vermelho) sΓ£o a cΓ³pia ΓΊnica para host e `--linux` β o container recebe o recorte pelo `node.command`. Importa `effectiveGroups` (`test-nodes.js`), `extractCsprojTargets`/`isInside`/`canonicalPath` (`test-runner.js`), `normalizePath` (`traceability-check.js` β um normalizador sΓ³ para `outputs`), `samePathCase` (`docker-audit.js` β sΓ³ a CAIXA vem do disco: um path que atravessa symlink/junction fica como escrito, senΓ£o o arquivo muda de nΓ³) e a introspecΓ§Γ£o .NET abaixo. `runs[].selection` sai sΓ³ no `--json`: `nodeStatuses()` nΓ£o o persiste. Piso `--minimum-expected-tests` de projeto MTP (#114): quando `floorOverride` o lΓͺ de UM lugar, o link recortado ganha `"-p:TestingPlatformCommandLineArguments=<valor com o piso em 1>"` (o `-p:` troca o valor inteiro β medido, sem recompilar); qualquer outra forma roda inteiro, e tambΓ©m o comando que jΓ‘ declara a propriedade (o `-p:` do recorte vem por ΓΊltimo e ganharia) e o link que roda mais de um projeto (o `-p:` Γ© global Γ invocaΓ§Γ£o e trocaria os argumentos do outro). Forma de comando nova se lΓͺ e se MEDE (`test/fixtures/test-output/README.md`) antes de estreitar; o resto roda INTEIRO com motivo. CanΓ‘rios: `test/lib/tasks/test-scope.test.js`, o bloco `runVerify β task-scope recorte` de `test/lib/verify-runner.test.js`, `test/commands/verify-filter.test.js` e a prosa em `test/framework/verify-stack-docs-prose.test.js` |
|
|
151
154
|
| `src/lib/tasks/dotnet-test-introspection.js` | SΓ³ LΓ (dividido de `test-scope.js`): o varredor C# que apaga literais (`blankCSharpLiterals`), o tripwire contra o texto CRU + o balanΓ§o de chaves (`csharpClasses`), `classNamesOf`, `dotnetTestMode` (lΓͺ o `global.json` por `jsoncToJson` de `framework/hooks/shared/comment-stripper.js`), o piso `--minimum-expected-tests` / `<VSTestTestCaseFilter>` do projeto, `floorOverride` (#114: o valor do `-p:` que rebaixa o piso a 1 e repassa o resto β sΓ³ com UM `<TestingPlatformCommandLineArguments>` literal no csproj, sem atributo, num `<PropertyGroup>` sem atributo direto sob `<Project>` (`enclosingElements` anda nas tags: `Condition` no GRUPO, `<Target>` e `<Choose>` injetavam um filtro que o MSBuild nΓ£o usaria), sem `TreatAsLocalProperty`, sem `<Import>`, sem a propriedade em nenhum outro arquivo que liga o projeto, sem piso no testconfig e sem `` $ @ % ; , & " ' ` \ ! `` no valor; `null` roda inteiro; `bindingMsbuildFiles` Γ© a lista ΓΊnica de arquivos MSBuild que `msbuildDeclares` tambΓ©m lΓͺ β o `.csproj.user` e `Directory.Build.props/.targets/.rsp` + `Directory.Packages.props` atΓ© a raiz do DISCO, porque o MSBuild nΓ£o para no repositΓ³rio), `ignoresExitCode` (o MESMO scan de csproj/Directory.Build.*/testconfig: `--ignore-exit-code` ou `TESTINGPLATFORM_EXITCODE_IGNORE` desarma a prova do recorte MTP que resta quando o sumΓ‘rio Γ© ilegΓvel, o exit code β desde a #115 a contagem do sumΓ‘rio, pt-BR/en-US, prova primeiro, e o scan fica porque decide ANTES da rodada), o projeto de teste dono de um `.cs` (`dotnetTestProjectOf` β sΓ³ `.cs`/`*.csproj`, via `nearestCsprojOf`), `nearestProjectOf` + `isDotnetTestProject` (qualquer arquivo; cobrem `.csproj`/`.fsproj`/`.vbproj` β `test-scope.js` roda o nΓ³ INTEIRO por QUALQUER arquivo nΓ£o-`.cs` que o nΓ³ pode rodar cujo projeto mais prΓ³ximo Γ© de teste: `.feature`, `.verified.txt`, `.razor`, dado de teste), `isNonCSharpSource`/`isDotnetSource`, `readsLikeTestSource` (o marcador FROUXO no texto cru atrΓ‘s de `unrecognisedTestFile` e do segundo braΓ§o do F2 β falso positivo sΓ³ custa relΓ³gio) e `CS_TEST_ATTRIBUTE` (nome inteiro numa seΓ§Γ£o de atributo que FECHA β `, TestContext` num construtor e uma coleΓ§Γ£o `[\n Env.Test,\n]` compravam um verde; o resΓduo sondado mora no JSDoc de `csharpClasses`). **Falha para o lado SEGURO:** varredura desbalanceada, literal sem fim, buraco de interpolaΓ§Γ£o cortado ou palavra-chave de tipo que nΓ£o virou declaraΓ§Γ£o β o nΓ³ roda INTEIRO; um erro que ESTREITA roda menos testes e sai verde. `test-scope.js` reexporta `dotnetTestMode`/`classNamesOf`, e `test/lib/tasks/dotnet-test-introspection.test.js` pina que sΓ£o as MESMAS funΓ§Γ΅es. A garantia e o que ela NΓO cobre moram no JSDoc de `csharpClasses`: mexeu no varredor, reescreva-o junto e prove por mutaΓ§Γ£o que o tripwire ainda morde |
|
|
152
155
|
| `src/lib/tasks/linux-runner.js` | `verify-runner.js` (`runNodesInLinux` faz as MESMAS prΓ©-decisΓ΅es do laΓ§o local e julga o resultado pelo MESMO `judgeRun` β uma segunda cΓ³pia dessa regra Γ© como container e host passam a discordar da mesma suΓte), `framework/hooks/shared/e2e-check.js` (`defaultExec`/`isDockerAvailable` sΓ£o reusados, nunca recriados), `src/lib/worktree/dirs.js` (nomes do contΓͺiner de worktree nas exclusΓ΅es do `tar`), `framework/schemas/config.schema.json` (`linuxImage`) e `test/lib/tasks/linux-runner.test.js` (o canΓ‘rio do bind mount: montar a Γ‘rvore em `/work` Γ© v9fs case-insensitive e nΓ£o reproduz F35). Os labels `morph-spec.verify`/`morph-spec.root` do `docker run` e `reapLabeledContainers` (`docker rm -f` por label, nunca por nome) sΓ£o o que limpa o container efΓͺmero sem tocar nos de outros projetos: `test/lib/tasks/linux-runner.test.js` pina o argv de `run` (filtra `args[0]==='run'`, porque o `ps`/`rm` do reap passam pelo mesmo `exec`). |
|
|
153
|
-
| `src/lib/mutation/**` (`morph-spec mutate`, a
|
|
154
|
-
| `--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` (
|
|
155
|
-
| `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
|
|
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
|
+
| `--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
|
+
| `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` |
|
|
156
159
|
| `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` |
|
|
157
160
|
| `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 |
|
|
158
161
|
| `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/` |
|
|
@@ -161,7 +164,7 @@ When modifying framework components, verify the full scope of related files.
|
|
|
161
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) |
|
|
162
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 |
|
|
163
166
|
| `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 |
|
|
164
|
-
| o ciclo de reuso (`reuse
|
|
167
|
+
| 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` |
|
|
165
168
|
| `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` |
|
|
166
169
|
| `src/lib/detectors/claude-config-detector.js` (`readEnabledPlugins`) e `src/lib/installers/mcp-installer.js` (`getInstalledMcps`) | os QUATRO chamadores de `orchestrateMcpSetup` (`mcp-setup.js` Γ3, `init.js`): chamada sem `enabledPlugins` recria a duplicata `mcp__X` Γ `mcp__plugin_X_X` (canΓ‘rio de fiaΓ§Γ£o em `test/lib/installers/mcp-installer.test.js`). `readEnabledPlugins` Γ© a leitura ΓNICA de "plugin ligado" β o `doctor --procs` a reusa em `procs/mcp-config.js`; o prefixo `plugin_<nome>_` sai de `serverNames` do registry via `mcpServerNames` |
|
|
167
170
|
| `src/lib/procs/**` (`doctor --procs`) | `src/commands/project/doctor-procs.js`, o ramo `--procs` de `doctor.js` (entra DEPOIS do de `--worktrees`, fora das fatias que `test/commands/doctor-ai.test.js` corta), `bin/morph-spec.js` (`--prune` Γ© UMA flag para os dois modos) e `test/commands/doctor-procs.test.js`. **SΓ³ `prune.js` mata, e por PID:** o canΓ‘rio varre o grafo estΓ‘tico do comando (`prune.js` sΓ³ entra por import dinΓ’mico dentro de `if (options.prune)`), reprova escrita, spawn e `process.kill` no caminho de leitura β com exceΓ§Γ£o nomeada ao `execFileSync` de `list.js` β e reprova `taskkill`/`Stop-Process`/`pkill`/`killall`/`/IM` em qualquer cΓ³digo de `src/` e dos hooks. As proteΓ§Γ΅es de `classify.js` e a checagem de PID reaproveitado de `prune.js` tΓͺm teste por mutaΓ§Γ£o sobre processo real: tirar uma delas mata um sleeper de teste. `null` Γ© desconhecido, nunca lista vazia. `MORPH_PROCS_FIXTURE` Γ© o ΓΊnico seam de teste; nada em produΓ§Γ£o o define |
|
|
@@ -172,10 +175,10 @@ When modifying framework components, verify the full scope of related files.
|
|
|
172
175
|
| `src/lib/worktree/worktree-include.js` (`.worktreeinclude`) e o `locked` de `parseWorktreeList` | O hook `WorktreeCreate` **substitui** a criaΓ§Γ£o nativa, e a doc do Claude Code diz que com hook o `.worktreeinclude` "nΓ£o Γ© processado": `copyWorktreeIncludes` Γ© a paridade, chamado por `runWorktreeSetup` e `runWorktreeProvision` (que ainda copia `.env*` por `copyRootEnvFiles`; a uniΓ£o Γ© de propΓ³sito, e o include nΓ£o repete o que o `.env*` jΓ‘ trouxe). Casa o padrΓ£o **e** Γ© ignorado NA ΓRVORE (`check-ignore` lΓ‘: arquivo que casa mas nΓ£o Γ© ignorado sujaria o worktree e o `finish` recusa), nunca `node_modules`/artefato de build (`isDisposableBuildArtifact`, de `ignored-triage.js`)/`.git` aninhado, nunca sobrescreve. Uma segunda noΓ§Γ£o de "o que copiar" faria o nativo e o morph discordarem. `parseWorktreeList` expΓ΅e `locked` (a linha `locked` do porcelain) e o `agent-reaper.js` o lΓͺ: `git worktree lock` Γ© a marca que o Claude Code usa em agente rodando, e a Γ‘rvore travada nunca Γ© tocada (razΓ£o `locked`). O SessionStart segue o `cwd` do payload (`sessionDir` em `worktree-context.js`) sΓ³ para worktree vinculado do MESMO repositΓ³rio β `CLAUDE_PROJECT_DIR` fica na raiz numa sessΓ£o que entra no worktree β, e usa o `isSecondaryWorktree` de `git-paths.js` (a cΓ³pia local comparava `--git-dir` com `--git-common-dir` em formas diferentes e tratava uma SUBPASTA da raiz como worktree). CanΓ‘rios: `test/lib/worktree/worktree-include.test.js`, `test/hooks/worktree-context.test.js`, `test/lib/worktree/agent-reaper.test.js` |
|
|
173
176
|
| Any `framework/` file that calls `morph-spec graph` | The fallback to grep must be declared in the SAME file β `test/framework/graph-fallback.test.js` enforces it (ADR-002: a skill that requires the graph with no fallback is a bug) |
|
|
174
177
|
| `framework/hooks/claude-code/core/worktree-create.js` / `worktree-remove.js` / `block-raw-worktree-add.js` / `worktree-env-inject.js` | `hooks-installer.js` (`MORPH_HOOKS`: eventos `WorktreeCreate`/`WorktreeRemove` + a ORDEM da chain Bash β inject por ΓΊltimo, um comando bloqueado nunca Γ© reescrito), `hooks-scanner.js` `EVENT_KEYS`, `chain.js` (`updatedInput` sΓ³ viaja com rewrite β `permissionDecision:'allow'` incondicional pularia o prompt de todo Bash), `worktree.js` (`runWorktreeProvision`, `--if-clean`, `envKeyForWorktree`), `worktree-audit.js` (`morph-task/*`, `out-of-container`), `worktree-context.js`, MORPH.md Β§2.2, `morph-apply` Β§3b-w, `test/framework/worktree-governance-prose.test.js`, `resident-size.test.js` (MORPH.md estΓ‘ NO teto). **Γncora de sessΓ£o:** `anchorSessionCommand` (`TEARDOWN_RE`) exporta `MORPH_SESSION_PROJECT_DIR`, que `sessionAnchoredInWorktree` (`worktree.js`) lΓͺ e que o `finish` (`--merge`/`--pr` adiam o desmonte) e o `worktree remove` (recusa `session-anchored-in-worktree`) consomem β mudar a chave ou o formato do comando num lado sΓ³ devolve o bug de 48 hooks `MODULE_NOT_FOUND` depois do `finish` |
|
|
175
|
-
| `src/lib/worktree/env-allocator.js` (`portLayout`) | os DOIS escritores do bloco β `buildEnvFileContent` (`.morph/worktree.env`) e `compose-env.js` (`buildComposeEnv`, usado por `e2e up`, pelo nΓ³ `e2e` do `verify`, por `stack-teardown.js` e por `morph-spec env`) β e os leitores `worktree-context.js`, `handoff-generator.js`, `worktree-env-inject.js`, o template `docker-compose.template.yml` (variΓ‘veis que ele publica) e o standard `local-compose-isolation.md` (tabela de offsets). CanΓ‘rio: "worktree.env e compose env concordam byte a byte" em `test/lib/e2e/compose-env.test.js`. Offset novo aqui sem entrar no template Γ© porta que ninguΓ©m publica |
|
|
178
|
+
| `src/lib/worktree/env-allocator.js` (`portLayout`) | os DOIS escritores do bloco β `buildEnvFileContent` (`.morph/worktree.env`) e `compose-env.js` (`buildComposeEnv`, usado por `e2e up`, pelo nΓ³ `e2e` do `verify`, por `stack-teardown.js` e por `morph-spec env`) β e os leitores `worktree-context.js`, `handoff-generator.js`, `worktree-env-inject.js`, o template `docker-compose.template.yml` (variΓ‘veis que ele publica) e o standard `local-compose-isolation.md` (tabela de offsets). CanΓ‘rio: "worktree.env e compose env concordam byte a byte" em `test/lib/e2e/compose-env.test.js`. Offset novo aqui sem entrar no template Γ© porta que ninguΓ©m publica. **Alias por projeto** (`e2e.portEnvNames`, `resolvePortAliases`): Γ© sΓ³ outro NOME para as mesmas quatro portas, entΓ£o nΓ£o cria offset nem toca o template; entra pelo `portLayout` e por isso os dois escritores o levam, o `layoutKeys` do `compose-env.js` o protege do `.env.e2e` herdado, e o hook `worktree-env-inject` injeta toda chave do manifesto (menos `MORPH_FEATURE`) em vez de uma lista literal que divergiria |
|
|
176
179
|
| `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 |
|
|
177
180
|
| `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 |
|
|
178
|
-
| `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
|
|
181
|
+
| `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`) |
|
|
179
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 |
|
|
180
183
|
| `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` |
|
|
181
184
|
| `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 |
|
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.45.0
|
|
7
7
|
**Requires:** Node.js 22+, Claude Code
|
|
8
8
|
|
|
9
9
|
---
|
|
@@ -69,7 +69,7 @@ your-project/
|
|
|
69
69
|
β β βββ {feature}/
|
|
70
70
|
β β βββ 0-proposal/ # Problem statement + acceptance criteria
|
|
71
71
|
β β βββ 1-design/ # UI/UX design (optional, UI-heavy features)
|
|
72
|
-
β β βββ 2-plan/ #
|
|
72
|
+
β β βββ 2-plan/ # usecases/ + spec.md + tasks.json
|
|
73
73
|
β β βββ 3-implement/ # Recap and implementation notes
|
|
74
74
|
β β βββ 4-review/ # Review report + Gate 3
|
|
75
75
|
β βββ state.json # READ-ONLY β managed by CLI only
|
|
@@ -100,7 +100,7 @@ proposal β [uiux] β plan β implement β review
|
|
|
100
100
|
| --------- | --------------- | ------------------------------------------------------------------ |
|
|
101
101
|
| Proposal | `0-proposal/` | `proposal.md`, `stack-scan.md`, `migration-plan.md` (optional) |
|
|
102
102
|
| Design | `1-design/` | `design-system.md`, `mockups.md`, `components.md`, `flows.md` |
|
|
103
|
-
| Plan | `2-plan/` | `
|
|
103
|
+
| Plan | `2-plan/` | `usecases/`, `spec.md`, `tasks.json`, `decisions.md` |
|
|
104
104
|
| Implement | `3-implement/` | `recap.md` + source code |
|
|
105
105
|
| Review | `4-review/` | `review-report.md` |
|
|
106
106
|
|
|
@@ -108,7 +108,7 @@ proposal β [uiux] β plan β implement β review
|
|
|
108
108
|
### Approval gates
|
|
109
109
|
|
|
110
110
|
- **Gate 1 (Proposal)** β Problem statement must be approved before plan.
|
|
111
|
-
- **Gate 2 (Plan)** β
|
|
111
|
+
- **Gate 2 (Plan)** β Use cases + spec + tasks must be approved before implement.
|
|
112
112
|
- **Gate 3 (Review)** β Review report must be approved before archive.
|
|
113
113
|
|
|
114
114
|
In `trust: "auto"` mode (default), gates auto-pass without risk signals. See `framework/MORPH.md` for risk-signal details.
|
|
@@ -229,7 +229,7 @@ morph-spec dispatches 8 flat domain personas β no tiers, no hierarchy β defi
|
|
|
229
229
|
| `ui-designer` | UI/UX, design systems | sonnet |
|
|
230
230
|
| `infra-engineer` | Jobs, deployment, observability | sonnet |
|
|
231
231
|
| `evaluator` | Independent code/architecture review (Gate 3) | opus |
|
|
232
|
-
| `scout` | Read-only codebase diagnosis |
|
|
232
|
+
| `scout` | Read-only codebase diagnosis | sonnet |
|
|
233
233
|
|
|
234
234
|
`morph-spec doctor` detects drift between `agents.json` and the generated `.claude/agents/morph-*.md` files β never hand-edit the generated files.
|
|
235
235
|
|
|
@@ -245,7 +245,7 @@ Guardrail hooks are installed into `.claude/settings.local.json` across 7 event
|
|
|
245
245
|
| `SessionStart` | `worktree-context.js` | Injects feature identity (worktree) or active-feature summary (root) |
|
|
246
246
|
| `PreToolUse` (Write\|Edit) | `protect-spec-files.js`, `enforce-phase-writes.js` | Blocks edits to the frozen plan contract post-Gate-2; enforces phase folders |
|
|
247
247
|
| `PreToolUse` (Bash) | `pre-bash-denylist.js` | Detects destructive shell patterns |
|
|
248
|
-
| `PostToolUse` | `
|
|
248
|
+
| `PostToolUse` | `tasks-json-guard.js`, `trace-autogen.js`, `comment-guard.js` | Guards `tasks.json` plan-contract edits; auto-generates execution traces; blocks added comments the comment standard forbids |
|
|
249
249
|
| `PostToolUse` | `state-sync.js`, `gate-guard.js`, `loop-detect.js` | Syncs feature state; enforces gate ordering; detects repeated-action loops |
|
|
250
250
|
| `Stop` | `validate-completion.js` | Warns about incomplete tasks, missing outputs, or pending approval gates |
|
|
251
251
|
|
|
@@ -263,7 +263,7 @@ Rules are installed to `.claude/rules/` based on your detected stack (`stacks:`
|
|
|
263
263
|
|
|
264
264
|
| Rule file | Applies to | Stack |
|
|
265
265
|
| ----------------------------- | ------------------------------------------------- | --------- |
|
|
266
|
-
| `morph-workflow.md` | Always active β spec-first
|
|
266
|
+
| `morph-workflow.md` | Always active β spec-first workflow | any |
|
|
267
267
|
| `csharp-standards.md` | `**/*.cs`, `**/*.csproj` | dotnet |
|
|
268
268
|
| `frontend-standards.md` | `**/*.tsx`, `**/*.css`, `**/*.scss` | nextjs |
|
|
269
269
|
| `nextjs-standards.md` | Next.js-specific patterns | nextjs |
|
|
@@ -308,12 +308,12 @@ After updating, run `morph-spec doctor` to confirm the installation is healthy.
|
|
|
308
308
|
|
|
309
309
|
**Never edit protected files directly:**
|
|
310
310
|
|
|
311
|
-
- `.morph/state.json` β derived automatically from `feature.json`; use `morph-spec approve`/`advance`/`
|
|
311
|
+
- `.morph/state.json` β derived automatically from `feature.json`; use `morph-spec approve`/`advance`/`gate-decision` to change feature state
|
|
312
312
|
- `.morph/framework/` β read-only, updated by `morph-spec update`
|
|
313
313
|
|
|
314
314
|
**Always document decisions.** Each feature can have a `2-plan/decisions.md`. Architectural choices go there, not in commit messages.
|
|
315
315
|
|
|
316
|
-
**
|
|
316
|
+
**Verify runs after every task.** `morph-spec verify` runs the build + test + validator chain relevant to your stack; a task closes when it is green and each `doneCriteria` has the evidence that proves it β red triggers an autonomous correction loop rather than a pause. Gate 3 is an independent evaluator that answers SHIP or FIX with evidence-typed blockers.
|
|
317
317
|
|
|
318
318
|
---
|
|
319
319
|
|
|
@@ -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.45.0 by [Polymorphism Tech](https://polymorphism.tech)*
|
package/bin/morph-spec.js
CHANGED
|
@@ -44,6 +44,7 @@ const envCommand = lazy('../src/commands/project/env.js', 'envCommand');
|
|
|
44
44
|
const claimCommand = lazy('../src/commands/project/claim.js', 'claimCommand');
|
|
45
45
|
const standardsCommand = lazy('../src/commands/project/standards.js', 'standardsCommand');
|
|
46
46
|
const reportCheckCommand = lazy('../src/commands/state/report-check.js', 'reportCheckCommand');
|
|
47
|
+
const scoutCheckCommand = lazy('../src/commands/validation/scout-check.js', 'scoutCheckCommand');
|
|
47
48
|
|
|
48
49
|
// === Approval gates (human-only) ===
|
|
49
50
|
const approveCommand = lazy('../src/commands/state/approve.js', 'approveCommand');
|
|
@@ -52,7 +53,6 @@ const unapproveCommand = lazy('../src/commands/state/approve.js', 'unapproveComm
|
|
|
52
53
|
const advanceCommand = lazy('../src/commands/state/advance.js', 'advanceCommand');
|
|
53
54
|
const archiveCommand = lazy('../src/commands/state/archive.js', 'archiveCommand');
|
|
54
55
|
const finishCommand = lazy('../src/commands/state/finish.js', 'finishCommand');
|
|
55
|
-
const scoreCommand = lazy('../src/commands/state/score.js', 'scoreCommand');
|
|
56
56
|
const gateDecisionCommand = lazy('../src/commands/state/gate-decision.js', 'gateDecisionCommand');
|
|
57
57
|
const gateCheckCommand = lazy('../src/commands/state/gate-check.js', 'gateCheckCommand');
|
|
58
58
|
const createCommand = lazy('../src/commands/state/create.js', 'createCommand');
|
|
@@ -251,7 +251,7 @@ program
|
|
|
251
251
|
program
|
|
252
252
|
.command('gate-check <feature> <gate>')
|
|
253
253
|
.description('Decide + record a gate\'s trust decision from disk-derived signals (shouldPauseGate) β the deterministic default; see gate-decision for the manual escape hatch')
|
|
254
|
-
.option('--signals <json>', 'Override ONLY standardsViolations/designSystemDivergence
|
|
254
|
+
.option('--signals <json>', 'Override ONLY standardsViolations/designSystemDivergence (not derivable from disk) β everything else is read from feature.json/tasks.json/git and cannot be overridden here')
|
|
255
255
|
.option('--trust <mode>', 'Trust mode to decide with (default: config.json β trust, or "auto")')
|
|
256
256
|
.option('--json', 'Output as JSON')
|
|
257
257
|
.action((feature, gateArg, options) => gateCheckCommand(feature, gateArg, options));
|
|
@@ -261,7 +261,7 @@ program
|
|
|
261
261
|
.description('Record the trust decision for a gate in feature.json (gateDecisions, server-side timestamp)')
|
|
262
262
|
.option('--pause <bool>', 'Whether the gate must pause for a human (true|false) β required')
|
|
263
263
|
.option('--reason <text>', 'Why this decision was taken (defaults by pause value)')
|
|
264
|
-
.option('--signals <json>', 'Risk signals as a JSON object, e.g. \'{"taskCount":12
|
|
264
|
+
.option('--signals <json>', 'Risk signals as a JSON object, e.g. \'{"taskCount":12}\'')
|
|
265
265
|
.option('--trust <mode>', 'Trust mode used for the decision (default: config.json β trust, or "auto")')
|
|
266
266
|
.option('--json', 'Output as JSON')
|
|
267
267
|
.action((feature, gateArg, options) => gateDecisionCommand(feature, gateArg, options));
|
|
@@ -280,16 +280,6 @@ program
|
|
|
280
280
|
.option('--json', 'Output as JSON')
|
|
281
281
|
.action((feature, options) => createCommand(feature, options));
|
|
282
282
|
|
|
283
|
-
program
|
|
284
|
-
// `[score]` is declared optional ONLY so a missing value reaches scoreCommand and gets
|
|
285
|
-
// a message that names the argument and the positional order, instead of Commander's
|
|
286
|
-
// raw "missing required argument". The value stays required β a blank one is refused.
|
|
287
|
-
.command('score <feature> <task> [score]')
|
|
288
|
-
.description('Record a task eval score in feature.json (taskScores, server-side timestamp) β <task> must match T<n> and <score> must be a number in 0-10; a blank/missing score is refused, never recorded as 0')
|
|
289
|
-
.option('--dims <dims...>', 'Four dimension scores in order a,c,q,t (architecture,contracts,code-quality,test-coverage) β comma- or space-separated; quote under PowerShell: --dims "9.5,9,9,9"')
|
|
290
|
-
.option('--json', 'Output as JSON')
|
|
291
|
-
.action((feature, task, score, options) => scoreCommand(feature, task, score, options));
|
|
292
|
-
|
|
293
283
|
program
|
|
294
284
|
.command('retype <feature> <newType>')
|
|
295
285
|
.description('Change a feature work type mid-flight (feature|bug|chore|hotfix) β resets only policy-stamped gates, preserves human approvals')
|
|
@@ -643,6 +633,13 @@ program
|
|
|
643
633
|
.option('--json', 'Output as JSON')
|
|
644
634
|
.action((feature, tasks, options) => reportCheckCommand(feature, tasks.length > 1 ? tasks : tasks[0], options));
|
|
645
635
|
|
|
636
|
+
program
|
|
637
|
+
.command('scout-check <map>')
|
|
638
|
+
.description('Audit a scout map: every path:line citation must exist and carry the literal line it quotes (Β±2 lines); absence/liveness verdicts only as hypotheses with the search command and its raw count. Exits 0 only when nothing failed')
|
|
639
|
+
.option('--root <dir>', 'Tree the citations are relative to (default: top of the current worktree)')
|
|
640
|
+
.option('--json', 'Output as JSON')
|
|
641
|
+
.action((map, options) => scoutCheckCommand(map, options));
|
|
642
|
+
|
|
646
643
|
// βββ Dashboard ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
|
647
644
|
program
|
|
648
645
|
.command('dashboard')
|