@polymorphism-tech/morph-spec 3.0.1 → 3.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (316) hide show
  1. package/CLAUDE.md +561 -63
  2. package/LICENSE +72 -72
  3. package/README.md +275 -79
  4. package/bin/detect-agents.js +3 -1
  5. package/bin/morph-spec.js +60 -1
  6. package/bin/render-template.js +61 -14
  7. package/bin/semantic-detect-agents.js +2 -1
  8. package/bin/{task-manager.js → task-manager.cjs} +113 -8
  9. package/bin/validate-agents-skills.js +10 -4
  10. package/bin/validate-agents.js +4 -3
  11. package/docs/api/fonts/Source-Sans-Pro/sourcesanspro-light-webfont.svg +977 -977
  12. package/docs/api/fonts/Source-Sans-Pro/sourcesanspro-regular-webfont.svg +1048 -1048
  13. package/docs/api/scripts/collapse.js +38 -38
  14. package/docs/api/scripts/commonNav.js +28 -28
  15. package/docs/api/scripts/linenumber.js +25 -25
  16. package/docs/api/scripts/nav.js +12 -12
  17. package/docs/api/scripts/polyfill.js +3 -3
  18. package/docs/api/scripts/prettify/Apache-License-2.0.txt +202 -202
  19. package/docs/api/scripts/prettify/lang-css.js +2 -2
  20. package/docs/api/scripts/prettify/prettify.js +28 -28
  21. package/docs/api/scripts/search.js +98 -98
  22. package/docs/api/styles/jsdoc.css +776 -776
  23. package/docs/api/styles/prettify.css +80 -80
  24. package/docs/cli-auto-detection.md +219 -0
  25. package/docs/examples.md +328 -328
  26. package/docs/getting-started.md +3 -3
  27. package/docs/llm-interaction-config.md +735 -0
  28. package/docs/templates.md +418 -418
  29. package/docs/troubleshooting.md +269 -0
  30. package/package.json +7 -3
  31. package/scripts/postinstall.js +132 -132
  32. package/scripts/reorganize-skills.cjs +1 -1
  33. package/scripts/validate-agents-structure.cjs +1 -1
  34. package/scripts/validate-skills.cjs +2 -2
  35. package/src/commands/advance-phase.js +93 -2
  36. package/src/commands/analyze-blazor-concurrency.js +193 -193
  37. package/src/commands/approve.js +221 -0
  38. package/src/commands/capture-pattern.js +121 -0
  39. package/src/commands/create-story.js +5 -2
  40. package/src/commands/deploy.js +780 -780
  41. package/src/commands/detect-agents.js +4 -2
  42. package/src/commands/generate.js +276 -149
  43. package/src/commands/init.js +37 -0
  44. package/src/commands/lint-fluent.js +352 -352
  45. package/src/commands/migrate-state.js +158 -0
  46. package/src/commands/rollback-phase.js +185 -185
  47. package/src/commands/search-patterns.js +126 -0
  48. package/src/commands/session-summary.js +291 -291
  49. package/src/commands/shard-spec.js +224 -224
  50. package/src/commands/spawn-team.js +172 -0
  51. package/src/commands/sprint-status.js +250 -250
  52. package/src/commands/task.js +3 -3
  53. package/src/commands/troubleshoot.js +222 -222
  54. package/src/commands/update.js +36 -0
  55. package/src/commands/upgrade.js +346 -0
  56. package/src/commands/validate-blazor-state.js +210 -210
  57. package/src/commands/validate-blazor.js +156 -156
  58. package/src/commands/validate-css.js +84 -84
  59. package/src/commands/validate-phase.js +221 -221
  60. package/src/generator/.gitkeep +0 -0
  61. package/src/generator/config-generator.js +206 -0
  62. package/src/generator/templates/config.json.template +40 -0
  63. package/src/generator/templates/project.md.template +67 -0
  64. package/src/lib/blazor-concurrency-analyzer.js +288 -288
  65. package/src/lib/blazor-state-validator.js +291 -291
  66. package/src/lib/blazor-validator.js +374 -374
  67. package/src/lib/checkpoint-hooks.js +258 -0
  68. package/src/lib/context-generator.js +7 -4
  69. package/src/lib/css-validator.js +352 -352
  70. package/src/lib/design-system-generator.js +298 -298
  71. package/src/lib/hook-executor.js +2 -1
  72. package/src/lib/learning-system.js +520 -520
  73. package/src/lib/metadata-extractor.js +380 -0
  74. package/src/lib/mockup-generator.js +366 -366
  75. package/src/lib/phase-state-machine.js +214 -0
  76. package/src/lib/stack-resolver.js +148 -0
  77. package/src/lib/standards-context-injector.js +4 -3
  78. package/src/lib/state-manager.js +120 -0
  79. package/src/lib/team-orchestrator.js +2 -1
  80. package/src/lib/template-data-sources.js +325 -0
  81. package/src/lib/troubleshoot-grep.js +204 -194
  82. package/src/lib/troubleshoot-index.js +144 -144
  83. package/src/lib/ui-detector.js +350 -350
  84. package/src/lib/validation-runner.js +2 -1
  85. package/src/lib/validators/architecture-validator.js +387 -387
  86. package/src/lib/validators/content-validator.js +351 -0
  87. package/src/lib/validators/package-validator.js +360 -360
  88. package/src/lib/validators/ui-contrast-validator.js +422 -422
  89. package/src/llm/.gitkeep +0 -0
  90. package/src/llm/analyzer.js +215 -0
  91. package/src/llm/environment-detector.js +43 -0
  92. package/src/llm/few-shot-examples.js +216 -0
  93. package/src/llm/project-config-schema.json +188 -0
  94. package/src/llm/prompt-builder.js +96 -0
  95. package/src/llm/schema-validator.js +121 -0
  96. package/src/orchestrator.js +206 -0
  97. package/src/sanitizer/.gitkeep +0 -0
  98. package/src/sanitizer/context-sanitizer.js +221 -0
  99. package/src/sanitizer/patterns.js +163 -0
  100. package/src/scanner/.gitkeep +0 -0
  101. package/src/scanner/project-scanner.js +242 -0
  102. package/src/types/index.js +477 -0
  103. package/src/ui/.gitkeep +0 -0
  104. package/src/ui/diff-display.js +91 -0
  105. package/src/ui/interactive-wizard.js +96 -0
  106. package/src/ui/user-review.js +211 -0
  107. package/src/ui/wizard-questions.js +190 -0
  108. package/src/utils/file-copier.js +3 -1
  109. package/src/utils/logger.js +32 -32
  110. package/src/utils/version-checker.js +175 -175
  111. package/src/writer/.gitkeep +0 -0
  112. package/src/writer/file-writer.js +86 -0
  113. package/{content → stacks/blazor-azure}/.azure/README.md +2 -2
  114. package/{content → stacks/blazor-azure}/.azure/pipelines/pipeline-variables.yml +1 -1
  115. package/{content → stacks/blazor-azure}/.azure/pipelines/prod-pipeline.yml +1 -1
  116. package/{content → stacks/blazor-azure}/.azure/pipelines/staging-pipeline.yml +1 -1
  117. package/{content → stacks/blazor-azure}/.claude/commands/morph-preflight.md +227 -227
  118. package/{content → stacks/blazor-azure}/.claude/commands/morph-troubleshoot.md +122 -122
  119. package/{content → stacks/blazor-azure}/.claude/skills/level-1-workflows/phase-setup.md +1 -1
  120. package/{content → stacks/blazor-azure}/.morph/docs/workflows/enforcement-pipeline.md +3 -3
  121. package/{content → stacks/blazor-azure}/.morph/hooks/README.md +12 -12
  122. package/{content → stacks/blazor-azure}/.morph/standards/agent-teams-workflow.md +2 -2
  123. package/{content → stacks/blazor-azure}/.morph/standards/migration-guide.md +2 -2
  124. package/{content → stacks/blazor-azure}/.morph/templates/infra/deploy-checklist.md +426 -426
  125. package/stacks/nextjs-supabase/.claude/skills/level-2-domains/backend/dotnet-supabase.md +244 -0
  126. package/stacks/nextjs-supabase/.claude/skills/level-2-domains/frontend/nextjs-supabase.md +335 -0
  127. package/stacks/nextjs-supabase/.claude/skills/level-2-domains/infrastructure/easypanel-deployer.md +189 -0
  128. package/stacks/nextjs-supabase/.claude/skills/level-2-domains/integrations/supabase-expert.md +170 -0
  129. package/stacks/nextjs-supabase/.morph/config/agents.json +345 -0
  130. package/stacks/nextjs-supabase/.morph/config/config.template.json +92 -0
  131. package/stacks/nextjs-supabase/.morph/docs/easypanel-setup.md +169 -0
  132. package/stacks/nextjs-supabase/.morph/docs/supabase-mcp-setup.md +247 -0
  133. package/stacks/nextjs-supabase/.morph/examples/crud-nextjs-supabase/README.md +697 -0
  134. package/stacks/nextjs-supabase/.morph/examples/crud-nextjs-supabase/spec.md +85 -0
  135. package/stacks/nextjs-supabase/.morph/examples/crud-nextjs-supabase/tasks.md +86 -0
  136. package/stacks/nextjs-supabase/.morph/examples/saas-nextjs-supabase/README.md +498 -0
  137. package/stacks/nextjs-supabase/.morph/examples/saas-nextjs-supabase/decisions.md +121 -0
  138. package/stacks/nextjs-supabase/.morph/examples/saas-nextjs-supabase/spec.md +138 -0
  139. package/stacks/nextjs-supabase/.morph/examples/saas-nextjs-supabase/tasks.md +162 -0
  140. package/stacks/nextjs-supabase/.morph/project.md +168 -0
  141. package/stacks/nextjs-supabase/.morph/standards/easypanel-deploy.md +191 -0
  142. package/stacks/nextjs-supabase/.morph/standards/nextjs-patterns.md +193 -0
  143. package/stacks/nextjs-supabase/.morph/standards/supabase-auth.md +171 -0
  144. package/stacks/nextjs-supabase/.morph/standards/supabase-pgvector.md +164 -0
  145. package/stacks/nextjs-supabase/.morph/standards/supabase-rls.md +179 -0
  146. package/stacks/nextjs-supabase/.morph/standards/supabase-storage.md +148 -0
  147. package/stacks/nextjs-supabase/.morph/templates/contracts.cs +173 -0
  148. package/stacks/nextjs-supabase/.morph/templates/contracts.ts +168 -0
  149. package/stacks/nextjs-supabase/.morph/templates/decisions.md +115 -0
  150. package/stacks/nextjs-supabase/.morph/templates/dockerfile-api.dockerfile +38 -0
  151. package/stacks/nextjs-supabase/.morph/templates/dockerfile-web.dockerfile +48 -0
  152. package/stacks/nextjs-supabase/.morph/templates/proposal.md +145 -0
  153. package/stacks/nextjs-supabase/.morph/templates/recap.md +134 -0
  154. package/stacks/nextjs-supabase/.morph/templates/rls-policy.sql +57 -0
  155. package/stacks/nextjs-supabase/.morph/templates/spec.md +231 -0
  156. package/stacks/nextjs-supabase/.morph/templates/supabase-migration.sql +100 -0
  157. package/stacks/nextjs-supabase/.morph/templates/tasks.md +257 -0
  158. package/stacks/nextjs-supabase/CLAUDE.md +149 -0
  159. package/stacks/nextjs-supabase/README.md +112 -0
  160. /package/{content → stacks/blazor-azure}/.azure/docs/azure-devops-setup.md +0 -0
  161. /package/{content → stacks/blazor-azure}/.azure/docs/branch-strategy.md +0 -0
  162. /package/{content → stacks/blazor-azure}/.azure/docs/local-development.md +0 -0
  163. /package/{content → stacks/blazor-azure}/.azure/pipelines/templates/build-dotnet.yml +0 -0
  164. /package/{content → stacks/blazor-azure}/.azure/pipelines/templates/deploy-app-service.yml +0 -0
  165. /package/{content → stacks/blazor-azure}/.azure/pipelines/templates/deploy-container-app.yml +0 -0
  166. /package/{content → stacks/blazor-azure}/.azure/pipelines/templates/infra-deploy.yml +0 -0
  167. /package/{content → stacks/blazor-azure}/.claude/commands/morph-apply.md +0 -0
  168. /package/{content → stacks/blazor-azure}/.claude/commands/morph-archive.md +0 -0
  169. /package/{content → stacks/blazor-azure}/.claude/commands/morph-deploy.md +0 -0
  170. /package/{content → stacks/blazor-azure}/.claude/commands/morph-infra.md +0 -0
  171. /package/{content → stacks/blazor-azure}/.claude/commands/morph-proposal.md +0 -0
  172. /package/{content → stacks/blazor-azure}/.claude/commands/morph-status.md +0 -0
  173. /package/{content → stacks/blazor-azure}/.claude/settings.local.json +0 -0
  174. /package/{content → stacks/blazor-azure}/.claude/skills/level-0-meta/README.md +0 -0
  175. /package/{content → stacks/blazor-azure}/.claude/skills/level-0-meta/code-review.md +0 -0
  176. /package/{content → stacks/blazor-azure}/.claude/skills/level-0-meta/morph-checklist.md +0 -0
  177. /package/{content → stacks/blazor-azure}/.claude/skills/level-0-meta/simulation-checklist.md +0 -0
  178. /package/{content → stacks/blazor-azure}/.claude/skills/level-1-workflows/README.md +0 -0
  179. /package/{content → stacks/blazor-azure}/.claude/skills/level-1-workflows/morph-replicate.md +0 -0
  180. /package/{content → stacks/blazor-azure}/.claude/skills/level-1-workflows/phase-clarify.md +0 -0
  181. /package/{content → stacks/blazor-azure}/.claude/skills/level-1-workflows/phase-design.md +0 -0
  182. /package/{content → stacks/blazor-azure}/.claude/skills/level-1-workflows/phase-tasks.md +0 -0
  183. /package/{content → stacks/blazor-azure}/.claude/skills/level-1-workflows/phase-uiux.md +0 -0
  184. /package/{content → stacks/blazor-azure}/.claude/skills/level-2-domains/README.md +0 -0
  185. /package/{content → stacks/blazor-azure}/.claude/skills/level-2-domains/ai-agents/ai-system-architect.md +0 -0
  186. /package/{content → stacks/blazor-azure}/.claude/skills/level-2-domains/architecture/po-pm-advisor.md +0 -0
  187. /package/{content → stacks/blazor-azure}/.claude/skills/level-2-domains/architecture/prompt-engineer.md +0 -0
  188. /package/{content → stacks/blazor-azure}/.claude/skills/level-2-domains/architecture/seo-growth-hacker.md +0 -0
  189. /package/{content → stacks/blazor-azure}/.claude/skills/level-2-domains/architecture/standards-architect.md +0 -0
  190. /package/{content → stacks/blazor-azure}/.claude/skills/level-2-domains/backend/dotnet-senior.md +0 -0
  191. /package/{content → stacks/blazor-azure}/.claude/skills/level-2-domains/backend/ef-modeler.md +0 -0
  192. /package/{content → stacks/blazor-azure}/.claude/skills/level-2-domains/backend/hangfire-orchestrator.md +0 -0
  193. /package/{content → stacks/blazor-azure}/.claude/skills/level-2-domains/backend/ms-agent-expert.md +0 -0
  194. /package/{content → stacks/blazor-azure}/.claude/skills/level-2-domains/frontend/blazor-builder.md +0 -0
  195. /package/{content → stacks/blazor-azure}/.claude/skills/level-2-domains/frontend/nextjs-expert.md +0 -0
  196. /package/{content → stacks/blazor-azure}/.claude/skills/level-2-domains/frontend/ui-ux-designer.md +0 -0
  197. /package/{content → stacks/blazor-azure}/.claude/skills/level-2-domains/infrastructure/azure-architect.md +0 -0
  198. /package/{content → stacks/blazor-azure}/.claude/skills/level-2-domains/infrastructure/azure-deploy-specialist.md +0 -0
  199. /package/{content → stacks/blazor-azure}/.claude/skills/level-2-domains/infrastructure/bicep-architect.md +0 -0
  200. /package/{content → stacks/blazor-azure}/.claude/skills/level-2-domains/infrastructure/container-specialist.md +0 -0
  201. /package/{content → stacks/blazor-azure}/.claude/skills/level-2-domains/infrastructure/devops-engineer.md +0 -0
  202. /package/{content → stacks/blazor-azure}/.claude/skills/level-2-domains/integrations/asaas-financial.md +0 -0
  203. /package/{content → stacks/blazor-azure}/.claude/skills/level-2-domains/integrations/azure-identity.md +0 -0
  204. /package/{content → stacks/blazor-azure}/.claude/skills/level-2-domains/integrations/clerk-auth.md +0 -0
  205. /package/{content → stacks/blazor-azure}/.claude/skills/level-2-domains/integrations/resend-email.md +0 -0
  206. /package/{content → stacks/blazor-azure}/.claude/skills/level-2-domains/quality/code-analyzer.md +0 -0
  207. /package/{content → stacks/blazor-azure}/.claude/skills/level-2-domains/quality/testing-specialist.md +0 -0
  208. /package/{content → stacks/blazor-azure}/.claude/skills/level-3-technologies/README.md +0 -0
  209. /package/{content → stacks/blazor-azure}/.claude/skills/level-4-patterns/README.md +0 -0
  210. /package/{content → stacks/blazor-azure}/.morph/.morphversion +0 -0
  211. /package/{content → stacks/blazor-azure}/.morph/archive/.gitkeep +0 -0
  212. /package/{content → stacks/blazor-azure}/.morph/config/agents.json +0 -0
  213. /package/{content → stacks/blazor-azure}/.morph/config/config.template.json +0 -0
  214. /package/{content → stacks/blazor-azure}/.morph/docs/STORY-DRIVEN-DEVELOPMENT.md +0 -0
  215. /package/{content → stacks/blazor-azure}/.morph/docs/workflows/design-impl.md +0 -0
  216. /package/{content → stacks/blazor-azure}/.morph/docs/workflows/fast-track.md +0 -0
  217. /package/{content → stacks/blazor-azure}/.morph/docs/workflows/full-morph.md +0 -0
  218. /package/{content → stacks/blazor-azure}/.morph/docs/workflows/standard.md +0 -0
  219. /package/{content → stacks/blazor-azure}/.morph/docs/workflows/ui-refresh.md +0 -0
  220. /package/{content → stacks/blazor-azure}/.morph/examples/api-nextjs/README.md +0 -0
  221. /package/{content → stacks/blazor-azure}/.morph/examples/api-nextjs/contracts.ts +0 -0
  222. /package/{content → stacks/blazor-azure}/.morph/examples/api-nextjs/spec.md +0 -0
  223. /package/{content → stacks/blazor-azure}/.morph/examples/api-nextjs/tasks.md +0 -0
  224. /package/{content → stacks/blazor-azure}/.morph/examples/micro-saas/README.md +0 -0
  225. /package/{content → stacks/blazor-azure}/.morph/examples/micro-saas/contracts.cs +0 -0
  226. /package/{content → stacks/blazor-azure}/.morph/examples/micro-saas/decisions.md +0 -0
  227. /package/{content → stacks/blazor-azure}/.morph/examples/micro-saas/spec.md +0 -0
  228. /package/{content → stacks/blazor-azure}/.morph/examples/micro-saas/tasks.md +0 -0
  229. /package/{content → stacks/blazor-azure}/.morph/examples/multi-agent/README.md +0 -0
  230. /package/{content → stacks/blazor-azure}/.morph/examples/multi-agent/contracts.cs +0 -0
  231. /package/{content → stacks/blazor-azure}/.morph/examples/multi-agent/spec.md +0 -0
  232. /package/{content → stacks/blazor-azure}/.morph/examples/multi-agent/tasks.md +0 -0
  233. /package/{content → stacks/blazor-azure}/.morph/examples/scheduled-reports/decisions.md +0 -0
  234. /package/{content → stacks/blazor-azure}/.morph/examples/scheduled-reports/proposal.md +0 -0
  235. /package/{content → stacks/blazor-azure}/.morph/examples/scheduled-reports/spec.md +0 -0
  236. /package/{content → stacks/blazor-azure}/.morph/examples/state-v3.json +0 -0
  237. /package/{content → stacks/blazor-azure}/.morph/features/.gitkeep +0 -0
  238. /package/{content → stacks/blazor-azure}/.morph/hooks/pre-commit-agents.sh +0 -0
  239. /package/{content → stacks/blazor-azure}/.morph/hooks/pre-commit-all.sh +0 -0
  240. /package/{content → stacks/blazor-azure}/.morph/hooks/pre-commit-specs.sh +0 -0
  241. /package/{content → stacks/blazor-azure}/.morph/hooks/pre-commit-tests.sh +0 -0
  242. /package/{content → stacks/blazor-azure}/.morph/hooks/task-completed.js +0 -0
  243. /package/{content → stacks/blazor-azure}/.morph/hooks/teammate-idle.js +0 -0
  244. /package/{content → stacks/blazor-azure}/.morph/project.md +0 -0
  245. /package/{content → stacks/blazor-azure}/.morph/schemas/agent.schema.json +0 -0
  246. /package/{content → stacks/blazor-azure}/.morph/schemas/tasks.schema.json +0 -0
  247. /package/{content → stacks/blazor-azure}/.morph/specs/.gitkeep +0 -0
  248. /package/{content → stacks/blazor-azure}/.morph/standards/agent-framework-blazor-ui.md +0 -0
  249. /package/{content → stacks/blazor-azure}/.morph/standards/agent-framework-production.md +0 -0
  250. /package/{content → stacks/blazor-azure}/.morph/standards/agent-framework-setup.md +0 -0
  251. /package/{content → stacks/blazor-azure}/.morph/standards/agent-framework-workflows.md +0 -0
  252. /package/{content → stacks/blazor-azure}/.morph/standards/architecture.md +0 -0
  253. /package/{content → stacks/blazor-azure}/.morph/standards/azure.md +0 -0
  254. /package/{content → stacks/blazor-azure}/.morph/standards/coding.md +0 -0
  255. /package/{content → stacks/blazor-azure}/.morph/standards/dotnet10-migration.md +0 -0
  256. /package/{content → stacks/blazor-azure}/.morph/standards/fluent-ui-setup.md +0 -0
  257. /package/{content → stacks/blazor-azure}/.morph/standards/passkeys-auth.md +0 -0
  258. /package/{content → stacks/blazor-azure}/.morph/standards/vector-search-rag.md +0 -0
  259. /package/{content → stacks/blazor-azure}/.morph/state.json +0 -0
  260. /package/{content → stacks/blazor-azure}/.morph/templates/CONTEXT-FEATURE.md +0 -0
  261. /package/{content → stacks/blazor-azure}/.morph/templates/CONTEXT.md +0 -0
  262. /package/{content → stacks/blazor-azure}/.morph/templates/FluentDesignTheme.cs +0 -0
  263. /package/{content → stacks/blazor-azure}/.morph/templates/MudTheme.cs +0 -0
  264. /package/{content → stacks/blazor-azure}/.morph/templates/agent.cs +0 -0
  265. /package/{content → stacks/blazor-azure}/.morph/templates/clarify-questions.md +0 -0
  266. /package/{content → stacks/blazor-azure}/.morph/templates/component.razor +0 -0
  267. /package/{content → stacks/blazor-azure}/.morph/templates/contracts/Commands.cs +0 -0
  268. /package/{content → stacks/blazor-azure}/.morph/templates/contracts/Entities.cs +0 -0
  269. /package/{content → stacks/blazor-azure}/.morph/templates/contracts/Queries.cs +0 -0
  270. /package/{content → stacks/blazor-azure}/.morph/templates/contracts/README.md +0 -0
  271. /package/{content → stacks/blazor-azure}/.morph/templates/contracts.cs +0 -0
  272. /package/{content → stacks/blazor-azure}/.morph/templates/decisions.md +0 -0
  273. /package/{content → stacks/blazor-azure}/.morph/templates/design-system.css +0 -0
  274. /package/{content → stacks/blazor-azure}/.morph/templates/infra/.dockerignore.example +0 -0
  275. /package/{content → stacks/blazor-azure}/.morph/templates/infra/Dockerfile.example +0 -0
  276. /package/{content → stacks/blazor-azure}/.morph/templates/infra/README.md +0 -0
  277. /package/{content → stacks/blazor-azure}/.morph/templates/infra/app-insights.bicep +0 -0
  278. /package/{content → stacks/blazor-azure}/.morph/templates/infra/app-service.bicep +0 -0
  279. /package/{content → stacks/blazor-azure}/.morph/templates/infra/azure-pipelines-deploy.yml +0 -0
  280. /package/{content → stacks/blazor-azure}/.morph/templates/infra/container-app-env.bicep +0 -0
  281. /package/{content → stacks/blazor-azure}/.morph/templates/infra/container-app.bicep +0 -0
  282. /package/{content → stacks/blazor-azure}/.morph/templates/infra/deploy.ps1 +0 -0
  283. /package/{content → stacks/blazor-azure}/.morph/templates/infra/deploy.sh +0 -0
  284. /package/{content → stacks/blazor-azure}/.morph/templates/infra/key-vault.bicep +0 -0
  285. /package/{content → stacks/blazor-azure}/.morph/templates/infra/main.bicep +0 -0
  286. /package/{content → stacks/blazor-azure}/.morph/templates/infra/parameters.dev.json +0 -0
  287. /package/{content → stacks/blazor-azure}/.morph/templates/infra/parameters.prod.json +0 -0
  288. /package/{content → stacks/blazor-azure}/.morph/templates/infra/parameters.staging.json +0 -0
  289. /package/{content → stacks/blazor-azure}/.morph/templates/infra/sql-database.bicep +0 -0
  290. /package/{content → stacks/blazor-azure}/.morph/templates/infra/storage.bicep +0 -0
  291. /package/{content → stacks/blazor-azure}/.morph/templates/integrations/asaas-client.cs +0 -0
  292. /package/{content → stacks/blazor-azure}/.morph/templates/integrations/asaas-webhook.cs +0 -0
  293. /package/{content → stacks/blazor-azure}/.morph/templates/integrations/azure-identity-config.cs +0 -0
  294. /package/{content → stacks/blazor-azure}/.morph/templates/integrations/clerk-config.cs +0 -0
  295. /package/{content → stacks/blazor-azure}/.morph/templates/job.cs +0 -0
  296. /package/{content → stacks/blazor-azure}/.morph/templates/migration.cs +0 -0
  297. /package/{content → stacks/blazor-azure}/.morph/templates/proposal.md +0 -0
  298. /package/{content → stacks/blazor-azure}/.morph/templates/recap.md +0 -0
  299. /package/{content → stacks/blazor-azure}/.morph/templates/repository.cs +0 -0
  300. /package/{content → stacks/blazor-azure}/.morph/templates/saas/subscription.cs +0 -0
  301. /package/{content → stacks/blazor-azure}/.morph/templates/saas/tenant.cs +0 -0
  302. /package/{content → stacks/blazor-azure}/.morph/templates/service.cs +0 -0
  303. /package/{content → stacks/blazor-azure}/.morph/templates/simulation.md +0 -0
  304. /package/{content → stacks/blazor-azure}/.morph/templates/spec.md +0 -0
  305. /package/{content → stacks/blazor-azure}/.morph/templates/sprint-status.yaml +0 -0
  306. /package/{content → stacks/blazor-azure}/.morph/templates/state.template.json +0 -0
  307. /package/{content → stacks/blazor-azure}/.morph/templates/story.md +0 -0
  308. /package/{content → stacks/blazor-azure}/.morph/templates/tasks.md +0 -0
  309. /package/{content → stacks/blazor-azure}/.morph/templates/test.cs +0 -0
  310. /package/{content → stacks/blazor-azure}/.morph/templates/ui-components.md +0 -0
  311. /package/{content → stacks/blazor-azure}/.morph/templates/ui-design-system.md +0 -0
  312. /package/{content → stacks/blazor-azure}/.morph/templates/ui-flows.md +0 -0
  313. /package/{content → stacks/blazor-azure}/.morph/templates/ui-mockups.md +0 -0
  314. /package/{content → stacks/blazor-azure}/.morph/test-infra/example.bicep +0 -0
  315. /package/{content → stacks/blazor-azure}/CLAUDE.md +0 -0
  316. /package/{content → stacks/blazor-azure}/README.md +0 -0
