@polymorphism-tech/morph-spec 8.46.0 → 9.0.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 +53 -44
- package/README.md +3 -4
- package/bin/morph-spec.js +38 -55
- package/bin/validate.js +5 -134
- package/claude-plugin.json +1 -1
- package/docs/CHEATSHEET.md +2 -2
- package/docs/QUICKSTART.md +1 -1
- package/framework/CLAUDE.md +41 -101
- package/framework/MORPH.md +95 -252
- package/framework/agents.json +75 -18
- package/framework/commands/create-umbraco-lp.md +2 -0
- package/framework/commands/migrate-lp-to-umbraco.md +2 -0
- package/framework/commands/morph-apply.md +5 -28
- package/framework/commands/morph-archive.md +2 -2
- package/framework/commands/morph-hotfix.md +8 -8
- package/framework/commands/morph-infra.md +17 -203
- package/framework/commands/morph-media.md +3 -0
- package/framework/commands/morph-preflight.md +7 -31
- package/framework/commands/morph-proposal.md +2 -50
- package/framework/commands/morph-status.md +3 -3
- package/framework/commands/morph-troubleshoot.md +2 -14
- package/framework/docs/CLI.md +585 -286
- package/framework/evals/review-checklist.md +82 -41
- package/framework/hooks/README.md +10 -11
- package/framework/hooks/claude-code/core/comment-guard.js +1 -1
- package/framework/hooks/claude-code/core/denylist.json +33 -3
- package/framework/hooks/claude-code/core/enforce-phase-writes.js +16 -20
- package/framework/hooks/claude-code/core/gate-guard.js +2 -11
- package/framework/hooks/claude-code/core/pre-bash-denylist.js +38 -0
- package/framework/hooks/claude-code/core/protect-spec-files.js +30 -242
- package/framework/hooks/claude-code/core/state-sync.js +17 -19
- package/framework/hooks/claude-code/core/telemetry-log.js +3 -3
- package/framework/hooks/claude-code/core/validate-completion.js +33 -97
- package/framework/hooks/claude-code/core/warn-bash-source-write.js +31 -0
- package/framework/hooks/claude-code/core/worktree-context.js +5 -4
- package/framework/hooks/claude-code/statusline.py +55 -41
- package/framework/hooks/package.json +3 -0
- package/framework/hooks/shared/activity-logger.js +1 -1
- package/framework/hooks/shared/build-check.js +10 -6
- package/framework/hooks/shared/command-tokens.js +169 -0
- package/framework/hooks/shared/comment-rules.js +1 -1
- package/framework/hooks/shared/comment-scanner.js +10 -23
- package/framework/hooks/shared/comment-stripper.js +1 -1
- package/framework/hooks/shared/e2e-check.js +67 -31
- package/framework/hooks/shared/errata.js +72 -0
- package/framework/hooks/shared/feature-locator.js +1 -1
- package/framework/hooks/shared/frontend-locations.js +6 -0
- package/framework/hooks/shared/gate-question.js +18 -0
- package/framework/hooks/shared/phase-utils.js +15 -32
- package/framework/hooks/shared/slice-progress.js +118 -0
- package/framework/hooks/shared/spawn-command.js +23 -0
- package/framework/hooks/shared/state-reader.js +61 -87
- package/framework/hooks/shared/state-writer.js +3 -3
- package/framework/mcp/registry.json +3 -4
- package/framework/phases.json +23 -39
- package/framework/rules/ai-agents-standards.md +18 -16
- package/framework/rules/asaas-standards.md +3 -1
- package/framework/rules/csharp-standards.md +10 -8
- package/framework/rules/data-standards.md +5 -5
- package/framework/rules/dotnet-templates.md +1 -1
- package/framework/rules/frontend-standards.md +4 -2
- package/framework/rules/gsap-standards.md +10 -5
- package/framework/rules/hangfire-standards.md +3 -1
- package/framework/rules/infrastructure-standards.md +4 -2
- package/framework/rules/neon-auth-standards.md +3 -1
- package/framework/rules/neon-standards.md +6 -4
- package/framework/rules/nextjs-standards.md +13 -11
- package/framework/rules/nextjs-templates.md +1 -1
- package/framework/rules/resend-standards.md +3 -1
- package/framework/schemas/config.schema.json +14 -9
- package/framework/schemas/feature.schema.json +15 -52
- package/framework/skills/README.md +4 -5
- package/framework/skills/core/create-umbraco-lp/SKILL.md +3 -3
- package/framework/skills/core/migrate-lp-to-umbraco/SKILL.md +3 -3
- package/framework/skills/core/morph-apply/SKILL.md +150 -223
- package/framework/skills/core/morph-hotfix/SKILL.md +34 -43
- package/framework/skills/core/morph-machine/SKILL.md +4 -4
- package/framework/skills/core/morph-media/SKILL.md +3 -3
- package/framework/skills/core/morph-plan/SKILL.md +138 -300
- package/framework/skills/core/morph-review/SKILL.md +112 -137
- package/framework/skills/core/morph-standards/SKILL.md +29 -41
- package/framework/skills/core/morph-uiux/SKILL.md +78 -153
- package/framework/standards/STANDARDS.json +90 -1219
- package/framework/standards/STANDARDS.meta.json +1305 -0
- package/framework/standards/ai-agents/evals-with-cache.md +2 -3
- package/framework/standards/ai-agents/llm-runtime-defaults.md +1 -1
- package/framework/standards/ai-agents/modalities-speech-to-text.md +36 -26
- package/framework/standards/ai-agents/prompt-sources.md +6 -4
- package/framework/standards/ai-agents/providers/model-registry.md +1 -1
- package/framework/standards/ai-agents/rag-custom-pgvector.md +67 -39
- package/framework/standards/ai-agents/setup.md +2 -2
- package/framework/standards/ai-agents/structured-output.md +1 -1
- package/framework/standards/ai-agents/sweet-spot.md +1 -1
- package/framework/standards/architecture/code-comments.md +8 -8
- package/framework/standards/architecture/vertical-slice/vertical-slice.md +18 -9
- package/framework/standards/backend/api/validation.md +135 -47
- package/framework/standards/backend/database/postgresql/database.md +11 -7
- package/framework/standards/backend/database/vector-search-rag.md +207 -324
- package/framework/standards/backend/dotnet/fluent-validation-vsa.md +14 -8
- package/framework/standards/backend/dotnet/result-pattern.md +40 -6
- package/framework/standards/backend/dotnet/scalar-ui.md +13 -7
- package/framework/standards/backend/dotnet/scrutor.md +1 -1
- package/framework/standards/backend/dotnet/testing.md +1 -1
- package/framework/standards/backend/dotnet/vsa-handler-patterns.md +7 -7
- package/framework/standards/backend/integrations/asaas/asaas-api.md +309 -165
- package/framework/standards/backend/integrations/hangfire/hangfire-jobs.md +134 -154
- package/framework/standards/data/nosql/cache/redis.md +120 -44
- package/framework/standards/data/nosql/cosmos-db.md +114 -71
- package/framework/standards/frontend/design-system/animations.md +1 -1
- package/framework/standards/frontend/design-system/naming.md +1 -1
- package/framework/standards/frontend/nextjs/motion-patterns.md +45 -29
- package/framework/standards/frontend/nextjs/naming-conventions.md +1 -1
- package/framework/standards/infrastructure/docker/coolify-deploy.md +8 -6
- package/framework/standards/infrastructure/neon/neon-pgvector.md +120 -75
- package/framework/standards/infrastructure/neon/neon-setup.md +53 -31
- package/framework/standards/integration/mcp/mcp-tools.md +7 -7
- package/framework/templates/README.md +2 -3
- package/framework/templates/REGISTRY.json +6 -170
- package/framework/templates/code/dotnet/contracts/contracts-vsa.cs +2 -2
- package/framework/templates/code/dotnet/extensions/database-extensions.cs +1 -1
- package/framework/templates/code/dotnet/extensions/result-extensions.cs +27 -0
- package/framework/templates/code/dotnet/feature/endpoint.cs.hbs +2 -2
- package/framework/templates/docs/plan.md +72 -0
- package/framework/templates/dotnet/ai-kit/README.md +1 -9
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/MediaDisclosureTests.cs +1 -1
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Morph.AiKit.Tests.csproj +0 -10
- package/framework/templates/infrastructure/docker/coolify.template.json +3 -3
- package/framework/templates/infrastructure/github/README.md +2 -1
- package/framework/templates/infrastructure/github/workflows/deploy-coolify.yml.hbs +2 -1
- package/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/e2e/e2e.js +2 -2
- package/src/commands/observability/cost.js +18 -60
- package/src/commands/observability/telemetry.js +3 -5
- package/src/commands/project/doctor-procs.js +1 -1
- package/src/commands/project/doctor.js +9 -9
- package/src/commands/project/fleet.js +4 -4
- package/src/commands/project/heavy.js +3 -3
- package/src/commands/project/init.js +6 -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 +79 -25
- package/src/commands/project/status.js +29 -69
- package/src/commands/project/update.js +33 -19
- package/src/commands/project/worktree.js +1 -1
- package/src/commands/state/advance.js +62 -46
- package/src/commands/state/approve.js +95 -138
- package/src/commands/state/archive.js +20 -84
- package/src/commands/state/auto-approval.js +68 -0
- package/src/commands/state/create.js +28 -1
- package/src/commands/state/delete.js +1 -1
- package/src/commands/state/finish.js +11 -23
- package/src/commands/state/gate-check.js +86 -95
- package/src/commands/state/gate-decision.js +6 -4
- package/src/commands/state/handoff.js +1 -1
- package/src/commands/state/list.js +9 -9
- package/src/commands/state/report-check.js +157 -311
- package/src/commands/state/retype.js +9 -3
- package/src/commands/validation/mutate.js +77 -14
- package/src/commands/validation/plan-check.js +81 -0
- package/src/commands/validation/validate-feature.js +60 -135
- package/src/commands/validation/verify.js +41 -82
- package/src/core/paths/output-schema.js +20 -67
- package/src/core/state/gate-order.js +5 -6
- package/src/core/state/lifecycle-audit.js +2 -2
- package/src/core/state/state-manager.js +17 -98
- package/src/core/templates/template-registry.js +2 -2
- package/src/core/templates/template-seed.js +22 -0
- package/src/core/work-types.js +10 -10
- package/src/dashboard/app/App.jsx +0 -2
- package/src/dashboard/app/components/ActivityFeed.jsx +2 -3
- package/src/dashboard/app/components/CommandPalette.jsx +7 -8
- package/src/dashboard/app/components/EntityLink.jsx +1 -2
- package/src/dashboard/app/components/Markdown.jsx +1 -1
- package/src/dashboard/app/components/Sidebar.jsx +1 -2
- package/src/dashboard/app/hooks/useMorphQuery.js +1 -1
- package/src/dashboard/app/lib/phases.js +2 -4
- package/src/dashboard/app/pages/Activity.jsx +1 -1
- package/src/dashboard/app/pages/FeatureDetail.jsx +139 -129
- package/src/dashboard/app/pages/Features.jsx +9 -9
- package/src/dashboard/app/pages/Guide.jsx +2 -2
- package/src/dashboard/app/pages/PhasePipeline.jsx +3 -4
- package/src/dashboard/app/pages/RulesMap.jsx +3 -3
- package/src/dashboard/app/pages/SkillsLibrary.jsx +3 -3
- package/src/dashboard/app/pages/Standards.jsx +1 -1
- package/src/dashboard/scanner/activity-scanner.js +6 -16
- package/src/dashboard/scanner/agents-scanner.js +1 -1
- package/src/dashboard/scanner/docs-scanner.js +5 -23
- package/src/dashboard/scanner/features-scanner.js +95 -60
- package/src/dashboard/scanner/index.js +0 -2
- package/src/dashboard/scanner/project-api.js +1 -1
- package/src/dashboard/scanner/rules-scanner.js +15 -10
- package/src/dashboard/scanner/skills-scanner.js +2 -3
- package/src/dashboard/scanner/standards-scanner.js +2 -1
- package/src/lib/ci/ci-verify.js +17 -1
- package/src/lib/ci/github-ci.js +1 -1
- package/src/lib/detectors/index.js +3 -8
- package/src/lib/generators/handoff-generator.js +34 -65
- package/src/lib/generators/settings-generator.js +11 -89
- package/src/lib/git/evidence-staleness.js +1 -1
- package/src/lib/git/tree-fingerprint.js +19 -7
- package/src/lib/git/tree-state.js +2 -2
- package/src/lib/heavy/cleanup.js +1 -5
- package/src/lib/installers/memory-installer.js +6 -8
- package/src/lib/mutation/commands.js +3 -2
- package/src/lib/{tasks/dotnet-test-introspection.js → mutation/dotnet-introspection.js} +17 -282
- package/src/lib/mutation/plan.js +14 -6
- package/src/lib/mutation/runner.js +1 -1
- package/src/lib/mutation/subset.js +46 -9
- package/src/lib/plan/artifact-vocabulary.js +60 -0
- package/src/lib/plan/checks.js +234 -0
- package/src/lib/plan/plan-parser.js +209 -0
- package/src/lib/plan/plan-template.js +18 -0
- package/src/lib/review-reports.js +222 -0
- package/src/lib/risk-detector.js +134 -135
- package/src/lib/specs/adr-index.js +50 -69
- package/src/lib/specs/design-seed.js +18 -0
- package/src/lib/specs/frontmatter.js +44 -0
- package/src/lib/specs/gfm-table.js +17 -0
- package/src/lib/specs/old-format.js +68 -0
- package/src/lib/specs/revocations.js +37 -95
- package/src/lib/specs/screen-checklist.js +18 -17
- package/src/lib/stack/dotnet-build-ignores.js +47 -0
- package/src/lib/stack/stack-profile.js +1 -1
- package/src/lib/stack/stack-scan.js +267 -0
- package/src/lib/standards/registry.js +43 -0
- package/src/lib/standards/sections.js +58 -0
- package/src/lib/tasks/eval-node.js +4 -12
- package/src/lib/tasks/test-nodes.js +1 -74
- package/src/lib/tasks/test-runner.js +75 -117
- package/src/lib/telemetry/metrics.js +8 -69
- package/src/lib/telemetry/telemetry-reader.js +5 -118
- package/src/lib/telemetry/verify-event.js +5 -7
- package/src/lib/validators/code-budget/code-budget-validator.js +1 -1
- package/src/lib/validators/contracts/contract-compliance-validator.js +2 -2
- package/src/lib/validators/reuse/reuse-validator.js +24 -48
- package/src/lib/validators/reuse/symbol-scanner.js +2 -4
- package/src/lib/validators/traceability-check.js +85 -129
- package/src/lib/validators/validation-runner.js +8 -88
- package/src/lib/validators/verify-runner.js +134 -301
- package/src/lib/worktree/docker-audit.js +1 -2
- package/src/lib/worktree/fleet-scanner.js +25 -38
- package/src/scripts/setup-infra.js +42 -22
- package/src/utils/agents-installer.js +92 -13
- package/src/utils/claude-settings-manager.js +0 -35
- package/src/utils/file-copier.js +34 -6
- package/src/utils/hooks-installer.js +16 -9
- package/src/utils/skills-installer.js +1 -2
- package/framework/commands/commit.md +0 -166
- 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/rubrics/backend.json +0 -41
- package/framework/rubrics/default.json +0 -36
- package/framework/rubrics/frontend.json +0 -36
- 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/project/validate.js +0 -97
- 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/dashboard/app/pages/Rubrics.jsx +0 -97
- package/src/dashboard/scanner/rubrics-scanner.js +0 -12
- package/src/lib/agents/output-collector.js +0 -76
- package/src/lib/evaluator-report.js +0 -91
- 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/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/lib/validators/architecture/architecture-validator.js +0 -270
- package/src/lib/validators/architecture/index.js +0 -7
- package/src/lib/validators/css/css-validator.js +0 -340
- package/src/lib/validators/css/index.js +0 -7
- package/src/lib/validators/design-system/design-system-validator.js +0 -237
- package/src/lib/validators/design-system/index.js +0 -7
- package/src/lib/validators/score-aggregator.js +0 -244
- package/src/lib/validators/ui/index.js +0 -7
- package/src/lib/validators/ui/ui-contrast-validator.js +0 -422
|
@@ -227,25 +227,18 @@ export function getFeature(featureName, projectPath) {
|
|
|
227
227
|
* Derive phase from filesystem (phase deleted from state).
|
|
228
228
|
*
|
|
229
229
|
* Walks the phase map in descending order and returns the first phase whose
|
|
230
|
-
* CANONICAL ARTIFACT exists. An empty folder is not enough —
|
|
231
|
-
*
|
|
232
|
-
* advances the phase before plan content is written, then
|
|
233
|
-
* `enforce-phase-writes` blocks writes back to `0-proposal/`.
|
|
230
|
+
* CANONICAL ARTIFACT exists. An empty folder is not enough — a pre-created
|
|
231
|
+
* sibling folder never advances the phase before its content is written.
|
|
234
232
|
*
|
|
235
233
|
* Canonical artifacts (one per phase) match MORPH.md §3:
|
|
236
|
-
* - 4-review
|
|
237
|
-
* - 3-implement →
|
|
238
|
-
* -
|
|
239
|
-
* -
|
|
240
|
-
* - 0-proposal → proposal.md
|
|
234
|
+
* - 4-review → verifier.md
|
|
235
|
+
* - 3-implement → any slice report (`reports/*.md`)
|
|
236
|
+
* - 1-design → design-system.md
|
|
237
|
+
* - 2-plan → plan.md
|
|
241
238
|
*
|
|
242
|
-
* If no canonical file is found, returns `'
|
|
243
|
-
*
|
|
244
|
-
* @param {string} featureName
|
|
245
|
-
* @param {string} [projectPath]
|
|
246
|
-
* @returns {string} Phase name
|
|
239
|
+
* If no canonical file is found, returns `'plan'` (the starting phase).
|
|
247
240
|
*/
|
|
248
|
-
export const PHASE_INDEX = {
|
|
241
|
+
export const PHASE_INDEX = { plan: 0, uiux: 1, implement: 2, review: 3 };
|
|
249
242
|
|
|
250
243
|
/**
|
|
251
244
|
* The canonical artifact of each phase, descending. Exported so a caller that
|
|
@@ -253,32 +246,61 @@ export const PHASE_INDEX = { proposal: 0, uiux: 1, plan: 2, implement: 3, review
|
|
|
253
246
|
* tree, say) can answer the same question without re-encoding the map.
|
|
254
247
|
*/
|
|
255
248
|
export const PHASE_ARTIFACTS = [
|
|
256
|
-
['4-review', 'review', '
|
|
257
|
-
['3-implement', 'implement', '
|
|
258
|
-
['2-plan', 'plan', 'spec.md'],
|
|
249
|
+
['4-review', 'review', 'verifier.md'],
|
|
250
|
+
['3-implement', 'implement', 'reports/'],
|
|
259
251
|
['1-design', 'uiux', 'design-system.md'],
|
|
260
|
-
['
|
|
252
|
+
['2-plan', 'plan', 'plan.md'],
|
|
261
253
|
];
|
|
262
254
|
|
|
255
|
+
const DESIGN_DIR = '1-design';
|
|
256
|
+
|
|
263
257
|
const gatesOf = (feature) => feature?.approvals || feature?.approvalGates;
|
|
264
258
|
|
|
265
259
|
/**
|
|
266
260
|
* Gate-implied phase from an ALREADY-LOADED feature object — no disk access.
|
|
267
|
-
*
|
|
268
|
-
*
|
|
269
|
-
*
|
|
270
|
-
*
|
|
261
|
+
* An approved gate authorizes the NEXT phase:
|
|
262
|
+
* review approved → review (terminal)
|
|
263
|
+
* plan approved → implement, or uiux while the design branch was taken
|
|
264
|
+
* (`1-design/` exists) and its gate is still open
|
|
265
|
+
* otherwise → plan
|
|
271
266
|
*
|
|
272
267
|
* @param {Object|null} feature
|
|
268
|
+
* @param {boolean} [designStarted] - whether `1-design/` exists for the feature
|
|
273
269
|
* @returns {string} phase name
|
|
274
270
|
*/
|
|
275
|
-
export function gateImpliedPhaseFrom(feature) {
|
|
271
|
+
export function gateImpliedPhaseFrom(feature, designStarted = false) {
|
|
276
272
|
const gates = gatesOf(feature);
|
|
277
273
|
const ok = (g) => gates?.[g]?.approved === true;
|
|
278
274
|
if (ok('review')) return 'review';
|
|
279
|
-
if (ok('plan')) return 'implement';
|
|
280
|
-
|
|
281
|
-
|
|
275
|
+
if (ok('plan')) return designStarted && !ok('uiux') ? 'uiux' : 'implement';
|
|
276
|
+
return 'plan';
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
/**
|
|
280
|
+
* Artifact predicate over a feature dir on disk. A path ending in `/` asks for
|
|
281
|
+
* any `.md` file directly under it: an empty folder is not an artifact.
|
|
282
|
+
*/
|
|
283
|
+
export function diskArtifactProbe(featureDir) {
|
|
284
|
+
return (rel) => {
|
|
285
|
+
const abs = join(featureDir, ...rel.split('/').filter(Boolean));
|
|
286
|
+
if (!rel.endsWith('/')) return existsSync(abs);
|
|
287
|
+
try {
|
|
288
|
+
return readdirSync(abs, { withFileTypes: true }).some((e) => e.isFile() && e.name.endsWith('.md'));
|
|
289
|
+
} catch {
|
|
290
|
+
return false;
|
|
291
|
+
}
|
|
292
|
+
};
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
/** The same predicate over a set of feature-relative POSIX paths (a git tree). */
|
|
296
|
+
export function fileSetArtifactProbe(files) {
|
|
297
|
+
return (rel) => {
|
|
298
|
+
if (!rel.endsWith('/')) return files.has(rel);
|
|
299
|
+
for (const f of files) {
|
|
300
|
+
if (f.startsWith(rel) && f.endsWith('.md') && !f.slice(rel.length).includes('/')) return true;
|
|
301
|
+
}
|
|
302
|
+
return false;
|
|
303
|
+
};
|
|
282
304
|
}
|
|
283
305
|
|
|
284
306
|
/**
|
|
@@ -286,44 +308,33 @@ export function gateImpliedPhaseFrom(feature) {
|
|
|
286
308
|
* access of its own, so the artifact universe can be a git tree.
|
|
287
309
|
*
|
|
288
310
|
* @param {Object|null} feature
|
|
289
|
-
* @param {(relPath: string) => boolean} hasArtifact -
|
|
290
|
-
* paths relative to the feature dir
|
|
311
|
+
* @param {(relPath: string) => boolean} hasArtifact - {@link diskArtifactProbe}
|
|
312
|
+
* or {@link fileSetArtifactProbe}: POSIX paths relative to the feature dir
|
|
291
313
|
* @returns {string} phase name
|
|
292
314
|
*/
|
|
293
315
|
export function derivePhaseFrom(feature, hasArtifact) {
|
|
294
|
-
let artifactPhase = '
|
|
316
|
+
let artifactPhase = 'plan';
|
|
295
317
|
for (const [folder, phase, canonical] of PHASE_ARTIFACTS) {
|
|
296
318
|
if (hasArtifact(`${folder}/${canonical}`)) { artifactPhase = phase; break; }
|
|
297
319
|
}
|
|
298
320
|
|
|
299
321
|
// Gate-aware: an approved gate advances the phase even before the next
|
|
300
|
-
// phase's first canonical artifact exists
|
|
301
|
-
//
|
|
302
|
-
|
|
303
|
-
const implied = gateImpliedPhaseFrom(feature);
|
|
322
|
+
// phase's first canonical artifact exists, or that artifact could never be
|
|
323
|
+
// written. Take whichever of (artifact, gate-implied) is later.
|
|
324
|
+
const designStarted = hasArtifact(DESIGN_DIR) || hasArtifact(`${DESIGN_DIR}/design-system.md`);
|
|
325
|
+
const implied = gateImpliedPhaseFrom(feature, designStarted);
|
|
304
326
|
return PHASE_INDEX[implied] > PHASE_INDEX[artifactPhase] ? implied : artifactPhase;
|
|
305
327
|
}
|
|
306
328
|
|
|
307
|
-
/**
|
|
308
|
-
* The phase implied purely by approved gates, independent of which artifacts
|
|
309
|
-
* exist on disk. An approved gate authorizes the NEXT phase, so:
|
|
310
|
-
* review approved → review (terminal)
|
|
311
|
-
* plan approved → implement
|
|
312
|
-
* uiux approved → plan
|
|
313
|
-
* otherwise → proposal (proposal-approval's fork between uiux/plan is
|
|
314
|
-
* resolved by enforce-phase-writes, not here)
|
|
315
|
-
* @returns {string} phase name
|
|
316
|
-
*/
|
|
329
|
+
/** The phase implied purely by approved gates — see {@link gateImpliedPhaseFrom}. */
|
|
317
330
|
export function gateImpliedPhase(featureName, projectPath) {
|
|
318
|
-
|
|
331
|
+
const designDir = join(projectPath || process.cwd(), '.morph/features', featureName, DESIGN_DIR);
|
|
332
|
+
return gateImpliedPhaseFrom(getFeature(featureName, projectPath), existsSync(designDir));
|
|
319
333
|
}
|
|
320
334
|
|
|
321
335
|
export function derivePhaseForFeature(featureName, projectPath) {
|
|
322
336
|
const basePath = join(projectPath || process.cwd(), '.morph/features', featureName);
|
|
323
|
-
return derivePhaseFrom(
|
|
324
|
-
getFeature(featureName, projectPath),
|
|
325
|
-
(rel) => existsSync(join(basePath, rel)),
|
|
326
|
-
);
|
|
337
|
+
return derivePhaseFrom(getFeature(featureName, projectPath), diskArtifactProbe(basePath));
|
|
327
338
|
}
|
|
328
339
|
|
|
329
340
|
/**
|
|
@@ -342,7 +353,7 @@ export function getFeaturePhase(featureName, projectPath) {
|
|
|
342
353
|
/**
|
|
343
354
|
* Get approval status for a gate.
|
|
344
355
|
* @param {string} featureName
|
|
345
|
-
* @param {string} gate - '
|
|
356
|
+
* @param {string} gate - 'plan'|'uiux'|'review'
|
|
346
357
|
* @param {string} [projectPath]
|
|
347
358
|
* @returns {boolean}
|
|
348
359
|
*/
|
|
@@ -384,43 +395,6 @@ export function getOutputs(featureName, projectPath) {
|
|
|
384
395
|
return feature?.outputs || null;
|
|
385
396
|
}
|
|
386
397
|
|
|
387
|
-
/**
|
|
388
|
-
* Get uncompleted required outputs for current phase.
|
|
389
|
-
* @param {string} featureName
|
|
390
|
-
* @param {string} [projectPath]
|
|
391
|
-
* @returns {Array<{type: string, path: string}>}
|
|
392
|
-
*/
|
|
393
|
-
/**
|
|
394
|
-
* Get task statistics directly from taskList (preferred over feature.tasks counters).
|
|
395
|
-
* Excludes expanded tasks from counts, matching calculateProgress() behavior.
|
|
396
|
-
*
|
|
397
|
-
* @param {Object} feature - Feature state object
|
|
398
|
-
* @returns {{ total: number, completed: number, inProgress: number, pending: number }}
|
|
399
|
-
*/
|
|
400
|
-
export function getTaskStats(feature) {
|
|
401
|
-
const list = feature?.taskList || [];
|
|
402
|
-
if (list.length > 0) {
|
|
403
|
-
const active = list.filter(t => t.status !== 'expanded');
|
|
404
|
-
return {
|
|
405
|
-
total: active.length,
|
|
406
|
-
completed: active.filter(t => t.status === 'completed').length,
|
|
407
|
-
inProgress: active.filter(t => t.status === 'in_progress').length,
|
|
408
|
-
pending: active.filter(t => t.status === 'pending').length,
|
|
409
|
-
};
|
|
410
|
-
}
|
|
411
|
-
// Fallback: read from legacy counter object
|
|
412
|
-
const tasks = feature?.tasks;
|
|
413
|
-
if (tasks && typeof tasks === 'object' && !Array.isArray(tasks)) {
|
|
414
|
-
return {
|
|
415
|
-
total: tasks.total || 0,
|
|
416
|
-
completed: tasks.completed || 0,
|
|
417
|
-
inProgress: tasks.inProgress || 0,
|
|
418
|
-
pending: tasks.pending || 0,
|
|
419
|
-
};
|
|
420
|
-
}
|
|
421
|
-
return { total: 0, completed: 0, inProgress: 0, pending: 0 };
|
|
422
|
-
}
|
|
423
|
-
|
|
424
398
|
export function getMissingOutputs(featureName, projectPath) {
|
|
425
399
|
const feature = getFeature(featureName, projectPath);
|
|
426
400
|
if (!feature?.outputs) return [];
|
|
@@ -101,7 +101,7 @@ export function persistFeature(featureName, feature, projectPath) {
|
|
|
101
101
|
* canonical shape — onto the feature and persists it. Returns true on success.
|
|
102
102
|
*
|
|
103
103
|
* @param {string} featureName
|
|
104
|
-
* @param {string} gate - '
|
|
104
|
+
* @param {string} gate - 'plan'|'uiux'|'review'
|
|
105
105
|
* @param {{ approvedBy?: string, timestamp?: string }} [meta]
|
|
106
106
|
* @param {string} [projectPath]
|
|
107
107
|
*/
|
|
@@ -114,6 +114,7 @@ export function setGateApproved(featureName, gate, meta = {}, projectPath) {
|
|
|
114
114
|
approved: true,
|
|
115
115
|
timestamp: meta.timestamp || now,
|
|
116
116
|
approvedBy: meta.approvedBy || 'manual',
|
|
117
|
+
...(meta.selfAssessed ? { selfAssessed: true } : {}),
|
|
117
118
|
};
|
|
118
119
|
feature.updatedAt = now;
|
|
119
120
|
return persistFeature(featureName, feature, projectPath);
|
|
@@ -141,9 +142,8 @@ export function createDefaultFeature(featureName) {
|
|
|
141
142
|
createdAt: now,
|
|
142
143
|
updatedAt: now,
|
|
143
144
|
approvalGates: {
|
|
144
|
-
proposal: { approved: false, timestamp: null, approvedBy: null },
|
|
145
|
-
uiux: { approved: false, timestamp: null, approvedBy: null },
|
|
146
145
|
plan: { approved: false, timestamp: null, approvedBy: null },
|
|
146
|
+
uiux: { approved: false, timestamp: null, approvedBy: null },
|
|
147
147
|
review: { approved: false, timestamp: null, approvedBy: null },
|
|
148
148
|
},
|
|
149
149
|
outputs: {},
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"description": "Centralized MCP registry with phase relevance and install configuration for morph-spec workflows",
|
|
4
4
|
"mcps": {
|
|
5
5
|
"context7": {
|
|
6
|
-
"phases": ["
|
|
6
|
+
"phases": ["plan", "implement"],
|
|
7
7
|
"tools": ["resolve-library-id", "query-docs"],
|
|
8
8
|
"serverNames": ["context7", "plugin_context7_context7"],
|
|
9
9
|
"detection": "enabledPlugins includes context7 or mcpServers includes context7",
|
|
@@ -216,7 +216,7 @@
|
|
|
216
216
|
}
|
|
217
217
|
},
|
|
218
218
|
"graphify": {
|
|
219
|
-
"phases": ["
|
|
219
|
+
"phases": ["plan", "implement", "review"],
|
|
220
220
|
"tools": ["query_graph", "get_node", "get_neighbors", "get_community", "god_nodes", "graph_stats", "shortest_path", "list_prs", "get_pr_impact", "triage_prs"],
|
|
221
221
|
"detection": "graphify-out/graph.json exists in the project root (written by morph-spec graph build, or by the graphify post-commit hook)",
|
|
222
222
|
"fallback": "Comportamento atual: Grep/Glob/Read. O grafo é um subconjunto estrito do grep — no eval não encontrou um único arquivo que o grep perderia — então cair para grep não perde recall, só devolve mais ruído",
|
|
@@ -281,9 +281,8 @@
|
|
|
281
281
|
},
|
|
282
282
|
"_phaseMatrixNote": "Keys must be the v5 phases from framework/phases.json. The v4 keys 'design' and 'tasks' both collapsed into 'plan'; 'review' was absent entirely, so a project in review resolved to undefined. Stack-scoped MCPs (asaas, neon) stay out of this matrix on purpose — only universal ones are listed.",
|
|
283
283
|
"phaseMatrix": {
|
|
284
|
-
"proposal": ["context7", "graphify"],
|
|
285
|
-
"uiux": ["playwright", "context7", "stitch", "morph-assets"],
|
|
286
284
|
"plan": ["context7", "graphify"],
|
|
285
|
+
"uiux": ["playwright", "context7", "stitch", "morph-assets"],
|
|
287
286
|
"implement": ["context7", "playwright", "vercel", "morph-assets", "graphify"],
|
|
288
287
|
"review": ["playwright", "context7", "graphify"]
|
|
289
288
|
}
|
package/framework/phases.json
CHANGED
|
@@ -1,22 +1,22 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": "5.0.0",
|
|
3
3
|
"phases": {
|
|
4
|
-
"
|
|
5
|
-
"id": "
|
|
4
|
+
"plan": {
|
|
5
|
+
"id": "plan",
|
|
6
6
|
"index": 0,
|
|
7
|
-
"folder": "
|
|
8
|
-
"displayName": "
|
|
7
|
+
"folder": "2-plan",
|
|
8
|
+
"displayName": "Plan",
|
|
9
9
|
"goals": [
|
|
10
|
-
"Understand the business problem and
|
|
11
|
-
"
|
|
12
|
-
"
|
|
10
|
+
"Understand the business problem and scan the stack (brownfield) or assume the default (greenfield)",
|
|
11
|
+
"Write plan.md — scenarios with proof, decisions, reuse and slices",
|
|
12
|
+
"Run plan-check to produce checks.json and reach the plan gate"
|
|
13
13
|
],
|
|
14
|
-
"requiredOutputs": ["
|
|
15
|
-
"optionalOutputs": [
|
|
14
|
+
"requiredOutputs": ["plan", "checks"],
|
|
15
|
+
"optionalOutputs": [],
|
|
16
16
|
"pausePoints": [
|
|
17
|
-
{ "id": "
|
|
17
|
+
{ "id": "plan", "label": "Gate do plano — Approve plan.md + checks.json" }
|
|
18
18
|
],
|
|
19
|
-
"skill": "morph-
|
|
19
|
+
"skill": "morph-plan"
|
|
20
20
|
},
|
|
21
21
|
"uiux": {
|
|
22
22
|
"id": "uiux",
|
|
@@ -35,50 +35,34 @@
|
|
|
35
35
|
],
|
|
36
36
|
"skill": "morph-uiux"
|
|
37
37
|
},
|
|
38
|
-
"plan": {
|
|
39
|
-
"id": "plan",
|
|
40
|
-
"index": 2,
|
|
41
|
-
"folder": "2-plan",
|
|
42
|
-
"displayName": "Plan",
|
|
43
|
-
"goals": [
|
|
44
|
-
"Write spec.md (technical spec)",
|
|
45
|
-
"Write tasks.json (ordered task breakdown)"
|
|
46
|
-
],
|
|
47
|
-
"requiredOutputs": ["spec", "tasks"],
|
|
48
|
-
"optionalOutputs": ["decisions"],
|
|
49
|
-
"pausePoints": [
|
|
50
|
-
{ "id": "plan", "label": "Gate 2 — Approve spec + tasks" }
|
|
51
|
-
],
|
|
52
|
-
"skill": "morph-plan"
|
|
53
|
-
},
|
|
54
38
|
"implement": {
|
|
55
39
|
"id": "implement",
|
|
56
|
-
"index":
|
|
40
|
+
"index": 2,
|
|
57
41
|
"folder": "3-implement",
|
|
58
42
|
"displayName": "Implementation",
|
|
59
43
|
"goals": [
|
|
60
|
-
"Execute
|
|
61
|
-
"
|
|
62
|
-
"
|
|
44
|
+
"Execute the slices of plan.md, in parallel waves when plan-check allows",
|
|
45
|
+
"Close a slice only on verify green and its scenarios proven",
|
|
46
|
+
"One report per slice (3-implement/reports/F{n}.md, with **Arquivos:** and **Standards:**) — the implementation record"
|
|
63
47
|
],
|
|
64
|
-
"requiredOutputs": [
|
|
48
|
+
"requiredOutputs": [],
|
|
65
49
|
"optionalOutputs": ["evidencias"],
|
|
66
50
|
"pausePoints": [],
|
|
67
|
-
"skill": "morph-
|
|
51
|
+
"skill": "morph-apply"
|
|
68
52
|
},
|
|
69
53
|
"review": {
|
|
70
54
|
"id": "review",
|
|
71
|
-
"index":
|
|
55
|
+
"index": 3,
|
|
72
56
|
"folder": "4-review",
|
|
73
57
|
"displayName": "Review",
|
|
74
58
|
"goals": [
|
|
75
|
-
"
|
|
76
|
-
"
|
|
59
|
+
"Verifier proves every scenario; reviewer (and security, when required) judge the diff at a fixed SHA",
|
|
60
|
+
"verifier.md + reviewer.md (+ security.md) with SHIP/FIX and evidence-typed blockers, then Gate 3"
|
|
77
61
|
],
|
|
78
|
-
"requiredOutputs": ["
|
|
79
|
-
"optionalOutputs": [],
|
|
62
|
+
"requiredOutputs": ["verifierReport", "reviewerReport"],
|
|
63
|
+
"optionalOutputs": ["securityReport"],
|
|
80
64
|
"pausePoints": [
|
|
81
|
-
{ "id": "review", "label": "Gate 3 — Approve review
|
|
65
|
+
{ "id": "review", "label": "Gate 3 — Approve the review reports" }
|
|
82
66
|
],
|
|
83
67
|
"skill": "morph-review"
|
|
84
68
|
}
|
|
@@ -9,26 +9,28 @@ stacks:
|
|
|
9
9
|
|
|
10
10
|
# Microsoft Agent Framework Standards
|
|
11
11
|
|
|
12
|
-
> **Layer 0/1 —
|
|
13
|
-
> agente MAF.
|
|
12
|
+
> **Layer 0/1 — resumo injetado pela rule.** São os standards core: aplicam-se a praticamente todo
|
|
13
|
+
> agente MAF. O resumo de cada um entra no contexto quando você toca código de agentes.
|
|
14
14
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
15
|
+
- **ai-agents-setup** (`ai-agents/setup.md`) — Versões exatas: Microsoft.Agents.AI 1.20.0, Microsoft.Extensions.AI 10.9.0, OpenAI 2.13.0. OpenAI pelo Responses API (`GetResponsesClient().AsAIAgent(...)`); Chat Completions só com `"api": "chat"`; `store: false`. PROIBIDO: orquestração Semantic Kernel, `AgentOpenTelemetryConsts` (não existe).
|
|
16
|
+
- **ai-agents-sweet-spot** (`ai-agents/sweet-spot.md`) — Chat + Structured Output + Tools + RAG custom cobre 95%. Single-agent é o padrão; multi-agent/workflow só com fan-in/out ou handoff real. Teto prático de ~12 tools por agente (podar, depois allow-list por fase, depois agent-as-tool). Leia no plan, antes de decidir a arquitetura.
|
|
17
|
+
- **ai-agents-providers-model-registry** (`ai-agents/providers/model-registry.md`) — `model-registry.json` v2 mapeia alias → { provider, model, api, options, constraints }; agente usa alias, nunca string de modelo. Todo alias declara `api` (`responses` padrão, `chat` opt-in). `temperature` e `reasoningEffort` são mutuamente exclusivos por alias. Sem range flutuante em PackageReference.
|
|
18
|
+
- **ai-agents-structured-output** (`ai-agents/structured-output.md`) — Saída que alimenta outro sistema é record tipado (`RunAsync<T>` ou `ChatResponseFormat.ForJsonSchema<T>()`): `sealed record` com `[Description]` e `[JsonPropertyName]`. Raiz nunca primitivo/array (use wrapper); em streaming, `ToAgentResponseAsync()` antes de desserializar. Regra de negócio se valida depois da chamada.
|
|
19
|
+
- **ai-agents-multi-agent-patterns** (`ai-agents/multi-agent-patterns.md`) — Do mais barato: agent-as-tool (`AsAIFunction()`, aninhamento ≤ 2) → sequencial → workflow; handoff vem depois de agent-as-tool, com Id estável e `WithAutonomousMode(turnLimit:)`; group chat ≤ 3 agentes. PROIBIDO: agente auxiliar DENTRO de uma tool (dois loops de function calling no mesmo turno).
|
|
20
|
+
- **ai-agents-prompt-sources** (`ai-agents/prompt-sources.md`) — Prompt fixo: tabela versionada `agent_prompts` (INSERT + flip de `is_active` numa transação, nunca UPDATE; id `Guid.CreateVersion7()`). Prompt por tenant: YAML no repo → compositor determinístico → spec renderizada em jsonb. O registro de execução grava a versão EFETIVA (e o hash do blob no caminho composto).
|
|
21
|
+
- **ai-agents-rag-custom-pgvector** (`ai-agents/rag-custom-pgvector.md`) — RAG em pgvector via EF Core: `IEmbeddingGenerator` injetado direto (sem EmbeddingService), alias `embeddings` do registry, `CosineDistance` LINQ, HNSW `vector_cosine_ops`, `tenant_id` filtrado em toda query e nunca exposto ao LLM. Ingestão delta por lookup-key: Unchanged nunca re-embeda, Removed só depois das escritas.
|
|
22
|
+
- **ai-agents-middleware-patterns** (`ai-agents/middleware-patterns.md`) — Registro sempre `.AsBuilder().Use(...).Build()`; o PRIMEIRO registrado é o mais externo. Ordem medida: timeout → usage-cost → FunctionInvokingChatClient → chat telemetry → provider. Só o delegate não-streaming rebaixa streaming em silêncio: use `Use(sharedFunc)`.
|
|
23
|
+
- **ai-agents-observability-patterns** (`ai-agents/observability-patterns.md`) — Registre os DOIS ActivitySources (`Experimental.Microsoft.Agents.AI` e `Experimental.Microsoft.Extensions.AI`); a API é `builder.UseOpenTelemetry(...)` (`agent.WithOpenTelemetry()` não existe). Instrumentar os dois níveis é normal; o mesmo nível duas vezes duplica. Prompt cru só em dev com opt-in, hash em produção; turno cancelado não grava nada.
|
|
24
|
+
- **ai-agents-workflows** (`ai-agents/workflows.md`) — Raro: só para fan-in/out real ou HITL longo; `foreach` + LINQ costuma bastar. `AgentWorkflowBuilder`/`WorkflowBuilder`/`InProcessExecution` vivem no pacote SEPARADO `Microsoft.Agents.AI.Workflows` 1.20.0. Agente declarativo (`Microsoft.Agents.AI.Declarative`) ainda pede `--prerelease`.
|
|
25
|
+
- **ai-agents-production** (`ai-agents/production.md`) — A2A (`Microsoft.Agents.AI.A2A`) é PRERELEASE. Isolamento multi-tenant (`AgentIsolationKeyProvider`, `UseClaimsBasedAgentIsolation`) com a chave vinda da credencial, nunca do payload. `AllowConcurrentInvocation` é opt-in e inseguro com DbContext scoped. Cache: Redis semântico + HybridCache.
|
|
26
|
+
|
|
27
|
+
> Resumo injetado quando você lê ou edita um arquivo casado por `paths:`. Texto completo: `morph-spec standards --find "<id>"` e leia só a seção necessária de `.morph/framework/standards/<path>`.
|
|
26
28
|
|
|
27
29
|
---
|
|
28
30
|
|
|
29
|
-
> **Layer 2 — sob demanda (
|
|
30
|
-
> MCP: relevantes apenas quando a feature usa especificamente esse recurso.
|
|
31
|
-
> `morph-standards` quando uma keyword corresponder — manter fora do
|
|
31
|
+
> **Layer 2 — sob demanda (sem resumo aqui).** São standards de modalidade, otimização e
|
|
32
|
+
> MCP: relevantes apenas quando a feature usa especificamente esse recurso. Busque via
|
|
33
|
+
> `morph-spec standards --find` (skill `morph-standards`) quando uma keyword corresponder — manter fora do resumo preserva o
|
|
32
34
|
> context window para o trabalho que importa.
|
|
33
35
|
|
|
34
36
|
| Standard | Carregue quando a feature envolve |
|
|
@@ -10,4 +10,6 @@ stacks:
|
|
|
10
10
|
|
|
11
11
|
# Asaas Payment Integration Standards
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
- **backend-integrations-asaas-asaas-api** (`backend/integrations/asaas/asaas-api.md`) — Só o backend .NET chama o Asaas (o Next passa pelo proxy): `AsaasClient` HttpClient tipado, base URL com barra final e paths relativos, headers `access_token` + `User-Agent`; respostas viram `Result`. Sem cobrança duplicada: `externalReference` + GET antes de criar, nunca retry automático em cartão. Webhook: `asaas-access-token` comparado com `FixedTimeEquals`, id de evento deduplicado. Nunca guarde dado de cartão.
|
|
14
|
+
|
|
15
|
+
> Resumo injetado quando você lê ou edita um arquivo casado por `paths:`. Texto completo: `morph-spec standards --find "<id>"` e leia só a seção necessária de `.morph/framework/standards/<path>`.
|
|
@@ -8,11 +8,13 @@ stacks:
|
|
|
8
8
|
|
|
9
9
|
# C# and .NET VSA Standards
|
|
10
10
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
11
|
+
- **architecture-vertical-slice-vertical-slice** (`architecture/vertical-slice/vertical-slice.md`) — Uma slice por operação em `Features/{Entity}Feature/{Verb}{Entity}/`: Request/Response `sealed record`, Handler, Validator e Endpoint `IApiEndpoint` `internal sealed`. Decorators Scrutor: Logging → Validation → Handler. PROIBIDO: MediatR/`IRequest`, AggregateRoot, DomainEvent, CQRS formal, camada Application Service, DTO compartilhado entre features, `IRepository<T>`/`IUnitOfWork`.
|
|
12
|
+
- **backend-dotnet-vsa-handler-patterns** (`backend/dotnet/vsa-handler-patterns.md`) — Handler `sealed class` com primary constructor implementando `IHandler<TRequest, TResponse>` (não MediatR): injeta `ApplicationDbContext` direto, usa `Guid.CreateVersion7()` (nunca `Guid.NewGuid()`), passa `CancellationToken` em tudo e devolve `Result<T>`. Erros em `{Feature}Errors.cs`. SQL cru ou store dedicado só com justificativa em `decisions.md`.
|
|
13
|
+
- **backend-dotnet-result-pattern** (`backend/dotnet/result-pattern.md`) — Falha esperada é `Result`/`Result<T>`, nunca exceção. Endpoint: `result.Match(value => Results.Ok(value), error => error.ToHttpResult())`; nunca leia `.Value` fora do `Match`. Status pelo `ErrorType` (Validation 400, NotFound 404, Conflict 409, Unauthorized 401, Forbidden 403, Unexpected 500), nunca BadRequest para tudo. PROIBIDO: try/catch no handler, retornar null, erro string sem código.
|
|
14
|
+
- **backend-dotnet-fluent-validation-vsa** (`backend/dotnet/fluent-validation-vsa.md`) — Um `sealed AbstractValidator<TRequest>` (FluentValidation 12) por request, na pasta da slice; o `ValidationDecorator` roda antes do handler e devolve `Result.Failure(ValidationError)`. `AddValidatorsFromAssembly()` ANTES de `AddHandlersFromAssembly()`. Sempre `.WithMessage()`; tempo via `TimeProvider` lido DENTRO da regra. Nunca lance `ValidationException`.
|
|
15
|
+
- **backend-dotnet-scrutor** (`backend/dotnet/scrutor.md`) — `AddHandlersFromAssembly` registra cada `IHandler<,>` como Scoped (filtro `!t.ContainsGenericParameters`) e depois `Decorate` Validation e então Logging (o último registrado é o mais externo). Endpoints `IApiEndpoint` via `TryAddEnumerable` Transient + `MapApiEndpoints`. Nunca registre handler um a um.
|
|
16
|
+
- **backend-dotnet-scalar-ui** (`backend/dotnet/scalar-ui.md`) — Scalar no lugar do Swagger: `Microsoft.AspNetCore.OpenApi` 10.0.0 + `Scalar.AspNetCore` 2.12.40, nunca Swashbuckle. `MapOpenApi()` só em `IsDevelopment()` e antes de `MapScalarApiReference`. Todo endpoint com `.WithTags(ApiTags.X)` (constantes), `.Produces<T>` e `.ProducesProblem(status)` por falha possível.
|
|
17
|
+
- **backend-api-validation** (`backend/api/validation.md`) — O validator checa só FORMA (sem DbContext); existência, unicidade e estado ficam no handler com `{Entity}Errors` tipados. Checagem de estado valida o INVÁLIDO (enum ordenado, estados de erro em 100+). PROIBIDO: lançar exceção, validar na lambda do endpoint, misturar DataAnnotations, `if (status != Expected)`, switch exaustivo sobre estados válidos.
|
|
18
|
+
- **architecture-code-comments** (`architecture/code-comments.md`) — Padrão é nenhum comentário: renomeie, extraia, tipe ou teste antes. Só o porquê que o código não mostra (invariante, ordem, quirk externo com link), até ~3 linhas. PROIBIDO: história de processo (task, UC, ADR, Gate), repetir o código, XML doc/JSDoc que repete a assinatura, banner, `#region`, código comentado, TODO solto. O hook `comment-guard` bloqueia.
|
|
19
|
+
|
|
20
|
+
> Resumo injetado quando você lê ou edita um arquivo casado por `paths:`. Texto completo: `morph-spec standards --find "<id>"` e leia só a seção necessária de `.morph/framework/standards/<path>`.
|
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
paths:
|
|
3
3
|
- "**/*Cache*.cs"
|
|
4
4
|
- "**/*Redis*.cs"
|
|
5
|
-
- "**/*Cosmos*.cs"
|
|
6
5
|
- "**/*Vector*.cs"
|
|
7
6
|
- "**/*Embedding*.cs"
|
|
8
7
|
- "**/*Rag*.cs"
|
|
@@ -12,7 +11,8 @@ stacks:
|
|
|
12
11
|
|
|
13
12
|
# Data & Cache Standards
|
|
14
13
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
14
|
+
- **data-nosql-cache-redis** (`data/nosql/cache/redis.md`) — Cache distribuído, nunca store primário. `Microsoft.Extensions.Caching.StackExchangeRedis` 10.0.12; `IConnectionMultiplexer` singleton LAZY (nunca conectar no registro). Cache-aside no handler com `IDistributedCache` (sem wrapper); invalide a chave exata depois do `SaveChangesAsync`. PROIBIDO: cachear segredo/PII sem criptografia, omitir TTL, engolir `RedisConnectionException`.
|
|
15
|
+
- **data-vector-search-rag-chunking** (`data/vector-search/rag-chunking.md`) — Prosa: sentence-aware, 256 tokens; doc técnico: semântico por headers; Q&A inteiro; código: a função inteira. Overlap 10–20%, mínimo 50 e máximo ~512 tokens. Sempre grave metadados do chunk (DocumentId, ChunkIndex, Section, SourceUrl) e reindexe quando o hash do conteúdo mudar.
|
|
16
|
+
- **backend-database-vector-search-rag** (`backend/database/vector-search-rag.md`) — Slices VSA sobre EF Core 10 + pgvector: o handler injeta `ApplicationDbContext` e `IEmbeddingGenerator` direto (sem EmbeddingService/SearchService). Busca por `d.Embedding!.CosineDistance(query)` com a distância em ordem ASC. Sem documento relevante, `NotFound` antes de chamar o LLM. Indexação em lote: um `GenerateAsync` e um `SaveChanges`.
|
|
17
|
+
|
|
18
|
+
> Resumo injetado quando você lê ou edita um arquivo casado por `paths:`. Texto completo: `morph-spec standards --find "<id>"` e leia só a seção necessária de `.morph/framework/standards/<path>`.
|
|
@@ -30,7 +30,7 @@ morph-spec scaffold dotnet book get-all-books
|
|
|
30
30
|
|
|
31
31
|
Generates 7 files: endpoint, handler, request, response, validator + entity, errors (shared at feature level). Entity/errors auto-skipped if they already exist.
|
|
32
32
|
|
|
33
|
-
**
|
|
33
|
+
**Adding a single file to an existing slice:** read its `.hbs` template (table below) and write the file following it — `scaffold` generates whole slices only, and there is no single-file render command.
|
|
34
34
|
|
|
35
35
|
## VSA Feature Templates (Vertical Slice Architecture)
|
|
36
36
|
|
|
@@ -9,5 +9,7 @@ stacks:
|
|
|
9
9
|
|
|
10
10
|
# Frontend Standards
|
|
11
11
|
|
|
12
|
-
|
|
13
|
-
|
|
12
|
+
- **frontend-design-system-naming** (`frontend/design-system/naming.md`) — BEM para componente reutilizável (`.card`, `.card__header`, `.card--featured`); prefixo de página para elemento específico (`.home-hero`). Custom properties em `:root` com namespace `--app-`. Estado por sufixo fixo: `--active`, `--disabled`, `--loading`, `--error`.
|
|
13
|
+
- **frontend-design-system-animations** (`frontend/design-system/animations.md`) — Classes `.animate-*`, `.duration-fast/normal/slow`, `.stagger-1..10`. Lista escalonada exige `animation-delay` E `animation-fill-mode: backwards`. Sempre o bloco `prefers-reduced-motion: reduce`. Além do CSS: Motion (`motion/react`) para enter/exit/layout; GSAP só para ScrollTrigger, SVG morph e timelines.
|
|
14
|
+
|
|
15
|
+
> Resumo injetado quando você lê ou edita um arquivo casado por `paths:`. Texto completo: `morph-spec standards --find "<id>"` e leia só a seção necessária de `.morph/framework/standards/<path>`.
|
|
@@ -1,13 +1,18 @@
|
|
|
1
1
|
---
|
|
2
2
|
paths:
|
|
3
|
-
- "
|
|
4
|
-
- "
|
|
3
|
+
- "**/animations/**"
|
|
4
|
+
- "**/*gsap*"
|
|
5
|
+
- "**/*Gsap*"
|
|
6
|
+
- "**/*scroll-trigger*"
|
|
7
|
+
- "**/*ScrollTrigger*"
|
|
5
8
|
stacks:
|
|
6
9
|
- nextjs
|
|
7
10
|
---
|
|
8
11
|
|
|
9
12
|
# GSAP Animation Standards
|
|
10
13
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
+
- **frontend-gsap-core** (`frontend/gsap/gsap-core.md`) — GSAP 3.13 + `@gsap/react` só para ScrollTrigger, SVG morph/draw, timelines complexas e SplitText; o resto é Motion, e NUNCA os dois no mesmo elemento. Código em `'use client'` dentro de `useGSAP()` com `{ scope: containerRef }` (nunca `useEffect`). Só transform/opacity; `prefers-reduced-motion` via `gsap.matchMedia()` é obrigatório; durations/easings vêm de `@/lib/design-tokens`.
|
|
15
|
+
- **frontend-gsap-scrolltrigger** (`frontend/gsap/gsap-scrolltrigger.md`) — `gsap.registerPlugin(ScrollTrigger)` uma vez no init, nunca em `useEffect`/loop, e nunca `ScrollTrigger.create()` no escopo do módulo. Scrub com ease `'none'` (0.3–1 em produção). `pin: true` + `anticipatePin: 1`; nunca pin aninhado. `ScrollTrigger.batch` em grids de 20+. `ScrollTrigger.refresh()` após conteúdo dinâmico, troca de rota e `onLoad` de imagem.
|
|
16
|
+
- **frontend-gsap-plugins** (`frontend/gsap/gsap-plugins.md`) — Todo plugin é gratuito no pacote público `gsap` (`npm install gsap @gsap/react`); o registry privado `@gsap/shockingly` é obsoleto. Flip: `getState()` → muda o DOM → `Flip.from(...)`. SplitText com `autoSplit: true`, animando em `onSplit`. MorphSVG exige `<path>`; DrawSVG exige stroke visível; Draggable com inércia exige InertiaPlugin.
|
|
17
|
+
|
|
18
|
+
> Resumo injetado quando você lê ou edita um arquivo casado por `paths:`. Texto completo: `morph-spec standards --find "<id>"` e leia só a seção necessária de `.morph/framework/standards/<path>`.
|
|
@@ -10,4 +10,6 @@ stacks:
|
|
|
10
10
|
|
|
11
11
|
# Hangfire Background Jobs Standards
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
- **backend-integrations-hangfire-hangfire-jobs** (`backend/integrations/hangfire/hangfire-jobs.md`) — Hangfire 1.8.25 só com razão real em `decisions.md`; o que cabe no request fica no handler. Storage `Hangfire.PostgreSql` 1.21.1 com `UseNpgsqlConnection` no endpoint DIRETO do Neon; referencie `Newtonsoft.Json` 13.0.4. Job é classe fina e idempotente que chama o handler; `[AutomaticRetry]`, sem try/catch nem retry manual. Dashboard exige Admin autenticado.
|
|
14
|
+
|
|
15
|
+
> Resumo injetado quando você lê ou edita um arquivo casado por `paths:`. Texto completo: `morph-spec standards --find "<id>"` e leia só a seção necessária de `.morph/framework/standards/<path>`.
|
|
@@ -10,8 +10,10 @@ stacks:
|
|
|
10
10
|
|
|
11
11
|
# Infrastructure Standards
|
|
12
12
|
|
|
13
|
-
|
|
14
|
-
|
|
13
|
+
- **infrastructure-docker-coolify-deploy** (`infrastructure/docker/coolify-deploy.md`) — Acesso só por SSH na VPS, API do Coolify em `localhost:8000/api/v1` com token server-side (nunca impresso). Env vars no Coolify, nunca no Dockerfile. Endpoint `/health`. Redeploy por `POST /api/v1/deploy?uuid=` (GET não deploya); sem `deployment_uuid` na resposta, nada foi deployado.
|
|
14
|
+
- **infrastructure-docker-local-compose-isolation** (`infrastructure/docker/local-compose-isolation.md`) — Cada worktree roda o próprio stack compose; portas do bloco da worktree (`PORT`, `API_PORT`, `POSTGRES_PORT`, `REDIS_PORT`) publicadas como `${VAR:-default}:container`. PROIBIDO: porta fixa no host, `container_name`. Healthcheck em todo serviço. Compose que lê a porta por outro nome: declare em `e2e.portEnvNames`.
|
|
15
|
+
|
|
16
|
+
> Resumo injetado quando você lê ou edita um arquivo casado por `paths:`. Texto completo: `morph-spec standards --find "<id>"` e leia só a seção necessária de `.morph/framework/standards/<path>`.
|
|
15
17
|
|
|
16
18
|
## Infrastructure Templates
|
|
17
19
|
|
|
@@ -10,4 +10,6 @@ stacks:
|
|
|
10
10
|
|
|
11
11
|
# Neon Auth Standards
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
- **backend-integrations-neon-auth-neon-auth** (`backend/integrations/neon-auth/neon-auth.md`) — SDK `@neondatabase/auth`: `createNeonAuth()` no server, `createAuthClient()` no client, `auth.middleware()` com `runtime='nodejs'`. Duas constantes de prefixo (`BACKEND_API_PREFIX`/`PROXY_API_PREFIX`), nunca uma só. O front não consulta o Neon para dado de negócio. .NET: token EdDSA/Ed25519, verificado pelo JWKS com BouncyCastle + `SignatureValidator` devolvendo `JsonWebToken`.
|
|
14
|
+
|
|
15
|
+
> Resumo injetado quando você lê ou edita um arquivo casado por `paths:`. Texto completo: `morph-spec standards --find "<id>"` e leia só a seção necessária de `.morph/framework/standards/<path>`.
|
|
@@ -10,10 +10,12 @@ stacks:
|
|
|
10
10
|
|
|
11
11
|
# Neon PostgreSQL Standards
|
|
12
12
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
13
|
+
- **infrastructure-neon-setup** (`infrastructure/neon/neon-setup.md`) — Duas connection strings: `DefaultConnection` (pooled `-pooler`) para a app e `MigrationConnection` (direta) para migrations e advisory locks; nunca commitadas (user-secrets / env do Coolify). Formato keyword (`Host=...;SSL Mode=Require`), não URI. `EnableRetryOnFailure(5, 10s)` por causa do scale-to-zero. Branch sempre com `--expires-at`.
|
|
14
|
+
- **infrastructure-neon-pgvector** (`infrastructure/neon/neon-pgvector.md`) — `Npgsql.EntityFrameworkCore.PostgreSQL` 10.0.3 + `Pgvector.EntityFrameworkCore` 0.3.0; coluna `Vector` (`vector(1536)`) e `UseVector()` no `UseNpgsql`. SEMPRE índice HNSW com operator class casando a função LINQ (senão seq scan); distância em ordem ASC. PROIBIDO: DocumentRepository, SQL cru do C# sem `decisions.md`, default `gen_random_uuid()`.
|
|
15
|
+
- **infrastructure-neon-pgrag** (`infrastructure/neon/neon-pgrag.md`) — A extensão `rag` é EXPERIMENTAL: protótipo e RAG só-SQL, não produção sem teste. Nunca misture modelos de embedding numa coluna (bge-small 384 × text-embedding-3-small 1536). Índice HNSW depois da carga inicial. Produção .NET com lógica própria: tool MAF tipada sobre pgvector.
|
|
16
|
+
- **backend-integrations-neon-auth-neon-auth** (`backend/integrations/neon-auth/neon-auth.md`) — SDK `@neondatabase/auth`: `createNeonAuth()` no server, `createAuthClient()` no client, `auth.middleware()` com `runtime='nodejs'`. Duas constantes de prefixo (`BACKEND_API_PREFIX`/`PROXY_API_PREFIX`), nunca uma só. O front não consulta o Neon para dado de negócio. .NET: token EdDSA/Ed25519, verificado pelo JWKS com BouncyCastle + `SignatureValidator` devolvendo `JsonWebToken`.
|
|
17
|
+
|
|
18
|
+
> Resumo injetado quando você lê ou edita um arquivo casado por `paths:`. Texto completo: `morph-spec standards --find "<id>"` e leia só a seção necessária de `.morph/framework/standards/<path>`.
|
|
17
19
|
|
|
18
20
|
## SQL Templates
|
|
19
21
|
|