@polymorphism-tech/morph-spec 9.0.0 → 9.1.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 +8 -9
- package/README.md +38 -36
- package/bin/morph-spec.js +4 -62
- package/bin/validate.js +1 -39
- package/claude-plugin.json +1 -1
- package/docs/CHEATSHEET.md +1 -1
- package/docs/QUICKSTART.md +1 -1
- package/framework/CLAUDE.md +1 -1
- package/framework/MORPH.md +1 -1
- package/framework/agents.json +12 -4
- package/framework/commands/morph-apply.md +1 -1
- package/framework/commands/morph-status.md +1 -1
- package/framework/docs/CLI.md +6 -17
- package/framework/hooks/claude-code/core/block-destructive-worktree-removal.js +6 -49
- package/framework/hooks/claude-code/core/block-raw-worktree-add.js +2 -35
- package/framework/hooks/claude-code/core/chain.js +4 -65
- package/framework/hooks/claude-code/core/comment-guard.js +2 -16
- package/framework/hooks/claude-code/core/enforce-phase-writes.js +6 -50
- package/framework/hooks/claude-code/core/gate-guard.js +7 -67
- package/framework/hooks/claude-code/core/graph-refresh-kick.js +3 -24
- package/framework/hooks/claude-code/core/heavy-gate.js +3 -21
- package/framework/hooks/claude-code/core/heavy-release.js +2 -13
- package/framework/hooks/claude-code/core/loop-detect.js +2 -31
- package/framework/hooks/claude-code/core/pre-bash-denylist.js +0 -4
- package/framework/hooks/claude-code/core/protect-spec-files.js +7 -67
- package/framework/hooks/claude-code/core/state-sync.js +11 -67
- package/framework/hooks/claude-code/core/telemetry-log.js +12 -93
- package/framework/hooks/claude-code/core/validate-completion.js +11 -57
- package/framework/hooks/claude-code/core/warn-bash-source-write.js +1 -9
- package/framework/hooks/claude-code/core/worktree-context.js +10 -110
- package/framework/hooks/claude-code/core/worktree-create.js +6 -70
- package/framework/hooks/claude-code/core/worktree-env-inject.js +5 -56
- package/framework/hooks/claude-code/core/worktree-remove.js +5 -31
- package/framework/hooks/shared/activity-logger.js +9 -89
- package/framework/hooks/shared/agent-reap.js +2 -17
- package/framework/hooks/shared/build-check.js +6 -75
- package/framework/hooks/shared/claim-registry.js +9 -119
- package/framework/hooks/shared/command-tokens.js +6 -51
- package/framework/hooks/shared/comment-rules.js +5 -20
- package/framework/hooks/shared/comment-scanner.js +6 -25
- package/framework/hooks/shared/comment-stripper.js +11 -85
- package/framework/hooks/shared/e2e-check.js +0 -0
- package/framework/hooks/shared/errata.js +2 -15
- package/framework/hooks/shared/feature-locator.js +11 -95
- package/framework/hooks/shared/frontend-locations.js +1 -3
- package/framework/hooks/shared/gate-question.js +1 -7
- package/framework/hooks/shared/git-exclude.js +3 -20
- package/framework/hooks/shared/git-paths.js +8 -104
- package/framework/hooks/shared/graph-freshness.js +22 -166
- package/framework/hooks/shared/graph-refresh.js +21 -149
- package/framework/hooks/shared/heavy-command.js +4 -12
- package/framework/hooks/shared/hook-response.js +8 -83
- package/framework/hooks/shared/morph-cli.js +8 -52
- package/framework/hooks/shared/payload-utils.js +10 -132
- package/framework/hooks/shared/phase-utils.js +1 -42
- package/framework/hooks/shared/redact-secrets.js +4 -15
- package/framework/hooks/shared/reparse-scan.js +6 -66
- package/framework/hooks/shared/slice-progress.js +4 -23
- package/framework/hooks/shared/source-files.js +4 -17
- package/framework/hooks/shared/state-reader.js +13 -183
- package/framework/hooks/shared/state-writer.js +8 -52
- package/framework/hooks/shared/stdin-reader.js +1 -11
- package/framework/hooks/shared/telemetry-logger.js +7 -67
- package/framework/hooks/shared/transcript-usage.js +7 -29
- package/framework/hooks/shared/worktree-root.js +7 -29
- package/framework/rules/csharp-standards.md +1 -1
- package/framework/rules/frontend-standards.md +1 -0
- package/framework/rules/hangfire-standards.md +1 -1
- package/framework/rules/neon-standards.md +1 -1
- 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 +2 -2
- package/framework/skills/core/morph-hotfix/SKILL.md +1 -1
- package/framework/skills/core/morph-machine/SKILL.md +1 -1
- package/framework/skills/core/morph-media/SKILL.md +1 -1
- package/framework/skills/core/morph-plan/SKILL.md +2 -2
- package/framework/skills/core/morph-review/SKILL.md +1 -1
- package/framework/skills/core/morph-standards/SKILL.md +1 -1
- package/framework/skills/core/morph-uiux/SKILL.md +29 -12
- package/framework/standards/STANDARDS.json +34 -12
- package/framework/standards/STANDARDS.meta.json +16 -2
- package/framework/standards/ai-agents/agent-archetypes.md +2 -2
- package/framework/standards/ai-agents/agent-session.md +1 -1
- package/framework/standards/ai-agents/conversational-agent-with-phases.md +4 -4
- package/framework/standards/ai-agents/durable-workflows-hitl.md +3 -3
- package/framework/standards/ai-agents/guardrails.md +1 -1
- package/framework/standards/ai-agents/multi-agent-patterns.md +2 -2
- package/framework/standards/ai-agents/observability-patterns.md +1 -1
- package/framework/standards/ai-agents/production.md +1 -1
- package/framework/standards/ai-agents/prompt-sources.md +2 -2
- package/framework/standards/ai-agents/providers/model-registry.md +1 -1
- package/framework/standards/ai-agents/rag-custom-pgvector.md +2 -2
- package/framework/standards/ai-agents/setup.md +2 -2
- package/framework/standards/ai-agents/sweet-spot.md +2 -2
- package/framework/standards/ai-agents/tool-adapters.md +0 -2
- package/framework/standards/ai-agents/vector-data-extensions.md +4 -4
- package/framework/standards/ai-agents/workflows.md +1 -1
- package/framework/standards/architecture/code-comments.md +1 -1
- package/framework/standards/architecture/vertical-slice/vertical-slice.md +1 -1
- package/framework/standards/backend/api/validation.md +1 -1
- package/framework/standards/backend/dotnet/vsa-handler-patterns.md +5 -5
- package/framework/standards/backend/integrations/hangfire/hangfire-jobs.md +1 -1
- package/framework/standards/backend/integrations/neon-auth/neon-auth.md +2 -2
- package/framework/standards/data/nosql/cache/redis.md +1 -1
- package/framework/standards/data/nosql/cosmos-db.md +3 -3
- package/framework/standards/frontend/admin-app/forms.md +1 -1
- package/framework/standards/frontend/design-system/art-direction.md +88 -0
- package/framework/standards/frontend/design-system/premium-finish.md +77 -2
- package/framework/standards/infrastructure/neon/neon-pgvector.md +5 -5
- package/framework/templates/CLAUDE.md.core.template +3 -3
- package/framework/templates/REGISTRY.json +9 -9
- package/framework/templates/docs/ui-mockups.md +4 -0
- package/package.json +1 -1
- package/src/commands/context/context-refresh.js +0 -16
- package/src/commands/dashboard/dashboard.js +2 -8
- package/src/commands/e2e/e2e.js +17 -104
- package/src/commands/graph/affected.js +0 -14
- package/src/commands/graph/build.js +11 -50
- package/src/commands/graph/explain.js +0 -16
- package/src/commands/graph/status.js +1 -12
- package/src/commands/mcp/mcp-setup.js +4 -62
- package/src/commands/observability/cost.js +5 -57
- package/src/commands/observability/telemetry.js +1 -14
- package/src/commands/project/branch.js +4 -45
- package/src/commands/project/claim.js +4 -48
- package/src/commands/project/doctor.js +48 -302
- package/src/commands/project/env.js +1 -27
- package/src/commands/project/fleet.js +3 -32
- package/src/commands/project/init.js +4 -68
- package/src/commands/project/install-plugin-cmd.js +0 -1
- package/src/commands/project/standards.js +1 -62
- package/src/commands/project/status.js +0 -53
- package/src/commands/project/update.js +31 -239
- package/src/commands/project/worktree.js +108 -882
- package/src/commands/scaffold/scaffold.js +0 -25
- package/src/commands/state/advance.js +14 -85
- package/src/commands/state/approve.js +14 -96
- package/src/commands/state/archive.js +12 -64
- package/src/commands/state/auto-approval.js +2 -5
- package/src/commands/state/create.js +22 -109
- package/src/commands/state/delete.js +11 -63
- package/src/commands/state/finish.js +98 -475
- package/src/commands/state/gate-check.js +22 -132
- package/src/commands/state/gate-decision.js +3 -39
- package/src/commands/state/handoff.js +3 -30
- package/src/commands/state/list.js +14 -84
- package/src/commands/state/report-check.js +8 -58
- package/src/commands/state/retype.js +4 -26
- package/src/commands/validation/index.js +0 -3
- package/src/commands/validation/mutate.js +2 -5
- package/src/commands/validation/validate-feature.js +5 -94
- package/src/commands/validation/verify.js +15 -98
- package/src/core/paths/output-schema.js +2 -101
- package/src/core/state/feature-identity.js +1 -25
- package/src/core/state/gate-order.js +0 -27
- package/src/core/state/lifecycle-audit.js +8 -100
- package/src/core/state/state-manager.js +22 -304
- package/src/core/templates/template-registry.js +2 -97
- package/src/core/templates/template-renderer.js +13 -93
- package/src/core/templates/template-seed.js +0 -1
- package/src/core/work-types.js +1 -36
- package/src/dashboard/app/App.jsx +1 -8
- package/src/dashboard/app/components/ActivityFeed.jsx +0 -5
- package/src/dashboard/app/components/CommandPalette.jsx +0 -7
- package/src/dashboard/app/components/DetailPanel.jsx +1 -5
- package/src/dashboard/app/components/EntityLink.jsx +0 -12
- package/src/dashboard/app/components/ErrorBoundary.jsx +1 -8
- package/src/dashboard/app/components/KnowledgeMarkdown.jsx +0 -11
- package/src/dashboard/app/components/Markdown.jsx +0 -9
- package/src/dashboard/app/components/PhaseRail.jsx +0 -11
- package/src/dashboard/app/components/SearchFilter.jsx +0 -4
- package/src/dashboard/app/components/Sidebar.jsx +0 -5
- package/src/dashboard/app/hooks/useMorphQuery.js +1 -11
- package/src/dashboard/app/hooks/usePref.js +1 -5
- package/src/dashboard/app/lib/format.js +0 -2
- package/src/dashboard/app/lib/phases.js +1 -5
- package/src/dashboard/app/lib/project.js +0 -12
- package/src/dashboard/app/pages/AgentMap.jsx +1 -8
- package/src/dashboard/app/pages/FeatureDetail.jsx +1 -5
- package/src/dashboard/app/pages/Features.jsx +1 -8
- package/src/dashboard/app/pages/Guide.jsx +0 -1
- package/src/dashboard/app/pages/HookInspector.jsx +2 -23
- package/src/dashboard/app/pages/PhasePipeline.jsx +0 -3
- package/src/dashboard/app/pages/Portfolio.jsx +0 -1
- package/src/dashboard/app/pages/RulesMap.jsx +1 -16
- package/src/dashboard/app/pages/SkillsLibrary.jsx +1 -16
- package/src/dashboard/app/pages/Standards.jsx +2 -17
- package/src/dashboard/scanner/activity-scanner.js +10 -69
- package/src/dashboard/scanner/agents-scanner.js +1 -23
- package/src/dashboard/scanner/docs-scanner.js +0 -6
- package/src/dashboard/scanner/feature-status.js +2 -5
- package/src/dashboard/scanner/features-scanner.js +10 -66
- package/src/dashboard/scanner/hooks-scanner.js +5 -28
- package/src/dashboard/scanner/index.js +0 -4
- package/src/dashboard/scanner/portfolio.js +5 -29
- package/src/dashboard/scanner/project-api.js +8 -54
- package/src/dashboard/scanner/rules-scanner.js +1 -2
- package/src/dashboard/scanner/skills-scanner.js +2 -12
- package/src/dashboard/scanner/standards-scanner.js +2 -6
- package/src/dashboard/vite-plugin-morph-data.js +5 -19
- package/src/lib/ai/ai-checks.js +7 -52
- package/src/lib/ai/ai-project-scan.js +14 -108
- package/src/lib/ai/code-audit.js +7 -63
- package/src/lib/ai/pin-drift.js +8 -51
- package/src/lib/ai/registry-audit.js +11 -90
- package/src/lib/ci/ci-verify.js +4 -19
- package/src/lib/ci/github-ci.js +9 -37
- package/src/lib/ci/workflow-scaffold.js +2 -10
- package/src/lib/detectors/claude-config-detector.js +13 -74
- package/src/lib/detectors/index.js +4 -50
- package/src/lib/e2e/compose-env.js +5 -66
- package/src/lib/e2e/stack-teardown.js +6 -40
- package/src/lib/generators/architecture-scanner.js +18 -156
- package/src/lib/generators/handoff-generator.js +9 -53
- package/src/lib/generators/scaffold-generator.js +5 -101
- package/src/lib/generators/settings-generator.js +4 -37
- package/src/lib/git/branch-classifier.js +11 -84
- package/src/lib/git/default-branch.js +14 -169
- package/src/lib/git/evidence-staleness.js +1 -0
- package/src/lib/git/gh.js +2 -17
- package/src/lib/git/pending-prs.js +6 -37
- package/src/lib/git/pr-files.js +3 -28
- package/src/lib/git/pr-index.js +3 -34
- package/src/lib/git/tree-fingerprint.js +3 -15
- package/src/lib/git/tree-state.js +6 -33
- package/src/lib/graph/god-nodes.js +2 -22
- package/src/lib/graph/graph-health.js +3 -24
- package/src/lib/graph/graph-loader.js +5 -56
- package/src/lib/graph/graphify-runtime.js +7 -37
- package/src/lib/graph/graphifyignore.js +8 -74
- package/src/lib/graph/sanitize.js +16 -136
- package/src/lib/heavy/cleanup.js +1 -1
- package/src/lib/installers/effects.js +8 -93
- package/src/lib/installers/mcp-installer.js +9 -169
- package/src/lib/installers/memory-installer.js +4 -27
- package/src/lib/installers/templates-reconcile.js +5 -60
- package/src/lib/mutation/dotnet-introspection.js +20 -106
- package/src/lib/mutation/subset.js +1 -4
- package/src/lib/plan/artifact-vocabulary.js +5 -25
- package/src/lib/plan/checks.js +5 -31
- package/src/lib/plan/plan-parser.js +1 -6
- package/src/lib/projects/registry.js +7 -44
- package/src/lib/review-reports.js +5 -23
- package/src/lib/risk-detector.js +39 -173
- package/src/lib/specs/adr-index.js +6 -34
- package/src/lib/specs/frontmatter.js +2 -15
- package/src/lib/specs/gfm-table.js +1 -7
- package/src/lib/specs/revocations.js +1 -14
- package/src/lib/specs/screen-checklist.js +47 -124
- package/src/lib/stack/dotnet-build-ignores.js +2 -6
- package/src/lib/stack/stack-profile.js +1 -28
- package/src/lib/stack/stack-scan.js +1 -8
- package/src/lib/stack-filter.js +4 -27
- package/src/lib/standards/registry.js +1 -7
- package/src/lib/standards/sections.js +2 -13
- package/src/lib/standards/verified-against.js +19 -191
- package/src/lib/tasks/eval-node.js +21 -169
- package/src/lib/tasks/linux-runner.js +18 -142
- package/src/lib/tasks/test-nodes.js +43 -240
- package/src/lib/tasks/test-runner.js +43 -341
- package/src/lib/telemetry/metrics.js +7 -36
- package/src/lib/telemetry/telemetry-reader.js +13 -125
- package/src/lib/telemetry/verify-event.js +2 -30
- package/src/lib/validators/code-budget/code-budget-validator.js +3 -43
- package/src/lib/validators/code-budget/index.js +0 -7
- package/src/lib/validators/contracts/contract-compliance-validator.js +2 -24
- package/src/lib/validators/contracts/index.js +0 -6
- package/src/lib/validators/nextjs/index.js +0 -5
- package/src/lib/validators/nextjs/next-component-validator.js +4 -81
- package/src/lib/validators/packages/index.js +0 -6
- package/src/lib/validators/packages/package-validator.js +6 -56
- package/src/lib/validators/reuse/index.js +0 -8
- package/src/lib/validators/reuse/reuse-validator.js +11 -101
- package/src/lib/validators/reuse/symbol-scanner.js +6 -62
- package/src/lib/validators/traceability-check.js +5 -63
- package/src/lib/validators/validation-runner.js +20 -125
- package/src/lib/validators/verify-runner.js +52 -358
- package/src/lib/work-type-detector.js +5 -67
- package/src/lib/worktree/docker-audit.js +17 -136
- package/src/lib/worktree/env-allocator.js +10 -98
- package/src/lib/worktree/feature-truth.js +12 -115
- package/src/lib/worktree/finish-backup.js +32 -242
- package/src/lib/worktree/fleet-scanner.js +13 -95
- package/src/lib/worktree/ignored-triage.js +15 -91
- package/src/lib/worktree/vscode-settings.js +2 -3
- package/src/lib/worktree/worktree-audit.js +21 -213
- package/src/lib/worktree/worktree-include.js +3 -5
- package/src/scripts/global-install.js +3 -9
- package/src/scripts/install-plugin.js +1 -13
- package/src/scripts/setup-infra.js +17 -147
- package/src/utils/agents-installer.js +28 -275
- package/src/utils/banner.js +4 -25
- package/src/utils/claude-md-injector.js +1 -37
- package/src/utils/claude-settings-manager.js +1 -52
- package/src/utils/file-copier.js +11 -95
- package/src/utils/hooks-installer.js +21 -298
- package/src/utils/skills-installer.js +4 -101
- package/src/utils/version-checker.js +4 -83
- package/framework/commands/morph-proposal.md +0 -9
- package/src/commands/utils/index.js +0 -3
- package/src/lib/detectors/design-system-detector.js +0 -188
- package/src/lib/generators/design-system-generator.js +0 -508
- package/src/lib/specs/old-format.js +0 -68
- package/src/utils/agent-id-mapper.js +0 -11
package/CLAUDE.md
CHANGED
|
@@ -34,7 +34,7 @@ npm run docs
|
|
|
34
34
|
npm run generate
|
|
35
35
|
```
|
|
36
36
|
|
|
37
|
-
No build step — pure ESM JavaScript, runs directly via Node.js
|
|
37
|
+
No build step — pure ESM JavaScript, runs directly via Node.js 22+.
|
|
38
38
|
|
|
39
39
|
## Architecture
|
|
40
40
|
|
|
@@ -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/` — Validators run by the `verify` validators node (code-budget, contracts, nextjs, packages, reuse)
|
|
52
|
-
- `generators/` — Code generation (
|
|
52
|
+
- `generators/` — Code generation (architecture context, handoff, scaffold, settings)
|
|
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)
|
|
@@ -79,7 +79,7 @@ No build step — pure ESM JavaScript, runs directly via Node.js 18+.
|
|
|
79
79
|
|
|
80
80
|
**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.
|
|
81
81
|
|
|
82
|
-
**Personas, not tiers:** `agents.json` has 10 flat personas — 6 domain implementers (dotnet-senior, ef-modeler, maf-expert, nextjs-expert, ui-designer, infra-engineer), the 3 read-only Gate 3 roles (verifier, reviewer, security) and 1 stage persona (`scout`, read-only). `model` is sized per stage: `sonnet` for implementers, `scout`, `verifier` and `reviewer`; `security` is `opus`; the dispatch routes the verifier to `opus` when `verifierModelFor` (risk-detector.js, read by `report-check --before-eval`) sees prompt/model or media (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.
|
|
82
|
+
**Personas, not tiers:** `agents.json` has 10 flat personas — 6 domain implementers (dotnet-senior, ef-modeler, maf-expert, nextjs-expert, ui-designer, infra-engineer), the 3 read-only Gate 3 roles (verifier, reviewer, security) and 1 stage persona (`scout`, read-only). `model` is sized per stage: `sonnet` for implementers, `scout`, `verifier` and `reviewer`; `security` and `ui-designer` (design quality is the deliverable, high effort) are `opus`; the dispatch routes the verifier to `opus` when `verifierModelFor` (risk-detector.js, read by `report-check --before-eval`) sees prompt/model or media (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.
|
|
83
83
|
|
|
84
84
|
### Phase Sequence
|
|
85
85
|
|
|
@@ -96,7 +96,7 @@ Authoritative, committed state: `.morph/features/{f}/feature.json` (status, appr
|
|
|
96
96
|
## Testing Conventions
|
|
97
97
|
|
|
98
98
|
- **Framework:** Node.js native `node:test` — no Jest or Mocha
|
|
99
|
-
- **Concurrency:** files run in parallel (`scripts/run-tests.mjs`) — `node --test` gives each file its own process, so `process.chdir()` stays inside the file. A test
|
|
99
|
+
- **Concurrency:** files run in parallel (`scripts/run-tests.mjs`) — `node --test` gives each file its own process, so `process.chdir()` stays inside the file. A test never writes a tracked file of this repo: a script under test reads its root from `MORPH_REPO_ROOT`, and the test points it at `copyTrackedRepo()` (`test/helpers/repo-copy.js`, a temp git repo with the tracked files). The runner fails if a run changes the tracked tree
|
|
100
100
|
- **Subprocesses:** drive the CLI through `test/helpers/cli.js` (`runCli`, no shell) and git through `test/helpers/git.js` (`git(cwd, ...args)`) — `execSync('git …')` pays a cmd.exe per call on Windows. Hooks that export `run(payload)` can be called in-process (see `test/hooks/loop-detect.test.js`); keep one spawn per hook for the standalone wiring
|
|
101
101
|
- **Temp dirs:** `createTempDir()` / `cleanupTempDir()` from `test/helpers/test-utils.js`
|
|
102
102
|
- **Home:** a suite that runs `setupInfra`/`updateCommand`/`initCommand` calls `isolateHome()` (test-utils) at the top — the installers write `~/.claude/settings.json` through `os.homedir()`. Canary: `test/meta/home-isolation.test.js`
|
|
@@ -118,7 +118,7 @@ When modifying framework components, verify the full scope of related files.
|
|
|
118
118
|
|
|
119
119
|
| When you modify... | Also verify... |
|
|
120
120
|
|----------------------------------|-------------------------------------------------------------------------------------------|
|
|
121
|
-
| `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 o corpo da persona manda gravar é lido por `PARTIAL_MARKER` de `framework/hooks/shared/slice-progress.js` (no `report-check`: `report-incomplete`, ação `resume`; no progresso: fatia `parcial`): 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
|
|
121
|
+
| `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 o corpo da persona manda gravar é lido por `PARTIAL_MARKER` de `framework/hooks/shared/slice-progress.js` (no `report-check`: `report-incomplete`, ação `resume`; no progresso: fatia `parcial`): 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`, menos a `ui-designer` (`high`, com `opus`: a qualidade visual é a entrega); `verifier`, `reviewer` e `security` sem o campo — o Gate 3 não é barateado. `reviewer`/`security` são read-only sobre o SHA (`git diff {base}..{sha}`, nunca a árvore): o corpo da persona diz isso, e é o que torna seguro o paralelo com a mutação do verificador (F46). **`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` usa para somar a lista em `standards:` da fatia. **Forma do corpo (`buildBody`):** só a persona no formato do 5.3.0 — implementadora (Write/Edit), sem `effort` e sem `surfaces` (`legacyShape`) — mantém `## Domains` e a lista de standards sem a linha de busca; no `agents.json` atual NENHUMA cai nesse caso (todo implementador tem `effort`; `verifier`/`reviewer`/`security` são read-only), e o formato vive pela fixture COMPAT acima. Toda outra persona perde `## Domains`, ganha `STANDARDS_HINT` (`--find <id>` e `--show <id>#<âncora>`) no topo de `## Standards to consult on demand` e, com `neon` no `mcp` (hoje `ef-modeler` e `infra-engineer`), o aviso `NEON_CAUTION` (ensaio de migração em branch descartável; tool destrutiva só com confirmação humana). Canários: `test/utils/agents-installer.test.js` e `test/framework/maf-expert-persona.test.js` |
|
|
122
122
|
| `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 |
|
|
123
123
|
| `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 |
|
|
124
124
|
| `scripts/generate-standards-registry.js` | Escreve DOIS arquivos: `framework/standards/STANDARDS.json` (o que o runtime lê: `id`, `name`, `path`, `category`, `stacks`, `tags`, `anchors`, `digest`) e o sidecar `STANDARDS.meta.json` (`aliases`, `verifiedAgainst`, `provedBy` por **id**, e `sharedAliases` no topo). REGENERE os dois, nunca patch textual. Os `aliases` do sidecar se preservam pelo id da entrada: renomear um id sem levar a chave do sidecar perde os aliases dele. `--check` compara os DOIS arquivos com o que geraria (drift em qualquer um reprova o `npm run generate:check`). As âncoras são validadas contra os headings pelo `headingSlugs` de `src/lib/standards/sections.js`, a mesma resolução do `--show`. `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 |
|
|
@@ -149,13 +149,13 @@ When modifying framework components, verify the full scope of related files.
|
|
|
149
149
|
| `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`) |
|
|
150
150
|
| `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 |
|
|
151
151
|
| `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 |
|
|
152
|
-
| `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, um relatório de fatia
|
|
152
|
+
| `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, um relatório de fatia) 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). `treeFingerprint` (`tree-fingerprint.js`) segue a MESMA régua (árvore commitada sem `.morph/`, `config.json` sem `expectedCount`) para `repeatOf` e para o `--ci` sem CI reaproveitar o stamp verde fresco; mudar uma sem a outra faz o reaproveitamento e o gate discordarem. 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` |
|
|
153
153
|
| `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 |
|
|
154
154
|
| `framework/hooks/shared/slice-progress.js` (progresso e relatório de fatia) | A régua ÚNICA de "em que pé está a fatia" (`sliceProgress`: relatório ausente = pendente, `PARTIAL_MARKER` na 1ª linha = parcial, senão feito) e de "o relatório está completo" (`inspectReport`/`REPORT_MARKERS`: prosa, `**Arquivos:**`, cada `C{n}` de `cobre:`). Leitores: `report-check.js` (reexporta), o Stop hook `validate-completion.js` (`auditSliceReports`), `worktree-context.js`, `handoff-generator.js`/`handoff.js`, `fleet-scanner.js` (pelo `git show` da branch da feature), `status.js` e o `features-scanner.js` do dashboard — nenhum reinterpreta relatório nem tem status próprio de fatia. O `statusline.py` é Python e carrega cópia do marcador: o canário de paridade em `test/hooks/statusline.test.js` a compara com a de JS. O exemplo de relatório da `morph-apply` passa pelos `REPORT_MARKERS` (`test/framework/slice-report-format-rule.test.js`), o Stop hook diz o que falta em cada marca (`MISSING_HINT`), e o do corpo da persona pelo `inspectReport` (`test/framework/morph-apply-skill.test.js`) |
|
|
155
155
|
| `src/lib/git/tree-state.js` (o bloco `tree` do `report-check`) | `report-check.js` (toda saída, inclusive o batch `report-check <f> F1 F2`, e a foto `--before-eval` em `4-review/tree-before-eval.json`) e as skills que leem os campos: `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 verificador commitou também aparece. Fora de git o bloco é `null`, nunca árvore vazia. `MUTATION_MARKER` tem a mesma grafia que as skills mandam gravar |
|
|
156
156
|
| `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) |
|
|
157
157
|
| `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`) — 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` (capturas medidas em `test/fixtures/test-output/`). `isInside` e `canonicalPath` também são lidos por `mutation/dotnet-introspection.js`, pelo registro de projetos e pelo dashboard — um segundo canonicalizador é como dois leitores passam a discordar sobre o mesmo path |
|
|
158
|
-
| `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` do `verifier.md`, lida por `verifierScreenChecklist` (`review-reports.js`) — ú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 `framework/evals/review-checklist.md` (seção "Verificador", "Contrato de tela") prescreve. A `Superfície` é validada contra as chaves de `surfaces` da `ui-designer` (`knownSurfaces`) — é a chave que o `morph-plan` §5 (`superfície:` da fatia) usa para somar standards |
|
|
158
|
+
| `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` do `verifier.md`, lida por `verifierScreenChecklist` (`review-reports.js`) — ú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. Na uiux o sinal também leva `prototypes` (`auditPrototypes`: a linha `**Protótipo:**` da tela, o HTML e os prints `-1280`/`-390` ao lado), lido pelo MESMO `uiuxChecklistPause` depois do checklist — o template `ui-mockups.md` e a `morph-uiux` §3 ensinam essa linha. 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 `framework/evals/review-checklist.md` (seção "Verificador", "Contrato de tela") prescreve. A `Superfície` é validada contra as chaves de `surfaces` da `ui-designer` (`knownSurfaces`) — é a chave que o `morph-plan` §5 (`superfície:` da fatia) usa para somar standards |
|
|
159
159
|
| `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 ("Scout"), `morph-plan` §4, `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` |
|
|
160
160
|
| `src/lib/stack/stack-scan.js` (`morph-spec stack-scan`) | Varredura determinística contra `config.project.stack` (`getProjectTags` de `stack-filter.js`; só stacks configurados são julgados), com `SEVERITIES` e regras por stack. O que fica fora vem de leitores únicos — `isTestPath` (`src/lib/graph/sanitize.js`), `WORKTREE_DIR_NAMES` (`src/lib/worktree/dirs.js`) e `PACKAGE_LOCATIONS` (`src/lib/detectors/index.js`) —, nunca listas próprias. `src/commands/project/stack-scan.js` é fino: com `<feature>` grava `2-plan/stack-scan.md` e recusa fora da árvore dona. Leitores do relatório: `morph-plan` (Bloqueante/Alto vira nó da árvore de decisões) e `morph-uiux`; regras e severidades descritas no bullet `stack-scan` de `framework/docs/CLI.md`, que muda junto. Canários: `test/lib/stack/stack-scan.test.js`, `test/commands/stack-scan.test.js` |
|
|
161
161
|
| `framework/hooks/shared/spawn-command.js` (`resolveSpawn`) | a regra ÚNICA de "como executar `npx`/`npm`/`pnpm`/`yarn` neste SO", com dois leitores: `defaultExec` de `e2e-check.js` (o spec do e2e, detectado ou `e2e.command`, e o seed do auth) e `heavy run` (`heavy.js`). No win32 o shim `.cmd` sem shell é ENOENT e o Node recusa `.cmd` com `shell: false`: `npx playwright` vira `node node_modules/@playwright/test/cli.js` quando o arquivo existe, o resto vai ao `.cmd` pelo shell. Canário: `test/hooks/spawn-command.test.js` (inclui um `npx --version` real) |
|
|
@@ -188,8 +188,7 @@ When modifying framework components, verify the full scope of related files.
|
|
|
188
188
|
| `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` reusa — 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 |
|
|
189
189
|
| `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`), a tabela `#### Sinais` sob `### gate-check` do `framework/docs/CLI.md` (a régua escrita do comando; o MORPH.md §5 só lista os sinais que a sessão passa por `--signals` — canário em `test/framework/verify-stack-docs-prose.test.js`). 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. Os relatórios do Gate 3 são lidos por UM leitor (`src/lib/review-reports.js`) e decididos por UMA regra (`reviewPause`): `approve` exige cada relatório exigido com `status: ok`, `gate-check` pausa em `FIX`, bloqueador aberto ou sem tipo, ou `Cn` sem linha PROVADO. **O plano entra pelo `checks.json`:** `planChecksPause` é o predicado ÚNICO de "o plano não foi extraído até zero erro" — o `gate-check` pausa o gate do plano por ele e `approve`/`advance` automáticos recusam por ele (`planAutoApprovalBlock`); `sliceCount > MAX_SLICES` pausa. `featureGeneratesMedia` lê os `standards` das fatias por id ou caminho (`standardIdOf`), e o `report-check --before-eval` usa o mesmo predicado para rotear o verificador a opus — canário ponta a ponta em `test/framework/media-claims.test.js`. A prova de verify é sempre da feature inteira: `--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`) |
|
|
190
190
|
| `src/lib/review-reports.js` (os relatórios do Gate 3) | O leitor ÚNICO de `4-review/{verifier,reviewer,security}.md`: veredito (o último, append-only), bloqueadores tipados (`BLOCKER_TYPES`), `PARCIAL` (o `PARTIAL_MARKER` de `slice-progress.js`, nunca uma cópia), `**Reviewed-SHA:**` e a `## Checks` do verificador. Leitores: `gate-check.js` (`collectReviewSignal` → `reviewSignal`), `approve.js` (status por papel, `--self-assessed` e o `Reviewed-SHA`, o pior vence), `report-check.js` (`reviews` por papel; `reviewModels` no `--before-eval`), `risk-detector.js` (`reviewPause` lê o sinal e `BLOCKER_TYPES`), `screen-checklist.js` (`verifierScreenChecklist`), o dashboard (`features-scanner.js`, por `reviewReportsFromDir` — sem git, `security` conta só presente) e o hook `state-sync` (que, por não importar `src/`, só testa a existência de `verifier.md`/`reviewer.md`). Quais papéis são exigidos é UMA regra: `requiredReviewRoles` + `securityReviewFor` via `collectReviewRequirement` (`gate-check.js`), lida pelos três comandos — uma segunda noção faz o approve aprovar o que o gate pausa. Cobertura: `checksCoverage` — status por igualdade EXATA (`NÃO PROVADO` contém `PROVADO`), Cn sem linha falta; `checks.json` ilegível pausa. Canários: `test/lib/review-reports.test.js` (medido por mutação: aceitar `NAO PROVADO` como prova e tratar Cn sem linha como coberto derrubam 3 testes cada) e o bloco "relatórios de review no Gate 3" de `test/commands/gate-check.test.js`; o fixture é `test/helpers/review-fixtures.js` |
|
|
191
|
-
| `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`,
|
|
192
|
-
| `src/lib/specs/old-format.js` (feature em voo no formato anterior) | A regra ÚNICA (`isOldFormatFeature`: `2-plan/tasks.json` ou `0-proposal/` sem `2-plan/plan.md`, status fora de `completed`/`abandoned`, raiz + toda worktree do `git worktree list`) e a mensagem ÚNICA (`oldFormatGuardLines`, que prescreve a 8.47 ou `archive <feature> --force`). As portas de escrita a aplicam ANTES de qualquer mutação, nenhuma dispensa por flag: `update.js` (`--force` não dispensa; `--dry-run` relata no topo) e `setupInfra` (`src/scripts/setup-infra.js`, também sob `force` e `reinstall`), por onde passam `init --force` e `setup-infra [--reinstall]`, que saem 1. Uma porta nova que sobrescreva `.morph/`/`.claude/` sem chamar a regra reabre o contorno. Texto da recusa em `framework/docs/CLI.md` (`### update`). Canários: `test/lib/specs/old-format.test.js` e `test/commands/update-old-format.test.js` (update, `setupInfra` com `force`/`reinstall`, `initCommand --force`, a CLI `setup-infra --reinstall`, e o `archive --force` que a mensagem prescreve rodando de verdade) |
|
|
191
|
+
| `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`, nove 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 de então sem matar nenhum dos 207 arquivos de teste |
|
|
193
192
|
| `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` |
|
|
194
193
|
| `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 |
|
|
195
194
|
| `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,14 +3,14 @@
|
|
|
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:** 9.
|
|
6
|
+
**Version:** 9.1.0
|
|
7
7
|
**Requires:** Node.js 22+, Claude Code
|
|
8
8
|
|
|
9
9
|
---
|
|
10
10
|
|
|
11
11
|
## What it does
|
|
12
12
|
|
|
13
|
-
morph-spec enforces a spec-first development workflow. When you ask for a feature, it does not write code immediately. It runs through structured phases (
|
|
13
|
+
morph-spec enforces a spec-first development workflow. When you ask for a feature, it does not write code immediately. It runs through structured phases (plan, implement, review) with approval gates before a single line of implementation is generated. Each phase produces traceable output files. Every decision is documented.
|
|
14
14
|
|
|
15
15
|
It integrates natively with Claude Code: skills become slash commands, agents become native subagents, hooks inject state context automatically, and rules enforce standards based on file paths.
|
|
16
16
|
|
|
@@ -61,17 +61,17 @@ your-project/
|
|
|
61
61
|
│ ├── config/
|
|
62
62
|
│ │ └── config.json # Project configuration
|
|
63
63
|
│ ├── framework/
|
|
64
|
-
│ │ ├── agents.json #
|
|
64
|
+
│ │ ├── agents.json # 10 personas (READ-ONLY)
|
|
65
65
|
│ │ ├── standards/ # Coding and architecture standards (READ-ONLY)
|
|
66
66
|
│ │ └── templates/ # Code and IaC templates (READ-ONLY)
|
|
67
67
|
│ ├── context/ # Project context (README.md, standards.md)
|
|
68
68
|
│ ├── features/ # Active features
|
|
69
69
|
│ │ └── {feature}/
|
|
70
|
-
│ │ ├──
|
|
71
|
-
│ │ ├──
|
|
72
|
-
│ │ ├──
|
|
73
|
-
│ │ ├── 3-implement/ #
|
|
74
|
-
│ │ └── 4-review/ #
|
|
70
|
+
│ │ ├── feature.json # Authoritative state (committed)
|
|
71
|
+
│ │ ├── 2-plan/ # plan.md + checks.json (from plan-check)
|
|
72
|
+
│ │ ├── 1-design/ # UI/UX design (optional, after the plan gate)
|
|
73
|
+
│ │ ├── 3-implement/ # reports/F{n}.md — one per plan slice
|
|
74
|
+
│ │ └── 4-review/ # verifier.md + reviewer.md [+ security.md]
|
|
75
75
|
│ └── state.json # READ-ONLY — managed by CLI only
|
|
76
76
|
└── .claude/
|
|
77
77
|
├── commands/ # Slash commands
|
|
@@ -88,28 +88,27 @@ your-project/
|
|
|
88
88
|
Features move through structured phases. Some are optional depending on feature type.
|
|
89
89
|
|
|
90
90
|
```
|
|
91
|
-
|
|
91
|
+
plan → [uiux] → implement → review
|
|
92
92
|
```
|
|
93
93
|
|
|
94
|
-
`uiux` is optional — applies only to UI-heavy features.
|
|
94
|
+
`uiux` is optional — it applies only to UI-heavy features and comes after the plan gate.
|
|
95
95
|
|
|
96
96
|
### Phase outputs
|
|
97
97
|
|
|
98
98
|
|
|
99
99
|
| Phase | Directory | Key files |
|
|
100
100
|
| --------- | --------------- | ------------------------------------------------------------------ |
|
|
101
|
-
|
|
|
101
|
+
| Plan | `2-plan/` | `plan.md` (scenarios `C{n}`, decisions, slices `F{n}`), `checks.json`, `stack-scan.md`, `scout-map.md` |
|
|
102
102
|
| Design | `1-design/` | `design-system.md`, `mockups.md`, `components.md`, `flows.md` |
|
|
103
|
-
|
|
|
104
|
-
|
|
|
105
|
-
| Review | `4-review/` | `review-report.md` |
|
|
103
|
+
| Implement | `3-implement/` | `reports/F{n}.md` + source code |
|
|
104
|
+
| Review | `4-review/` | `verifier.md`, `reviewer.md`, `security.md` (when required) |
|
|
106
105
|
|
|
107
106
|
|
|
108
107
|
### Approval gates
|
|
109
108
|
|
|
110
|
-
- **
|
|
111
|
-
- **
|
|
112
|
-
- **Gate 3 (Review)** —
|
|
109
|
+
- **Plan gate** — `plan.md` must pass `morph-spec plan-check` with zero errors (it writes `checks.json`) before implement.
|
|
110
|
+
- **UI/UX gate** (optional) — the `1-design/` outputs, after the plan gate.
|
|
111
|
+
- **Gate 3 (Review)** — independent sub-agents write `verifier.md` (every `C{n}` proved), `reviewer.md` and, when required, `security.md`, each answering SHIP or FIX.
|
|
113
112
|
|
|
114
113
|
In `trust: "auto"` mode (default), gates auto-pass without risk signals. See `framework/MORPH.md` for risk-signal details.
|
|
115
114
|
|
|
@@ -123,9 +122,9 @@ morph-spec approval-status {feature}
|
|
|
123
122
|
|
|
124
123
|
## Work-type routing & worktrees
|
|
125
124
|
|
|
126
|
-
Not every change deserves the full pipeline. Every feature is born with a **work type** — `feature`, `bug`, `chore`, or `hotfix` — set via `morph-spec create {feature} --type <type>` or classified from free text with `--request "<text>"`. The type dimensions the pipeline: `feature`
|
|
125
|
+
Not every change deserves the full pipeline. Every feature is born with a **work type** — `feature`, `bug`, `chore`, or `hotfix` — set via `morph-spec create {feature} --type <type>` or classified from free text with `--request "<text>"`. The type dimensions the pipeline: `feature` and `bug` run the full plan → [uiux] → implement → review sequence; `chore` skips straight to implement (plan gate stamped by policy at birth); `hotfix` runs scout (read-only root-cause) → surgical fix → test loop, and its Gate 3 always pauses for a human even under `trust: auto`. Change the type mid-flight with `morph-spec retype {feature} {newType}` (undoes only policy-stamped gates, preserves human approvals, blocked after Gate 3).
|
|
127
126
|
|
|
128
|
-
Independent features can also be developed **in parallel, each isolated in its own git worktree** — one feature, one worktree, one Claude Code instance. `morph-spec create {feature} --worktree` (or `morph-spec worktree setup {feature}`) creates `worktrees/{feature}/` on branch `morph/{feature}`. The authoritative `feature.json` travels committed on the branch; shared infra (`node_modules`, `.claude`, `.morph/framework`) is linked via junctions (`morph-spec worktree link --all` repairs it if missing). Close out with `morph-spec
|
|
127
|
+
Independent features can also be developed **in parallel, each isolated in its own git worktree** — one feature, one worktree, one Claude Code instance. `morph-spec create {feature} --worktree` (or `morph-spec worktree setup {feature}`) creates `worktrees/{feature}/` on branch `morph/{feature}`. The authoritative `feature.json` travels committed on the branch; shared infra (`node_modules`, `.claude`, `.morph/framework`) is linked via junctions (`morph-spec worktree link --all` repairs it if missing). Close out with `morph-spec finish {feature} --pr|--merge` (archive + integrate + teardown); `morph-spec worktree list` shows every active worktree with its phase and gate status.
|
|
129
128
|
|
|
130
129
|
---
|
|
131
130
|
|
|
@@ -136,7 +135,7 @@ These commands are available inside Claude Code after init.
|
|
|
136
135
|
|
|
137
136
|
| Command | Description |
|
|
138
137
|
| ------------------------------ | ---------------------------------------------------------- |
|
|
139
|
-
| `/morph-
|
|
138
|
+
| `/morph-plan {feature}` | Interview, `plan.md`, `plan-check` and the plan gate |
|
|
140
139
|
| `/morph-apply {feature}` | Autonomous execution + review (Gate 3) |
|
|
141
140
|
| `/morph-hotfix {feature}` | Surgical hotfix ADW — scout → fix → human Gate 3 → ship |
|
|
142
141
|
| `/morph-status [feature]` | Status of the active (or specified) feature |
|
|
@@ -164,7 +163,7 @@ morph-spec doctor --full # Full health check (all file verificatio
|
|
|
164
163
|
```bash
|
|
165
164
|
morph-spec fleet # Every active feature (root + worktrees): phase, gates, activity
|
|
166
165
|
morph-spec status {feature} # Feature status dashboard
|
|
167
|
-
morph-spec approve {feature} {gate} # Approve a phase gate (
|
|
166
|
+
morph-spec approve {feature} {gate} # Approve a phase gate (plan, uiux, review)
|
|
168
167
|
morph-spec advance {feature} # Approve the current gate and scaffold the next phase folder
|
|
169
168
|
morph-spec approval-status {feature} # Show approval status for all gates
|
|
170
169
|
morph-spec finish {feature} --pr|--merge # Close a feature: archive + integrate (PR or local merge) + teardown
|
|
@@ -173,7 +172,7 @@ morph-spec archive {feature} # Archive WITHOUT integrating (le
|
|
|
173
172
|
|
|
174
173
|
`finish` is the normal close for both flows: it archives (committing the move inside `morph/{feature}`), then integrates — `--merge` merges `--no-ff` into the default branch locally; `--pr` pushes the branch and runs `gh pr create` (needs `origin` + authenticated `gh`) — and tears down the worktree if there is one. Plain `archive` is reserved for legacy features (born before every feature had a `morph/{feature}` branch) and cleanup.
|
|
175
174
|
|
|
176
|
-
|
|
175
|
+
Progress within a feature is the slice reports `3-implement/reports/F{n}.md`: absent means pending, `PARCIAL` on the first line means partial. There is no CLI command for starting or finishing a slice.
|
|
177
176
|
|
|
178
177
|
### Validation
|
|
179
178
|
|
|
@@ -181,7 +180,7 @@ Task progress within a feature is recorded in `tasks.json` — Claude writes the
|
|
|
181
180
|
morph-spec validate # Run all validators
|
|
182
181
|
morph-spec validate {validator} # Run specific validator
|
|
183
182
|
morph-spec validate-feature {feature} # Content-aware feature validation
|
|
184
|
-
morph-spec verify {feature}
|
|
183
|
+
morph-spec verify {feature} # Deterministic build+tests+validators chain, before the LLM judge
|
|
185
184
|
```
|
|
186
185
|
|
|
187
186
|
### Work-type routing
|
|
@@ -190,7 +189,6 @@ morph-spec verify {feature} [task] # Deterministic build+tests+validators ch
|
|
|
190
189
|
morph-spec create {feature} --type feature|bug|chore|hotfix --description "<what/why>" # Explicit birth: type dimensions the pipeline, description is required
|
|
191
190
|
morph-spec create {feature} --request "<free text>" # Detector classifies the type AND seeds the description
|
|
192
191
|
morph-spec retype {feature} {newType} # Change work type mid-flight (blocked after Gate 3)
|
|
193
|
-
morph-spec dag {feature} [--mermaid] # DAG dispatch decision; --mermaid emits a flowchart
|
|
194
192
|
```
|
|
195
193
|
|
|
196
194
|
### Worktrees (parallel development)
|
|
@@ -200,7 +198,7 @@ morph-spec create {feature} --description "<what/why>" --worktree # Birth the
|
|
|
200
198
|
morph-spec worktree setup {feature} # Create worktrees/{feature}/ on branch morph/{feature}
|
|
201
199
|
morph-spec worktree link [--all] # Relink shared infra (node_modules, .claude, .morph/framework)
|
|
202
200
|
morph-spec worktree list [--json] # List active feature worktrees with phase/gate status
|
|
203
|
-
# Close a worktree feature with `morph-spec finish {feature} --pr|--merge` (see Feature workflow)
|
|
201
|
+
# Close a worktree feature with `morph-spec finish {feature} --pr|--merge` (see Feature workflow).
|
|
204
202
|
```
|
|
205
203
|
|
|
206
204
|
**Codebase graph** — optional, fail-open. `init` installs [graphify](https://pypi.org/project/graphifyy/) (pinned 0.9.50) when a Python toolchain is available and writes a stack-aware `.graphifyignore`; without one, it records the fact and moves on. Everything degrades to grep.
|
|
@@ -218,7 +216,7 @@ The gain is **precision, not recall**: the graph finds nothing grep would miss,
|
|
|
218
216
|
|
|
219
217
|
## Agent model
|
|
220
218
|
|
|
221
|
-
morph-spec dispatches
|
|
219
|
+
morph-spec dispatches 10 flat personas — no tiers, no hierarchy — defined in `.morph/framework/agents.json` (single source of truth) and installed identically to `.claude/agents/morph-{id}.md`. Claude picks the right persona per plan slice from the domains/keywords in `agents.json`, then dispatches it via the `Agent` tool.
|
|
222
220
|
|
|
223
221
|
| Persona | Domain | Model |
|
|
224
222
|
| ---------------- | -------------------------------------- | ------ |
|
|
@@ -228,7 +226,9 @@ morph-spec dispatches 8 flat domain personas — no tiers, no hierarchy — defi
|
|
|
228
226
|
| `nextjs-expert` | Frontend (Next.js/React) | sonnet |
|
|
229
227
|
| `ui-designer` | UI/UX, design systems | sonnet |
|
|
230
228
|
| `infra-engineer` | Jobs, deployment, observability | sonnet |
|
|
231
|
-
| `
|
|
229
|
+
| `verifier` | Gate 3: proves every scenario `C{n}` (read-only) | sonnet (opus for prompt/model or media) |
|
|
230
|
+
| `reviewer` | Gate 3: judges the diff (read-only) | sonnet |
|
|
231
|
+
| `security` | Gate 3: security review (read-only) | opus |
|
|
232
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.
|
|
@@ -237,16 +237,18 @@ morph-spec dispatches 8 flat domain personas — no tiers, no hierarchy — defi
|
|
|
237
237
|
|
|
238
238
|
## Hooks
|
|
239
239
|
|
|
240
|
-
Guardrail hooks are installed into `.claude/settings.local.json` across
|
|
240
|
+
Guardrail hooks are installed into `.claude/settings.local.json` across 13 event bindings. All are **fail-open** — they check that state exists first and silently no-op on missing/corrupt state; they never throw, write state, or orchestrate.
|
|
241
241
|
|
|
242
242
|
|
|
243
243
|
| Event | Hook(s) | Purpose |
|
|
244
244
|
| --------------------------- | ---------------------------------------------------------------------- | ------------------------------------------------------------------------------ |
|
|
245
245
|
| `SessionStart` | `worktree-context.js` | Injects feature identity (worktree) or active-feature summary (root) |
|
|
246
|
-
| `PreToolUse` (Write\|Edit) | `protect-spec-files.js`, `enforce-phase-writes.js` |
|
|
247
|
-
| `PreToolUse` (Bash) | `pre-bash-denylist.js`
|
|
248
|
-
| `
|
|
249
|
-
| `PostToolUse`
|
|
246
|
+
| `PreToolUse` (Write\|Edit) | `protect-spec-files.js`, `enforce-phase-writes.js` | Freezes `plan.md` after the plan gate (only `plan-check` writes `checks.json`); enforces phase folders |
|
|
247
|
+
| `PreToolUse` (Bash) | `pre-bash-denylist.js`, `block-destructive-worktree-removal.js`, `block-raw-worktree-add.js`, `warn-bash-source-write.js`, `heavy-gate.js`, `worktree-env-inject.js` | Destructive-command guards, worktree governance, heavy-node semaphore, per-worktree env |
|
|
248
|
+
| `WorktreeCreate` / `WorktreeRemove` | `worktree-create.js`, `worktree-remove.js` | Native worktrees delegate to the CLI |
|
|
249
|
+
| `PostToolUse` (Write\|Edit) | `comment-guard.js` | Blocks added comments the comment standard forbids |
|
|
250
|
+
| `PostToolUse` | `state-sync.js`, `gate-guard.js` | Syncs feature state; enforces gate ordering |
|
|
251
|
+
| `PostToolUse`, `PostToolUseFailure`, `PostToolBatch`, `SubagentStop`, `UserPromptSubmit` | `telemetry-log.js`, `loop-detect.js`, `graph-refresh-kick.js`, `heavy-release.js` | Telemetry, repeated-action loops, graph refresh, heavy-slot release |
|
|
250
252
|
| `Stop` | `validate-completion.js` | Warns about incomplete tasks, missing outputs, or pending approval gates |
|
|
251
253
|
|
|
252
254
|
|
|
@@ -303,16 +305,16 @@ After updating, run `morph-spec doctor` to confirm the installation is healthy.
|
|
|
303
305
|
|
|
304
306
|
## Key rules
|
|
305
307
|
|
|
306
|
-
**Never skip phases.** Every feature starts with a
|
|
308
|
+
**Never skip phases.** Every feature starts with a plan. No code is written until `plan.md` passes `plan-check` and the plan gate is approved.
|
|
307
309
|
|
|
308
310
|
**Never edit protected files directly:**
|
|
309
311
|
|
|
310
312
|
- `.morph/state.json` — derived automatically from `feature.json`; use `morph-spec approve`/`advance`/`gate-decision` to change feature state
|
|
311
313
|
- `.morph/framework/` — read-only, updated by `morph-spec update`
|
|
312
314
|
|
|
313
|
-
**Always document decisions.**
|
|
315
|
+
**Always document decisions.** Architectural choices go in the `## Decisões` section of `2-plan/plan.md` (ADRs are indexed when the feature closes), not in commit messages.
|
|
314
316
|
|
|
315
|
-
**Verify runs
|
|
317
|
+
**Verify runs once per feature.** `morph-spec verify {feature}` runs the build + test + validator chain relevant to your stack; red triggers an autonomous correction loop rather than a pause. Gate 3 is three independent sub-agents (verifier, reviewer, security when required) that answer SHIP or FIX with evidence-typed blockers.
|
|
316
318
|
|
|
317
319
|
---
|
|
318
320
|
|
|
@@ -378,4 +380,4 @@ Code generated by morph-spec (contracts, templates, implementation output) belon
|
|
|
378
380
|
|
|
379
381
|
---
|
|
380
382
|
|
|
381
|
-
*morph-spec v9.
|
|
383
|
+
*morph-spec v9.1.0 by [Polymorphism Tech](https://polymorphism.tech)*
|