@polymorphism-tech/morph-spec 8.47.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 +51 -43
- package/README.md +39 -38
- package/bin/morph-spec.js +41 -113
- package/bin/validate.js +1 -147
- package/claude-plugin.json +1 -1
- package/docs/CHEATSHEET.md +1 -1
- package/docs/QUICKSTART.md +1 -1
- package/framework/CLAUDE.md +41 -101
- package/framework/MORPH.md +95 -252
- package/framework/agents.json +86 -21
- package/framework/commands/morph-apply.md +5 -28
- package/framework/commands/morph-archive.md +1 -1
- package/framework/commands/morph-hotfix.md +8 -8
- package/framework/commands/morph-infra.md +1 -1
- package/framework/commands/morph-preflight.md +2 -3
- package/framework/commands/morph-status.md +4 -4
- package/framework/docs/CLI.md +574 -286
- package/framework/evals/review-checklist.md +82 -41
- package/framework/hooks/README.md +10 -11
- 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 +3 -17
- package/framework/hooks/claude-code/core/denylist.json +33 -3
- package/framework/hooks/claude-code/core/enforce-phase-writes.js +15 -63
- package/framework/hooks/claude-code/core/gate-guard.js +9 -78
- 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 +36 -2
- package/framework/hooks/claude-code/core/protect-spec-files.js +25 -297
- package/framework/hooks/claude-code/core/state-sync.js +15 -85
- package/framework/hooks/claude-code/core/telemetry-log.js +15 -96
- package/framework/hooks/claude-code/core/validate-completion.js +37 -147
- package/framework/hooks/claude-code/core/warn-bash-source-write.js +23 -0
- package/framework/hooks/claude-code/core/worktree-context.js +15 -114
- 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/claude-code/statusline.py +55 -41
- package/framework/hooks/package.json +3 -0
- 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 +11 -76
- package/framework/hooks/shared/claim-registry.js +9 -119
- package/framework/hooks/shared/command-tokens.js +168 -44
- package/framework/hooks/shared/comment-rules.js +5 -20
- package/framework/hooks/shared/comment-scanner.js +11 -43
- package/framework/hooks/shared/comment-stripper.js +11 -85
- package/framework/hooks/shared/e2e-check.js +0 -0
- package/framework/hooks/shared/errata.js +59 -0
- package/framework/hooks/shared/feature-locator.js +11 -95
- package/framework/hooks/shared/frontend-locations.js +4 -0
- package/framework/hooks/shared/gate-question.js +12 -0
- 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 +14 -72
- 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 +99 -0
- package/framework/hooks/shared/source-files.js +4 -17
- package/framework/hooks/shared/spawn-command.js +23 -0
- package/framework/hooks/shared/state-reader.js +51 -247
- package/framework/hooks/shared/state-writer.js +9 -54
- 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/mcp/registry.json +3 -4
- package/framework/phases.json +23 -39
- 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/schemas/config.schema.json +14 -9
- package/framework/schemas/feature.schema.json +15 -50
- package/framework/skills/README.md +3 -4
- 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 +149 -222
- package/framework/skills/core/morph-hotfix/SKILL.md +33 -42
- package/framework/skills/core/morph-machine/SKILL.md +2 -2
- package/framework/skills/core/morph-media/SKILL.md +1 -1
- package/framework/skills/core/morph-plan/SKILL.md +137 -299
- package/framework/skills/core/morph-review/SKILL.md +111 -136
- package/framework/skills/core/morph-standards/SKILL.md +27 -39
- package/framework/skills/core/morph-uiux/SKILL.md +94 -152
- package/framework/standards/STANDARDS.json +109 -1219
- package/framework/standards/STANDARDS.meta.json +1319 -0
- 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/evals-with-cache.md +1 -2
- 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 +3 -3
- 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 +7 -7
- 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/standards/integration/mcp/mcp-tools.md +7 -7
- package/framework/templates/CLAUDE.md.core.template +3 -3
- package/framework/templates/README.md +2 -3
- package/framework/templates/REGISTRY.json +15 -179
- package/framework/templates/docs/plan.md +72 -0
- package/framework/templates/docs/ui-mockups.md +4 -0
- package/framework/templates/dotnet/ai-kit/README.md +1 -9
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Morph.AiKit.Tests.csproj +0 -10
- package/framework/templates/meta-prompts/squad-leaders/backend-squad.md +1 -1
- package/framework/templates/meta-prompts/squad-leaders/frontend-squad.md +1 -1
- package/framework/templates/meta-prompts/squad-leaders/squad-leader.md +1 -1
- package/framework/templates/meta-prompts/validators/pre-commit-validator.md +3 -4
- 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 +18 -105
- 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 +17 -111
- 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-procs.js +1 -1
- package/src/commands/project/doctor.js +53 -307
- package/src/commands/project/env.js +1 -27
- package/src/commands/project/fleet.js +6 -35
- package/src/commands/project/heavy.js +3 -3
- package/src/commands/project/init.js +8 -68
- package/src/commands/project/install-plugin-cmd.js +0 -1
- package/src/commands/project/setup-infra-cmd.js +2 -1
- package/src/commands/project/stack-scan.js +58 -0
- package/src/commands/project/standards.js +65 -72
- package/src/commands/project/status.js +28 -121
- package/src/commands/project/update.js +31 -223
- package/src/commands/project/worktree.js +109 -883
- package/src/commands/scaffold/scaffold.js +0 -25
- package/src/commands/state/advance.js +65 -120
- package/src/commands/state/approve.js +87 -229
- package/src/commands/state/archive.js +23 -139
- package/src/commands/state/auto-approval.js +65 -0
- package/src/commands/state/create.js +49 -109
- package/src/commands/state/delete.js +11 -63
- package/src/commands/state/finish.js +105 -494
- package/src/commands/state/gate-check.js +74 -183
- package/src/commands/state/gate-decision.js +8 -42
- package/src/commands/state/handoff.js +4 -31
- package/src/commands/state/list.js +21 -91
- package/src/commands/state/report-check.js +134 -338
- package/src/commands/state/retype.js +12 -28
- package/src/commands/validation/index.js +0 -3
- package/src/commands/validation/mutate.js +79 -19
- package/src/commands/validation/plan-check.js +81 -0
- package/src/commands/validation/validate-feature.js +52 -216
- package/src/commands/validation/verify.js +45 -160
- package/src/core/paths/output-schema.js +9 -155
- package/src/core/state/feature-identity.js +1 -25
- package/src/core/state/gate-order.js +2 -30
- package/src/core/state/lifecycle-audit.js +8 -100
- package/src/core/state/state-manager.js +32 -395
- 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 +21 -0
- package/src/core/work-types.js +10 -45
- package/src/dashboard/app/App.jsx +1 -8
- package/src/dashboard/app/components/ActivityFeed.jsx +2 -8
- package/src/dashboard/app/components/CommandPalette.jsx +4 -11
- 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 +3 -9
- package/src/dashboard/app/lib/project.js +0 -12
- package/src/dashboard/app/pages/Activity.jsx +1 -1
- package/src/dashboard/app/pages/AgentMap.jsx +1 -8
- package/src/dashboard/app/pages/FeatureDetail.jsx +139 -133
- package/src/dashboard/app/pages/Features.jsx +9 -16
- package/src/dashboard/app/pages/Guide.jsx +2 -3
- package/src/dashboard/app/pages/HookInspector.jsx +2 -23
- package/src/dashboard/app/pages/PhasePipeline.jsx +3 -7
- 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 +14 -83
- package/src/dashboard/scanner/agents-scanner.js +1 -23
- package/src/dashboard/scanner/docs-scanner.js +1 -7
- package/src/dashboard/scanner/feature-status.js +2 -5
- package/src/dashboard/scanner/features-scanner.js +92 -113
- 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 +4 -7
- 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 +14 -13
- 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 +8 -59
- 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 +37 -112
- package/src/lib/generators/scaffold-generator.js +5 -101
- package/src/lib/generators/settings-generator.js +11 -122
- 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 +2 -1
- 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 +17 -17
- 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 +2 -6
- package/src/lib/installers/effects.js +8 -93
- package/src/lib/installers/mcp-installer.js +9 -169
- package/src/lib/installers/memory-installer.js +8 -33
- package/src/lib/installers/templates-reconcile.js +5 -60
- package/src/lib/mutation/commands.js +3 -2
- package/src/lib/mutation/dotnet-introspection.js +281 -0
- package/src/lib/mutation/plan.js +14 -6
- package/src/lib/mutation/runner.js +1 -1
- package/src/lib/mutation/subset.js +47 -13
- package/src/lib/plan/artifact-vocabulary.js +40 -0
- package/src/lib/plan/checks.js +208 -0
- package/src/lib/plan/plan-parser.js +204 -0
- package/src/lib/plan/plan-template.js +18 -0
- package/src/lib/projects/registry.js +7 -44
- package/src/lib/review-reports.js +204 -0
- package/src/lib/risk-detector.js +131 -274
- package/src/lib/specs/adr-index.js +38 -85
- package/src/lib/specs/design-seed.js +18 -0
- package/src/lib/specs/frontmatter.js +31 -0
- package/src/lib/specs/gfm-table.js +11 -0
- package/src/lib/specs/revocations.js +31 -102
- package/src/lib/specs/screen-checklist.js +56 -132
- package/src/lib/stack/dotnet-build-ignores.js +43 -0
- package/src/lib/stack/stack-profile.js +1 -28
- package/src/lib/stack/stack-scan.js +260 -0
- package/src/lib/stack-filter.js +4 -27
- package/src/lib/standards/registry.js +37 -0
- package/src/lib/standards/sections.js +47 -0
- package/src/lib/standards/verified-against.js +19 -191
- package/src/lib/tasks/eval-node.js +23 -179
- package/src/lib/tasks/linux-runner.js +18 -142
- package/src/lib/tasks/test-nodes.js +43 -313
- package/src/lib/tasks/test-runner.js +107 -447
- package/src/lib/telemetry/metrics.js +13 -103
- package/src/lib/telemetry/telemetry-reader.js +13 -238
- package/src/lib/telemetry/verify-event.js +4 -34
- package/src/lib/validators/code-budget/code-budget-validator.js +4 -44
- package/src/lib/validators/code-budget/index.js +0 -7
- package/src/lib/validators/contracts/contract-compliance-validator.js +4 -26
- 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 +24 -137
- package/src/lib/validators/reuse/symbol-scanner.js +6 -62
- package/src/lib/validators/traceability-check.js +54 -156
- package/src/lib/validators/validation-runner.js +20 -125
- package/src/lib/validators/verify-runner.js +152 -575
- package/src/lib/work-type-detector.js +5 -67
- package/src/lib/worktree/docker-audit.js +17 -137
- 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 +31 -126
- 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 -138
- package/src/utils/agents-installer.js +92 -272
- package/src/utils/banner.js +4 -25
- package/src/utils/claude-md-injector.js +1 -37
- package/src/utils/claude-settings-manager.js +1 -87
- package/src/utils/file-copier.js +32 -90
- package/src/utils/hooks-installer.js +26 -296
- package/src/utils/skills-installer.js +4 -102
- package/src/utils/version-checker.js +4 -83
- package/framework/commands/morph-proposal.md +0 -57
- package/framework/evals/trace-schema.md +0 -67
- package/framework/hooks/claude-code/core/tasks-json-guard.js +0 -83
- package/framework/hooks/claude-code/core/trace-autogen.js +0 -277
- package/framework/hooks/git/pre-commit/specs.sh +0 -50
- package/framework/hooks/shared/tasks-validator.js +0 -111
- package/framework/rules/morph-task-tracking.md +0 -54
- package/framework/rules/morph-workflow.md +0 -244
- package/framework/schemas/tasks.schema.json +0 -79
- package/framework/skills/core/morph-brainstorming/SKILL.md +0 -161
- package/framework/skills/core/morph-implement/SKILL.md +0 -194
- package/framework/skills/core/morph-proposal/SKILL.md +0 -152
- package/framework/skills/core/morph-stack-scan/SKILL.md +0 -145
- package/framework/templates/docs/proposal.md +0 -192
- package/framework/templates/docs/spec.md +0 -57
- package/framework/templates/docs/ui-components.md +0 -52
- package/framework/templates/docs/ui-design-system.md +0 -76
- package/framework/templates/docs/ui-flows.md +0 -167
- package/framework/templates/examples/spec-examples.md +0 -91
- package/framework/templates/feature/decisions.md +0 -124
- package/framework/templates/feature/recap.md +0 -145
- package/framework/templates/tasks.template.json +0 -17
- package/src/commands/state/usecase.js +0 -143
- package/src/commands/tasks/dag.js +0 -181
- package/src/commands/utils/index.js +0 -3
- package/src/lib/agents/output-collector.js +0 -76
- package/src/lib/detectors/design-system-detector.js +0 -188
- package/src/lib/evaluator-report.js +0 -104
- package/src/lib/generators/design-system-generator.js +0 -508
- package/src/lib/specs/acervo-format.js +0 -70
- package/src/lib/specs/acervo-lifecycle.js +0 -264
- package/src/lib/specs/done-criteria-check.js +0 -200
- package/src/lib/specs/usecase-check.js +0 -301
- package/src/lib/specs/usecase-promoter.js +0 -301
- package/src/lib/specs/usecase-registry.js +0 -237
- package/src/lib/tasks/dispatch-planner.js +0 -319
- package/src/lib/tasks/dotnet-test-introspection.js +0 -632
- package/src/lib/tasks/mermaid-renderer.js +0 -132
- package/src/lib/tasks/task-classifier.js +0 -30
- package/src/lib/tasks/task-parser.js +0 -209
- package/src/lib/tasks/tasks-json-parser.js +0 -296
- package/src/lib/tasks/test-scope.js +0 -597
- package/src/utils/agent-id-mapper.js +0 -11
|
@@ -1,59 +1,78 @@
|
|
|
1
|
-
# Checklist do
|
|
1
|
+
# Checklist do Gate 3 — verificador, revisor, segurança
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
## Saída (`4-review/evaluator-report.md`)
|
|
3
|
+
Três agentes em contexto fresco, cada um com o seu relatório em `4-review/`. Ninguém dá nota. Cada
|
|
4
|
+
relatório começa igual e é append-only (vale o último `**Verdict:**`):
|
|
7
5
|
|
|
8
6
|
```markdown
|
|
9
7
|
**Verdict:** SHIP ← ou FIX
|
|
8
|
+
**Reviewed-SHA:** <sha do dispatch>
|
|
10
9
|
|
|
11
10
|
## Bloqueadores
|
|
12
11
|
|
|
13
|
-
|
|
|
14
|
-
|
|
15
|
-
| B1 |
|
|
12
|
+
| id | tipo | onde | evidência |
|
|
13
|
+
|----|------|------|-----------|
|
|
14
|
+
| B1 | check | src/Orders/Create.cs:42 | C3: pedido sem itens é aceito — `POST /orders {items:[]}` → 201 |
|
|
16
15
|
|
|
17
16
|
## Notas
|
|
18
17
|
|
|
19
18
|
- (o que melhoraria, sem bloquear)
|
|
20
|
-
|
|
21
|
-
## Navegado
|
|
22
|
-
|
|
23
|
-
- (fluxos exercitados na app, quando o stack estava no ar; o que viu no console/network)
|
|
24
19
|
```
|
|
25
20
|
|
|
26
21
|
Sem bloqueador, escreva `Nenhum.` na seção. `SHIP` com bloqueador aberto é contradição e pausa o gate.
|
|
22
|
+
Confirmação depois de uma correção: seção nova ao fim com `**Verdict:**` e o `**Reviewed-SHA:**` novo —
|
|
23
|
+
julgando só os seus bloqueadores, ou, sem eles, só o delta `{sha anterior}..{sha novo}`. Bloqueador
|
|
24
|
+
que a correção fechou sai de `## Bloqueadores` (escreva `Nenhum.`) e o fechamento vai em `## Notas`;
|
|
25
|
+
a `## Checklist de tela` que vale é a da última seção, então repita-a inteira.
|
|
26
|
+
Parcial (limite de turnos): `PARCIAL` na 1ª linha, o feito e o que falta, SEM `**Verdict:**`.
|
|
27
|
+
|
|
28
|
+
**O que bloqueia — só com evidência de um destes tipos.** Todo o resto (estilo, nome, "eu faria
|
|
29
|
+
diferente") é nota; nota não bloqueia e não pede rodada nova. Na dúvida, sem evidência tipada, é nota.
|
|
30
|
+
|
|
31
|
+
| Tipo | Quem | Quando | Evidência mínima |
|
|
32
|
+
|------|------|--------|------------------|
|
|
33
|
+
| `check` | verificador | cenário `Cn` NÃO PROVADO ou FALHA | o que falta provar, ou o repro que falha |
|
|
34
|
+
| `teste` | verificador | teste vermelho; teste que não morde a regra que diz proteger | saída do verify, mutante sobrevivente, assert que passa pelo motivo errado |
|
|
35
|
+
| `regressao` | revisor | comportamento que funcionava e quebrou | consumidor afetado + o caminho que quebra |
|
|
36
|
+
| `escopo` | revisor | o diff faz o que nenhuma fatia pediu, ou deixa de fazer o que o contrato pede | arquivo/trecho fora das fatias, ou o item do contrato ausente |
|
|
37
|
+
| `arquitetura` | revisor | padrão ou abstração nova contra o stack e os standards | o trecho e o standard que ele contraria |
|
|
38
|
+
| `performance` | revisor | custo que o volume real torna visível | a consulta/lista e o volume que a derruba |
|
|
39
|
+
| `seguranca` | segurança | authz, segredo, injeção, PII, custo sem teto, migration destrutiva | o caminho de exploração |
|
|
40
|
+
|
|
41
|
+
## Verificador (`4-review/verifier.md`)
|
|
42
|
+
|
|
43
|
+
Missão: **provar cada cenário** do `2-plan/checks.json`. O default de todo `Cn` é NÃO PROVADO;
|
|
44
|
+
critério que você não verificou nunca vira PROVADO por inferência.
|
|
27
45
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
| Tipo | Quando | Evidência mínima |
|
|
31
|
-
|------|--------|------------------|
|
|
32
|
-
| `uc` | pós-condição de UC ou `doneCriteria` não atendida | repro (comando, request, teste) ou o trecho que prova |
|
|
33
|
-
| `teste` | teste vermelho; teste novo que não morde a regra que diz proteger | saída do verify, mutante sobrevivente, ou o assert que passa pelo motivo errado |
|
|
34
|
-
| `regressao` | comportamento que funcionava e quebrou | teste ou repro contra o comportamento anterior |
|
|
35
|
-
| `seguranca` | segredo exposto, authz faltando, injeção, dado pessoal vazando, custo sem teto | o caminho de exploração |
|
|
46
|
+
```markdown
|
|
47
|
+
## Checks
|
|
36
48
|
|
|
37
|
-
|
|
38
|
-
|
|
49
|
+
| Cn | status | evidência |
|
|
50
|
+
|----|--------|-----------|
|
|
51
|
+
| C1 | PROVADO | `src/Orders/Create.cs:42` + `OrdersTests.RecusaSemItens` (morde: M1 morto) |
|
|
52
|
+
| C2 | NÃO PROVADO | nenhum teste exercita o caminho de escrita |
|
|
53
|
+
```
|
|
39
54
|
|
|
40
|
-
|
|
55
|
+
Uma linha por cenário, status exato `PROVADO`, `NÃO PROVADO` ou `FALHA`. O gate pausa com cenário
|
|
56
|
+
sem linha ou não PROVADO. Numa confirmação, uma `## Checks` nova só com os Cn que mudaram: vale a
|
|
57
|
+
última linha de cada Cn. Para cada `Cn`, em ordem de dano:
|
|
41
58
|
|
|
42
|
-
1. **
|
|
59
|
+
1. **Onde vale**: `arquivo:linha` do código que entrega o cenário.
|
|
43
60
|
2. **Fiação**: existe caminho da entrada real ao efeito observável? A camada pura testada não prova
|
|
44
61
|
que o handler a chama. Inventarie quem consome o eixo novo.
|
|
45
|
-
3. **
|
|
46
|
-
|
|
47
|
-
|
|
62
|
+
3. **O teste morde?** Nasceu verde e afirma o que já era verdade; só testa a recusa e nunca o caminho
|
|
63
|
+
de escrita; guard cujos dois lados passam pelo mesmo serializador; asserção sobre texto que mede
|
|
64
|
+
vocabulário, não sentido.
|
|
48
65
|
4. **Verificações mecânicas** (.NET minimal API / Next): todo serviço injetado numa lambda de endpoint
|
|
49
66
|
tem registro em `Program.cs` (senão a app não sobe); prefixo de rota server-direct e de proxy são
|
|
50
67
|
constantes distintas, cada uma usada só do seu lado.
|
|
51
|
-
5. **
|
|
52
|
-
|
|
68
|
+
5. **Runtime**: com o stack no ar (URL no dispatch), navegue o fluxo do cenário e confira console e
|
|
69
|
+
network. Stack fora do ar é achado de infra, não defeito seu a provar.
|
|
53
70
|
|
|
54
|
-
|
|
71
|
+
A suíte inteira é do orquestrador. Você roda no máximo um filtro de testes de um cenário, em primeiro
|
|
72
|
+
plano, por `morph-spec heavy run -- <comando>`.
|
|
55
73
|
|
|
56
|
-
|
|
74
|
+
**Prova por mutação — opcional, no máximo 3, só depois que revisor e segurança terminaram.** Só
|
|
75
|
+
quando o diff tem uma linha de guarda ou regra que um cenário exige e você não achou o teste que a
|
|
57
76
|
morde. Escreva `4-review/mutants.json` e rode `morph-spec mutate {feature}` **uma vez**:
|
|
58
77
|
|
|
59
78
|
```json
|
|
@@ -63,20 +82,15 @@ morde. Escreva `4-review/mutants.json` e rode `morph-spec mutate {feature}` **um
|
|
|
63
82
|
|
|
64
83
|
`expect` é obrigatório: diga qual teste deve falhar e por quê **antes** de rodar — mutante cujo
|
|
65
84
|
efeito você não sabe prever tende a ser equivalente. Barra invertida do código vira `{{BS}}`.
|
|
66
|
-
Sobreviveu numa linha que um
|
|
67
|
-
`## Mutações` que a ferramenta imprime. A reversão é da ferramenta; rodada morta no meio:
|
|
85
|
+
Sobreviveu numa linha que um cenário exige → bloqueador `teste`. Sobreviveu em outra → nota. Cole a
|
|
86
|
+
seção `## Mutações` que a ferramenta imprime. A reversão é da ferramenta; rodada morta no meio:
|
|
68
87
|
`morph-spec mutate --restore`.
|
|
69
88
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
**Feature que gera mídia** (alguma task cita em `standards[]` `standards/ai-agents/modalities-image-gen.md`,
|
|
89
|
+
**Feature que gera mídia** (alguma fatia cita em `standards:` `standards/ai-agents/modalities-image-gen.md`,
|
|
73
90
|
`media-video.md` ou `media-pipeline.md`): grave `## Mídia gerada`, uma linha por afirmação — custo
|
|
74
91
|
controlado, revisão humana, mídia marcada, hot path não bloqueado —, cada uma com o código que a prende
|
|
75
92
|
e o teste que a prova. Afirmação falsa é bloqueador. Sem a seção, o gate pausa.
|
|
76
93
|
|
|
77
|
-
**Feature que chama LLM**: dá para reconstruir o que foi ao provedor e o que ele respondeu neste turno?
|
|
78
|
-
Não → nota, ou bloqueador quando um UC exige rastreabilidade.
|
|
79
|
-
|
|
80
94
|
**Prompt ou modelo alterado**: o nó `evals` do verify é a prova; sem ele, o gate pausa sozinho.
|
|
81
95
|
|
|
82
96
|
**Contrato de tela** (`1-design/mockups.md` com `## Tela:`): grave `## Checklist de tela`, uma linha por
|
|
@@ -86,5 +100,32 @@ item decidido de cada tela:
|
|
|
86
100
|
|
|
87
101
|
Veredito `cumpre` / `diverge` / `não verificável` (com o motivo). Paginação, ordenação ou filtro que o
|
|
88
102
|
checklist põe no servidor e a tela faz em memória, ou estado vazio/carregando/erro decidido e
|
|
89
|
-
ausente, é bloqueador `
|
|
90
|
-
item decidido, o gate pausa.
|
|
103
|
+
ausente, é bloqueador `check`. Divergência registrada em `plan.md ## Decisões` é nota. Sem a seção,
|
|
104
|
+
havendo item decidido, o gate pausa.
|
|
105
|
+
|
|
106
|
+
## Revisor (`4-review/reviewer.md`)
|
|
107
|
+
|
|
108
|
+
Read-only, sobre o commit do dispatch: `git diff {base}..{sha}`, `git show {sha}:<caminho>`. Nunca a
|
|
109
|
+
árvore de trabalho — o verificador pode estar com um mutante aplicado nela. Default SHIP.
|
|
110
|
+
|
|
111
|
+
1. **Escopo**: o diff contra as fatias (`arquivos:`) e o `## Contrato de implementação` do plano.
|
|
112
|
+
2. **Arquitetura**: padrão ou abstração nova contra o stack do projeto e os standards (camada que o
|
|
113
|
+
projeto não usa, repositório sobre o DbContext, dependência nova sem decisão no plano).
|
|
114
|
+
3. **Regressão**: para cada símbolo público alterado, `morph-spec graph affected <símbolo> --prod --json`.
|
|
115
|
+
Sem grafo (`no-graph`, `not-found`, `ambiguous`), caia para `git grep -n <símbolo> {sha}` e siga — a
|
|
116
|
+
ausência de grafo nunca bloqueia, e ausência de consumidor nunca sai do grafo.
|
|
117
|
+
4. **O que a correção abriu**: revisando a correção de uma rodada anterior, releia o comportamento
|
|
118
|
+
vizinho ao que ela mudou.
|
|
119
|
+
5. **Performance só com evidência**: lista sem paginação no servidor sobre volume real, N+1 visível.
|
|
120
|
+
6. **Chamada a LLM**: dá para reconstruir o que foi ao provedor e o que ele respondeu neste turno?
|
|
121
|
+
Não → nota.
|
|
122
|
+
|
|
123
|
+
## Segurança (`4-review/security.md`)
|
|
124
|
+
|
|
125
|
+
Despachada só quando o diff toca auth, segredo, permissão, dado pessoal, pagamento ou migration.
|
|
126
|
+
Mesmo contrato de leitura do revisor (só o SHA). Procure: authz faltando ou checada do lado errado;
|
|
127
|
+
segredo no código, no log ou no cliente; injeção (SQL, comando, prompt); dado pessoal vazando para log,
|
|
128
|
+
resposta ou provedor; custo sem teto (chamada paga em laço, sem limite por usuário); mídia publicada
|
|
129
|
+
sem revisão humana; migration destrutiva (DROP/rename de coluna com dado, branch Neon de produção).
|
|
130
|
+
Bloqueador `seguranca` exige o caminho de exploração — quem, por qual entrada, chega a quê. Sem
|
|
131
|
+
caminho, é nota.
|
|
@@ -12,7 +12,8 @@ framework/hooks/
|
|
|
12
12
|
│ │ ├── worktree-create.js # WorktreeCreate: governed tree via `morph-spec worktree setup|provision`
|
|
13
13
|
│ │ ├── worktree-remove.js # WorktreeRemove: safe teardown via `worktree remove --if-clean`
|
|
14
14
|
│ │ ├── block-destructive-worktree-removal.js # PreToolUse(Bash): raw removal follows junctions out
|
|
15
|
-
│ │
|
|
15
|
+
│ │ ├── block-raw-worktree-add.js # PreToolUse(Bash): raw `git worktree add` is ungoverned
|
|
16
|
+
│ │ └── warn-bash-source-write.js # PreToolUse(Bash): source written by redirect skips the Write/Edit guards
|
|
16
17
|
│ ├── user-prompt/
|
|
17
18
|
│ │ └── enrich-prompt.js # Context-aware prompt enrichment
|
|
18
19
|
│ ├── pre-tool-use/
|
|
@@ -32,8 +33,7 @@ framework/hooks/
|
|
|
32
33
|
├── git/ # Git hooks (Bash)
|
|
33
34
|
│ ├── pre-commit/
|
|
34
35
|
│ │ ├── orchestrator.sh # Master hook dispatcher
|
|
35
|
-
│ │
|
|
36
|
-
│ │ └── specs.sh # Validates spec.md sections
|
|
36
|
+
│ │ └── agents.sh # Validates agents.json schema
|
|
37
37
|
│ ├── commit-msg/
|
|
38
38
|
│ │ └── conventional-commits.sh # Enforces conventional commits
|
|
39
39
|
│ └── pre-push/
|
|
@@ -45,18 +45,17 @@ framework/hooks/
|
|
|
45
45
|
|
|
46
46
|
| Event | Hook | Type | Purpose |
|
|
47
47
|
|-------|------|------|---------|
|
|
48
|
-
| **SessionStart** | worktree-context.js | Context inject | Secondary worktree → feature identity (name/branch/phase) + missing-junction warning; primary root → active-feature summary (phase,
|
|
48
|
+
| **SessionStart** | worktree-context.js | Context inject | Secondary worktree → feature identity (name/branch/phase) + missing-junction warning; primary root → active-feature summary (phase, slices done/total, pending gates), silent if none. Also seeds a worktree that has no codebase graph from the root's, and launches the background refresh of the ROOT's graph (`shared/graph-refresh.js`) |
|
|
49
49
|
| **PreToolUse** (Write\|Edit) | _(native permissions.deny)_ | Block | Blocks edits to state.json and .morph/framework/ |
|
|
50
|
-
| **PreToolUse** (Write\|Edit) | protect-spec-files.js | Block | Blocks edits to
|
|
50
|
+
| **PreToolUse** (Write\|Edit) | protect-spec-files.js | Block | Blocks edits to `plan.md` (and the uiux artifacts) after their gate is approved; an append-only `## Errata` block at the end of `plan.md` passes |
|
|
51
51
|
| **PreToolUse** (Write\|Edit) | enforce-phase-writes.js | Block | Ensures writes go to current phase directory |
|
|
52
52
|
| **PreToolUse** (Bash) | pre-bash-denylist.js | Block | Blocks `rm -rf .morph/` and direct state edits |
|
|
53
53
|
| **PreToolUse** (Bash) | block-destructive-worktree-removal.js | Block | Blocks `git worktree remove` / `rm -rf` / `Remove-Item -Recurse` on a directory holding reparse points (the infra junctions a raw removal would FOLLOW out, emptying the root's `.claude/`); names `morph-spec worktree remove` |
|
|
54
54
|
| **PreToolUse** (Bash) | block-raw-worktree-add.js | Block | Blocks raw `git worktree add` (a tree with no junctions → no hooks, no port block, no identity, outside `worktrees/`); names `morph-spec worktree setup <feature>` / `worktree provision <name> --base <ref>`. `list`/`prune`/`remove`/`lock`/`move`/`repair` pass |
|
|
55
|
+
| **PreToolUse** (Bash) | warn-bash-source-write.js | Warn | Heredoc/redirect/`tee` into project source (`isProjectSource`): context naming the file, the command still runs — Write/Edit is what phase, protected-file and comment guards see |
|
|
55
56
|
| **WorktreeCreate** | worktree-create.js | Replaces native | Fires for `claude -w`, EnterWorktree and `Agent isolation:"worktree"`; replaces Claude Code's bare `git worktree add`. Runs `morph-spec worktree setup <n>` (known feature) or `worktree provision <n>` (task tree, branch `morph-task/<n>`), bases a task tree on the dispatcher's `morph/<f>` branch when `worktree.baseRef` is unset (`"head"` = current HEAD, `"fresh"` = default branch), prints the path as its last stdout line. CLI unreachable → plain git, still under `<root>/worktrees/` |
|
|
56
57
|
| **WorktreeRemove** | worktree-remove.js | Fail-open | Routes Claude Code's own cleanup through `morph-spec worktree remove --target --if-clean` (junctions dropped first; a dirty tree is left alone). Always exit 0. Secondary net — the event was observed NOT to fire headless; the Bash guard above stays the primary defense |
|
|
57
|
-
| **PostToolUse** (Write\|Edit) |
|
|
58
|
-
| **PostToolUse** (Write\|Edit) | trace-autogen.js | State sync | Auto-generates `.morph/traces/YYYY-MM-DD-{feature}-implement.json` from tasks.json + recap.md on every tasks.json write — the skills never write traces by hand |
|
|
59
|
-
| **PostToolUse** (Write\|Edit) | comment-guard.js | Blocking | Blocks when the edit ADDED a comment the `architecture-code-comments` standard never allows: process history (UC/ADR/task/gate), banner/`#region`, a block over 3 lines, or a doc-comment outside a published package. Old comments are never reported; last link of the chain |
|
|
58
|
+
| **PostToolUse** (Write\|Edit) | comment-guard.js | Blocking | Blocks when the edit ADDED a comment the `architecture-code-comments` standard never allows: process history (ADR, scenario/slice id, gate, plan.md), banner/`#region`, a block over 3 lines, or a doc-comment outside a published package. Old comments are never reported; last link of the chain |
|
|
60
59
|
| **PostToolUse** (Write\|Edit\|AskUserQuestion) | state-sync.js | State sync | Autonomous state updates (feature register, output tracking, gate approval) |
|
|
61
60
|
| **PostToolUse** (Write\|Edit\|AskUserQuestion) | gate-guard.js | Advisory | Warns (never blocks) when a gate action disagrees with the persisted gateDecisions; logs one `coherent`/`divergent` line per check in `.morph/logs/activity.jsonl` (issue #63) |
|
|
62
61
|
| **PostToolUse** (all tools) | telemetry-log.js | Telemetry | Appends every tool call (skill, sub-agent dispatch + prompt, knowledge-base read, bash/edit) to `.morph/logs/events.jsonl`; silent, fail-open — the NLH execution-path capture read back by `morph-spec telemetry` |
|
|
@@ -66,7 +65,7 @@ framework/hooks/
|
|
|
66
65
|
| **PostToolBatch** | telemetry-log.js | Telemetry | One `ToolBatch` event per model turn that used tools (the turn counter of `cost --metrics`) |
|
|
67
66
|
| **SubagentStop** | telemetry-log.js | Telemetry | `AgentUsage`: the sub-agent's token usage read from its own transcript, as a delta per agent; never blocks the stop |
|
|
68
67
|
| **UserPromptSubmit** | telemetry-log.js | Telemetry | Appends the user's prompt to the same `events.jsonl` stream; silent, fail-open |
|
|
69
|
-
| **Stop** | validate-completion.js | Advisory | Warns about incomplete
|
|
68
|
+
| **Stop** | validate-completion.js | Advisory | Warns about incomplete slice reports (hash-deduped, only re-injects when the warning set changes) |
|
|
70
69
|
|
|
71
70
|
## Design Principles
|
|
72
71
|
|
|
@@ -150,8 +149,8 @@ Claude Code sends JSON to stdin: { tool_input: { file_path: "..." } }
|
|
|
150
149
|
↓
|
|
151
150
|
protect-spec-files.js
|
|
152
151
|
├── Is in .morph/features/{feature}/?
|
|
153
|
-
│ ├── Is
|
|
154
|
-
│ ├── Is
|
|
152
|
+
│ ├── Is plan.md and plan gate approved? → BLOCK (unless append-only ## Errata)
|
|
153
|
+
│ ├── Is a 1-design/ artifact and uiux gate approved? → BLOCK
|
|
155
154
|
│ └── Gate not approved → pass
|
|
156
155
|
└── Not a feature file → pass
|
|
157
156
|
↓
|
|
@@ -6,70 +6,31 @@ import { stateExists } from '../../shared/state-reader.js';
|
|
|
6
6
|
import { scanForBlockers } from '../../shared/reparse-scan.js';
|
|
7
7
|
import { tokenize } from '../../shared/command-tokens.js';
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
* someone reading documentation.
|
|
12
|
-
*
|
|
13
|
-
* A morph worktree carries JUNCTIONS to the primary root's shared infra
|
|
14
|
-
* (`node_modules`, `.claude`, `.morph/framework`). Every generic recursive
|
|
15
|
-
* removal — `git worktree remove` (plain AND `--force`), `rm -rf`,
|
|
16
|
-
* `Remove-Item -Recurse`, `rmdir /s` — FOLLOWS those links out of the worktree
|
|
17
|
-
* and deletes what they point at, leaving the primary root's `.claude/` and
|
|
18
|
-
* `.morph/framework/` as empty directories. That is the incident this hook
|
|
19
|
-
* exists for, and it is not hypothetical: it happened, and the command was
|
|
20
|
-
* copied from a message the CLI itself printed.
|
|
21
|
-
*
|
|
22
|
-
* `morph-spec worktree remove` is safe because it drops the junctions BEFORE
|
|
23
|
-
* letting git near the directory. This hook is what stops the unsafe path from
|
|
24
|
-
* being reachable by accident.
|
|
25
|
-
*
|
|
26
|
-
* DELIBERATELY NARROW — an inert or over-eager guardrail is worse than none:
|
|
27
|
-
* - only recursive-removal shapes, only when a target path can be extracted;
|
|
28
|
-
* - only when that path exists AND the scan finds reparse points under it
|
|
29
|
-
* (an already-unlinked worktree, or one that never had junctions, passes);
|
|
30
|
-
* - anything unparseable passes.
|
|
31
|
-
* The scan is the authority, not the path shape: a worktree kept outside
|
|
32
|
-
* `worktrees/` is protected just the same, and a `worktrees/` directory with
|
|
33
|
-
* nothing to traverse is not blocked for looking dangerous.
|
|
34
|
-
*/
|
|
9
|
+
// Generic recursive removals FOLLOW a worktree's junctions and empty the root's `.claude/` and `.morph/framework/`.
|
|
10
|
+
// The reparse scan is the authority, not the path shape; anything unparseable passes.
|
|
35
11
|
|
|
36
|
-
//
|
|
37
|
-
// reads `git worktree add` off the same tokens, and a hook must never import a
|
|
38
|
-
// sibling hook (its standalone tail would fire in the importer's process).
|
|
12
|
+
// A hook never imports a sibling hook (its standalone tail would fire), so the tokenizer lives in shared/.
|
|
39
13
|
export { tokenize };
|
|
40
14
|
|
|
41
|
-
/**
|
|
42
|
-
* The path a recursive-removal command would destroy, or null when this is not
|
|
43
|
-
* such a command (or the target cannot be read off it).
|
|
44
|
-
*
|
|
45
|
-
* @param {string} command
|
|
46
|
-
* @returns {string|null}
|
|
47
|
-
*/
|
|
48
15
|
export function extractRemovalTarget(command) {
|
|
49
16
|
const tokens = tokenize(command);
|
|
50
17
|
if (!tokens.length) return null;
|
|
51
18
|
const lower = tokens.map((t) => t.toLowerCase());
|
|
52
19
|
|
|
53
|
-
//
|
|
54
|
-
// non-flag token; git has no form of this command without one.
|
|
20
|
+
// The path is always the last non-flag token.
|
|
55
21
|
const gi = lower.indexOf('git');
|
|
56
22
|
if (gi !== -1 && lower[gi + 1] === 'worktree' && lower[gi + 2] === 'remove') {
|
|
57
23
|
const rest = tokens.slice(gi + 3).filter((t) => !t.startsWith('-'));
|
|
58
24
|
return rest.length ? rest[rest.length - 1] : null;
|
|
59
25
|
}
|
|
60
26
|
|
|
61
|
-
//
|
|
62
|
-
// flag — a plain `rm file` or `Remove-Item file` cannot traverse a junction's
|
|
63
|
-
// contents and is none of this hook's business.
|
|
27
|
+
// Needs both the command word and a recursive flag: a plain `rm file` cannot traverse a junction.
|
|
64
28
|
const recursive = tokens.some((t) => /^(-[a-z]*r[a-z]*|--recursive|\/s)$/i.test(t));
|
|
65
29
|
const removalCmd = lower.find((t) => /^(rm|remove-item|ri|rd|rmdir|del)$/.test(t));
|
|
66
30
|
if (!removalCmd || !recursive) return null;
|
|
67
31
|
|
|
68
32
|
const idx = lower.indexOf(removalCmd);
|
|
69
|
-
// A cmd.exe switch is a slash plus
|
|
70
|
-
// longer that starts with `/` is a POSIX ABSOLUTE PATH — filtering on the
|
|
71
|
-
// bare `/` made every Linux target look like a flag, so `rm -rf /tmp/wt`
|
|
72
|
-
// extracted nothing and the hook passed exactly where it had to block.
|
|
33
|
+
// A cmd.exe switch is a slash plus one or two letters; anything longer is a POSIX absolute path.
|
|
73
34
|
const isCmdSwitch = (t) => /^\/[a-z]{1,2}$/i.test(t);
|
|
74
35
|
const operands = tokens.slice(idx + 1).filter((t) => !t.startsWith('-') && !isCmdSwitch(t));
|
|
75
36
|
// Named-parameter form: `Remove-Item -Path <p> -Recurse`.
|
|
@@ -78,10 +39,6 @@ export function extractRemovalTarget(command) {
|
|
|
78
39
|
return operands.length ? operands[0] : null;
|
|
79
40
|
}
|
|
80
41
|
|
|
81
|
-
/**
|
|
82
|
-
* @param {object} payload - parsed PreToolUse stdin payload (Bash matcher)
|
|
83
|
-
* @returns {Promise<object|null>} `{decision:'block',reason}` or null (pass).
|
|
84
|
-
*/
|
|
85
42
|
export async function run(payload) {
|
|
86
43
|
try {
|
|
87
44
|
if (!stateExists()) return null;
|
|
@@ -5,56 +5,23 @@ import { stateExists } from '../../shared/state-reader.js';
|
|
|
5
5
|
// standalone tail (stdin read + exit) inside THIS process.
|
|
6
6
|
import { tokenize } from '../../shared/command-tokens.js';
|
|
7
7
|
|
|
8
|
-
/**
|
|
9
|
-
* block-raw-worktree-add — PreToolUse (Bash) guard against `git worktree add`.
|
|
10
|
-
*
|
|
11
|
-
* A worktree created by raw git is a worktree morph cannot see: no infra
|
|
12
|
-
* junctions (`.claude`, `node_modules`, `.morph/framework` — so NO hooks run
|
|
13
|
-
* in there), no port block (its dev server collides with the root's), no
|
|
14
|
-
* feature/task identity (invisible to `fleet`, `doctor --worktrees`,
|
|
15
|
-
* `worktree list`), and a location git chose, not `<root>/worktrees/`. The
|
|
16
|
-
* sibling guard (block-destructive-worktree-removal) protects the way OUT;
|
|
17
|
-
* this one protects the way IN, by naming the two commands that build a
|
|
18
|
-
* governed tree:
|
|
19
|
-
*
|
|
20
|
-
* morph-spec worktree setup <feature> — a feature's tree
|
|
21
|
-
* morph-spec worktree provision <name> [--base <ref>] — a task-isolation tree
|
|
22
|
-
*
|
|
23
|
-
* DELIBERATELY NARROW: only the `add` subcommand. `list`, `prune`, `remove`
|
|
24
|
-
* (the sibling's business), `lock`, `unlock`, `move`, `repair` all pass. The
|
|
25
|
-
* `git` token may be followed by global options (`-C <dir>`, `--git-dir=<d>`,
|
|
26
|
-
* `-c k=v`) before `worktree`, and may sit anywhere in a chained command
|
|
27
|
-
* (`cd a && git worktree add b`) — every `git` occurrence is checked.
|
|
28
|
-
*/
|
|
29
|
-
|
|
30
|
-
/** Global `git` options that take a separate value: `git -C <dir> worktree add`. */
|
|
31
8
|
const GIT_GLOBAL_WITH_VALUE = new Set(['-c', '-C', '--git-dir', '--work-tree', '--namespace', '--exec-path', '--config-env']);
|
|
32
9
|
|
|
33
|
-
/**
|
|
34
|
-
* Does this command line invoke `git worktree add` anywhere?
|
|
35
|
-
* @param {string} command
|
|
36
|
-
* @returns {boolean}
|
|
37
|
-
*/
|
|
38
10
|
export function isRawWorktreeAdd(command) {
|
|
39
11
|
const tokens = tokenize(command);
|
|
40
12
|
const lower = tokens.map((t) => t.toLowerCase());
|
|
41
13
|
for (let i = 0; i < lower.length; i++) {
|
|
42
14
|
if (lower[i] !== 'git' && !/(^|[\\/])git(\.exe)?$/.test(lower[i])) continue;
|
|
43
15
|
let j = i + 1;
|
|
44
|
-
// Skip global options between `git` and the subcommand.
|
|
45
16
|
while (j < lower.length && lower[j].startsWith('-')) {
|
|
46
|
-
if (GIT_GLOBAL_WITH_VALUE.has(tokens[j])) j += 2;
|
|
47
|
-
else j += 1;
|
|
17
|
+
if (GIT_GLOBAL_WITH_VALUE.has(tokens[j])) j += 2;
|
|
18
|
+
else j += 1;
|
|
48
19
|
}
|
|
49
20
|
if (lower[j] === 'worktree' && lower[j + 1] === 'add') return true;
|
|
50
21
|
}
|
|
51
22
|
return false;
|
|
52
23
|
}
|
|
53
24
|
|
|
54
|
-
/**
|
|
55
|
-
* @param {object} payload - parsed PreToolUse stdin payload (Bash matcher)
|
|
56
|
-
* @returns {Promise<object|null>} `{decision:'block',reason}` or null (pass).
|
|
57
|
-
*/
|
|
58
25
|
export async function run(payload) {
|
|
59
26
|
try {
|
|
60
27
|
if (!stateExists()) return null;
|
|
@@ -1,54 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
*
|
|
5
|
-
* Motivação (onda de performance R2): cada hook registrado no Claude Code é
|
|
6
|
-
* hoje um processo node próprio (~60ms de cold start). Um único Write/Edit
|
|
7
|
-
* disparava até 9 desses processos — 2 PreToolUse + 3+2+2 PostToolUse. Este
|
|
8
|
-
* script agrupa os elos de um mesmo event+matcher (a mesma entrada do
|
|
9
|
-
* registry em src/utils/hooks-installer.js) num só spawn: lê stdin UMA vez,
|
|
10
|
-
* importa e chama `run(payload)` de cada hook em sequência, e combina as
|
|
11
|
-
* respostas — reduzindo esses 9 spawns para 4 por Write/Edit (1 PreToolUse +
|
|
12
|
-
* 3 PostToolUse: Write|Edit / Write|Edit|AskUserQuestion / matcher-`*`).
|
|
13
|
-
*
|
|
14
|
-
* Uso: `node chain.js <hook1> <hook2> ...` — nomes de arquivo SEM `.js`,
|
|
15
|
-
* resolvidos no mesmo diretório deste script (core/), na ORDEM em que devem
|
|
16
|
-
* rodar. A ordem é a mesma que o registry antigo (duas entries separadas)
|
|
17
|
-
* já implicava — ver o comentário de cada grupo em hooks-installer.js.
|
|
18
|
-
*
|
|
19
|
-
* Cada hook core exporta `run(payload)` (ver framework/hooks/shared/hook-response.js
|
|
20
|
-
* `runStandalone`) — o arquivo continua executável standalone (spawnSync
|
|
21
|
-
* direto, como os testes já fazem) porque a própria cauda do arquivo só lê
|
|
22
|
-
* stdin/imprime/sai quando NÃO está sendo importado por esta cadeia.
|
|
23
|
-
*
|
|
24
|
-
* Regras de combinação:
|
|
25
|
-
* - PreToolUse (decision:'block'): o PRIMEIRO elo que retornar um bloqueio
|
|
26
|
-
* VENCE e encerra a cadeia — os elos seguintes nem chegam a rodar. Isto é
|
|
27
|
-
* uma otimização NOVA desta consolidação (antes, Claude Code podia
|
|
28
|
-
* disparar os dois processos de qualquer forma); o resultado observável
|
|
29
|
-
* pro Claude Code é idêntico (o tool call é bloqueado do mesmo jeito).
|
|
30
|
-
* - PostToolUse / outros eventos de contexto: os textos de TODOS os elos que
|
|
31
|
-
* retornarem `hookSpecificOutput.additionalContext` (ou `decision:'approve'`
|
|
32
|
-
* com additionalContext) são concatenados, na ORDEM dos argv, separados por
|
|
33
|
-
* uma linha em branco — nenhum elo é pulado por um vizinho ter respondido.
|
|
34
|
-
* - Nome do evento (hookSpecificOutput.hookEventName) no resultado final:
|
|
35
|
-
* o valor que o PRIMEIRO elo a declarar um venceu (mesma regra de
|
|
36
|
-
* "primeiro vence" do bloqueio acima — na prática todos os elos de um
|
|
37
|
-
* mesmo grupo observam o MESMO evento real do Claude Code, então isto só
|
|
38
|
-
* importa como desempate determinístico); na ausência de qualquer elo
|
|
39
|
-
* declarando, cai para `payload.hook_event_name`; por último, `'PostToolUse'`.
|
|
40
|
-
* - updatedInput (PreToolUse): um elo pode devolver
|
|
41
|
-
* `hookSpecificOutput.updatedInput` (ver `updatedInputResult` em
|
|
42
|
-
* hook-response.js) para reescrever o tool_input. O ÚLTIMO elo a devolver um
|
|
43
|
-
* vence (só um elo do grupo reescreve, por construção). `permissionDecision:
|
|
44
|
-
* 'allow'` + `updatedInput` só entram no resultado final quando algum elo
|
|
45
|
-
* reescreveu — um `allow` incondicional pularia o prompt de permissão de
|
|
46
|
-
* TODO Bash, que é exatamente o contrário de um guardrail.
|
|
47
|
-
* - Fail-open POR ELO: uma exceção não tratada por um hook individual (import
|
|
48
|
-
* falhou, run() não existe, etc.) não derruba a cadeia — os elos seguintes
|
|
49
|
-
* ainda rodam. Cada hook já fail-opens sozinho dentro do seu próprio
|
|
50
|
-
* try/catch; isto é a segunda rede de segurança, para o import em si.
|
|
51
|
-
*/
|
|
2
|
+
// Usage: node chain.js <hook> ... (names without .js, run in argv order). The first block wins and ends the chain,
|
|
3
|
+
// the contexts of every link concatenate, and an exception in one link never stops the next.
|
|
52
4
|
|
|
53
5
|
import { dirname, join } from 'path';
|
|
54
6
|
import { fileURLToPath, pathToFileURL } from 'url';
|
|
@@ -56,20 +8,12 @@ import { readStdin } from '../../shared/stdin-reader.js';
|
|
|
56
8
|
|
|
57
9
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
58
10
|
|
|
59
|
-
//
|
|
60
|
-
// cauda de execução standalone de cada hook (runStandalone() em
|
|
61
|
-
// hook-response.js) só dispara quando esta flag NÃO está setada. Setada
|
|
62
|
-
// ANTES do primeiro import dinâmico — cada hook importado herda o mesmo
|
|
63
|
-
// process.env deste processo.
|
|
11
|
+
// Set before the first import: each hook's standalone tail runs only when this is unset.
|
|
64
12
|
process.env.MORPH_HOOK_CHAINED = '1';
|
|
65
13
|
|
|
66
|
-
//
|
|
67
|
-
// de core/, sem precisar ser hooks reais nem satisfazer as convenções que
|
|
68
|
-
// hook-wiring.test.js exige de todo arquivo em core/). Produção nunca seta
|
|
69
|
-
// isto — resolve sempre no próprio diretório deste script.
|
|
14
|
+
// Test-only override; production always resolves next to this script.
|
|
70
15
|
const coreDir = process.env.MORPH_HOOK_CORE_DIR || __dirname;
|
|
71
16
|
|
|
72
|
-
/** Extrai o texto de contexto de um resultado de hook, se houver. */
|
|
73
17
|
function contextOf(result) {
|
|
74
18
|
if (!result) return null;
|
|
75
19
|
if (result.decision === 'approve' && typeof result.additionalContext === 'string') {
|
|
@@ -93,13 +37,11 @@ async function main() {
|
|
|
93
37
|
const mod = await import(pathToFileURL(join(coreDir, `${name}.js`)).href);
|
|
94
38
|
result = await mod.run(payload);
|
|
95
39
|
} catch {
|
|
96
|
-
// Fail-open POR ELO — ver doc do módulo acima.
|
|
97
40
|
result = null;
|
|
98
41
|
}
|
|
99
42
|
|
|
100
43
|
if (!result) continue;
|
|
101
44
|
|
|
102
|
-
// PreToolUse: o primeiro bloqueio vence e encerra a cadeia.
|
|
103
45
|
if (result.decision === 'block') {
|
|
104
46
|
// A later link blocking must not swallow what earlier links reported (a tsc error on the same edit).
|
|
105
47
|
if (contexts.length) result = { ...result, reason: [...contexts, result.reason].join('\n\n') };
|
|
@@ -116,9 +58,6 @@ async function main() {
|
|
|
116
58
|
const ctx = contextOf(result);
|
|
117
59
|
if (ctx) {
|
|
118
60
|
contexts.push(ctx);
|
|
119
|
-
// First link to declare a name wins (mirrors block-first-wins above) —
|
|
120
|
-
// in practice every link in a group observes the SAME real Claude Code
|
|
121
|
-
// event, so this only matters as a deterministic tie-break.
|
|
122
61
|
eventName = eventName || result?.hookSpecificOutput?.hookEventName;
|
|
123
62
|
}
|
|
124
63
|
}
|
|
@@ -1,17 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
* `architecture-code-comments` standard never allows.
|
|
6
|
-
*
|
|
7
|
-
* Event: PostToolUse | Matcher: Write|Edit
|
|
8
|
-
*
|
|
9
|
-
* `decision: block` makes the agent fix it in the same turn; the edit itself
|
|
10
|
-
* already landed. Only comments absent from `tool_response.originalFile`
|
|
11
|
-
* count, so editing a legacy file never reports its old comments.
|
|
12
|
-
*
|
|
13
|
-
* Fail-open: any error passes silently.
|
|
14
|
-
*/
|
|
3
|
+
// `decision: block` makes the agent fix it in the same turn (the edit already landed). Only comments absent from
|
|
4
|
+
// `tool_response.originalFile` count, so editing a legacy file never reports its old comments.
|
|
15
5
|
|
|
16
6
|
import { readFileSync } from 'fs';
|
|
17
7
|
import { isAbsolute, relative, resolve } from 'path';
|
|
@@ -23,7 +13,7 @@ import { isGenerated, isProjectSource } from '../../shared/source-files.js';
|
|
|
23
13
|
const MAX_LISTED = 10;
|
|
24
14
|
|
|
25
15
|
const RULE_LABEL = {
|
|
26
|
-
process: 'história de processo (
|
|
16
|
+
process: 'história de processo (ADR, cenário, fatia, gate, plan.md)',
|
|
27
17
|
banner: 'banner ou #region',
|
|
28
18
|
'doc-comment': '///·JSDoc em código que não é pacote publicado',
|
|
29
19
|
'long-block': `bloco com mais de ${MAX_BLOCK_LINES} linhas`,
|
|
@@ -78,10 +68,6 @@ function describe(file, findings) {
|
|
|
78
68
|
].join('\n');
|
|
79
69
|
}
|
|
80
70
|
|
|
81
|
-
/**
|
|
82
|
-
* @param {object} payload - PostToolUse stdin payload
|
|
83
|
-
* @returns {Promise<object|null>}
|
|
84
|
-
*/
|
|
85
71
|
export async function run(payload) {
|
|
86
72
|
try {
|
|
87
73
|
if (!['Write', 'Edit'].includes(payload?.tool_name)) return null;
|
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version": "1.
|
|
2
|
+
"version": "1.2.0",
|
|
3
3
|
"patterns": [
|
|
4
|
-
"rm -rf /",
|
|
5
|
-
"rm -rf ~",
|
|
6
4
|
"DROP TABLE",
|
|
7
5
|
"DROP DATABASE",
|
|
8
6
|
"format c:",
|
|
@@ -10,5 +8,37 @@
|
|
|
10
8
|
"> /dev/sda",
|
|
11
9
|
"dd if=/dev/zero",
|
|
12
10
|
":(){:|:&};:"
|
|
11
|
+
],
|
|
12
|
+
"executableArgs": [
|
|
13
|
+
{
|
|
14
|
+
"executable": "taskkill",
|
|
15
|
+
"args": [
|
|
16
|
+
"/im",
|
|
17
|
+
"-im"
|
|
18
|
+
],
|
|
19
|
+
"reason": "mata por nome de imagem e derruba os MCPs de todas as sessões; encerre pelo PID que você subiu (`/PID <n>`)"
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
"executable": "rm",
|
|
23
|
+
"flags": [
|
|
24
|
+
[
|
|
25
|
+
"-r",
|
|
26
|
+
"-R",
|
|
27
|
+
"--recursive"
|
|
28
|
+
],
|
|
29
|
+
[
|
|
30
|
+
"-f",
|
|
31
|
+
"--force"
|
|
32
|
+
]
|
|
33
|
+
],
|
|
34
|
+
"args": [
|
|
35
|
+
"/",
|
|
36
|
+
"/*",
|
|
37
|
+
"~",
|
|
38
|
+
"~/",
|
|
39
|
+
"~/*"
|
|
40
|
+
],
|
|
41
|
+
"reason": "apaga a raiz ou o home inteiro; remova o caminho específico"
|
|
42
|
+
}
|
|
13
43
|
]
|
|
14
44
|
}
|