@@ -0,0 +1,244 @@
1
+ # .NET + Supabase Backend
2
+
3
+ > **Layer:** 2 | **Load:** on-keyword | **Keywords:** dotnet, supabase, npgsql, dapper, jwt, minimal-api, postgresql
4
+
5
+ ## Identity
6
+
7
+ Backend specialist for .NET Minimal API with Supabase (PostgreSQL). Uses Npgsql for connections, Dapper for data access with records, JWT middleware for Supabase Auth token validation, and typed Minimal API endpoints. No EF Core -- direct SQL with Dapper for maximum control over Supabase's PostgreSQL.
8
+
9
+ ## Domains
10
+
11
+ - data-access
12
+ - auth-middleware
13
+ - api-endpoints
14
+
15
+ ## Standards
16
+
17
+ - coding.md -- C# naming, sealed classes, CancellationToken, Result pattern
18
+ - architecture.md -- Layer boundaries (API -> Application -> Domain)
19
+
20
+ ## Patterns
21
+
22
+ ### Npgsql Connection Setup
23
+
24
+ ```csharp
25
+ // Program.cs
26
+ builder.Services.AddSingleton<NpgsqlDataSource>(sp =>
27
+ {
28
+ var connectionString = builder.Configuration.GetConnectionString("Supabase")
29
+ ?? throw new InvalidOperationException("Supabase connection string not configured");
30
+ var dataSourceBuilder = new NpgsqlDataSourceBuilder(connectionString);
31
+ dataSourceBuilder.UseVector(); // pgvector support
32
+ return dataSourceBuilder.Build();
33
+ });
34
+
35
+ // appsettings.json
36
+ {
37
+ "ConnectionStrings": {
38
+ "Supabase": "Host=db.xxx.supabase.co;Port=5432;Database=postgres;Username=postgres;Password=${DB_PASSWORD};SSL Mode=Require;Trust Server Certificate=true"
39
+ }
40
+ }
41
+ ```
42
+
43
+ ### Dapper Queries with Records
44
+
45
+ ```csharp
46
+ namespace MyApp.Infrastructure.Repositories;
47
+
48
+ public sealed class DocumentRepository(NpgsqlDataSource dataSource) : IDocumentRepository
49
+ {
50
+ public async Task<DocumentDto?> GetByIdAsync(Guid id, CancellationToken ct = default)
51
+ {
52
+ await using var connection = await dataSource.OpenConnectionAsync(ct);
53
+ return await connection.QueryFirstOrDefaultAsync<DocumentDto>(
54
+ """
55
+ SELECT id, title, content, user_id AS UserId, created_at AS CreatedAt
56
+ FROM documents
57
+ WHERE id = @Id
58
+ """,
59
+ new { Id = id });
60
+ }
61
+
62
+ public async Task<PagedResult<DocumentDto>> GetPagedAsync(
63
+ PaginationQuery query,
64
+ Guid userId,
65
+ CancellationToken ct = default)
66
+ {
67
+ await using var connection = await dataSource.OpenConnectionAsync(ct);
68
+
69
+ var count = await connection.ExecuteScalarAsync<int>(
70
+ "SELECT count(*) FROM documents WHERE user_id = @UserId",
71
+ new { UserId = userId });
72
+
73
+ var items = await connection.QueryAsync<DocumentDto>(
74
+ """
75
+ SELECT id, title, content, user_id AS UserId, created_at AS CreatedAt
76
+ FROM documents
77
+ WHERE user_id = @UserId
78
+ ORDER BY created_at DESC
79
+ LIMIT @PageSize OFFSET @Offset
80
+ """,
81
+ new { UserId = userId, query.PageSize, Offset = (query.Page - 1) * query.PageSize });
82
+
83
+ return new PagedResult<DocumentDto>(items.ToList(), count, query.Page, query.PageSize);
84
+ }
85
+
86
+ public async Task<Guid> CreateAsync(CreateDocumentRequest request, Guid userId, CancellationToken ct = default)
87
+ {
88
+ await using var connection = await dataSource.OpenConnectionAsync(ct);
89
+ return await connection.ExecuteScalarAsync<Guid>(
90
+ """
91
+ INSERT INTO documents (title, content, user_id)
92
+ VALUES (@Title, @Content, @UserId)
93
+ RETURNING id
94
+ """,
95
+ new { request.Title, request.Content, UserId = userId });
96
+ }
97
+ }
98
+ ```
99
+
100
+ ### JWT Middleware Setup (Supabase Auth)
101
+
102
+ ```csharp
103
+ // Program.cs
104
+ builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
105
+ .AddJwtBearer(options =>
106
+ {
107
+ var supabaseUrl = builder.Configuration["Supabase:Url"]
108
+ ?? throw new InvalidOperationException("Supabase:Url not configured");
109
+ var jwtSecret = builder.Configuration["Supabase:JwtSecret"]
110
+ ?? throw new InvalidOperationException("Supabase:JwtSecret not configured");
111
+
112
+ options.TokenValidationParameters = new TokenValidationParameters
113
+ {
114
+ ValidateIssuer = true,
115
+ ValidIssuer = $"{supabaseUrl}/auth/v1",
116
+ ValidateAudience = true,
117
+ ValidAudience = "authenticated",
118
+ ValidateIssuerSigningKey = true,
119
+ IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(jwtSecret)),
120
+ ValidateLifetime = true,
121
+ ClockSkew = TimeSpan.FromSeconds(30)
122
+ };
123
+ });
124
+
125
+ builder.Services.AddAuthorization();
126
+ app.UseAuthentication();
127
+ app.UseAuthorization();
128
+ ```
129
+
130
+ ### User ID Extraction
131
+
132
+ ```csharp
133
+ // Extension method for ClaimsPrincipal
134
+ public static class ClaimsPrincipalExtensions
135
+ {
136
+ public static Guid GetUserId(this ClaimsPrincipal user)
137
+ {
138
+ var sub = user.FindFirstValue(ClaimTypes.NameIdentifier)
139
+ ?? throw new UnauthorizedAccessException("User ID claim not found");
140
+ return Guid.Parse(sub);
141
+ }
142
+ }
143
+ ```
144
+
145
+ ### Minimal API Endpoints
146
+
147
+ ```csharp
148
+ namespace MyApp.Api.Endpoints;
149
+
150
+ public static class DocumentEndpoints
151
+ {
152
+ public static void MapDocumentEndpoints(this WebApplication app)
153
+ {
154
+ var group = app.MapGroup("/api/documents")
155
+ .WithTags("Documents")
156
+ .RequireAuthorization();
157
+
158
+ group.MapGet("/", GetAllAsync);
159
+ group.MapGet("/{id:guid}", GetByIdAsync);
160
+ group.MapPost("/", CreateAsync);
161
+ group.MapPut("/{id:guid}", UpdateAsync);
162
+ group.MapDelete("/{id:guid}", DeleteAsync);
163
+ }
164
+
165
+ private static async Task<IResult> GetAllAsync(
166
+ [AsParameters] PaginationQuery query,
167
+ ClaimsPrincipal user,
168
+ IDocumentService service,
169
+ CancellationToken ct)
170
+ {
171
+ var userId = user.GetUserId();
172
+ var result = await service.GetPagedAsync(query, userId, ct);
173
+ return Results.Ok(result);
174
+ }
175
+
176
+ private static async Task<IResult> CreateAsync(
177
+ CreateDocumentRequest request,
178
+ ClaimsPrincipal user,
179
+ IDocumentService service,
180
+ CancellationToken ct)
181
+ {
182
+ var userId = user.GetUserId();
183
+ var result = await service.CreateAsync(request, userId, ct);
184
+ return result.IsSuccess
185
+ ? Results.Created($"/api/documents/{result.Value.Id}", result.Value)
186
+ : Results.BadRequest(result.Error);
187
+ }
188
+ }
189
+ ```
190
+
191
+ ### Supabase Client Wrapper (for Storage/Auth Admin)
192
+
193
+ ```csharp
194
+ namespace MyApp.Infrastructure.Supabase;
195
+
196
+ public sealed class SupabaseAdmin(HttpClient httpClient, IOptions<SupabaseOptions> options)
197
+ {
198
+ private readonly string _serviceKey = options.Value.ServiceRoleKey;
199
+
200
+ public async Task<string> UploadFileAsync(
201
+ string bucket,
202
+ string path,
203
+ Stream content,
204
+ string contentType,
205
+ CancellationToken ct = default)
206
+ {
207
+ using var request = new HttpRequestMessage(HttpMethod.Post,
208
+ $"{options.Value.Url}/storage/v1/object/{bucket}/{path}");
209
+ request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", _serviceKey);
210
+ request.Headers.Add("apikey", _serviceKey);
211
+ request.Content = new StreamContent(content);
212
+ request.Content.Headers.ContentType = new MediaTypeHeaderValue(contentType);
213
+
214
+ var response = await httpClient.SendAsync(request, ct);
215
+ response.EnsureSuccessStatusCode();
216
+
217
+ var result = await response.Content.ReadFromJsonAsync<StorageUploadResult>(ct);
218
+ return $"{options.Value.Url}/storage/v1/object/public/{bucket}/{result?.Key}";
219
+ }
220
+ }
221
+
222
+ public sealed record SupabaseOptions
223
+ {
224
+ public required string Url { get; init; }
225
+ public required string AnonKey { get; init; }
226
+ public required string ServiceRoleKey { get; init; }
227
+ public required string JwtSecret { get; init; }
228
+ }
229
+ ```
230
+
231
+ ## Checklist
232
+
233
+ - [ ] NpgsqlDataSource registered as Singleton (connection pooling)
234
+ - [ ] Dapper queries use parameterized SQL (never string interpolation)
235
+ - [ ] JWT middleware validates issuer, audience, signing key, and lifetime
236
+ - [ ] service_role key stored in environment variables (not appsettings)
237
+ - [ ] All async methods accept CancellationToken
238
+ - [ ] All DTOs are records (not classes)
239
+ - [ ] Endpoints use RequireAuthorization()
240
+ - [ ] User ID extracted from JWT claims (not request body)
241
+
242
+ ---
243
+
244
+ *MORPH-SPEC by Polymorphism Tech*
@@ -0,0 +1,335 @@
1
+ # Next.js + Supabase Frontend
2
+
3
+ > **Layer:** 2 | **Load:** on-keyword | **Keywords:** nextjs, supabase, ssr, auth, react-query, shadcn, tailwind, middleware
4
+
5
+ ## Identity
6
+
7
+ Frontend specialist for Next.js App Router with Supabase. Implements @supabase/ssr for server and browser clients, auth middleware for route protection, React Query for data fetching with Supabase, shadcn/ui components with Tailwind CSS, and Realtime subscriptions.
8
+
9
+ ## Domains
10
+
11
+ - auth-ui
12
+ - data-fetching
13
+ - realtime
14
+ - components
15
+
16
+ ## Standards
17
+
18
+ - css-naming.md -- Tailwind utility class ordering conventions
19
+ - architecture.md -- Server vs Client component boundaries
20
+
21
+ ## Patterns
22
+
23
+ ### @supabase/ssr Client Setup
24
+
25
+ ```typescript
26
+ // lib/supabase/client.ts (Browser client)
27
+ import { createBrowserClient } from "@supabase/ssr";
28
+ import type { Database } from "@/types/database";
29
+
30
+ export function createClient() {
31
+ return createBrowserClient<Database>(
32
+ process.env.NEXT_PUBLIC_SUPABASE_URL!,
33
+ process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!
34
+ );
35
+ }
36
+ ```
37
+
38
+ ```typescript
39
+ // lib/supabase/server.ts (Server client)
40
+ import { createServerClient } from "@supabase/ssr";
41
+ import { cookies } from "next/headers";
42
+ import type { Database } from "@/types/database";
43
+
44
+ export async function createServerSupabaseClient() {
45
+ const cookieStore = await cookies();
46
+
47
+ return createServerClient<Database>(
48
+ process.env.NEXT_PUBLIC_SUPABASE_URL!,
49
+ process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
50
+ {
51
+ cookies: {
52
+ getAll() {
53
+ return cookieStore.getAll();
54
+ },
55
+ setAll(cookiesToSet) {
56
+ try {
57
+ cookiesToSet.forEach(({ name, value, options }) =>
58
+ cookieStore.set(name, value, options)
59
+ );
60
+ } catch {
61
+ // The `setAll` method is called from a Server Component
62
+ // if middleware refreshes the session. Ignore in that context.
63
+ }
64
+ },
65
+ },
66
+ }
67
+ );
68
+ }
69
+ ```
70
+
71
+ ### Auth Middleware
72
+
73
+ ```typescript
74
+ // middleware.ts
75
+ import { createServerClient } from "@supabase/ssr";
76
+ import { NextResponse, type NextRequest } from "next/server";
77
+
78
+ const publicRoutes = ["/", "/login", "/signup", "/auth/callback"];
79
+
80
+ export async function middleware(request: NextRequest) {
81
+ let response = NextResponse.next({ request });
82
+
83
+ const supabase = createServerClient(
84
+ process.env.NEXT_PUBLIC_SUPABASE_URL!,
85
+ process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
86
+ {
87
+ cookies: {
88
+ getAll() {
89
+ return request.cookies.getAll();
90
+ },
91
+ setAll(cookiesToSet) {
92
+ cookiesToSet.forEach(({ name, value }) =>
93
+ request.cookies.set(name, value)
94
+ );
95
+ response = NextResponse.next({ request });
96
+ cookiesToSet.forEach(({ name, value, options }) =>
97
+ response.cookies.set(name, value, options)
98
+ );
99
+ },
100
+ },
101
+ }
102
+ );
103
+
104
+ const {
105
+ data: { user },
106
+ } = await supabase.auth.getUser();
107
+
108
+ const isPublicRoute = publicRoutes.some((route) =>
109
+ request.nextUrl.pathname.startsWith(route)
110
+ );
111
+
112
+ if (!user && !isPublicRoute) {
113
+ const url = request.nextUrl.clone();
114
+ url.pathname = "/login";
115
+ url.searchParams.set("redirectTo", request.nextUrl.pathname);
116
+ return NextResponse.redirect(url);
117
+ }
118
+
119
+ return response;
120
+ }
121
+
122
+ export const config = {
123
+ matcher: ["/((?!_next/static|_next/image|favicon.ico|api/health).*)"],
124
+ };
125
+ ```
126
+
127
+ ### Auth Callback Route
128
+
129
+ ```typescript
130
+ // app/auth/callback/route.ts
131
+ import { createServerSupabaseClient } from "@/lib/supabase/server";
132
+ import { NextResponse } from "next/server";
133
+
134
+ export async function GET(request: Request) {
135
+ const { searchParams, origin } = new URL(request.url);
136
+ const code = searchParams.get("code");
137
+ const next = searchParams.get("next") ?? "/dashboard";
138
+
139
+ if (code) {
140
+ const supabase = await createServerSupabaseClient();
141
+ const { error } = await supabase.auth.exchangeCodeForSession(code);
142
+ if (!error) {
143
+ return NextResponse.redirect(`${origin}${next}`);
144
+ }
145
+ }
146
+
147
+ return NextResponse.redirect(`${origin}/login?error=auth_failed`);
148
+ }
149
+ ```
150
+
151
+ ### React Query + Supabase
152
+
153
+ ```typescript
154
+ // hooks/use-documents.ts
155
+ "use client";
156
+ import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query";
157
+ import { createClient } from "@/lib/supabase/client";
158
+ import type { Document, CreateDocumentInput } from "@/types/database";
159
+
160
+ const supabase = createClient();
161
+
162
+ export function useDocuments(page = 1, pageSize = 10) {
163
+ return useQuery({
164
+ queryKey: ["documents", page, pageSize],
165
+ queryFn: async () => {
166
+ const from = (page - 1) * pageSize;
167
+ const to = from + pageSize - 1;
168
+
169
+ const { data, error, count } = await supabase
170
+ .from("documents")
171
+ .select("*", { count: "exact" })
172
+ .order("created_at", { ascending: false })
173
+ .range(from, to);
174
+
175
+ if (error) throw error;
176
+ return { items: data as Document[], totalCount: count ?? 0, page, pageSize };
177
+ },
178
+ });
179
+ }
180
+
181
+ export function useCreateDocument() {
182
+ const queryClient = useQueryClient();
183
+
184
+ return useMutation({
185
+ mutationFn: async (input: CreateDocumentInput) => {
186
+ const { data, error } = await supabase
187
+ .from("documents")
188
+ .insert(input)
189
+ .select()
190
+ .single();
191
+
192
+ if (error) throw error;
193
+ return data as Document;
194
+ },
195
+ onSuccess: () => {
196
+ queryClient.invalidateQueries({ queryKey: ["documents"] });
197
+ },
198
+ });
199
+ }
200
+ ```
201
+
202
+ ### Form with react-hook-form + zod
203
+
204
+ ```typescript
205
+ // components/document-form.tsx
206
+ "use client";
207
+ import { useForm } from "react-hook-form";
208
+ import { zodResolver } from "@hookform/resolvers/zod";
209
+ import { z } from "zod";
210
+ import { Button } from "@/components/ui/button";
211
+ import { Input } from "@/components/ui/input";
212
+ import { Textarea } from "@/components/ui/textarea";
213
+ import { useCreateDocument } from "@/hooks/use-documents";
214
+
215
+ const schema = z.object({
216
+ title: z.string().min(1, "Title is required").max(200),
217
+ content: z.string().min(1, "Content is required"),
218
+ });
219
+
220
+ type FormData = z.infer<typeof schema>;
221
+
222
+ export function DocumentForm({ onSuccess }: { onSuccess?: () => void }) {
223
+ const { register, handleSubmit, formState: { errors }, reset } = useForm<FormData>({
224
+ resolver: zodResolver(schema),
225
+ });
226
+
227
+ const createDocument = useCreateDocument();
228
+
229
+ const onSubmit = async (data: FormData) => {
230
+ await createDocument.mutateAsync(data);
231
+ reset();
232
+ onSuccess?.();
233
+ };
234
+
235
+ return (
236
+ <form onSubmit={handleSubmit(onSubmit)} className="space-y-4">
237
+ <div>
238
+ <Input placeholder="Document title" {...register("title")} />
239
+ {errors.title && <p className="text-sm text-destructive mt-1">{errors.title.message}</p>}
240
+ </div>
241
+ <div>
242
+ <Textarea placeholder="Content..." rows={6} {...register("content")} />
243
+ {errors.content && <p className="text-sm text-destructive mt-1">{errors.content.message}</p>}
244
+ </div>
245
+ <Button type="submit" disabled={createDocument.isPending}>
246
+ {createDocument.isPending ? "Creating..." : "Create Document"}
247
+ </Button>
248
+ </form>
249
+ );
250
+ }
251
+ ```
252
+
253
+ ### Realtime Subscription Hook
254
+
255
+ ```typescript
256
+ // hooks/use-realtime.ts
257
+ "use client";
258
+ import { useEffect } from "react";
259
+ import { useQueryClient } from "@tanstack/react-query";
260
+ import { createClient } from "@/lib/supabase/client";
261
+ import type { RealtimePostgresChangesPayload } from "@supabase/supabase-js";
262
+
263
+ export function useRealtimeSubscription<T extends Record<string, unknown>>(
264
+ table: string,
265
+ queryKey: string[]
266
+ ) {
267
+ const queryClient = useQueryClient();
268
+ const supabase = createClient();
269
+
270
+ useEffect(() => {
271
+ const channel = supabase
272
+ .channel(`${table}-changes`)
273
+ .on<T>(
274
+ "postgres_changes",
275
+ { event: "*", schema: "public", table },
276
+ (payload: RealtimePostgresChangesPayload<T>) => {
277
+ queryClient.invalidateQueries({ queryKey });
278
+ }
279
+ )
280
+ .subscribe();
281
+
282
+ return () => {
283
+ supabase.removeChannel(channel);
284
+ };
285
+ }, [table, queryKey, queryClient, supabase]);
286
+ }
287
+
288
+ // Usage in component:
289
+ // useRealtimeSubscription<Document>("documents", ["documents"]);
290
+ ```
291
+
292
+ ### Server Component Data Fetching
293
+
294
+ ```typescript
295
+ // app/dashboard/page.tsx
296
+ import { createServerSupabaseClient } from "@/lib/supabase/server";
297
+ import { DocumentList } from "@/components/document-list";
298
+
299
+ export default async function DashboardPage() {
300
+ const supabase = await createServerSupabaseClient();
301
+ const { data: user } = await supabase.auth.getUser();
302
+
303
+ const { data: documents } = await supabase
304
+ .from("documents")
305
+ .select("*")
306
+ .order("created_at", { ascending: false })
307
+ .limit(20);
308
+
309
+ return (
310
+ <main className="container mx-auto py-8">
311
+ <h1 className="text-2xl font-bold mb-6">
312
+ Welcome, {user.user?.email}
313
+ </h1>
314
+ <DocumentList initialDocuments={documents ?? []} />
315
+ </main>
316
+ );
317
+ }
318
+ ```
319
+
320
+ ## Checklist
321
+
322
+ - [ ] @supabase/ssr used (not @supabase/auth-helpers-nextjs)
323
+ - [ ] Server and browser clients in separate files
324
+ - [ ] Middleware refreshes session on every request
325
+ - [ ] Public routes excluded from auth check
326
+ - [ ] Auth callback route handles code exchange
327
+ - [ ] React Query configured with QueryClientProvider
328
+ - [ ] Forms validated with zod schemas
329
+ - [ ] Realtime subscriptions clean up on unmount
330
+ - [ ] Server Components fetch data directly (no useEffect)
331
+ - [ ] Environment variables: NEXT_PUBLIC_SUPABASE_URL, NEXT_PUBLIC_SUPABASE_ANON_KEY
332
+
333
+ ---
334
+
335
+ *MORPH-SPEC by Polymorphism Tech*