@thierrynakoa/fire-flow 12.2.2 → 13.0.1

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 (501) hide show
  1. package/CREDITS.md +25 -0
  2. package/DOMINION-FLOW-OVERVIEW.md +182 -38
  3. package/README.md +399 -690
  4. package/TROUBLESHOOTING.md +264 -367
  5. package/agents/fire-debugger.md +54 -0
  6. package/agents/fire-executor.md +1610 -1033
  7. package/agents/fire-fact-checker.md +1 -1
  8. package/agents/fire-planner.md +85 -17
  9. package/agents/fire-project-researcher.md +1 -1
  10. package/agents/fire-researcher.md +4 -22
  11. package/agents/{fire-phoenix-analyst.md → fire-resurrection-analyst.md} +394 -394
  12. package/agents/fire-reviewer.md +552 -499
  13. package/agents/fire-verifier.md +114 -19
  14. package/bin/cli.js +18 -101
  15. package/commands/fire-0-orient.md +2 -2
  16. package/commands/fire-1a-new.md +50 -15
  17. package/commands/fire-1c-setup.md +33 -5
  18. package/commands/fire-1d-discuss.md +87 -1
  19. package/commands/fire-2-plan.md +556 -527
  20. package/commands/fire-3-execute.md +2046 -1356
  21. package/commands/fire-4-verify.md +975 -906
  22. package/commands/fire-5-handoff.md +46 -5
  23. package/commands/fire-6-resume.md +2 -31
  24. package/commands/fire-add-new-skill.md +138 -19
  25. package/commands/fire-autonomous.md +14 -2
  26. package/commands/fire-complete-milestone.md +1 -1
  27. package/commands/fire-cost.md +179 -183
  28. package/commands/fire-debug.md +1 -6
  29. package/commands/fire-loop-resume.md +2 -2
  30. package/commands/fire-loop-stop.md +1 -1
  31. package/commands/fire-loop.md +2 -15
  32. package/commands/fire-map-codebase.md +1 -1
  33. package/commands/fire-migrate-database.md +548 -0
  34. package/commands/fire-new-milestone.md +1 -1
  35. package/commands/fire-reflect.md +1 -2
  36. package/commands/fire-research.md +142 -21
  37. package/commands/{fire-phoenix.md → fire-resurrect.md} +859 -603
  38. package/commands/fire-scaffold.md +297 -0
  39. package/commands/fire-search.md +1 -2
  40. package/commands/fire-security-scan.md +483 -484
  41. package/commands/fire-setup.md +359 -0
  42. package/commands/fire-skill.md +770 -0
  43. package/commands/fire-skills-diff.md +506 -506
  44. package/commands/fire-skills-history.md +388 -388
  45. package/commands/fire-skills-rollback.md +7 -7
  46. package/commands/fire-skills-sync.md +470 -470
  47. package/commands/fire-test.md +5 -5
  48. package/commands/fire-todos.md +1 -1
  49. package/commands/fire-update.md +5 -5
  50. package/commands/fire-validate-skills.md +282 -0
  51. package/commands/fire-verify-uat.md +9 -177
  52. package/commands/fire-vuln-scan.md +492 -493
  53. package/hooks/run-hook.sh +8 -8
  54. package/hooks/run-session-end.sh +7 -7
  55. package/hooks/session-end.sh +90 -90
  56. package/hooks/session-start.sh +1 -1
  57. package/package.json +4 -25
  58. package/plugin.json +7 -7
  59. package/references/autonomy-levels.md +235 -0
  60. package/references/behavioral-directives.md +95 -3
  61. package/references/blocker-tracking.md +1 -1
  62. package/references/circuit-breaker.md +93 -2
  63. package/references/context-engineering.md +227 -9
  64. package/references/honesty-protocols.md +70 -1
  65. package/references/issue-to-pr-pipeline.md +149 -150
  66. package/references/metrics-and-trends.md +1 -2
  67. package/references/research-improvements.md +4 -108
  68. package/references/sdlc-mapping.md +73 -0
  69. package/references/state-machine.md +151 -0
  70. package/skills-library/AVAILABLE_TOOLS_REFERENCE.md +333 -0
  71. package/skills-library/SKILLS-INDEX.md +57 -558
  72. package/skills-library/SKILLS_LIBRARY_INDEX.md +532 -0
  73. package/skills-library/_general/api-patterns/api-field-name-mismatch.md +107 -0
  74. package/skills-library/_general/api-patterns/streaming-command-timeout.md +122 -0
  75. package/skills-library/_general/api-patterns/streaming-proxy-cors-bypass.md +102 -0
  76. package/skills-library/_general/automation/settings-gui-generator.md +172 -0
  77. package/skills-library/_general/database-solutions/data-type-mapping-reference.md +181 -0
  78. package/skills-library/_general/database-solutions/mysql-limit-offset-string-coercion.md +102 -0
  79. package/skills-library/_general/database-solutions/mysql-to-pg-migration.md +195 -0
  80. package/skills-library/_general/database-solutions/orm-schema-portability.md +193 -0
  81. package/skills-library/_general/database-solutions/persistent-analysis-storage.md +207 -0
  82. package/skills-library/_general/database-solutions/pg-to-mysql-schema-migration-methodology.md +190 -0
  83. package/skills-library/_general/database-solutions/sql-dialect-compatibility-matrix.md +306 -0
  84. package/skills-library/_general/database-solutions/sqlite-to-pg-migration.md +219 -0
  85. package/skills-library/_general/frontend/canvas-bubble-animation-grouping.md +270 -0
  86. package/skills-library/_general/frontend/color-token-migration.md +112 -0
  87. package/skills-library/_general/frontend/framer-motion-layoutid-grouping.md +150 -0
  88. package/skills-library/_general/frontend/pyqt6-settings-dialog.md +191 -0
  89. package/skills-library/_general/frontend/react-flow-animated-layout-switching.md +101 -0
  90. package/skills-library/_general/frontend/react-hooks-order-debugging.md +141 -0
  91. package/skills-library/_general/frontend/redux-localstorage-auth-desync.md +126 -0
  92. package/skills-library/_general/frontend/safari-csp-theme-color-debugging.md +124 -0
  93. package/skills-library/_general/frontend/safari-sw-cache-poisoning.md +138 -0
  94. package/skills-library/_general/frontend/svg-sparkline-no-charting-library.md +131 -0
  95. package/skills-library/_general/growth-marketing/oss-daily-growth-intelligence.md +224 -0
  96. package/skills-library/_general/integrations/claude-code-local-mcp-integration.md +250 -0
  97. package/skills-library/_general/integrations/mcp-composite-tool-orchestration.md +200 -0
  98. package/skills-library/_general/methodology/AGENT_SDK_STANDALONE_TOOLING.md +181 -0
  99. package/skills-library/_general/methodology/AGENT_TEAMS_GUIDE.md +169 -0
  100. package/skills-library/_general/methodology/ALAS_STATEFUL_EXECUTION.md +207 -0
  101. package/skills-library/_general/methodology/AUTO_REVIEWER_SUBAGENT.md +211 -0
  102. package/skills-library/_general/methodology/CONSISTENCY_CHECK_AMBIGUITY_GATE.md +96 -0
  103. package/skills-library/_general/methodology/DEAD_ENDS_SHELF.md +4 -4
  104. package/skills-library/_general/methodology/DISTILL_NOT_DUMP.md +108 -0
  105. package/skills-library/_general/methodology/EXECUTION_PROGRESS_MONITOR.md +157 -0
  106. package/skills-library/_general/methodology/HIERARCHICAL_REVIEW_MARS.md +122 -0
  107. package/skills-library/_general/methodology/MCP_INTER_AGENT_BRIDGE.md +207 -0
  108. package/skills-library/_general/methodology/MERMAID_WIZARD_DIAGRAMS.md +77 -0
  109. package/skills-library/_general/methodology/MISSING_DIMENSION_DETECTOR.md +89 -0
  110. package/skills-library/_general/methodology/MULTI_AGENT_COORDINATION.md +397 -0
  111. package/skills-library/_general/methodology/OBSERVATION_MASKING.md +100 -0
  112. package/skills-library/_general/methodology/PHOENIX_REBUILD_METHODOLOGY.md +82 -11
  113. package/skills-library/_general/methodology/REVIEW_BACKTRACK_PANEL.md +140 -0
  114. package/skills-library/_general/methodology/REVIEW_FIX_LOOP.md +117 -0
  115. package/skills-library/_general/methodology/VOTING_VERDICT_ARBITRATION.md +155 -0
  116. package/skills-library/_general/methodology/ZERO_FRICTION_CLI_SETUP.md +2 -2
  117. package/skills-library/_general/methodology/dead-code-activation.md +123 -0
  118. package/skills-library/_general/methodology/debug-swarm-researcher-escape-hatch.md +240 -240
  119. package/skills-library/_general/methodology/shell-autonomous-loop-fixplan.md +1 -1
  120. package/skills-library/_general/patterns-standards/GOF_DESIGN_PATTERNS_FOR_AI_AGENTS.md +5 -5
  121. package/skills-library/_general/patterns-standards/cascading-failure-diagnosis.md +119 -0
  122. package/skills-library/_general/patterns-standards/domain-specific-layout-algorithms.md +209 -0
  123. package/skills-library/_general/patterns-standards/python-desktop-app-architecture.md +399 -0
  124. package/skills-library/_general/patterns-standards/realtime-monitoring-dashboard.md +457 -0
  125. package/skills-library/_general/patterns-standards/togglable-processing-pipeline.md +169 -0
  126. package/skills-library/_general/performance/liveclock-extraction.md +112 -0
  127. package/skills-library/_general/performance/ref-based-canvas-animation.md +117 -0
  128. package/skills-library/_general/performance/use-visible-interval.md +131 -0
  129. package/skills-library/_general/testing/playwright-firefox-withcredentials-auth-issue.md +104 -0
  130. package/skills-library/_quarantine/README.md +30 -0
  131. package/skills-library/api-patterns/BROADCAST_SCHEDULER_SHARED_EXECUTE_FUNCTION.md +150 -0
  132. package/skills-library/api-patterns/ERROR_RESPONSE_STANDARDS.md +145 -0
  133. package/skills-library/api-patterns/EXPRESS_ROUTE_ORDERING_MIDDLEWARE_INTERCEPTION.md +326 -0
  134. package/skills-library/api-patterns/PAGINATION_PATTERNS.md +137 -0
  135. package/skills-library/api-patterns/PODCAST_PROGRESS_TRACKING_THREE_ROOT_CAUSES.md +277 -0
  136. package/skills-library/api-patterns/RATE_LIMITING_TOGGLE.md +155 -0
  137. package/skills-library/api-patterns/graphql-content-queries.md +708 -0
  138. package/skills-library/appointment-scheduler-design.md +423 -0
  139. package/skills-library/automation/AUTO_POPULATE_COMPLETE_GUIDE.md +631 -0
  140. package/skills-library/automation/CC_WORKFLOW_STUDIO.md +83 -0
  141. package/skills-library/automation/CLAUDE_CODE_SWARM_MODE.md +95 -0
  142. package/skills-library/automation/DAEMON_TRIGGER_FILE_IPC.md +195 -0
  143. package/skills-library/automation/scheduled-content-publishing.md +608 -0
  144. package/skills-library/awesome-workflows/Blogging-Platform-Instructions/view_commands.md +25 -0
  145. package/skills-library/awesome-workflows/CREDENTIAL-SECURITY-WORKFLOW.md +109 -0
  146. package/skills-library/awesome-workflows/DEBUGGING-WORKFLOW.md +124 -0
  147. package/skills-library/awesome-workflows/Design-Review-Workflow/README.md +31 -0
  148. package/skills-library/awesome-workflows/Design-Review-Workflow/design-principles-example.md +129 -0
  149. package/skills-library/awesome-workflows/Design-Review-Workflow/design-review-agent.md +107 -0
  150. package/skills-library/awesome-workflows/Design-Review-Workflow/design-review-claude-md-snippet.md +24 -0
  151. package/skills-library/awesome-workflows/Design-Review-Workflow/design-review-slash-command.md +38 -0
  152. package/skills-library/awesome-workflows/PARALLEL-RESEARCH-WORKFLOW.md +89 -0
  153. package/skills-library/awesome-workflows/PHASE-EXECUTION-WORKFLOW.md +97 -0
  154. package/skills-library/awesome-workflows/SESSION-HANDOFF-WORKFLOW.md +116 -0
  155. package/skills-library/cms-patterns/content-branch-preview.md +515 -0
  156. package/skills-library/cms-patterns/inline-visual-editing.md +666 -0
  157. package/skills-library/cms-patterns/mdx-component-content.md +649 -0
  158. package/skills-library/cms-patterns/media-manager-abstraction.md +827 -0
  159. package/skills-library/cms-patterns/schema-driven-form-generator.md +838 -0
  160. package/skills-library/complexity-metrics/complexity-divider.md +707 -0
  161. package/skills-library/complexity-metrics/work-with-complexity.md +193 -0
  162. package/skills-library/creative-multimedia/animation-stack-guide.md +577 -0
  163. package/skills-library/creative-multimedia/audio-enhancement-pipeline.md +625 -0
  164. package/skills-library/creative-multimedia/content-repurposing-pipeline.md +1146 -0
  165. package/skills-library/creative-multimedia/data-visualization-generator.md +862 -0
  166. package/skills-library/creative-multimedia/doc-to-podcast-pipeline.md +2184 -0
  167. package/skills-library/creative-multimedia/ffmpeg-command-generator.md +405 -0
  168. package/skills-library/creative-multimedia/image-optimization-pipeline.md +605 -0
  169. package/skills-library/creative-multimedia/multi-format-content-generator.md +1759 -0
  170. package/skills-library/creative-multimedia/og-image-generator.md +635 -0
  171. package/skills-library/creative-multimedia/podcast-audio-composition.md +1355 -0
  172. package/skills-library/creative-multimedia/podcast-quality-evaluation.md +1452 -0
  173. package/skills-library/creative-multimedia/podcast-script-generation.md +1841 -0
  174. package/skills-library/creative-multimedia/svg-generation.md +750 -0
  175. package/skills-library/creative-multimedia/text-to-speech-provider-selector.md +1414 -0
  176. package/skills-library/creative-multimedia/transcription-pipeline-selector.md +677 -0
  177. package/skills-library/creative-multimedia/video-streaming-setup.md +559 -0
  178. package/skills-library/database-solutions/AI_RESPONSE_DATABASE_CACHING.md +520 -0
  179. package/skills-library/database-solutions/CONDITIONAL_SQL_MIGRATION_PATTERN.md +119 -0
  180. package/skills-library/database-solutions/DATABASE_COLUMN_NAME_MISMATCH.md +393 -0
  181. package/skills-library/database-solutions/DATABASE_SCHEMA.md +394 -0
  182. package/skills-library/database-solutions/DATABASE_SCHEMA_VERIFICATION_GUIDE.md +348 -0
  183. package/skills-library/database-solutions/DATABASE_STRATEGY.md +71 -0
  184. package/skills-library/database-solutions/ES_MODULE_SEED_SCRIPT_PATTERN.md +52 -0
  185. package/skills-library/database-solutions/MIGRATION_GUIDE.md +3 -0
  186. package/skills-library/database-solutions/PLPGSQL_VARIABLE_CONFLICT_FIX.md +208 -0
  187. package/skills-library/database-solutions/POSTGRESQL_JSONB_DOUBLE_STRINGIFY_FIX.md +245 -0
  188. package/skills-library/database-solutions/POSTGRESQL_LICENSE_TABLE_DESIGN.md +393 -0
  189. package/skills-library/database-solutions/POSTGRESQL_UUID_DOCUMENT_RAG_DUAL_SCOPE.md +732 -0
  190. package/skills-library/database-solutions/POSTGRES_SQL_TEMPLATE_BINDING_ERROR.md +240 -0
  191. package/skills-library/database-solutions/PRISMA_DB_PUSH_DATA_LOSS_PREVENTION.md +141 -0
  192. package/skills-library/database-solutions/PRODUCTION_QUERY_OPTIMIZATION_RESTART_FIX.md +389 -0
  193. package/skills-library/database-solutions/RLS_SECURITY_GUIDE.md +107 -0
  194. package/skills-library/database-solutions/SCHEMA_ENHANCEMENTS_GUIDE.md +373 -0
  195. package/skills-library/database-solutions/SCHEMA_MIGRATION_GUIDE.md +368 -0
  196. package/skills-library/database-solutions/SCHEMA_VERIFICATION_QUICK_REFERENCE.md +104 -0
  197. package/skills-library/database-solutions/ai-erd-generator.md +1213 -0
  198. package/skills-library/database-solutions/content-publishing-states.md +631 -0
  199. package/skills-library/database-solutions/database-schema-designer.md +522 -0
  200. package/skills-library/database-solutions/er-diagram-components.md +569 -0
  201. package/skills-library/database-solutions/er-to-ddl-mapping.md +1405 -0
  202. package/skills-library/database-solutions/erd-creator-textbook-research.md +433 -0
  203. package/skills-library/database-solutions/erd-react-flow-architecture.md +1965 -0
  204. package/skills-library/database-solutions/mariadb-aggregate-function-replacement.md +145 -0
  205. package/skills-library/database-solutions/normalization-validator.md +778 -0
  206. package/skills-library/database-solutions/postgres-full-text-search-content.md +494 -0
  207. package/skills-library/database-solutions/postgresql-to-mysql-runtime-translation.md +286 -0
  208. package/skills-library/database-solutions/regex-alternation-ordering-sql-types.md +92 -0
  209. package/skills-library/database-solutions/reserved-word-context-aware-quoting.md +142 -0
  210. package/skills-library/database-solutions/sql-ddl-generator.md +756 -0
  211. package/skills-library/database-solutions/supabase-connection-pooler-fix.md +102 -0
  212. package/skills-library/deployment-security/CPANEL_NODE_DEPLOYMENT.md +166 -0
  213. package/skills-library/deployment-security/DEPLOYMENT.md +275 -0
  214. package/skills-library/deployment-security/DEPLOYMENT_CHECKLIST.md +363 -0
  215. package/skills-library/deployment-security/DEPLOYMENT_PLAN.md +669 -0
  216. package/skills-library/deployment-security/KNEX_DATABASE_ABSTRACTION.md +444 -0
  217. package/skills-library/deployment-security/LICENSE_KEY_SYSTEM.md +206 -0
  218. package/skills-library/deployment-security/NODE18_DEPENDENCY_COMPATIBILITY.md +284 -0
  219. package/skills-library/deployment-security/PHP_INSTALLER_WIZARD_GUIDE.md +315 -0
  220. package/skills-library/deployment-security/PM2_ENVIRONMENT_VARIABLE_CACHING.md +256 -0
  221. package/skills-library/deployment-security/PM2_MEMORY_EXHAUSTION_FIX.md +370 -0
  222. package/skills-library/deployment-security/PRODUCTION_DEPLOYMENT_GUIDE.md +592 -0
  223. package/skills-library/deployment-security/PRODUCTION_HARDENING_DOCUMENTATION.md +307 -0
  224. package/skills-library/deployment-security/PRODUCTION_RECOVERY_CHERRY_PICK_PATTERN.md +202 -0
  225. package/skills-library/deployment-security/PYINSTALLER_CUDA_WHISPER_BUNDLING.md +236 -0
  226. package/skills-library/deployment-security/SECURITY.md +41 -0
  227. package/skills-library/deployment-security/SMTP_SSL_HOSTNAME_MISMATCH_SHARED_HOSTING.md +220 -0
  228. package/skills-library/deployment-security/SPA_SEO_OPTIMIZATION_CPANEL.md +200 -0
  229. package/skills-library/deployment-security/SUPABASE_EDGE_FUNCTIONS.md +338 -0
  230. package/skills-library/deployment-security/VERCEL_GITHUB_DEPLOYMENT_GUIDE.md +858 -0
  231. package/skills-library/deployment-security/VPS_DEPLOYMENT_READINESS.md +356 -0
  232. package/skills-library/deployment-security/deployment-changes-not-applying.md +241 -0
  233. package/skills-library/deployment-security/env-file-management-production-local.md +203 -0
  234. package/skills-library/deployment-security/express-secure-file-downloads.md +413 -0
  235. package/skills-library/deployment-security/react-production-deployment-desktop-guide.md +2011 -0
  236. package/skills-library/deployment-security/self-hosted-supabase-coolify-guide.md +1684 -0
  237. package/skills-library/deployment-security/unique-features-ai-strategy-plaid-security.md +1613 -0
  238. package/skills-library/deployment-security/vps-deployment.md +135 -0
  239. package/skills-library/document-processing/WORD_EXPORT_MARKDOWN_FORMATTING.md +482 -0
  240. package/skills-library/document-processing/document-ai-landingai-integration.md +677 -0
  241. package/skills-library/document-processing/express-secure-file-downloads-mern.md +413 -0
  242. package/skills-library/document-processing/express-secure-file-downloads.md +413 -0
  243. package/skills-library/document-processing/md-to-word-converter.md +318 -0
  244. package/skills-library/document-processing/pdf-forms-integration/README.md +101 -0
  245. package/skills-library/document-processing/pdf-forms-integration/SKILL.md +662 -0
  246. package/skills-library/ecommerce/ADMIN_PRODUCTS_GUIDE.md +428 -0
  247. package/skills-library/ecommerce/ECOMMERCE_API_REFERENCE.md +776 -0
  248. package/skills-library/ecommerce/ECOMMERCE_COMPLETION_SUMMARY.md +673 -0
  249. package/skills-library/ecommerce/ECOMMERCE_IMPLEMENTATION_GUIDE.md +729 -0
  250. package/skills-library/ecommerce/ECOMMERCE_QUICK_REFERENCE.md +521 -0
  251. package/skills-library/ecommerce/ECOMMERCE_TESTING_CHECKLIST.md +565 -0
  252. package/skills-library/ecommerce/ECOMMERCE_WORKFLOW_GUIDE.md +1059 -0
  253. package/skills-library/ecommerce/PRODUCT_CREATION_EXPANDED.md +522 -0
  254. package/skills-library/ecommerce/agentic-commerce-protocol.md +203 -0
  255. package/skills-library/ecommerce/cart-abandonment-recovery.md +236 -0
  256. package/skills-library/ecommerce/cart-architecture-patterns.md +300 -0
  257. package/skills-library/ecommerce/cart-item-count-indicator.md +264 -0
  258. package/skills-library/ecommerce/checkout-ux-conversion.md +227 -0
  259. package/skills-library/ecommerce/composable-commerce-selection.md +166 -0
  260. package/skills-library/ecommerce/ecommerce-analytics-patterns.md +167 -0
  261. package/skills-library/ecommerce/fraud-detection-patterns.md +179 -0
  262. package/skills-library/ecommerce/inventory-stock-management.md +270 -0
  263. package/skills-library/ecommerce/order-saga-state-machine.md +336 -0
  264. package/skills-library/ecommerce/payment-provider-abstraction.md +245 -0
  265. package/skills-library/ecommerce/pci-compliance-checklist.md +192 -0
  266. package/skills-library/ecommerce/refund-chargeback-handling.md +177 -0
  267. package/skills-library/ecommerce/shipping-carrier-integration.md +218 -0
  268. package/skills-library/ecommerce/webhook-idempotency-patterns.md +253 -0
  269. package/skills-library/excalidraw-diagrams/.github/workflows/ci.yml +558 -0
  270. package/skills-library/excalidraw-diagrams/.github/workflows/prompt-gallery.yml +448 -0
  271. package/skills-library/excalidraw-diagrams/.github/workflows/release.yml +42 -0
  272. package/skills-library/excalidraw-diagrams/.github/workflows/test-reusable-ci.yml +25 -0
  273. package/skills-library/excalidraw-diagrams/CLAUDE.md +57 -0
  274. package/skills-library/excalidraw-diagrams/LICENSE +21 -0
  275. package/skills-library/excalidraw-diagrams/README.md +178 -0
  276. package/skills-library/excalidraw-diagrams/SKILL.md +715 -0
  277. package/skills-library/form-solutions/BUTTON_TYPE_FORM_SUBMISSION.md +336 -0
  278. package/skills-library/form-solutions/FILLABLE_PDF_IMPLEMENTATION.md +226 -0
  279. package/skills-library/form-solutions/SURVEYJS_QUESTIONNAIRE_SYSTEM.md +367 -0
  280. package/skills-library/form-solutions/tiptap-minimal-setup.md +690 -0
  281. package/skills-library/frontend/scholarly-classification-bubble-map.md +149 -0
  282. package/skills-library/infrastructure/ci-cd-pipeline-builder.md +517 -0
  283. package/skills-library/infrastructure/observability-designer.md +264 -0
  284. package/skills-library/infrastructure/performance-profiler.md +621 -0
  285. package/skills-library/installer-wizard-patterns.md +249 -0
  286. package/skills-library/integrations/CLAUDE_CODE_TOKEN_ANALYTICS.md +160 -0
  287. package/skills-library/integrations/CONFIGURABLE_AI_PROVIDER_SELECTION.md +728 -0
  288. package/skills-library/integrations/SOCKET_IO_BROADCAST_ALL_VS_ROOM.md +141 -0
  289. package/skills-library/integrations/VIRTUAL_MEETINGS_IMPLEMENTATION.md +374 -0
  290. package/skills-library/integrations/WORDPRESS_LEARNDASH_DATA_RECOVERY.md +53 -0
  291. package/skills-library/integrations/YOUTUBE_API_SETUP.md +141 -0
  292. package/skills-library/integrations/YOUTUBE_BOOKMARKING_EXPLANATION.md +252 -0
  293. package/skills-library/integrations/YOUTUBE_BOOKMARKING_SOLUTION.md +268 -0
  294. package/skills-library/integrations/YOUTUBE_OAUTH_SETUP_GUIDE.md +200 -0
  295. package/skills-library/integrations/YOUTUBE_VIDEO_FIX_COMPLETE.md +192 -0
  296. package/skills-library/integrations/ai-ml/GEMINI_AI_RAG_PIPELINE_COMPLETE_GUIDE.md +195 -0
  297. package/skills-library/integrations/ai-ml/GEMINI_IMAGE_GENERATION_SETUP.md +64 -0
  298. package/skills-library/integrations/cloudflare/cloudflare-turnstile-debugging.md +202 -0
  299. package/skills-library/integrations/cloudflare/cloudflare-turnstile-implementation.md +476 -0
  300. package/skills-library/integrations/cloudflare-turnstile-debugging.md +202 -0
  301. package/skills-library/integrations/cloudflare-turnstile-implementation.md +476 -0
  302. package/skills-library/integrations/ghost-creator-monetization-pattern.md +454 -0
  303. package/skills-library/integrations/headless-cms-architecture.md +484 -0
  304. package/skills-library/integrations/headless-cms-stack-selection.md +183 -0
  305. package/skills-library/integrations/payload-cms-patterns.md +674 -0
  306. package/skills-library/integrations/realtimestt-openwakeword-cuda-windows.md +229 -0
  307. package/skills-library/integrations/rss-podcast-integration.md +300 -0
  308. package/skills-library/integrations/wordpress/WORDPRESS_LEARNDASH_DATA_RECOVERY.md +53 -0
  309. package/skills-library/integrations/youtube/YOUTUBE_API_SETUP.md +141 -0
  310. package/skills-library/integrations/youtube/YOUTUBE_BOOKMARKING_EXPLANATION.md +252 -0
  311. package/skills-library/integrations/youtube/YOUTUBE_BOOKMARKING_SOLUTION.md +268 -0
  312. package/skills-library/integrations/youtube/YOUTUBE_OAUTH_SETUP_GUIDE.md +200 -0
  313. package/skills-library/integrations/youtube/YOUTUBE_VIDEO_FIX_COMPLETE.md +192 -0
  314. package/skills-library/marketing/campaign-analytics.md +97 -0
  315. package/skills-library/marketing/content-creator.md +105 -0
  316. package/skills-library/marketing/marketing-strategy-pmm.md +94 -0
  317. package/skills-library/marketing/social-media-analyzer.md +81 -0
  318. package/skills-library/methodology/ADVANCED_ORCHESTRATION_PATTERNS.md +401 -0
  319. package/skills-library/methodology/AGENT_SELF_IMPROVEMENT_LOOP.md +179 -0
  320. package/skills-library/methodology/BREATH_BASED_PARALLEL_EXECUTION.md +1 -1
  321. package/skills-library/methodology/CLEANSING_CYCLE.md +358 -0
  322. package/skills-library/methodology/CONFIDENCE_ANNOTATION_PATTERN.md +143 -0
  323. package/skills-library/methodology/CRITICAL_PATTERNS_DOCUMENTATION_COMPLETE.md +204 -0
  324. package/skills-library/methodology/DELIVERABLES_SUMMARY.md +341 -0
  325. package/skills-library/methodology/DIFFICULTY_AWARE_AGENT_ROUTING.md +252 -0
  326. package/skills-library/methodology/EVOLUTIONARY_SKILL_SYNTHESIS.md +219 -0
  327. package/skills-library/methodology/GLOMERULUS_DECISION_GATE.md +223 -0
  328. package/skills-library/methodology/HIBERNATION_SYSTEM.md +231 -0
  329. package/skills-library/methodology/INSTRUMENTATION_OVER_RESTRICTION.md +192 -0
  330. package/skills-library/methodology/MASTER_COMPLETION_SUMMARY.md +444 -0
  331. package/skills-library/methodology/MASTER_SESSION_COMPLETION.md +743 -0
  332. package/skills-library/methodology/MERN_QUICK_REFERENCE.md +358 -0
  333. package/skills-library/methodology/ORGAN_AGENT_MAPPING.md +177 -0
  334. package/skills-library/methodology/PARALLEL_WAVE_BASED_REFACTORING.md +440 -0
  335. package/skills-library/methodology/QUICK_REFERENCE.md +358 -0
  336. package/skills-library/methodology/SDFT_ONPOLICY_SELF_DISTILLATION.md +186 -0
  337. package/skills-library/methodology/SELF_QUESTIONING_TASK_GENERATION.md +270 -0
  338. package/skills-library/methodology/SESSION_COMPLETION_SUMMARY.md +304 -0
  339. package/skills-library/methodology/SESSION_SUMMARY.md +432 -0
  340. package/skills-library/methodology/WARRIOR_WORKFLOW_DEBUGGING_PROTOCOL.md +252 -0
  341. package/skills-library/methodology/tech-debt-tracker.md +570 -0
  342. package/skills-library/parallel-debug/SKILL.md +60 -0
  343. package/skills-library/patterns-standards/API_PATTERN_FIX_SUMMARY.md +236 -0
  344. package/skills-library/patterns-standards/BATCH_OPERATIONS_WITH_PROGRESS_MODAL.md +362 -0
  345. package/skills-library/patterns-standards/CRITICAL_CODING_PATTERNS.md +639 -0
  346. package/skills-library/patterns-standards/DARK_MODE_MODAL_VISIBILITY.md +258 -0
  347. package/skills-library/patterns-standards/ERROR_RESILIENCE_IMPLEMENTATION.md +375 -0
  348. package/skills-library/patterns-standards/ES_MODULE_IMPORT_HOISTING_DOTENV.md +298 -0
  349. package/skills-library/patterns-standards/NESTED_BACKDROP_FILTER_CSS_ARTIFACT_FIX.md +76 -0
  350. package/skills-library/patterns-standards/ORDERED_DETECTOR_PIPELINE_GRACEFUL_FALLBACK.md +333 -0
  351. package/skills-library/patterns-standards/PHASE_IMPORT_ERROR_DEBUGGING.md +271 -0
  352. package/skills-library/patterns-standards/PYNPUT_GLOBAL_HOTKEY_VK_MATCHING.md +252 -0
  353. package/skills-library/patterns-standards/REACT_USEEFFECT_CASCADE_RESET_FIX.md +132 -0
  354. package/skills-library/patterns-standards/SUBMENU_HOVER_DROPDOWN_PATTERN.md +225 -0
  355. package/skills-library/patterns-standards/TAILWIND_TEXT_VISIBILITY_OVERRIDE.md +322 -0
  356. package/skills-library/patterns-standards/THEME_AWARE_CSS_VARIABLES_PATTERN.md +209 -0
  357. package/skills-library/patterns-standards/THEME_USER_OBJECT_PROPERTY_NAMING.md +194 -0
  358. package/skills-library/patterns-standards/TOOLTIP_BLOCKING_CLICKS_FIX.md +267 -0
  359. package/skills-library/patterns-standards/claude-code-plugin-structure.md +235 -0
  360. package/skills-library/patterns-standards/react-i18next-setup.md +429 -0
  361. package/skills-library/patterns-standards/thesys-c1-generative-ui-integration.md +967 -0
  362. package/skills-library/plugin-development/CLAUDE_CODE_COMMAND_REGISTRATION_SILENT_FAILURE.md +315 -0
  363. package/skills-library/plugin-development/plugin-command-namespace-vs-global.md +390 -0
  364. package/skills-library/plugin-development/plugin-doc-auto-generation.md +172 -0
  365. package/skills-library/security/GITHUB_REPO_SECURITY_AUDIT.md +115 -0
  366. package/skills-library/security/admin-deletion-safety.md +396 -0
  367. package/skills-library/security/application-vuln-patterns.md +477 -0
  368. package/skills-library/security/env-secrets-manager.md +686 -0
  369. package/skills-library/security/secure-ai-application-templates.md +347 -0
  370. package/skills-library/security/sql-injection-prevention-postgresjs.md +151 -0
  371. package/skills-library/supabase-connection-pooler-fix.md +102 -0
  372. package/skills-library/system-context/POWERSHELL_BASH_INTEROP.md +82 -0
  373. package/skills-library/system-context/SERVICE_LIFECYCLE_MANAGEMENT.md +119 -0
  374. package/skills-library/system-context/SKILL.md +40 -0
  375. package/skills-library/system-context/WINDOWS_DEV_ENVIRONMENT.md +73 -0
  376. package/skills-library/testing/E2E_PLAYWRIGHT_PATTERNS.md +99 -0
  377. package/skills-library/testing/INTEGRATION_TEST_STRATEGY.md +82 -0
  378. package/skills-library/testing/RED_GREEN_BUGFIX_GATE.md +203 -0
  379. package/skills-library/testing/TEST_DATA_MANAGEMENT.md +69 -0
  380. package/skills-library/testing/VITEST_UNIT_TEST_PATTERNS.md +75 -0
  381. package/skills-library/testing/playwright-api-security-tests.md +202 -0
  382. package/skills-library/toolbox/SKILL.md +84 -0
  383. package/skills-library/toolbox/code-graph-and-web-scraping-mcps.md +237 -0
  384. package/skills-library/ui-ux-pro-max/ACCESSIBILITY_ESSENTIALS.md +115 -0
  385. package/skills-library/ui-ux-pro-max/DESIGN_SYSTEM_SCAFFOLDING.md +133 -0
  386. package/skills-library/ui-ux-pro-max/RESPONSIVE_LAYOUT_PATTERNS.md +119 -0
  387. package/skills-library/ui-ux-pro-max/SKILL.md +386 -0
  388. package/skills-library/ui-ux-pro-max/data/charts.csv +26 -0
  389. package/skills-library/ui-ux-pro-max/data/colors.csv +97 -0
  390. package/skills-library/ui-ux-pro-max/data/icons.csv +101 -0
  391. package/skills-library/ui-ux-pro-max/data/landing.csv +31 -0
  392. package/skills-library/ui-ux-pro-max/data/products.csv +97 -0
  393. package/skills-library/ui-ux-pro-max/data/react-performance.csv +45 -0
  394. package/skills-library/ui-ux-pro-max/data/stacks/astro.csv +54 -0
  395. package/skills-library/ui-ux-pro-max/data/stacks/flutter.csv +53 -0
  396. package/skills-library/ui-ux-pro-max/data/stacks/html-tailwind.csv +56 -0
  397. package/skills-library/ui-ux-pro-max/data/stacks/jetpack-compose.csv +53 -0
  398. package/skills-library/ui-ux-pro-max/data/stacks/nextjs.csv +53 -0
  399. package/skills-library/ui-ux-pro-max/data/stacks/nuxt-ui.csv +51 -0
  400. package/skills-library/ui-ux-pro-max/data/stacks/nuxtjs.csv +59 -0
  401. package/skills-library/ui-ux-pro-max/data/stacks/react-native.csv +52 -0
  402. package/skills-library/ui-ux-pro-max/data/stacks/react.csv +54 -0
  403. package/skills-library/ui-ux-pro-max/data/stacks/shadcn.csv +61 -0
  404. package/skills-library/ui-ux-pro-max/data/stacks/svelte.csv +54 -0
  405. package/skills-library/ui-ux-pro-max/data/stacks/swiftui.csv +51 -0
  406. package/skills-library/ui-ux-pro-max/data/stacks/vue.csv +50 -0
  407. package/skills-library/ui-ux-pro-max/data/styles.csv +68 -0
  408. package/skills-library/ui-ux-pro-max/data/typography.csv +58 -0
  409. package/skills-library/ui-ux-pro-max/data/ui-reasoning.csv +101 -0
  410. package/skills-library/ui-ux-pro-max/data/ux-guidelines.csv +100 -0
  411. package/skills-library/ui-ux-pro-max/data/web-interface.csv +31 -0
  412. package/skills-library/wordpress-style-theme-components.md +1526 -0
  413. package/templates/ASSUMPTIONS.md +1 -1
  414. package/templates/DECISION_LOG.md +0 -1
  415. package/templates/phase-prompt.md +1 -1
  416. package/templates/phoenix-comparison.md +6 -6
  417. package/templates/skill-api-integration.md +106 -0
  418. package/templates/skill-architecture-pattern.md +92 -0
  419. package/templates/skill-debug-pattern.md +98 -0
  420. package/templates/skill-devops-recipe.md +107 -0
  421. package/templates/skill-general.md +65 -0
  422. package/templates/skill-ui-component.md +113 -0
  423. package/version.json +7 -3
  424. package/workflows/handoff-session.md +2 -2
  425. package/workflows/new-project.md +2 -2
  426. package/workflows/plan-phase.md +1 -1
  427. package/.claude-plugin/plugin.json +0 -64
  428. package/skills-library/_general/methodology/LIVE_BREADCRUMB_PROTOCOL.md +0 -242
  429. package/skills-library/_general/methodology/llm-judge-memory-crud.md +0 -241
  430. package/skills-library/methodology/REFLEXION_MEMORY_PATTERN.md +0 -183
  431. package/skills-library/methodology/RESEARCH_BACKED_WORKFLOW_UPGRADE.md +0 -263
  432. package/skills-library/methodology/SABBATH_REST_PATTERN.md +0 -267
  433. package/skills-library/methodology/STONE_AND_SCAFFOLD.md +0 -220
  434. package/skills-library/specialists/api-architecture/api-designer.md +0 -49
  435. package/skills-library/specialists/api-architecture/graphql-architect.md +0 -49
  436. package/skills-library/specialists/api-architecture/mcp-developer.md +0 -51
  437. package/skills-library/specialists/api-architecture/microservices-architect.md +0 -50
  438. package/skills-library/specialists/api-architecture/websocket-engineer.md +0 -48
  439. package/skills-library/specialists/backend/django-expert.md +0 -52
  440. package/skills-library/specialists/backend/fastapi-expert.md +0 -52
  441. package/skills-library/specialists/backend/laravel-specialist.md +0 -52
  442. package/skills-library/specialists/backend/nestjs-expert.md +0 -51
  443. package/skills-library/specialists/backend/rails-expert.md +0 -53
  444. package/skills-library/specialists/backend/spring-boot-engineer.md +0 -56
  445. package/skills-library/specialists/data-ml/fine-tuning-expert.md +0 -48
  446. package/skills-library/specialists/data-ml/ml-pipeline.md +0 -47
  447. package/skills-library/specialists/data-ml/pandas-pro.md +0 -47
  448. package/skills-library/specialists/data-ml/rag-architect.md +0 -51
  449. package/skills-library/specialists/data-ml/spark-engineer.md +0 -47
  450. package/skills-library/specialists/frontend/angular-architect.md +0 -52
  451. package/skills-library/specialists/frontend/flutter-expert.md +0 -51
  452. package/skills-library/specialists/frontend/nextjs-developer.md +0 -54
  453. package/skills-library/specialists/frontend/react-native-expert.md +0 -50
  454. package/skills-library/specialists/frontend/vue-expert.md +0 -51
  455. package/skills-library/specialists/infrastructure/chaos-engineer.md +0 -74
  456. package/skills-library/specialists/infrastructure/cloud-architect.md +0 -70
  457. package/skills-library/specialists/infrastructure/database-optimizer.md +0 -64
  458. package/skills-library/specialists/infrastructure/devops-engineer.md +0 -70
  459. package/skills-library/specialists/infrastructure/kubernetes-specialist.md +0 -52
  460. package/skills-library/specialists/infrastructure/monitoring-expert.md +0 -70
  461. package/skills-library/specialists/infrastructure/sre-engineer.md +0 -70
  462. package/skills-library/specialists/infrastructure/terraform-engineer.md +0 -51
  463. package/skills-library/specialists/languages/cpp-pro.md +0 -74
  464. package/skills-library/specialists/languages/csharp-developer.md +0 -69
  465. package/skills-library/specialists/languages/dotnet-core-expert.md +0 -54
  466. package/skills-library/specialists/languages/golang-pro.md +0 -51
  467. package/skills-library/specialists/languages/java-architect.md +0 -49
  468. package/skills-library/specialists/languages/javascript-pro.md +0 -68
  469. package/skills-library/specialists/languages/kotlin-specialist.md +0 -68
  470. package/skills-library/specialists/languages/php-pro.md +0 -49
  471. package/skills-library/specialists/languages/python-pro.md +0 -52
  472. package/skills-library/specialists/languages/react-expert.md +0 -51
  473. package/skills-library/specialists/languages/rust-engineer.md +0 -50
  474. package/skills-library/specialists/languages/sql-pro.md +0 -56
  475. package/skills-library/specialists/languages/swift-expert.md +0 -69
  476. package/skills-library/specialists/languages/typescript-pro.md +0 -51
  477. package/skills-library/specialists/platform/atlassian-mcp.md +0 -52
  478. package/skills-library/specialists/platform/embedded-systems.md +0 -53
  479. package/skills-library/specialists/platform/game-developer.md +0 -53
  480. package/skills-library/specialists/platform/salesforce-developer.md +0 -53
  481. package/skills-library/specialists/platform/shopify-expert.md +0 -49
  482. package/skills-library/specialists/platform/wordpress-pro.md +0 -49
  483. package/skills-library/specialists/quality/browser-use-expert.md +0 -210
  484. package/skills-library/specialists/quality/code-documenter.md +0 -51
  485. package/skills-library/specialists/quality/code-reviewer.md +0 -67
  486. package/skills-library/specialists/quality/debugging-wizard.md +0 -51
  487. package/skills-library/specialists/quality/fullstack-guardian.md +0 -51
  488. package/skills-library/specialists/quality/legacy-modernizer.md +0 -50
  489. package/skills-library/specialists/quality/playwright-expert.md +0 -65
  490. package/skills-library/specialists/quality/spec-miner.md +0 -56
  491. package/skills-library/specialists/quality/test-master.md +0 -65
  492. package/skills-library/specialists/security/secure-code-guardian.md +0 -55
  493. package/skills-library/specialists/security/security-reviewer.md +0 -53
  494. package/skills-library/specialists/workflow/architecture-designer.md +0 -53
  495. package/skills-library/specialists/workflow/cli-developer.md +0 -70
  496. package/skills-library/specialists/workflow/feature-forge.md +0 -65
  497. package/skills-library/specialists/workflow/prompt-engineer.md +0 -54
  498. package/skills-library/specialists/workflow/the-fool.md +0 -62
  499. /package/skills-library/{performance → _general/performance}/cache-augmented-generation.md +0 -0
  500. /package/skills-library/{debugging → parallel-debug}/FAILURE_TAXONOMY_CLASSIFICATION.md +0 -0
  501. /package/skills-library/{debugging → parallel-debug}/THREE_AGENT_HYPOTHESIS_DEBUGGING.md +0 -0
