@skill-graph/cli 0.5.6

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 (330) hide show
  1. package/CHANGELOG.md +247 -0
  2. package/LICENSE +200 -0
  3. package/NOTICE +62 -0
  4. package/README.md +398 -0
  5. package/SKILL_GRAPH.md +443 -0
  6. package/bin/skill-graph.js +374 -0
  7. package/docs/ADOPTION.md +117 -0
  8. package/docs/CONFORMANCE.md +66 -0
  9. package/docs/PRIMER.md +384 -0
  10. package/docs/QUICKSTART-30MIN.md +333 -0
  11. package/docs/ROUTING-METRICS.md +120 -0
  12. package/docs/SKILL-MD-FORMAT-COMPATIBILITY.md +127 -0
  13. package/docs/SKILL_AUDIT_CHECKLIST.md +199 -0
  14. package/docs/SKILL_AUDIT_LOOP.md +195 -0
  15. package/docs/SKILL_METADATA_PROTOCOL.md +609 -0
  16. package/docs/_archived/marketplace-publication-priority-2026-05-18.md +239 -0
  17. package/docs/adr/0001-predicate-set.md +69 -0
  18. package/docs/adr/0002-json-ld-context.md +82 -0
  19. package/docs/adr/0003-ontoclean-rigidity-tags.md +65 -0
  20. package/docs/adr/0004-persistent-identifiers.md +74 -0
  21. package/docs/adr/0005-freshness-consolidation.md +70 -0
  22. package/docs/adr/0006-revise-predicate-rename.md +105 -0
  23. package/docs/adr/0007-audit-loop-cadence.md +99 -0
  24. package/docs/adr/0008-skill-surface-split-and-curation-policy.md +93 -0
  25. package/docs/category-consumers.md +168 -0
  26. package/docs/concept-map.md +194 -0
  27. package/docs/diagrams/drift-states.mmd +21 -0
  28. package/docs/diagrams/manifest-pipeline.mmd +25 -0
  29. package/docs/diagrams/routing-harness.mmd +41 -0
  30. package/docs/diagrams/starter-graph.mmd +53 -0
  31. package/docs/field-decision-guide.md +315 -0
  32. package/docs/field-rationale.md +211 -0
  33. package/docs/field-reference.generated.md +624 -0
  34. package/docs/field-reference.md +1426 -0
  35. package/docs/glossary.md +190 -0
  36. package/docs/head-noun-glossary.md +63 -0
  37. package/docs/images/audit-phases.png +0 -0
  38. package/docs/images/drift-states.png +0 -0
  39. package/docs/images/graded-mode.png +0 -0
  40. package/docs/images/manifest-pipeline.png +0 -0
  41. package/docs/images/routing-harness.png +0 -0
  42. package/docs/images/skill-anatomy.png +0 -0
  43. package/docs/images/starter-graph.png +0 -0
  44. package/docs/images/system-model.png +0 -0
  45. package/docs/integrations/github-actions.md +155 -0
  46. package/docs/manifest-field-mapping.md +443 -0
  47. package/docs/marketplace-publication-queue.generated.md +240 -0
  48. package/docs/marketplace-release-agent-prompt.md +82 -0
  49. package/docs/marketplace-skill-candidate-list.md +272 -0
  50. package/docs/marketplace-syndication.md +222 -0
  51. package/docs/migration-sample-review.md +155 -0
  52. package/docs/migrations/v4-to-v5.md +168 -0
  53. package/docs/migrations/v5-to-v6.md +221 -0
  54. package/docs/name-exceptions.yaml +37 -0
  55. package/docs/plans/marketplace-p1-public-migration-plan.md +41 -0
  56. package/docs/plans/multi-root-workspace.md +148 -0
  57. package/docs/plans/scripts-roadmap.md +107 -0
  58. package/docs/plans/v4-schema-bump.md +160 -0
  59. package/docs/plans/wave-2-extraction.md +122 -0
  60. package/docs/positioning-vs-marketplaces.md +175 -0
  61. package/docs/proposals/skill-audit-loop-positioning.md +160 -0
  62. package/docs/quality-doctrine.md +138 -0
  63. package/docs/recommended-skills.md +150 -0
  64. package/docs/research/skill-comprehension-eval-research.md +1830 -0
  65. package/docs/research/skill-retrieval-evidence.md +66 -0
  66. package/docs/skill-metadata-protocol.md +471 -0
  67. package/docs/skills-sh-maintainer-cleanup-request.md +80 -0
  68. package/examples/audits/a11y/findings.md +52 -0
  69. package/examples/audits/a11y/scorecard.md +21 -0
  70. package/examples/audits/a11y/verdict.md +44 -0
  71. package/examples/audits/debugging/findings.md +59 -0
  72. package/examples/audits/debugging/scorecard.md +22 -0
  73. package/examples/audits/debugging/verdict.md +33 -0
  74. package/examples/audits/documentation/findings.md +59 -0
  75. package/examples/audits/documentation/scorecard.md +22 -0
  76. package/examples/audits/documentation/verdict.md +33 -0
  77. package/examples/evals/a11y.json +140 -0
  78. package/examples/evals/api-design.json +52 -0
  79. package/examples/evals/code-review.json +52 -0
  80. package/examples/evals/data-modeling.json +52 -0
  81. package/examples/evals/database-migration.json +52 -0
  82. package/examples/evals/debugging.json +118 -0
  83. package/examples/evals/dependency-architecture.json +52 -0
  84. package/examples/evals/design-system-architecture.json +52 -0
  85. package/examples/evals/error-tracking.json +52 -0
  86. package/examples/evals/event-contract-design.json +52 -0
  87. package/examples/evals/form-ux-architecture.json +52 -0
  88. package/examples/evals/framework-fit-analysis.json +52 -0
  89. package/examples/evals/graph-audit.json +139 -0
  90. package/examples/evals/information-architecture.json +52 -0
  91. package/examples/evals/interaction-feedback.json +52 -0
  92. package/examples/evals/interaction-patterns.json +52 -0
  93. package/examples/evals/layout-composition.json +52 -0
  94. package/examples/evals/lint-overlay.json +117 -0
  95. package/examples/evals/microcopy.json +52 -0
  96. package/examples/evals/observability-modeling.json +52 -0
  97. package/examples/evals/pattern-recognition.json +96 -0
  98. package/examples/evals/performance-engineering.json +52 -0
  99. package/examples/evals/refactor.json +128 -0
  100. package/examples/evals/semiotics.json +52 -0
  101. package/examples/evals/skill-infrastructure.json +96 -0
  102. package/examples/evals/skill-router.json +140 -0
  103. package/examples/evals/skill-router.routing.json +113 -0
  104. package/examples/evals/system-interface-contracts.json +52 -0
  105. package/examples/evals/task-analysis.json +52 -0
  106. package/examples/evals/testing-strategy.json +118 -0
  107. package/examples/evals/type-safety.json +249 -0
  108. package/examples/evals/visual-design-foundations.json +52 -0
  109. package/examples/evals/webhook-integration.json +52 -0
  110. package/examples/exports/a11y.skill-md.md +80 -0
  111. package/examples/exports/debugging.skill-md.md +80 -0
  112. package/examples/exports/refactor.skill-md.md +78 -0
  113. package/examples/exports/testing-strategy.skill-md.md +81 -0
  114. package/examples/projects/markdown-static-site/README.md +115 -0
  115. package/examples/projects/markdown-static-site/skills/content-source-router/SKILL.md +131 -0
  116. package/examples/projects/markdown-static-site/skills/image-optimization-pipeline-config/SKILL.md +132 -0
  117. package/examples/projects/markdown-static-site/skills/link-rot-detection/SKILL.md +103 -0
  118. package/examples/projects/markdown-static-site/skills/markdown-post-frontmatter-validation/SKILL.md +133 -0
  119. package/examples/projects/markdown-static-site/skills/migrate-posts-to-v2-frontmatter/SKILL.md +140 -0
  120. package/examples/projects/saas-stripe-postgres/README.md +208 -0
  121. package/examples/projects/saas-stripe-postgres/db/migrations/0004_canonicalize_orders.sql +37 -0
  122. package/examples/projects/saas-stripe-postgres/db/schema.sql +112 -0
  123. package/examples/projects/saas-stripe-postgres/skills/migrate-orders-to-canonical-schema/SKILL.md +149 -0
  124. package/examples/projects/saas-stripe-postgres/skills/nextjs-server-action-validation/SKILL.md +154 -0
  125. package/examples/projects/saas-stripe-postgres/skills/payment-provider-router/SKILL.md +153 -0
  126. package/examples/projects/saas-stripe-postgres/skills/postgres-rls-pattern/SKILL.md +163 -0
  127. package/examples/projects/saas-stripe-postgres/skills/stripe-webhook-signature-verification/SKILL.md +137 -0
  128. package/examples/protocol/skill-metadata-template.md +301 -0
  129. package/examples/protocol/skills.manifest.sample.json +13245 -0
  130. package/examples/skill-metadata-template.md +317 -0
  131. package/examples/skills.manifest.sample.json +13519 -0
  132. package/examples/tests/v3-1-skos-fixture/SKILL.md +93 -0
  133. package/marketplace/README.md +17 -0
  134. package/marketplace/skills/a11y/SKILL.md +66 -0
  135. package/marketplace/skills/acid-fundamentals/SKILL.md +106 -0
  136. package/marketplace/skills/agent-engineering/SKILL.md +386 -0
  137. package/marketplace/skills/agent-eval-design/SKILL.md +55 -0
  138. package/marketplace/skills/ai-native-development/SKILL.md +294 -0
  139. package/marketplace/skills/api-design/SKILL.md +60 -0
  140. package/marketplace/skills/architecture-decision-records/SKILL.md +55 -0
  141. package/marketplace/skills/background-jobs/SKILL.md +265 -0
  142. package/marketplace/skills/bounded-context-mapping/SKILL.md +55 -0
  143. package/marketplace/skills/cap-theorem-tradeoffs/SKILL.md +127 -0
  144. package/marketplace/skills/client-server-boundary/SKILL.md +187 -0
  145. package/marketplace/skills/code-review/SKILL.md +120 -0
  146. package/marketplace/skills/color-system-design/SKILL.md +43 -0
  147. package/marketplace/skills/component-architecture/SKILL.md +126 -0
  148. package/marketplace/skills/compression/SKILL.md +112 -0
  149. package/marketplace/skills/conceptual-modeling/SKILL.md +181 -0
  150. package/marketplace/skills/connection-pooling/SKILL.md +105 -0
  151. package/marketplace/skills/constraint-awareness/SKILL.md +287 -0
  152. package/marketplace/skills/content-monitor/SKILL.md +209 -0
  153. package/marketplace/skills/context-engineering/SKILL.md +320 -0
  154. package/marketplace/skills/context-graph/SKILL.md +174 -0
  155. package/marketplace/skills/context-management/SKILL.md +174 -0
  156. package/marketplace/skills/context-window/SKILL.md +239 -0
  157. package/marketplace/skills/contract-testing/SKILL.md +120 -0
  158. package/marketplace/skills/cron-scheduling/SKILL.md +223 -0
  159. package/marketplace/skills/dark-mode-implementation/SKILL.md +47 -0
  160. package/marketplace/skills/data-modeling/SKILL.md +59 -0
  161. package/marketplace/skills/data-modeling-fundamentals/SKILL.md +117 -0
  162. package/marketplace/skills/database-migration/SKILL.md +429 -0
  163. package/marketplace/skills/debugging/SKILL.md +67 -0
  164. package/marketplace/skills/dependency-architecture/SKILL.md +58 -0
  165. package/marketplace/skills/design-module-composition/SKILL.md +43 -0
  166. package/marketplace/skills/design-system-architecture/SKILL.md +61 -0
  167. package/marketplace/skills/design-thinking/SKILL.md +44 -0
  168. package/marketplace/skills/diagnosis/SKILL.md +296 -0
  169. package/marketplace/skills/diff-analysis/SKILL.md +188 -0
  170. package/marketplace/skills/e2e-test-design/SKILL.md +113 -0
  171. package/marketplace/skills/entity-relationship-modeling/SKILL.md +218 -0
  172. package/marketplace/skills/epistemic-grounding/SKILL.md +112 -0
  173. package/marketplace/skills/error-boundary/SKILL.md +235 -0
  174. package/marketplace/skills/error-tracking/SKILL.md +261 -0
  175. package/marketplace/skills/eval-driven-development/SKILL.md +147 -0
  176. package/marketplace/skills/evaluation/SKILL.md +113 -0
  177. package/marketplace/skills/event-contract-design/SKILL.md +60 -0
  178. package/marketplace/skills/event-storming/SKILL.md +56 -0
  179. package/marketplace/skills/form-ux-architecture/SKILL.md +60 -0
  180. package/marketplace/skills/framework-fit-analysis/SKILL.md +59 -0
  181. package/marketplace/skills/frontend-architecture/SKILL.md +43 -0
  182. package/marketplace/skills/generative-ui/SKILL.md +118 -0
  183. package/marketplace/skills/graph-audit/SKILL.md +81 -0
  184. package/marketplace/skills/guardrails/SKILL.md +118 -0
  185. package/marketplace/skills/hooks-patterns/SKILL.md +185 -0
  186. package/marketplace/skills/http-semantics/SKILL.md +136 -0
  187. package/marketplace/skills/ideation/SKILL.md +41 -0
  188. package/marketplace/skills/indexing-strategy/SKILL.md +108 -0
  189. package/marketplace/skills/information-architecture/SKILL.md +59 -0
  190. package/marketplace/skills/integration-test-design/SKILL.md +111 -0
  191. package/marketplace/skills/intent-recognition/SKILL.md +136 -0
  192. package/marketplace/skills/interaction-feedback/SKILL.md +59 -0
  193. package/marketplace/skills/interaction-patterns/SKILL.md +59 -0
  194. package/marketplace/skills/journey-mapping/SKILL.md +41 -0
  195. package/marketplace/skills/keywords/SKILL.md +213 -0
  196. package/marketplace/skills/knowledge-modeling/SKILL.md +232 -0
  197. package/marketplace/skills/layout-composition/SKILL.md +59 -0
  198. package/marketplace/skills/linguistics/SKILL.md +429 -0
  199. package/marketplace/skills/lint-overlay/SKILL.md +76 -0
  200. package/marketplace/skills/mental-models/SKILL.md +126 -0
  201. package/marketplace/skills/merge-queue/SKILL.md +94 -0
  202. package/marketplace/skills/methodology/SKILL.md +317 -0
  203. package/marketplace/skills/microcopy/SKILL.md +232 -0
  204. package/marketplace/skills/middleware-patterns/SKILL.md +363 -0
  205. package/marketplace/skills/mobile-responsive-ux/SKILL.md +287 -0
  206. package/marketplace/skills/mutation-testing/SKILL.md +112 -0
  207. package/marketplace/skills/naming-conventions/SKILL.md +112 -0
  208. package/marketplace/skills/observability-modeling/SKILL.md +59 -0
  209. package/marketplace/skills/ontology-modeling/SKILL.md +67 -0
  210. package/marketplace/skills/owasp-security/SKILL.md +153 -0
  211. package/marketplace/skills/pattern-recognition/SKILL.md +472 -0
  212. package/marketplace/skills/performance-budgets/SKILL.md +185 -0
  213. package/marketplace/skills/performance-engineering/SKILL.md +58 -0
  214. package/marketplace/skills/performance-testing/SKILL.md +125 -0
  215. package/marketplace/skills/printify/SKILL.md +42 -0
  216. package/marketplace/skills/prioritization/SKILL.md +118 -0
  217. package/marketplace/skills/problem-framing/SKILL.md +41 -0
  218. package/marketplace/skills/problem-locating-solving/SKILL.md +203 -0
  219. package/marketplace/skills/project-knowledge-extraction/SKILL.md +54 -0
  220. package/marketplace/skills/prompt-craft/SKILL.md +134 -0
  221. package/marketplace/skills/prompt-injection-defense/SKILL.md +132 -0
  222. package/marketplace/skills/property-based-testing/SKILL.md +100 -0
  223. package/marketplace/skills/prototyping/SKILL.md +43 -0
  224. package/marketplace/skills/query-optimization/SKILL.md +144 -0
  225. package/marketplace/skills/real-time-updates/SKILL.md +324 -0
  226. package/marketplace/skills/ref-patterns/SKILL.md +284 -0
  227. package/marketplace/skills/refactor/SKILL.md +65 -0
  228. package/marketplace/skills/rendering-models/SKILL.md +142 -0
  229. package/marketplace/skills/replication-patterns/SKILL.md +110 -0
  230. package/marketplace/skills/research-synthesis/SKILL.md +41 -0
  231. package/marketplace/skills/route-handler-design/SKILL.md +347 -0
  232. package/marketplace/skills/schema-evolution/SKILL.md +140 -0
  233. package/marketplace/skills/security-fundamentals/SKILL.md +139 -0
  234. package/marketplace/skills/semantic-center/SKILL.md +194 -0
  235. package/marketplace/skills/semantic-relations/SKILL.md +250 -0
  236. package/marketplace/skills/semantics/SKILL.md +366 -0
  237. package/marketplace/skills/semiotics/SKILL.md +230 -0
  238. package/marketplace/skills/seo-strategy/SKILL.md +260 -0
  239. package/marketplace/skills/server-actions-design/SKILL.md +243 -0
  240. package/marketplace/skills/server-components-design/SKILL.md +190 -0
  241. package/marketplace/skills/sharding-strategy/SKILL.md +123 -0
  242. package/marketplace/skills/shopify/SKILL.md +42 -0
  243. package/marketplace/skills/skill-infrastructure/SKILL.md +320 -0
  244. package/marketplace/skills/skill-router/SKILL.md +71 -0
  245. package/marketplace/skills/skill-scaffold/SKILL.md +105 -0
  246. package/marketplace/skills/snapshot-testing/SKILL.md +120 -0
  247. package/marketplace/skills/spec-driven-development/SKILL.md +148 -0
  248. package/marketplace/skills/state-machine-modeling/SKILL.md +56 -0
  249. package/marketplace/skills/state-management/SKILL.md +134 -0
  250. package/marketplace/skills/streaming-architecture/SKILL.md +194 -0
  251. package/marketplace/skills/summarization/SKILL.md +156 -0
  252. package/marketplace/skills/suspense-patterns/SKILL.md +265 -0
  253. package/marketplace/skills/system-interface-contracts/SKILL.md +59 -0
  254. package/marketplace/skills/task-analysis/SKILL.md +201 -0
  255. package/marketplace/skills/taxonomy-design/SKILL.md +66 -0
  256. package/marketplace/skills/test-coverage-strategy/SKILL.md +108 -0
  257. package/marketplace/skills/test-doubles-design/SKILL.md +98 -0
  258. package/marketplace/skills/test-driven-development/SKILL.md +96 -0
  259. package/marketplace/skills/testing-strategy/SKILL.md +67 -0
  260. package/marketplace/skills/theme-system-design/SKILL.md +43 -0
  261. package/marketplace/skills/tool-call-flow/SKILL.md +229 -0
  262. package/marketplace/skills/tool-call-strategy/SKILL.md +292 -0
  263. package/marketplace/skills/transaction-isolation/SKILL.md +98 -0
  264. package/marketplace/skills/type-safety/SKILL.md +177 -0
  265. package/marketplace/skills/typography-system/SKILL.md +43 -0
  266. package/marketplace/skills/usability-testing/SKILL.md +43 -0
  267. package/marketplace/skills/user-research/SKILL.md +43 -0
  268. package/marketplace/skills/vercel-composition-patterns/SKILL.md +157 -0
  269. package/marketplace/skills/version-control/SKILL.md +233 -0
  270. package/marketplace/skills/visual-design-foundations/SKILL.md +59 -0
  271. package/marketplace/skills/visual-hierarchy/SKILL.md +43 -0
  272. package/marketplace/skills/webhook-integration/SKILL.md +331 -0
  273. package/marketplace/skills/writing-humanizer/SKILL.md +380 -0
  274. package/package.json +67 -0
  275. package/schemas/manifest.schema.json +811 -0
  276. package/schemas/manifest.v2.schema.json +164 -0
  277. package/schemas/manifest.v3.schema.json +758 -0
  278. package/schemas/manifest.v4.schema.json +755 -0
  279. package/schemas/manifest.v5.schema.json +755 -0
  280. package/schemas/manifest.v6.schema.json +811 -0
  281. package/schemas/skill.context.jsonld +279 -0
  282. package/schemas/skill.schema.json +919 -0
  283. package/schemas/skill.v2.schema.json +201 -0
  284. package/schemas/skill.v3.schema.json +827 -0
  285. package/schemas/skill.v4.schema.json +822 -0
  286. package/schemas/skill.v5.schema.json +830 -0
  287. package/schemas/skill.v6.schema.json +946 -0
  288. package/schemas/vocabulary/keywords.json +180 -0
  289. package/schemas/vocabulary/workspace_tags.json +23 -0
  290. package/scripts/__tests__/migrate-skill-v2-to-v3.test.js +161 -0
  291. package/scripts/__tests__/migrate-skill-v3-to-v4.test.js +158 -0
  292. package/scripts/__tests__/test-export-parser-drift.js +149 -0
  293. package/scripts/__tests__/test-marketplace-export.js +114 -0
  294. package/scripts/__tests__/test-router-paths.js +82 -0
  295. package/scripts/__tests__/test-stability-promotion.js +244 -0
  296. package/scripts/__tests__/test-v3-1-alias-contract.js +109 -0
  297. package/scripts/__tests__/test-v3-1-skos-runtime.js +116 -0
  298. package/scripts/backfill-schema-version.js +198 -0
  299. package/scripts/build-field-reference.js +160 -0
  300. package/scripts/build-retrieval-baseline.js +511 -0
  301. package/scripts/check-markdown-links.js +211 -0
  302. package/scripts/check-protocol-consistency.js +979 -0
  303. package/scripts/export-marketplace-skills.js +610 -0
  304. package/scripts/export-skill.js +374 -0
  305. package/scripts/generate-manifest.js +787 -0
  306. package/scripts/lib/alias-contract.js +83 -0
  307. package/scripts/lib/audit-prompt-builder.js +771 -0
  308. package/scripts/lib/mock-grader.js +134 -0
  309. package/scripts/lib/parse-frontmatter.js +429 -0
  310. package/scripts/lib/roots.js +119 -0
  311. package/scripts/lint/check-archetype-sections.js +185 -0
  312. package/scripts/lint/check-category-enum.js +83 -0
  313. package/scripts/lint/check-routing-eval.js +146 -0
  314. package/scripts/lint/check-routing-quality.js +211 -0
  315. package/scripts/lint/check-stability-promotion.js +220 -0
  316. package/scripts/lint/format-code-frame.js +206 -0
  317. package/scripts/marketplace-install.js +125 -0
  318. package/scripts/migrate-category-to-enum.js +169 -0
  319. package/scripts/migrate-skill-v2-to-v3.js +424 -0
  320. package/scripts/migrate-skill-v3-to-v4.js +200 -0
  321. package/scripts/migrate-skill-v5-to-v6.js +304 -0
  322. package/scripts/restructure-by-category.js +85 -0
  323. package/scripts/seed-publication-classification.js +282 -0
  324. package/scripts/skill-audit.js +893 -0
  325. package/scripts/skill-graph-drift.js +483 -0
  326. package/scripts/skill-graph-route.js +766 -0
  327. package/scripts/skill-graph-routing-eval.js +393 -0
  328. package/scripts/skill-lint.js +1317 -0
  329. package/scripts/skill-overlap.js +213 -0
  330. package/scripts/verify-skill-md-export.js +201 -0
