siesa-agents 2.1.36 → 2.1.38
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 +148 -121
- package/_bmad/_config/agent-manifest.csv +20 -0
- package/_bmad/_config/agents/bmb-agent-builder.customize.yaml +41 -0
- package/_bmad/_config/agents/bmb-module-builder.customize.yaml +41 -0
- package/_bmad/_config/agents/bmb-workflow-builder.customize.yaml +41 -0
- package/_bmad/_config/agents/bmm-analyst.customize.yaml +41 -0
- package/_bmad/_config/agents/bmm-architect.customize.yaml +41 -0
- package/_bmad/_config/agents/bmm-dev.customize.yaml +41 -0
- package/_bmad/_config/agents/bmm-pm.customize.yaml +41 -0
- package/_bmad/_config/agents/bmm-quick-flow-solo-dev.customize.yaml +41 -0
- package/_bmad/_config/agents/bmm-sm.customize.yaml +41 -0
- package/_bmad/_config/agents/bmm-tea.customize.yaml +41 -0
- package/_bmad/_config/agents/bmm-tech-writer.customize.yaml +41 -0
- package/_bmad/_config/agents/bmm-ux-designer.customize.yaml +41 -0
- package/_bmad/_config/agents/cis-brainstorming-coach.customize.yaml +41 -0
- package/_bmad/_config/agents/cis-creative-problem-solver.customize.yaml +41 -0
- package/_bmad/_config/agents/cis-design-thinking-coach.customize.yaml +41 -0
- package/_bmad/_config/agents/cis-innovation-strategist.customize.yaml +41 -0
- package/_bmad/_config/agents/cis-presentation-master.customize.yaml +41 -0
- package/_bmad/_config/agents/cis-storyteller.customize.yaml +41 -0
- package/_bmad/_config/agents/core-bmad-master.customize.yaml +41 -0
- package/_bmad/_config/files-manifest.csv +469 -0
- package/_bmad/_config/ides/claude-code.yaml +6 -0
- package/_bmad/_config/manifest.yaml +14 -0
- package/_bmad/_config/task-manifest.csv +6 -0
- package/_bmad/_config/tool-manifest.csv +1 -0
- package/_bmad/_config/workflow-manifest.csv +45 -0
- package/_bmad/_memory/config.yaml +11 -0
- package/_bmad/_memory/storyteller-sidecar/stories-told.md +7 -0
- package/_bmad/_memory/storyteller-sidecar/story-preferences.md +7 -0
- package/_bmad/bmb/README.md +25 -0
- package/_bmad/bmb/agents/agent-builder.md +57 -0
- package/_bmad/bmb/agents/module-builder.md +60 -0
- package/_bmad/bmb/agents/workflow-builder.md +56 -0
- package/_bmad/bmb/config.yaml +12 -0
- package/_bmad/bmb/docs/workflows/architecture.md +220 -0
- package/_bmad/bmb/docs/workflows/common-workflow-tools.csv +19 -0
- package/_bmad/bmb/docs/workflows/csv-data-file-standards.md +206 -0
- package/_bmad/bmb/docs/workflows/intent-vs-prescriptive-spectrum.md +220 -0
- package/_bmad/bmb/docs/workflows/step-file-rules.md +469 -0
- package/_bmad/bmb/docs/workflows/templates/step-01-init-continuable-template.md +241 -0
- package/_bmad/bmb/docs/workflows/templates/step-1b-template.md +223 -0
- package/_bmad/bmb/docs/workflows/templates/step-file.md +139 -0
- package/_bmad/bmb/docs/workflows/templates/step-template.md +290 -0
- package/_bmad/bmb/docs/workflows/templates/workflow-template.md +104 -0
- package/_bmad/bmb/docs/workflows/templates/workflow.md +58 -0
- package/_bmad/bmb/docs/workflows/terms.md +97 -0
- package/_bmad/bmb/reference/agents/simple-examples/README.md +223 -0
- package/_bmad/bmb/reference/readme.md +3 -0
- package/_bmad/bmb/reference/workflows/meal-prep-nutrition/data/dietary-restrictions.csv +18 -0
- package/_bmad/bmb/reference/workflows/meal-prep-nutrition/data/macro-calculator.csv +16 -0
- package/_bmad/bmb/reference/workflows/meal-prep-nutrition/data/recipe-database.csv +28 -0
- package/_bmad/bmb/reference/workflows/meal-prep-nutrition/steps/step-01-init.md +177 -0
- package/_bmad/bmb/reference/workflows/meal-prep-nutrition/steps/step-01b-continue.md +121 -0
- package/_bmad/bmb/reference/workflows/meal-prep-nutrition/steps/step-02-profile.md +165 -0
- package/_bmad/bmb/reference/workflows/meal-prep-nutrition/steps/step-03-assessment.md +154 -0
- package/_bmad/bmb/reference/workflows/meal-prep-nutrition/steps/step-04-strategy.md +183 -0
- package/_bmad/bmb/reference/workflows/meal-prep-nutrition/steps/step-05-shopping.md +168 -0
- package/_bmad/bmb/reference/workflows/meal-prep-nutrition/steps/step-06-prep-schedule.md +195 -0
- package/_bmad/bmb/reference/workflows/meal-prep-nutrition/templates/assessment-section.md +25 -0
- package/_bmad/bmb/reference/workflows/meal-prep-nutrition/templates/nutrition-plan.md +68 -0
- package/_bmad/bmb/reference/workflows/meal-prep-nutrition/templates/prep-schedule-section.md +29 -0
- package/_bmad/bmb/reference/workflows/meal-prep-nutrition/templates/profile-section.md +47 -0
- package/_bmad/bmb/reference/workflows/meal-prep-nutrition/templates/shopping-section.md +37 -0
- package/_bmad/bmb/reference/workflows/meal-prep-nutrition/templates/strategy-section.md +18 -0
- package/_bmad/bmb/reference/workflows/meal-prep-nutrition/workflow.md +59 -0
- package/_bmad/bmb/workflows/agent/data/agent-compilation.md +273 -0
- package/_bmad/bmb/workflows/agent/data/agent-menu-patterns.md +233 -0
- package/_bmad/bmb/workflows/agent/data/agent-metadata.md +208 -0
- package/_bmad/bmb/workflows/agent/data/brainstorm-context.md +146 -0
- package/_bmad/bmb/workflows/agent/data/communication-presets.csv +61 -0
- package/_bmad/bmb/workflows/agent/data/critical-actions.md +120 -0
- package/_bmad/bmb/workflows/agent/data/expert-agent-architecture.md +236 -0
- package/_bmad/bmb/workflows/agent/data/expert-agent-validation.md +173 -0
- package/_bmad/bmb/workflows/agent/data/module-agent-validation.md +124 -0
- package/_bmad/bmb/workflows/agent/data/persona-properties.md +266 -0
- package/_bmad/bmb/workflows/agent/data/principles-crafting.md +292 -0
- package/_bmad/bmb/workflows/agent/data/reference/expert-examples/journal-keeper/journal-keeper-sidecar/entries/yy-mm-dd-entry-template.md +17 -0
- package/_bmad/bmb/workflows/agent/data/reference/module-examples/architect.md +68 -0
- package/_bmad/bmb/workflows/agent/data/simple-agent-architecture.md +204 -0
- package/_bmad/bmb/workflows/agent/data/simple-agent-validation.md +132 -0
- package/_bmad/bmb/workflows/agent/data/understanding-agent-types.md +222 -0
- package/_bmad/bmb/workflows/agent/steps-c/step-01-brainstorm.md +126 -0
- package/_bmad/bmb/workflows/agent/steps-c/step-02-discovery.md +168 -0
- package/_bmad/bmb/workflows/agent/steps-c/step-03-type-metadata.md +294 -0
- package/_bmad/bmb/workflows/agent/steps-c/step-04-persona.md +210 -0
- package/_bmad/bmb/workflows/agent/steps-c/step-05-commands-menu.md +176 -0
- package/_bmad/bmb/workflows/agent/steps-c/step-06-activation.md +275 -0
- package/_bmad/bmb/workflows/agent/steps-c/step-07a-build-simple.md +185 -0
- package/_bmad/bmb/workflows/agent/steps-c/step-07b-build-expert.md +201 -0
- package/_bmad/bmb/workflows/agent/steps-c/step-07c-build-module.md +258 -0
- package/_bmad/bmb/workflows/agent/steps-c/step-08a-plan-traceability.md +203 -0
- package/_bmad/bmb/workflows/agent/steps-c/step-08b-metadata-validation.md +135 -0
- package/_bmad/bmb/workflows/agent/steps-c/step-08c-persona-validation.md +161 -0
- package/_bmad/bmb/workflows/agent/steps-c/step-08d-menu-validation.md +158 -0
- package/_bmad/bmb/workflows/agent/steps-c/step-08e-structure-validation.md +306 -0
- package/_bmad/bmb/workflows/agent/steps-c/step-08f-sidecar-validation.md +462 -0
- package/_bmad/bmb/workflows/agent/steps-c/step-09-celebrate.md +244 -0
- package/_bmad/bmb/workflows/agent/steps-e/e-01-load-existing.md +214 -0
- package/_bmad/bmb/workflows/agent/steps-e/e-02-discover-edits.md +191 -0
- package/_bmad/bmb/workflows/agent/steps-e/e-03a-validate-metadata.md +78 -0
- package/_bmad/bmb/workflows/agent/steps-e/e-03b-validate-persona.md +76 -0
- package/_bmad/bmb/workflows/agent/steps-e/e-03c-validate-menu.md +75 -0
- package/_bmad/bmb/workflows/agent/steps-e/e-03d-validate-structure.md +75 -0
- package/_bmad/bmb/workflows/agent/steps-e/e-03e-validate-sidecar.md +78 -0
- package/_bmad/bmb/workflows/agent/steps-e/e-03f-validation-summary.md +119 -0
- package/_bmad/bmb/workflows/agent/steps-e/e-04-type-metadata.md +122 -0
- package/_bmad/bmb/workflows/agent/steps-e/e-05-persona.md +132 -0
- package/_bmad/bmb/workflows/agent/steps-e/e-06-commands-menu.md +120 -0
- package/_bmad/bmb/workflows/agent/steps-e/e-07-activation.md +122 -0
- package/_bmad/bmb/workflows/agent/steps-e/e-08a-edit-simple.md +134 -0
- package/_bmad/bmb/workflows/agent/steps-e/e-08b-edit-expert.md +117 -0
- package/_bmad/bmb/workflows/agent/steps-e/e-08c-edit-module.md +120 -0
- package/_bmad/bmb/workflows/agent/steps-e/e-09a-validate-metadata.md +70 -0
- package/_bmad/bmb/workflows/agent/steps-e/e-09b-validate-persona.md +70 -0
- package/_bmad/bmb/workflows/agent/steps-e/e-09c-validate-menu.md +69 -0
- package/_bmad/bmb/workflows/agent/steps-e/e-09d-validate-structure.md +69 -0
- package/_bmad/bmb/workflows/agent/steps-e/e-09e-validate-sidecar.md +70 -0
- package/_bmad/bmb/workflows/agent/steps-e/e-09f-validation-summary.md +111 -0
- package/_bmad/bmb/workflows/agent/steps-e/e-10-celebrate.md +150 -0
- package/_bmad/bmb/workflows/agent/steps-v/v-01-load-review.md +128 -0
- package/_bmad/bmb/workflows/agent/steps-v/v-02a-validate-metadata.md +73 -0
- package/_bmad/bmb/workflows/agent/steps-v/v-02b-validate-persona.md +72 -0
- package/_bmad/bmb/workflows/agent/steps-v/v-02c-validate-menu.md +71 -0
- package/_bmad/bmb/workflows/agent/steps-v/v-02d-validate-structure.md +71 -0
- package/_bmad/bmb/workflows/agent/steps-v/v-02e-validate-sidecar.md +76 -0
- package/_bmad/bmb/workflows/agent/steps-v/v-03-summary.md +100 -0
- package/_bmad/bmb/workflows/agent/templates/agent-plan.template.md +5 -0
- package/_bmad/bmb/workflows/agent/templates/expert-agent-template/expert-agent.template.md +76 -0
- package/_bmad/bmb/workflows/agent/templates/simple-agent.template.md +71 -0
- package/_bmad/bmb/workflows/agent/workflow.md +123 -0
- package/_bmad/bmb/workflows/create-module/steps/step-01-init.md +156 -0
- package/_bmad/bmb/workflows/create-module/steps/step-01b-continue.md +170 -0
- package/_bmad/bmb/workflows/create-module/steps/step-02-concept.md +218 -0
- package/_bmad/bmb/workflows/create-module/steps/step-03-components.md +268 -0
- package/_bmad/bmb/workflows/create-module/steps/step-04-structure.md +229 -0
- package/_bmad/bmb/workflows/create-module/steps/step-05-config.md +234 -0
- package/_bmad/bmb/workflows/create-module/steps/step-06-agents.md +297 -0
- package/_bmad/bmb/workflows/create-module/steps/step-07-workflows.md +229 -0
- package/_bmad/bmb/workflows/create-module/steps/step-08-installer.md +187 -0
- package/_bmad/bmb/workflows/create-module/steps/step-09-documentation.md +310 -0
- package/_bmad/bmb/workflows/create-module/steps/step-10-roadmap.md +338 -0
- package/_bmad/bmb/workflows/create-module/steps/step-11-validate.md +336 -0
- package/_bmad/bmb/workflows/create-module/templates/agent.template.md +313 -0
- package/_bmad/bmb/workflows/create-module/templates/installer.template.js +47 -0
- package/_bmad/bmb/workflows/create-module/templates/module-plan.template.md +5 -0
- package/_bmad/bmb/workflows/create-module/templates/module.template.yaml +53 -0
- package/_bmad/bmb/workflows/create-module/templates/workflow-plan-template.md +23 -0
- package/_bmad/bmb/workflows/create-module/validation.md +126 -0
- package/_bmad/bmb/workflows/create-module/workflow.md +56 -0
- package/_bmad/bmb/workflows/create-workflow/data/examples/meal-prep-nutrition/data/dietary-restrictions.csv +18 -0
- package/_bmad/bmb/workflows/create-workflow/data/examples/meal-prep-nutrition/data/macro-calculator.csv +16 -0
- package/_bmad/bmb/workflows/create-workflow/data/examples/meal-prep-nutrition/data/recipe-database.csv +28 -0
- package/_bmad/bmb/workflows/create-workflow/data/examples/meal-prep-nutrition/steps/step-01-init.md +177 -0
- package/_bmad/bmb/workflows/create-workflow/data/examples/meal-prep-nutrition/steps/step-01b-continue.md +150 -0
- package/_bmad/bmb/workflows/create-workflow/data/examples/meal-prep-nutrition/steps/step-02-profile.md +164 -0
- package/_bmad/bmb/workflows/create-workflow/data/examples/meal-prep-nutrition/steps/step-03-assessment.md +152 -0
- package/_bmad/bmb/workflows/create-workflow/data/examples/meal-prep-nutrition/steps/step-04-strategy.md +182 -0
- package/_bmad/bmb/workflows/create-workflow/data/examples/meal-prep-nutrition/steps/step-05-shopping.md +167 -0
- package/_bmad/bmb/workflows/create-workflow/data/examples/meal-prep-nutrition/steps/step-06-prep-schedule.md +194 -0
- package/_bmad/bmb/workflows/create-workflow/data/examples/meal-prep-nutrition/templates/assessment-section.md +25 -0
- package/_bmad/bmb/workflows/create-workflow/data/examples/meal-prep-nutrition/templates/nutrition-plan.md +68 -0
- package/_bmad/bmb/workflows/create-workflow/data/examples/meal-prep-nutrition/templates/prep-schedule-section.md +29 -0
- package/_bmad/bmb/workflows/create-workflow/data/examples/meal-prep-nutrition/templates/profile-section.md +47 -0
- package/_bmad/bmb/workflows/create-workflow/data/examples/meal-prep-nutrition/templates/shopping-section.md +37 -0
- package/_bmad/bmb/workflows/create-workflow/data/examples/meal-prep-nutrition/templates/strategy-section.md +18 -0
- package/_bmad/bmb/workflows/create-workflow/data/examples/meal-prep-nutrition/workflow.md +58 -0
- package/_bmad/bmb/workflows/create-workflow/steps/step-01-init.md +158 -0
- package/_bmad/bmb/workflows/create-workflow/steps/step-02-gather.md +212 -0
- package/_bmad/bmb/workflows/create-workflow/steps/step-03-tools-configuration.md +251 -0
- package/_bmad/bmb/workflows/create-workflow/steps/step-04-plan-review.md +217 -0
- package/_bmad/bmb/workflows/create-workflow/steps/step-05-output-format-design.md +290 -0
- package/_bmad/bmb/workflows/create-workflow/steps/step-06-design.md +272 -0
- package/_bmad/bmb/workflows/create-workflow/steps/step-07-build.md +323 -0
- package/_bmad/bmb/workflows/create-workflow/steps/step-08-review.md +285 -0
- package/_bmad/bmb/workflows/create-workflow/steps/step-09-complete.md +188 -0
- package/_bmad/bmb/workflows/create-workflow/workflow.md +59 -0
- package/_bmad/bmb/workflows/edit-workflow/steps/step-01-analyze.md +217 -0
- package/_bmad/bmb/workflows/edit-workflow/steps/step-02-discover.md +254 -0
- package/_bmad/bmb/workflows/edit-workflow/steps/step-03-improve.md +218 -0
- package/_bmad/bmb/workflows/edit-workflow/steps/step-04-validate.md +194 -0
- package/_bmad/bmb/workflows/edit-workflow/steps/step-05-compliance-check.md +246 -0
- package/_bmad/bmb/workflows/edit-workflow/templates/completion-summary.md +75 -0
- package/_bmad/bmb/workflows/edit-workflow/templates/improvement-goals.md +68 -0
- package/_bmad/bmb/workflows/edit-workflow/templates/improvement-log.md +40 -0
- package/_bmad/bmb/workflows/edit-workflow/templates/validation-results.md +51 -0
- package/_bmad/bmb/workflows/edit-workflow/templates/workflow-analysis.md +56 -0
- package/_bmad/bmb/workflows/edit-workflow/workflow.md +59 -0
- package/_bmad/bmb/workflows/workflow-compliance-check/steps/step-01-validate-goal.md +153 -0
- package/_bmad/bmb/workflows/workflow-compliance-check/steps/step-02-workflow-validation.md +244 -0
- package/_bmad/bmb/workflows/workflow-compliance-check/steps/step-03-step-validation.md +275 -0
- package/_bmad/bmb/workflows/workflow-compliance-check/steps/step-04-file-validation.md +296 -0
- package/_bmad/bmb/workflows/workflow-compliance-check/steps/step-05-intent-spectrum-validation.md +265 -0
- package/_bmad/bmb/workflows/workflow-compliance-check/steps/step-06-web-subprocess-validation.md +361 -0
- package/_bmad/bmb/workflows/workflow-compliance-check/steps/step-07-holistic-analysis.md +259 -0
- package/_bmad/bmb/workflows/workflow-compliance-check/steps/step-08-generate-report.md +302 -0
- package/_bmad/bmb/workflows/workflow-compliance-check/templates/compliance-report.md +140 -0
- package/_bmad/bmb/workflows/workflow-compliance-check/workflow.md +59 -0
- package/_bmad/bmb/workflows-legacy/edit-module/README.md +171 -0
- package/_bmad/bmb/workflows-legacy/edit-module/checklist.md +163 -0
- package/_bmad/bmb/workflows-legacy/edit-module/instructions.md +340 -0
- package/_bmad/bmb/workflows-legacy/edit-module/workflow.yaml +33 -0
- package/_bmad/bmb/workflows-legacy/module-brief/README.md +264 -0
- package/_bmad/bmb/workflows-legacy/module-brief/checklist.md +116 -0
- package/_bmad/bmb/workflows-legacy/module-brief/instructions.md +268 -0
- package/_bmad/bmb/workflows-legacy/module-brief/template.md +275 -0
- package/_bmad/bmb/workflows-legacy/module-brief/workflow.yaml +35 -0
- package/_bmad/bmm/agents/analyst.md +76 -0
- package/_bmad/bmm/agents/architect.md +68 -0
- package/_bmad/bmm/agents/dev.md +70 -0
- package/_bmad/bmm/agents/pm.md +70 -0
- package/_bmad/bmm/agents/quick-flow-solo-dev.md +68 -0
- package/_bmad/bmm/agents/sm.md +71 -0
- package/_bmad/bmm/agents/tea.md +71 -0
- package/_bmad/bmm/agents/tech-writer.md +72 -0
- package/_bmad/bmm/agents/ux-designer.md +68 -0
- package/_bmad/bmm/config.yaml +18 -0
- package/_bmad/bmm/data/README.md +29 -0
- package/_bmad/bmm/data/documentation-standards.md +262 -0
- package/_bmad/bmm/data/project-context-template.md +40 -0
- package/_bmad/bmm/teams/default-party.csv +21 -0
- package/_bmad/bmm/teams/team-fullstack.yaml +12 -0
- package/_bmad/bmm/testarch/knowledge/api-request.md +303 -0
- package/_bmad/bmm/testarch/knowledge/auth-session.md +356 -0
- package/_bmad/bmm/testarch/knowledge/burn-in.md +273 -0
- package/_bmad/bmm/testarch/knowledge/ci-burn-in.md +675 -0
- package/_bmad/bmm/testarch/knowledge/component-tdd.md +486 -0
- package/_bmad/bmm/testarch/knowledge/contract-testing.md +957 -0
- package/_bmad/bmm/testarch/knowledge/data-factories.md +500 -0
- package/_bmad/bmm/testarch/knowledge/email-auth.md +721 -0
- package/_bmad/bmm/testarch/knowledge/error-handling.md +725 -0
- package/_bmad/bmm/testarch/knowledge/feature-flags.md +750 -0
- package/_bmad/bmm/testarch/knowledge/file-utils.md +260 -0
- package/_bmad/bmm/testarch/knowledge/fixture-architecture.md +401 -0
- package/_bmad/bmm/testarch/knowledge/fixtures-composition.md +382 -0
- package/_bmad/bmm/testarch/knowledge/intercept-network-call.md +280 -0
- package/_bmad/bmm/testarch/knowledge/log.md +294 -0
- package/_bmad/bmm/testarch/knowledge/network-error-monitor.md +272 -0
- package/_bmad/bmm/testarch/knowledge/network-first.md +486 -0
- package/_bmad/bmm/testarch/knowledge/network-recorder.md +265 -0
- package/_bmad/bmm/testarch/knowledge/nfr-criteria.md +670 -0
- package/_bmad/bmm/testarch/knowledge/overview.md +283 -0
- package/_bmad/bmm/testarch/knowledge/playwright-config.md +730 -0
- package/_bmad/bmm/testarch/knowledge/probability-impact.md +601 -0
- package/_bmad/bmm/testarch/knowledge/recurse.md +296 -0
- package/_bmad/bmm/testarch/knowledge/risk-governance.md +615 -0
- package/_bmad/bmm/testarch/knowledge/selective-testing.md +732 -0
- package/_bmad/bmm/testarch/knowledge/selector-resilience.md +527 -0
- package/_bmad/bmm/testarch/knowledge/test-healing-patterns.md +644 -0
- package/_bmad/bmm/testarch/knowledge/test-levels-framework.md +473 -0
- package/_bmad/bmm/testarch/knowledge/test-priorities-matrix.md +373 -0
- package/_bmad/bmm/testarch/knowledge/test-quality.md +664 -0
- package/_bmad/bmm/testarch/knowledge/timing-debugging.md +372 -0
- package/_bmad/bmm/testarch/knowledge/visual-debugging.md +524 -0
- package/_bmad/bmm/testarch/tea-index.csv +33 -0
- package/_bmad/bmm/workflows/1-analysis/create-product-brief/product-brief.template.md +10 -0
- package/_bmad/bmm/workflows/1-analysis/create-product-brief/steps/step-01-init.md +182 -0
- package/_bmad/bmm/workflows/1-analysis/create-product-brief/steps/step-01b-continue.md +166 -0
- package/_bmad/bmm/workflows/1-analysis/create-product-brief/steps/step-02-vision.md +204 -0
- package/_bmad/bmm/workflows/1-analysis/create-product-brief/steps/step-03-users.md +207 -0
- package/_bmad/bmm/workflows/1-analysis/create-product-brief/steps/step-04-metrics.md +210 -0
- package/_bmad/bmm/workflows/1-analysis/create-product-brief/steps/step-05-scope.md +224 -0
- package/_bmad/bmm/workflows/1-analysis/create-product-brief/steps/step-06-complete.md +199 -0
- package/_bmad/bmm/workflows/1-analysis/create-product-brief/workflow.md +58 -0
- package/_bmad/bmm/workflows/1-analysis/research/domain-steps/step-01-init.md +137 -0
- package/_bmad/bmm/workflows/1-analysis/research/domain-steps/step-02-domain-analysis.md +229 -0
- package/_bmad/bmm/workflows/1-analysis/research/domain-steps/step-03-competitive-landscape.md +238 -0
- package/_bmad/bmm/workflows/1-analysis/research/domain-steps/step-04-regulatory-focus.md +206 -0
- package/_bmad/bmm/workflows/1-analysis/research/domain-steps/step-05-technical-trends.md +234 -0
- package/_bmad/bmm/workflows/1-analysis/research/domain-steps/step-06-research-synthesis.md +443 -0
- package/_bmad/bmm/workflows/1-analysis/research/market-steps/step-01-init.md +182 -0
- package/_bmad/bmm/workflows/1-analysis/research/market-steps/step-02-customer-behavior.md +237 -0
- package/_bmad/bmm/workflows/1-analysis/research/market-steps/step-02-customer-insights.md +200 -0
- package/_bmad/bmm/workflows/1-analysis/research/market-steps/step-03-customer-pain-points.md +249 -0
- package/_bmad/bmm/workflows/1-analysis/research/market-steps/step-04-customer-decisions.md +259 -0
- package/_bmad/bmm/workflows/1-analysis/research/market-steps/step-05-competitive-analysis.md +177 -0
- package/_bmad/bmm/workflows/1-analysis/research/market-steps/step-06-research-completion.md +475 -0
- package/_bmad/bmm/workflows/1-analysis/research/research.template.md +29 -0
- package/_bmad/bmm/workflows/1-analysis/research/technical-steps/step-01-init.md +137 -0
- package/_bmad/bmm/workflows/1-analysis/research/technical-steps/step-02-technical-overview.md +239 -0
- package/_bmad/bmm/workflows/1-analysis/research/technical-steps/step-03-integration-patterns.md +248 -0
- package/_bmad/bmm/workflows/1-analysis/research/technical-steps/step-04-architectural-patterns.md +202 -0
- package/_bmad/bmm/workflows/1-analysis/research/technical-steps/step-05-implementation-research.md +239 -0
- package/_bmad/bmm/workflows/1-analysis/research/technical-steps/step-06-research-synthesis.md +486 -0
- package/_bmad/bmm/workflows/1-analysis/research/workflow.md +173 -0
- package/_bmad/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-01-init.md +135 -0
- package/_bmad/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-01b-continue.md +127 -0
- package/_bmad/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-02-discovery.md +190 -0
- package/_bmad/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-03-core-experience.md +216 -0
- package/_bmad/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-04-emotional-response.md +219 -0
- package/_bmad/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-05-inspiration.md +234 -0
- package/_bmad/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-06-design-system.md +252 -0
- package/_bmad/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-07-defining-experience.md +254 -0
- package/_bmad/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-08-visual-foundation.md +224 -0
- package/_bmad/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-09-design-directions.md +224 -0
- package/_bmad/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-10-user-journeys.md +241 -0
- package/_bmad/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-11-component-strategy.md +248 -0
- package/_bmad/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-12-ux-patterns.md +237 -0
- package/_bmad/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-13-responsive-accessibility.md +264 -0
- package/_bmad/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-14-complete.md +228 -0
- package/_bmad/bmm/workflows/2-plan-workflows/create-ux-design/ux-design-template.md +13 -0
- package/_bmad/bmm/workflows/2-plan-workflows/create-ux-design/workflow.md +43 -0
- package/_bmad/bmm/workflows/2-plan-workflows/prd/domain-complexity.csv +13 -0
- package/_bmad/bmm/workflows/2-plan-workflows/prd/prd-template.md +11 -0
- package/_bmad/bmm/workflows/2-plan-workflows/prd/project-types.csv +11 -0
- package/_bmad/bmm/workflows/2-plan-workflows/prd/steps/step-01-init.md +197 -0
- package/_bmad/bmm/workflows/2-plan-workflows/prd/steps/step-01b-continue.md +166 -0
- package/_bmad/bmm/workflows/2-plan-workflows/prd/steps/step-02-discovery.md +421 -0
- package/_bmad/bmm/workflows/2-plan-workflows/prd/steps/step-03-success.md +290 -0
- package/_bmad/bmm/workflows/2-plan-workflows/prd/steps/step-04-journeys.md +291 -0
- package/_bmad/bmm/workflows/2-plan-workflows/prd/steps/step-05-domain.md +271 -0
- package/_bmad/bmm/workflows/2-plan-workflows/prd/steps/step-06-innovation.md +262 -0
- package/_bmad/bmm/workflows/2-plan-workflows/prd/steps/step-07-project-type.md +258 -0
- package/_bmad/bmm/workflows/2-plan-workflows/prd/steps/step-08-scoping.md +299 -0
- package/_bmad/bmm/workflows/2-plan-workflows/prd/steps/step-09-functional.md +270 -0
- package/_bmad/bmm/workflows/2-plan-workflows/prd/steps/step-10-nonfunctional.md +294 -0
- package/_bmad/bmm/workflows/2-plan-workflows/prd/steps/step-11-complete.md +186 -0
- package/_bmad/bmm/workflows/2-plan-workflows/prd/workflow.md +63 -0
- package/_bmad/bmm/workflows/3-solutioning/check-implementation-readiness/steps/step-01-document-discovery.md +190 -0
- package/_bmad/bmm/workflows/3-solutioning/check-implementation-readiness/steps/step-02-prd-analysis.md +178 -0
- package/_bmad/bmm/workflows/3-solutioning/check-implementation-readiness/steps/step-03-epic-coverage-validation.md +179 -0
- package/_bmad/bmm/workflows/3-solutioning/check-implementation-readiness/steps/step-04-ux-alignment.md +139 -0
- package/_bmad/bmm/workflows/3-solutioning/check-implementation-readiness/steps/step-05-epic-quality-review.md +252 -0
- package/_bmad/bmm/workflows/3-solutioning/check-implementation-readiness/steps/step-06-final-assessment.md +133 -0
- package/_bmad/bmm/workflows/3-solutioning/check-implementation-readiness/templates/readiness-report-template.md +4 -0
- package/_bmad/bmm/workflows/3-solutioning/check-implementation-readiness/workflow.md +55 -0
- package/_bmad/bmm/workflows/3-solutioning/create-architecture/architecture-decision-template.md +12 -0
- package/{bmad-core/data → _bmad/bmm/workflows/3-solutioning/create-architecture/data/company-standards}/architecture-patterns.md +415 -415
- package/{bmad-core/data → _bmad/bmm/workflows/3-solutioning/create-architecture/data/company-standards}/backend-standards.md +811 -811
- package/{bmad-core/data → _bmad/bmm/workflows/3-solutioning/create-architecture/data/company-standards}/frontend-standards.md +375 -373
- package/{bmad-core/data → _bmad/bmm/workflows/3-solutioning/create-architecture/data/company-standards}/technical-preferences-ux.md +422 -422
- package/{bmad-core/data → _bmad/bmm/workflows/3-solutioning/create-architecture/data/company-standards}/technology-stack.md +235 -235
- package/_bmad/bmm/workflows/3-solutioning/create-architecture/data/domain-complexity.csv +11 -0
- package/_bmad/bmm/workflows/3-solutioning/create-architecture/data/project-types.csv +7 -0
- package/_bmad/bmm/workflows/3-solutioning/create-architecture/steps/step-01-init.md +166 -0
- package/_bmad/bmm/workflows/3-solutioning/create-architecture/steps/step-01b-continue.md +164 -0
- package/_bmad/bmm/workflows/3-solutioning/create-architecture/steps/step-02-context.md +224 -0
- package/_bmad/bmm/workflows/3-solutioning/create-architecture/steps/step-03-starter.md +342 -0
- package/_bmad/bmm/workflows/3-solutioning/create-architecture/steps/step-04-decisions.md +328 -0
- package/_bmad/bmm/workflows/3-solutioning/create-architecture/steps/step-05-patterns.md +368 -0
- package/_bmad/bmm/workflows/3-solutioning/create-architecture/steps/step-06-structure.md +379 -0
- package/_bmad/bmm/workflows/3-solutioning/create-architecture/steps/step-07-validation.md +366 -0
- package/_bmad/bmm/workflows/3-solutioning/create-architecture/steps/step-08-complete.md +352 -0
- package/_bmad/bmm/workflows/3-solutioning/create-architecture/workflow.md +51 -0
- package/_bmad/bmm/workflows/3-solutioning/create-epics-and-stories/steps/step-01-validate-prerequisites.md +259 -0
- package/_bmad/bmm/workflows/3-solutioning/create-epics-and-stories/steps/step-02-design-epics.md +233 -0
- package/_bmad/bmm/workflows/3-solutioning/create-epics-and-stories/steps/step-03-create-stories.md +272 -0
- package/_bmad/bmm/workflows/3-solutioning/create-epics-and-stories/steps/step-04-final-validation.md +145 -0
- package/_bmad/bmm/workflows/3-solutioning/create-epics-and-stories/templates/epics-template.md +57 -0
- package/_bmad/bmm/workflows/3-solutioning/create-epics-and-stories/workflow.md +59 -0
- package/_bmad/bmm/workflows/4-implementation/code-review/checklist.md +23 -0
- package/_bmad/bmm/workflows/4-implementation/code-review/instructions.xml +225 -0
- package/_bmad/bmm/workflows/4-implementation/code-review/workflow.yaml +50 -0
- package/_bmad/bmm/workflows/4-implementation/correct-course/checklist.md +279 -0
- package/_bmad/bmm/workflows/4-implementation/correct-course/instructions.md +206 -0
- package/_bmad/bmm/workflows/4-implementation/correct-course/workflow.yaml +58 -0
- package/_bmad/bmm/workflows/4-implementation/create-story/checklist.md +358 -0
- package/_bmad/bmm/workflows/4-implementation/create-story/steps/step-01-determine-story.md +85 -0
- package/_bmad/bmm/workflows/4-implementation/create-story/steps/step-02-analyze.md +67 -0
- package/_bmad/bmm/workflows/4-implementation/create-story/steps/step-03-architecture.md +71 -0
- package/_bmad/bmm/workflows/4-implementation/create-story/steps/step-04-web-research.md +58 -0
- package/_bmad/bmm/workflows/4-implementation/create-story/steps/step-05-create-file.md +76 -0
- package/_bmad/bmm/workflows/4-implementation/create-story/steps/step-06-finalize.md +66 -0
- package/_bmad/bmm/workflows/4-implementation/create-story/template.md +49 -0
- package/_bmad/bmm/workflows/4-implementation/create-story/workflow.md +58 -0
- package/_bmad/bmm/workflows/4-implementation/create-story/workflow.yaml.bak +63 -0
- package/_bmad/bmm/workflows/4-implementation/dev-story/checklist.md +85 -0
- package/_bmad/bmm/workflows/4-implementation/dev-story/instructions.xml +471 -0
- package/_bmad/bmm/workflows/4-implementation/dev-story/workflow.yaml +25 -0
- package/_bmad/bmm/workflows/4-implementation/retrospective/instructions.md +1443 -0
- package/_bmad/bmm/workflows/4-implementation/retrospective/workflow.yaml +57 -0
- package/_bmad/bmm/workflows/4-implementation/sprint-planning/checklist.md +33 -0
- package/_bmad/bmm/workflows/4-implementation/sprint-planning/instructions.md +225 -0
- package/_bmad/bmm/workflows/4-implementation/sprint-planning/sprint-status-template.yaml +55 -0
- package/_bmad/bmm/workflows/4-implementation/sprint-planning/workflow.yaml +52 -0
- package/_bmad/bmm/workflows/4-implementation/sprint-status/instructions.md +229 -0
- package/_bmad/bmm/workflows/4-implementation/sprint-status/workflow.yaml +35 -0
- package/_bmad/bmm/workflows/bmad-quick-flow/create-tech-spec/steps/step-01-understand.md +189 -0
- package/_bmad/bmm/workflows/bmad-quick-flow/create-tech-spec/steps/step-02-investigate.md +144 -0
- package/_bmad/bmm/workflows/bmad-quick-flow/create-tech-spec/steps/step-03-generate.md +128 -0
- package/_bmad/bmm/workflows/bmad-quick-flow/create-tech-spec/steps/step-04-review.md +173 -0
- package/_bmad/bmm/workflows/bmad-quick-flow/create-tech-spec/tech-spec-template.md +74 -0
- package/_bmad/bmm/workflows/bmad-quick-flow/create-tech-spec/workflow.md +79 -0
- package/_bmad/bmm/workflows/bmad-quick-flow/quick-dev/steps/step-01-mode-detection.md +156 -0
- package/_bmad/bmm/workflows/bmad-quick-flow/quick-dev/steps/step-02-context-gathering.md +120 -0
- package/_bmad/bmm/workflows/bmad-quick-flow/quick-dev/steps/step-03-execute.md +113 -0
- package/_bmad/bmm/workflows/bmad-quick-flow/quick-dev/steps/step-04-self-check.md +113 -0
- package/_bmad/bmm/workflows/bmad-quick-flow/quick-dev/steps/step-05-adversarial-review.md +106 -0
- package/_bmad/bmm/workflows/bmad-quick-flow/quick-dev/steps/step-06-resolve-findings.md +140 -0
- package/_bmad/bmm/workflows/bmad-quick-flow/quick-dev/workflow.md +52 -0
- package/_bmad/bmm/workflows/document-project/checklist.md +245 -0
- package/_bmad/bmm/workflows/document-project/documentation-requirements.csv +12 -0
- package/_bmad/bmm/workflows/document-project/instructions.md +221 -0
- package/_bmad/bmm/workflows/document-project/templates/deep-dive-template.md +345 -0
- package/_bmad/bmm/workflows/document-project/templates/index-template.md +169 -0
- package/_bmad/bmm/workflows/document-project/templates/project-overview-template.md +103 -0
- package/_bmad/bmm/workflows/document-project/templates/project-scan-report-schema.json +160 -0
- package/_bmad/bmm/workflows/document-project/templates/source-tree-template.md +135 -0
- package/_bmad/bmm/workflows/document-project/workflow.yaml +28 -0
- package/_bmad/bmm/workflows/document-project/workflows/deep-dive-instructions.md +298 -0
- package/_bmad/bmm/workflows/document-project/workflows/deep-dive.yaml +31 -0
- package/_bmad/bmm/workflows/document-project/workflows/full-scan-instructions.md +1106 -0
- package/_bmad/bmm/workflows/document-project/workflows/full-scan.yaml +31 -0
- package/_bmad/bmm/workflows/excalidraw-diagrams/_shared/excalidraw-library.json +90 -0
- package/_bmad/bmm/workflows/excalidraw-diagrams/_shared/excalidraw-templates.yaml +127 -0
- package/_bmad/bmm/workflows/excalidraw-diagrams/create-dataflow/checklist.md +39 -0
- package/_bmad/bmm/workflows/excalidraw-diagrams/create-dataflow/instructions.md +130 -0
- package/_bmad/bmm/workflows/excalidraw-diagrams/create-dataflow/workflow.yaml +26 -0
- package/_bmad/bmm/workflows/excalidraw-diagrams/create-diagram/checklist.md +43 -0
- package/_bmad/bmm/workflows/excalidraw-diagrams/create-diagram/instructions.md +141 -0
- package/_bmad/bmm/workflows/excalidraw-diagrams/create-diagram/workflow.yaml +26 -0
- package/_bmad/bmm/workflows/excalidraw-diagrams/create-flowchart/checklist.md +49 -0
- package/_bmad/bmm/workflows/excalidraw-diagrams/create-flowchart/instructions.md +241 -0
- package/_bmad/bmm/workflows/excalidraw-diagrams/create-flowchart/workflow.yaml +26 -0
- package/_bmad/bmm/workflows/excalidraw-diagrams/create-wireframe/checklist.md +38 -0
- package/_bmad/bmm/workflows/excalidraw-diagrams/create-wireframe/instructions.md +133 -0
- package/_bmad/bmm/workflows/excalidraw-diagrams/create-wireframe/workflow.yaml +26 -0
- package/_bmad/bmm/workflows/generate-project-context/project-context-template.md +21 -0
- package/_bmad/bmm/workflows/generate-project-context/steps/step-01-discover.md +218 -0
- package/_bmad/bmm/workflows/generate-project-context/steps/step-02-generate.md +318 -0
- package/_bmad/bmm/workflows/generate-project-context/steps/step-03-complete.md +278 -0
- package/_bmad/bmm/workflows/generate-project-context/workflow.md +50 -0
- package/_bmad/bmm/workflows/testarch/atdd/atdd-checklist-template.md +364 -0
- package/_bmad/bmm/workflows/testarch/atdd/checklist.md +374 -0
- package/_bmad/bmm/workflows/testarch/atdd/instructions.md +806 -0
- package/_bmad/bmm/workflows/testarch/atdd/workflow.yaml +45 -0
- package/_bmad/bmm/workflows/testarch/automate/checklist.md +582 -0
- package/_bmad/bmm/workflows/testarch/automate/instructions.md +1324 -0
- package/_bmad/bmm/workflows/testarch/automate/workflow.yaml +52 -0
- package/_bmad/bmm/workflows/testarch/ci/checklist.md +248 -0
- package/_bmad/bmm/workflows/testarch/ci/github-actions-template.yaml +198 -0
- package/_bmad/bmm/workflows/testarch/ci/gitlab-ci-template.yaml +149 -0
- package/_bmad/bmm/workflows/testarch/ci/instructions.md +536 -0
- package/_bmad/bmm/workflows/testarch/ci/workflow.yaml +45 -0
- package/_bmad/bmm/workflows/testarch/framework/checklist.md +321 -0
- package/_bmad/bmm/workflows/testarch/framework/instructions.md +481 -0
- package/_bmad/bmm/workflows/testarch/framework/workflow.yaml +47 -0
- package/_bmad/bmm/workflows/testarch/nfr-assess/checklist.md +407 -0
- package/_bmad/bmm/workflows/testarch/nfr-assess/instructions.md +722 -0
- package/_bmad/bmm/workflows/testarch/nfr-assess/nfr-report-template.md +445 -0
- package/_bmad/bmm/workflows/testarch/nfr-assess/workflow.yaml +47 -0
- package/_bmad/bmm/workflows/testarch/test-design/checklist.md +235 -0
- package/_bmad/bmm/workflows/testarch/test-design/instructions.md +788 -0
- package/_bmad/bmm/workflows/testarch/test-design/test-design-template.md +294 -0
- package/_bmad/bmm/workflows/testarch/test-design/workflow.yaml +54 -0
- package/_bmad/bmm/workflows/testarch/test-review/checklist.md +472 -0
- package/_bmad/bmm/workflows/testarch/test-review/instructions.md +628 -0
- package/_bmad/bmm/workflows/testarch/test-review/test-review-template.md +390 -0
- package/_bmad/bmm/workflows/testarch/test-review/workflow.yaml +46 -0
- package/_bmad/bmm/workflows/testarch/trace/checklist.md +655 -0
- package/_bmad/bmm/workflows/testarch/trace/instructions.md +1047 -0
- package/_bmad/bmm/workflows/testarch/trace/trace-template.md +675 -0
- package/_bmad/bmm/workflows/testarch/trace/workflow.yaml +55 -0
- package/_bmad/bmm/workflows/workflow-status/init/instructions.md +346 -0
- package/_bmad/bmm/workflows/workflow-status/init/workflow.yaml +29 -0
- package/_bmad/bmm/workflows/workflow-status/instructions.md +395 -0
- package/_bmad/bmm/workflows/workflow-status/paths/enterprise-brownfield.yaml +103 -0
- package/_bmad/bmm/workflows/workflow-status/paths/enterprise-greenfield.yaml +100 -0
- package/_bmad/bmm/workflows/workflow-status/paths/method-brownfield.yaml +103 -0
- package/_bmad/bmm/workflows/workflow-status/paths/method-greenfield.yaml +100 -0
- package/_bmad/bmm/workflows/workflow-status/project-levels.yaml +59 -0
- package/_bmad/bmm/workflows/workflow-status/workflow-status-template.yaml +24 -0
- package/_bmad/bmm/workflows/workflow-status/workflow.yaml +30 -0
- package/_bmad/cis/agents/brainstorming-coach.md +60 -0
- package/_bmad/cis/agents/creative-problem-solver.md +60 -0
- package/_bmad/cis/agents/design-thinking-coach.md +60 -0
- package/_bmad/cis/agents/innovation-strategist.md +60 -0
- package/_bmad/cis/agents/presentation-master.md +66 -0
- package/_bmad/cis/agents/storyteller/storyteller.md +57 -0
- package/_bmad/cis/config.yaml +11 -0
- package/_bmad/cis/teams/creative-squad.yaml +7 -0
- package/_bmad/cis/teams/default-party.csv +12 -0
- package/_bmad/cis/workflows/README.md +139 -0
- package/_bmad/cis/workflows/design-thinking/README.md +56 -0
- package/_bmad/cis/workflows/design-thinking/design-methods.csv +31 -0
- package/_bmad/cis/workflows/design-thinking/instructions.md +202 -0
- package/_bmad/cis/workflows/design-thinking/template.md +111 -0
- package/_bmad/cis/workflows/design-thinking/workflow.yaml +27 -0
- package/_bmad/cis/workflows/innovation-strategy/README.md +56 -0
- package/_bmad/cis/workflows/innovation-strategy/innovation-frameworks.csv +31 -0
- package/_bmad/cis/workflows/innovation-strategy/instructions.md +276 -0
- package/_bmad/cis/workflows/innovation-strategy/template.md +189 -0
- package/_bmad/cis/workflows/innovation-strategy/workflow.yaml +27 -0
- package/_bmad/cis/workflows/problem-solving/README.md +56 -0
- package/_bmad/cis/workflows/problem-solving/instructions.md +252 -0
- package/_bmad/cis/workflows/problem-solving/solving-methods.csv +31 -0
- package/_bmad/cis/workflows/problem-solving/template.md +165 -0
- package/_bmad/cis/workflows/problem-solving/workflow.yaml +27 -0
- package/_bmad/cis/workflows/storytelling/README.md +58 -0
- package/_bmad/cis/workflows/storytelling/instructions.md +293 -0
- package/_bmad/cis/workflows/storytelling/story-types.csv +26 -0
- package/_bmad/cis/workflows/storytelling/template.md +113 -0
- package/_bmad/cis/workflows/storytelling/workflow.yaml +27 -0
- package/_bmad/core/agents/bmad-master.md +57 -0
- package/_bmad/core/config.yaml +9 -0
- package/_bmad/core/resources/excalidraw/README.md +160 -0
- package/_bmad/core/resources/excalidraw/excalidraw-helpers.md +127 -0
- package/_bmad/core/resources/excalidraw/library-loader.md +50 -0
- package/_bmad/core/resources/excalidraw/validate-json-instructions.md +79 -0
- package/_bmad/core/tasks/index-docs.xml +65 -0
- package/_bmad/core/tasks/review-adversarial-general.xml +41 -0
- package/_bmad/core/tasks/shard-doc.xml +109 -0
- package/_bmad/core/tasks/validate-workflow.xml +89 -0
- package/_bmad/core/tasks/workflow.xml +235 -0
- package/_bmad/core/workflows/advanced-elicitation/methods.csv +51 -0
- package/_bmad/core/workflows/advanced-elicitation/workflow.xml +117 -0
- package/_bmad/core/workflows/brainstorming/brain-methods.csv +62 -0
- package/_bmad/core/workflows/brainstorming/steps/step-01-session-setup.md +197 -0
- package/_bmad/core/workflows/brainstorming/steps/step-01b-continue.md +122 -0
- package/_bmad/core/workflows/brainstorming/steps/step-02a-user-selected.md +225 -0
- package/_bmad/core/workflows/brainstorming/steps/step-02b-ai-recommended.md +237 -0
- package/_bmad/core/workflows/brainstorming/steps/step-02c-random-selection.md +209 -0
- package/_bmad/core/workflows/brainstorming/steps/step-02d-progressive-flow.md +264 -0
- package/_bmad/core/workflows/brainstorming/steps/step-03-technique-execution.md +340 -0
- package/_bmad/core/workflows/brainstorming/steps/step-04-idea-organization.md +303 -0
- package/_bmad/core/workflows/brainstorming/template.md +15 -0
- package/_bmad/core/workflows/brainstorming/workflow.md +51 -0
- package/_bmad/core/workflows/party-mode/steps/step-01-agent-loading.md +139 -0
- package/_bmad/core/workflows/party-mode/steps/step-02-discussion-orchestration.md +204 -0
- package/_bmad/core/workflows/party-mode/steps/step-03-graceful-exit.md +159 -0
- package/_bmad/core/workflows/party-mode/workflow.md +206 -0
- package/bin/install.js +507 -508
- package/bin/prepare-publish.js +26 -26
- package/bin/restore-folders.js +26 -26
- package/claude/commands/bmad/bmb/agents/agent-builder.md +14 -0
- package/claude/commands/bmad/bmb/agents/module-builder.md +14 -0
- package/claude/commands/bmad/bmb/agents/workflow-builder.md +14 -0
- package/claude/commands/bmad/bmb/workflows/Meal Prep & Nutrition Plan.md +5 -0
- package/claude/commands/bmad/bmb/workflows/agent.md +5 -0
- package/claude/commands/bmad/bmb/workflows/create-module.md +5 -0
- package/claude/commands/bmad/bmb/workflows/create-workflow.md +5 -0
- package/claude/commands/bmad/bmb/workflows/edit-workflow.md +5 -0
- package/claude/commands/bmad/bmb/workflows/workflow-compliance-check.md +5 -0
- package/claude/commands/bmad/bmm/agents/analyst.md +14 -0
- package/claude/commands/bmad/bmm/agents/architect.md +14 -0
- package/claude/commands/bmad/bmm/agents/dev.md +14 -0
- package/claude/commands/bmad/bmm/agents/pm.md +14 -0
- package/claude/commands/bmad/bmm/agents/quick-flow-solo-dev.md +14 -0
- package/claude/commands/bmad/bmm/agents/sm.md +14 -0
- package/claude/commands/bmad/bmm/agents/tea.md +14 -0
- package/claude/commands/bmad/bmm/agents/tech-writer.md +14 -0
- package/claude/commands/bmad/bmm/agents/ux-designer.md +14 -0
- package/claude/commands/bmad/bmm/workflows/check-implementation-readiness.md +5 -0
- package/claude/commands/bmad/bmm/workflows/code-review.md +13 -0
- package/claude/commands/bmad/bmm/workflows/correct-course.md +13 -0
- package/claude/commands/bmad/bmm/workflows/create-architecture.md +5 -0
- package/claude/commands/bmad/bmm/workflows/create-epics-and-stories.md +5 -0
- package/claude/commands/bmad/bmm/workflows/create-excalidraw-dataflow.md +13 -0
- package/claude/commands/bmad/bmm/workflows/create-excalidraw-diagram.md +13 -0
- package/claude/commands/bmad/bmm/workflows/create-excalidraw-flowchart.md +13 -0
- package/claude/commands/bmad/bmm/workflows/create-excalidraw-wireframe.md +13 -0
- package/claude/commands/bmad/bmm/workflows/create-prd.md +5 -0
- package/claude/commands/bmad/bmm/workflows/create-product-brief.md +5 -0
- package/claude/commands/bmad/bmm/workflows/create-story.md +13 -0
- package/claude/commands/bmad/bmm/workflows/create-tech-spec.md +5 -0
- package/claude/commands/bmad/bmm/workflows/create-ux-design.md +5 -0
- package/claude/commands/bmad/bmm/workflows/dev-story.md +13 -0
- package/claude/commands/bmad/bmm/workflows/document-project.md +13 -0
- package/claude/commands/bmad/bmm/workflows/generate-project-context.md +5 -0
- package/claude/commands/bmad/bmm/workflows/quick-dev.md +5 -0
- package/claude/commands/bmad/bmm/workflows/research.md +5 -0
- package/claude/commands/bmad/bmm/workflows/retrospective.md +13 -0
- package/claude/commands/bmad/bmm/workflows/sprint-planning.md +13 -0
- package/claude/commands/bmad/bmm/workflows/sprint-status.md +13 -0
- package/claude/commands/bmad/bmm/workflows/testarch-atdd.md +13 -0
- package/claude/commands/bmad/bmm/workflows/testarch-automate.md +13 -0
- package/claude/commands/bmad/bmm/workflows/testarch-ci.md +13 -0
- package/claude/commands/bmad/bmm/workflows/testarch-framework.md +13 -0
- package/claude/commands/bmad/bmm/workflows/testarch-nfr.md +13 -0
- package/claude/commands/bmad/bmm/workflows/testarch-test-design.md +13 -0
- package/claude/commands/bmad/bmm/workflows/testarch-test-review.md +13 -0
- package/claude/commands/bmad/bmm/workflows/testarch-trace.md +13 -0
- package/claude/commands/bmad/bmm/workflows/workflow-init.md +13 -0
- package/claude/commands/bmad/bmm/workflows/workflow-status.md +13 -0
- package/claude/commands/bmad/cis/agents/brainstorming-coach.md +14 -0
- package/claude/commands/bmad/cis/agents/creative-problem-solver.md +14 -0
- package/claude/commands/bmad/cis/agents/design-thinking-coach.md +14 -0
- package/claude/commands/bmad/cis/agents/innovation-strategist.md +14 -0
- package/claude/commands/bmad/cis/agents/presentation-master.md +14 -0
- package/claude/commands/bmad/cis/agents/storyteller.md +14 -0
- package/claude/commands/bmad/cis/workflows/design-thinking.md +13 -0
- package/claude/commands/bmad/cis/workflows/innovation-strategy.md +13 -0
- package/claude/commands/bmad/cis/workflows/problem-solving.md +13 -0
- package/claude/commands/bmad/cis/workflows/storytelling.md +13 -0
- package/claude/commands/bmad/core/agents/bmad-master.md +14 -0
- package/claude/commands/bmad/core/tasks/index-docs.md +9 -0
- package/claude/commands/bmad/core/workflows/brainstorming.md +5 -0
- package/claude/commands/bmad/core/workflows/party-mode.md +5 -0
- package/claude/hooks/file-restriction-hook.py +51 -51
- package/claude/hooks/track-agent.py +67 -67
- package/claude/settings.local.json +76 -76
- package/gemini/commands/bmad-agent-bmb-agent-builder.toml +12 -0
- package/gemini/commands/bmad-agent-bmb-module-builder.toml +12 -0
- package/gemini/commands/bmad-agent-bmb-workflow-builder.toml +12 -0
- package/gemini/commands/bmad-agent-bmm-analyst.toml +12 -0
- package/gemini/commands/bmad-agent-bmm-architect.toml +12 -0
- package/gemini/commands/bmad-agent-bmm-dev.toml +12 -0
- package/gemini/commands/bmad-agent-bmm-pm.toml +12 -0
- package/gemini/commands/bmad-agent-bmm-quick-flow-solo-dev.toml +12 -0
- package/gemini/commands/bmad-agent-bmm-sm.toml +12 -0
- package/gemini/commands/bmad-agent-bmm-tea.toml +12 -0
- package/gemini/commands/bmad-agent-bmm-tech-writer.toml +12 -0
- package/gemini/commands/bmad-agent-bmm-ux-designer.toml +12 -0
- package/gemini/commands/bmad-agent-cis-brainstorming-coach.toml +12 -0
- package/gemini/commands/bmad-agent-cis-creative-problem-solver.toml +12 -0
- package/gemini/commands/bmad-agent-cis-design-thinking-coach.toml +12 -0
- package/gemini/commands/bmad-agent-cis-innovation-strategist.toml +12 -0
- package/gemini/commands/bmad-agent-cis-presentation-master.toml +12 -0
- package/gemini/commands/bmad-agent-cis-storyteller.toml +12 -0
- package/gemini/commands/bmad-agent-core-bmad-master.toml +12 -0
- package/gemini/commands/bmad-task-core-index-docs.toml +12 -0
- package/gemini/commands/bmad-task-core-review-adversarial-general.toml +12 -0
- package/gemini/commands/bmad-task-core-shard-doc.toml +12 -0
- package/gemini/commands/bmad-task-core-validate-workflow.toml +12 -0
- package/gemini/commands/bmad-task-core-workflow.toml +12 -0
- package/gemini/commands/bmad-workflow-bmb-Meal Prep & Nutrition Plan.toml +4 -0
- package/gemini/commands/bmad-workflow-bmb-agent.toml +4 -0
- package/gemini/commands/bmad-workflow-bmb-create-module.toml +4 -0
- package/gemini/commands/bmad-workflow-bmb-create-workflow.toml +4 -0
- package/gemini/commands/bmad-workflow-bmb-edit-workflow.toml +4 -0
- package/gemini/commands/bmad-workflow-bmb-workflow-compliance-check.toml +4 -0
- package/gemini/commands/bmad-workflow-bmm-check-implementation-readiness.toml +4 -0
- package/gemini/commands/bmad-workflow-bmm-code-review.toml +12 -0
- package/gemini/commands/bmad-workflow-bmm-correct-course.toml +12 -0
- package/gemini/commands/bmad-workflow-bmm-create-architecture.toml +4 -0
- package/gemini/commands/bmad-workflow-bmm-create-epics-and-stories.toml +4 -0
- package/gemini/commands/bmad-workflow-bmm-create-excalidraw-dataflow.toml +12 -0
- package/gemini/commands/bmad-workflow-bmm-create-excalidraw-diagram.toml +12 -0
- package/gemini/commands/bmad-workflow-bmm-create-excalidraw-flowchart.toml +12 -0
- package/gemini/commands/bmad-workflow-bmm-create-excalidraw-wireframe.toml +12 -0
- package/gemini/commands/bmad-workflow-bmm-create-prd.toml +4 -0
- package/gemini/commands/bmad-workflow-bmm-create-product-brief.toml +4 -0
- package/gemini/commands/bmad-workflow-bmm-create-story.toml +12 -0
- package/gemini/commands/bmad-workflow-bmm-create-tech-spec.toml +4 -0
- package/gemini/commands/bmad-workflow-bmm-create-ux-design.toml +4 -0
- package/gemini/commands/bmad-workflow-bmm-dev-story.toml +12 -0
- package/gemini/commands/bmad-workflow-bmm-document-project.toml +12 -0
- package/gemini/commands/bmad-workflow-bmm-generate-project-context.toml +4 -0
- package/gemini/commands/bmad-workflow-bmm-quick-dev.toml +4 -0
- package/gemini/commands/bmad-workflow-bmm-research.toml +4 -0
- package/gemini/commands/bmad-workflow-bmm-retrospective.toml +12 -0
- package/gemini/commands/bmad-workflow-bmm-sprint-planning.toml +12 -0
- package/gemini/commands/bmad-workflow-bmm-sprint-status.toml +12 -0
- package/gemini/commands/bmad-workflow-bmm-testarch-atdd.toml +12 -0
- package/gemini/commands/bmad-workflow-bmm-testarch-automate.toml +12 -0
- package/gemini/commands/bmad-workflow-bmm-testarch-ci.toml +12 -0
- package/gemini/commands/bmad-workflow-bmm-testarch-framework.toml +12 -0
- package/gemini/commands/bmad-workflow-bmm-testarch-nfr.toml +12 -0
- package/gemini/commands/bmad-workflow-bmm-testarch-test-design.toml +12 -0
- package/gemini/commands/bmad-workflow-bmm-testarch-test-review.toml +12 -0
- package/gemini/commands/bmad-workflow-bmm-testarch-trace.toml +12 -0
- package/gemini/commands/bmad-workflow-bmm-workflow-init.toml +12 -0
- package/gemini/commands/bmad-workflow-bmm-workflow-status.toml +12 -0
- package/gemini/commands/bmad-workflow-cis-design-thinking.toml +12 -0
- package/gemini/commands/bmad-workflow-cis-innovation-strategy.toml +12 -0
- package/gemini/commands/bmad-workflow-cis-problem-solving.toml +12 -0
- package/gemini/commands/bmad-workflow-cis-storytelling.toml +12 -0
- package/gemini/commands/bmad-workflow-core-brainstorming.toml +4 -0
- package/gemini/commands/bmad-workflow-core-party-mode.toml +4 -0
- package/github/CODEOWNERS +7 -0
- package/github/agents/bmd-custom-bmb-agent-builder.agent.md +17 -0
- package/github/agents/bmd-custom-bmb-module-builder.agent.md +17 -0
- package/github/agents/bmd-custom-bmb-workflow-builder.agent.md +17 -0
- package/github/agents/bmd-custom-bmm-analyst.agent.md +17 -0
- package/github/agents/bmd-custom-bmm-architect.agent.md +17 -0
- package/github/agents/bmd-custom-bmm-dev.agent.md +17 -0
- package/github/agents/bmd-custom-bmm-pm.agent.md +17 -0
- package/github/agents/bmd-custom-bmm-quick-flow-solo-dev.agent.md +17 -0
- package/github/agents/bmd-custom-bmm-sm.agent.md +17 -0
- package/github/agents/bmd-custom-bmm-tea.agent.md +17 -0
- package/github/agents/bmd-custom-bmm-tech-writer.agent.md +17 -0
- package/github/agents/bmd-custom-bmm-ux-designer.agent.md +17 -0
- package/github/agents/bmd-custom-cis-brainstorming-coach.agent.md +17 -0
- package/github/agents/bmd-custom-cis-creative-problem-solver.agent.md +17 -0
- package/github/agents/bmd-custom-cis-design-thinking-coach.agent.md +17 -0
- package/github/agents/bmd-custom-cis-innovation-strategist.agent.md +17 -0
- package/github/agents/bmd-custom-cis-presentation-master.agent.md +17 -0
- package/github/agents/bmd-custom-cis-storyteller.agent.md +17 -0
- package/github/agents/bmd-custom-core-bmad-master.agent.md +17 -0
- package/github/workflows/publish.yml +150 -0
- package/index.js +9 -9
- package/mcp.json +14 -14
- package/package.json +40 -41
- package/resources/images/Siesa_Logosimbolo_Azul.svg +24 -24
- package/resources/images/Siesa_Logosimbolo_Blanco.svg +24 -24
- package/resources/images/Siesa_Simbolo_Azul.svg +14 -14
- package/resources/images/Siesa_Simbolo_Blanco.svg +14 -14
- package/vscode/mcp.json +15 -15
- package/vscode/settings.json +12 -12
- package/bmad-core/agent-teams/team-all.yaml +0 -15
- package/bmad-core/agent-teams/team-fullstack.yaml +0 -19
- package/bmad-core/agent-teams/team-ide-minimal.yaml +0 -11
- package/bmad-core/agent-teams/team-no-ui.yaml +0 -14
- package/bmad-core/agents/analyst.md +0 -84
- package/bmad-core/agents/architect.md +0 -116
- package/bmad-core/agents/backend-agent.md +0 -178
- package/bmad-core/agents/bmad-master.md +0 -110
- package/bmad-core/agents/bmad-orchestrator.md +0 -147
- package/bmad-core/agents/dev.md +0 -86
- package/bmad-core/agents/frontend-agent.md +0 -169
- package/bmad-core/agents/pm.md +0 -106
- package/bmad-core/agents/po.md +0 -105
- package/bmad-core/agents/qa.md +0 -91
- package/bmad-core/agents/sm.md +0 -65
- package/bmad-core/agents/ux-expert.md +0 -71
- package/bmad-core/checklists/architect-checklist.md +0 -442
- package/bmad-core/checklists/backend-checklist.md +0 -147
- package/bmad-core/checklists/change-checklist.md +0 -184
- package/bmad-core/checklists/frontend-checklist.md +0 -106
- package/bmad-core/checklists/pm-checklist.md +0 -372
- package/bmad-core/checklists/po-master-checklist.md +0 -434
- package/bmad-core/checklists/story-dod-checklist.md +0 -97
- package/bmad-core/checklists/story-draft-checklist.md +0 -155
- package/bmad-core/core-config.yaml +0 -22
- package/bmad-core/data/bmad-kb.md +0 -809
- package/bmad-core/data/brainstorming-techniques.md +0 -38
- package/bmad-core/data/elicitation-methods.md +0 -156
- package/bmad-core/data/jira-preferences.md +0 -153
- package/bmad-core/data/technical-preferences.md +0 -110
- package/bmad-core/data/test-levels-framework.md +0 -148
- package/bmad-core/data/test-priorities-matrix.md +0 -174
- package/bmad-core/enhanced-ide-development-workflow.md +0 -248
- package/bmad-core/install-manifest.yaml +0 -230
- package/bmad-core/tasks/advanced-elicitation.md +0 -119
- package/bmad-core/tasks/apply-qa-fixes.md +0 -150
- package/bmad-core/tasks/brownfield-create-epic.md +0 -162
- package/bmad-core/tasks/brownfield-create-story.md +0 -149
- package/bmad-core/tasks/correct-course.md +0 -72
- package/bmad-core/tasks/create-brownfield-story.md +0 -314
- package/bmad-core/tasks/create-component.md +0 -103
- package/bmad-core/tasks/create-deep-research-prompt.md +0 -280
- package/bmad-core/tasks/create-doc.md +0 -206
- package/bmad-core/tasks/create-entity.md +0 -133
- package/bmad-core/tasks/create-feature.md +0 -91
- package/bmad-core/tasks/create-next-story.md +0 -145
- package/bmad-core/tasks/create-service.md +0 -118
- package/bmad-core/tasks/create-use-case.md +0 -141
- package/bmad-core/tasks/create-user-guide.md +0 -605
- package/bmad-core/tasks/document-project.md +0 -345
- package/bmad-core/tasks/execute-checklist.md +0 -88
- package/bmad-core/tasks/facilitate-brainstorming-session.md +0 -138
- package/bmad-core/tasks/generate-ai-frontend-prompt.md +0 -53
- package/bmad-core/tasks/index-docs.md +0 -175
- package/bmad-core/tasks/jira-setup.md +0 -396
- package/bmad-core/tasks/kb-mode-interaction.md +0 -77
- package/bmad-core/tasks/nfr-assess.md +0 -345
- package/bmad-core/tasks/qa-gate.md +0 -163
- package/bmad-core/tasks/review-story.md +0 -316
- package/bmad-core/tasks/risk-profile.md +0 -355
- package/bmad-core/tasks/scaffold-backend.md +0 -115
- package/bmad-core/tasks/scaffold-frontend.md +0 -79
- package/bmad-core/tasks/shard-doc.md +0 -187
- package/bmad-core/tasks/sync-epic-files-to-jira.md +0 -632
- package/bmad-core/tasks/sync-prd-to-jira.md +0 -439
- package/bmad-core/tasks/sync-stories-batch-to-jira.md +0 -561
- package/bmad-core/tasks/sync-story-to-jira.md +0 -628
- package/bmad-core/tasks/test-design.md +0 -176
- package/bmad-core/tasks/trace-requirements.md +0 -266
- package/bmad-core/tasks/update-epic-stories-jira-status.md +0 -799
- package/bmad-core/tasks/update-story-jira-status.md +0 -600
- package/bmad-core/tasks/validate-jira-auth.md +0 -251
- package/bmad-core/tasks/validate-next-story.md +0 -136
- package/bmad-core/templates/architecture-tmpl.yaml +0 -662
- package/bmad-core/templates/brainstorming-output-tmpl.yaml +0 -156
- package/bmad-core/templates/brownfield-architecture-tmpl.yaml +0 -477
- package/bmad-core/templates/brownfield-prd-tmpl.yaml +0 -281
- package/bmad-core/templates/competitor-analysis-tmpl.yaml +0 -307
- package/bmad-core/templates/front-end-architecture-tmpl.yaml +0 -258
- package/bmad-core/templates/front-end-spec-tmpl.yaml +0 -350
- package/bmad-core/templates/fullstack-architecture-tmpl.yaml +0 -824
- package/bmad-core/templates/jira-conf-tmpl.md +0 -13
- package/bmad-core/templates/market-research-tmpl.yaml +0 -253
- package/bmad-core/templates/prd-tmpl.yaml +0 -203
- package/bmad-core/templates/project-brief-tmpl.yaml +0 -222
- package/bmad-core/templates/qa-gate-tmpl.yaml +0 -103
- package/bmad-core/templates/story-tmpl.yaml +0 -138
- package/bmad-core/user-guide.md +0 -530
- package/bmad-core/utils/bmad-doc-template.md +0 -327
- package/bmad-core/utils/workflow-management.md +0 -71
- package/bmad-core/workflows/brownfield-fullstack.yaml +0 -298
- package/bmad-core/workflows/brownfield-service.yaml +0 -188
- package/bmad-core/workflows/brownfield-ui.yaml +0 -198
- package/bmad-core/workflows/greenfield-fullstack.yaml +0 -241
- package/bmad-core/workflows/greenfield-service.yaml +0 -207
- package/bmad-core/workflows/greenfield-ui.yaml +0 -236
- package/bmad-core/working-in-the-brownfield.md +0 -606
- package/claude/commands/BMad/agents/analyst.md +0 -88
- package/claude/commands/BMad/agents/architect.md +0 -109
- package/claude/commands/BMad/agents/backend.md +0 -175
- package/claude/commands/BMad/agents/bmad-master.md +0 -114
- package/claude/commands/BMad/agents/bmad-orchestrator.md +0 -151
- package/claude/commands/BMad/agents/dev.md +0 -88
- package/claude/commands/BMad/agents/frontend.md +0 -151
- package/claude/commands/BMad/agents/pm.md +0 -100
- package/claude/commands/BMad/agents/po.md +0 -108
- package/claude/commands/BMad/agents/qa.md +0 -95
- package/claude/commands/BMad/agents/sm.md +0 -69
- package/claude/commands/BMad/agents/ux-expert.md +0 -73
- package/claude/commands/BMad/tasks/advanced-elicitation.md +0 -123
- package/claude/commands/BMad/tasks/apply-qa-fixes.md +0 -154
- package/claude/commands/BMad/tasks/brownfield-create-epic.md +0 -166
- package/claude/commands/BMad/tasks/brownfield-create-story.md +0 -153
- package/claude/commands/BMad/tasks/correct-course.md +0 -76
- package/claude/commands/BMad/tasks/create-brownfield-story.md +0 -318
- package/claude/commands/BMad/tasks/create-deep-research-prompt.md +0 -284
- package/claude/commands/BMad/tasks/create-doc.md +0 -195
- package/claude/commands/BMad/tasks/create-next-story.md +0 -118
- package/claude/commands/BMad/tasks/document-project.md +0 -349
- package/claude/commands/BMad/tasks/execute-checklist.md +0 -92
- package/claude/commands/BMad/tasks/facilitate-brainstorming-session.md +0 -142
- package/claude/commands/BMad/tasks/generate-ai-frontend-prompt.md +0 -57
- package/claude/commands/BMad/tasks/index-docs.md +0 -179
- package/claude/commands/BMad/tasks/kb-mode-interaction.md +0 -81
- package/claude/commands/BMad/tasks/nfr-assess.md +0 -349
- package/claude/commands/BMad/tasks/qa-gate.md +0 -167
- package/claude/commands/BMad/tasks/review-story.md +0 -320
- package/claude/commands/BMad/tasks/risk-profile.md +0 -359
- package/claude/commands/BMad/tasks/shard-doc.md +0 -191
- package/claude/commands/BMad/tasks/test-design.md +0 -180
- package/claude/commands/BMad/tasks/trace-requirements.md +0 -270
- package/claude/commands/BMad/tasks/validate-next-story.md +0 -140
- package/gemini/commands/BMad/agents/analyst.toml +0 -6
- package/gemini/commands/BMad/agents/architect.toml +0 -6
- package/gemini/commands/BMad/agents/bmad-master.toml +0 -6
- package/gemini/commands/BMad/agents/bmad-orchestrator.toml +0 -6
- package/gemini/commands/BMad/agents/dev.toml +0 -6
- package/gemini/commands/BMad/agents/pm.toml +0 -6
- package/gemini/commands/BMad/agents/po.toml +0 -6
- package/gemini/commands/BMad/agents/qa.toml +0 -6
- package/gemini/commands/BMad/agents/sm.toml +0 -6
- package/gemini/commands/BMad/agents/ux-expert.toml +0 -6
- package/gemini/commands/BMad/tasks/advanced-elicitation.toml +0 -6
- package/gemini/commands/BMad/tasks/apply-qa-fixes.toml +0 -6
- package/gemini/commands/BMad/tasks/brownfield-create-epic.toml +0 -6
- package/gemini/commands/BMad/tasks/brownfield-create-story.toml +0 -6
- package/gemini/commands/BMad/tasks/correct-course.toml +0 -6
- package/gemini/commands/BMad/tasks/create-brownfield-story.toml +0 -6
- package/gemini/commands/BMad/tasks/create-deep-research-prompt.toml +0 -6
- package/gemini/commands/BMad/tasks/create-doc.toml +0 -6
- package/gemini/commands/BMad/tasks/create-next-story.toml +0 -6
- package/gemini/commands/BMad/tasks/document-project.toml +0 -6
- package/gemini/commands/BMad/tasks/execute-checklist.toml +0 -6
- package/gemini/commands/BMad/tasks/facilitate-brainstorming-session.toml +0 -6
- package/gemini/commands/BMad/tasks/generate-ai-frontend-prompt.toml +0 -6
- package/gemini/commands/BMad/tasks/index-docs.toml +0 -6
- package/gemini/commands/BMad/tasks/kb-mode-interaction.toml +0 -6
- package/gemini/commands/BMad/tasks/nfr-assess.toml +0 -6
- package/gemini/commands/BMad/tasks/qa-gate.toml +0 -6
- package/gemini/commands/BMad/tasks/review-story.toml +0 -6
- package/gemini/commands/BMad/tasks/risk-profile.toml +0 -6
- package/gemini/commands/BMad/tasks/shard-doc.toml +0 -6
- package/gemini/commands/BMad/tasks/test-design.toml +0 -6
- package/gemini/commands/BMad/tasks/trace-requirements.toml +0 -6
- package/gemini/commands/BMad/tasks/validate-next-story.toml +0 -6
- package/github/b-mad-expert.md +0 -742
- package/github/chatmodes/analyst.chatmode.md +0 -89
- package/github/chatmodes/architect.chatmode.md +0 -103
- package/github/chatmodes/backend.chatmode.md +0 -219
- package/github/chatmodes/bmad-master.chatmode.md +0 -115
- package/github/chatmodes/bmad-orchestrator.chatmode.md +0 -152
- package/github/chatmodes/dev.chatmode.md +0 -97
- package/github/chatmodes/frontend.chatmode.md +0 -158
- package/github/chatmodes/pm.chatmode.md +0 -98
- package/github/chatmodes/po.chatmode.md +0 -100
- package/github/chatmodes/qa.chatmode.md +0 -102
- package/github/chatmodes/sm.chatmode.md +0 -70
- package/github/chatmodes/ux-expert.chatmode.md +0 -74
- package/kiro/README.md +0 -13
- package/kiro/steering/agent-detection.md +0 -46
|
@@ -1,812 +1,812 @@
|
|
|
1
|
-
# Backend Development Standards
|
|
2
|
-
|
|
3
|
-
> **Note**: For architecture patterns and principles (Clean Architecture, DDD, folder structure), see [architecture-patterns.md](./architecture-patterns.md)
|
|
4
|
-
|
|
5
|
-
## Technology Stack Standards
|
|
6
|
-
|
|
7
|
-
### Core Technologies
|
|
8
|
-
- **.NET 10**: Main framework with C#
|
|
9
|
-
- **C# Minimal API**: Lightweight and modern approach for APIs
|
|
10
|
-
- **Entity Framework Core 10**: ORM for database operations
|
|
11
|
-
- **xUnit**: Unit and integration testing framework
|
|
12
|
-
- **FluentValidation**: Validation for DTOs and models
|
|
13
|
-
|
|
14
|
-
### Framework Standards
|
|
15
|
-
- **Default Framework**: .NET 10 with C# Minimal API
|
|
16
|
-
- **Database**: PostgreSQL with Entity Framework Core 10 (mandatory)
|
|
17
|
-
- **ORM Extensions**:
|
|
18
|
-
- **linq2db**: For highly optimized complex queries requiring maximum performance
|
|
19
|
-
- **DynamicLinq**: For runtime dynamic filters from user input or configurable scenarios
|
|
20
|
-
- **LinqKit**: For composable type-safe predicates in DDD repositories with complex business rules
|
|
21
|
-
- **Testing**: TDD approach with xUnit and high test coverage
|
|
22
|
-
- **Documentation**: Scalar (NO Swagger) auto-generated
|
|
23
|
-
- **Primary Keys**: UUID (Guid) for all entities
|
|
24
|
-
|
|
25
|
-
## CRITICAL: DateTime Type Standards
|
|
26
|
-
|
|
27
|
-
### ⚠️ Mandatory Rule: Use DateTimeOffset for Timestamps
|
|
28
|
-
|
|
29
|
-
**ALWAYS use `DateTimeOffset` instead of `DateTime`** for all timestamp fields in entities, DTOs, and database models.
|
|
30
|
-
|
|
31
|
-
#### Why DateTimeOffset?
|
|
32
|
-
|
|
33
|
-
- **PostgreSQL Compatibility**: PostgreSQL uses `TIMESTAMP WITH TIME ZONE` which maps correctly to `DateTimeOffset` but NOT to `DateTime`
|
|
34
|
-
- **Timezone Awareness**: `DateTimeOffset` preserves timezone information, preventing timezone conversion issues
|
|
35
|
-
- **UTC Consistency**: Store all timestamps in UTC for consistency across different timezones
|
|
36
|
-
- **Avoiding Bugs**: Using `DateTime` with PostgreSQL causes:
|
|
37
|
-
- Data loss during timezone conversions
|
|
38
|
-
- Inconsistent timestamp comparisons
|
|
39
|
-
- Ambiguous datetime values during DST transitions
|
|
40
|
-
|
|
41
|
-
#### Correct Usage
|
|
42
|
-
|
|
43
|
-
```csharp
|
|
44
|
-
// ✅ CORRECT - Use DateTimeOffset for timestamps
|
|
45
|
-
public class UserEntity : AggregateRoot
|
|
46
|
-
{
|
|
47
|
-
public Guid Id { get; private set; }
|
|
48
|
-
public string Email { get; private set; }
|
|
49
|
-
public DateTimeOffset CreatedAt { get; private set; } = DateTimeOffset.UtcNow;
|
|
50
|
-
public DateTimeOffset UpdatedAt { get; private set; } = DateTimeOffset.UtcNow;
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
// ✅ CORRECT - Use DateOnly for date-only fields
|
|
54
|
-
public class ExpenseEntity : AggregateRoot
|
|
55
|
-
{
|
|
56
|
-
public Guid Id { get; private set; }
|
|
57
|
-
public decimal Amount { get; private set; }
|
|
58
|
-
public DateOnly ExpenseDate { get; private set; } // Date without time
|
|
59
|
-
public DateTimeOffset CreatedAt { get; private set; } = DateTimeOffset.UtcNow;
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
// ❌ WRONG - Never use DateTime for timestamp fields
|
|
63
|
-
public class UserEntity : AggregateRoot
|
|
64
|
-
{
|
|
65
|
-
public DateTime CreatedAt { get; private set; } // ❌ WRONG - causes PostgreSQL issues
|
|
66
|
-
public DateTime UpdatedAt { get; private set; } // ❌ WRONG - causes PostgreSQL issues
|
|
67
|
-
}
|
|
68
|
-
```
|
|
69
|
-
|
|
70
|
-
#### Entity Framework Core Configuration
|
|
71
|
-
|
|
72
|
-
```csharp
|
|
73
|
-
public class UserEntityConfiguration : IEntityTypeConfiguration<UserEntity>
|
|
74
|
-
{
|
|
75
|
-
public void Configure(EntityTypeBuilder<UserEntity> builder)
|
|
76
|
-
{
|
|
77
|
-
builder.ToTable("users");
|
|
78
|
-
|
|
79
|
-
// DateTimeOffset fields - automatically map to TIMESTAMP WITH TIME ZONE
|
|
80
|
-
builder.Property(u => u.CreatedAt)
|
|
81
|
-
.IsRequired()
|
|
82
|
-
.HasDefaultValueSql("NOW()"); // PostgreSQL auto-generates UTC timestamp
|
|
83
|
-
|
|
84
|
-
builder.Property(u => u.UpdatedAt)
|
|
85
|
-
.IsRequired()
|
|
86
|
-
.HasDefaultValueSql("NOW()");
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
```
|
|
90
|
-
|
|
91
|
-
#### Type Selection Guide
|
|
92
|
-
|
|
93
|
-
| Use Case | Type | Example |
|
|
94
|
-
|----------|------|---------|
|
|
95
|
-
| Timestamp with timezone | `DateTimeOffset` | `CreatedAt`, `UpdatedAt`, `DeletedAt`, `LastLoginAt` |
|
|
96
|
-
| Date only (no time) | `DateOnly` | `BirthDate`, `ExpenseDate`, `DueDate` |
|
|
97
|
-
| Time only (no date) | `TimeOnly` | `OpeningTime`, `ClosingTime` |
|
|
98
|
-
| Never use | `DateTime` | ❌ Not compatible with PostgreSQL TIMESTAMP WITH TIME ZONE |
|
|
99
|
-
|
|
100
|
-
#### Best Practices
|
|
101
|
-
|
|
102
|
-
1. **Always use `DateTimeOffset`** for `CreatedAt`, `UpdatedAt`, and any timestamp field
|
|
103
|
-
2. **Always use UTC**: Set values with `DateTimeOffset.UtcNow`
|
|
104
|
-
3. **Configure EF Core**: Use `HasDefaultValueSql("NOW()")` for automatic timestamps
|
|
105
|
-
4. **DTOs must match**: DTOs also use `DateTimeOffset` (serializes to ISO 8601 with timezone)
|
|
106
|
-
5. **Never use `DateTime`** for entity timestamp fields in PostgreSQL projects
|
|
107
|
-
|
|
108
|
-
### ORM Strategy Guidelines
|
|
109
|
-
|
|
110
|
-
#### When to Use Entity Framework Core 10
|
|
111
|
-
✔ **Primary ORM for:**
|
|
112
|
-
- Standard CRUD operations
|
|
113
|
-
- DDD entities with tracking (Aggregate Roots, Domain Events)
|
|
114
|
-
- Simple to moderate queries
|
|
115
|
-
- Navigation properties and relationships
|
|
116
|
-
- Change tracking scenarios
|
|
117
|
-
|
|
118
|
-
#### When to Use linq2db
|
|
119
|
-
✔ **Use for maximum performance:**
|
|
120
|
-
- Highly optimized complex queries (subqueries, complex projections, multiple joins)
|
|
121
|
-
- Queries that EF Core cannot translate efficiently
|
|
122
|
-
- High-performance endpoints: dashboards, analytics, reports, high data volumes
|
|
123
|
-
- Pure queries without tracking needs
|
|
124
|
-
- When EF Core generates inefficient SQL
|
|
125
|
-
|
|
126
|
-
🚫 **Avoid when:**
|
|
127
|
-
- Need complete DDD entities with aggregate lifecycle
|
|
128
|
-
- Query participates in domain events or tracking
|
|
129
|
-
|
|
130
|
-
#### When to Use DynamicLinq
|
|
131
|
-
✔ **Use for runtime flexibility:**
|
|
132
|
-
- Dynamic filter systems: `?filter=Age > 30 AND Country == "CO"`
|
|
133
|
-
- Simplified OData-style APIs
|
|
134
|
-
- Configurable grids or data explorers
|
|
135
|
-
- Variable column searches
|
|
136
|
-
|
|
137
|
-
🚫 **Avoid when:**
|
|
138
|
-
- Filters are fully controlled in code
|
|
139
|
-
- Require strict security (needs sanitization)
|
|
140
|
-
- Need maximum performance (use linq2db or LinqKit)
|
|
141
|
-
|
|
142
|
-
#### When to Use LinqKit
|
|
143
|
-
✔ **Use for type-safe composition:**
|
|
144
|
-
- Complex business rules in queries with composable predicates
|
|
145
|
-
- Dynamic filters with type safety (no strings → expressions)
|
|
146
|
-
- DDD repositories with multiple criteria, conditional searches, reusable queries
|
|
147
|
-
- Enhance EF Core when composing complex expressions
|
|
148
|
-
- 100% compatible with EF Core translation
|
|
149
|
-
|
|
150
|
-
🚫 **Avoid when:**
|
|
151
|
-
- Filters are extremely simple
|
|
152
|
-
- Query is highly dynamic and text-based (use DynamicLinq)
|
|
153
|
-
- Need maximum raw performance (use linq2db)
|
|
154
|
-
|
|
155
|
-
### Development Tools
|
|
156
|
-
- **dotnet CLI**: Project management, build, test, and restore
|
|
157
|
-
- **NuGet**: Package manager
|
|
158
|
-
- **EF Core 10 Migrations**: Database schema version control
|
|
159
|
-
- **Docker**: For production only
|
|
160
|
-
- **Code Analyzers**: Static C# code analysis
|
|
161
|
-
- **EditorConfig**: Code formatting standards
|
|
162
|
-
|
|
163
|
-
## Domain-Driven Design Implementation
|
|
164
|
-
|
|
165
|
-
### Entity Structure
|
|
166
|
-
```csharp
|
|
167
|
-
public class UserEntity : AggregateRoot
|
|
168
|
-
{
|
|
169
|
-
// Parameterless constructor for EF Core (materialization from DB)
|
|
170
|
-
private UserEntity() : base(Guid.Empty)
|
|
171
|
-
{
|
|
172
|
-
// EF Core needs this to reconstruct the entity from the database
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
private UserEntity(
|
|
176
|
-
Guid id,
|
|
177
|
-
EmailValueObject email,
|
|
178
|
-
NameValueObject name) : base(id)
|
|
179
|
-
{
|
|
180
|
-
Email = email;
|
|
181
|
-
Name = name;
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
public EmailValueObject Email { get; private set; } = null!;
|
|
185
|
-
public NameValueObject Name { get; private set; } = null!;
|
|
186
|
-
|
|
187
|
-
public static UserEntity Create(string email, string name)
|
|
188
|
-
{
|
|
189
|
-
var userEmail = EmailValueObject.Create(email);
|
|
190
|
-
var userName = NameValueObject.Create(name);
|
|
191
|
-
|
|
192
|
-
var user = new UserEntity(Guid.NewGuid(), userEmail, userName);
|
|
193
|
-
user.AddDomainEvent(new UserCreatedEvent(user.Id));
|
|
194
|
-
|
|
195
|
-
return user;
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
public void UpdateEmail(string newEmail)
|
|
199
|
-
{
|
|
200
|
-
var newEmailVO = EmailValueObject.Create(newEmail);
|
|
201
|
-
if (Email.Equals(newEmailVO)) return;
|
|
202
|
-
|
|
203
|
-
Email = newEmailVO;
|
|
204
|
-
AddDomainEvent(new UserEmailUpdatedEvent(Id, Email));
|
|
205
|
-
}
|
|
206
|
-
}
|
|
207
|
-
```
|
|
208
|
-
|
|
209
|
-
### Value Object Structure
|
|
210
|
-
```csharp
|
|
211
|
-
public class EmailValueObject : ValueObject
|
|
212
|
-
{
|
|
213
|
-
public string Value { get; }
|
|
214
|
-
|
|
215
|
-
private EmailValueObject(string value)
|
|
216
|
-
{
|
|
217
|
-
Value = value;
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
public static EmailValueObject Create(string value)
|
|
221
|
-
{
|
|
222
|
-
if (string.IsNullOrWhiteSpace(value))
|
|
223
|
-
throw new ArgumentException("Email cannot be empty", nameof(value));
|
|
224
|
-
|
|
225
|
-
if (!IsValidEmail(value))
|
|
226
|
-
throw new InvalidEmailException(value);
|
|
227
|
-
|
|
228
|
-
return new EmailValueObject(value);
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
private static bool IsValidEmail(string email)
|
|
232
|
-
{
|
|
233
|
-
// Email validation logic
|
|
234
|
-
return System.Text.RegularExpressions.Regex.IsMatch(
|
|
235
|
-
email,
|
|
236
|
-
@"^[^@\s]+@[^@\s]+\.[^@\s]+$");
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
protected override IEnumerable<object> GetEqualityComponents()
|
|
240
|
-
{
|
|
241
|
-
yield return Value;
|
|
242
|
-
}
|
|
243
|
-
}
|
|
244
|
-
```
|
|
245
|
-
|
|
246
|
-
### Repository Pattern
|
|
247
|
-
```csharp
|
|
248
|
-
// Interface (in Application layer)
|
|
249
|
-
public interface IUserRepository
|
|
250
|
-
{
|
|
251
|
-
Task<UserEntity> SaveAsync(UserEntity user, CancellationToken cancellationToken = default);
|
|
252
|
-
Task<UserEntity?> FindByIdAsync(Guid id, CancellationToken cancellationToken = default);
|
|
253
|
-
Task<UserEntity?> FindByEmailAsync(EmailValueObject email, CancellationToken cancellationToken = default);
|
|
254
|
-
Task DeleteAsync(Guid id, CancellationToken cancellationToken = default);
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
// Implementation (in Infrastructure layer)
|
|
258
|
-
public class UserRepository : IUserRepository
|
|
259
|
-
{
|
|
260
|
-
private readonly ApplicationDbContext _context;
|
|
261
|
-
|
|
262
|
-
public UserRepository(ApplicationDbContext context)
|
|
263
|
-
{
|
|
264
|
-
_context = context;
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
public async Task<UserEntity> SaveAsync(UserEntity user, CancellationToken cancellationToken = default)
|
|
268
|
-
{
|
|
269
|
-
var existingUser = await _context.Users.FindAsync(new object[] { user.Id }, cancellationToken);
|
|
270
|
-
|
|
271
|
-
if (existingUser == null)
|
|
272
|
-
await _context.Users.AddAsync(user, cancellationToken);
|
|
273
|
-
else
|
|
274
|
-
_context.Users.Update(user);
|
|
275
|
-
|
|
276
|
-
await _context.SaveChangesAsync(cancellationToken);
|
|
277
|
-
return user;
|
|
278
|
-
}
|
|
279
|
-
|
|
280
|
-
public async Task<UserEntity?> FindByIdAsync(Guid id, CancellationToken cancellationToken = default)
|
|
281
|
-
{
|
|
282
|
-
return await _context.Users.FindAsync(new object[] { id }, cancellationToken);
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
public async Task<UserEntity?> FindByEmailAsync(EmailValueObject email, CancellationToken cancellationToken = default)
|
|
286
|
-
{
|
|
287
|
-
return await _context.Users
|
|
288
|
-
.FirstOrDefaultAsync(u => u.Email.Value == email.Value, cancellationToken);
|
|
289
|
-
}
|
|
290
|
-
|
|
291
|
-
public async Task DeleteAsync(Guid id, CancellationToken cancellationToken = default)
|
|
292
|
-
{
|
|
293
|
-
var user = await FindByIdAsync(id, cancellationToken);
|
|
294
|
-
if (user != null)
|
|
295
|
-
{
|
|
296
|
-
_context.Users.Remove(user);
|
|
297
|
-
await _context.SaveChangesAsync(cancellationToken);
|
|
298
|
-
}
|
|
299
|
-
}
|
|
300
|
-
}
|
|
301
|
-
```
|
|
302
|
-
|
|
303
|
-
## Command/Query (CQRS) Standards
|
|
304
|
-
|
|
305
|
-
### Command Structure
|
|
306
|
-
```csharp
|
|
307
|
-
public record CreateUserCommand(string Email, string Name);
|
|
308
|
-
|
|
309
|
-
public class CreateUserCommandValidator : AbstractValidator<CreateUserCommand>
|
|
310
|
-
{
|
|
311
|
-
public CreateUserCommandValidator()
|
|
312
|
-
{
|
|
313
|
-
RuleFor(x => x.Email)
|
|
314
|
-
.NotEmpty().WithMessage("Email is required")
|
|
315
|
-
.EmailAddress().WithMessage("Email must be valid");
|
|
316
|
-
|
|
317
|
-
RuleFor(x => x.Name)
|
|
318
|
-
.NotEmpty().WithMessage("Name is required")
|
|
319
|
-
.MaximumLength(100).WithMessage("Name must not exceed 100 characters");
|
|
320
|
-
}
|
|
321
|
-
}
|
|
322
|
-
|
|
323
|
-
public class CreateUserCommandHandler
|
|
324
|
-
{
|
|
325
|
-
private readonly IUserRepository _userRepository;
|
|
326
|
-
private readonly IValidator<CreateUserCommand> _validator;
|
|
327
|
-
|
|
328
|
-
public CreateUserCommandHandler(
|
|
329
|
-
IUserRepository userRepository,
|
|
330
|
-
IValidator<CreateUserCommand> validator)
|
|
331
|
-
{
|
|
332
|
-
_userRepository = userRepository;
|
|
333
|
-
_validator = validator;
|
|
334
|
-
}
|
|
335
|
-
|
|
336
|
-
public async Task<UserResponseDto> HandleAsync(
|
|
337
|
-
CreateUserCommand command,
|
|
338
|
-
CancellationToken cancellationToken = default)
|
|
339
|
-
{
|
|
340
|
-
// 1. Validate command
|
|
341
|
-
await _validator.ValidateAndThrowAsync(command, cancellationToken);
|
|
342
|
-
|
|
343
|
-
// 2. Validate business rules
|
|
344
|
-
await ValidateUserDoesNotExistAsync(command.Email, cancellationToken);
|
|
345
|
-
|
|
346
|
-
// 3. Create domain entity
|
|
347
|
-
var user = UserEntity.Create(command.Email, command.Name);
|
|
348
|
-
|
|
349
|
-
// 4. Persist entity
|
|
350
|
-
var savedUser = await _userRepository.SaveAsync(user, cancellationToken);
|
|
351
|
-
|
|
352
|
-
// 5. Return response DTO
|
|
353
|
-
return UserResponseDto.FromEntity(savedUser);
|
|
354
|
-
}
|
|
355
|
-
|
|
356
|
-
private async Task ValidateUserDoesNotExistAsync(string email, CancellationToken cancellationToken)
|
|
357
|
-
{
|
|
358
|
-
var emailVO = EmailValueObject.Create(email);
|
|
359
|
-
var existingUser = await _userRepository.FindByEmailAsync(emailVO, cancellationToken);
|
|
360
|
-
if (existingUser != null)
|
|
361
|
-
throw new UserAlreadyExistsException(email);
|
|
362
|
-
}
|
|
363
|
-
}
|
|
364
|
-
```
|
|
365
|
-
|
|
366
|
-
### Query Structure
|
|
367
|
-
```csharp
|
|
368
|
-
public record GetUserByIdQuery(Guid Id);
|
|
369
|
-
|
|
370
|
-
public class GetUserByIdQueryHandler
|
|
371
|
-
{
|
|
372
|
-
private readonly IUserRepository _userRepository;
|
|
373
|
-
|
|
374
|
-
public GetUserByIdQueryHandler(IUserRepository userRepository)
|
|
375
|
-
{
|
|
376
|
-
_userRepository = userRepository;
|
|
377
|
-
}
|
|
378
|
-
|
|
379
|
-
public async Task<UserResponseDto?> HandleAsync(
|
|
380
|
-
GetUserByIdQuery query,
|
|
381
|
-
CancellationToken cancellationToken = default)
|
|
382
|
-
{
|
|
383
|
-
var user = await _userRepository.FindByIdAsync(query.Id, cancellationToken);
|
|
384
|
-
return user != null ? UserResponseDto.FromEntity(user) : null;
|
|
385
|
-
}
|
|
386
|
-
}
|
|
387
|
-
```
|
|
388
|
-
|
|
389
|
-
### DTO Structure
|
|
390
|
-
```csharp
|
|
391
|
-
public record UserResponseDto
|
|
392
|
-
{
|
|
393
|
-
public Guid Id { get; init; }
|
|
394
|
-
public string Email { get; init; }
|
|
395
|
-
public string Name { get; init; }
|
|
396
|
-
|
|
397
|
-
public static UserResponseDto FromEntity(UserEntity user)
|
|
398
|
-
{
|
|
399
|
-
return new UserResponseDto
|
|
400
|
-
{
|
|
401
|
-
Id = user.Id,
|
|
402
|
-
Email = user.Email.Value,
|
|
403
|
-
Name = user.Name.Value
|
|
404
|
-
};
|
|
405
|
-
}
|
|
406
|
-
}
|
|
407
|
-
|
|
408
|
-
```
|
|
409
|
-
|
|
410
|
-
## Minimal API Standards
|
|
411
|
-
|
|
412
|
-
### Endpoint Structure
|
|
413
|
-
```csharp
|
|
414
|
-
// Program.cs or separate endpoint configuration
|
|
415
|
-
public static class UserEndpoints
|
|
416
|
-
{
|
|
417
|
-
public static void MapUserEndpoints(this IEndpointRouteBuilder app)
|
|
418
|
-
{
|
|
419
|
-
var group = app.MapGroup("/api/users")
|
|
420
|
-
.WithTags("Users")
|
|
421
|
-
.WithOpenApi();
|
|
422
|
-
|
|
423
|
-
group.MapPost("/", CreateUser)
|
|
424
|
-
.WithName("CreateUser")
|
|
425
|
-
.WithSummary("Create a new user")
|
|
426
|
-
.Produces<UserResponseDto>(StatusCodes.Status201Created)
|
|
427
|
-
.ProducesValidationProblem();
|
|
428
|
-
|
|
429
|
-
group.MapGet("/{id:guid}", GetUserById)
|
|
430
|
-
.WithName("GetUserById")
|
|
431
|
-
.WithSummary("Get user by ID")
|
|
432
|
-
.Produces<UserResponseDto>()
|
|
433
|
-
.Produces(StatusCodes.Status404NotFound);
|
|
434
|
-
}
|
|
435
|
-
|
|
436
|
-
private static async Task<IResult> CreateUser(
|
|
437
|
-
CreateUserCommand command,
|
|
438
|
-
CreateUserCommandHandler handler,
|
|
439
|
-
CancellationToken cancellationToken)
|
|
440
|
-
{
|
|
441
|
-
try
|
|
442
|
-
{
|
|
443
|
-
var result = await handler.HandleAsync(command, cancellationToken);
|
|
444
|
-
return Results.Created($"/api/users/{result.Id}", result);
|
|
445
|
-
}
|
|
446
|
-
catch (ValidationException ex)
|
|
447
|
-
{
|
|
448
|
-
return Results.ValidationProblem(ex.Errors.ToDictionary(
|
|
449
|
-
e => e.PropertyName,
|
|
450
|
-
e => new[] { e.ErrorMessage }));
|
|
451
|
-
}
|
|
452
|
-
catch (UserAlreadyExistsException ex)
|
|
453
|
-
{
|
|
454
|
-
return Results.Conflict(new { message = ex.Message });
|
|
455
|
-
}
|
|
456
|
-
}
|
|
457
|
-
|
|
458
|
-
private static async Task<IResult> GetUserById(
|
|
459
|
-
Guid id,
|
|
460
|
-
GetUserByIdQueryHandler handler,
|
|
461
|
-
CancellationToken cancellationToken)
|
|
462
|
-
{
|
|
463
|
-
var result = await handler.HandleAsync(new GetUserByIdQuery(id), cancellationToken);
|
|
464
|
-
return result != null ? Results.Ok(result) : Results.NotFound();
|
|
465
|
-
}
|
|
466
|
-
}
|
|
467
|
-
```
|
|
468
|
-
|
|
469
|
-
## Testing Standards
|
|
470
|
-
|
|
471
|
-
### Testing Strategy
|
|
472
|
-
- **Unit Tests**: Domain entities, value objects, command/query handlers (isolated logic)
|
|
473
|
-
- **Integration Tests**: Repository implementations, database operations with EF Core InMemory or Test Containers
|
|
474
|
-
- **TDD Approach**: Write tests before or alongside implementation
|
|
475
|
-
- **High Coverage**: Aim for >80% code coverage
|
|
476
|
-
|
|
477
|
-
### Test Structure (xUnit)
|
|
478
|
-
```csharp
|
|
479
|
-
public class CreateUserCommandHandlerTests
|
|
480
|
-
{
|
|
481
|
-
private readonly Mock<IUserRepository> _userRepositoryMock;
|
|
482
|
-
private readonly Mock<IValidator<CreateUserCommand>> _validatorMock;
|
|
483
|
-
private readonly CreateUserCommandHandler _handler;
|
|
484
|
-
|
|
485
|
-
public CreateUserCommandHandlerTests()
|
|
486
|
-
{
|
|
487
|
-
_userRepositoryMock = new Mock<IUserRepository>();
|
|
488
|
-
_validatorMock = new Mock<IValidator<CreateUserCommand>>();
|
|
489
|
-
_handler = new CreateUserCommandHandler(
|
|
490
|
-
_userRepositoryMock.Object,
|
|
491
|
-
_validatorMock.Object);
|
|
492
|
-
}
|
|
493
|
-
|
|
494
|
-
[Fact]
|
|
495
|
-
public async Task HandleAsync_WithValidCommand_ShouldCreateUser()
|
|
496
|
-
{
|
|
497
|
-
// Arrange
|
|
498
|
-
var command = new CreateUserCommand("test@example.com", "John Doe");
|
|
499
|
-
_validatorMock
|
|
500
|
-
.Setup(v => v.ValidateAndThrowAsync(command, It.IsAny<CancellationToken>()))
|
|
501
|
-
.Returns(Task.CompletedTask);
|
|
502
|
-
_userRepositoryMock
|
|
503
|
-
.Setup(r => r.FindByEmailAsync(It.IsAny<EmailValueObject>(), It.IsAny<CancellationToken>()))
|
|
504
|
-
.ReturnsAsync((UserEntity?)null);
|
|
505
|
-
_userRepositoryMock
|
|
506
|
-
.Setup(r => r.SaveAsync(It.IsAny<UserEntity>(), It.IsAny<CancellationToken>()))
|
|
507
|
-
.ReturnsAsync((UserEntity u, CancellationToken _) => u);
|
|
508
|
-
|
|
509
|
-
// Act
|
|
510
|
-
var result = await _handler.HandleAsync(command, CancellationToken.None);
|
|
511
|
-
|
|
512
|
-
// Assert
|
|
513
|
-
Assert.NotNull(result);
|
|
514
|
-
Assert.Equal(command.Email, result.Email);
|
|
515
|
-
Assert.Equal(command.Name, result.Name);
|
|
516
|
-
_userRepositoryMock.Verify(r => r.SaveAsync(It.IsAny<UserEntity>(), It.IsAny<CancellationToken>()), Times.Once);
|
|
517
|
-
}
|
|
518
|
-
|
|
519
|
-
[Fact]
|
|
520
|
-
public async Task HandleAsync_WithExistingUser_ShouldThrowException()
|
|
521
|
-
{
|
|
522
|
-
// Arrange
|
|
523
|
-
var command = new CreateUserCommand("test@example.com", "John Doe");
|
|
524
|
-
var existingUser = UserEntity.Create("test@example.com", "Existing User");
|
|
525
|
-
_validatorMock
|
|
526
|
-
.Setup(v => v.ValidateAndThrowAsync(command, It.IsAny<CancellationToken>()))
|
|
527
|
-
.Returns(Task.CompletedTask);
|
|
528
|
-
_userRepositoryMock
|
|
529
|
-
.Setup(r => r.FindByEmailAsync(It.IsAny<EmailValueObject>(), It.IsAny<CancellationToken>()))
|
|
530
|
-
.ReturnsAsync(existingUser);
|
|
531
|
-
|
|
532
|
-
// Act & Assert
|
|
533
|
-
await Assert.ThrowsAsync<UserAlreadyExistsException>(() =>
|
|
534
|
-
_handler.HandleAsync(command, CancellationToken.None));
|
|
535
|
-
}
|
|
536
|
-
}
|
|
537
|
-
```
|
|
538
|
-
|
|
539
|
-
### Integration Test with EF Core InMemory
|
|
540
|
-
```csharp
|
|
541
|
-
public class UserRepositoryIntegrationTests : IDisposable
|
|
542
|
-
{
|
|
543
|
-
private readonly ApplicationDbContext _context;
|
|
544
|
-
private readonly UserRepository _repository;
|
|
545
|
-
|
|
546
|
-
public UserRepositoryIntegrationTests()
|
|
547
|
-
{
|
|
548
|
-
var options = new DbContextOptionsBuilder<ApplicationDbContext>()
|
|
549
|
-
.UseInMemoryDatabase(databaseName: Guid.NewGuid().ToString())
|
|
550
|
-
.Options;
|
|
551
|
-
|
|
552
|
-
_context = new ApplicationDbContext(options);
|
|
553
|
-
_repository = new UserRepository(_context);
|
|
554
|
-
}
|
|
555
|
-
|
|
556
|
-
[Fact]
|
|
557
|
-
public async Task SaveAsync_ShouldPersistUser()
|
|
558
|
-
{
|
|
559
|
-
// Arrange
|
|
560
|
-
var user = UserEntity.Create("test@example.com", "John Doe");
|
|
561
|
-
|
|
562
|
-
// Act
|
|
563
|
-
var savedUser = await _repository.SaveAsync(user);
|
|
564
|
-
|
|
565
|
-
// Assert
|
|
566
|
-
var retrievedUser = await _repository.FindByIdAsync(savedUser.Id);
|
|
567
|
-
Assert.NotNull(retrievedUser);
|
|
568
|
-
Assert.Equal(user.Email.Value, retrievedUser.Email.Value);
|
|
569
|
-
}
|
|
570
|
-
|
|
571
|
-
public void Dispose()
|
|
572
|
-
{
|
|
573
|
-
_context.Database.EnsureDeleted();
|
|
574
|
-
_context.Dispose();
|
|
575
|
-
}
|
|
576
|
-
}
|
|
577
|
-
```
|
|
578
|
-
|
|
579
|
-
## EF Core Configuration Standards
|
|
580
|
-
|
|
581
|
-
### DbContext Configuration
|
|
582
|
-
```csharp
|
|
583
|
-
public class ApplicationDbContext : DbContext
|
|
584
|
-
{
|
|
585
|
-
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
|
|
586
|
-
: base(options)
|
|
587
|
-
{
|
|
588
|
-
}
|
|
589
|
-
|
|
590
|
-
public DbSet<UserEntity> Users { get; set; }
|
|
591
|
-
public DbSet<QuoteEntity> Quotes { get; set; }
|
|
592
|
-
|
|
593
|
-
protected override void OnModelCreating(ModelBuilder modelBuilder)
|
|
594
|
-
{
|
|
595
|
-
base.OnModelCreating(modelBuilder);
|
|
596
|
-
|
|
597
|
-
// Apply all configurations from assembly
|
|
598
|
-
modelBuilder.ApplyConfigurationsFromAssembly(typeof(ApplicationDbContext).Assembly);
|
|
599
|
-
}
|
|
600
|
-
}
|
|
601
|
-
```
|
|
602
|
-
|
|
603
|
-
### Entity Configuration
|
|
604
|
-
```csharp
|
|
605
|
-
public class UserEntityConfiguration : IEntityTypeConfiguration<UserEntity>
|
|
606
|
-
{
|
|
607
|
-
public void Configure(EntityTypeBuilder<UserEntity> builder)
|
|
608
|
-
{
|
|
609
|
-
builder.ToTable("Users");
|
|
610
|
-
|
|
611
|
-
builder.HasKey(u => u.Id);
|
|
612
|
-
|
|
613
|
-
builder.Property(u => u.Id)
|
|
614
|
-
.HasColumnType("uuid")
|
|
615
|
-
.IsRequired();
|
|
616
|
-
|
|
617
|
-
// Configure Value Object as owned entity
|
|
618
|
-
builder.OwnsOne(u => u.Email, email =>
|
|
619
|
-
{
|
|
620
|
-
email.Property(e => e.Value)
|
|
621
|
-
.HasColumnName("Email")
|
|
622
|
-
.HasMaxLength(256)
|
|
623
|
-
.IsRequired();
|
|
624
|
-
});
|
|
625
|
-
|
|
626
|
-
builder.OwnsOne(u => u.Name, name =>
|
|
627
|
-
{
|
|
628
|
-
name.Property(n => n.Value)
|
|
629
|
-
.HasColumnName("Name")
|
|
630
|
-
.HasMaxLength(100)
|
|
631
|
-
.IsRequired();
|
|
632
|
-
});
|
|
633
|
-
|
|
634
|
-
// Ignore domain events (not persisted)
|
|
635
|
-
builder.Ignore(u => u.DomainEvents);
|
|
636
|
-
}
|
|
637
|
-
}
|
|
638
|
-
```
|
|
639
|
-
|
|
640
|
-
## Security Standards
|
|
641
|
-
|
|
642
|
-
### Authentication & Authorization
|
|
643
|
-
- **JWT Tokens**: Proper expiration and refresh token handling
|
|
644
|
-
- **RBAC**: Role-based access control
|
|
645
|
-
- **Validation**: Input validation on all endpoints (FluentValidation)
|
|
646
|
-
- **Rate Limiting**: Throttle public endpoints to prevent abuse
|
|
647
|
-
- **Environment**: HTTPS only in production, secrets in env variables
|
|
648
|
-
|
|
649
|
-
### Data Protection
|
|
650
|
-
- Encrypt sensitive data at rest and in transit
|
|
651
|
-
- Never commit secrets to repository
|
|
652
|
-
- Implement audit logging for critical operations
|
|
653
|
-
- OWASP Top 10 compliance
|
|
654
|
-
- Regular dependency security audits with `dotnet list package --vulnerable`
|
|
655
|
-
|
|
656
|
-
## Performance Standards
|
|
657
|
-
|
|
658
|
-
### Database Optimization
|
|
659
|
-
- Proper indexing on frequently queried fields
|
|
660
|
-
- Connection pooling via EF Core 10
|
|
661
|
-
- Pagination for large datasets (Skip/Take or cursor-based)
|
|
662
|
-
- Avoid N+1 queries with `.Include()` and `.ThenInclude()`
|
|
663
|
-
- Query monitoring and slow query logging
|
|
664
|
-
- Use `AsNoTracking()` for read-only queries
|
|
665
|
-
|
|
666
|
-
### Caching Strategy
|
|
667
|
-
- **Distributed Cache**: Redis for session data and frequently accessed data
|
|
668
|
-
- **Memory Cache**: In-memory caching for configuration
|
|
669
|
-
- **TTL Strategy**: Appropriate time-to-live for different data types
|
|
670
|
-
- **Invalidation**: Event-driven cache invalidation
|
|
671
|
-
|
|
672
|
-
## Error Handling
|
|
673
|
-
|
|
674
|
-
### Exception Hierarchy
|
|
675
|
-
- Domain exceptions for business rule violations
|
|
676
|
-
- Application exceptions for use case errors
|
|
677
|
-
- Infrastructure exceptions for external service failures
|
|
678
|
-
- Global exception middleware for API responses
|
|
679
|
-
|
|
680
|
-
### Error Response Format (Problem Details RFC 7807)
|
|
681
|
-
```csharp
|
|
682
|
-
public record ProblemDetailsResponse
|
|
683
|
-
{
|
|
684
|
-
public int Status { get; init; }
|
|
685
|
-
public string Title { get; init; }
|
|
686
|
-
public string Detail { get; init; }
|
|
687
|
-
public string Instance { get; init; }
|
|
688
|
-
public DateTimeOffset Timestamp { get; init; }
|
|
689
|
-
public Dictionary<string, string[]>? Errors { get; init; }
|
|
690
|
-
}
|
|
691
|
-
|
|
692
|
-
// Example response
|
|
693
|
-
{
|
|
694
|
-
"status": 400,
|
|
695
|
-
"title": "Validation Error",
|
|
696
|
-
"detail": "One or more validation errors occurred",
|
|
697
|
-
"instance": "/api/users",
|
|
698
|
-
"timestamp": "2025-11-26T10:30:00Z",
|
|
699
|
-
"errors": {
|
|
700
|
-
"Email": ["Email is required", "Email must be valid"],
|
|
701
|
-
"Name": ["Name is required"]
|
|
702
|
-
}
|
|
703
|
-
}
|
|
704
|
-
```
|
|
705
|
-
|
|
706
|
-
### Global Exception Middleware
|
|
707
|
-
```csharp
|
|
708
|
-
public class ExceptionHandlingMiddleware
|
|
709
|
-
{
|
|
710
|
-
private readonly RequestDelegate _next;
|
|
711
|
-
private readonly ILogger<ExceptionHandlingMiddleware> _logger;
|
|
712
|
-
|
|
713
|
-
public ExceptionHandlingMiddleware(
|
|
714
|
-
RequestDelegate next,
|
|
715
|
-
ILogger<ExceptionHandlingMiddleware> logger)
|
|
716
|
-
{
|
|
717
|
-
_next = next;
|
|
718
|
-
_logger = logger;
|
|
719
|
-
}
|
|
720
|
-
|
|
721
|
-
public async Task InvokeAsync(HttpContext context)
|
|
722
|
-
{
|
|
723
|
-
try
|
|
724
|
-
{
|
|
725
|
-
await _next(context);
|
|
726
|
-
}
|
|
727
|
-
catch (ValidationException ex)
|
|
728
|
-
{
|
|
729
|
-
await HandleValidationExceptionAsync(context, ex);
|
|
730
|
-
}
|
|
731
|
-
catch (DomainException ex)
|
|
732
|
-
{
|
|
733
|
-
await HandleDomainExceptionAsync(context, ex);
|
|
734
|
-
}
|
|
735
|
-
catch (Exception ex)
|
|
736
|
-
{
|
|
737
|
-
_logger.LogError(ex, "Unhandled exception occurred");
|
|
738
|
-
await HandleUnhandledExceptionAsync(context, ex);
|
|
739
|
-
}
|
|
740
|
-
}
|
|
741
|
-
|
|
742
|
-
private static Task HandleValidationExceptionAsync(HttpContext context, ValidationException exception)
|
|
743
|
-
{
|
|
744
|
-
context.Response.StatusCode = StatusCodes.Status400BadRequest;
|
|
745
|
-
context.Response.ContentType = "application/problem+json";
|
|
746
|
-
|
|
747
|
-
var problemDetails = new ProblemDetailsResponse
|
|
748
|
-
{
|
|
749
|
-
Status = StatusCodes.Status400BadRequest,
|
|
750
|
-
Title = "Validation Error",
|
|
751
|
-
Detail = "One or more validation errors occurred",
|
|
752
|
-
Instance = context.Request.Path,
|
|
753
|
-
Timestamp = DateTimeOffset.UtcNow,
|
|
754
|
-
Errors = exception.Errors.GroupBy(e => e.PropertyName)
|
|
755
|
-
.ToDictionary(g => g.Key, g => g.Select(e => e.ErrorMessage).ToArray())
|
|
756
|
-
};
|
|
757
|
-
|
|
758
|
-
return context.Response.WriteAsJsonAsync(problemDetails);
|
|
759
|
-
}
|
|
760
|
-
}
|
|
761
|
-
```
|
|
762
|
-
|
|
763
|
-
## Security Standards
|
|
764
|
-
|
|
765
|
-
### Authentication & Authorization
|
|
766
|
-
- **JWT Tokens**: Proper expiration and refresh token handling
|
|
767
|
-
- **RBAC**: Role-based access control with Authorization Policies
|
|
768
|
-
- **Validation**: Input validation on all endpoints (FluentValidation)
|
|
769
|
-
- **Rate Limiting**: ASP.NET Core Rate Limiting middleware
|
|
770
|
-
- **Environment**: HTTPS only in production, secrets in Azure Key Vault or User Secrets
|
|
771
|
-
|
|
772
|
-
### Data Protection
|
|
773
|
-
- Encrypt sensitive data at rest and in transit
|
|
774
|
-
- Never commit secrets to repository
|
|
775
|
-
- Implement audit logging for critical operations
|
|
776
|
-
- OWASP Top 10 compliance
|
|
777
|
-
- Regular dependency security audits with `dotnet list package --vulnerable`
|
|
778
|
-
|
|
779
|
-
## Performance Standards
|
|
780
|
-
|
|
781
|
-
### Database Optimization
|
|
782
|
-
- Proper indexing on frequently queried fields
|
|
783
|
-
- Connection pooling via EF Core (configured in connection string)
|
|
784
|
-
- Pagination for large datasets (Skip/Take or Keyset pagination)
|
|
785
|
-
- Avoid N+1 queries with `.Include()` and `.ThenInclude()`
|
|
786
|
-
- Query monitoring and slow query logging
|
|
787
|
-
- Use `AsNoTracking()` for read-only queries
|
|
788
|
-
|
|
789
|
-
### Caching Strategy
|
|
790
|
-
- **Distributed Cache**: Redis or SQL Server for distributed scenarios
|
|
791
|
-
- **Memory Cache**: `IMemoryCache` for single-instance caching
|
|
792
|
-
- **TTL Strategy**: Appropriate time-to-live for different data types
|
|
793
|
-
- **Invalidation**: Event-driven cache invalidation
|
|
794
|
-
- **Response Caching**: Use `[ResponseCache]` attribute for GET endpoints
|
|
795
|
-
|
|
796
|
-
## Additional Standards
|
|
797
|
-
|
|
798
|
-
### Logging
|
|
799
|
-
- Use `ILogger<T>` for structured logging
|
|
800
|
-
- Log levels: Trace, Debug, Information, Warning, Error, Critical
|
|
801
|
-
- Include correlation IDs for request tracing
|
|
802
|
-
- Never log sensitive information (passwords, tokens, PII)
|
|
803
|
-
|
|
804
|
-
### API Versioning
|
|
805
|
-
- Use URL versioning: `/api/v1/users`
|
|
806
|
-
- Support multiple versions simultaneously during migration
|
|
807
|
-
- Deprecation notices in response headers
|
|
808
|
-
|
|
809
|
-
### Health Checks
|
|
810
|
-
- Implement `/health` endpoint for liveness
|
|
811
|
-
- Implement `/health/ready` for readiness checks
|
|
1
|
+
# Backend Development Standards
|
|
2
|
+
|
|
3
|
+
> **Note**: For architecture patterns and principles (Clean Architecture, DDD, folder structure), see [architecture-patterns.md](./architecture-patterns.md)
|
|
4
|
+
|
|
5
|
+
## Technology Stack Standards
|
|
6
|
+
|
|
7
|
+
### Core Technologies
|
|
8
|
+
- **.NET 10**: Main framework with C#
|
|
9
|
+
- **C# Minimal API**: Lightweight and modern approach for APIs
|
|
10
|
+
- **Entity Framework Core 10**: ORM for database operations
|
|
11
|
+
- **xUnit**: Unit and integration testing framework
|
|
12
|
+
- **FluentValidation**: Validation for DTOs and models
|
|
13
|
+
|
|
14
|
+
### Framework Standards
|
|
15
|
+
- **Default Framework**: .NET 10 with C# Minimal API
|
|
16
|
+
- **Database**: PostgreSQL with Entity Framework Core 10 (mandatory)
|
|
17
|
+
- **ORM Extensions**:
|
|
18
|
+
- **linq2db**: For highly optimized complex queries requiring maximum performance
|
|
19
|
+
- **DynamicLinq**: For runtime dynamic filters from user input or configurable scenarios
|
|
20
|
+
- **LinqKit**: For composable type-safe predicates in DDD repositories with complex business rules
|
|
21
|
+
- **Testing**: TDD approach with xUnit and high test coverage
|
|
22
|
+
- **Documentation**: Scalar (NO Swagger) auto-generated
|
|
23
|
+
- **Primary Keys**: UUID (Guid) for all entities
|
|
24
|
+
|
|
25
|
+
## CRITICAL: DateTime Type Standards
|
|
26
|
+
|
|
27
|
+
### ⚠️ Mandatory Rule: Use DateTimeOffset for Timestamps
|
|
28
|
+
|
|
29
|
+
**ALWAYS use `DateTimeOffset` instead of `DateTime`** for all timestamp fields in entities, DTOs, and database models.
|
|
30
|
+
|
|
31
|
+
#### Why DateTimeOffset?
|
|
32
|
+
|
|
33
|
+
- **PostgreSQL Compatibility**: PostgreSQL uses `TIMESTAMP WITH TIME ZONE` which maps correctly to `DateTimeOffset` but NOT to `DateTime`
|
|
34
|
+
- **Timezone Awareness**: `DateTimeOffset` preserves timezone information, preventing timezone conversion issues
|
|
35
|
+
- **UTC Consistency**: Store all timestamps in UTC for consistency across different timezones
|
|
36
|
+
- **Avoiding Bugs**: Using `DateTime` with PostgreSQL causes:
|
|
37
|
+
- Data loss during timezone conversions
|
|
38
|
+
- Inconsistent timestamp comparisons
|
|
39
|
+
- Ambiguous datetime values during DST transitions
|
|
40
|
+
|
|
41
|
+
#### Correct Usage
|
|
42
|
+
|
|
43
|
+
```csharp
|
|
44
|
+
// ✅ CORRECT - Use DateTimeOffset for timestamps
|
|
45
|
+
public class UserEntity : AggregateRoot
|
|
46
|
+
{
|
|
47
|
+
public Guid Id { get; private set; }
|
|
48
|
+
public string Email { get; private set; }
|
|
49
|
+
public DateTimeOffset CreatedAt { get; private set; } = DateTimeOffset.UtcNow;
|
|
50
|
+
public DateTimeOffset UpdatedAt { get; private set; } = DateTimeOffset.UtcNow;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// ✅ CORRECT - Use DateOnly for date-only fields
|
|
54
|
+
public class ExpenseEntity : AggregateRoot
|
|
55
|
+
{
|
|
56
|
+
public Guid Id { get; private set; }
|
|
57
|
+
public decimal Amount { get; private set; }
|
|
58
|
+
public DateOnly ExpenseDate { get; private set; } // Date without time
|
|
59
|
+
public DateTimeOffset CreatedAt { get; private set; } = DateTimeOffset.UtcNow;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// ❌ WRONG - Never use DateTime for timestamp fields
|
|
63
|
+
public class UserEntity : AggregateRoot
|
|
64
|
+
{
|
|
65
|
+
public DateTime CreatedAt { get; private set; } // ❌ WRONG - causes PostgreSQL issues
|
|
66
|
+
public DateTime UpdatedAt { get; private set; } // ❌ WRONG - causes PostgreSQL issues
|
|
67
|
+
}
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
#### Entity Framework Core Configuration
|
|
71
|
+
|
|
72
|
+
```csharp
|
|
73
|
+
public class UserEntityConfiguration : IEntityTypeConfiguration<UserEntity>
|
|
74
|
+
{
|
|
75
|
+
public void Configure(EntityTypeBuilder<UserEntity> builder)
|
|
76
|
+
{
|
|
77
|
+
builder.ToTable("users");
|
|
78
|
+
|
|
79
|
+
// DateTimeOffset fields - automatically map to TIMESTAMP WITH TIME ZONE
|
|
80
|
+
builder.Property(u => u.CreatedAt)
|
|
81
|
+
.IsRequired()
|
|
82
|
+
.HasDefaultValueSql("NOW()"); // PostgreSQL auto-generates UTC timestamp
|
|
83
|
+
|
|
84
|
+
builder.Property(u => u.UpdatedAt)
|
|
85
|
+
.IsRequired()
|
|
86
|
+
.HasDefaultValueSql("NOW()");
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
#### Type Selection Guide
|
|
92
|
+
|
|
93
|
+
| Use Case | Type | Example |
|
|
94
|
+
|----------|------|---------|
|
|
95
|
+
| Timestamp with timezone | `DateTimeOffset` | `CreatedAt`, `UpdatedAt`, `DeletedAt`, `LastLoginAt` |
|
|
96
|
+
| Date only (no time) | `DateOnly` | `BirthDate`, `ExpenseDate`, `DueDate` |
|
|
97
|
+
| Time only (no date) | `TimeOnly` | `OpeningTime`, `ClosingTime` |
|
|
98
|
+
| Never use | `DateTime` | ❌ Not compatible with PostgreSQL TIMESTAMP WITH TIME ZONE |
|
|
99
|
+
|
|
100
|
+
#### Best Practices
|
|
101
|
+
|
|
102
|
+
1. **Always use `DateTimeOffset`** for `CreatedAt`, `UpdatedAt`, and any timestamp field
|
|
103
|
+
2. **Always use UTC**: Set values with `DateTimeOffset.UtcNow`
|
|
104
|
+
3. **Configure EF Core**: Use `HasDefaultValueSql("NOW()")` for automatic timestamps
|
|
105
|
+
4. **DTOs must match**: DTOs also use `DateTimeOffset` (serializes to ISO 8601 with timezone)
|
|
106
|
+
5. **Never use `DateTime`** for entity timestamp fields in PostgreSQL projects
|
|
107
|
+
|
|
108
|
+
### ORM Strategy Guidelines
|
|
109
|
+
|
|
110
|
+
#### When to Use Entity Framework Core 10
|
|
111
|
+
✔ **Primary ORM for:**
|
|
112
|
+
- Standard CRUD operations
|
|
113
|
+
- DDD entities with tracking (Aggregate Roots, Domain Events)
|
|
114
|
+
- Simple to moderate queries
|
|
115
|
+
- Navigation properties and relationships
|
|
116
|
+
- Change tracking scenarios
|
|
117
|
+
|
|
118
|
+
#### When to Use linq2db
|
|
119
|
+
✔ **Use for maximum performance:**
|
|
120
|
+
- Highly optimized complex queries (subqueries, complex projections, multiple joins)
|
|
121
|
+
- Queries that EF Core cannot translate efficiently
|
|
122
|
+
- High-performance endpoints: dashboards, analytics, reports, high data volumes
|
|
123
|
+
- Pure queries without tracking needs
|
|
124
|
+
- When EF Core generates inefficient SQL
|
|
125
|
+
|
|
126
|
+
🚫 **Avoid when:**
|
|
127
|
+
- Need complete DDD entities with aggregate lifecycle
|
|
128
|
+
- Query participates in domain events or tracking
|
|
129
|
+
|
|
130
|
+
#### When to Use DynamicLinq
|
|
131
|
+
✔ **Use for runtime flexibility:**
|
|
132
|
+
- Dynamic filter systems: `?filter=Age > 30 AND Country == "CO"`
|
|
133
|
+
- Simplified OData-style APIs
|
|
134
|
+
- Configurable grids or data explorers
|
|
135
|
+
- Variable column searches
|
|
136
|
+
|
|
137
|
+
🚫 **Avoid when:**
|
|
138
|
+
- Filters are fully controlled in code
|
|
139
|
+
- Require strict security (needs sanitization)
|
|
140
|
+
- Need maximum performance (use linq2db or LinqKit)
|
|
141
|
+
|
|
142
|
+
#### When to Use LinqKit
|
|
143
|
+
✔ **Use for type-safe composition:**
|
|
144
|
+
- Complex business rules in queries with composable predicates
|
|
145
|
+
- Dynamic filters with type safety (no strings → expressions)
|
|
146
|
+
- DDD repositories with multiple criteria, conditional searches, reusable queries
|
|
147
|
+
- Enhance EF Core when composing complex expressions
|
|
148
|
+
- 100% compatible with EF Core translation
|
|
149
|
+
|
|
150
|
+
🚫 **Avoid when:**
|
|
151
|
+
- Filters are extremely simple
|
|
152
|
+
- Query is highly dynamic and text-based (use DynamicLinq)
|
|
153
|
+
- Need maximum raw performance (use linq2db)
|
|
154
|
+
|
|
155
|
+
### Development Tools
|
|
156
|
+
- **dotnet CLI**: Project management, build, test, and restore
|
|
157
|
+
- **NuGet**: Package manager
|
|
158
|
+
- **EF Core 10 Migrations**: Database schema version control
|
|
159
|
+
- **Docker**: For production only
|
|
160
|
+
- **Code Analyzers**: Static C# code analysis
|
|
161
|
+
- **EditorConfig**: Code formatting standards
|
|
162
|
+
|
|
163
|
+
## Domain-Driven Design Implementation
|
|
164
|
+
|
|
165
|
+
### Entity Structure
|
|
166
|
+
```csharp
|
|
167
|
+
public class UserEntity : AggregateRoot
|
|
168
|
+
{
|
|
169
|
+
// Parameterless constructor for EF Core (materialization from DB)
|
|
170
|
+
private UserEntity() : base(Guid.Empty)
|
|
171
|
+
{
|
|
172
|
+
// EF Core needs this to reconstruct the entity from the database
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
private UserEntity(
|
|
176
|
+
Guid id,
|
|
177
|
+
EmailValueObject email,
|
|
178
|
+
NameValueObject name) : base(id)
|
|
179
|
+
{
|
|
180
|
+
Email = email;
|
|
181
|
+
Name = name;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
public EmailValueObject Email { get; private set; } = null!;
|
|
185
|
+
public NameValueObject Name { get; private set; } = null!;
|
|
186
|
+
|
|
187
|
+
public static UserEntity Create(string email, string name)
|
|
188
|
+
{
|
|
189
|
+
var userEmail = EmailValueObject.Create(email);
|
|
190
|
+
var userName = NameValueObject.Create(name);
|
|
191
|
+
|
|
192
|
+
var user = new UserEntity(Guid.NewGuid(), userEmail, userName);
|
|
193
|
+
user.AddDomainEvent(new UserCreatedEvent(user.Id));
|
|
194
|
+
|
|
195
|
+
return user;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
public void UpdateEmail(string newEmail)
|
|
199
|
+
{
|
|
200
|
+
var newEmailVO = EmailValueObject.Create(newEmail);
|
|
201
|
+
if (Email.Equals(newEmailVO)) return;
|
|
202
|
+
|
|
203
|
+
Email = newEmailVO;
|
|
204
|
+
AddDomainEvent(new UserEmailUpdatedEvent(Id, Email));
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
### Value Object Structure
|
|
210
|
+
```csharp
|
|
211
|
+
public class EmailValueObject : ValueObject
|
|
212
|
+
{
|
|
213
|
+
public string Value { get; }
|
|
214
|
+
|
|
215
|
+
private EmailValueObject(string value)
|
|
216
|
+
{
|
|
217
|
+
Value = value;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
public static EmailValueObject Create(string value)
|
|
221
|
+
{
|
|
222
|
+
if (string.IsNullOrWhiteSpace(value))
|
|
223
|
+
throw new ArgumentException("Email cannot be empty", nameof(value));
|
|
224
|
+
|
|
225
|
+
if (!IsValidEmail(value))
|
|
226
|
+
throw new InvalidEmailException(value);
|
|
227
|
+
|
|
228
|
+
return new EmailValueObject(value);
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
private static bool IsValidEmail(string email)
|
|
232
|
+
{
|
|
233
|
+
// Email validation logic
|
|
234
|
+
return System.Text.RegularExpressions.Regex.IsMatch(
|
|
235
|
+
email,
|
|
236
|
+
@"^[^@\s]+@[^@\s]+\.[^@\s]+$");
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
protected override IEnumerable<object> GetEqualityComponents()
|
|
240
|
+
{
|
|
241
|
+
yield return Value;
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
### Repository Pattern
|
|
247
|
+
```csharp
|
|
248
|
+
// Interface (in Application layer)
|
|
249
|
+
public interface IUserRepository
|
|
250
|
+
{
|
|
251
|
+
Task<UserEntity> SaveAsync(UserEntity user, CancellationToken cancellationToken = default);
|
|
252
|
+
Task<UserEntity?> FindByIdAsync(Guid id, CancellationToken cancellationToken = default);
|
|
253
|
+
Task<UserEntity?> FindByEmailAsync(EmailValueObject email, CancellationToken cancellationToken = default);
|
|
254
|
+
Task DeleteAsync(Guid id, CancellationToken cancellationToken = default);
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
// Implementation (in Infrastructure layer)
|
|
258
|
+
public class UserRepository : IUserRepository
|
|
259
|
+
{
|
|
260
|
+
private readonly ApplicationDbContext _context;
|
|
261
|
+
|
|
262
|
+
public UserRepository(ApplicationDbContext context)
|
|
263
|
+
{
|
|
264
|
+
_context = context;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
public async Task<UserEntity> SaveAsync(UserEntity user, CancellationToken cancellationToken = default)
|
|
268
|
+
{
|
|
269
|
+
var existingUser = await _context.Users.FindAsync(new object[] { user.Id }, cancellationToken);
|
|
270
|
+
|
|
271
|
+
if (existingUser == null)
|
|
272
|
+
await _context.Users.AddAsync(user, cancellationToken);
|
|
273
|
+
else
|
|
274
|
+
_context.Users.Update(user);
|
|
275
|
+
|
|
276
|
+
await _context.SaveChangesAsync(cancellationToken);
|
|
277
|
+
return user;
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
public async Task<UserEntity?> FindByIdAsync(Guid id, CancellationToken cancellationToken = default)
|
|
281
|
+
{
|
|
282
|
+
return await _context.Users.FindAsync(new object[] { id }, cancellationToken);
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
public async Task<UserEntity?> FindByEmailAsync(EmailValueObject email, CancellationToken cancellationToken = default)
|
|
286
|
+
{
|
|
287
|
+
return await _context.Users
|
|
288
|
+
.FirstOrDefaultAsync(u => u.Email.Value == email.Value, cancellationToken);
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
public async Task DeleteAsync(Guid id, CancellationToken cancellationToken = default)
|
|
292
|
+
{
|
|
293
|
+
var user = await FindByIdAsync(id, cancellationToken);
|
|
294
|
+
if (user != null)
|
|
295
|
+
{
|
|
296
|
+
_context.Users.Remove(user);
|
|
297
|
+
await _context.SaveChangesAsync(cancellationToken);
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
```
|
|
302
|
+
|
|
303
|
+
## Command/Query (CQRS) Standards
|
|
304
|
+
|
|
305
|
+
### Command Structure
|
|
306
|
+
```csharp
|
|
307
|
+
public record CreateUserCommand(string Email, string Name);
|
|
308
|
+
|
|
309
|
+
public class CreateUserCommandValidator : AbstractValidator<CreateUserCommand>
|
|
310
|
+
{
|
|
311
|
+
public CreateUserCommandValidator()
|
|
312
|
+
{
|
|
313
|
+
RuleFor(x => x.Email)
|
|
314
|
+
.NotEmpty().WithMessage("Email is required")
|
|
315
|
+
.EmailAddress().WithMessage("Email must be valid");
|
|
316
|
+
|
|
317
|
+
RuleFor(x => x.Name)
|
|
318
|
+
.NotEmpty().WithMessage("Name is required")
|
|
319
|
+
.MaximumLength(100).WithMessage("Name must not exceed 100 characters");
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
public class CreateUserCommandHandler
|
|
324
|
+
{
|
|
325
|
+
private readonly IUserRepository _userRepository;
|
|
326
|
+
private readonly IValidator<CreateUserCommand> _validator;
|
|
327
|
+
|
|
328
|
+
public CreateUserCommandHandler(
|
|
329
|
+
IUserRepository userRepository,
|
|
330
|
+
IValidator<CreateUserCommand> validator)
|
|
331
|
+
{
|
|
332
|
+
_userRepository = userRepository;
|
|
333
|
+
_validator = validator;
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
public async Task<UserResponseDto> HandleAsync(
|
|
337
|
+
CreateUserCommand command,
|
|
338
|
+
CancellationToken cancellationToken = default)
|
|
339
|
+
{
|
|
340
|
+
// 1. Validate command
|
|
341
|
+
await _validator.ValidateAndThrowAsync(command, cancellationToken);
|
|
342
|
+
|
|
343
|
+
// 2. Validate business rules
|
|
344
|
+
await ValidateUserDoesNotExistAsync(command.Email, cancellationToken);
|
|
345
|
+
|
|
346
|
+
// 3. Create domain entity
|
|
347
|
+
var user = UserEntity.Create(command.Email, command.Name);
|
|
348
|
+
|
|
349
|
+
// 4. Persist entity
|
|
350
|
+
var savedUser = await _userRepository.SaveAsync(user, cancellationToken);
|
|
351
|
+
|
|
352
|
+
// 5. Return response DTO
|
|
353
|
+
return UserResponseDto.FromEntity(savedUser);
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
private async Task ValidateUserDoesNotExistAsync(string email, CancellationToken cancellationToken)
|
|
357
|
+
{
|
|
358
|
+
var emailVO = EmailValueObject.Create(email);
|
|
359
|
+
var existingUser = await _userRepository.FindByEmailAsync(emailVO, cancellationToken);
|
|
360
|
+
if (existingUser != null)
|
|
361
|
+
throw new UserAlreadyExistsException(email);
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
```
|
|
365
|
+
|
|
366
|
+
### Query Structure
|
|
367
|
+
```csharp
|
|
368
|
+
public record GetUserByIdQuery(Guid Id);
|
|
369
|
+
|
|
370
|
+
public class GetUserByIdQueryHandler
|
|
371
|
+
{
|
|
372
|
+
private readonly IUserRepository _userRepository;
|
|
373
|
+
|
|
374
|
+
public GetUserByIdQueryHandler(IUserRepository userRepository)
|
|
375
|
+
{
|
|
376
|
+
_userRepository = userRepository;
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
public async Task<UserResponseDto?> HandleAsync(
|
|
380
|
+
GetUserByIdQuery query,
|
|
381
|
+
CancellationToken cancellationToken = default)
|
|
382
|
+
{
|
|
383
|
+
var user = await _userRepository.FindByIdAsync(query.Id, cancellationToken);
|
|
384
|
+
return user != null ? UserResponseDto.FromEntity(user) : null;
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
```
|
|
388
|
+
|
|
389
|
+
### DTO Structure
|
|
390
|
+
```csharp
|
|
391
|
+
public record UserResponseDto
|
|
392
|
+
{
|
|
393
|
+
public Guid Id { get; init; }
|
|
394
|
+
public string Email { get; init; }
|
|
395
|
+
public string Name { get; init; }
|
|
396
|
+
|
|
397
|
+
public static UserResponseDto FromEntity(UserEntity user)
|
|
398
|
+
{
|
|
399
|
+
return new UserResponseDto
|
|
400
|
+
{
|
|
401
|
+
Id = user.Id,
|
|
402
|
+
Email = user.Email.Value,
|
|
403
|
+
Name = user.Name.Value
|
|
404
|
+
};
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
```
|
|
409
|
+
|
|
410
|
+
## Minimal API Standards
|
|
411
|
+
|
|
412
|
+
### Endpoint Structure
|
|
413
|
+
```csharp
|
|
414
|
+
// Program.cs or separate endpoint configuration
|
|
415
|
+
public static class UserEndpoints
|
|
416
|
+
{
|
|
417
|
+
public static void MapUserEndpoints(this IEndpointRouteBuilder app)
|
|
418
|
+
{
|
|
419
|
+
var group = app.MapGroup("/api/users")
|
|
420
|
+
.WithTags("Users")
|
|
421
|
+
.WithOpenApi();
|
|
422
|
+
|
|
423
|
+
group.MapPost("/", CreateUser)
|
|
424
|
+
.WithName("CreateUser")
|
|
425
|
+
.WithSummary("Create a new user")
|
|
426
|
+
.Produces<UserResponseDto>(StatusCodes.Status201Created)
|
|
427
|
+
.ProducesValidationProblem();
|
|
428
|
+
|
|
429
|
+
group.MapGet("/{id:guid}", GetUserById)
|
|
430
|
+
.WithName("GetUserById")
|
|
431
|
+
.WithSummary("Get user by ID")
|
|
432
|
+
.Produces<UserResponseDto>()
|
|
433
|
+
.Produces(StatusCodes.Status404NotFound);
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
private static async Task<IResult> CreateUser(
|
|
437
|
+
CreateUserCommand command,
|
|
438
|
+
CreateUserCommandHandler handler,
|
|
439
|
+
CancellationToken cancellationToken)
|
|
440
|
+
{
|
|
441
|
+
try
|
|
442
|
+
{
|
|
443
|
+
var result = await handler.HandleAsync(command, cancellationToken);
|
|
444
|
+
return Results.Created($"/api/users/{result.Id}", result);
|
|
445
|
+
}
|
|
446
|
+
catch (ValidationException ex)
|
|
447
|
+
{
|
|
448
|
+
return Results.ValidationProblem(ex.Errors.ToDictionary(
|
|
449
|
+
e => e.PropertyName,
|
|
450
|
+
e => new[] { e.ErrorMessage }));
|
|
451
|
+
}
|
|
452
|
+
catch (UserAlreadyExistsException ex)
|
|
453
|
+
{
|
|
454
|
+
return Results.Conflict(new { message = ex.Message });
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
private static async Task<IResult> GetUserById(
|
|
459
|
+
Guid id,
|
|
460
|
+
GetUserByIdQueryHandler handler,
|
|
461
|
+
CancellationToken cancellationToken)
|
|
462
|
+
{
|
|
463
|
+
var result = await handler.HandleAsync(new GetUserByIdQuery(id), cancellationToken);
|
|
464
|
+
return result != null ? Results.Ok(result) : Results.NotFound();
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
```
|
|
468
|
+
|
|
469
|
+
## Testing Standards
|
|
470
|
+
|
|
471
|
+
### Testing Strategy
|
|
472
|
+
- **Unit Tests**: Domain entities, value objects, command/query handlers (isolated logic)
|
|
473
|
+
- **Integration Tests**: Repository implementations, database operations with EF Core InMemory or Test Containers
|
|
474
|
+
- **TDD Approach**: Write tests before or alongside implementation
|
|
475
|
+
- **High Coverage**: Aim for >80% code coverage
|
|
476
|
+
|
|
477
|
+
### Test Structure (xUnit)
|
|
478
|
+
```csharp
|
|
479
|
+
public class CreateUserCommandHandlerTests
|
|
480
|
+
{
|
|
481
|
+
private readonly Mock<IUserRepository> _userRepositoryMock;
|
|
482
|
+
private readonly Mock<IValidator<CreateUserCommand>> _validatorMock;
|
|
483
|
+
private readonly CreateUserCommandHandler _handler;
|
|
484
|
+
|
|
485
|
+
public CreateUserCommandHandlerTests()
|
|
486
|
+
{
|
|
487
|
+
_userRepositoryMock = new Mock<IUserRepository>();
|
|
488
|
+
_validatorMock = new Mock<IValidator<CreateUserCommand>>();
|
|
489
|
+
_handler = new CreateUserCommandHandler(
|
|
490
|
+
_userRepositoryMock.Object,
|
|
491
|
+
_validatorMock.Object);
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
[Fact]
|
|
495
|
+
public async Task HandleAsync_WithValidCommand_ShouldCreateUser()
|
|
496
|
+
{
|
|
497
|
+
// Arrange
|
|
498
|
+
var command = new CreateUserCommand("test@example.com", "John Doe");
|
|
499
|
+
_validatorMock
|
|
500
|
+
.Setup(v => v.ValidateAndThrowAsync(command, It.IsAny<CancellationToken>()))
|
|
501
|
+
.Returns(Task.CompletedTask);
|
|
502
|
+
_userRepositoryMock
|
|
503
|
+
.Setup(r => r.FindByEmailAsync(It.IsAny<EmailValueObject>(), It.IsAny<CancellationToken>()))
|
|
504
|
+
.ReturnsAsync((UserEntity?)null);
|
|
505
|
+
_userRepositoryMock
|
|
506
|
+
.Setup(r => r.SaveAsync(It.IsAny<UserEntity>(), It.IsAny<CancellationToken>()))
|
|
507
|
+
.ReturnsAsync((UserEntity u, CancellationToken _) => u);
|
|
508
|
+
|
|
509
|
+
// Act
|
|
510
|
+
var result = await _handler.HandleAsync(command, CancellationToken.None);
|
|
511
|
+
|
|
512
|
+
// Assert
|
|
513
|
+
Assert.NotNull(result);
|
|
514
|
+
Assert.Equal(command.Email, result.Email);
|
|
515
|
+
Assert.Equal(command.Name, result.Name);
|
|
516
|
+
_userRepositoryMock.Verify(r => r.SaveAsync(It.IsAny<UserEntity>(), It.IsAny<CancellationToken>()), Times.Once);
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
[Fact]
|
|
520
|
+
public async Task HandleAsync_WithExistingUser_ShouldThrowException()
|
|
521
|
+
{
|
|
522
|
+
// Arrange
|
|
523
|
+
var command = new CreateUserCommand("test@example.com", "John Doe");
|
|
524
|
+
var existingUser = UserEntity.Create("test@example.com", "Existing User");
|
|
525
|
+
_validatorMock
|
|
526
|
+
.Setup(v => v.ValidateAndThrowAsync(command, It.IsAny<CancellationToken>()))
|
|
527
|
+
.Returns(Task.CompletedTask);
|
|
528
|
+
_userRepositoryMock
|
|
529
|
+
.Setup(r => r.FindByEmailAsync(It.IsAny<EmailValueObject>(), It.IsAny<CancellationToken>()))
|
|
530
|
+
.ReturnsAsync(existingUser);
|
|
531
|
+
|
|
532
|
+
// Act & Assert
|
|
533
|
+
await Assert.ThrowsAsync<UserAlreadyExistsException>(() =>
|
|
534
|
+
_handler.HandleAsync(command, CancellationToken.None));
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
```
|
|
538
|
+
|
|
539
|
+
### Integration Test with EF Core InMemory
|
|
540
|
+
```csharp
|
|
541
|
+
public class UserRepositoryIntegrationTests : IDisposable
|
|
542
|
+
{
|
|
543
|
+
private readonly ApplicationDbContext _context;
|
|
544
|
+
private readonly UserRepository _repository;
|
|
545
|
+
|
|
546
|
+
public UserRepositoryIntegrationTests()
|
|
547
|
+
{
|
|
548
|
+
var options = new DbContextOptionsBuilder<ApplicationDbContext>()
|
|
549
|
+
.UseInMemoryDatabase(databaseName: Guid.NewGuid().ToString())
|
|
550
|
+
.Options;
|
|
551
|
+
|
|
552
|
+
_context = new ApplicationDbContext(options);
|
|
553
|
+
_repository = new UserRepository(_context);
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
[Fact]
|
|
557
|
+
public async Task SaveAsync_ShouldPersistUser()
|
|
558
|
+
{
|
|
559
|
+
// Arrange
|
|
560
|
+
var user = UserEntity.Create("test@example.com", "John Doe");
|
|
561
|
+
|
|
562
|
+
// Act
|
|
563
|
+
var savedUser = await _repository.SaveAsync(user);
|
|
564
|
+
|
|
565
|
+
// Assert
|
|
566
|
+
var retrievedUser = await _repository.FindByIdAsync(savedUser.Id);
|
|
567
|
+
Assert.NotNull(retrievedUser);
|
|
568
|
+
Assert.Equal(user.Email.Value, retrievedUser.Email.Value);
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
public void Dispose()
|
|
572
|
+
{
|
|
573
|
+
_context.Database.EnsureDeleted();
|
|
574
|
+
_context.Dispose();
|
|
575
|
+
}
|
|
576
|
+
}
|
|
577
|
+
```
|
|
578
|
+
|
|
579
|
+
## EF Core Configuration Standards
|
|
580
|
+
|
|
581
|
+
### DbContext Configuration
|
|
582
|
+
```csharp
|
|
583
|
+
public class ApplicationDbContext : DbContext
|
|
584
|
+
{
|
|
585
|
+
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
|
|
586
|
+
: base(options)
|
|
587
|
+
{
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
public DbSet<UserEntity> Users { get; set; }
|
|
591
|
+
public DbSet<QuoteEntity> Quotes { get; set; }
|
|
592
|
+
|
|
593
|
+
protected override void OnModelCreating(ModelBuilder modelBuilder)
|
|
594
|
+
{
|
|
595
|
+
base.OnModelCreating(modelBuilder);
|
|
596
|
+
|
|
597
|
+
// Apply all configurations from assembly
|
|
598
|
+
modelBuilder.ApplyConfigurationsFromAssembly(typeof(ApplicationDbContext).Assembly);
|
|
599
|
+
}
|
|
600
|
+
}
|
|
601
|
+
```
|
|
602
|
+
|
|
603
|
+
### Entity Configuration
|
|
604
|
+
```csharp
|
|
605
|
+
public class UserEntityConfiguration : IEntityTypeConfiguration<UserEntity>
|
|
606
|
+
{
|
|
607
|
+
public void Configure(EntityTypeBuilder<UserEntity> builder)
|
|
608
|
+
{
|
|
609
|
+
builder.ToTable("Users");
|
|
610
|
+
|
|
611
|
+
builder.HasKey(u => u.Id);
|
|
612
|
+
|
|
613
|
+
builder.Property(u => u.Id)
|
|
614
|
+
.HasColumnType("uuid")
|
|
615
|
+
.IsRequired();
|
|
616
|
+
|
|
617
|
+
// Configure Value Object as owned entity
|
|
618
|
+
builder.OwnsOne(u => u.Email, email =>
|
|
619
|
+
{
|
|
620
|
+
email.Property(e => e.Value)
|
|
621
|
+
.HasColumnName("Email")
|
|
622
|
+
.HasMaxLength(256)
|
|
623
|
+
.IsRequired();
|
|
624
|
+
});
|
|
625
|
+
|
|
626
|
+
builder.OwnsOne(u => u.Name, name =>
|
|
627
|
+
{
|
|
628
|
+
name.Property(n => n.Value)
|
|
629
|
+
.HasColumnName("Name")
|
|
630
|
+
.HasMaxLength(100)
|
|
631
|
+
.IsRequired();
|
|
632
|
+
});
|
|
633
|
+
|
|
634
|
+
// Ignore domain events (not persisted)
|
|
635
|
+
builder.Ignore(u => u.DomainEvents);
|
|
636
|
+
}
|
|
637
|
+
}
|
|
638
|
+
```
|
|
639
|
+
|
|
640
|
+
## Security Standards
|
|
641
|
+
|
|
642
|
+
### Authentication & Authorization
|
|
643
|
+
- **JWT Tokens**: Proper expiration and refresh token handling
|
|
644
|
+
- **RBAC**: Role-based access control
|
|
645
|
+
- **Validation**: Input validation on all endpoints (FluentValidation)
|
|
646
|
+
- **Rate Limiting**: Throttle public endpoints to prevent abuse
|
|
647
|
+
- **Environment**: HTTPS only in production, secrets in env variables
|
|
648
|
+
|
|
649
|
+
### Data Protection
|
|
650
|
+
- Encrypt sensitive data at rest and in transit
|
|
651
|
+
- Never commit secrets to repository
|
|
652
|
+
- Implement audit logging for critical operations
|
|
653
|
+
- OWASP Top 10 compliance
|
|
654
|
+
- Regular dependency security audits with `dotnet list package --vulnerable`
|
|
655
|
+
|
|
656
|
+
## Performance Standards
|
|
657
|
+
|
|
658
|
+
### Database Optimization
|
|
659
|
+
- Proper indexing on frequently queried fields
|
|
660
|
+
- Connection pooling via EF Core 10
|
|
661
|
+
- Pagination for large datasets (Skip/Take or cursor-based)
|
|
662
|
+
- Avoid N+1 queries with `.Include()` and `.ThenInclude()`
|
|
663
|
+
- Query monitoring and slow query logging
|
|
664
|
+
- Use `AsNoTracking()` for read-only queries
|
|
665
|
+
|
|
666
|
+
### Caching Strategy
|
|
667
|
+
- **Distributed Cache**: Redis for session data and frequently accessed data
|
|
668
|
+
- **Memory Cache**: In-memory caching for configuration
|
|
669
|
+
- **TTL Strategy**: Appropriate time-to-live for different data types
|
|
670
|
+
- **Invalidation**: Event-driven cache invalidation
|
|
671
|
+
|
|
672
|
+
## Error Handling
|
|
673
|
+
|
|
674
|
+
### Exception Hierarchy
|
|
675
|
+
- Domain exceptions for business rule violations
|
|
676
|
+
- Application exceptions for use case errors
|
|
677
|
+
- Infrastructure exceptions for external service failures
|
|
678
|
+
- Global exception middleware for API responses
|
|
679
|
+
|
|
680
|
+
### Error Response Format (Problem Details RFC 7807)
|
|
681
|
+
```csharp
|
|
682
|
+
public record ProblemDetailsResponse
|
|
683
|
+
{
|
|
684
|
+
public int Status { get; init; }
|
|
685
|
+
public string Title { get; init; }
|
|
686
|
+
public string Detail { get; init; }
|
|
687
|
+
public string Instance { get; init; }
|
|
688
|
+
public DateTimeOffset Timestamp { get; init; }
|
|
689
|
+
public Dictionary<string, string[]>? Errors { get; init; }
|
|
690
|
+
}
|
|
691
|
+
|
|
692
|
+
// Example response
|
|
693
|
+
{
|
|
694
|
+
"status": 400,
|
|
695
|
+
"title": "Validation Error",
|
|
696
|
+
"detail": "One or more validation errors occurred",
|
|
697
|
+
"instance": "/api/users",
|
|
698
|
+
"timestamp": "2025-11-26T10:30:00Z",
|
|
699
|
+
"errors": {
|
|
700
|
+
"Email": ["Email is required", "Email must be valid"],
|
|
701
|
+
"Name": ["Name is required"]
|
|
702
|
+
}
|
|
703
|
+
}
|
|
704
|
+
```
|
|
705
|
+
|
|
706
|
+
### Global Exception Middleware
|
|
707
|
+
```csharp
|
|
708
|
+
public class ExceptionHandlingMiddleware
|
|
709
|
+
{
|
|
710
|
+
private readonly RequestDelegate _next;
|
|
711
|
+
private readonly ILogger<ExceptionHandlingMiddleware> _logger;
|
|
712
|
+
|
|
713
|
+
public ExceptionHandlingMiddleware(
|
|
714
|
+
RequestDelegate next,
|
|
715
|
+
ILogger<ExceptionHandlingMiddleware> logger)
|
|
716
|
+
{
|
|
717
|
+
_next = next;
|
|
718
|
+
_logger = logger;
|
|
719
|
+
}
|
|
720
|
+
|
|
721
|
+
public async Task InvokeAsync(HttpContext context)
|
|
722
|
+
{
|
|
723
|
+
try
|
|
724
|
+
{
|
|
725
|
+
await _next(context);
|
|
726
|
+
}
|
|
727
|
+
catch (ValidationException ex)
|
|
728
|
+
{
|
|
729
|
+
await HandleValidationExceptionAsync(context, ex);
|
|
730
|
+
}
|
|
731
|
+
catch (DomainException ex)
|
|
732
|
+
{
|
|
733
|
+
await HandleDomainExceptionAsync(context, ex);
|
|
734
|
+
}
|
|
735
|
+
catch (Exception ex)
|
|
736
|
+
{
|
|
737
|
+
_logger.LogError(ex, "Unhandled exception occurred");
|
|
738
|
+
await HandleUnhandledExceptionAsync(context, ex);
|
|
739
|
+
}
|
|
740
|
+
}
|
|
741
|
+
|
|
742
|
+
private static Task HandleValidationExceptionAsync(HttpContext context, ValidationException exception)
|
|
743
|
+
{
|
|
744
|
+
context.Response.StatusCode = StatusCodes.Status400BadRequest;
|
|
745
|
+
context.Response.ContentType = "application/problem+json";
|
|
746
|
+
|
|
747
|
+
var problemDetails = new ProblemDetailsResponse
|
|
748
|
+
{
|
|
749
|
+
Status = StatusCodes.Status400BadRequest,
|
|
750
|
+
Title = "Validation Error",
|
|
751
|
+
Detail = "One or more validation errors occurred",
|
|
752
|
+
Instance = context.Request.Path,
|
|
753
|
+
Timestamp = DateTimeOffset.UtcNow,
|
|
754
|
+
Errors = exception.Errors.GroupBy(e => e.PropertyName)
|
|
755
|
+
.ToDictionary(g => g.Key, g => g.Select(e => e.ErrorMessage).ToArray())
|
|
756
|
+
};
|
|
757
|
+
|
|
758
|
+
return context.Response.WriteAsJsonAsync(problemDetails);
|
|
759
|
+
}
|
|
760
|
+
}
|
|
761
|
+
```
|
|
762
|
+
|
|
763
|
+
## Security Standards
|
|
764
|
+
|
|
765
|
+
### Authentication & Authorization
|
|
766
|
+
- **JWT Tokens**: Proper expiration and refresh token handling
|
|
767
|
+
- **RBAC**: Role-based access control with Authorization Policies
|
|
768
|
+
- **Validation**: Input validation on all endpoints (FluentValidation)
|
|
769
|
+
- **Rate Limiting**: ASP.NET Core Rate Limiting middleware
|
|
770
|
+
- **Environment**: HTTPS only in production, secrets in Azure Key Vault or User Secrets
|
|
771
|
+
|
|
772
|
+
### Data Protection
|
|
773
|
+
- Encrypt sensitive data at rest and in transit
|
|
774
|
+
- Never commit secrets to repository
|
|
775
|
+
- Implement audit logging for critical operations
|
|
776
|
+
- OWASP Top 10 compliance
|
|
777
|
+
- Regular dependency security audits with `dotnet list package --vulnerable`
|
|
778
|
+
|
|
779
|
+
## Performance Standards
|
|
780
|
+
|
|
781
|
+
### Database Optimization
|
|
782
|
+
- Proper indexing on frequently queried fields
|
|
783
|
+
- Connection pooling via EF Core (configured in connection string)
|
|
784
|
+
- Pagination for large datasets (Skip/Take or Keyset pagination)
|
|
785
|
+
- Avoid N+1 queries with `.Include()` and `.ThenInclude()`
|
|
786
|
+
- Query monitoring and slow query logging
|
|
787
|
+
- Use `AsNoTracking()` for read-only queries
|
|
788
|
+
|
|
789
|
+
### Caching Strategy
|
|
790
|
+
- **Distributed Cache**: Redis or SQL Server for distributed scenarios
|
|
791
|
+
- **Memory Cache**: `IMemoryCache` for single-instance caching
|
|
792
|
+
- **TTL Strategy**: Appropriate time-to-live for different data types
|
|
793
|
+
- **Invalidation**: Event-driven cache invalidation
|
|
794
|
+
- **Response Caching**: Use `[ResponseCache]` attribute for GET endpoints
|
|
795
|
+
|
|
796
|
+
## Additional Standards
|
|
797
|
+
|
|
798
|
+
### Logging
|
|
799
|
+
- Use `ILogger<T>` for structured logging
|
|
800
|
+
- Log levels: Trace, Debug, Information, Warning, Error, Critical
|
|
801
|
+
- Include correlation IDs for request tracing
|
|
802
|
+
- Never log sensitive information (passwords, tokens, PII)
|
|
803
|
+
|
|
804
|
+
### API Versioning
|
|
805
|
+
- Use URL versioning: `/api/v1/users`
|
|
806
|
+
- Support multiple versions simultaneously during migration
|
|
807
|
+
- Deprecation notices in response headers
|
|
808
|
+
|
|
809
|
+
### Health Checks
|
|
810
|
+
- Implement `/health` endpoint for liveness
|
|
811
|
+
- Implement `/health/ready` for readiness checks
|
|
812
812
|
- Include database, cache, and external service checks
|