@@ -0,0 +1,1405 @@
1
+ ---
2
+ name: er-to-ddl-mapping
3
+ category: database-solutions
4
+ version: 1.0.0
5
+ contributed: 2026-03-09
6
+ contributor: fire-research
7
+ last_updated: 2026-03-09
8
+ tags: [erd, ddl, sql, schema-generation, database-design, mapping-rules]
9
+ difficulty: hard
10
+ ---
11
+
12
+ # ER-to-DDL Mapping
13
+
14
+
15
+ ## Problem
16
+
17
+ Converting a visual ER diagram into executable SQL DDL requires deterministic mapping rules that handle every construct in the ER model: strong entities, weak entities, all relationship cardinalities, multivalued attributes, composite attributes, derived attributes, participation constraints, and n-ary relationships. The mapping must produce correct, dialect-specific SQL for PostgreSQL, MySQL, and SQLite while preserving referential integrity through appropriate FK actions. Ad-hoc or intuition-based conversion leads to missing junction tables, incorrect cascade rules, broken composite keys, and normalization violations.
18
+
19
+ This skill encodes the complete algorithm: the 7 canonical mapping rules, the generation pipeline, multi-database type mapping, FK action selection, AI-enhanced generation patterns, and a full TypeScript implementation suitable for an ERD Creator tool.
20
+
21
+ ---
22
+
23
+ ## The 7 Canonical Mapping Rules
24
+
25
+ ### Rule 1: Strong Entity --> Table
26
+
27
+ Every strong entity becomes its own table. This is the foundation of the entire mapping.
28
+
29
+ **Attribute handling:**
30
+ - **Simple attributes** --> columns with appropriate data types
31
+ - **Key attribute** --> PRIMARY KEY constraint
32
+ - **Composite attributes** --> flatten to leaf-level columns (do NOT create a column for the parent)
33
+ - **Multivalued attributes** --> separate table (see Rule 7)
34
+ - **Derived attributes** --> skip (compute at query time) OR use generated columns
35
+
36
+ ```sql
37
+ -- Entity: Employee
38
+ -- Attributes: emp_id (key), name (composite: first, last), age (derived from dob)
39
+ CREATE TABLE employees (
40
+ emp_id INTEGER PRIMARY KEY,
41
+ first_name VARCHAR(50) NOT NULL, -- flattened from composite "name"
42
+ last_name VARCHAR(50) NOT NULL, -- flattened from composite "name"
43
+ dob DATE NOT NULL,
44
+ salary NUMERIC(10,2),
45
+ -- age is DERIVED: skip or use generated column
46
+ age INTEGER GENERATED ALWAYS AS (
47
+ EXTRACT(YEAR FROM AGE(CURRENT_DATE, dob))
48
+ ) STORED -- PostgreSQL syntax
49
+ );
50
+ ```
51
+
52
+ **Edge cases:**
53
+ - Composite within composite: flatten recursively (Address.Street.Number becomes `street_number`)
54
+ - Multiple key attributes: composite PK unless one is designated as sole identifier
55
+ - No key attribute defined: error state -- every entity MUST have a key
56
+
57
+ ---
58
+
59
+ ### Rule 2: Weak Entity --> Table with Composite PK
60
+
61
+ A weak entity cannot exist without its identifying (owner) entity. Its table gets a composite primary key: partial key + owner's primary key. The FK to the owner MUST use `ON DELETE CASCADE`.
62
+
63
+ ```sql
64
+ -- Weak Entity: Dependent (of Employee)
65
+ -- Partial key: dependent_name
66
+ -- Owner: Employee (emp_id)
67
+ CREATE TABLE dependents (
68
+ dependent_name VARCHAR(50) NOT NULL,
69
+ emp_id INTEGER NOT NULL,
70
+ relationship VARCHAR(20),
71
+ birth_date DATE,
72
+ CONSTRAINT pk_dependent PRIMARY KEY (dependent_name, emp_id),
73
+ CONSTRAINT fk_dependent_employee FOREIGN KEY (emp_id)
74
+ REFERENCES employees(emp_id)
75
+ ON DELETE CASCADE -- MUST cascade: dependent cannot exist without employee
76
+ ON UPDATE CASCADE
77
+ );
78
+ ```
79
+
80
+ **Edge cases:**
81
+ - Weak entity owned by another weak entity: chain the PKs (all owner PKs propagate down)
82
+ - Weak entity with its own multivalued attribute: apply Rule 7 using the composite PK as FK
83
+ - Multiple identifying relationships: include all owner PKs in the composite PK
84
+
85
+ ---
86
+
87
+ ### Rule 3: 1:1 Relationship --> FK on Either Side
88
+
89
+ Add the PK of one entity as a FK in the other entity's table. The choice of which side gets the FK follows a priority:
90
+
91
+ 1. **Total participation side** gets the FK (the entity that MUST participate -- guarantees no NULL FKs)
92
+ 2. **If both total**: merge into a single table (valid when both entities always coexist)
93
+ 3. **If both partial**: put FK on the side with fewer rows, or the side more frequently queried
94
+
95
+ Relationship attributes go on the table that receives the FK.
96
+
97
+ ```sql
98
+ -- 1:1: Department (total) <--manages--> Employee (partial)
99
+ -- Every department MUST have a manager, but not every employee manages a department
100
+ -- FK goes on Department (total participation side)
101
+ ALTER TABLE departments
102
+ ADD COLUMN manager_id INTEGER NOT NULL UNIQUE, -- NOT NULL enforces total participation; UNIQUE enforces 1:1
103
+ ADD CONSTRAINT fk_dept_manager
104
+ FOREIGN KEY (manager_id) REFERENCES employees(emp_id)
105
+ ON DELETE RESTRICT; -- RESTRICT: can't delete employee who manages a department
106
+ -- Note: total participation = NOT NULL + RESTRICT. If participation is partial, use nullable + SET NULL.
107
+
108
+ -- Relationship attribute: start_date (when the employee started managing)
109
+ ALTER TABLE departments
110
+ ADD COLUMN management_start_date DATE;
111
+ ```
112
+
113
+ **Edge cases:**
114
+ - Both total with identical lifecycles: merge tables, avoid unnecessary join
115
+ - Recursive 1:1 (entity relates to itself): use a self-referencing FK with UNIQUE constraint
116
+
117
+ ---
118
+
119
+ ### Rule 4: 1:N Relationship --> FK on the "Many" Side
120
+
121
+ Add the PK of the "one" side as a FK column on the "many" side table. This is the most common relationship pattern in relational databases.
122
+
123
+ ```sql
124
+ -- 1:N: Department (one) <--works_in--> Employee (many)
125
+ -- Each employee works in one department; a department has many employees
126
+ ALTER TABLE employees
127
+ ADD COLUMN department_id INTEGER,
128
+ ADD CONSTRAINT fk_emp_dept
129
+ FOREIGN KEY (department_id) REFERENCES departments(department_id)
130
+ ON DELETE SET NULL -- employee survives if department dissolved
131
+ ON UPDATE CASCADE;
132
+
133
+ -- Always index the FK column on the many side
134
+ CREATE INDEX idx_employees_dept ON employees(department_id);
135
+ ```
136
+
137
+ **Relationship attributes** go on the many-side table:
138
+ ```sql
139
+ -- If the "works_in" relationship has a "start_date" attribute:
140
+ ALTER TABLE employees ADD COLUMN dept_start_date DATE;
141
+ ```
142
+
143
+ **Edge cases:**
144
+ - Total participation on many side: FK should be `NOT NULL`
145
+ - Partial participation on many side: FK is nullable
146
+ - Self-referencing 1:N (Employee supervises Employee): `supervisor_id INTEGER REFERENCES employees(emp_id)`
147
+
148
+ ---
149
+
150
+ ### Rule 5: M:N Relationship --> Junction/Bridge Table
151
+
152
+ Create a new junction table with a composite PK consisting of both entity PKs. This is the ONLY correct way to represent M:N in relational schema.
153
+
154
+ ```sql
155
+ -- M:N: Student <--enrolls_in--> Course
156
+ -- Relationship attributes: grade, enrollment_date
157
+ CREATE TABLE enrollments (
158
+ student_id INTEGER NOT NULL,
159
+ course_id INTEGER NOT NULL,
160
+ grade CHAR(2),
161
+ enrollment_date DATE DEFAULT CURRENT_DATE,
162
+ CONSTRAINT pk_enrollment PRIMARY KEY (student_id, course_id),
163
+ CONSTRAINT fk_enroll_student FOREIGN KEY (student_id)
164
+ REFERENCES students(student_id)
165
+ ON DELETE CASCADE, -- if student removed, remove their enrollments
166
+ CONSTRAINT fk_enroll_course FOREIGN KEY (course_id)
167
+ REFERENCES courses(course_id)
168
+ ON DELETE CASCADE -- if course removed, remove its enrollments
169
+ );
170
+
171
+ -- Index both FK columns (composite PK covers student_id; add index for course_id lookups)
172
+ CREATE INDEX idx_enrollment_course ON enrollments(course_id);
173
+ ```
174
+
175
+ **Edge cases:**
176
+ - Junction table with its own key (surrogate PK): valid when enrollments need independent identity (e.g., enrollment_id for referencing from other tables)
177
+ - M:N with attributes: attributes become columns in the junction table
178
+ - Self-referencing M:N (User friends User): junction table with two FKs to same table, add CHECK constraint to prevent self-friendship
179
+
180
+ ---
181
+
182
+ ### Rule 6: N-ary Relationship --> Junction Table with N FKs
183
+
184
+ For ternary and higher relationships, create a junction table with FKs to ALL participating entities. The PK composition depends on the cardinality constraints.
185
+
186
+ ```sql
187
+ -- Ternary: Supplier <--supplies--> Part <--for--> Project
188
+ -- A supplier provides specific parts for specific projects
189
+ CREATE TABLE supply (
190
+ supplier_id INTEGER NOT NULL,
191
+ part_id INTEGER NOT NULL,
192
+ project_id INTEGER NOT NULL,
193
+ quantity INTEGER DEFAULT 0,
194
+ unit_price NUMERIC(10,2),
195
+ CONSTRAINT pk_supply PRIMARY KEY (supplier_id, part_id, project_id),
196
+ CONSTRAINT fk_supply_supplier FOREIGN KEY (supplier_id)
197
+ REFERENCES suppliers(supplier_id) ON DELETE CASCADE,
198
+ CONSTRAINT fk_supply_part FOREIGN KEY (part_id)
199
+ REFERENCES parts(part_id) ON DELETE CASCADE,
200
+ CONSTRAINT fk_supply_project FOREIGN KEY (project_id)
201
+ REFERENCES projects(project_id) ON DELETE CASCADE
202
+ );
203
+ ```
204
+
205
+ **PK composition rules for n-ary:**
206
+ - If all sides are "many": PK = all N FKs (most common)
207
+ - If one side is "one": that FK is NOT part of the PK but has a UNIQUE constraint per combination of other FKs
208
+ - Analyze the functional dependencies to determine the minimal PK
209
+
210
+ ---
211
+
212
+ ### Rule 7: Multivalued Attribute --> Separate Table
213
+
214
+ A multivalued attribute (one that can have multiple values per entity instance) becomes its own table with a composite PK of the entity's PK + the attribute value.
215
+
216
+ ```sql
217
+ -- Employee has multivalued attribute: phone_numbers
218
+ CREATE TABLE employee_phones (
219
+ emp_id INTEGER NOT NULL,
220
+ phone_number VARCHAR(20) NOT NULL,
221
+ phone_type VARCHAR(10) DEFAULT 'work', -- optional classification
222
+ CONSTRAINT pk_emp_phone PRIMARY KEY (emp_id, phone_number),
223
+ CONSTRAINT fk_emp_phone FOREIGN KEY (emp_id)
224
+ REFERENCES employees(emp_id)
225
+ ON DELETE CASCADE -- phones deleted when employee deleted
226
+ );
227
+ ```
228
+
229
+ **Edge cases:**
230
+ - Multivalued composite attribute (e.g., previous_degrees with university + year + degree_name): all components become columns, composite PK includes entity PK + all components that form a unique combination
231
+ - Multivalued attribute of a weak entity: FK is the weak entity's composite PK
232
+
233
+ ---
234
+
235
+ ## DDL Generation Algorithm
236
+
237
+ ### Full Pseudocode
238
+
239
+ ```
240
+ ALGORITHM: ER-to-DDL Mapping
241
+ INPUT: ERDModel (entities, relationships, attributes)
242
+ OUTPUT: SQL DDL string (ordered by dependency)
243
+
244
+ Phase 1: TABLE CREATION (entities)
245
+ ────────────────────────────────────────────────────
246
+ for each strong_entity in model.entities where NOT weak:
247
+ CREATE TABLE entity.name (
248
+ for each attribute in entity.attributes:
249
+ if attribute.type == SIMPLE:
250
+ emit column: name, dataType, constraints
251
+ if attribute.type == KEY:
252
+ emit column: name, dataType, NOT NULL
253
+ collect for PRIMARY KEY
254
+ if attribute.type == COMPOSITE:
255
+ recursively flatten to leaf attributes
256
+ emit each leaf as column
257
+ if attribute.type == DERIVED:
258
+ if dialect supports GENERATED:
259
+ emit GENERATED ALWAYS AS (expression) STORED
260
+ else:
261
+ skip (compute at query time)
262
+ if attribute.type == MULTIVALUED:
263
+ defer to Phase 5
264
+ emit PRIMARY KEY constraint
265
+ )
266
+
267
+ Phase 2: WEAK ENTITY TABLES
268
+ ────────────────────────────────────────────────────
269
+ for each weak_entity in model.entities where IS weak:
270
+ identify owner_entity via identifying_relationship
271
+ CREATE TABLE weak_entity.name (
272
+ emit weak entity's own attributes as columns
273
+ emit owner_pk as FK column
274
+ PRIMARY KEY (partial_key_columns, owner_pk_column)
275
+ FOREIGN KEY (owner_pk_column) REFERENCES owner_table(owner_pk)
276
+ ON DELETE CASCADE ON UPDATE CASCADE
277
+ )
278
+
279
+ Phase 3: 1:1 RELATIONSHIPS
280
+ ────────────────────────────────────────────────────
281
+ for each relationship where cardinality == ONE_TO_ONE:
282
+ determine FK_side:
283
+ if one side has total participation: FK_side = that side
284
+ if both total: consider merging tables
285
+ if both partial: FK_side = side with fewer expected rows
286
+ ALTER TABLE FK_side
287
+ ADD COLUMN other_pk as FK (with UNIQUE constraint)
288
+ ADD relationship attributes as columns
289
+ FOREIGN KEY REFERENCES other_table
290
+ ON DELETE based on participation (CASCADE if total, SET NULL if partial)
291
+
292
+ Phase 4: 1:N RELATIONSHIPS
293
+ ────────────────────────────────────────────────────
294
+ for each relationship where cardinality == ONE_TO_MANY:
295
+ many_side = entity on the "many" end
296
+ one_side = entity on the "one" end
297
+ ALTER TABLE many_side
298
+ ADD COLUMN one_side_pk as FK
299
+ ADD relationship attributes as columns
300
+ FOREIGN KEY REFERENCES one_side_table
301
+ ON DELETE (SET NULL if partial, RESTRICT if critical, CASCADE if dependent)
302
+ CREATE INDEX on many_side(FK_column)
303
+
304
+ Phase 5: M:N RELATIONSHIPS
305
+ ────────────────────────────────────────────────────
306
+ for each relationship where cardinality == MANY_TO_MANY:
307
+ CREATE TABLE junction_name (
308
+ entity_a_pk column,
309
+ entity_b_pk column,
310
+ relationship attribute columns,
311
+ PRIMARY KEY (entity_a_pk, entity_b_pk),
312
+ FOREIGN KEY (entity_a_pk) REFERENCES entity_a ON DELETE CASCADE,
313
+ FOREIGN KEY (entity_b_pk) REFERENCES entity_b ON DELETE CASCADE
314
+ )
315
+ CREATE INDEX on junction_name(entity_b_pk) -- composite PK covers entity_a_pk
316
+
317
+ Phase 6: N-ARY RELATIONSHIPS
318
+ ────────────────────────────────────────────────────
319
+ for each relationship where participants.length > 2:
320
+ CREATE TABLE junction_name (
321
+ for each participant: emit participant_pk as FK column
322
+ emit relationship attributes as columns
323
+ PRIMARY KEY = determine from cardinality analysis
324
+ for each participant: FOREIGN KEY REFERENCES participant ON DELETE CASCADE
325
+ )
326
+
327
+ Phase 7: MULTIVALUED ATTRIBUTES
328
+ ────────────────────────────────────────────────────
329
+ for each multivalued_attribute collected in Phase 1:
330
+ CREATE TABLE entity_attribute_name (
331
+ owner_pk column(s),
332
+ attribute_value column(s),
333
+ PRIMARY KEY (owner_pk, attribute_value),
334
+ FOREIGN KEY (owner_pk) REFERENCES owner_table ON DELETE CASCADE
335
+ )
336
+
337
+ Phase 8: DEPENDENCY ORDERING
338
+ ────────────────────────────────────────────────────
339
+ topological_sort all CREATE TABLE statements by FK dependencies
340
+ emit in order: tables with no FKs first, then tables referencing those, etc.
341
+ ```
342
+
343
+ ---
344
+
345
+ ## TypeScript Data Model
346
+
347
+ These are the type definitions for the internal ERD model that feeds the DDL generator.
348
+
349
+ ```typescript
350
+ // ============================================================
351
+ // ERD Model Types — the internal representation of an ER diagram
352
+ // ============================================================
353
+
354
+ /** Supported SQL dialects for DDL generation */
355
+ type SQLDialect = 'postgresql' | 'mysql' | 'sqlite';
356
+
357
+ /** Attribute classification per ER theory */
358
+ type AttributeKind =
359
+ | 'simple'
360
+ | 'key'
361
+ | 'partial_key' // weak entity identifier
362
+ | 'composite'
363
+ | 'multivalued'
364
+ | 'derived';
365
+
366
+ /** Relationship cardinality */
367
+ type Cardinality = '1:1' | '1:N' | 'M:N';
368
+
369
+ /** Participation constraint */
370
+ type Participation = 'total' | 'partial';
371
+
372
+ /** FK referential actions */
373
+ type ReferentialAction = 'CASCADE' | 'SET NULL' | 'RESTRICT' | 'SET DEFAULT' | 'NO ACTION';
374
+
375
+ // ── Attributes ──────────────────────────────────────────────
376
+
377
+ interface ERDAttribute {
378
+ id: string;
379
+ name: string;
380
+ kind: AttributeKind;
381
+ dataType: string; // logical type: "string", "integer", "date", etc.
382
+ isNullable: boolean;
383
+ defaultValue?: string;
384
+ children?: ERDAttribute[]; // for composite attributes: leaf-level sub-attributes
385
+ derivedExpression?: string; // for derived attributes: the computation expression
386
+ multivaluedType?: string; // for multivalued: the value type (e.g., "VARCHAR(20)")
387
+ }
388
+
389
+ // ── Entities ────────────────────────────────────────────────
390
+
391
+ interface ERDEntity {
392
+ id: string;
393
+ name: string;
394
+ isWeak: boolean;
395
+ attributes: ERDAttribute[];
396
+ /** For weak entities: the ID of the owning strong entity */
397
+ ownerId?: string;
398
+ /** For weak entities: the identifying relationship ID */
399
+ identifyingRelationshipId?: string;
400
+ }
401
+
402
+ // ── Relationships ───────────────────────────────────────────
403
+
404
+ interface ERDRelationshipParticipant {
405
+ entityId: string;
406
+ cardinality: '1' | 'N' | 'M';
407
+ participation: Participation;
408
+ }
409
+
410
+ interface ERDRelationship {
411
+ id: string;
412
+ name: string;
413
+ participants: ERDRelationshipParticipant[]; // 2 for binary, 3+ for n-ary
414
+ attributes: ERDAttribute[]; // relationship can have its own attributes
415
+ isIdentifying: boolean; // true for weak entity identifying relationships
416
+ }
417
+
418
+ // ── The Complete Model ──────────────────────────────────────
419
+
420
+ interface ERDModel {
421
+ name: string; // database/schema name
422
+ entities: ERDEntity[];
423
+ relationships: ERDRelationship[];
424
+ }
425
+
426
+ // ── DDL Generation Options ──────────────────────────────────
427
+
428
+ interface DDLGeneratorOptions {
429
+ dialect: SQLDialect;
430
+ useNamedConstraints: boolean; // e.g., CONSTRAINT fk_emp_dept vs inline
431
+ includeIndexes: boolean; // auto-create indexes on FK columns
432
+ includeDropIfExists: boolean; // prepend DROP TABLE IF EXISTS
433
+ schemaName?: string; // e.g., "public" for PostgreSQL
434
+ idStrategy: 'serial' | 'uuid' | 'cuid'; // primary key generation strategy
435
+ timestampColumns: boolean; // auto-add created_at, updated_at
436
+ }
437
+ ```
438
+
439
+ ---
440
+
441
+ ## Multi-Database Type Mapping
442
+
443
+ ### Logical-to-Physical Type Map
444
+
445
+ ```typescript
446
+ const TYPE_MAP: Record<string, Record<SQLDialect, string>> = {
447
+ // ── Identifiers ─────────────────────────────────────────
448
+ 'serial': { postgresql: 'SERIAL', mysql: 'INT AUTO_INCREMENT', sqlite: 'INTEGER' },
449
+ 'bigserial': { postgresql: 'BIGSERIAL', mysql: 'BIGINT AUTO_INCREMENT', sqlite: 'INTEGER' },
450
+ 'uuid': { postgresql: 'UUID', mysql: 'CHAR(36)', sqlite: 'TEXT' },
451
+ 'cuid': { postgresql: 'VARCHAR(30)', mysql: 'VARCHAR(30)', sqlite: 'TEXT' },
452
+
453
+ // ── Strings ─────────────────────────────────────────────
454
+ 'varchar': { postgresql: 'VARCHAR', mysql: 'VARCHAR', sqlite: 'TEXT' },
455
+ 'text': { postgresql: 'TEXT', mysql: 'TEXT', sqlite: 'TEXT' },
456
+ 'longtext': { postgresql: 'TEXT', mysql: 'LONGTEXT', sqlite: 'TEXT' },
457
+ 'char': { postgresql: 'CHAR', mysql: 'CHAR', sqlite: 'TEXT' },
458
+
459
+ // ── Numbers ─────────────────────────────────────────────
460
+ 'integer': { postgresql: 'INTEGER', mysql: 'INT', sqlite: 'INTEGER' },
461
+ 'smallint': { postgresql: 'SMALLINT', mysql: 'SMALLINT', sqlite: 'INTEGER' },
462
+ 'bigint': { postgresql: 'BIGINT', mysql: 'BIGINT', sqlite: 'INTEGER' },
463
+ 'decimal': { postgresql: 'NUMERIC', mysql: 'DECIMAL', sqlite: 'REAL' },
464
+ 'float': { postgresql: 'REAL', mysql: 'FLOAT', sqlite: 'REAL' },
465
+ 'double': { postgresql: 'DOUBLE PRECISION', mysql: 'DOUBLE', sqlite: 'REAL' },
466
+
467
+ // ── Boolean ─────────────────────────────────────────────
468
+ 'boolean': { postgresql: 'BOOLEAN', mysql: 'TINYINT(1)', sqlite: 'INTEGER' },
469
+
470
+ // ── Date/Time ───────────────────────────────────────────
471
+ 'date': { postgresql: 'DATE', mysql: 'DATE', sqlite: 'TEXT' },
472
+ 'time': { postgresql: 'TIME', mysql: 'TIME', sqlite: 'TEXT' },
473
+ 'timestamp': { postgresql: 'TIMESTAMPTZ', mysql: 'DATETIME', sqlite: 'TEXT' },
474
+ 'interval': { postgresql: 'INTERVAL', mysql: 'VARCHAR(50)', sqlite: 'TEXT' },
475
+
476
+ // ── JSON ────────────────────────────────────────────────
477
+ 'json': { postgresql: 'JSONB', mysql: 'JSON', sqlite: 'TEXT' },
478
+
479
+ // ── Binary ──────────────────────────────────────────────
480
+ 'blob': { postgresql: 'BYTEA', mysql: 'BLOB', sqlite: 'BLOB' },
481
+
482
+ // ── Enum (special handling) ─────────────────────────────
483
+ 'enum': { postgresql: 'TEXT', mysql: 'ENUM', sqlite: 'TEXT' },
484
+ // PostgreSQL enums require CREATE TYPE; handled separately in generator
485
+ };
486
+ ```
487
+
488
+ ### Dialect-Specific Syntax Differences
489
+
490
+ | Feature | PostgreSQL | MySQL | SQLite |
491
+ |------------------------|--------------------------------|--------------------------------|---------------------------|
492
+ | Auto-increment PK | `SERIAL PRIMARY KEY` | `INT PRIMARY KEY AUTO_INCREMENT` | `INTEGER PRIMARY KEY` |
493
+ | UUID generation | `DEFAULT gen_random_uuid()` | `DEFAULT (UUID())` | Application-generated |
494
+ | Boolean literal | `TRUE / FALSE` | `1 / 0` | `1 / 0` |
495
+ | Current timestamp | `CURRENT_TIMESTAMP` | `CURRENT_TIMESTAMP` | `CURRENT_TIMESTAMP` |
496
+ | Generated column | `GENERATED ALWAYS AS (...) STORED` | `GENERATED ALWAYS AS (...) STORED` | Not supported |
497
+ | IF NOT EXISTS | `CREATE TABLE IF NOT EXISTS` | `CREATE TABLE IF NOT EXISTS` | `CREATE TABLE IF NOT EXISTS` |
498
+ | Drop + cascade | `DROP TABLE IF EXISTS t CASCADE` | `DROP TABLE IF EXISTS t` | `DROP TABLE IF EXISTS t` |
499
+ | Schema qualification | `schema.table` | `` `database`.`table` `` | Not applicable |
500
+ | ENUM type | `CREATE TYPE ... AS ENUM` | Inline `ENUM('a','b')` | Use CHECK constraint |
501
+ | Partial index | `WHERE condition` | Not supported (use generated column + index) | `WHERE condition` |
502
+ | Comment on column | `COMMENT ON COLUMN ...` | `COMMENT 'text'` inline | Not supported |
503
+
504
+ ---
505
+
506
+ ## FK Referential Actions
507
+
508
+ ### Action Matrix — When to Use Each
509
+
510
+ | Action | Behavior on Parent DELETE | Use When |
511
+ |----------------|----------------------------------------|-----------------------------------------------------------------|
512
+ | **CASCADE** | Delete all child rows automatically | Weak entities, junction tables, dependent data that has no meaning without parent |
513
+ | **SET NULL** | Set FK column to NULL | Optional relationships where child survives parent deletion (e.g., employee.department_id when department dissolved) |
514
+ | **RESTRICT** | Block parent deletion if children exist | **Safe default.** Critical references where deletion should be prevented (e.g., cannot delete a customer with open orders) |
515
+ | **SET DEFAULT**| Set FK to its DEFAULT value | Rare. When a fallback parent exists (e.g., "unassigned" category) |
516
+ | **NO ACTION** | Like RESTRICT but deferred-checkable | Same as RESTRICT; use when you need deferred constraint checking within a transaction |
517
+
518
+ ### Decision Tree
519
+
520
+ ```
521
+ Is the child entity DEPENDENT on the parent (weak entity, junction table)?
522
+ YES --> CASCADE
523
+ NO --> Can the child exist without the parent?
524
+ YES --> Is the FK nullable?
525
+ YES --> SET NULL
526
+ NO --> RESTRICT (or add a default parent)
527
+ NO --> CASCADE
528
+ ```
529
+
530
+ ### ON UPDATE Actions
531
+
532
+ Most systems should use `ON UPDATE CASCADE` for all FKs. This handles the (rare) case where a parent PK is updated. Exception: if using immutable surrogate keys (UUID/CUID), ON UPDATE is effectively a no-op.
533
+
534
+ ---
535
+
536
+ ## AI-Enhanced Generation
537
+
538
+ ### Text2Schema Pipeline (arXiv 2025)
539
+
540
+ Multi-agent decomposition for natural language to DDL. The key insight: breaking the NL-to-DDL task into sub-tasks with specialized agents yields significantly better schemas than single-pass generation.
541
+
542
+ ```
543
+ Input: "Build a university system where students enroll in courses
544
+ taught by professors in departments"
545
+
546
+ Agent Pipeline:
547
+ 1. ENTITY EXTRACTOR
548
+ --> Student, Course, Professor, Department
549
+
550
+ 2. RELATIONSHIP CLASSIFIER
551
+ --> Student M:N Course (enrollment)
552
+ --> Professor 1:N Course (teaches)
553
+ --> Department 1:N Professor (belongs_to)
554
+ --> Department 1:N Course (offered_by)
555
+
556
+ 3. ATTRIBUTE ENRICHER
557
+ --> Student: student_id (PK), first_name, last_name, email, enrollment_date
558
+ --> Course: course_id (PK), title, credits, max_enrollment
559
+ --> Professor: prof_id (PK), first_name, last_name, email, hire_date
560
+ --> Department: dept_id (PK), name, building, budget
561
+
562
+ 4. CONSTRAINT INTEGRATOR
563
+ --> enrollment junction: grade, semester (relationship attrs)
564
+ --> email UNIQUE on Student, Professor
565
+ --> credits CHECK (credits > 0 AND credits <= 6)
566
+
567
+ 5. DDL CODE ARTICULATOR
568
+ --> Applies the 7 mapping rules
569
+ --> Outputs dialect-specific SQL
570
+
571
+ 6. VERIFIER
572
+ --> Checks 3NF compliance
573
+ --> Validates FK references
574
+ --> Confirms no orphan tables
575
+ ```
576
+
577
+ ### NOMAD Agent Roles (arXiv 2025)
578
+
579
+ Similar multi-agent approach but emphasizes the **articulation** step -- translating the abstract model into implementation-ready code. Key pattern: the code articulator agent receives the verified model and must produce syntactically valid, runnable DDL. The verifier then executes it against a test database to confirm.
580
+
581
+ ### Prompt Template for LLM-Assisted Mapping
582
+
583
+ ```
584
+ You are a database schema designer. Given the following ER model in JSON format,
585
+ generate SQL DDL for {dialect}.
586
+
587
+ Rules:
588
+ 1. Strong entities become tables with their simple attributes as columns
589
+ 2. Key attributes become PRIMARY KEY
590
+ 3. Composite attributes are flattened to leaf columns
591
+ 4. Weak entities get composite PK (partial_key + owner_pk) with ON DELETE CASCADE
592
+ 5. 1:1 relationships: FK on total-participation side with UNIQUE constraint
593
+ 6. 1:N relationships: FK on the many side
594
+ 7. M:N relationships: junction table with composite PK
595
+ 8. Multivalued attributes: separate table with composite PK
596
+
597
+ Output requirements:
598
+ - Named constraints (pk_, fk_, uq_, ck_ prefixes)
599
+ - Appropriate ON DELETE actions
600
+ - Indexes on all FK columns
601
+ - Tables ordered by dependency (no forward references)
602
+
603
+ ER Model:
604
+ {model_json}
605
+ ```
606
+
607
+ ---
608
+
609
+ ## Implementation Architecture
610
+
611
+ ### DrawDB Parser/Generator Pattern (Gold Standard)
612
+
613
+ DrawDB is the open-source reference implementation for visual ERD-to-DDL. Its architecture separates concerns cleanly:
614
+
615
+ ```
616
+ Visual Editor (React Flow)
617
+ |
618
+ v
619
+ Internal JSON Model <-- the canonical representation
620
+ |
621
+ +--> PostgreSQL Generator
622
+ +--> MySQL Generator
623
+ +--> SQLite Generator
624
+ +--> SQL Server Generator
625
+ |
626
+ v
627
+ SQL DDL String Output
628
+
629
+ Reverse path:
630
+ SQL DDL String --> Parser --> Internal JSON Model --> Visual Editor
631
+ ```
632
+
633
+ **Key pattern:** Each dialect has its own generator module that reads the same JSON model. The generators share a base class with common logic (constraint naming, dependency sorting) and override dialect-specific methods (type mapping, auto-increment syntax, enum handling).
634
+
635
+ ### node-sql-parser Integration
636
+
637
+ Bidirectional SQL parsing/generation using AST:
638
+
639
+ ```typescript
640
+ import { Parser } from 'node-sql-parser';
641
+
642
+ const parser = new Parser();
643
+
644
+ // DDL string --> AST (for importing existing schemas)
645
+ const ast = parser.astify('CREATE TABLE users (id INT PRIMARY KEY, name VARCHAR(50));');
646
+
647
+ // AST --> DDL string (for generating from modified model)
648
+ const sql = parser.sqlify(ast);
649
+
650
+ // Supports dialect-specific parsing
651
+ const pgAst = parser.astify(ddlString, { database: 'PostgreSQL' });
652
+ const mysqlAst = parser.astify(ddlString, { database: 'MySQL' });
653
+ ```
654
+
655
+ ### sql-ddl-to-json-schema
656
+
657
+ For importing existing DDL into the ERD model:
658
+
659
+ ```typescript
660
+ // Parse DDL into JSON Schema (ERD-friendly format)
661
+ import { Parser as DDLParser } from 'sql-ddl-to-json-schema';
662
+
663
+ const ddlParser = new DDLParser('mysql');
664
+ ddlParser.feed(existingDDL);
665
+ const jsonSchema = ddlParser.results;
666
+ // jsonSchema contains tables, columns, constraints, FKs
667
+ // Map this into the ERDModel interface for visualization
668
+ ```
669
+
670
+ ### ERFlow MCP Pattern
671
+
672
+ ERFlow (MCP Server) demonstrates an agent-driven approach with 25+ tools for schema manipulation:
673
+ - Natural language schema edits ("add a status column to orders")
674
+ - Checkpoint-based migration generation (diff between schema states)
675
+ - Multi-dialect export from a single canonical model
676
+
677
+ ---
678
+
679
+ ## Code Example: TypeScript DDL Generator
680
+
681
+ ```typescript
682
+ // ============================================================
683
+ // ERD-to-DDL Generator — complete implementation
684
+ // ============================================================
685
+
686
+ /**
687
+ * Resolves a logical data type to a dialect-specific SQL type.
688
+ */
689
+ function resolveType(
690
+ logicalType: string,
691
+ dialect: SQLDialect,
692
+ length?: number,
693
+ precision?: number,
694
+ scale?: number
695
+ ): string {
696
+ const base = TYPE_MAP[logicalType.toLowerCase()]?.[dialect]
697
+ ?? TYPE_MAP['text'][dialect]; // fallback to TEXT
698
+
699
+ // Apply length/precision modifiers
700
+ if (length && ['varchar', 'char'].includes(logicalType.toLowerCase())) {
701
+ return `${base}(${length})`;
702
+ }
703
+ if (precision && ['decimal', 'numeric'].includes(logicalType.toLowerCase())) {
704
+ return scale ? `${base}(${precision},${scale})` : `${base}(${precision})`;
705
+ }
706
+ return base;
707
+ }
708
+
709
+ /**
710
+ * Generates a constraint name following naming conventions.
711
+ */
712
+ function constraintName(
713
+ type: 'pk' | 'fk' | 'uq' | 'ck' | 'idx',
714
+ tableName: string,
715
+ columnOrDetail: string
716
+ ): string {
717
+ return `${type}_${tableName}_${columnOrDetail}`.toLowerCase();
718
+ }
719
+
720
+ /**
721
+ * Flattens composite attributes recursively into leaf-level columns.
722
+ */
723
+ function flattenAttributes(attrs: ERDAttribute[], prefix = ''): ERDAttribute[] {
724
+ const result: ERDAttribute[] = [];
725
+ for (const attr of attrs) {
726
+ if (attr.kind === 'composite' && attr.children?.length) {
727
+ result.push(...flattenAttributes(attr.children, `${prefix}${attr.name}_`));
728
+ } else if (attr.kind !== 'multivalued') {
729
+ result.push({
730
+ ...attr,
731
+ name: `${prefix}${attr.name}`,
732
+ });
733
+ }
734
+ // multivalued attributes handled separately in Phase 7
735
+ }
736
+ return result;
737
+ }
738
+
739
+ /**
740
+ * Collects all multivalued attributes from an entity (including nested in composites).
741
+ */
742
+ function collectMultivalued(
743
+ entity: ERDEntity
744
+ ): { entityId: string; entityName: string; attr: ERDAttribute }[] {
745
+ const results: { entityId: string; entityName: string; attr: ERDAttribute }[] = [];
746
+
747
+ function walk(attrs: ERDAttribute[]) {
748
+ for (const attr of attrs) {
749
+ if (attr.kind === 'multivalued') {
750
+ results.push({ entityId: entity.id, entityName: entity.name, attr });
751
+ }
752
+ if (attr.children) walk(attr.children);
753
+ }
754
+ }
755
+
756
+ walk(entity.attributes);
757
+ return results;
758
+ }
759
+
760
+ /**
761
+ * Gets the primary key column names for an entity.
762
+ */
763
+ function getPKColumns(entity: ERDEntity): string[] {
764
+ return entity.attributes
765
+ .filter(a => a.kind === 'key' || a.kind === 'partial_key')
766
+ .map(a => a.name);
767
+ }
768
+
769
+ /**
770
+ * Determines the appropriate ON DELETE action based on relationship context.
771
+ */
772
+ function determineOnDelete(
773
+ participation: Participation,
774
+ isWeakEntityFK: boolean,
775
+ isJunctionFK: boolean
776
+ ): ReferentialAction {
777
+ if (isWeakEntityFK || isJunctionFK) return 'CASCADE';
778
+ if (participation === 'total') return 'RESTRICT';
779
+ return 'SET NULL';
780
+ }
781
+
782
+ /**
783
+ * Topologically sorts tables by FK dependencies.
784
+ * Tables with no FK dependencies come first.
785
+ */
786
+ function topologicalSort(
787
+ tables: { name: string; dependsOn: string[] }[]
788
+ ): string[] {
789
+ const sorted: string[] = [];
790
+ const visited = new Set<string>();
791
+ const visiting = new Set<string>();
792
+ const tableMap = new Map(tables.map(t => [t.name, t]));
793
+
794
+ function visit(name: string) {
795
+ if (visited.has(name)) return;
796
+ if (visiting.has(name)) {
797
+ // Circular dependency — emit as-is (will need ALTER TABLE for FK)
798
+ sorted.push(name);
799
+ visited.add(name);
800
+ return;
801
+ }
802
+ visiting.add(name);
803
+ const table = tableMap.get(name);
804
+ if (table) {
805
+ for (const dep of table.dependsOn) {
806
+ if (tableMap.has(dep)) visit(dep);
807
+ }
808
+ }
809
+ visiting.delete(name);
810
+ visited.add(name);
811
+ sorted.push(name);
812
+ }
813
+
814
+ for (const table of tables) {
815
+ visit(table.name);
816
+ }
817
+ return sorted;
818
+ }
819
+
820
+ // ── Statement Builders ──────────────────────────────────────
821
+
822
+ interface TableStatement {
823
+ name: string;
824
+ sql: string;
825
+ dependsOn: string[];
826
+ }
827
+
828
+ interface IndexStatement {
829
+ sql: string;
830
+ }
831
+
832
+ /**
833
+ * Main generator function: ERDModel --> SQL DDL string.
834
+ */
835
+ function generateDDL(model: ERDModel, options: DDLGeneratorOptions): string {
836
+ const tables: TableStatement[] = [];
837
+ const indexes: IndexStatement[] = [];
838
+ const entityTableMap = new Map<string, string>(); // entity.id -> table name
839
+
840
+ // Build entity ID -> table name lookup
841
+ for (const entity of model.entities) {
842
+ entityTableMap.set(entity.id, entity.name.toLowerCase());
843
+ }
844
+
845
+ // ── Phase 1: Strong Entity Tables ─────────────────────────
846
+
847
+ for (const entity of model.entities.filter(e => !e.isWeak)) {
848
+ const tableName = entity.name.toLowerCase();
849
+ const flatAttrs = flattenAttributes(entity.attributes);
850
+ const pkCols = getPKColumns(entity);
851
+ const columns: string[] = [];
852
+
853
+ for (const attr of flatAttrs) {
854
+ const colName = attr.name.toLowerCase();
855
+ const colType = resolveType(attr.dataType, options.dialect);
856
+ const constraints: string[] = [];
857
+
858
+ if (attr.kind === 'key') constraints.push('NOT NULL');
859
+ if (!attr.isNullable && attr.kind !== 'key') constraints.push('NOT NULL');
860
+ if (attr.defaultValue) constraints.push(`DEFAULT ${attr.defaultValue}`);
861
+
862
+ if (attr.kind === 'derived' && attr.derivedExpression) {
863
+ if (options.dialect !== 'sqlite') {
864
+ columns.push(
865
+ ` ${colName} ${colType} GENERATED ALWAYS AS (${attr.derivedExpression}) STORED`
866
+ );
867
+ }
868
+ // SQLite: skip derived columns
869
+ continue;
870
+ }
871
+
872
+ columns.push(` ${colName} ${colType}${constraints.length ? ' ' + constraints.join(' ') : ''}`);
873
+ }
874
+
875
+ // Timestamp columns
876
+ if (options.timestampColumns) {
877
+ const tsType = resolveType('timestamp', options.dialect);
878
+ columns.push(` created_at ${tsType} NOT NULL DEFAULT CURRENT_TIMESTAMP`);
879
+ columns.push(` updated_at ${tsType} NOT NULL DEFAULT CURRENT_TIMESTAMP`);
880
+ }
881
+
882
+ // PK constraint
883
+ const pkConstraint = options.useNamedConstraints
884
+ ? ` CONSTRAINT ${constraintName('pk', tableName, pkCols.join('_'))} PRIMARY KEY (${pkCols.join(', ')})`
885
+ : ` PRIMARY KEY (${pkCols.join(', ')})`;
886
+
887
+ const ddl = [
888
+ `CREATE TABLE ${tableName} (`,
889
+ [...columns, pkConstraint].join(',\n'),
890
+ `);`,
891
+ ].join('\n');
892
+
893
+ tables.push({ name: tableName, sql: ddl, dependsOn: [] });
894
+ }
895
+
896
+ // ── Phase 2: Weak Entity Tables ───────────────────────────
897
+
898
+ for (const entity of model.entities.filter(e => e.isWeak)) {
899
+ const tableName = entity.name.toLowerCase();
900
+ const owner = model.entities.find(e => e.id === entity.ownerId);
901
+ if (!owner) continue;
902
+
903
+ const ownerTable = owner.name.toLowerCase();
904
+ const ownerPK = getPKColumns(owner);
905
+ const partialKey = entity.attributes.filter(a => a.kind === 'partial_key').map(a => a.name);
906
+ const flatAttrs = flattenAttributes(entity.attributes.filter(a => a.kind !== 'partial_key'));
907
+ const columns: string[] = [];
908
+
909
+ // Partial key columns
910
+ for (const pk of partialKey) {
911
+ const attr = entity.attributes.find(a => a.name === pk)!;
912
+ columns.push(` ${pk.toLowerCase()} ${resolveType(attr.dataType, options.dialect)} NOT NULL`);
913
+ }
914
+
915
+ // Owner PK as FK columns
916
+ for (const ownerCol of ownerPK) {
917
+ const ownerAttr = owner.attributes.find(a => a.name === ownerCol)!;
918
+ columns.push(` ${ownerCol.toLowerCase()} ${resolveType(ownerAttr.dataType, options.dialect)} NOT NULL`);
919
+ }
920
+
921
+ // Other attributes
922
+ for (const attr of flatAttrs) {
923
+ const colType = resolveType(attr.dataType, options.dialect);
924
+ const nullable = attr.isNullable ? '' : ' NOT NULL';
925
+ columns.push(` ${attr.name.toLowerCase()} ${colType}${nullable}`);
926
+ }
927
+
928
+ // Composite PK
929
+ const compositePK = [...partialKey, ...ownerPK].map(c => c.toLowerCase());
930
+ const pkLine = options.useNamedConstraints
931
+ ? ` CONSTRAINT ${constraintName('pk', tableName, compositePK.join('_'))} PRIMARY KEY (${compositePK.join(', ')})`
932
+ : ` PRIMARY KEY (${compositePK.join(', ')})`;
933
+
934
+ // FK to owner
935
+ const fkLine = options.useNamedConstraints
936
+ ? ` CONSTRAINT ${constraintName('fk', tableName, ownerTable)} FOREIGN KEY (${ownerPK.join(', ')}) REFERENCES ${ownerTable}(${ownerPK.join(', ')}) ON DELETE CASCADE ON UPDATE CASCADE`
937
+ : ` FOREIGN KEY (${ownerPK.join(', ')}) REFERENCES ${ownerTable}(${ownerPK.join(', ')}) ON DELETE CASCADE ON UPDATE CASCADE`;
938
+
939
+ const ddl = [
940
+ `CREATE TABLE ${tableName} (`,
941
+ [...columns, pkLine, fkLine].join(',\n'),
942
+ `);`,
943
+ ].join('\n');
944
+
945
+ tables.push({ name: tableName, sql: ddl, dependsOn: [ownerTable] });
946
+ }
947
+
948
+ // ── Phase 3-6: Relationships ──────────────────────────────
949
+
950
+ for (const rel of model.relationships) {
951
+ if (rel.isIdentifying) continue; // already handled in Phase 2
952
+
953
+ const participants = rel.participants;
954
+
955
+ if (participants.length === 2) {
956
+ const [a, b] = participants;
957
+ const cardA = a.cardinality;
958
+ const cardB = b.cardinality;
959
+ const entityA = model.entities.find(e => e.id === a.entityId)!;
960
+ const entityB = model.entities.find(e => e.id === b.entityId)!;
961
+ const tableA = entityA.name.toLowerCase();
962
+ const tableB = entityB.name.toLowerCase();
963
+ const pkA = getPKColumns(entityA);
964
+ const pkB = getPKColumns(entityB);
965
+
966
+ // ── 1:1 ──
967
+ if (cardA === '1' && cardB === '1') {
968
+ // FK goes on total participation side (or side A by default)
969
+ const fkSide = b.participation === 'total' ? 'b' : 'a';
970
+ const [fkTable, refTable, refPK] = fkSide === 'b'
971
+ ? [tableB, tableA, pkA]
972
+ : [tableA, tableB, pkB];
973
+ const fkCol = `${refTable}_id`;
974
+ const onDelete = determineOnDelete(
975
+ fkSide === 'b' ? b.participation : a.participation, false, false
976
+ );
977
+
978
+ // Find the table statement and add FK column
979
+ const tableStmt = tables.find(t => t.name === fkTable);
980
+ if (tableStmt) {
981
+ const fkColType = resolveType('integer', options.dialect); // match ref PK type
982
+ const insertBefore = ');';
983
+ const fkLines = [
984
+ `,\n ${fkCol} ${fkColType} UNIQUE`,
985
+ options.useNamedConstraints
986
+ ? `,\n CONSTRAINT ${constraintName('fk', fkTable, refTable)} FOREIGN KEY (${fkCol}) REFERENCES ${refTable}(${refPK.join(', ')}) ON DELETE ${onDelete}`
987
+ : `,\n FOREIGN KEY (${fkCol}) REFERENCES ${refTable}(${refPK.join(', ')}) ON DELETE ${onDelete}`,
988
+ ];
989
+ // Add relationship attributes
990
+ for (const attr of rel.attributes) {
991
+ fkLines.unshift(`,\n ${attr.name.toLowerCase()} ${resolveType(attr.dataType, options.dialect)}`);
992
+ }
993
+ tableStmt.sql = tableStmt.sql.replace(insertBefore, fkLines.join('') + '\n' + insertBefore);
994
+ tableStmt.dependsOn.push(refTable);
995
+ }
996
+ }
997
+
998
+ // ── 1:N ──
999
+ else if (
1000
+ (cardA === '1' && (cardB === 'N' || cardB === 'M')) ||
1001
+ ((cardA === 'N' || cardA === 'M') && cardB === '1')
1002
+ ) {
1003
+ const [oneEntity, manyEntity, manyParticipation] =
1004
+ cardA === '1'
1005
+ ? [entityA, entityB, b.participation]
1006
+ : [entityB, entityA, a.participation];
1007
+ const oneTable = oneEntity.name.toLowerCase();
1008
+ const manyTable = manyEntity.name.toLowerCase();
1009
+ const onePK = getPKColumns(oneEntity);
1010
+ const fkCol = `${oneTable}_id`;
1011
+ const onDelete = determineOnDelete(manyParticipation, false, false);
1012
+ const nullable = manyParticipation === 'partial';
1013
+
1014
+ const tableStmt = tables.find(t => t.name === manyTable);
1015
+ if (tableStmt) {
1016
+ const fkColType = resolveType('integer', options.dialect);
1017
+ const nullStr = nullable ? '' : ' NOT NULL';
1018
+ const insertBefore = ');';
1019
+ const fkLines = [
1020
+ `,\n ${fkCol} ${fkColType}${nullStr}`,
1021
+ options.useNamedConstraints
1022
+ ? `,\n CONSTRAINT ${constraintName('fk', manyTable, oneTable)} FOREIGN KEY (${fkCol}) REFERENCES ${oneTable}(${onePK.join(', ')}) ON DELETE ${onDelete}`
1023
+ : `,\n FOREIGN KEY (${fkCol}) REFERENCES ${oneTable}(${onePK.join(', ')}) ON DELETE ${onDelete}`,
1024
+ ];
1025
+ for (const attr of rel.attributes) {
1026
+ fkLines.unshift(`,\n ${attr.name.toLowerCase()} ${resolveType(attr.dataType, options.dialect)}`);
1027
+ }
1028
+ tableStmt.sql = tableStmt.sql.replace(insertBefore, fkLines.join('') + '\n' + insertBefore);
1029
+ tableStmt.dependsOn.push(oneTable);
1030
+
1031
+ if (options.includeIndexes) {
1032
+ indexes.push({
1033
+ sql: `CREATE INDEX ${constraintName('idx', manyTable, fkCol)} ON ${manyTable}(${fkCol});`,
1034
+ });
1035
+ }
1036
+ }
1037
+ }
1038
+
1039
+ // ── M:N ──
1040
+ else if ((cardA === 'N' || cardA === 'M') && (cardB === 'N' || cardB === 'M')) {
1041
+ const junctionName = rel.name.toLowerCase() || `${tableA}_${tableB}`;
1042
+ const fkColA = `${tableA}_${pkA[0]}`.toLowerCase();
1043
+ const fkColB = `${tableB}_${pkB[0]}`.toLowerCase();
1044
+ const fkTypeA = resolveType('integer', options.dialect);
1045
+ const fkTypeB = resolveType('integer', options.dialect);
1046
+
1047
+ const columns: string[] = [
1048
+ ` ${fkColA} ${fkTypeA} NOT NULL`,
1049
+ ` ${fkColB} ${fkTypeB} NOT NULL`,
1050
+ ];
1051
+
1052
+ // Relationship attributes
1053
+ for (const attr of rel.attributes) {
1054
+ const colType = resolveType(attr.dataType, options.dialect);
1055
+ const nullable = attr.isNullable ? '' : ' NOT NULL';
1056
+ const def = attr.defaultValue ? ` DEFAULT ${attr.defaultValue}` : '';
1057
+ columns.push(` ${attr.name.toLowerCase()} ${colType}${nullable}${def}`);
1058
+ }
1059
+
1060
+ const pkLine = options.useNamedConstraints
1061
+ ? ` CONSTRAINT ${constraintName('pk', junctionName, `${fkColA}_${fkColB}`)} PRIMARY KEY (${fkColA}, ${fkColB})`
1062
+ : ` PRIMARY KEY (${fkColA}, ${fkColB})`;
1063
+
1064
+ const fkLineA = options.useNamedConstraints
1065
+ ? ` CONSTRAINT ${constraintName('fk', junctionName, tableA)} FOREIGN KEY (${fkColA}) REFERENCES ${tableA}(${pkA.join(', ')}) ON DELETE CASCADE`
1066
+ : ` FOREIGN KEY (${fkColA}) REFERENCES ${tableA}(${pkA.join(', ')}) ON DELETE CASCADE`;
1067
+
1068
+ const fkLineB = options.useNamedConstraints
1069
+ ? ` CONSTRAINT ${constraintName('fk', junctionName, tableB)} FOREIGN KEY (${fkColB}) REFERENCES ${tableB}(${pkB.join(', ')}) ON DELETE CASCADE`
1070
+ : ` FOREIGN KEY (${fkColB}) REFERENCES ${tableB}(${pkB.join(', ')}) ON DELETE CASCADE`;
1071
+
1072
+ const ddl = [
1073
+ `CREATE TABLE ${junctionName} (`,
1074
+ [...columns, pkLine, fkLineA, fkLineB].join(',\n'),
1075
+ `);`,
1076
+ ].join('\n');
1077
+
1078
+ tables.push({ name: junctionName, sql: ddl, dependsOn: [tableA, tableB] });
1079
+
1080
+ if (options.includeIndexes) {
1081
+ indexes.push({
1082
+ sql: `CREATE INDEX ${constraintName('idx', junctionName, fkColB)} ON ${junctionName}(${fkColB});`,
1083
+ });
1084
+ }
1085
+ }
1086
+ }
1087
+
1088
+ // ── N-ary (3+ participants) ──
1089
+ else if (participants.length > 2) {
1090
+ const junctionName = rel.name.toLowerCase();
1091
+ const fkCols: { col: string; type: string; table: string; pk: string }[] = [];
1092
+
1093
+ for (const p of participants) {
1094
+ const entity = model.entities.find(e => e.id === p.entityId)!;
1095
+ const table = entity.name.toLowerCase();
1096
+ const pk = getPKColumns(entity);
1097
+ fkCols.push({
1098
+ col: `${table}_${pk[0]}`.toLowerCase(),
1099
+ type: resolveType('integer', options.dialect),
1100
+ table,
1101
+ pk: pk.join(', '),
1102
+ });
1103
+ }
1104
+
1105
+ const columns = fkCols.map(fk => ` ${fk.col} ${fk.type} NOT NULL`);
1106
+
1107
+ for (const attr of rel.attributes) {
1108
+ columns.push(` ${attr.name.toLowerCase()} ${resolveType(attr.dataType, options.dialect)}`);
1109
+ }
1110
+
1111
+ const pkLine = ` PRIMARY KEY (${fkCols.map(fk => fk.col).join(', ')})`;
1112
+ const fkLines = fkCols.map(fk =>
1113
+ options.useNamedConstraints
1114
+ ? ` CONSTRAINT ${constraintName('fk', junctionName, fk.table)} FOREIGN KEY (${fk.col}) REFERENCES ${fk.table}(${fk.pk}) ON DELETE CASCADE`
1115
+ : ` FOREIGN KEY (${fk.col}) REFERENCES ${fk.table}(${fk.pk}) ON DELETE CASCADE`
1116
+ );
1117
+
1118
+ const ddl = [
1119
+ `CREATE TABLE ${junctionName} (`,
1120
+ [...columns, pkLine, ...fkLines].join(',\n'),
1121
+ `);`,
1122
+ ].join('\n');
1123
+
1124
+ tables.push({
1125
+ name: junctionName,
1126
+ sql: ddl,
1127
+ dependsOn: fkCols.map(fk => fk.table),
1128
+ });
1129
+ }
1130
+ }
1131
+
1132
+ // ── Phase 7: Multivalued Attributes ───────────────────────
1133
+
1134
+ for (const entity of model.entities) {
1135
+ const multivaluedAttrs = collectMultivalued(entity);
1136
+ for (const { entityName, attr } of multivaluedAttrs) {
1137
+ const ownerTable = entityName.toLowerCase();
1138
+ const tableName = `${ownerTable}_${attr.name.toLowerCase()}`;
1139
+ const ownerPK = getPKColumns(entity);
1140
+ const ownerPKType = resolveType('integer', options.dialect);
1141
+ const valueType = resolveType(attr.multivaluedType ?? attr.dataType, options.dialect);
1142
+
1143
+ const ddl = [
1144
+ `CREATE TABLE ${tableName} (`,
1145
+ ` ${ownerPK[0].toLowerCase()} ${ownerPKType} NOT NULL,`,
1146
+ ` ${attr.name.toLowerCase()} ${valueType} NOT NULL,`,
1147
+ options.useNamedConstraints
1148
+ ? ` CONSTRAINT ${constraintName('pk', tableName, `${ownerPK[0]}_${attr.name}`)} PRIMARY KEY (${ownerPK[0].toLowerCase()}, ${attr.name.toLowerCase()}),`
1149
+ : ` PRIMARY KEY (${ownerPK[0].toLowerCase()}, ${attr.name.toLowerCase()}),`,
1150
+ options.useNamedConstraints
1151
+ ? ` CONSTRAINT ${constraintName('fk', tableName, ownerTable)} FOREIGN KEY (${ownerPK[0].toLowerCase()}) REFERENCES ${ownerTable}(${ownerPK[0].toLowerCase()}) ON DELETE CASCADE`
1152
+ : ` FOREIGN KEY (${ownerPK[0].toLowerCase()}) REFERENCES ${ownerTable}(${ownerPK[0].toLowerCase()}) ON DELETE CASCADE`,
1153
+ `);`,
1154
+ ].join('\n');
1155
+
1156
+ tables.push({ name: tableName, sql: ddl, dependsOn: [ownerTable] });
1157
+ }
1158
+ }
1159
+
1160
+ // ── Phase 8: Dependency Ordering + Output ─────────────────
1161
+
1162
+ const orderedNames = topologicalSort(tables);
1163
+ const orderedTables = orderedNames
1164
+ .map(name => tables.find(t => t.name === name)!)
1165
+ .filter(Boolean);
1166
+
1167
+ const header = [
1168
+ `-- Generated by Dominion Flow ERD Creator`,
1169
+ `-- Database: ${model.name}`,
1170
+ `-- Dialect: ${options.dialect}`,
1171
+ `-- Generated: ${new Date().toISOString().split('T')[0]}`,
1172
+ ``,
1173
+ ].join('\n');
1174
+
1175
+ const dropStatements = options.includeDropIfExists
1176
+ ? orderedNames
1177
+ .reverse()
1178
+ .map(name => {
1179
+ const cascade = options.dialect === 'postgresql' ? ' CASCADE' : '';
1180
+ return `DROP TABLE IF EXISTS ${name}${cascade};`;
1181
+ })
1182
+ .join('\n') + '\n\n'
1183
+ : '';
1184
+
1185
+ // Re-reverse for CREATE order (drops go in reverse dependency order)
1186
+ if (options.includeDropIfExists) orderedNames.reverse();
1187
+
1188
+ const createStatements = orderedTables.map(t => t.sql).join('\n\n');
1189
+ const indexStatements = indexes.length
1190
+ ? '\n\n-- Indexes\n' + indexes.map(i => i.sql).join('\n')
1191
+ : '';
1192
+
1193
+ return header + dropStatements + createStatements + indexStatements + '\n';
1194
+ }
1195
+ ```
1196
+
1197
+ ### Usage Example
1198
+
1199
+ ```typescript
1200
+ const universityModel: ERDModel = {
1201
+ name: 'university_system',
1202
+ entities: [
1203
+ {
1204
+ id: 'e1', name: 'departments', isWeak: false,
1205
+ attributes: [
1206
+ { id: 'a1', name: 'department_id', kind: 'key', dataType: 'serial', isNullable: false },
1207
+ { id: 'a2', name: 'name', kind: 'simple', dataType: 'varchar', isNullable: false },
1208
+ { id: 'a3', name: 'building', kind: 'simple', dataType: 'varchar', isNullable: true },
1209
+ { id: 'a4', name: 'budget', kind: 'simple', dataType: 'decimal', isNullable: true, defaultValue: '0.00' },
1210
+ ],
1211
+ },
1212
+ {
1213
+ id: 'e2', name: 'professors', isWeak: false,
1214
+ attributes: [
1215
+ { id: 'a5', name: 'professor_id', kind: 'key', dataType: 'serial', isNullable: false },
1216
+ { id: 'a6', name: 'name', kind: 'composite', dataType: 'varchar', isNullable: false,
1217
+ children: [
1218
+ { id: 'a7', name: 'first_name', kind: 'simple', dataType: 'varchar', isNullable: false },
1219
+ { id: 'a8', name: 'last_name', kind: 'simple', dataType: 'varchar', isNullable: false },
1220
+ ]},
1221
+ { id: 'a9', name: 'email', kind: 'simple', dataType: 'varchar', isNullable: false },
1222
+ { id: 'a10', name: 'phone_numbers', kind: 'multivalued', dataType: 'varchar', isNullable: false,
1223
+ multivaluedType: 'VARCHAR(20)' },
1224
+ ],
1225
+ },
1226
+ {
1227
+ id: 'e3', name: 'students', isWeak: false,
1228
+ attributes: [
1229
+ { id: 'a11', name: 'student_id', kind: 'key', dataType: 'serial', isNullable: false },
1230
+ { id: 'a12', name: 'first_name', kind: 'simple', dataType: 'varchar', isNullable: false },
1231
+ { id: 'a13', name: 'last_name', kind: 'simple', dataType: 'varchar', isNullable: false },
1232
+ { id: 'a14', name: 'dob', kind: 'simple', dataType: 'date', isNullable: false },
1233
+ { id: 'a15', name: 'age', kind: 'derived', dataType: 'integer', isNullable: true,
1234
+ derivedExpression: "EXTRACT(YEAR FROM AGE(CURRENT_DATE, dob))" },
1235
+ ],
1236
+ },
1237
+ {
1238
+ id: 'e4', name: 'courses', isWeak: false,
1239
+ attributes: [
1240
+ { id: 'a16', name: 'course_id', kind: 'key', dataType: 'serial', isNullable: false },
1241
+ { id: 'a17', name: 'title', kind: 'simple', dataType: 'varchar', isNullable: false },
1242
+ { id: 'a18', name: 'credits', kind: 'simple', dataType: 'integer', isNullable: false },
1243
+ ],
1244
+ },
1245
+ ],
1246
+ relationships: [
1247
+ {
1248
+ id: 'r1', name: 'belongs_to', isIdentifying: false,
1249
+ participants: [
1250
+ { entityId: 'e1', cardinality: '1', participation: 'partial' },
1251
+ { entityId: 'e2', cardinality: 'N', participation: 'total' },
1252
+ ],
1253
+ attributes: [],
1254
+ },
1255
+ {
1256
+ id: 'r2', name: 'enrollments', isIdentifying: false,
1257
+ participants: [
1258
+ { entityId: 'e3', cardinality: 'M', participation: 'partial' },
1259
+ { entityId: 'e4', cardinality: 'N', participation: 'partial' },
1260
+ ],
1261
+ attributes: [
1262
+ { id: 'ra1', name: 'grade', kind: 'simple', dataType: 'char', isNullable: true },
1263
+ { id: 'ra2', name: 'enrollment_date', kind: 'simple', dataType: 'date', isNullable: false, defaultValue: 'CURRENT_DATE' },
1264
+ ],
1265
+ },
1266
+ ],
1267
+ };
1268
+
1269
+ const ddl = generateDDL(universityModel, {
1270
+ dialect: 'postgresql',
1271
+ useNamedConstraints: true,
1272
+ includeIndexes: true,
1273
+ includeDropIfExists: true,
1274
+ idStrategy: 'serial',
1275
+ timestampColumns: true,
1276
+ });
1277
+
1278
+ console.log(ddl);
1279
+ ```
1280
+
1281
+ ### Expected Output
1282
+
1283
+ ```sql
1284
+ -- Generated by Dominion Flow ERD Creator
1285
+ -- Database: university_system
1286
+ -- Dialect: postgresql
1287
+ -- Generated: 2026-03-09
1288
+
1289
+ DROP TABLE IF EXISTS enrollments CASCADE;
1290
+ DROP TABLE IF EXISTS professors_phone_numbers CASCADE;
1291
+ DROP TABLE IF EXISTS courses CASCADE;
1292
+ DROP TABLE IF EXISTS students CASCADE;
1293
+ DROP TABLE IF EXISTS professors CASCADE;
1294
+ DROP TABLE IF EXISTS departments CASCADE;
1295
+
1296
+ CREATE TABLE departments (
1297
+ department_id SERIAL NOT NULL,
1298
+ name VARCHAR NOT NULL,
1299
+ building VARCHAR,
1300
+ budget NUMERIC DEFAULT 0.00,
1301
+ created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
1302
+ updated_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
1303
+ CONSTRAINT pk_departments_department_id PRIMARY KEY (department_id)
1304
+ );
1305
+
1306
+ CREATE TABLE professors (
1307
+ professor_id SERIAL NOT NULL,
1308
+ name_first_name VARCHAR NOT NULL,
1309
+ name_last_name VARCHAR NOT NULL,
1310
+ email VARCHAR NOT NULL,
1311
+ created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
1312
+ updated_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
1313
+ CONSTRAINT pk_professors_professor_id PRIMARY KEY (professor_id),
1314
+ departments_id INTEGER NOT NULL,
1315
+ CONSTRAINT fk_professors_departments FOREIGN KEY (departments_id)
1316
+ REFERENCES departments(department_id) ON DELETE RESTRICT
1317
+ );
1318
+
1319
+ CREATE TABLE students (
1320
+ student_id SERIAL NOT NULL,
1321
+ first_name VARCHAR NOT NULL,
1322
+ last_name VARCHAR NOT NULL,
1323
+ dob DATE NOT NULL,
1324
+ age INTEGER GENERATED ALWAYS AS (EXTRACT(YEAR FROM AGE(CURRENT_DATE, dob))) STORED,
1325
+ created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
1326
+ updated_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
1327
+ CONSTRAINT pk_students_student_id PRIMARY KEY (student_id)
1328
+ );
1329
+
1330
+ CREATE TABLE courses (
1331
+ course_id SERIAL NOT NULL,
1332
+ title VARCHAR NOT NULL,
1333
+ credits INTEGER NOT NULL,
1334
+ created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
1335
+ updated_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
1336
+ CONSTRAINT pk_courses_course_id PRIMARY KEY (course_id)
1337
+ );
1338
+
1339
+ CREATE TABLE enrollments (
1340
+ students_student_id INTEGER NOT NULL,
1341
+ courses_course_id INTEGER NOT NULL,
1342
+ grade CHAR,
1343
+ enrollment_date DATE NOT NULL DEFAULT CURRENT_DATE,
1344
+ CONSTRAINT pk_enrollments_students_student_id_courses_course_id
1345
+ PRIMARY KEY (students_student_id, courses_course_id),
1346
+ CONSTRAINT fk_enrollments_students FOREIGN KEY (students_student_id)
1347
+ REFERENCES students(student_id) ON DELETE CASCADE,
1348
+ CONSTRAINT fk_enrollments_courses FOREIGN KEY (courses_course_id)
1349
+ REFERENCES courses(course_id) ON DELETE CASCADE
1350
+ );
1351
+
1352
+ CREATE TABLE professors_phone_numbers (
1353
+ professor_id INTEGER NOT NULL,
1354
+ phone_numbers VARCHAR(20) NOT NULL,
1355
+ CONSTRAINT pk_professors_phone_numbers_professor_id_phone_numbers
1356
+ PRIMARY KEY (professor_id, phone_numbers),
1357
+ CONSTRAINT fk_professors_phone_numbers_professors
1358
+ FOREIGN KEY (professor_id) REFERENCES professors(professor_id)
1359
+ ON DELETE CASCADE
1360
+ );
1361
+
1362
+ -- Indexes
1363
+ CREATE INDEX idx_professors_departments_id ON professors(departments_id);
1364
+ CREATE INDEX idx_enrollments_courses_course_id ON enrollments(courses_course_id);
1365
+ ```
1366
+
1367
+ ---
1368
+
1369
+ ## When to Use
1370
+
1371
+ - Building an ERD Creator / visual database design tool
1372
+ - Implementing a schema-from-diagram code generation feature
1373
+ - Porting an existing ER diagram from a whiteboard or academic tool to running SQL
1374
+ - Teaching database design -- the 7 rules are the canonical reference
1375
+ - Validating that a schema correctly implements an ER model
1376
+ - Generating multi-dialect DDL from a single source-of-truth model
1377
+
1378
+ ## When NOT to Use
1379
+
1380
+ - **Reverse engineering existing SQL to ERD** -- use `sql-ddl-to-json-schema` or `node-sql-parser` instead, then map the JSON into ERDModel types
1381
+ - **Schema migration / diffing** -- use Prisma Migrate, Drizzle Kit, or Alembic; this skill generates initial DDL, not incremental changes
1382
+ - **NoSQL / document databases** -- these mapping rules are exclusively for relational databases
1383
+ - **Denormalization for performance** -- this produces normalized schemas; deliberate denormalization is a separate concern (see data warehousing patterns)
1384
+ - **ORM schema definition** -- if you already use Prisma/Drizzle/TypeORM, define schemas in their DSL and let the ORM handle DDL
1385
+
1386
+ ## Related Skills
1387
+
1388
+ - `database-schema-designer.md` -- broader schema design patterns (multi-tenancy, RLS, audit trails, seeding)
1389
+ - `erd-creator-textbook-research.md` -- raw research findings that feed this skill
1390
+ - `normalization-validator.md` -- planned: 1NF/2NF/3NF violation detection
1391
+ - `regex-alternation-ordering-sql-types.md` -- SQL type parsing for DDL import
1392
+ - `reserved-word-context-aware-quoting.md` -- identifier quoting across dialects
1393
+ - `postgresql-to-mysql-runtime-translation.md` -- runtime SQL translation patterns
1394
+
1395
+ ## References
1396
+
1397
+ 1. **LibreTexts "Database Design"** -- Dr. Sarah North, Chapters 6-10. CC BY 4.0. Canonical ER mapping rules.
1398
+ 2. **Text2Schema (arXiv 2025)** -- Multi-agent NL-to-DDL decomposition. Entity/relationship/constraint extraction pipeline.
1399
+ 3. **NOMAD (arXiv 2025)** -- Agent roles for schema generation: extractor, classifier, integrator, code articulator, verifier.
1400
+ 4. **DrawDB** (github.com/drawdb-io/drawdb) -- OSS visual database designer. Gold standard for dialect-specific DDL generators from JSON model.
1401
+ 5. **ChartDB** (github.com/chartdb/chartdb) -- AI-powered DDL export via LLM prompting.
1402
+ 6. **ERFlow** -- MCP Server with 25+ tools for NL schema edits and checkpoint-based migration generation.
1403
+ 7. **node-sql-parser** (npm) -- Bidirectional SQL-to-AST parsing. `parser.astify()` / `parser.sqlify()`.
1404
+ 8. **sql-ddl-to-json-schema** (npm) -- DDL parser outputting ERD-friendly JSON Schema.
1405
+ 9. **Peter Chen (1976)** -- "The Entity-Relationship Model: Toward a Unified View of Data." Original ER notation.