@polymorphism-tech/morph-spec 4.12.0 → 4.13.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/README.md +21 -29
- package/bin/morph-spec.js +41 -24
- package/bin/task-manager.js +962 -1341
- package/bin/validate.js +1 -1
- package/claude-plugin.json +1 -1
- package/docs/CHEATSHEET.md +10 -11
- package/docs/QUICKSTART.md +15 -15
- package/framework/CLAUDE.md +5 -2
- package/framework/agents/README.md +3 -4
- package/framework/agents/ai-agents/ai-system-architect.md +6 -10
- package/framework/agents/architecture/seo-growth-hacker.md +166 -326
- package/framework/agents/architecture/standards-architect.md +21 -31
- package/framework/agents/backend/dotnet-senior.md +160 -86
- package/framework/agents/backend/ef-modeler.md +2 -6
- package/framework/agents/backend/hangfire-orchestrator.md +8 -9
- package/framework/agents/backend/ms-agent-expert.md +1 -1
- package/framework/agents/frontend/nextjs-expert.md +198 -133
- package/framework/agents/frontend/ui-ux-designer.md +226 -267
- package/framework/agents/infrastructure/infra-architect.md +191 -27
- package/framework/agents/integrations/clerk-auth.md +19 -26
- package/framework/agents/integrations/hangfire-integration.md +1 -4
- package/framework/agents.json +215 -1453
- package/framework/commands/morph-apply.md +4 -4
- package/framework/commands/morph-preflight.md +4 -26
- package/framework/commands/morph-proposal.md +19 -19
- package/framework/commands/morph-status.md +2 -2
- package/framework/commands/morph-troubleshoot.md +6 -13
- package/framework/hooks/README.md +1 -6
- package/framework/hooks/claude-code/post-tool-use/validator-feedback.js +4 -6
- package/framework/hooks/claude-code/pre-compact/save-morph-context.js +12 -7
- package/framework/hooks/claude-code/pre-tool-use/enforce-phase-writes.js +2 -2
- package/framework/hooks/claude-code/pre-tool-use/protect-spec-files.js +2 -2
- package/framework/hooks/claude-code/pre-tool-use/task-tracking-guard.js +5 -10
- package/framework/hooks/claude-code/session-start/inject-morph-context.js +63 -30
- package/framework/hooks/claude-code/session-start/resume-context.js +77 -0
- package/framework/hooks/claude-code/statusline.py +4 -5
- package/framework/hooks/claude-code/stop/validate-completion.js +4 -4
- package/framework/hooks/claude-code/user-prompt/enrich-prompt.js +8 -8
- package/framework/hooks/shared/compact-restore.js +28 -5
- package/framework/hooks/shared/phase-utils.js +4 -8
- package/framework/hooks/shared/state-reader.js +34 -3
- package/framework/phases.json +7 -45
- package/framework/rules/ai-agents-standards.md +15 -0
- package/framework/rules/asaas-standards.md +13 -0
- package/framework/rules/clerk-standards.md +13 -0
- package/framework/rules/csharp-standards.md +8 -4
- package/framework/rules/data-standards.md +18 -0
- package/framework/rules/dotnet-templates.md +87 -0
- package/framework/rules/frontend-standards.md +13 -13
- package/framework/rules/hangfire-standards.md +13 -0
- package/framework/rules/infrastructure-standards.md +27 -16
- package/framework/rules/morph-workflow.md +73 -13
- package/framework/rules/nextjs-standards.md +1 -0
- package/framework/rules/nextjs-templates.md +40 -0
- package/framework/rules/resend-standards.md +12 -0
- package/framework/rules/supabase-standards.md +29 -0
- package/framework/skills/README.md +6 -8
- package/framework/skills/level-0-meta/mcp-registry.json +21 -99
- package/framework/{standards/ai-agents/team-orchestration.md → skills/level-0-meta/morph-agent-teams/SKILL.md} +472 -479
- package/framework/skills/level-0-meta/morph-brainstorming/SKILL.md +28 -1
- package/framework/skills/level-0-meta/morph-checklist/SKILL.md +2 -2
- package/framework/skills/level-0-meta/morph-code-review/SKILL.md +1 -5
- package/framework/skills/level-0-meta/morph-code-review/scripts/scan-csharp.mjs +1 -1
- package/framework/skills/level-0-meta/morph-frontend-review/SKILL.md +23 -41
- package/framework/skills/level-0-meta/morph-init/SKILL.md +18 -14
- package/framework/skills/level-0-meta/morph-parallel-dispatch/SKILL.md +225 -0
- package/framework/skills/level-0-meta/morph-post-implementation/SKILL.md +20 -0
- package/framework/skills/level-0-meta/morph-tool-usage-guide/SKILL.md +4 -8
- package/framework/skills/level-0-meta/morph-tool-usage-guide/references/tools-per-phase.md +15 -59
- package/framework/skills/level-0-meta/morph-verification-before-completion/SKILL.md +7 -19
- package/framework/skills/level-0-meta/morph-verification-before-completion/scripts/check-phase-outputs.mjs +8 -8
- package/framework/skills/level-1-workflows/README.md +1 -1
- package/framework/skills/level-1-workflows/morph-phase-codebase-analysis/SKILL.md +49 -50
- package/framework/skills/level-1-workflows/morph-phase-design/SKILL.md +58 -124
- package/framework/skills/level-1-workflows/morph-phase-design/references/architecture-analysis-guide.md +5 -50
- package/framework/skills/level-1-workflows/morph-phase-design/references/spec-example.md +1 -1
- package/framework/skills/level-1-workflows/morph-phase-implement/SKILL.md +127 -159
- package/framework/skills/level-1-workflows/morph-phase-implement/prompts/implementer-prompt.md +4 -3
- package/framework/skills/level-1-workflows/morph-phase-implement/prompts/spec-reviewer-prompt.md +2 -2
- package/framework/skills/level-1-workflows/morph-phase-implement/references/recap-example.md +1 -1
- package/framework/skills/level-1-workflows/morph-phase-plan/SKILL.md +17 -13
- package/framework/skills/level-1-workflows/morph-phase-tasks/SKILL.md +22 -40
- package/framework/skills/level-1-workflows/morph-phase-tasks/references/task-planning-patterns.md +0 -2
- package/framework/skills/level-1-workflows/morph-phase-tasks/references/tasks-example.md +5 -5
- package/framework/skills/level-1-workflows/morph-phase-uiux/SKILL.md +57 -77
- package/framework/skills/level-1-workflows/morph-scope-escalation/SKILL.md +1 -1
- package/framework/squad-templates/backend-only.json +6 -6
- package/framework/squad-templates/frontend-only.json +17 -8
- package/framework/squad-templates/full-stack.json +29 -24
- package/framework/standards/STANDARDS.json +1021 -2369
- package/framework/standards/backend/dotnet/fluent-validation-vsa.md +200 -0
- package/framework/standards/backend/dotnet/result-pattern.md +213 -0
- package/framework/standards/backend/dotnet/scalar-ui.md +172 -0
- package/framework/standards/backend/dotnet/scrutor.md +158 -0
- package/framework/standards/backend/dotnet/vsa-handler-patterns.md +274 -0
- package/framework/standards/frontend/nextjs/motion-patterns.md +1 -1
- package/framework/standards/infrastructure/supabase/mcp-setup.md +83 -193
- package/framework/standards/integration/mcp/mcp-tools.md +91 -77
- package/framework/templates/IDE-SNIPPETS.md +2 -6
- package/framework/templates/README.md +709 -814
- package/framework/templates/REGISTRY.json +223 -762
- package/framework/templates/code/dotnet/abstractions/api-endpoint.cs +7 -0
- package/framework/templates/code/dotnet/abstractions/error.cs +60 -0
- package/framework/templates/code/dotnet/abstractions/handler.cs +12 -0
- package/framework/templates/code/dotnet/abstractions/result.cs +74 -0
- package/framework/templates/code/dotnet/backend/repository.cs +148 -141
- package/framework/templates/code/dotnet/constants/api-tags.cs +6 -0
- package/framework/templates/code/dotnet/exceptions/exception-handler.cs +32 -0
- package/framework/templates/code/dotnet/extensions/database-extensions.cs +20 -0
- package/framework/templates/code/dotnet/extensions/handler-registration.cs +38 -0
- package/framework/templates/code/dotnet/extensions/health-checks.cs +24 -0
- package/framework/templates/code/dotnet/extensions/map-endpoints.cs +35 -0
- package/framework/templates/code/dotnet/extensions/result-extensions.cs +29 -0
- package/framework/templates/code/dotnet/feature/endpoint.cs.hbs +21 -0
- package/framework/templates/code/dotnet/feature/entity.cs.hbs +8 -0
- package/framework/templates/code/dotnet/feature/errors.cs.hbs +8 -0
- package/framework/templates/code/dotnet/feature/handler.cs.hbs +24 -0
- package/framework/templates/code/dotnet/feature/request.cs.hbs +4 -0
- package/framework/templates/code/dotnet/feature/response.cs.hbs +4 -0
- package/framework/templates/code/dotnet/feature/validator.cs.hbs +15 -0
- package/framework/templates/code/dotnet/jobs/job.cs +21 -17
- package/framework/templates/code/dotnet/pipelines/logging-decorator.cs +31 -0
- package/framework/templates/code/dotnet/pipelines/validation-decorator.cs +97 -0
- package/framework/templates/code/dotnet/test.cs +192 -106
- package/framework/templates/context/ARCHITECTURE.md +89 -0
- package/framework/templates/context/CONTEXT-FEATURE.md +2 -2
- package/framework/templates/context/CONTEXT.md +180 -181
- package/framework/templates/context/design-system.md +1 -5
- package/framework/templates/docs/onboarding.md +2 -3
- package/framework/templates/docs/proposal.md +171 -182
- package/framework/templates/docs/spec.md +1 -3
- package/framework/templates/docs/ui-components.md +0 -72
- package/framework/templates/examples/design-system-examples.md +4 -4
- package/framework/templates/feature/decisions.md +0 -66
- package/framework/templates/feature/recap.md +1 -2
- package/framework/templates/infrastructure/github/README.md +56 -593
- package/framework/templates/integrations/clerk-config.cs +0 -41
- package/framework/templates/meta-prompts/parallel-workers/parallel-worker.md +2 -2
- package/framework/templates/meta-prompts/squad-leaders/backend-squad.md +1 -2
- package/framework/templates/meta-prompts/squad-leaders/frontend-squad.md +83 -126
- package/framework/templates/meta-prompts/validators/pre-commit-validator.md +2 -2
- package/framework/templates/project/validate.js +2 -4
- package/framework/templates/ui/design-system.css +1 -1
- package/framework/workflows/README.md +8 -8
- package/framework/workflows/configs/fast-track.json +51 -0
- package/framework/workflows/configs/full.json +68 -0
- package/framework/workflows/configs/standard.json +7 -3
- package/framework/workflows/docs/STORY-DRIVEN-DEVELOPMENT.md +1 -1
- package/framework/workflows/docs/enforcement-pipeline.md +18 -19
- package/framework/workflows/docs/full-morph.md +69 -76
- package/framework/workflows/docs/standard.md +49 -44
- package/framework/workflows/docs/ui-refresh.md +1 -1
- package/package.json +3 -3
- package/src/commands/agents/dispatch-agents.js +55 -116
- package/src/commands/context/context-refresh.js +79 -0
- package/src/commands/phase/phase-reset.js +1 -1
- package/src/commands/project/doctor.js +10 -32
- package/src/commands/project/init.js +12 -0
- package/src/commands/project/status.js +6 -12
- package/src/commands/scaffold/scaffold.js +131 -0
- package/src/commands/scope/escalate.js +1 -1
- package/src/commands/state/advance-phase.js +15 -29
- package/src/commands/state/state.js +20 -5
- package/src/commands/tasks/task.js +0 -30
- package/src/commands/validation/validate-feature.js +3 -10
- package/src/core/paths/output-schema.js +12 -13
- package/src/core/state/phase-state-machine.js +211 -217
- package/src/core/state/state-manager.js +9 -9
- package/src/lib/agents/agents-manager.js +2 -2
- package/src/lib/agents/output-collector.js +1 -2
- package/src/lib/detectors/design-system-detector.js +4 -4
- package/src/lib/detectors/index.js +1 -11
- package/src/lib/generators/architecture-scanner.js +640 -0
- package/src/lib/generators/context-generator.js +3 -3
- package/src/lib/generators/design-system-generator.js +508 -587
- package/src/lib/generators/index.js +12 -10
- package/src/lib/generators/metadata-extractor.js +386 -387
- package/src/lib/generators/recap-generator.js +4 -4
- package/src/lib/generators/scaffold-generator.js +317 -0
- package/src/lib/generators/settings-generator.js +0 -1
- package/src/lib/phase-chain/phase-validator.js +64 -42
- package/src/lib/stack/stack-profile.js +2 -2
- package/src/lib/standards/digest-builder.js +72 -285
- package/src/lib/tasks/task-parser.js +11 -5
- package/src/lib/validators/architecture/architecture-validator.js +259 -387
- package/src/lib/validators/content/content-validator.js +287 -287
- package/src/lib/validators/css/css-validator.js +340 -352
- package/src/lib/validators/packages/package-validator.js +347 -360
- package/src/lib/validators/shared/issue-counter.js +14 -18
- package/src/lib/validators/validation-runner.js +3 -19
- package/src/scripts/setup-infra.js +1 -1
- package/src/utils/agents-installer.js +24 -3
- package/src/utils/file-copier.js +1 -0
- package/src/utils/hooks-installer.js +11 -19
- package/src/utils/skills-installer.js +178 -178
- package/framework/agents/architecture/prompt-engineer.md +0 -195
- package/framework/agents/backend/api-designer.md +0 -106
- package/framework/agents/frontend/blazor-builder.md +0 -229
- package/framework/agents/frontend/nextjs-ui-specialist.md +0 -218
- package/framework/agents/infrastructure/azure-architect.md +0 -150
- package/framework/agents/infrastructure/azure-deploy-specialist.md +0 -707
- package/framework/agents/infrastructure/bicep-architect.md +0 -132
- package/framework/agents/infrastructure/container-specialist.md +0 -137
- package/framework/agents/infrastructure/devops-engineer.md +0 -126
- package/framework/agents/integrations/azure-identity.md +0 -148
- package/framework/agents/quality/code-analyzer.md +0 -239
- package/framework/agents/quality/testing-specialist.md +0 -130
- package/framework/hooks/claude-code/notification/approval-reminder.js +0 -57
- package/framework/hooks/claude-code/post-tool-use/context-refresh.js +0 -109
- package/framework/hooks/claude-code/post-tool-use/skill-reminder.js +0 -78
- package/framework/index/troubleshooting-index.json +0 -184
- package/framework/rules/testing-standards.md +0 -14
- package/framework/skills/level-0-meta/morph-replicate/SKILL.md +0 -230
- package/framework/skills/level-0-meta/morph-replicate/references/blazor-html-mapping.md +0 -70
- package/framework/skills/level-1-workflows/morph-phase-clarify/SKILL.md +0 -256
- package/framework/skills/level-1-workflows/morph-phase-clarify/references/clarifications-example.md +0 -117
- package/framework/skills/level-1-workflows/morph-phase-setup/SKILL.md +0 -269
- package/framework/standards/ai-agents/blazor-ui.md +0 -364
- package/framework/standards/architecture/ddd/aggregates.md +0 -120
- package/framework/standards/architecture/ddd/bounded-contexts.md +0 -105
- package/framework/standards/architecture/ddd/complexity-levels.md +0 -108
- package/framework/standards/architecture/ddd/entities.md +0 -99
- package/framework/standards/architecture/ddd/ubiquitous-language.md +0 -58
- package/framework/standards/architecture/ddd/value-objects.md +0 -124
- package/framework/standards/backend/api/minimal-api.md +0 -494
- package/framework/standards/backend/api/rest.md +0 -492
- package/framework/standards/backend/authentication/passkeys.md +0 -428
- package/framework/standards/backend/database/ef-core.md +0 -199
- package/framework/standards/backend/database/migrations.md +0 -393
- package/framework/standards/backend/database/repository-patterns.md +0 -528
- package/framework/standards/backend/dotnet/async.md +0 -366
- package/framework/standards/backend/dotnet/core.md +0 -117
- package/framework/standards/backend/dotnet/di.md +0 -439
- package/framework/standards/backend/dotnet/program-cs-checklist.md +0 -92
- package/framework/standards/context/analytics.md +0 -96
- package/framework/standards/context/bundles.md +0 -110
- package/framework/standards/context/priming.md +0 -78
- package/framework/standards/core/architecture.md +0 -185
- package/framework/standards/core/coding.md +0 -214
- package/framework/standards/core/git-branching-strategy.md +0 -403
- package/framework/standards/core/git.md +0 -185
- package/framework/standards/core/testing.md +0 -295
- package/framework/standards/data/nosql/blob-storage.md +0 -102
- package/framework/standards/data/vector-search/azure-ai-search.md +0 -121
- package/framework/standards/frontend/blazor/design-checklist.md +0 -222
- package/framework/standards/frontend/blazor/fluent-ui-setup.md +0 -595
- package/framework/standards/frontend/blazor/fluent-ui.md +0 -137
- package/framework/standards/frontend/blazor/html-conversion.md +0 -184
- package/framework/standards/frontend/blazor/lifecycle.md +0 -195
- package/framework/standards/frontend/blazor/pitfalls.md +0 -198
- package/framework/standards/frontend/blazor/state.md +0 -191
- package/framework/standards/frontend/design-system/aesthetic-direction.md +0 -266
- package/framework/standards/frontend/design-system/design-trends.md +0 -171
- package/framework/standards/infrastructure/azure/azure.md +0 -624
- package/framework/standards/infrastructure/azure/bicep/bicep-patterns.md +0 -422
- package/framework/standards/infrastructure/azure/devops/azure-devops-setup.md +0 -516
- package/framework/standards/infrastructure/azure/devops/local-development.md +0 -520
- package/framework/standards/infrastructure/azure/services/functions.md +0 -486
- package/framework/standards/infrastructure/azure/services/service-bus.md +0 -459
- package/framework/standards/infrastructure/azure/services/storage.md +0 -407
- package/framework/standards/infrastructure/vercel/vercel-database.md +0 -106
- package/framework/standards/integration/api/graphql.md +0 -91
- package/framework/standards/integration/api/grpc.md +0 -114
- package/framework/standards/integration/api/rest-design.md +0 -95
- package/framework/standards/integration/event-driven/cqrs.md +0 -101
- package/framework/standards/integration/event-driven/event-sourcing.md +0 -124
- package/framework/standards/integration/event-driven/service-bus.md +0 -95
- package/framework/standards/observability/logging.md +0 -131
- package/framework/standards/observability/metrics.md +0 -121
- package/framework/standards/observability/monitoring.md +0 -114
- package/framework/standards/observability/tracing.md +0 -132
- package/framework/standards/workflows/parallel-execution.md +0 -112
- package/framework/standards/workflows/thread-management.md +0 -113
- package/framework/templates/code/dotnet/backend/service.cs +0 -139
- package/framework/templates/code/dotnet/contracts/Commands.cs +0 -74
- package/framework/templates/code/dotnet/contracts/Entities.cs +0 -25
- package/framework/templates/code/dotnet/contracts/Queries.cs +0 -74
- package/framework/templates/code/dotnet/contracts/api-contracts.cs +0 -173
- package/framework/templates/code/dotnet/contracts/contracts-level1.cs +0 -69
- package/framework/templates/code/dotnet/contracts/contracts-level2.cs +0 -86
- package/framework/templates/code/dotnet/contracts/contracts-level3.cs +0 -41
- package/framework/templates/code/dotnet/frontend/component.razor +0 -239
- package/framework/templates/infrastructure/azure/Dockerfile.example +0 -82
- package/framework/templates/infrastructure/azure/README.md +0 -286
- package/framework/templates/infrastructure/azure/app-insights.bicep +0 -63
- package/framework/templates/infrastructure/azure/app-service.bicep +0 -164
- package/framework/templates/infrastructure/azure/container-app-env.bicep +0 -49
- package/framework/templates/infrastructure/azure/container-app.bicep +0 -156
- package/framework/templates/infrastructure/azure/deploy-checklist.md +0 -426
- package/framework/templates/infrastructure/azure/deploy.ps1 +0 -229
- package/framework/templates/infrastructure/azure/deploy.sh +0 -208
- package/framework/templates/infrastructure/azure/key-vault.bicep +0 -91
- package/framework/templates/infrastructure/azure/main.bicep +0 -189
- package/framework/templates/infrastructure/azure/parameters.dev.json +0 -29
- package/framework/templates/infrastructure/azure/parameters.prod.json +0 -29
- package/framework/templates/infrastructure/azure/parameters.staging.json +0 -29
- package/framework/templates/infrastructure/azure/sql-database.bicep +0 -103
- package/framework/templates/infrastructure/azure/storage.bicep +0 -106
- package/framework/templates/infrastructure/github/actions/azure-auth/action.yml.hbs +0 -22
- package/framework/templates/infrastructure/github/workflows/deploy-azure-app-service.yml.hbs +0 -61
- package/framework/templates/integrations/azure-identity-config.cs +0 -288
- package/framework/templates/project-structure/dotnet-ddd.md +0 -70
- package/framework/templates/saas/subscription.cs +0 -347
- package/framework/templates/saas/tenant.cs +0 -338
- package/framework/templates/ui/FluentDesignTheme.cs +0 -149
- package/framework/templates/ui/MudTheme.cs +0 -281
- package/framework/workflows/configs/nodejs-cli.json +0 -40
- package/src/commands/standards/context-budget.js +0 -89
- package/src/lib/standards/index.js +0 -7
- package/src/lib/standards/standards-context-injector.js +0 -302
- package/src/lib/validators/blazor/blazor-concurrency-analyzer.js +0 -316
- package/src/lib/validators/blazor/blazor-state-validator.js +0 -279
- package/src/lib/validators/blazor/blazor-validator.js +0 -369
- package/src/lib/validators/blazor/index.js +0 -9
|
@@ -1,238 +1,324 @@
|
|
|
1
1
|
// ============================================================
|
|
2
|
-
// TEST TEMPLATE
|
|
2
|
+
// TEST TEMPLATE (VSA Pattern — Handler + Validator)
|
|
3
3
|
// Generated by MORPH Framework
|
|
4
4
|
// ============================================================
|
|
5
5
|
|
|
6
6
|
using FluentAssertions;
|
|
7
|
+
using FluentValidation;
|
|
8
|
+
using FluentValidation.Results;
|
|
7
9
|
using Microsoft.Extensions.Logging;
|
|
8
10
|
using NSubstitute;
|
|
11
|
+
using {{pascalCase PROJECT_NAME}}.Repository;
|
|
9
12
|
using Xunit;
|
|
10
13
|
|
|
11
|
-
namespace {{
|
|
14
|
+
namespace {{pascalCase PROJECT_NAME}}.Tests.Unit.Features.{{pascalCase FEATURE_NAME}};
|
|
15
|
+
|
|
16
|
+
// ============================================================
|
|
17
|
+
// Handler Tests
|
|
18
|
+
// ============================================================
|
|
12
19
|
|
|
13
20
|
/// <summary>
|
|
14
|
-
/// Unit tests for {{pascalCase FEATURE_NAME}}
|
|
21
|
+
/// Unit tests for {{pascalCase FEATURE_NAME}} handlers.
|
|
22
|
+
/// Tests handlers directly with mocked IRepository and IUnitOfWork.
|
|
15
23
|
/// </summary>
|
|
16
|
-
public class {{pascalCase FEATURE_NAME}}
|
|
24
|
+
public class {{pascalCase FEATURE_NAME}}HandlerTests
|
|
17
25
|
{
|
|
18
|
-
private readonly
|
|
19
|
-
private readonly
|
|
20
|
-
private readonly {{pascalCase FEATURE_NAME}}
|
|
26
|
+
private readonly IRepository<{{pascalCase FEATURE_NAME}}> _repository;
|
|
27
|
+
private readonly IUnitOfWork _unitOfWork;
|
|
28
|
+
private readonly ILogger<Create{{pascalCase FEATURE_NAME}}.Handler> _logger;
|
|
21
29
|
|
|
22
|
-
public {{pascalCase FEATURE_NAME}}
|
|
30
|
+
public {{pascalCase FEATURE_NAME}}HandlerTests()
|
|
23
31
|
{
|
|
24
|
-
_repository = Substitute.For<
|
|
25
|
-
|
|
26
|
-
|
|
32
|
+
_repository = Substitute.For<IRepository<{{pascalCase FEATURE_NAME}}>>();
|
|
33
|
+
_unitOfWork = Substitute.For<IUnitOfWork>();
|
|
34
|
+
_logger = Substitute.For<ILogger<Create{{pascalCase FEATURE_NAME}}.Handler>>();
|
|
27
35
|
}
|
|
28
36
|
|
|
29
|
-
#region
|
|
37
|
+
#region Create Handler Tests
|
|
30
38
|
|
|
31
39
|
[Fact]
|
|
32
|
-
public async Task
|
|
40
|
+
public async Task Create_HandleAsync_WithValidCommand_ReturnsSuccessResult()
|
|
33
41
|
{
|
|
34
42
|
// Arrange
|
|
35
|
-
var
|
|
36
|
-
var
|
|
37
|
-
|
|
38
|
-
|
|
43
|
+
var command = new Create{{pascalCase FEATURE_NAME}}.Command("Test {{pascalCase FEATURE_NAME}}");
|
|
44
|
+
var handler = new Create{{pascalCase FEATURE_NAME}}.Handler(_repository, _unitOfWork, _logger);
|
|
45
|
+
|
|
46
|
+
_repository.AddAsync(Arg.Any<{{pascalCase FEATURE_NAME}}>(), Arg.Any<CancellationToken>())
|
|
47
|
+
.Returns(callInfo => callInfo.Arg<{{pascalCase FEATURE_NAME}}>());
|
|
48
|
+
|
|
49
|
+
_unitOfWork.CommitAsync(Arg.Any<CancellationToken>())
|
|
50
|
+
.Returns(1);
|
|
39
51
|
|
|
40
52
|
// Act
|
|
41
|
-
var result = await
|
|
53
|
+
var result = await handler.HandleAsync(command, CancellationToken.None);
|
|
42
54
|
|
|
43
55
|
// Assert
|
|
44
|
-
result.Should().
|
|
45
|
-
result
|
|
46
|
-
result.Name.Should().Be("Test {{pascalCase FEATURE_NAME}}");
|
|
56
|
+
result.IsSuccess.Should().BeTrue();
|
|
57
|
+
result.Value.Should().NotBeNull();
|
|
58
|
+
result.Value.Name.Should().Be("Test {{pascalCase FEATURE_NAME}}");
|
|
59
|
+
|
|
60
|
+
await _repository.Received(1).AddAsync(Arg.Any<{{pascalCase FEATURE_NAME}}>(), Arg.Any<CancellationToken>());
|
|
61
|
+
await _unitOfWork.Received(1).CommitAsync(Arg.Any<CancellationToken>());
|
|
47
62
|
}
|
|
48
63
|
|
|
49
64
|
[Fact]
|
|
50
|
-
public async Task
|
|
65
|
+
public async Task Create_HandleAsync_WhenCommitFails_ReturnsFailureResult()
|
|
51
66
|
{
|
|
52
67
|
// Arrange
|
|
53
|
-
var
|
|
54
|
-
|
|
55
|
-
|
|
68
|
+
var command = new Create{{pascalCase FEATURE_NAME}}.Command("Test {{pascalCase FEATURE_NAME}}");
|
|
69
|
+
var handler = new Create{{pascalCase FEATURE_NAME}}.Handler(_repository, _unitOfWork, _logger);
|
|
70
|
+
|
|
71
|
+
_repository.AddAsync(Arg.Any<{{pascalCase FEATURE_NAME}}>(), Arg.Any<CancellationToken>())
|
|
72
|
+
.Returns(callInfo => callInfo.Arg<{{pascalCase FEATURE_NAME}}>());
|
|
73
|
+
|
|
74
|
+
_unitOfWork.CommitAsync(Arg.Any<CancellationToken>())
|
|
75
|
+
.ThrowsAsync(new InvalidOperationException("Database error"));
|
|
56
76
|
|
|
57
77
|
// Act
|
|
58
|
-
var
|
|
78
|
+
var act = async () => await handler.HandleAsync(command, CancellationToken.None);
|
|
59
79
|
|
|
60
80
|
// Assert
|
|
61
|
-
|
|
81
|
+
await act.Should().ThrowAsync<InvalidOperationException>()
|
|
82
|
+
.WithMessage("Database error");
|
|
62
83
|
}
|
|
63
84
|
|
|
64
85
|
#endregion
|
|
65
86
|
|
|
66
|
-
#region
|
|
87
|
+
#region GetById Handler Tests
|
|
67
88
|
|
|
68
89
|
[Fact]
|
|
69
|
-
public async Task
|
|
90
|
+
public async Task GetById_HandleAsync_WithValidId_ReturnsSuccessResult()
|
|
70
91
|
{
|
|
71
92
|
// Arrange
|
|
72
|
-
var
|
|
73
|
-
{
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
_repository.GetAllAsync(Arg.Any<CancellationToken>())
|
|
79
|
-
.Returns(entities);
|
|
93
|
+
var query = new GetById{{pascalCase FEATURE_NAME}}.Query(1);
|
|
94
|
+
var entity = CreateTestEntity(1, "Test {{pascalCase FEATURE_NAME}}");
|
|
95
|
+
var handler = new GetById{{pascalCase FEATURE_NAME}}.Handler(_repository);
|
|
96
|
+
|
|
97
|
+
_repository.GetByIdAsync(1, Arg.Any<CancellationToken>())
|
|
98
|
+
.Returns(entity);
|
|
80
99
|
|
|
81
100
|
// Act
|
|
82
|
-
var result = await
|
|
101
|
+
var result = await handler.HandleAsync(query, CancellationToken.None);
|
|
83
102
|
|
|
84
103
|
// Assert
|
|
85
|
-
result.Should().
|
|
86
|
-
result
|
|
104
|
+
result.IsSuccess.Should().BeTrue();
|
|
105
|
+
result.Value.Should().NotBeNull();
|
|
106
|
+
result.Value!.Name.Should().Be("Test {{pascalCase FEATURE_NAME}}");
|
|
87
107
|
}
|
|
88
108
|
|
|
89
109
|
[Fact]
|
|
90
|
-
public async Task
|
|
110
|
+
public async Task GetById_HandleAsync_WithInvalidId_ReturnsFailureResult()
|
|
91
111
|
{
|
|
92
112
|
// Arrange
|
|
93
|
-
|
|
94
|
-
|
|
113
|
+
var query = new GetById{{pascalCase FEATURE_NAME}}.Query(999);
|
|
114
|
+
var handler = new GetById{{pascalCase FEATURE_NAME}}.Handler(_repository);
|
|
115
|
+
|
|
116
|
+
_repository.GetByIdAsync(999, Arg.Any<CancellationToken>())
|
|
117
|
+
.Returns(({{pascalCase FEATURE_NAME}}?)null);
|
|
95
118
|
|
|
96
119
|
// Act
|
|
97
|
-
var result = await
|
|
120
|
+
var result = await handler.HandleAsync(query, CancellationToken.None);
|
|
98
121
|
|
|
99
122
|
// Assert
|
|
100
|
-
result.Should().
|
|
123
|
+
result.IsSuccess.Should().BeFalse();
|
|
101
124
|
}
|
|
102
125
|
|
|
103
126
|
#endregion
|
|
104
127
|
|
|
105
|
-
#region
|
|
128
|
+
#region Update Handler Tests
|
|
106
129
|
|
|
107
130
|
[Fact]
|
|
108
|
-
public async Task
|
|
131
|
+
public async Task Update_HandleAsync_WithValidCommand_ReturnsSuccessResult()
|
|
109
132
|
{
|
|
110
133
|
// Arrange
|
|
111
|
-
var
|
|
134
|
+
var command = new Update{{pascalCase FEATURE_NAME}}.Command(1, "Updated Name");
|
|
135
|
+
var entity = CreateTestEntity(1, "Original Name");
|
|
136
|
+
var handler = new Update{{pascalCase FEATURE_NAME}}.Handler(_repository, _unitOfWork);
|
|
137
|
+
|
|
138
|
+
_repository.GetByIdAsync(1, Arg.Any<CancellationToken>())
|
|
139
|
+
.Returns(entity);
|
|
112
140
|
|
|
113
|
-
|
|
114
|
-
.
|
|
115
|
-
.Do(x =>
|
|
116
|
-
{
|
|
117
|
-
// Simulate ID assignment
|
|
118
|
-
var entity = x.Arg<Domain.Entities.{{pascalCase FEATURE_NAME}}>();
|
|
119
|
-
// entity.Id would be set by EF Core
|
|
120
|
-
});
|
|
141
|
+
_unitOfWork.CommitAsync(Arg.Any<CancellationToken>())
|
|
142
|
+
.Returns(1);
|
|
121
143
|
|
|
122
144
|
// Act
|
|
123
|
-
var result = await
|
|
145
|
+
var result = await handler.HandleAsync(command, CancellationToken.None);
|
|
124
146
|
|
|
125
147
|
// Assert
|
|
126
|
-
result.Should().
|
|
127
|
-
result.Name.Should().Be("New {{pascalCase FEATURE_NAME}}");
|
|
148
|
+
result.IsSuccess.Should().BeTrue();
|
|
128
149
|
|
|
129
|
-
await _repository.Received(1).
|
|
130
|
-
await
|
|
150
|
+
await _repository.Received(1).UpdateAsync(Arg.Any<{{pascalCase FEATURE_NAME}}>(), Arg.Any<CancellationToken>());
|
|
151
|
+
await _unitOfWork.Received(1).CommitAsync(Arg.Any<CancellationToken>());
|
|
131
152
|
}
|
|
132
153
|
|
|
133
|
-
[
|
|
134
|
-
|
|
135
|
-
[InlineData(" ")]
|
|
136
|
-
[InlineData(null)]
|
|
137
|
-
public async Task CreateAsync_WithInvalidName_ThrowsValidationException(string? name)
|
|
154
|
+
[Fact]
|
|
155
|
+
public async Task Update_HandleAsync_WithNonExistentId_ReturnsFailureResult()
|
|
138
156
|
{
|
|
139
157
|
// Arrange
|
|
140
|
-
var
|
|
158
|
+
var command = new Update{{pascalCase FEATURE_NAME}}.Command(999, "Updated");
|
|
159
|
+
var handler = new Update{{pascalCase FEATURE_NAME}}.Handler(_repository, _unitOfWork);
|
|
160
|
+
|
|
161
|
+
_repository.GetByIdAsync(999, Arg.Any<CancellationToken>())
|
|
162
|
+
.Returns(({{pascalCase FEATURE_NAME}}?)null);
|
|
141
163
|
|
|
142
164
|
// Act
|
|
143
|
-
var
|
|
165
|
+
var result = await handler.HandleAsync(command, CancellationToken.None);
|
|
144
166
|
|
|
145
167
|
// Assert
|
|
146
|
-
|
|
168
|
+
result.IsSuccess.Should().BeFalse();
|
|
169
|
+
await _unitOfWork.DidNotReceive().CommitAsync(Arg.Any<CancellationToken>());
|
|
147
170
|
}
|
|
148
171
|
|
|
149
172
|
#endregion
|
|
150
173
|
|
|
151
|
-
#region
|
|
174
|
+
#region Delete Handler Tests
|
|
152
175
|
|
|
153
176
|
[Fact]
|
|
154
|
-
public async Task
|
|
177
|
+
public async Task Delete_HandleAsync_WithValidId_ReturnsSuccessResult()
|
|
155
178
|
{
|
|
156
179
|
// Arrange
|
|
157
|
-
var
|
|
158
|
-
var entity = CreateTestEntity(
|
|
159
|
-
var
|
|
180
|
+
var command = new Delete{{pascalCase FEATURE_NAME}}.Command(1);
|
|
181
|
+
var entity = CreateTestEntity(1, "To Delete");
|
|
182
|
+
var handler = new Delete{{pascalCase FEATURE_NAME}}.Handler(_repository, _unitOfWork);
|
|
160
183
|
|
|
161
|
-
_repository.GetByIdAsync(
|
|
184
|
+
_repository.GetByIdAsync(1, Arg.Any<CancellationToken>())
|
|
162
185
|
.Returns(entity);
|
|
163
186
|
|
|
187
|
+
_unitOfWork.CommitAsync(Arg.Any<CancellationToken>())
|
|
188
|
+
.Returns(1);
|
|
189
|
+
|
|
164
190
|
// Act
|
|
165
|
-
await
|
|
191
|
+
var result = await handler.HandleAsync(command, CancellationToken.None);
|
|
166
192
|
|
|
167
193
|
// Assert
|
|
168
|
-
|
|
169
|
-
|
|
194
|
+
result.IsSuccess.Should().BeTrue();
|
|
195
|
+
|
|
196
|
+
await _repository.Received(1).DeleteAsync(entity, Arg.Any<CancellationToken>());
|
|
197
|
+
await _unitOfWork.Received(1).CommitAsync(Arg.Any<CancellationToken>());
|
|
170
198
|
}
|
|
171
199
|
|
|
172
200
|
[Fact]
|
|
173
|
-
public async Task
|
|
201
|
+
public async Task Delete_HandleAsync_WithNonExistentId_ReturnsFailureResult()
|
|
174
202
|
{
|
|
175
203
|
// Arrange
|
|
176
|
-
var
|
|
177
|
-
var
|
|
204
|
+
var command = new Delete{{pascalCase FEATURE_NAME}}.Command(999);
|
|
205
|
+
var handler = new Delete{{pascalCase FEATURE_NAME}}.Handler(_repository, _unitOfWork);
|
|
178
206
|
|
|
179
|
-
_repository.GetByIdAsync(
|
|
180
|
-
.Returns((
|
|
207
|
+
_repository.GetByIdAsync(999, Arg.Any<CancellationToken>())
|
|
208
|
+
.Returns(({{pascalCase FEATURE_NAME}}?)null);
|
|
181
209
|
|
|
182
210
|
// Act
|
|
183
|
-
var
|
|
211
|
+
var result = await handler.HandleAsync(command, CancellationToken.None);
|
|
184
212
|
|
|
185
213
|
// Assert
|
|
186
|
-
|
|
214
|
+
result.IsSuccess.Should().BeFalse();
|
|
215
|
+
await _unitOfWork.DidNotReceive().CommitAsync(Arg.Any<CancellationToken>());
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
#endregion
|
|
219
|
+
|
|
220
|
+
#region Helper Methods
|
|
221
|
+
|
|
222
|
+
private static {{pascalCase FEATURE_NAME}} CreateTestEntity(int id, string name)
|
|
223
|
+
{
|
|
224
|
+
return {{pascalCase FEATURE_NAME}}.Create(name);
|
|
225
|
+
// Note: In real tests, use reflection or a test factory to set the Id
|
|
187
226
|
}
|
|
188
227
|
|
|
189
228
|
#endregion
|
|
229
|
+
}
|
|
190
230
|
|
|
191
|
-
|
|
231
|
+
// ============================================================
|
|
232
|
+
// Validator Tests
|
|
233
|
+
// ============================================================
|
|
234
|
+
|
|
235
|
+
/// <summary>
|
|
236
|
+
/// Unit tests for {{pascalCase FEATURE_NAME}} validators.
|
|
237
|
+
/// Tests FluentValidation rules using ValidateAsync.
|
|
238
|
+
/// </summary>
|
|
239
|
+
public class {{pascalCase FEATURE_NAME}}ValidatorTests
|
|
240
|
+
{
|
|
241
|
+
#region Create Validator Tests
|
|
192
242
|
|
|
193
243
|
[Fact]
|
|
194
|
-
public async Task
|
|
244
|
+
public async Task CreateValidator_WithValidCommand_PassesValidation()
|
|
195
245
|
{
|
|
196
246
|
// Arrange
|
|
197
|
-
var
|
|
198
|
-
var
|
|
247
|
+
var validator = new Create{{pascalCase FEATURE_NAME}}.Validator();
|
|
248
|
+
var command = new Create{{pascalCase FEATURE_NAME}}.Command("Valid Name");
|
|
199
249
|
|
|
200
|
-
|
|
201
|
-
|
|
250
|
+
// Act
|
|
251
|
+
ValidationResult result = await validator.ValidateAsync(command);
|
|
252
|
+
|
|
253
|
+
// Assert
|
|
254
|
+
result.IsValid.Should().BeTrue();
|
|
255
|
+
result.Errors.Should().BeEmpty();
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
[Theory]
|
|
259
|
+
[InlineData("")]
|
|
260
|
+
[InlineData(" ")]
|
|
261
|
+
[InlineData(null)]
|
|
262
|
+
public async Task CreateValidator_WithInvalidName_FailsValidation(string? name)
|
|
263
|
+
{
|
|
264
|
+
// Arrange
|
|
265
|
+
var validator = new Create{{pascalCase FEATURE_NAME}}.Validator();
|
|
266
|
+
var command = new Create{{pascalCase FEATURE_NAME}}.Command(name!);
|
|
202
267
|
|
|
203
268
|
// Act
|
|
204
|
-
await
|
|
269
|
+
ValidationResult result = await validator.ValidateAsync(command);
|
|
205
270
|
|
|
206
271
|
// Assert
|
|
207
|
-
|
|
208
|
-
|
|
272
|
+
result.IsValid.Should().BeFalse();
|
|
273
|
+
result.Errors.Should().Contain(e => e.PropertyName == nameof(Create{{pascalCase FEATURE_NAME}}.Command.Name));
|
|
209
274
|
}
|
|
210
275
|
|
|
211
276
|
[Fact]
|
|
212
|
-
public async Task
|
|
277
|
+
public async Task CreateValidator_WithNameExceedingMaxLength_FailsValidation()
|
|
213
278
|
{
|
|
214
279
|
// Arrange
|
|
215
|
-
var
|
|
216
|
-
|
|
217
|
-
_repository.GetByIdAsync(id, Arg.Any<CancellationToken>())
|
|
218
|
-
.Returns((Domain.Entities.{{pascalCase FEATURE_NAME}}?)null);
|
|
280
|
+
var validator = new Create{{pascalCase FEATURE_NAME}}.Validator();
|
|
281
|
+
var command = new Create{{pascalCase FEATURE_NAME}}.Command(new string('A', 201));
|
|
219
282
|
|
|
220
283
|
// Act
|
|
221
|
-
|
|
284
|
+
ValidationResult result = await validator.ValidateAsync(command);
|
|
222
285
|
|
|
223
286
|
// Assert
|
|
224
|
-
|
|
287
|
+
result.IsValid.Should().BeFalse();
|
|
288
|
+
result.Errors.Should().Contain(e => e.PropertyName == nameof(Create{{pascalCase FEATURE_NAME}}.Command.Name));
|
|
225
289
|
}
|
|
226
290
|
|
|
227
291
|
#endregion
|
|
228
292
|
|
|
229
|
-
#region
|
|
293
|
+
#region Update Validator Tests
|
|
294
|
+
|
|
295
|
+
[Fact]
|
|
296
|
+
public async Task UpdateValidator_WithValidCommand_PassesValidation()
|
|
297
|
+
{
|
|
298
|
+
// Arrange
|
|
299
|
+
var validator = new Update{{pascalCase FEATURE_NAME}}.Validator();
|
|
300
|
+
var command = new Update{{pascalCase FEATURE_NAME}}.Command(1, "Updated Name");
|
|
301
|
+
|
|
302
|
+
// Act
|
|
303
|
+
ValidationResult result = await validator.ValidateAsync(command);
|
|
304
|
+
|
|
305
|
+
// Assert
|
|
306
|
+
result.IsValid.Should().BeTrue();
|
|
307
|
+
}
|
|
230
308
|
|
|
231
|
-
|
|
309
|
+
[Fact]
|
|
310
|
+
public async Task UpdateValidator_WithInvalidId_FailsValidation()
|
|
232
311
|
{
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
312
|
+
// Arrange
|
|
313
|
+
var validator = new Update{{pascalCase FEATURE_NAME}}.Validator();
|
|
314
|
+
var command = new Update{{pascalCase FEATURE_NAME}}.Command(0, "Updated Name");
|
|
315
|
+
|
|
316
|
+
// Act
|
|
317
|
+
ValidationResult result = await validator.ValidateAsync(command);
|
|
318
|
+
|
|
319
|
+
// Assert
|
|
320
|
+
result.IsValid.Should().BeFalse();
|
|
321
|
+
result.Errors.Should().Contain(e => e.PropertyName == nameof(Update{{pascalCase FEATURE_NAME}}.Command.Id));
|
|
236
322
|
}
|
|
237
323
|
|
|
238
324
|
#endregion
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
# {{projectName}} — Architecture Context
|
|
2
|
+
|
|
3
|
+
> Auto-generated by `morph-spec context refresh`
|
|
4
|
+
> Last updated: {{timestamp}}
|
|
5
|
+
|
|
6
|
+
{{#if hasDotnet}}
|
|
7
|
+
## .NET Entities ({{entityCount}})
|
|
8
|
+
|
|
9
|
+
| Entity | Type | Path |
|
|
10
|
+
|--------|------|------|
|
|
11
|
+
{{#each entities}}
|
|
12
|
+
| `{{name}}` | {{type}} | `{{path}}` |
|
|
13
|
+
{{/each}}
|
|
14
|
+
|
|
15
|
+
## API Endpoints ({{endpointCount}})
|
|
16
|
+
|
|
17
|
+
| Endpoint | Method | Route | Path |
|
|
18
|
+
|----------|--------|-------|------|
|
|
19
|
+
{{#each endpoints}}
|
|
20
|
+
| `{{name}}` | {{method}} | `{{route}}` | `{{path}}` |
|
|
21
|
+
{{/each}}
|
|
22
|
+
{{/if}}
|
|
23
|
+
|
|
24
|
+
{{#if hasNextjs}}
|
|
25
|
+
## Next.js Pages ({{pageCount}})
|
|
26
|
+
|
|
27
|
+
| Route | Path |
|
|
28
|
+
|-------|------|
|
|
29
|
+
{{#each pages}}
|
|
30
|
+
| `{{route}}` | `{{path}}` |
|
|
31
|
+
{{/each}}
|
|
32
|
+
|
|
33
|
+
{{#if layouts.length}}
|
|
34
|
+
### Layouts
|
|
35
|
+
|
|
36
|
+
{{#each layouts}}
|
|
37
|
+
- `{{path}}`
|
|
38
|
+
{{/each}}
|
|
39
|
+
{{/if}}
|
|
40
|
+
|
|
41
|
+
{{#if componentCount}}
|
|
42
|
+
## Components ({{componentCount}})
|
|
43
|
+
|
|
44
|
+
| Component | Path |
|
|
45
|
+
|-----------|------|
|
|
46
|
+
{{#each components}}
|
|
47
|
+
| `{{name}}` | `{{path}}` |
|
|
48
|
+
{{/each}}
|
|
49
|
+
{{/if}}
|
|
50
|
+
{{/if}}
|
|
51
|
+
|
|
52
|
+
{{#if hasIntegrations}}
|
|
53
|
+
## Integrations
|
|
54
|
+
|
|
55
|
+
| Service | Type | Configured |
|
|
56
|
+
|---------|------|------------|
|
|
57
|
+
{{#each integrations}}
|
|
58
|
+
| {{name}} | {{type}} | {{#if configured}}Yes{{else}}No{{/if}} |
|
|
59
|
+
{{/each}}
|
|
60
|
+
{{/if}}
|
|
61
|
+
|
|
62
|
+
{{#if hasTables}}
|
|
63
|
+
## Database Tables ({{tableCount}})
|
|
64
|
+
|
|
65
|
+
| Table | Source Migration |
|
|
66
|
+
|-------|----------------|
|
|
67
|
+
{{#each tables}}
|
|
68
|
+
| `{{table}}` | `{{migration}}` |
|
|
69
|
+
{{/each}}
|
|
70
|
+
{{/if}}
|
|
71
|
+
|
|
72
|
+
{{#if hasDotnetPackages}}
|
|
73
|
+
## .NET Packages
|
|
74
|
+
|
|
75
|
+
{{#each dotnetPackages}}
|
|
76
|
+
- `{{name}}` {{version}}
|
|
77
|
+
{{/each}}
|
|
78
|
+
{{/if}}
|
|
79
|
+
|
|
80
|
+
{{#if hasNpmPackages}}
|
|
81
|
+
## NPM Packages
|
|
82
|
+
|
|
83
|
+
{{#each npmPackages}}
|
|
84
|
+
- `{{name}}` {{version}}
|
|
85
|
+
{{/each}}
|
|
86
|
+
{{/if}}
|
|
87
|
+
|
|
88
|
+
---
|
|
89
|
+
*Generated by morph-spec v{{morphVersion}}*
|
|
@@ -230,8 +230,8 @@ No validation runs yet. Validators run automatically via checkpoints.
|
|
|
230
230
|
|
|
231
231
|
### Continue Implementation
|
|
232
232
|
```bash
|
|
233
|
-
# Start
|
|
234
|
-
morph-spec task
|
|
233
|
+
# Start a task (dispatch gate + state tracking)
|
|
234
|
+
morph-spec task start {{FEATURE_NAME}} <task-id>
|
|
235
235
|
|
|
236
236
|
# Complete current task (runs validators)
|
|
237
237
|
morph-spec task done {{FEATURE_NAME}} {{CURRENT_TASK_ID}}
|