@@ -0,0 +1,429 @@
1
+ ---
2
+ name: database-migration
3
+ description: "Use when planning or applying a raw-SQL database migration to a live PostgreSQL database — adding columns, renaming columns or tables, changing types, creating indexes, adding foreign keys, or running data backfills. Covers zero-downtime patterns (expand / contract, batched backfill, NOT VALID foreign keys, CONCURRENTLY indexes), the unpooled-connection requirement for DDL, branched-database workflows, and rollback strategy. Do NOT use for ORM-managed migrations driven by Prisma/Drizzle/TypeORM CLI scaffolding (the generation rules are tool-specific), for chasing a migration that has already failed in production (use `debugging`), or for designing the row-level-security model itself (use `owasp-security`)."
4
+ license: MIT
5
+ compatibility: "PostgreSQL 12+ (covers concurrent index, NOT VALID foreign keys, generated columns). Connection examples target raw `psql` and the unpooled side of any PgBouncer-style pooler. Branching examples reference `neonctl` as one provider — substitute the equivalent CLI for Supabase, Xata, or self-hosted clones if your platform supports branched databases."
6
+ allowed-tools: Read Grep Bash Edit
7
+ metadata:
8
+ metadata: "{\"schema_version\":6,\"version\":\"1.0.0\",\"type\":\"capability\",\"category\":\"engineering\",\"domain\":\"data/migrations\",\"scope\":\"portable\",\"owner\":\"skill-graph-maintainer\",\"freshness\":\"2026-05-06\",\"drift_check\":\"{\\\\\\\"last_verified\\\\\\\":\\\\\\\"2026-05-06\\\\\\\"}\",\"eval_artifacts\":\"present\",\"eval_state\":\"unverified\",\"routing_eval\":\"absent\",\"stability\":\"experimental\",\"keywords\":\"[\\\\\\\"database migration\\\\\\\",\\\\\\\"schema migration\\\\\\\",\\\\\\\"zero-downtime migration\\\\\\\",\\\\\\\"DDL migration\\\\\\\",\\\\\\\"raw SQL migration\\\\\\\",\\\\\\\"Postgres DDL\\\\\\\",\\\\\\\"alter table production\\\\\\\",\\\\\\\"expand contract migration\\\\\\\",\\\\\\\"concurrent index creation\\\\\\\",\\\\\\\"migration rollback\\\\\\\",\\\\\\\"add column nullable\\\\\\\",\\\\\\\"rename column zero downtime\\\\\\\",\\\\\\\"batched backfill\\\\\\\",\\\\\\\"non-constant default\\\\\\\",\\\\\\\"NOT VALID foreign key\\\\\\\",\\\\\\\"branched database migration\\\\\\\",\\\\\\\"point-in-time restore\\\\\\\",\\\\\\\"DDL transaction\\\\\\\"]\",\"examples\":\"[\\\\\\\"add a nullable column to a 50M-row orders table without taking downtime\\\\\\\",\\\\\\\"rename the `display_name` column to `username` while the app is live\\\\\\\",\\\\\\\"create a btree index on a 100M-row table without locking writes\\\\\\\",\\\\\\\"the migration takes ACCESS EXCLUSIVE — how do I avoid the lock?\\\\\\\",\\\\\\\"add a foreign key to a 10M-row table without blocking writes\\\\\\\",\\\\\\\"should I use ADD COLUMN ... NOT NULL DEFAULT 0 in this migration?\\\\\\\",\\\\\\\"write a rollback strategy for this schema change in case production breaks\\\\\\\",\\\\\\\"split the migration into expand and contract phases across two deploys\\\\\\\"]\",\"anti_examples\":\"[\\\\\\\"design the row-level-security model for our new tenant table\\\\\\\",\\\\\\\"the migration crashed in production — find the root cause\\\\\\\",\\\\\\\"explain our migration conventions in the contributor docs\\\\\\\",\\\\\\\"refactor the migration runner helper for clarity\\\\\\\",\\\\\\\"decide whether this column rename needs an automated regression test\\\\\\\",\\\\\\\"review this AI-generated DDL diff for correctness\\\\\\\"]\",\"relations\":\"{\\\\\\\"boundary\\\\\\\":[{\\\\\\\"skill\\\\\\\":\\\\\\\"debugging\\\\\\\",\\\\\\\"reason\\\\\\\":\\\\\\\"debugging chases an observed migration failure; database-migration plans the safe DDL path forward before execution\\\\\\\"},{\\\\\\\"skill\\\\\\\":\\\\\\\"refactor\\\\\\\",\\\\\\\"reason\\\\\\\":\\\\\\\"refactor is behavior-preserving code reorganization; database-migration is schema mutation that may require coordinated code changes across two or more deploys\\\\\\\"},{\\\\\\\"skill\\\\\\\":\\\\\\\"testing-strategy\\\\\\\",\\\\\\\"reason\\\\\\\":\\\\\\\"testing-strategy decides what deserves a regression test; database-migration owns the verification dance that runs before, during, and after applying DDL\\\\\\\"}],\\\\\\\"related\\\\\\\":[\\\\\\\"testing-strategy\\\\\\\",\\\\\\\"debugging\\\\\\\",\\\\\\\"owasp-security\\\\\\\"],\\\\\\\"verify_with\\\\\\\":[\\\\\\\"testing-strategy\\\\\\\",\\\\\\\"code-review\\\\\\\"]}\",\"portability\":\"{\\\\\\\"readiness\\\\\\\":\\\\\\\"scripted\\\\\\\",\\\\\\\"targets\\\\\\\":[\\\\\\\"skill-md\\\\\\\"]}\",\"lifecycle\":\"{\\\\\\\"stale_after_days\\\\\\\":90,\\\\\\\"review_cadence\\\\\\\":\\\\\\\"quarterly\\\\\\\"}\",\"skill_graph_source_repo\":\"https://github.com/jacob-balslev/skill-graph\",\"skill_graph_protocol\":\"Skill Metadata Protocol v5\",\"skill_graph_project\":\"Skill Graph\",\"skill_graph_canonical_skill\":\"skills/database-migration/SKILL.md\"}"
9
+ skill_graph_source_repo: "https://github.com/jacob-balslev/skill-graph"
10
+ skill_graph_protocol: Skill Metadata Protocol v4
11
+ skill_graph_project: Skill Graph
12
+ skill_graph_canonical_skill: skills/database-migration/SKILL.md
13
+ ---
14
+
15
+ # Database Migration
16
+
17
+ ## Coverage
18
+
19
+ - Migration file conventions: chronological filenames, headers documenting purpose and rollback, `BEGIN/COMMIT` framing, when DDL must escape the transaction
20
+ - Connection requirements: why DDL needs an unpooled connection (PgBouncer transaction-mode rejects DDL session state), and the two-URL pattern for live applications
21
+ - Branched-database workflow: create a branch, apply migration, schema-diff against parent, apply to main, prune the branch — with a vendor-capability matrix
22
+ - Common DDL patterns: nullable column, column with constant default, column with non-constant default on a large table (batched backfill), zero-downtime rename via expand / contract, type change via shadow column, concurrent index creation, low-lock foreign key (NOT VALID + VALIDATE)
23
+ - Tenant-scoped schema additions: how to keep multi-tenant isolation safe across migrations (RLS-policy-in-migration discipline), without owning the policy design itself
24
+ - Zero-downtime table rename: compatibility view pattern that lets old code keep reading while the new name takes over
25
+ - Rollback strategy: transactional rollback for structural changes, `DOWN` paths for non-transactional ones, point-in-time restore as the last resort
26
+ - Pre-production checklist: schema diff, branch test run, NOT VALID gating, CONCURRENTLY gating, RLS gating, rollback documented in header
27
+
28
+ ## Philosophy
29
+
30
+ A migration is the only operation in the application stack that is *both* shipped as code *and* irreversible by default. Application code can be reverted by re-deploying yesterday's commit; a `DROP COLUMN` cannot. Treat every migration as a one-way door unless you have explicitly designed the reverse door alongside it.
31
+
32
+ The dominant failure mode is the *plausible-looking single-statement migration*: `ALTER TABLE orders ADD COLUMN sync_version INTEGER NOT NULL DEFAULT 0;` looks fine on a small dev database and locks the table for minutes on a 50M-row production table. Zero-downtime migration is a discipline of *splitting* the apparent single change into a sequence of low-lock steps that each remain compatible with the running application. The split is not theoretical — it is the difference between a deploy and an outage.
33
+
34
+ The second failure mode is connection routing. Most production Postgres deployments place a transaction-mode pooler in front of the database for application traffic; that pooler does not support most DDL because DDL needs session-level state the pooler discards between transactions. A migration script that runs fine on a developer laptop and fails silently in production is almost always pointed at the pooled URL. The fix is a non-negotiable two-URL convention: pooled for application reads and writes, unpooled for migrations.
35
+
36
+ The third failure mode is *invisible* — running a migration without a rollback path documented next to it. Six months later, someone has to undo the change under incident pressure, with no record of what the reverse looks like. The discipline is to write the `DOWN` while the `UP` is still fresh, and to test it on a branched database before merging.
37
+
38
+ ## Migration File Convention
39
+
40
+ Migrations are versioned, chronological, and applied in deterministic order. The naming convention has to encode both *when* the migration was authored and *what* it does:
41
+
42
+ ```
43
+ db/migrations/2026_05_06_add_subscription_plan.sql
44
+ db/migrations/2026_05_07_rename_full_name_to_display_name.sql
45
+ db/migrations/2026_05_08_add_index_orders_org_created.sql
46
+ ```
47
+
48
+ Use underscore-separated date parts (`YYYY_MM_DD`) so file-system sort order matches authoring order even at month boundaries. Append a snake_case description that names the change in 3-6 words.
49
+
50
+ **Critical rule: never rename an applied migration.** Migration runners track applied files by name; renaming makes a previously-applied migration appear new, leading to duplicate application or conflicts on the next run. If a name is wrong, accept it and move on.
51
+
52
+ Every migration file opens with a header that names purpose, related ticket (if any), and safety classification:
53
+
54
+ ```sql
55
+ -- Migration: 2026_05_06_add_subscription_plan
56
+ -- Purpose: Add subscription_plans table with org_id, RLS, and app_role grants.
57
+ -- Related: <ticket-id, optional>
58
+ -- Safety: ONLINE (alternatives: REQUIRES MAINTENANCE WINDOW, FORWARD-ONLY)
59
+ -- Rollback: DROP TABLE subscription_plans; -- safe to run; no downstream FKs yet.
60
+
61
+ BEGIN;
62
+
63
+ -- ... DDL ...
64
+
65
+ COMMIT;
66
+ ```
67
+
68
+ The `Rollback:` line is mandatory. If the rollback is "rebuild from a point-in-time snapshot," say so explicitly — the absence of a row-level rollback is a deliberate design choice that the next reader needs to know.
69
+
70
+ ## Connection: Unpooled for DDL
71
+
72
+ DDL statements (`ALTER TABLE`, `CREATE INDEX`, `CREATE TABLE`, `DROP TABLE`, `CREATE OR REPLACE FUNCTION`) need session-level state that PgBouncer's transaction mode does not preserve. The standard pattern is two connection URLs sourced from environment variables:
73
+
74
+ ```
75
+ DATABASE_URL — pooled, for application reads and writes
76
+ DATABASE_URL_UNPOOLED — direct, for migrations and operations that need session GUCs
77
+ ```
78
+
79
+ The pooled URL typically points at the pooler host on its dedicated port (commonly `6543`) with a `pgbouncer=true` query parameter; the unpooled URL points at the database host directly (commonly `5432`). Migration runners always use the unpooled URL:
80
+
81
+ ```bash
82
+ psql "$DATABASE_URL_UNPOOLED" -f db/migrations/2026_05_06_add_subscription_plan.sql
83
+ ```
84
+
85
+ A migration that runs cleanly on a developer database (which is direct, no pooler) and fails or behaves oddly in CI or production is almost always pointed at the pooled URL by default. Audit the runner before assuming the SQL is wrong.
86
+
87
+ ## Branched-Database Workflow
88
+
89
+ If your Postgres provider supports branching (Neon, Supabase clones, Xata, or self-hosted Postgres-with-snapshots), every non-trivial migration runs on a branch first:
90
+
91
+ | Capability | Why it matters for migrations |
92
+ |---|---|
93
+ | Branch from current `main` state | Isolates the migration from live traffic during verification |
94
+ | Schema diff between branches | Confirms the migration changed only what was intended |
95
+ | Point-in-time restore on the parent | Last-resort rollback if the migration shipped and corrupted data |
96
+ | Per-branch connection string | Lets the agent run the migration against the branch with a single env var swap |
97
+
98
+ Example workflow with one provider's CLI (substitute the equivalent for your platform):
99
+
100
+ ```bash
101
+ # 1. Create a migration branch from main
102
+ neonctl branches create --name "migration/add-subscription-plan" --parent main
103
+
104
+ # 2. Get the unpooled connection string for the branch
105
+ BRANCH_URL=$(neonctl cs migration/add-subscription-plan --pooled false)
106
+
107
+ # 3. Apply the migration on the branch
108
+ psql "$BRANCH_URL" -f db/migrations/2026_05_06_add_subscription_plan.sql
109
+
110
+ # 4. Verify the resulting structure
111
+ psql "$BRANCH_URL" -c "\d subscription_plans"
112
+
113
+ # 5. Schema-diff the branch against main — should show only intended changes
114
+ neonctl branches schema-diff main migration/add-subscription-plan
115
+
116
+ # 6. Apply to main once the diff is clean
117
+ psql "$DATABASE_URL_UNPOOLED" -f db/migrations/2026_05_06_add_subscription_plan.sql
118
+
119
+ # 7. Delete the migration branch
120
+ neonctl branches delete migration/add-subscription-plan
121
+ ```
122
+
123
+ If your provider does not support branching, the substitute is a fresh database restored from a recent production snapshot; the workflow is the same in shape but slower in cycle time.
124
+
125
+ ## Common DDL Patterns
126
+
127
+ ### Add a Column
128
+
129
+ Adding a nullable column or a column with a *constant* default is metadata-only on PostgreSQL 11+ — no table rewrite, no exclusive lock:
130
+
131
+ ```sql
132
+ -- Nullable: instant, safe
133
+ ALTER TABLE orders
134
+ ADD COLUMN IF NOT EXISTS fulfilled_at TIMESTAMPTZ;
135
+
136
+ -- Constant default: instant, safe (PostgreSQL 11+)
137
+ ALTER TABLE orders
138
+ ADD COLUMN IF NOT EXISTS sync_version INTEGER NOT NULL DEFAULT 0;
139
+ ```
140
+
141
+ ### Add a Column with a Non-Constant Default (Large Tables)
142
+
143
+ A *non-constant* default (`DEFAULT now()`, `DEFAULT gen_random_uuid()`, `DEFAULT some_function(other_column)`) forces a full table rewrite. On a 50M-row table that means a multi-minute exclusive lock. The split is:
144
+
145
+ ```sql
146
+ -- Step 1: add the column nullable (instant, no lock)
147
+ ALTER TABLE order_items
148
+ ADD COLUMN IF NOT EXISTS unit_weight_grams NUMERIC(10,2);
149
+
150
+ -- Step 2: backfill in batches (avoids long lock and long transaction)
151
+ DO $$
152
+ DECLARE
153
+ batch_size INT := 1000;
154
+ last_id UUID := '00000000-0000-0000-0000-000000000000';
155
+ max_id UUID;
156
+ BEGIN
157
+ LOOP
158
+ SELECT MAX(id) INTO max_id
159
+ FROM (
160
+ SELECT id FROM order_items
161
+ WHERE id > last_id
162
+ ORDER BY id
163
+ LIMIT batch_size
164
+ ) sub;
165
+
166
+ EXIT WHEN max_id IS NULL;
167
+
168
+ UPDATE order_items
169
+ SET unit_weight_grams = 250
170
+ WHERE id > last_id AND id <= max_id;
171
+
172
+ last_id := max_id;
173
+ COMMIT;
174
+ END LOOP;
175
+ END;
176
+ $$;
177
+
178
+ -- Step 3: enforce NOT NULL after backfill is complete
179
+ ALTER TABLE order_items
180
+ ALTER COLUMN unit_weight_grams SET NOT NULL;
181
+ ```
182
+
183
+ The batch size is workload-dependent. 1000 rows per batch is a reasonable default for narrow rows; tune down if the table has wide rows or many indexes.
184
+
185
+ ### Rename a Column (Zero-Downtime — Expand / Contract)
186
+
187
+ A direct `ALTER TABLE ... RENAME COLUMN` is *technically* metadata-only and fast, but it is *not* zero-downtime: the application is reading the old name during the milliseconds between the rename and the new code's deploy, and any in-flight transaction can see the table in either state. The discipline:
188
+
189
+ ```sql
190
+ -- Phase 1 (expand): add the new column, keep both in sync via trigger, backfill
191
+ ALTER TABLE users
192
+ ADD COLUMN IF NOT EXISTS display_name TEXT;
193
+
194
+ CREATE OR REPLACE FUNCTION sync_display_name()
195
+ RETURNS TRIGGER LANGUAGE plpgsql AS $$
196
+ BEGIN
197
+ NEW.display_name := COALESCE(NEW.display_name, NEW.full_name);
198
+ NEW.full_name := COALESCE(NEW.full_name, NEW.display_name);
199
+ RETURN NEW;
200
+ END;
201
+ $$;
202
+
203
+ CREATE TRIGGER trg_sync_display_name
204
+ BEFORE INSERT OR UPDATE ON users
205
+ FOR EACH ROW EXECUTE FUNCTION sync_display_name();
206
+
207
+ UPDATE users SET display_name = full_name WHERE display_name IS NULL;
208
+ ```
209
+
210
+ Deploy application code that reads and writes `display_name`. Wait until no caller still references `full_name` (typically one or two release cycles).
211
+
212
+ ```sql
213
+ -- Phase 2 (contract): drop trigger, function, and old column
214
+ DROP TRIGGER IF EXISTS trg_sync_display_name ON users;
215
+ DROP FUNCTION IF EXISTS sync_display_name();
216
+ ALTER TABLE users DROP COLUMN full_name;
217
+ ```
218
+
219
+ The two phases ship in *separate migrations and separate deploys*. Bundling them defeats the point.
220
+
221
+ ### Change a Column Type
222
+
223
+ Compatible types (e.g. `INT` → `BIGINT`, `TEXT` → `VARCHAR(n)` where `n` is large enough) are direct and safe:
224
+
225
+ ```sql
226
+ ALTER TABLE orders
227
+ ALTER COLUMN external_shop_id TYPE BIGINT;
228
+ ```
229
+
230
+ Incompatible types (e.g. `NUMERIC(10,2)` → `BIGINT` cents, `TEXT` → `INTEGER`) need the shadow-column pattern:
231
+
232
+ ```sql
233
+ -- Step 1: add shadow column with the new type
234
+ ALTER TABLE orders
235
+ ADD COLUMN IF NOT EXISTS amount_cents BIGINT;
236
+
237
+ -- Step 2: backfill (batched if the table is large)
238
+ UPDATE orders
239
+ SET amount_cents = ROUND(amount_decimal * 100)
240
+ WHERE amount_cents IS NULL;
241
+
242
+ -- Step 3 (after deploying code that reads amount_cents): drop the old column
243
+ ALTER TABLE orders DROP COLUMN amount_decimal;
244
+ ALTER TABLE orders RENAME COLUMN amount_cents TO amount;
245
+ ```
246
+
247
+ ### Create an Index Concurrently
248
+
249
+ `CREATE INDEX` takes an `ACCESS EXCLUSIVE` lock by default — readers are unaffected but writers block for the duration. On a large table that is unacceptable. `CONCURRENTLY` builds the index without holding the heavy lock:
250
+
251
+ ```sql
252
+ CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_orders_org_created
253
+ ON orders (org_id, created_at DESC);
254
+
255
+ -- Symmetrical drop
256
+ DROP INDEX CONCURRENTLY IF EXISTS idx_orders_org_created;
257
+ ```
258
+
259
+ `CONCURRENTLY` *cannot* run inside a `BEGIN/COMMIT` block — it must be the entire script, or sit between explicit transaction boundaries. Migrations that need an index plus other DDL must split the index step into its own file.
260
+
261
+ A failed `CREATE INDEX CONCURRENTLY` leaves an *invalid* index on the table; check with `\d <table>` and `DROP INDEX` the invalid one before retrying.
262
+
263
+ ### Add a Foreign Key (Low Lock)
264
+
265
+ Adding a foreign key with the default validation behavior locks both tables while it scans every existing row. The two-step pattern adds the constraint without scanning, then validates without an exclusive lock:
266
+
267
+ ```sql
268
+ -- Step 1: add as NOT VALID (no scan, near-instant, locks briefly)
269
+ ALTER TABLE order_items
270
+ ADD CONSTRAINT fk_order_items_orders
271
+ FOREIGN KEY (order_id) REFERENCES orders(id) ON DELETE CASCADE
272
+ NOT VALID;
273
+
274
+ -- Step 2: validate (acquires SHARE UPDATE EXCLUSIVE — readers and writers continue)
275
+ ALTER TABLE order_items
276
+ VALIDATE CONSTRAINT fk_order_items_orders;
277
+ ```
278
+
279
+ `NOT VALID` means *new* rows are checked against the constraint, but *existing* rows are not — which is fine if the application's prior writes already respected the constraint. If they did not, the `VALIDATE` step fails and surfaces the offending rows.
280
+
281
+ ## Tenant-Scoped Schema Additions
282
+
283
+ If your application uses Postgres row-level security to isolate tenants, every new tenant-scoped table must include the policy in the *same migration that creates the table*. Adding it later is a window during which the table is readable across tenants.
284
+
285
+ ```sql
286
+ CREATE TABLE IF NOT EXISTS subscription_plans (
287
+ id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
288
+ org_id UUID NOT NULL REFERENCES organizations(id) ON DELETE CASCADE,
289
+ plan_name TEXT NOT NULL,
290
+ tier TEXT NOT NULL CHECK (tier IN ('free', 'starter', 'pro', 'enterprise')),
291
+ created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
292
+ updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
293
+ );
294
+
295
+ ALTER TABLE subscription_plans ENABLE ROW LEVEL SECURITY;
296
+ ALTER TABLE subscription_plans FORCE ROW LEVEL SECURITY;
297
+
298
+ CREATE POLICY subscription_plans_org_isolation
299
+ ON subscription_plans
300
+ USING (
301
+ org_id IS NOT NULL
302
+ AND org_id = current_setting('app.organization_id', TRUE)::uuid
303
+ )
304
+ WITH CHECK (
305
+ org_id IS NOT NULL
306
+ AND org_id = current_setting('app.organization_id', TRUE)::uuid
307
+ );
308
+
309
+ GRANT SELECT, INSERT, UPDATE, DELETE ON subscription_plans TO app_role;
310
+
311
+ CREATE INDEX idx_subscription_plans_org
312
+ ON subscription_plans (org_id);
313
+ ```
314
+
315
+ After the migration applies, verify the policy is attached:
316
+
317
+ ```sql
318
+ SELECT tablename, policyname, cmd, qual
319
+ FROM pg_policies
320
+ WHERE tablename = 'subscription_plans';
321
+ ```
322
+
323
+ ### Session Variable Discipline
324
+
325
+ The session variable name in `current_setting()` and the name your application sets via `SET LOCAL app.<name>` must match exactly. A typo (`app.org_id` vs `app.organization_id`) silently fails: `current_setting()` returns `NULL`, the policy never matches, and the table appears empty to every caller. Write the policy and the application's session-variable code in the *same* review cycle, and add a test that calls a known cross-tenant query and confirms it returns zero rows.
326
+
327
+ The design of the RLS model itself (which tables are tenant-scoped, what the isolation predicate should be, where the variable is set) belongs to a security-design skill, not this one. This skill only enforces that *if* the policy exists in your model, the migration includes it.
328
+
329
+ ## Zero-Downtime Table Rename
330
+
331
+ Renaming a table while the application is live needs a compatibility view so old code paths keep working:
332
+
333
+ ```sql
334
+ -- Phase 1: rename
335
+ ALTER TABLE orders_v1 RENAME TO orders;
336
+
337
+ -- Phase 2: compatibility view for callers still using the old name
338
+ CREATE OR REPLACE VIEW orders_v1 AS
339
+ SELECT * FROM orders;
340
+
341
+ GRANT SELECT ON orders_v1 TO app_role;
342
+ ```
343
+
344
+ Once every caller has migrated to the new name, drop the view in a follow-up migration:
345
+
346
+ ```sql
347
+ DROP VIEW IF EXISTS orders_v1;
348
+ ```
349
+
350
+ Writes through the view are usually safe for simple `SELECT *` views, but verify your application's INSERT/UPDATE paths hit the underlying table directly during the transition.
351
+
352
+ ## Rollback Patterns
353
+
354
+ ### Transactional Rollback (Structural Changes)
355
+
356
+ Wrap structural changes in a `BEGIN/COMMIT` block with a verification step. If verification fails, `RAISE EXCEPTION` triggers automatic rollback of the entire migration:
357
+
358
+ ```sql
359
+ BEGIN;
360
+
361
+ ALTER TABLE users ADD COLUMN phone TEXT;
362
+
363
+ DO $$
364
+ BEGIN
365
+ IF NOT EXISTS (
366
+ SELECT 1 FROM information_schema.columns
367
+ WHERE table_name = 'users' AND column_name = 'phone'
368
+ ) THEN
369
+ RAISE EXCEPTION 'phone column not created';
370
+ END IF;
371
+ END;
372
+ $$;
373
+
374
+ COMMIT;
375
+ ```
376
+
377
+ Anything that cannot run in a transaction (`CREATE INDEX CONCURRENTLY`, `ALTER TYPE ... ADD VALUE` in older versions, etc.) cannot use this pattern — it must have a separate `DOWN` migration ready to apply if the `UP` causes problems downstream.
378
+
379
+ ### Point-in-Time Restore via Branched Database
380
+
381
+ Branched-database providers can restore a parent branch to a prior timestamp. This is the rollback of last resort — it loses any data written between the bad migration and the restore point.
382
+
383
+ ```bash
384
+ # 1. Restore main to a known-good timestamp (preserves the bad state under a renamed branch)
385
+ neonctl branches restore main \
386
+ main@2026-05-06T09:00:00Z \
387
+ --preserve-under-name "main-post-bad-migration"
388
+
389
+ # 2. Verify the restored state
390
+ psql "$(neonctl cs main --pooled false)" -c "SELECT COUNT(*) FROM orders"
391
+
392
+ # 3. Apply the corrected migration
393
+ psql "$DATABASE_URL_UNPOOLED" -f db/migrations/2026_05_06_corrected.sql
394
+
395
+ # 4. Once confirmed safe, delete the preserved bad branch
396
+ neonctl branches delete main-post-bad-migration
397
+ ```
398
+
399
+ If your provider lacks point-in-time restore, the substitute is a backup-and-restore from the most recent snapshot — same shape, longer window of data loss.
400
+
401
+ ## Evals
402
+
403
+ This skill ships a comprehension-eval artifact at [`examples/evals/database-migration.json`](https://github.com/jacob-balslev/skill-graph/blob/main/examples/evals/database-migration.json). The checklist below is the authoring gate for migration rollout decisions; the eval file is the grader surface.
404
+
405
+ ## Verification
406
+
407
+ - [ ] Migration filename matches the chronological convention (`YYYY_MM_DD_description.sql`)
408
+ - [ ] File header documents purpose, safety classification, and rollback path
409
+ - [ ] DDL targets the unpooled connection URL, not the pooled one
410
+ - [ ] If your platform supports branched databases, the migration ran on a branch and the schema diff against main shows only intended changes
411
+ - [ ] Nullable columns added with `IF NOT EXISTS`; non-constant defaults split into add-nullable + batched-backfill + set-NOT-NULL
412
+ - [ ] Column renames use expand / contract across two deploys, not direct `RENAME COLUMN`
413
+ - [ ] Type changes incompatible with `ALTER COLUMN ... TYPE` use the shadow-column pattern
414
+ - [ ] Indexes on large tables created with `CONCURRENTLY`, outside any `BEGIN/COMMIT` block
415
+ - [ ] Foreign keys on large tables added as `NOT VALID`, then validated separately
416
+ - [ ] If the application uses RLS for tenancy, the policy is created in the same migration as the new table — never deferred to a follow-up
417
+ - [ ] Rollback strategy is written in the file header (transactional rollback for structural changes; explicit `DOWN` for non-transactional ones; point-in-time restore explicitly named when row-level rollback is impossible)
418
+ - [ ] Migration was reviewed by the team or a `code-review` pass before merging
419
+
420
+ ## Do NOT Use When
421
+
422
+ | Use instead | When |
423
+ |---|---|
424
+ | `documentation` | Writing the migration-conventions page for the contributor docs |
425
+ | `debugging` | Chasing a migration that has already failed in production |
426
+ | `refactor` | Reorganizing the migration runner script or helper code |
427
+ | `testing-strategy` | Deciding whether a column rename needs an automated regression test |
428
+ | `code-review` | Reviewing an AI-generated DDL diff for correctness |
429
+ | `owasp-security` | Designing the row-level-security model itself (this skill only enforces that the policy ships with the table) |
@@ -0,0 +1,67 @@
1
+ ---
2
+ name: debugging
3
+ description: "Use when behavior is broken, a test is failing, or runtime output contradicts expectations. Covers failure reproduction, scope reduction by bisection, evidence capture at the moment of failure, root-cause isolation (not symptom patching), fix verification against the same evidence path, and regression-test creation. Do NOT use for feature planning, architectural design, or behavior-preserving refactor."
4
+ license: MIT
5
+ compatibility: "Markdown, Git, any codebase"
6
+ allowed-tools: Read Grep Bash
7
+ metadata:
8
+ metadata: "{\"schema_version\":6,\"version\":\"1.0.0\",\"type\":\"workflow\",\"category\":\"engineering\",\"scope\":\"portable\",\"owner\":\"skill-graph-maintainer\",\"freshness\":\"2026-04-18\",\"drift_check\":\"{\\\\\\\"last_verified\\\\\\\":\\\\\\\"2026-04-18\\\\\\\"}\",\"eval_artifacts\":\"present\",\"eval_state\":\"passing\",\"routing_eval\":\"present\",\"stability\":\"experimental\",\"keywords\":\"[\\\\\\\"debugging\\\\\\\",\\\\\\\"reproduce failure\\\\\\\",\\\\\\\"reproduce bug\\\\\\\",\\\\\\\"failing test\\\\\\\",\\\\\\\"root cause\\\\\\\",\\\\\\\"symptom vs cause\\\\\\\",\\\\\\\"minimum reproduction\\\\\\\",\\\\\\\"bisect\\\\\\\",\\\\\\\"what caused it\\\\\\\",\\\\\\\"my tests are failing\\\\\\\",\\\\\\\"why is this broken\\\\\\\",\\\\\\\"it broke in production\\\\\\\",\\\\\\\"cannot reproduce\\\\\\\",\\\\\\\"test passes locally\\\\\\\",\\\\\\\"stack trace\\\\\\\",\\\\\\\"used to work\\\\\\\",\\\\\\\"worked yesterday\\\\\\\",\\\\\\\"what changed\\\\\\\",\\\\\\\"was working before\\\\\\\",\\\\\\\"agent stuck\\\\\\\",\\\\\\\"stuck in a loop\\\\\\\",\\\\\\\"stuck in loop\\\\\\\",\\\\\\\"blocking my commit\\\\\\\",\\\\\\\"blocking the build\\\\\\\",\\\\\\\"specific error\\\\\\\",\\\\\\\"specific failure\\\\\\\",\\\\\\\"diagnose failure\\\\\\\",\\\\\\\"error blocking\\\\\\\",\\\\\\\"broke the build\\\\\\\",\\\\\\\"broke build\\\\\\\"]\",\"triggers\":\"[\\\\\\\"debugging-skill\\\\\\\"]\",\"examples\":\"[\\\\\\\"my tests pass locally but fail in CI — why?\\\\\\\",\\\\\\\"this function used to work yesterday; what changed?\\\\\\\",\\\\\\\"reproduce this Stripe webhook failure from production logs\\\\\\\",\\\\\\\"I see the symptom but can't find the root cause of this nil panic\\\\\\\"]\",\"anti_examples\":\"[\\\\\\\"plan test coverage for a new feature\\\\\\\",\\\\\\\"document what this function does for future readers\\\\\\\",\\\\\\\"refactor this messy code while the test suite is green\\\\\\\"]\",\"relations\":\"{\\\\\\\"boundary\\\\\\\":[{\\\\\\\"skill\\\\\\\":\\\\\\\"testing-strategy\\\\\\\",\\\\\\\"reason\\\\\\\":\\\\\\\"testing-strategy plans what to test before a failure exists; debugging chases a specific observed failure\\\\\\\"},{\\\\\\\"skill\\\\\\\":\\\\\\\"refactor\\\\\\\",\\\\\\\"reason\\\\\\\":\\\\\\\"refactor is behavior-preserving code change with green tests; debugging is invoked because tests or behavior are NOT green\\\\\\\"}],\\\\\\\"verify_with\\\\\\\":[\\\\\\\"testing-strategy\\\\\\\"]}\",\"portability\":\"{\\\\\\\"readiness\\\\\\\":\\\\\\\"scripted\\\\\\\",\\\\\\\"targets\\\\\\\":[\\\\\\\"skill-md\\\\\\\"]}\",\"skill_graph_source_repo\":\"https://github.com/jacob-balslev/skill-graph\",\"skill_graph_protocol\":\"Skill Metadata Protocol v5\",\"skill_graph_project\":\"Skill Graph\",\"skill_graph_canonical_skill\":\"skills/debugging/SKILL.md\"}"
9
+ skill_graph_source_repo: "https://github.com/jacob-balslev/skill-graph"
10
+ skill_graph_protocol: Skill Metadata Protocol v4
11
+ skill_graph_project: Skill Graph
12
+ skill_graph_canonical_skill: skills/debugging/SKILL.md
13
+ ---
14
+
15
+ # Debugging
16
+
17
+ ## Coverage
18
+
19
+ - Reproduction: turning a vague bug report into a deterministic failing case
20
+ - Scope reduction: isolating the smallest surface where the failure still reproduces
21
+ - Evidence capture: collecting logs, stack traces, and state snapshots at the moment of failure
22
+ - Root-cause isolation: distinguishing symptoms from causes and resisting the urge to patch symptoms
23
+ - Fix verification: re-running the original failure path to confirm the fix is real
24
+ - Regression prevention: converting the failing case into a permanent test so the same bug cannot return silently
25
+
26
+ ## Philosophy
27
+
28
+ The fastest way to fix a bug is usually the wrong fix. A working reproduction is worth more than a plausible hypothesis; a plausible hypothesis is worth more than a clever fix; a clever fix that skips the reproduction step ships the same bug again under a different name. When pressure is high the temptation to jump from symptom to patch is also high — resist it, because the cost of a wrong fix is paid again by the next person who hits the same failure with less context than you had.
29
+
30
+ ## Workflow
31
+
32
+ Each step asks a question. The answer decides the next step. Do not skip steps to save time; the steps exist because skipping them is how bugs return.
33
+
34
+ | Step | Ask | If yes | If no |
35
+ |---|---|---|---|
36
+ | 1. Reproduce | Do you have a deterministic failing case? | Go to step 2 | Add logging, narrow inputs, or run the failing path in a loop until the failure is reliable |
37
+ | 2. Scope | Can you reproduce it in a surface smaller than the full system? | Go to step 3 | Bisect — halve the code path, data, or config and retry |
38
+ | 3. Evidence | Do you have the state at the moment of failure, not just the symptom after? | Go to step 4 | Add instrumentation at the boundary where state flips wrong |
39
+ | 4. Cause | Does your hypothesis explain ALL of the evidence, not just the visible symptom? | Go to step 5 | Form a better hypothesis — partial explanations hide shared root causes |
40
+ | 5. Verify | Does the same evidence path pass with the fix applied, and fail with it reverted? | Go to step 6 | The fix did not land or the cause was wrong — return to step 4 |
41
+ | 6. Regression test | Does the test you just wrote fail without the fix and pass with it? | Done | Your test is not isolating the cause — rewrite it |
42
+
43
+ ### When to stop and escalate
44
+
45
+ - Step 1 is still unreproducible after ~60 min of narrowing → suspect non-determinism (race, timing, clock, network). This is a design issue, not a debugging issue.
46
+ - Step 3 instrumentation shows contradictory state on the same object → suspect memory corruption, concurrent mutation, or stale cache. Out of scope for a single-file debugger; escalate to architectural review.
47
+ - The same bug returned after a previous fix → the previous fix patched a symptom. Start over at step 1 and find the real cause.
48
+
49
+ ## Evals
50
+
51
+ This skill ships a comprehension-eval artifact at [`examples/evals/debugging.json`](https://github.com/jacob-balslev/skill-graph/blob/main/examples/evals/debugging.json). The `Verification` checklist below is the authoring gate for a completed debugging pass; the eval file is how this skill is graded by `scripts/skill-audit.js --graded`. Do not conflate them — the checklist is for the debugger, the eval is for the grader.
52
+
53
+ ## Verification
54
+
55
+ - [ ] The original failure was reproduced deterministically, not just described
56
+ - [ ] The hypothesis explains every piece of evidence collected, not a subset
57
+ - [ ] The fix was verified by the same evidence path that revealed the bug
58
+ - [ ] A regression test fails without the fix and passes with it
59
+ - [ ] The next engineer who hits this failure can reach the fix from the regression test alone
60
+
61
+ ## Do NOT Use When
62
+
63
+ | Use instead | When |
64
+ |---|---|
65
+ | `refactor` | The task is structural cleanup, not failure-driven diagnosis |
66
+ | `testing-strategy` | The task is planning what to test, not chasing a known failure |
67
+ | `documentation` | The task is explaining behavior, not fixing broken behavior |
@@ -0,0 +1,58 @@
1
+ ---
2
+ name: dependency-architecture
3
+ description: "Use when designing or auditing dependency structure: package boundaries, runtime vs build dependencies, adapter layers, duplicate-purpose libraries, supply-chain risk, upgrade policy, lock-in, and dependency graph health. Do NOT use for choosing a major framework (use `framework-fit-analysis`), vulnerability-only review (use `owasp-security`), or routine refactoring without dependency boundary changes (use `refactor`)."
4
+ license: MIT
5
+ compatibility: "Portable dependency architecture guidance for monorepos, package.json ecosystems, service boundaries, SDKs, and internal libraries."
6
+ allowed-tools: Read Grep
7
+ metadata:
8
+ metadata: "{\"schema_version\":6,\"version\":\"1.0.0\",\"type\":\"capability\",\"category\":\"engineering\",\"domain\":\"architecture/dependencies\",\"scope\":\"portable\",\"owner\":\"skill-graph-maintainer\",\"freshness\":\"2026-05-11\",\"drift_check\":\"{\\\\\\\"last_verified\\\\\\\":\\\\\\\"2026-05-11\\\\\\\"}\",\"eval_artifacts\":\"present\",\"eval_state\":\"unverified\",\"routing_eval\":\"absent\",\"stability\":\"experimental\",\"keywords\":\"[\\\\\\\"dependency architecture\\\\\\\",\\\\\\\"dependency graph\\\\\\\",\\\\\\\"package boundaries\\\\\\\",\\\\\\\"runtime dependency\\\\\\\",\\\\\\\"build dependency\\\\\\\",\\\\\\\"duplicate libraries\\\\\\\",\\\\\\\"supply chain risk\\\\\\\",\\\\\\\"adapter layer\\\\\\\",\\\\\\\"lock-in\\\\\\\",\\\\\\\"upgrade policy\\\\\\\"]\",\"examples\":\"[\\\\\\\"audit whether this repo has duplicate-purpose dependencies and unsafe package boundaries\\\\\\\",\\\\\\\"should this SDK be wrapped behind an adapter or imported everywhere?\\\\\\\",\\\\\\\"design dependency rules for packages in this monorepo\\\\\\\",\\\\\\\"evaluate dependency lock-in and upgrade risk before adding this library\\\\\\\"]\",\"anti_examples\":\"[\\\\\\\"choose between Next.js, Remix, and Astro for a new app\\\\\\\",\\\\\\\"scan dependencies only for known vulnerabilities\\\\\\\",\\\\\\\"refactor this module without changing dependency boundaries\\\\\\\",\\\\\\\"write an ADR after the dependency decision is accepted\\\\\\\"]\",\"relations\":\"{\\\\\\\"boundary\\\\\\\":[{\\\\\\\"skill\\\\\\\":\\\\\\\"framework-fit-analysis\\\\\\\",\\\\\\\"reason\\\\\\\":\\\\\\\"framework-fit-analysis evaluates major technology choices; dependency-architecture governs dependency graph structure and package boundaries\\\\\\\"},{\\\\\\\"skill\\\\\\\":\\\\\\\"owasp-security\\\\\\\",\\\\\\\"reason\\\\\\\":\\\\\\\"owasp-security owns vulnerability and security review; dependency-architecture includes supply-chain risk as one design dimension\\\\\\\"},{\\\\\\\"skill\\\\\\\":\\\\\\\"refactor\\\\\\\",\\\\\\\"reason\\\\\\\":\\\\\\\"refactor preserves behavior in code structure; dependency-architecture changes or audits dependency boundaries\\\\\\\"},{\\\\\\\"skill\\\\\\\":\\\\\\\"architecture-decision-records\\\\\\\",\\\\\\\"reason\\\\\\\":\\\\\\\"architecture-decision-records records accepted dependency decisions; dependency-architecture analyzes the dependency design\\\\\\\"}],\\\\\\\"related\\\\\\\":[\\\\\\\"framework-fit-analysis\\\\\\\",\\\\\\\"system-interface-contracts\\\\\\\",\\\\\\\"version-control\\\\\\\",\\\\\\\"owasp-security\\\\\\\"],\\\\\\\"verify_with\\\\\\\":[\\\\\\\"owasp-security\\\\\\\",\\\\\\\"code-review\\\\\\\"]}\",\"portability\":\"{\\\\\\\"readiness\\\\\\\":\\\\\\\"scripted\\\\\\\",\\\\\\\"targets\\\\\\\":[\\\\\\\"skill-md\\\\\\\"]}\",\"lifecycle\":\"{\\\\\\\"stale_after_days\\\\\\\":180,\\\\\\\"review_cadence\\\\\\\":\\\\\\\"quarterly\\\\\\\"}\",\"skill_graph_source_repo\":\"https://github.com/jacob-balslev/skill-graph\",\"skill_graph_protocol\":\"Skill Metadata Protocol v5\",\"skill_graph_project\":\"Skill Graph\",\"skill_graph_canonical_skill\":\"skills/dependency-architecture/SKILL.md\"}"
9
+ skill_graph_source_repo: "https://github.com/jacob-balslev/skill-graph"
10
+ skill_graph_protocol: Skill Metadata Protocol v4
11
+ skill_graph_project: Skill Graph
12
+ skill_graph_canonical_skill: skills/dependency-architecture/SKILL.md
13
+ ---
14
+
15
+ # Dependency Architecture
16
+
17
+ ## Coverage
18
+
19
+ Design and audit the dependency graph of a codebase. Covers direct vs transitive dependencies, runtime vs dev/build dependencies, package boundaries, import direction, adapter layers, duplicate-purpose libraries, lock-in, upgrade policy, supply-chain risk, and dependency drift.
20
+
21
+ ## Philosophy
22
+
23
+ Dependencies are architecture. Every package adds API surface, operational risk, update cost, and implicit design direction. A dependency that solves one task can still be wrong if it creates long-term coupling or duplicates an existing standard.
24
+
25
+ Prefer fewer, clearer dependencies with explicit ownership. Wrap volatile external SDKs at boundaries. Let application code depend on local contracts, not vendor shapes, when the vendor is likely to change or be replaced.
26
+
27
+ ## Method
28
+
29
+ 1. Inventory dependencies by purpose, owner, and import surface.
30
+ 2. Classify each as runtime, dev, build, test, or optional.
31
+ 3. Identify duplicate-purpose libraries and unauthorized standards.
32
+ 4. Check import direction and package boundary rules.
33
+ 5. Decide where adapters are needed for external SDKs or volatile APIs.
34
+ 6. Assess security, maintenance, license, and ecosystem health.
35
+ 7. Define upgrade, pinning, and removal policy.
36
+
37
+ ## Evals
38
+
39
+ This skill ships a comprehension-eval artifact at [`examples/evals/dependency-architecture.json`](https://github.com/jacob-balslev/skill-graph/blob/main/examples/evals/dependency-architecture.json). The checklist below is the authoring gate for dependency-boundary decisions; the eval file is the grader surface.
40
+
41
+ ## Verification
42
+
43
+ - [ ] Each dependency has a purpose and owner
44
+ - [ ] Runtime dependencies are not hidden as dev/build dependencies
45
+ - [ ] Duplicate-purpose packages are justified or removed
46
+ - [ ] External SDKs do not leak vendor types across core boundaries without intent
47
+ - [ ] Package import direction is enforceable
48
+ - [ ] Upgrade policy and lockfile discipline are clear
49
+ - [ ] Security and license risks have been checked for high-impact dependencies
50
+
51
+ ## Do NOT Use When
52
+
53
+ | Use instead | When |
54
+ |---|---|
55
+ | `framework-fit-analysis` | You are selecting a major framework, platform, runtime, or database. |
56
+ | `owasp-security` | The task is vulnerability-focused security review. |
57
+ | `refactor` | You are restructuring code without changing dependency architecture. |
58
+ | `architecture-decision-records` | The dependency decision is already made and needs a record. |
@@ -0,0 +1,43 @@
1
+ ---
2
+ name: design-module-composition
3
+ description: "Use when designing reusable component modules — composition patterns, compound components, slot/children APIs, render props, headless component contracts, and the choice between configuration and composition. Do NOT use for application-level architecture, single-use feature components, or visual styling decisions."
4
+ license: CC-BY-4.0
5
+ metadata:
6
+ metadata: "{\"schema_version\":6,\"version\":\"1.0.0\",\"type\":\"capability\",\"category\":\"design\",\"scope\":\"portable\",\"owner\":\"skill-graph-maintainer\",\"freshness\":\"2026-05-12\",\"drift_check\":\"{\\\\\\\"last_verified\\\\\\\":\\\\\\\"2026-05-12\\\\\\\"}\",\"eval_artifacts\":\"planned\",\"eval_state\":\"unverified\",\"routing_eval\":\"absent\",\"stability\":\"experimental\",\"keywords\":\"[\\\\\\\"component composition\\\\\\\",\\\\\\\"compound components\\\\\\\",\\\\\\\"slot api\\\\\\\",\\\\\\\"headless components\\\\\\\",\\\\\\\"render props\\\\\\\",\\\\\\\"polymorphic components\\\\\\\",\\\\\\\"asChild pattern\\\\\\\",\\\\\\\"children as api\\\\\\\",\\\\\\\"composition over configuration\\\\\\\",\\\\\\\"component contract design\\\\\\\",\\\\\\\"component module patterns\\\\\\\",\\\\\\\"selectable rows sortable columns\\\\\\\",\\\\\\\"primitives composites templates\\\\\\\"]\",\"triggers\":\"[\\\\\\\"compound component\\\\\\\",\\\\\\\"composition over configuration\\\\\\\",\\\\\\\"headless component\\\\\\\",\\\\\\\"slot pattern\\\\\\\",\\\\\\\"asChild\\\\\\\"]\",\"examples\":\"[\\\\\\\"Design a Dialog component whose trigger, content, and close button are addressable by consumers\\\\\\\",\\\\\\\"Refactor a Card with 14 boolean props into a composition-based API\\\\\\\",\\\\\\\"Build a headless table primitive that exposes state and behavior without imposing markup\\\\\\\"]\",\"anti_examples\":\"[\\\\\\\"Choose the border radius value for cards\\\\\\\",\\\\\\\"Decide where the OrderDetailPage component lives in the folder structure\\\\\\\",\\\\\\\"Pick the brand font for headings\\\\\\\"]\",\"relations\":\"{\\\\\\\"related\\\\\\\":[\\\\\\\"design-system-architecture\\\\\\\",\\\\\\\"frontend-architecture\\\\\\\",\\\\\\\"interaction-patterns\\\\\\\",\\\\\\\"a11y\\\\\\\"],\\\\\\\"boundary\\\\\\\":[{\\\\\\\"skill\\\\\\\":\\\\\\\"frontend-architecture\\\\\\\",\\\\\\\"reason\\\\\\\":\\\\\\\"frontend-architecture covers application-level organization; this skill covers the internal API of a reusable module that the application composes.\\\\\\\"},{\\\\\\\"skill\\\\\\\":\\\\\\\"a11y\\\\\\\",\\\\\\\"reason\\\\\\\":\\\\\\\"Composition choices affect accessibility (focus management, ARIA wiring); when the question is whether a pattern meets WCAG requirements, hand off to a11y.\\\\\\\"}]}\",\"skill_graph_source_repo\":\"https://github.com/jacob-balslev/skill-graph\",\"skill_graph_protocol\":\"Skill Metadata Protocol v5\",\"skill_graph_project\":\"Skill Graph\",\"skill_graph_canonical_skill\":\"skills/design-module-composition/SKILL.md\"}"
7
+ skill_graph_source_repo: "https://github.com/jacob-balslev/skill-graph"
8
+ skill_graph_protocol: Skill Metadata Protocol v4
9
+ skill_graph_project: Skill Graph
10
+ skill_graph_canonical_skill: skills/design-module-composition/SKILL.md
11
+ ---
12
+
13
+ # Design Module Composition
14
+
15
+ ## Coverage
16
+ A composable component module exposes its parts to consumers rather than hiding them behind a configuration prop. The four mainstream patterns are compound components (a parent and a named set of children share context: <Tabs>, <Tabs.List>, <Tabs.Trigger>, <Tabs.Panel>), slot/children APIs (named slots accept arbitrary content: <Card header={...} footer={...} />), render props or function-as-children (the parent provides state, the consumer provides markup: <Tooltip>{({open}) => ...}</Tooltip>), and headless primitives (state and behavior are exposed as hooks or unstyled components — Radix, Headless UI, TanStack Table — leaving all markup and styling to the consumer).
17
+
18
+ The "asChild" or polymorphic pattern (Radix's term; also called "as" prop, "render" prop in some libraries) lets a consumer change the rendered element while inheriting all behavior: <Dialog.Trigger asChild><Button>Open</Button></Dialog.Trigger>. The pattern collapses two-level wrappers and avoids the "button inside button" accessibility error, but requires the parent to clone or render-prop its single child carefully.
19
+
20
+ Choosing between configuration and composition is a trade-off between control surface and expressiveness. A prop-heavy API (<Card title={} subtitle={} action={} variant={} />) is fast to consume for the common case and friction-heavy for variants the original author didn't anticipate. A composition API (<Card><Card.Header>...</Card.Header></Card>) reverses this: more typing for the common case, no friction for variants. Mature design systems often offer both: a high-level "summary" component that consumes the low-level composable primitives.
21
+
22
+ State sharing between compound-component pieces uses React context (or framework-equivalent). The context contract — what the parent provides, what the children expect — is the real API of the module, and changing it is a breaking change even when the prop signatures stay the same. Headless primitives push this further: state and behavior leave the module entirely, and the visual layer is the consumer's responsibility.
23
+
24
+ ## Philosophy
25
+ Composition externalizes variation. Every boolean prop on a component is a decision the module author made on behalf of every future consumer; every slot is a decision deferred. The discipline is to ask whether the variant being added is part of the module's identity (it should be a prop) or part of how a specific consumer uses the module (it should be a slot).
26
+
27
+ Headless primitives separate three concerns that are routinely conflated: state (open/closed, selected, expanded), behavior (focus trapping, keyboard navigation, ARIA attribute wiring), and presentation (markup and styles). Conflation is convenient until the design system needs a second visual treatment of the same behavior; separation makes that addition trivial.
28
+
29
+ ## Verification
30
+ - The component's primary variations are achievable by composing children rather than passing boolean props; a count of boolean props is in single digits.
31
+ - Compound-component children render outside the parent only with an explicit error (they require the parent's context).
32
+ - Slot props accept ReactNode (or framework equivalent) rather than typed sub-shapes; consumers can pass any valid element.
33
+ - Accessibility wiring (aria-controls, aria-expanded, focus return on dialog close) is the module's responsibility, not the consumer's, even when markup is delegated via asChild or headless patterns.
34
+ - Documentation shows the compositional pattern as the primary example, with prop-API shortcuts noted as conveniences.
35
+ - Replacing the rendered element type via asChild or polymorphic-as preserves all behavior and ARIA attributes.
36
+ - The module has at least one example of being composed into a non-obvious shape (a tabs control becoming a vertical sidebar) without modification.
37
+
38
+ ## Do NOT Use When
39
+ - The component is single-use within one feature and will never be reused. Reach for the simpler prop-driven shape.
40
+ - The question is which features live in which folders or which application owns which screen. Use frontend-architecture.
41
+ - The decision is purely visual — color, spacing, type. Use visual-design-foundations or layout-composition.
42
+ - The work is configuring or publishing the shared library that hosts these modules. Use design-system-architecture.
43
+ - The concern is meeting specific accessibility criteria for a control pattern. Use a11y for the criteria; this skill for the API shape.