@polymorphism-tech/morph-spec 8.37.1 → 8.40.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 +37 -13
- package/README.md +2 -2
- package/bin/morph-spec.js +75 -49
- package/claude-plugin.json +1 -1
- package/docs/CHEATSHEET.md +1 -1
- package/docs/QUICKSTART.md +1 -1
- package/framework/CLAUDE.md +6 -5
- package/framework/MORPH.md +25 -22
- package/framework/agents.json +96 -36
- package/framework/docs/CLI.md +54 -17
- package/framework/evals/rubrics/code-quality.md +5 -0
- package/framework/evals/rubrics/condensed.md +6 -2
- package/framework/evals/rubrics/contracts.md +20 -0
- package/framework/evals/rubrics/test-coverage.md +17 -6
- package/framework/hooks/README.md +9 -3
- package/framework/hooks/claude-code/core/graph-refresh-kick.js +52 -0
- package/framework/hooks/claude-code/core/telemetry-log.js +172 -87
- package/framework/hooks/claude-code/core/validate-completion.js +16 -90
- package/framework/hooks/claude-code/core/worktree-context.js +10 -0
- package/framework/hooks/shared/command-tokens.js +96 -0
- package/framework/hooks/shared/git-exclude.js +44 -0
- package/framework/hooks/shared/git-paths.js +52 -0
- package/framework/hooks/shared/graph-freshness.js +380 -0
- package/framework/hooks/shared/graph-refresh.js +416 -0
- package/framework/hooks/shared/morph-cli.js +17 -5
- package/framework/hooks/shared/redact-secrets.js +55 -0
- package/framework/hooks/shared/telemetry-logger.js +25 -2
- package/framework/hooks/shared/transcript-usage.js +80 -0
- package/framework/mcp/registry.json +33 -2
- package/framework/rubrics/backend.json +2 -2
- package/framework/rubrics/default.json +2 -2
- package/framework/rubrics/frontend.json +2 -2
- package/framework/rules/csharp-standards.md +1 -0
- package/framework/rules/infrastructure-standards.md +0 -1
- package/framework/rules/morph-workflow.md +10 -4
- package/framework/rules/nextjs-standards.md +1 -0
- package/framework/schemas/config.schema.json +41 -1
- package/framework/schemas/feature.schema.json +5 -1
- package/framework/skills/core/create-umbraco-lp/SKILL.md +1 -1
- package/framework/skills/core/migrate-lp-to-umbraco/SKILL.md +1 -1
- package/framework/skills/core/morph-apply/SKILL.md +40 -11
- package/framework/skills/core/morph-brainstorming/SKILL.md +16 -7
- package/framework/skills/core/morph-eval/SKILL.md +1 -1
- package/framework/skills/core/morph-hotfix/SKILL.md +1 -1
- package/framework/skills/core/morph-implement/SKILL.md +44 -10
- package/framework/skills/core/morph-media/SKILL.md +1 -1
- package/framework/skills/core/morph-plan/SKILL.md +73 -15
- package/framework/skills/core/morph-proposal/SKILL.md +1 -1
- package/framework/skills/core/morph-review/SKILL.md +51 -13
- package/framework/skills/core/morph-stack-scan/SKILL.md +1 -1
- package/framework/skills/core/morph-standards/SKILL.md +1 -1
- package/framework/skills/core/morph-uiux/SKILL.md +39 -13
- package/framework/standards/STANDARDS.json +189 -14
- package/framework/standards/ai-agents/context-providers.md +16 -26
- package/framework/standards/ai-agents/durable-workflows-hitl.md +15 -21
- package/framework/standards/ai-agents/modalities-image-gen.md +21 -25
- package/framework/standards/ai-agents/modalities-speech-to-text.md +4 -15
- package/framework/standards/ai-agents/modalities-text-to-speech.md +2 -11
- package/framework/standards/ai-agents/modalities-vision.md +1 -18
- package/framework/standards/ai-agents/prompt-sources.md +0 -4
- package/framework/standards/ai-agents/testing-ai.md +2 -1
- package/framework/standards/architecture/code-comments.md +100 -0
- package/framework/standards/backend/dotnet/testing.md +22 -0
- package/framework/standards/backend/integrations/neon-auth/neon-auth.md +63 -50
- package/framework/standards/frontend/admin-app/data-table.md +562 -0
- package/framework/standards/frontend/admin-app/feedback.md +236 -0
- package/framework/standards/frontend/admin-app/forms.md +232 -0
- package/framework/standards/frontend/admin-app/page-structure.md +203 -0
- package/framework/standards/frontend/design-system/ai-image-generation.md +5 -5
- package/framework/standards/frontend/nextjs/components.md +37 -58
- package/framework/standards/frontend/nextjs/data-fetching.md +1 -0
- package/framework/standards/frontend/nextjs/forms.md +31 -30
- package/framework/standards/frontend/nextjs/state-management.md +2 -1
- package/framework/standards/frontend/nextjs/testing.md +18 -0
- package/framework/standards/integration/mcp/mcp-tools.md +48 -117
- package/framework/templates/code/dotnet/abstractions/result.cs +0 -2
- package/framework/templates/code/dotnet/ai-agents/AgentBootstrap.cs.template +26 -39
- package/framework/templates/code/dotnet/ai-agents/EmbeddingService.cs.template +28 -68
- package/framework/templates/code/dotnet/ai-agents/ModelRegistry.cs.template +99 -117
- package/framework/templates/code/dotnet/ai-agents/PromptRepository.cs.template +2 -44
- package/framework/templates/code/dotnet/ai-agents/ResilientToolMiddleware.cs.template +40 -61
- package/framework/templates/code/dotnet/ai-agents/VectorSearchTool.cs.template +54 -107
- package/framework/templates/code/dotnet/ai-agents/model-registry.json.template +53 -58
- package/framework/templates/code/dotnet/contracts/contracts-vsa.cs +192 -281
- package/framework/templates/code/dotnet/database/migration.cs +49 -83
- package/framework/templates/code/dotnet/database/vector-entity.cs +2 -46
- package/framework/templates/code/dotnet/extensions/result-extensions.cs +0 -2
- package/framework/templates/code/dotnet/feature/validator.cs.hbs +11 -15
- package/framework/templates/code/dotnet/jobs/agent.cs +3 -51
- package/framework/templates/code/dotnet/jobs/job.cs +2 -97
- package/framework/templates/code/dotnet/test.cs +2 -87
- package/framework/templates/code/typescript/contracts.ts +140 -167
- package/framework/templates/context/design-system.md +3 -3
- package/framework/templates/docs/proposal.md +11 -0
- package/framework/templates/docs/spec.md +15 -0
- package/framework/templates/docs/ui-mockups.md +115 -98
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Agents/AgentApi.cs +1 -48
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Agents/AgentFactory.cs +3 -79
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Agents/AgentFactoryOptions.cs +1 -70
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Agents/AgentSpec.cs +1 -47
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Agents/AgentSpecOptions.cs +2 -99
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/AiKitAssemblyMarker.cs +0 -6
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Conversation/ConversationStore.cs +3 -88
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Conversation/IConversationStore.cs +1 -41
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Conversation/InMemoryConversationStore.cs +0 -21
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/CountingChatClient.cs +1 -25
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/EvalAssertion.cs +2 -63
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/EvalCacheMissException.cs +0 -30
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/EvalCacheOptions.cs +1 -52
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/EvalCacheStamp.cs +3 -78
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/EvalFixture.cs +4 -109
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/EvalMode.cs +0 -22
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/EvalQualityEvaluators.cs +1 -36
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/EvalScenario.cs +2 -57
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/EvalSummary.cs +0 -75
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/OfflineChatClient.cs +2 -59
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/ShortPathResponseCache.cs +4 -111
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/IMediaGenerator.cs +0 -34
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/IMediaProvider.cs +1 -57
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaArtifact.cs +0 -19
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaDisclosure.cs +2 -74
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaGenerator.cs +2 -33
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaIdempotencyKey.cs +1 -43
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaJob.cs +1 -96
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaJobId.cs +0 -13
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaJobState.cs +0 -27
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaPricing.cs +0 -33
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaProvenance.cs +0 -31
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaRequest.cs +3 -75
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaResult.cs +0 -42
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaReview.cs +0 -39
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaSeams.cs +1 -55
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaStore.cs +3 -59
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MorphMedia.cs +0 -46
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/Providers/GoogleImageProvider.cs +3 -28
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/Providers/OpenAIImageProvider.cs +7 -61
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/Providers/VeoVideoProvider.cs +2 -37
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Middleware/IUsageCostSink.cs +1 -23
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Middleware/UsageCostMiddleware.cs +2 -82
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Middleware/UsageCostRecord.cs +1 -68
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Observability/MorphOpenTelemetryExtensions.cs +1 -62
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Observability/MorphTelemetryOptions.cs +2 -62
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Providers/IProviderClientFactory.cs +1 -25
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Providers/ModelAlias.cs +0 -124
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Providers/ModelRegistry.cs +1 -73
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Rag/DeltaIngest.cs +1 -66
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Rag/DeltaPlan.cs +0 -41
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/AgentFactoryTests.cs +2 -112
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/AgentSpecTests.cs +2 -43
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/ConversationStoreTests.cs +5 -110
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/DeltaIngestTests.cs +7 -103
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Evals/EvalAssertionTests.cs +1 -42
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Evals/EvalCacheMissTests.cs +1 -30
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Evals/EvalCacheStampTests.cs +1 -19
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Evals/EvalFailuresTests.cs +0 -48
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Evals/EvalFixtureTests.cs +1 -29
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Evals/EvalRefreshTests.cs +2 -38
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Evals/EvalWorkspace.cs +1 -14
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Evals/KitEvals.cs +4 -69
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Evals/OfflineChatClientTests.cs +1 -10
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Fakes/FakeChatClient.cs +1 -57
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Fakes/FakeChatClientTests.cs +0 -26
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Fakes/FakeClock.cs +1 -19
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Fakes/FakeMediaProvider.cs +0 -32
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Fakes/FakeProviderClientFactory.cs +0 -14
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Fakes/FakeUsageCostSink.cs +0 -24
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Fakes/InMemoryMediaStore.cs +0 -32
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/MediaAccessibilityTests.cs +0 -37
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/MediaAliasIsNotAnAgentTests.cs +0 -24
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/MediaDisclosureTests.cs +0 -78
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/MediaGeneratorTests.cs +0 -53
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/MediaIdempotencyKeyTests.cs +0 -44
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/MediaJobStateTests.cs +0 -44
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/MediaReviewTests.cs +0 -19
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/MediaStoreTests.cs +0 -24
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/ModelRegistryTests.cs +6 -48
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/SkeletonSmokeTests.cs +1 -13
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/TelemetryTests.cs +2 -71
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/UsageCostMiddlewareTests.cs +5 -100
- package/framework/templates/feature/decisions.md +124 -122
- package/framework/templates/frontend/nextjs/Dockerfile.nextjs.hbs +39 -43
- package/framework/templates/frontend/nextjs/env.mjs.hbs +29 -32
- package/framework/templates/frontend/nextjs/page.tsx.hbs +21 -22
- package/framework/templates/frontend/nextjs/use-feature.ts.hbs +42 -54
- package/framework/templates/integrations/asaas-client.cs +332 -387
- package/framework/templates/integrations/asaas-webhook.cs +290 -351
- package/framework/templates/integrations/neon-auth-config.cs +9 -105
- package/framework/templates/project/validate.js +97 -120
- package/package.json +2 -2
- package/src/commands/dashboard/dashboard.js +80 -53
- package/src/commands/graph/affected.js +4 -3
- package/src/commands/graph/build.js +109 -18
- package/src/commands/graph/explain.js +8 -5
- package/src/commands/graph/status.js +14 -8
- package/src/commands/mcp/mcp-setup.js +37 -9
- package/src/commands/observability/cost.js +148 -18
- package/src/commands/project/doctor.js +85 -15
- package/src/commands/project/init.js +6 -3
- package/src/commands/project/install-plugin-cmd.js +1 -1
- package/src/commands/project/update.js +30 -0
- package/src/commands/project/worktree.js +73 -90
- package/src/commands/state/advance.js +18 -0
- package/src/commands/state/approve.js +15 -0
- package/src/commands/state/archive.js +62 -9
- package/src/commands/state/finish.js +62 -14
- package/src/commands/state/gate-check.js +8 -1
- package/src/commands/state/gate-decision.js +8 -4
- package/src/commands/state/report-check.js +90 -9
- package/src/commands/state/usecase.js +66 -0
- package/src/commands/tasks/dag.js +17 -2
- package/src/commands/validation/validate-feature.js +6 -1
- package/src/commands/validation/verify.js +20 -1
- package/src/dashboard/app/App.jsx +80 -16
- package/src/dashboard/app/components/ActivityFeed.jsx +172 -0
- package/src/dashboard/app/components/CommandPalette.jsx +155 -0
- package/src/dashboard/app/components/DetailPanel.jsx +47 -11
- package/src/dashboard/app/components/EntityLink.jsx +55 -0
- package/src/dashboard/app/components/ErrorBoundary.jsx +54 -0
- package/src/dashboard/app/components/FlowGraph.jsx +19 -17
- package/src/dashboard/app/components/KnowledgeMarkdown.jsx +53 -0
- package/src/dashboard/app/components/Markdown.jsx +47 -0
- package/src/dashboard/app/components/PhaseRail.jsx +111 -0
- package/src/dashboard/app/components/SearchFilter.jsx +40 -22
- package/src/dashboard/app/components/Sidebar.jsx +146 -37
- package/src/dashboard/app/hooks/useMorphQuery.js +54 -0
- package/src/dashboard/app/hooks/usePref.js +38 -0
- package/src/dashboard/app/lib/format.js +69 -0
- package/src/dashboard/app/lib/phases.js +56 -0
- package/src/dashboard/app/lib/project.js +50 -0
- package/src/dashboard/app/pages/Activity.jsx +24 -0
- package/src/dashboard/app/pages/AgentMap.jsx +371 -370
- package/src/dashboard/app/pages/FeatureDetail.jsx +604 -0
- package/src/dashboard/app/pages/Features.jsx +400 -0
- package/src/dashboard/app/pages/Guide.jsx +146 -0
- package/src/dashboard/app/pages/HookInspector.jsx +357 -322
- package/src/dashboard/app/pages/PhasePipeline.jsx +277 -408
- package/src/dashboard/app/pages/Portfolio.jsx +156 -0
- package/src/dashboard/app/pages/Rubrics.jsx +101 -0
- package/src/dashboard/app/pages/RulesMap.jsx +300 -273
- package/src/dashboard/app/pages/SkillsLibrary.jsx +483 -366
- package/src/dashboard/app/pages/Standards.jsx +407 -332
- package/src/dashboard/app/styles/index.css +187 -47
- package/src/dashboard/index.html +14 -12
- package/src/dashboard/package.json +32 -24
- package/src/dashboard/scanner/activity-scanner.js +351 -0
- package/src/dashboard/scanner/agents-scanner.js +63 -1
- package/src/dashboard/scanner/docs-scanner.js +59 -0
- package/src/dashboard/scanner/feature-status.js +10 -0
- package/src/dashboard/scanner/features-scanner.js +360 -0
- package/src/dashboard/scanner/hooks-scanner.js +8 -1
- package/src/dashboard/scanner/index.js +14 -7
- package/src/dashboard/scanner/phases-scanner.js +14 -15
- package/src/dashboard/scanner/portfolio.js +153 -0
- package/src/dashboard/scanner/project-api.js +195 -0
- package/src/dashboard/scanner/rules-scanner.js +42 -37
- package/src/dashboard/scanner/skills-scanner.js +105 -43
- package/src/dashboard/scanner/standards-scanner.js +4 -1
- package/src/dashboard/vite-plugin-morph-data.js +134 -17
- package/src/lib/ai/comment-stripper.js +101 -4
- package/src/lib/detectors/claude-config-detector.js +2 -7
- package/src/lib/detectors/index.js +5 -1
- package/src/lib/evaluator-report.js +47 -0
- package/src/lib/generators/handoff-generator.js +4 -18
- package/src/lib/generators/settings-generator.js +208 -209
- package/src/lib/git/default-branch.js +52 -0
- package/src/lib/git/tree-fingerprint.js +58 -0
- package/src/lib/git/tree-state.js +167 -0
- package/src/lib/graph/god-nodes.js +12 -3
- package/src/lib/graph/graph-health.js +48 -2
- package/src/lib/graph/graph-loader.js +99 -25
- package/src/lib/graph/graphifyignore.js +145 -28
- package/src/lib/graph/sanitize.js +117 -11
- package/src/lib/installers/mcp-installer.js +724 -719
- package/src/lib/installers/memory-installer.js +1 -2
- package/src/lib/projects/registry.js +154 -0
- package/src/lib/risk-detector.js +90 -0
- package/src/lib/specs/acervo-format.js +70 -0
- package/src/lib/specs/acervo-lifecycle.js +264 -0
- package/src/lib/specs/adr-index.js +169 -0
- package/src/lib/specs/revocations.js +156 -0
- package/src/lib/specs/screen-checklist.js +357 -0
- package/src/lib/specs/usecase-check.js +20 -16
- package/src/lib/specs/usecase-promoter.js +103 -38
- package/src/lib/specs/usecase-registry.js +7 -7
- package/src/lib/tasks/tasks-json-parser.js +2 -3
- package/src/lib/telemetry/metrics.js +386 -0
- package/src/lib/telemetry/telemetry-reader.js +161 -51
- package/src/lib/telemetry/verify-event.js +79 -0
- package/src/lib/validators/code-budget/code-budget-validator.js +200 -0
- package/src/lib/validators/code-budget/comment-scanner.js +75 -0
- package/src/lib/validators/code-budget/index.js +9 -0
- package/src/lib/validators/reuse/reuse-validator.js +4 -2
- package/src/lib/validators/reuse/symbol-scanner.js +27 -22
- package/src/lib/validators/validation-runner.js +21 -5
- package/src/lib/validators/verify-runner.js +11 -0
- package/src/lib/worktree/finish-backup.js +739 -0
- package/src/lib/worktree/ignored-triage.js +15 -8
- package/src/lib/worktree/worktree-audit.js +5 -0
- package/src/scripts/install-plugin.js +1 -1
- package/src/scripts/setup-infra.js +31 -22
- package/src/utils/agents-installer.js +338 -24
- package/src/utils/banner.js +1 -1
- package/src/utils/hooks-installer.js +75 -15
- package/src/commands/index.js +0 -13
- package/src/commands/project/checkpoint.js +0 -209
- package/src/core/state/phase-state-machine.js +0 -197
- package/src/dashboard/app/hooks/useDetailPanel.js +0 -15
- package/src/dashboard/app/pages/LiveState.jsx +0 -206
- package/src/dashboard/scanner/squads-scanner.js +0 -13
- package/src/dashboard/scanner/state-scanner.js +0 -85
- package/src/lib/generators/index.js +0 -10
- package/src/lib/hooks/hook-executor.js +0 -167
- package/src/lib/validators/checkpoint-persistence.js +0 -39
- package/src/lib/validators/shared/emit-validator-dispatch.js +0 -14
- package/src/lib/validators/shared/index.js +0 -12
- package/src/lib/validators/shared/issue-counter.js +0 -14
- package/src/lib/validators/shared/result-formatter.js +0 -124
package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/UsageCostMiddlewareTests.cs
CHANGED
|
@@ -7,16 +7,10 @@ using Xunit;
|
|
|
7
7
|
|
|
8
8
|
namespace Morph.AiKit.Tests;
|
|
9
9
|
|
|
10
|
-
/// <summary>
|
|
11
|
-
/// Canarios do <see cref="UsageCostMiddleware"/> (T11). A afirmacao central da task e uma
|
|
12
|
-
/// so: <b><c>cost_usd</c> nao sai nulo, e o zero nunca e ambiguo</b> — ou e medicao, ou e
|
|
13
|
-
/// dispensa escrita, ou e lacuna declarada.
|
|
14
|
-
/// </summary>
|
|
15
10
|
public sealed class UsageCostMiddlewareTests
|
|
16
11
|
{
|
|
17
12
|
private static readonly DateTimeOffset FixedNow = new(2026, 9, 8, 12, 0, 0, TimeSpan.Zero);
|
|
18
13
|
|
|
19
|
-
/// <summary>Tarifa ilustrativa com desconto de cache — a que exercita a aritmetica inteira.</summary>
|
|
20
14
|
private static ModelPricing Tariff()
|
|
21
15
|
=> new(InputPerMTok: 1.25m, OutputPerMTok: 10.0m, CachedInputPerMTok: 0.125m);
|
|
22
16
|
|
|
@@ -42,22 +36,11 @@ public sealed class UsageCostMiddlewareTests
|
|
|
42
36
|
|
|
43
37
|
var sink = new FakeUsageCostSink();
|
|
44
38
|
|
|
45
|
-
// A extensao publica e o caminho de registro: se ela quebrar, o teste quebra junto.
|
|
46
39
|
var client = inner.AsBuilder().UseUsageCost(alias, sink, new FakeClock(FixedNow)).Build();
|
|
47
40
|
|
|
48
41
|
return (client, inner, sink);
|
|
49
42
|
}
|
|
50
43
|
|
|
51
|
-
// ---------------------------------------------------------------------------------
|
|
52
|
-
// doneCriteria 1: Usage conhecido + ModelPricing conhecido => CostUsd EXATO
|
|
53
|
-
// ---------------------------------------------------------------------------------
|
|
54
|
-
|
|
55
|
-
/// <summary>
|
|
56
|
-
/// O valor esperado esta escrito a mao, com a semantica correta de token cacheado:
|
|
57
|
-
/// <b>cacheado e SUBCONJUNTO da entrada</b>. 1000 de entrada com 400 servidos do cache
|
|
58
|
-
/// custam 600 na tarifa cheia mais 400 na de cache — nunca 1000 mais 400. E por isso que
|
|
59
|
-
/// este caso, e nao um sem cache, e o que denuncia uma segunda aritmetica.
|
|
60
|
-
/// </summary>
|
|
61
44
|
[Fact]
|
|
62
45
|
public async Task AKnownUsageAgainstAKnownTariffProducesTheExactCostInUsd()
|
|
63
46
|
{
|
|
@@ -83,13 +66,6 @@ public sealed class UsageCostMiddlewareTests
|
|
|
83
66
|
Assert.Equal(FixedNow, record.At);
|
|
84
67
|
}
|
|
85
68
|
|
|
86
|
-
/// <summary>
|
|
87
|
-
/// O canario que torna "nao existe uma segunda conta do custo" verificavel em vez de
|
|
88
|
-
/// prometido: o custo publicado tem de ser <b>identico</b> ao de
|
|
89
|
-
/// <see cref="ModelPricing.ComputeCostUsd"/> — inclusive nos casos que separam as duas
|
|
90
|
-
/// formulas plausiveis (cacheado sem tarifa de cache, cacheado igual a entrada, saida
|
|
91
|
-
/// zerada).
|
|
92
|
-
/// </summary>
|
|
93
69
|
[Theory]
|
|
94
70
|
[InlineData(1000, 200, 400L)]
|
|
95
71
|
[InlineData(1000, 200, null)]
|
|
@@ -111,11 +87,6 @@ public sealed class UsageCostMiddlewareTests
|
|
|
111
87
|
Assert.Equal(pricing.ComputeCostUsd(input, output, cached), sink.Records[0].CostUsd);
|
|
112
88
|
}
|
|
113
89
|
|
|
114
|
-
/// <summary>
|
|
115
|
-
/// Tarifa sem desconto de cache declarado: nulo em <c>CachedInputPerMTok</c> significa
|
|
116
|
-
/// "sem desconto", <b>nunca</b> "de graca" — a entrada inteira e cobrada cheia mesmo com
|
|
117
|
-
/// tokens vindos do cache.
|
|
118
|
-
/// </summary>
|
|
119
90
|
[Fact]
|
|
120
91
|
public async Task WithoutADeclaredCacheTariffTheWholeInputIsChargedAsInput()
|
|
121
92
|
{
|
|
@@ -127,19 +98,10 @@ public sealed class UsageCostMiddlewareTests
|
|
|
127
98
|
[new ChatMessage(ChatRole.User, "oi")],
|
|
128
99
|
cancellationToken: TestContext.Current.CancellationToken);
|
|
129
100
|
|
|
130
|
-
// (1000 * 1.25) + (200 * 10.0) = 3250 por 1M tokens
|
|
101
|
+
// (1000 * 1.25) + (200 * 10.0) = 3250 por 1M tokens: o cacheado nao rende desconto.
|
|
131
102
|
Assert.Equal(0.00325m, sink.Records[0].CostUsd);
|
|
132
103
|
}
|
|
133
104
|
|
|
134
|
-
// ---------------------------------------------------------------------------------
|
|
135
|
-
// doneCriteria 2: alias sem Pricing => CostUsd == 0, ausencia sinalizada, SEM excecao
|
|
136
|
-
// ---------------------------------------------------------------------------------
|
|
137
|
-
|
|
138
|
-
/// <summary>
|
|
139
|
-
/// Alias sem tarifa e caminho previsto, nao erro. E o zero nao e anonimo: o motivo
|
|
140
|
-
/// escrito no <c>pricingWaiver</c> do registry viaja no record. Nao ha excecao — e nao
|
|
141
|
-
/// ha nulo em <c>CostUsd</c>.
|
|
142
|
-
/// </summary>
|
|
143
105
|
[Fact]
|
|
144
106
|
public async Task AnAliasWithoutATariffRecordsTokensWithZeroCostAndSaysWhy()
|
|
145
107
|
{
|
|
@@ -156,19 +118,14 @@ public sealed class UsageCostMiddlewareTests
|
|
|
156
118
|
|
|
157
119
|
var record = Assert.Single(sink.Records);
|
|
158
120
|
|
|
159
|
-
Assert.Equal(0m, record.CostUsd);
|
|
121
|
+
Assert.Equal(0m, record.CostUsd);
|
|
160
122
|
Assert.Equal(UsageCostStatus.Waived, record.Status);
|
|
161
|
-
Assert.False(record.IsMeasuredCost);
|
|
123
|
+
Assert.False(record.IsMeasuredCost);
|
|
162
124
|
Assert.Equal(alias.PricingWaiver, record.PricingWaiver);
|
|
163
|
-
Assert.Equal(900, record.InputTokens);
|
|
125
|
+
Assert.Equal(900, record.InputTokens);
|
|
164
126
|
Assert.Equal(100, record.OutputTokens);
|
|
165
127
|
}
|
|
166
128
|
|
|
167
|
-
/// <summary>
|
|
168
|
-
/// A regra que sustenta a anterior mora no registry (T8): um alias sem tarifa <b>e</b>
|
|
169
|
-
/// sem dispensa nao carrega. Por isso um record <c>Waived</c> sempre tem motivo — a
|
|
170
|
-
/// ausencia por esquecimento morre antes de chegar ao middleware.
|
|
171
|
-
/// </summary>
|
|
172
129
|
[Fact]
|
|
173
130
|
public void AWaivedRecordAlwaysHasAReasonBecauseTheRegistryRefusesAnAliasWithNeither()
|
|
174
131
|
{
|
|
@@ -180,13 +137,9 @@ public sealed class UsageCostMiddlewareTests
|
|
|
180
137
|
var refusal = Assert.Throws<ModelRegistryException>(() => ModelAlias.ValidatePricingDecision(orphan));
|
|
181
138
|
Assert.Contains("sem-decisao", refusal.Message, StringComparison.Ordinal);
|
|
182
139
|
|
|
183
|
-
ModelAlias.ValidatePricingDecision(WaivedAlias());
|
|
140
|
+
ModelAlias.ValidatePricingDecision(WaivedAlias());
|
|
184
141
|
}
|
|
185
142
|
|
|
186
|
-
/// <summary>
|
|
187
|
-
/// A soma so junta o que foi medido. Uma volta tarifada mais uma dispensada somam o
|
|
188
|
-
/// custo da primeira — o zero da dispensa nao "abaixa a media" nem some do relatorio.
|
|
189
|
-
/// </summary>
|
|
190
143
|
[Fact]
|
|
191
144
|
public async Task OnlyMeasuredCostsEnterTheSum()
|
|
192
145
|
{
|
|
@@ -210,15 +163,6 @@ public sealed class UsageCostMiddlewareTests
|
|
|
210
163
|
Assert.Equal(0.0028m, sink.MeasuredTotalUsd);
|
|
211
164
|
}
|
|
212
165
|
|
|
213
|
-
// ---------------------------------------------------------------------------------
|
|
214
|
-
// O terceiro zero: o provider nao reportou uso
|
|
215
|
-
// ---------------------------------------------------------------------------------
|
|
216
|
-
|
|
217
|
-
/// <summary>
|
|
218
|
-
/// Resposta sem <c>Usage</c>: a linha sai marcada como lacuna. Nao publicar nada faria o
|
|
219
|
-
/// custo desaparecer do relatorio, que e a mesma dor por outro angulo; publicar como
|
|
220
|
-
/// <c>Computed</c> mentiria que a volta custou zero.
|
|
221
|
-
/// </summary>
|
|
222
166
|
[Fact]
|
|
223
167
|
public async Task AResponseWithoutUsageIsRecordedAsAGapNotAsFree()
|
|
224
168
|
{
|
|
@@ -236,11 +180,6 @@ public sealed class UsageCostMiddlewareTests
|
|
|
236
180
|
Assert.Equal(0m, sink.MeasuredTotalUsd);
|
|
237
181
|
}
|
|
238
182
|
|
|
239
|
-
/// <summary>
|
|
240
|
-
/// <c>cached > input</c> e o provider se contradizendo. A conta do T8 recusa esse par
|
|
241
|
-
/// com excecao — e recusar aqui derrubaria a resposta do usuario por causa de um
|
|
242
|
-
/// contador. O middleware degrada para lacuna declarada, sem lancar.
|
|
243
|
-
/// </summary>
|
|
244
183
|
[Fact]
|
|
245
184
|
public async Task InconsistentTokenCountsBecomeADeclaredGapInsteadOfAThrownRequest()
|
|
246
185
|
{
|
|
@@ -259,26 +198,6 @@ public sealed class UsageCostMiddlewareTests
|
|
|
259
198
|
Assert.Equal(UsageCostStatus.UsageMissing, Assert.Single(sink.Records).Status);
|
|
260
199
|
}
|
|
261
200
|
|
|
262
|
-
// ---------------------------------------------------------------------------------
|
|
263
|
-
// Uma linha por volta ao modelo — inclusive no streaming
|
|
264
|
-
// ---------------------------------------------------------------------------------
|
|
265
|
-
|
|
266
|
-
/// <summary>
|
|
267
|
-
/// Duas <b>chamadas</b> a <c>GetResponseAsync</c> publicam <b>duas</b> linhas: uma por
|
|
268
|
-
/// ida ao modelo, cada uma com o seu proprio numero.
|
|
269
|
-
/// </summary>
|
|
270
|
-
/// <remarks>
|
|
271
|
-
/// O que este teste prova, e so isto: <b>uma linha por chamada</b>, sem acumulo de
|
|
272
|
-
/// estado entre chamadas. Ele NAO prova nada sobre a posicao do middleware no pipeline
|
|
273
|
-
/// — sao duas chamadas separadas, nao uma chamada com volta de tool, e aqui o
|
|
274
|
-
/// middleware e montado sozinho, sem <c>FunctionInvokingChatClient</c> nenhum. A
|
|
275
|
-
/// posicao real e o oposto do que uma leitura apressada deste teste sugeriria: o
|
|
276
|
-
/// middleware fica <b>ACIMA</b> da invocacao de funcoes, e quem prova isso e
|
|
277
|
-
/// <c>AgentFactoryTests.ThePipelineOrderIsUsageCostAboveFunctionInvocationAndTelemetryBelowIt</c>
|
|
278
|
-
/// (com <c>...SeesTheUsageOfEveryToolRoundSummedOnce</c> mostrando o uso ja somado das
|
|
279
|
-
/// duas voltas). Acima do FICC, uma conversa com tool call e <b>uma</b> chamada com o
|
|
280
|
-
/// uso ja totalizado — nao duas.
|
|
281
|
-
/// </remarks>
|
|
282
201
|
[Fact]
|
|
283
202
|
public async Task EveryTripToTheModelPublishesItsOwnLine()
|
|
284
203
|
{
|
|
@@ -301,10 +220,6 @@ public sealed class UsageCostMiddlewareTests
|
|
|
301
220
|
Assert.Equal(0.0035m, sink.Records[1].CostUsd); // (2000*1.25 + 100*10) / 1M
|
|
302
221
|
}
|
|
303
222
|
|
|
304
|
-
/// <summary>
|
|
305
|
-
/// No streaming o uso chega como <c>UsageContent</c> avulso. A linha sai <b>uma</b> vez,
|
|
306
|
-
/// ao fim da enumeracao, com o mesmo numero do caminho nao-streaming.
|
|
307
|
-
/// </summary>
|
|
308
223
|
[Fact]
|
|
309
224
|
public async Task StreamingPublishesOneLineAtTheEndWithTheSameNumber()
|
|
310
225
|
{
|
|
@@ -325,11 +240,6 @@ public sealed class UsageCostMiddlewareTests
|
|
|
325
240
|
Assert.Equal(0.0028m, Assert.Single(sink.Records).CostUsd);
|
|
326
241
|
}
|
|
327
242
|
|
|
328
|
-
// ---------------------------------------------------------------------------------
|
|
329
|
-
// Higiene de construcao
|
|
330
|
-
// ---------------------------------------------------------------------------------
|
|
331
|
-
|
|
332
|
-
/// <summary>Alias ou sink nulos sao defeito de composicao e falham na construcao.</summary>
|
|
333
243
|
[Fact]
|
|
334
244
|
public void TheMiddlewareRefusesToBeBuiltWithoutAnAliasOrASink()
|
|
335
245
|
{
|
|
@@ -342,11 +252,6 @@ public sealed class UsageCostMiddlewareTests
|
|
|
342
252
|
() => new UsageCostMiddleware(inner, PricedAlias(), null!));
|
|
343
253
|
}
|
|
344
254
|
|
|
345
|
-
/// <summary>
|
|
346
|
-
/// O instante do record vem do <see cref="TimeProvider"/> injetado, nao do relogio do
|
|
347
|
-
/// processo — sem isso o campo <c>At</c> seria intestavel (standard
|
|
348
|
-
/// <c>backend-dotnet-testing</c>).
|
|
349
|
-
/// </summary>
|
|
350
255
|
[Fact]
|
|
351
256
|
public async Task TheTimestampComesFromTheInjectedClock()
|
|
352
257
|
{
|
|
@@ -1,122 +1,124 @@
|
|
|
1
|
-
# Architecture Decision Records - {{titleCase FEATURE_NAME}}
|
|
2
|
-
|
|
3
|
-
## ADR-001: {Decision Title}
|
|
4
|
-
|
|
5
|
-
**Date:** {{DATE}}
|
|
6
|
-
**Status:** Proposed | Accepted | Deprecated | Superseded
|
|
7
|
-
**Deciders:** MORPH Agents, Developer
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
- {Positive consequence
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
-
|
|
34
|
-
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
-
|
|
39
|
-
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
**
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
-
|
|
62
|
-
-
|
|
63
|
-
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
-
|
|
68
|
-
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
**
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
-
|
|
90
|
-
-
|
|
91
|
-
-
|
|
92
|
-
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
**
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
1
|
+
# Architecture Decision Records - {{titleCase FEATURE_NAME}}
|
|
2
|
+
|
|
3
|
+
## ADR-001: {Decision Title}
|
|
4
|
+
|
|
5
|
+
**Date:** {{DATE}}
|
|
6
|
+
**Status:** Proposed | Accepted | Deprecated | Superseded
|
|
7
|
+
**Deciders:** MORPH Agents, Developer
|
|
8
|
+
**Alcance:** feature
|
|
9
|
+
|
|
10
|
+
### Context
|
|
11
|
+
|
|
12
|
+
{Describe the context and problem that led to this decision}
|
|
13
|
+
|
|
14
|
+
### Decision
|
|
15
|
+
|
|
16
|
+
{Describe the decision made}
|
|
17
|
+
|
|
18
|
+
### Consequences
|
|
19
|
+
|
|
20
|
+
**Pros:**
|
|
21
|
+
- {Positive consequence 1}
|
|
22
|
+
- {Positive consequence 2}
|
|
23
|
+
|
|
24
|
+
**Cons:**
|
|
25
|
+
- {Negative consequence 1}
|
|
26
|
+
|
|
27
|
+
**Risks:**
|
|
28
|
+
- {Identified risk}
|
|
29
|
+
|
|
30
|
+
### Alternatives Considered
|
|
31
|
+
|
|
32
|
+
#### Option A: {Name}
|
|
33
|
+
- Pros: {advantages}
|
|
34
|
+
- Cons: {disadvantages}
|
|
35
|
+
- **Rejected because:** {reason}
|
|
36
|
+
|
|
37
|
+
#### Option B: {Name}
|
|
38
|
+
- Pros: {advantages}
|
|
39
|
+
- Cons: {disadvantages}
|
|
40
|
+
- **Rejected because:** {reason}
|
|
41
|
+
|
|
42
|
+
---
|
|
43
|
+
|
|
44
|
+
{{#if (eq STACK "nextjs-neon")}}
|
|
45
|
+
## ADR-002: Database Access Strategy
|
|
46
|
+
|
|
47
|
+
**Date:** {{DATE}}
|
|
48
|
+
**Status:** Accepted
|
|
49
|
+
|
|
50
|
+
### Context
|
|
51
|
+
|
|
52
|
+
Need to decide how the .NET API accesses Neon PostgreSQL.
|
|
53
|
+
|
|
54
|
+
### Decision
|
|
55
|
+
|
|
56
|
+
Use Dapper with Npgsql for direct SQL queries. No EF Core.
|
|
57
|
+
|
|
58
|
+
### Consequences
|
|
59
|
+
|
|
60
|
+
**Pros:**
|
|
61
|
+
- Full control over SQL queries
|
|
62
|
+
- Better performance (no ORM overhead)
|
|
63
|
+
- Direct access to PostgreSQL features (jsonb, pgvector, functions)
|
|
64
|
+
- RLS policies apply naturally through connection
|
|
65
|
+
|
|
66
|
+
**Cons:**
|
|
67
|
+
- No automatic migrations (use Neon CLI / dotnet ef instead)
|
|
68
|
+
- Manual SQL query writing
|
|
69
|
+
- No change tracking
|
|
70
|
+
|
|
71
|
+
---
|
|
72
|
+
|
|
73
|
+
## ADR-003: Auth Strategy
|
|
74
|
+
|
|
75
|
+
**Date:** {{DATE}}
|
|
76
|
+
**Status:** Accepted
|
|
77
|
+
|
|
78
|
+
### Context
|
|
79
|
+
|
|
80
|
+
Need authentication for both Next.js frontend and .NET API.
|
|
81
|
+
|
|
82
|
+
### Decision
|
|
83
|
+
|
|
84
|
+
Use Neon Auth (Better Auth) for authentication. Auth data lives in the Neon database. JWT validated by .NET API middleware.
|
|
85
|
+
|
|
86
|
+
### Consequences
|
|
87
|
+
|
|
88
|
+
**Pros:**
|
|
89
|
+
- Auth data in same database — no external provider, no sync delays
|
|
90
|
+
- RLS policies work with Neon Auth JWT via auth.user_id() / pg_session_jwt
|
|
91
|
+
- Built-in social login, email/password via @neondatabase/auth SDK
|
|
92
|
+
- Branch isolation — each Neon branch gets isolated auth data
|
|
93
|
+
- No webhooks needed — query neon_auth schema directly
|
|
94
|
+
|
|
95
|
+
**Cons:**
|
|
96
|
+
- Neon Auth is currently in Beta
|
|
97
|
+
- JWT must be forwarded to .NET API for backend auth
|
|
98
|
+
{{/if}}
|
|
99
|
+
|
|
100
|
+
---
|
|
101
|
+
|
|
102
|
+
## Template for New ADRs
|
|
103
|
+
|
|
104
|
+
```markdown
|
|
105
|
+
## ADR-XXX: {Title}
|
|
106
|
+
|
|
107
|
+
**Date:**
|
|
108
|
+
**Status:** Proposed | Accepted | Deprecated | Superseded
|
|
109
|
+
**Alcance:** feature | projeto — `projeto` when the rule holds beyond this feature: `morph-spec finish` copies it to `.morph/specs/adrs/`, the folder brainstorming and plan read instead of archived decisions.md
|
|
110
|
+
|
|
111
|
+
### Context
|
|
112
|
+
{Why is this decision needed?}
|
|
113
|
+
|
|
114
|
+
### Decision
|
|
115
|
+
{What was decided?}
|
|
116
|
+
|
|
117
|
+
### Consequences
|
|
118
|
+
**Pros:** {list}
|
|
119
|
+
**Cons:** {list}
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
---
|
|
123
|
+
|
|
124
|
+
*Generated by MORPH Framework*
|
|
@@ -1,43 +1,39 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
ENV PORT=3000
|
|
41
|
-
ENV HOSTNAME="0.0.0.0"
|
|
42
|
-
|
|
43
|
-
CMD ["node", "server.js"]
|
|
1
|
+
FROM node:20-alpine AS deps
|
|
2
|
+
RUN apk add --no-cache libc6-compat
|
|
3
|
+
WORKDIR /app
|
|
4
|
+
|
|
5
|
+
COPY package.json package-lock.json* ./
|
|
6
|
+
RUN npm ci --only=production && npm cache clean --force
|
|
7
|
+
|
|
8
|
+
FROM node:20-alpine AS builder
|
|
9
|
+
WORKDIR /app
|
|
10
|
+
|
|
11
|
+
COPY --from=deps /app/node_modules ./node_modules
|
|
12
|
+
COPY . .
|
|
13
|
+
|
|
14
|
+
# NEXT_PUBLIC_ vars are inlined by next build, so they must exist at build time
|
|
15
|
+
ARG NEXT_PUBLIC_APP_URL
|
|
16
|
+
ENV NEXT_PUBLIC_APP_URL=$NEXT_PUBLIC_APP_URL
|
|
17
|
+
|
|
18
|
+
RUN npm run build
|
|
19
|
+
|
|
20
|
+
FROM node:20-alpine AS runner
|
|
21
|
+
WORKDIR /app
|
|
22
|
+
|
|
23
|
+
ENV NODE_ENV=production
|
|
24
|
+
ENV NEXT_TELEMETRY_DISABLED=1
|
|
25
|
+
|
|
26
|
+
RUN addgroup --system --gid 1001 nodejs
|
|
27
|
+
RUN adduser --system --uid 1001 nextjs
|
|
28
|
+
|
|
29
|
+
COPY --from=builder /app/public ./public
|
|
30
|
+
COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
|
|
31
|
+
COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static
|
|
32
|
+
|
|
33
|
+
USER nextjs
|
|
34
|
+
|
|
35
|
+
EXPOSE 3000
|
|
36
|
+
ENV PORT=3000
|
|
37
|
+
ENV HOSTNAME="0.0.0.0"
|
|
38
|
+
|
|
39
|
+
CMD ["node", "server.js"]
|
|
@@ -1,32 +1,29 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
export const env = { ...serverEnv.data, ...clientEnv.data };
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
|
|
3
|
+
const serverSchema = z.object({
|
|
4
|
+
API_URL: z.string().url('API_URL must be a valid URL'),
|
|
5
|
+
NODE_ENV: z.enum(['development', 'test', 'production']).default('development'),
|
|
6
|
+
});
|
|
7
|
+
|
|
8
|
+
const clientSchema = z.object({
|
|
9
|
+
NEXT_PUBLIC_APP_URL: z.string().url().optional(),
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
const serverEnv = serverSchema.safeParse(process.env);
|
|
13
|
+
if (!serverEnv.success) {
|
|
14
|
+
console.error('❌ Invalid server environment variables:');
|
|
15
|
+
console.error(serverEnv.error.flatten().fieldErrors);
|
|
16
|
+
throw new Error('Invalid environment variables');
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// Listed one by one: Next.js only inlines NEXT_PUBLIC_ vars read by their literal name.
|
|
20
|
+
const clientEnv = clientSchema.safeParse({
|
|
21
|
+
NEXT_PUBLIC_APP_URL: process.env.NEXT_PUBLIC_APP_URL,
|
|
22
|
+
});
|
|
23
|
+
if (!clientEnv.success) {
|
|
24
|
+
console.error('❌ Invalid client environment variables:');
|
|
25
|
+
console.error(clientEnv.error.flatten().fieldErrors);
|
|
26
|
+
throw new Error('Invalid client environment variables');
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export const env = { ...serverEnv.data, ...clientEnv.data };
|
|
@@ -1,22 +1,21 @@
|
|
|
1
|
-
// app/(dashboard)/{{kebabCase featureName}}/page.tsx
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
});
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
<
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
}
|
|
1
|
+
// app/(dashboard)/{{kebabCase featureName}}/page.tsx
|
|
2
|
+
import { {{pascalCase featureName}}List } from '@/features/{{kebabCase featureName}}';
|
|
3
|
+
|
|
4
|
+
async function get{{pascalCase featureName}}List() {
|
|
5
|
+
const res = await fetch(`${process.env.API_URL}/api/{{kebabCase featureName}}`, {
|
|
6
|
+
next: { revalidate: 30 },
|
|
7
|
+
headers: { 'Content-Type': 'application/json' },
|
|
8
|
+
});
|
|
9
|
+
if (!res.ok) throw new Error('Failed to fetch {{featureName}}');
|
|
10
|
+
return res.json();
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export default async function {{pascalCase featureName}}Page() {
|
|
14
|
+
const data = await get{{pascalCase featureName}}List();
|
|
15
|
+
return (
|
|
16
|
+
<div className="container py-6">
|
|
17
|
+
<h1 className="text-2xl font-bold mb-6">{{pascalCase featureName}}</h1>
|
|
18
|
+
<{{pascalCase featureName}}List initialData={data} />
|
|
19
|
+
</div>
|
|
20
|
+
);
|
|
21
|
+
}
|