@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.
- package/CHANGELOG.md +247 -0
- package/LICENSE +200 -0
- package/NOTICE +62 -0
- package/README.md +398 -0
- package/SKILL_GRAPH.md +443 -0
- package/bin/skill-graph.js +374 -0
- package/docs/ADOPTION.md +117 -0
- package/docs/CONFORMANCE.md +66 -0
- package/docs/PRIMER.md +384 -0
- package/docs/QUICKSTART-30MIN.md +333 -0
- package/docs/ROUTING-METRICS.md +120 -0
- package/docs/SKILL-MD-FORMAT-COMPATIBILITY.md +127 -0
- package/docs/SKILL_AUDIT_CHECKLIST.md +199 -0
- package/docs/SKILL_AUDIT_LOOP.md +195 -0
- package/docs/SKILL_METADATA_PROTOCOL.md +609 -0
- package/docs/_archived/marketplace-publication-priority-2026-05-18.md +239 -0
- package/docs/adr/0001-predicate-set.md +69 -0
- package/docs/adr/0002-json-ld-context.md +82 -0
- package/docs/adr/0003-ontoclean-rigidity-tags.md +65 -0
- package/docs/adr/0004-persistent-identifiers.md +74 -0
- package/docs/adr/0005-freshness-consolidation.md +70 -0
- package/docs/adr/0006-revise-predicate-rename.md +105 -0
- package/docs/adr/0007-audit-loop-cadence.md +99 -0
- package/docs/adr/0008-skill-surface-split-and-curation-policy.md +93 -0
- package/docs/category-consumers.md +168 -0
- package/docs/concept-map.md +194 -0
- package/docs/diagrams/drift-states.mmd +21 -0
- package/docs/diagrams/manifest-pipeline.mmd +25 -0
- package/docs/diagrams/routing-harness.mmd +41 -0
- package/docs/diagrams/starter-graph.mmd +53 -0
- package/docs/field-decision-guide.md +315 -0
- package/docs/field-rationale.md +211 -0
- package/docs/field-reference.generated.md +624 -0
- package/docs/field-reference.md +1426 -0
- package/docs/glossary.md +190 -0
- package/docs/head-noun-glossary.md +63 -0
- package/docs/images/audit-phases.png +0 -0
- package/docs/images/drift-states.png +0 -0
- package/docs/images/graded-mode.png +0 -0
- package/docs/images/manifest-pipeline.png +0 -0
- package/docs/images/routing-harness.png +0 -0
- package/docs/images/skill-anatomy.png +0 -0
- package/docs/images/starter-graph.png +0 -0
- package/docs/images/system-model.png +0 -0
- package/docs/integrations/github-actions.md +155 -0
- package/docs/manifest-field-mapping.md +443 -0
- package/docs/marketplace-publication-queue.generated.md +240 -0
- package/docs/marketplace-release-agent-prompt.md +82 -0
- package/docs/marketplace-skill-candidate-list.md +272 -0
- package/docs/marketplace-syndication.md +222 -0
- package/docs/migration-sample-review.md +155 -0
- package/docs/migrations/v4-to-v5.md +168 -0
- package/docs/migrations/v5-to-v6.md +221 -0
- package/docs/name-exceptions.yaml +37 -0
- package/docs/plans/marketplace-p1-public-migration-plan.md +41 -0
- package/docs/plans/multi-root-workspace.md +148 -0
- package/docs/plans/scripts-roadmap.md +107 -0
- package/docs/plans/v4-schema-bump.md +160 -0
- package/docs/plans/wave-2-extraction.md +122 -0
- package/docs/positioning-vs-marketplaces.md +175 -0
- package/docs/proposals/skill-audit-loop-positioning.md +160 -0
- package/docs/quality-doctrine.md +138 -0
- package/docs/recommended-skills.md +150 -0
- package/docs/research/skill-comprehension-eval-research.md +1830 -0
- package/docs/research/skill-retrieval-evidence.md +66 -0
- package/docs/skill-metadata-protocol.md +471 -0
- package/docs/skills-sh-maintainer-cleanup-request.md +80 -0
- package/examples/audits/a11y/findings.md +52 -0
- package/examples/audits/a11y/scorecard.md +21 -0
- package/examples/audits/a11y/verdict.md +44 -0
- package/examples/audits/debugging/findings.md +59 -0
- package/examples/audits/debugging/scorecard.md +22 -0
- package/examples/audits/debugging/verdict.md +33 -0
- package/examples/audits/documentation/findings.md +59 -0
- package/examples/audits/documentation/scorecard.md +22 -0
- package/examples/audits/documentation/verdict.md +33 -0
- package/examples/evals/a11y.json +140 -0
- package/examples/evals/api-design.json +52 -0
- package/examples/evals/code-review.json +52 -0
- package/examples/evals/data-modeling.json +52 -0
- package/examples/evals/database-migration.json +52 -0
- package/examples/evals/debugging.json +118 -0
- package/examples/evals/dependency-architecture.json +52 -0
- package/examples/evals/design-system-architecture.json +52 -0
- package/examples/evals/error-tracking.json +52 -0
- package/examples/evals/event-contract-design.json +52 -0
- package/examples/evals/form-ux-architecture.json +52 -0
- package/examples/evals/framework-fit-analysis.json +52 -0
- package/examples/evals/graph-audit.json +139 -0
- package/examples/evals/information-architecture.json +52 -0
- package/examples/evals/interaction-feedback.json +52 -0
- package/examples/evals/interaction-patterns.json +52 -0
- package/examples/evals/layout-composition.json +52 -0
- package/examples/evals/lint-overlay.json +117 -0
- package/examples/evals/microcopy.json +52 -0
- package/examples/evals/observability-modeling.json +52 -0
- package/examples/evals/pattern-recognition.json +96 -0
- package/examples/evals/performance-engineering.json +52 -0
- package/examples/evals/refactor.json +128 -0
- package/examples/evals/semiotics.json +52 -0
- package/examples/evals/skill-infrastructure.json +96 -0
- package/examples/evals/skill-router.json +140 -0
- package/examples/evals/skill-router.routing.json +113 -0
- package/examples/evals/system-interface-contracts.json +52 -0
- package/examples/evals/task-analysis.json +52 -0
- package/examples/evals/testing-strategy.json +118 -0
- package/examples/evals/type-safety.json +249 -0
- package/examples/evals/visual-design-foundations.json +52 -0
- package/examples/evals/webhook-integration.json +52 -0
- package/examples/exports/a11y.skill-md.md +80 -0
- package/examples/exports/debugging.skill-md.md +80 -0
- package/examples/exports/refactor.skill-md.md +78 -0
- package/examples/exports/testing-strategy.skill-md.md +81 -0
- package/examples/projects/markdown-static-site/README.md +115 -0
- package/examples/projects/markdown-static-site/skills/content-source-router/SKILL.md +131 -0
- package/examples/projects/markdown-static-site/skills/image-optimization-pipeline-config/SKILL.md +132 -0
- package/examples/projects/markdown-static-site/skills/link-rot-detection/SKILL.md +103 -0
- package/examples/projects/markdown-static-site/skills/markdown-post-frontmatter-validation/SKILL.md +133 -0
- package/examples/projects/markdown-static-site/skills/migrate-posts-to-v2-frontmatter/SKILL.md +140 -0
- package/examples/projects/saas-stripe-postgres/README.md +208 -0
- package/examples/projects/saas-stripe-postgres/db/migrations/0004_canonicalize_orders.sql +37 -0
- package/examples/projects/saas-stripe-postgres/db/schema.sql +112 -0
- package/examples/projects/saas-stripe-postgres/skills/migrate-orders-to-canonical-schema/SKILL.md +149 -0
- package/examples/projects/saas-stripe-postgres/skills/nextjs-server-action-validation/SKILL.md +154 -0
- package/examples/projects/saas-stripe-postgres/skills/payment-provider-router/SKILL.md +153 -0
- package/examples/projects/saas-stripe-postgres/skills/postgres-rls-pattern/SKILL.md +163 -0
- package/examples/projects/saas-stripe-postgres/skills/stripe-webhook-signature-verification/SKILL.md +137 -0
- package/examples/protocol/skill-metadata-template.md +301 -0
- package/examples/protocol/skills.manifest.sample.json +13245 -0
- package/examples/skill-metadata-template.md +317 -0
- package/examples/skills.manifest.sample.json +13519 -0
- package/examples/tests/v3-1-skos-fixture/SKILL.md +93 -0
- package/marketplace/README.md +17 -0
- package/marketplace/skills/a11y/SKILL.md +66 -0
- package/marketplace/skills/acid-fundamentals/SKILL.md +106 -0
- package/marketplace/skills/agent-engineering/SKILL.md +386 -0
- package/marketplace/skills/agent-eval-design/SKILL.md +55 -0
- package/marketplace/skills/ai-native-development/SKILL.md +294 -0
- package/marketplace/skills/api-design/SKILL.md +60 -0
- package/marketplace/skills/architecture-decision-records/SKILL.md +55 -0
- package/marketplace/skills/background-jobs/SKILL.md +265 -0
- package/marketplace/skills/bounded-context-mapping/SKILL.md +55 -0
- package/marketplace/skills/cap-theorem-tradeoffs/SKILL.md +127 -0
- package/marketplace/skills/client-server-boundary/SKILL.md +187 -0
- package/marketplace/skills/code-review/SKILL.md +120 -0
- package/marketplace/skills/color-system-design/SKILL.md +43 -0
- package/marketplace/skills/component-architecture/SKILL.md +126 -0
- package/marketplace/skills/compression/SKILL.md +112 -0
- package/marketplace/skills/conceptual-modeling/SKILL.md +181 -0
- package/marketplace/skills/connection-pooling/SKILL.md +105 -0
- package/marketplace/skills/constraint-awareness/SKILL.md +287 -0
- package/marketplace/skills/content-monitor/SKILL.md +209 -0
- package/marketplace/skills/context-engineering/SKILL.md +320 -0
- package/marketplace/skills/context-graph/SKILL.md +174 -0
- package/marketplace/skills/context-management/SKILL.md +174 -0
- package/marketplace/skills/context-window/SKILL.md +239 -0
- package/marketplace/skills/contract-testing/SKILL.md +120 -0
- package/marketplace/skills/cron-scheduling/SKILL.md +223 -0
- package/marketplace/skills/dark-mode-implementation/SKILL.md +47 -0
- package/marketplace/skills/data-modeling/SKILL.md +59 -0
- package/marketplace/skills/data-modeling-fundamentals/SKILL.md +117 -0
- package/marketplace/skills/database-migration/SKILL.md +429 -0
- package/marketplace/skills/debugging/SKILL.md +67 -0
- package/marketplace/skills/dependency-architecture/SKILL.md +58 -0
- package/marketplace/skills/design-module-composition/SKILL.md +43 -0
- package/marketplace/skills/design-system-architecture/SKILL.md +61 -0
- package/marketplace/skills/design-thinking/SKILL.md +44 -0
- package/marketplace/skills/diagnosis/SKILL.md +296 -0
- package/marketplace/skills/diff-analysis/SKILL.md +188 -0
- package/marketplace/skills/e2e-test-design/SKILL.md +113 -0
- package/marketplace/skills/entity-relationship-modeling/SKILL.md +218 -0
- package/marketplace/skills/epistemic-grounding/SKILL.md +112 -0
- package/marketplace/skills/error-boundary/SKILL.md +235 -0
- package/marketplace/skills/error-tracking/SKILL.md +261 -0
- package/marketplace/skills/eval-driven-development/SKILL.md +147 -0
- package/marketplace/skills/evaluation/SKILL.md +113 -0
- package/marketplace/skills/event-contract-design/SKILL.md +60 -0
- package/marketplace/skills/event-storming/SKILL.md +56 -0
- package/marketplace/skills/form-ux-architecture/SKILL.md +60 -0
- package/marketplace/skills/framework-fit-analysis/SKILL.md +59 -0
- package/marketplace/skills/frontend-architecture/SKILL.md +43 -0
- package/marketplace/skills/generative-ui/SKILL.md +118 -0
- package/marketplace/skills/graph-audit/SKILL.md +81 -0
- package/marketplace/skills/guardrails/SKILL.md +118 -0
- package/marketplace/skills/hooks-patterns/SKILL.md +185 -0
- package/marketplace/skills/http-semantics/SKILL.md +136 -0
- package/marketplace/skills/ideation/SKILL.md +41 -0
- package/marketplace/skills/indexing-strategy/SKILL.md +108 -0
- package/marketplace/skills/information-architecture/SKILL.md +59 -0
- package/marketplace/skills/integration-test-design/SKILL.md +111 -0
- package/marketplace/skills/intent-recognition/SKILL.md +136 -0
- package/marketplace/skills/interaction-feedback/SKILL.md +59 -0
- package/marketplace/skills/interaction-patterns/SKILL.md +59 -0
- package/marketplace/skills/journey-mapping/SKILL.md +41 -0
- package/marketplace/skills/keywords/SKILL.md +213 -0
- package/marketplace/skills/knowledge-modeling/SKILL.md +232 -0
- package/marketplace/skills/layout-composition/SKILL.md +59 -0
- package/marketplace/skills/linguistics/SKILL.md +429 -0
- package/marketplace/skills/lint-overlay/SKILL.md +76 -0
- package/marketplace/skills/mental-models/SKILL.md +126 -0
- package/marketplace/skills/merge-queue/SKILL.md +94 -0
- package/marketplace/skills/methodology/SKILL.md +317 -0
- package/marketplace/skills/microcopy/SKILL.md +232 -0
- package/marketplace/skills/middleware-patterns/SKILL.md +363 -0
- package/marketplace/skills/mobile-responsive-ux/SKILL.md +287 -0
- package/marketplace/skills/mutation-testing/SKILL.md +112 -0
- package/marketplace/skills/naming-conventions/SKILL.md +112 -0
- package/marketplace/skills/observability-modeling/SKILL.md +59 -0
- package/marketplace/skills/ontology-modeling/SKILL.md +67 -0
- package/marketplace/skills/owasp-security/SKILL.md +153 -0
- package/marketplace/skills/pattern-recognition/SKILL.md +472 -0
- package/marketplace/skills/performance-budgets/SKILL.md +185 -0
- package/marketplace/skills/performance-engineering/SKILL.md +58 -0
- package/marketplace/skills/performance-testing/SKILL.md +125 -0
- package/marketplace/skills/printify/SKILL.md +42 -0
- package/marketplace/skills/prioritization/SKILL.md +118 -0
- package/marketplace/skills/problem-framing/SKILL.md +41 -0
- package/marketplace/skills/problem-locating-solving/SKILL.md +203 -0
- package/marketplace/skills/project-knowledge-extraction/SKILL.md +54 -0
- package/marketplace/skills/prompt-craft/SKILL.md +134 -0
- package/marketplace/skills/prompt-injection-defense/SKILL.md +132 -0
- package/marketplace/skills/property-based-testing/SKILL.md +100 -0
- package/marketplace/skills/prototyping/SKILL.md +43 -0
- package/marketplace/skills/query-optimization/SKILL.md +144 -0
- package/marketplace/skills/real-time-updates/SKILL.md +324 -0
- package/marketplace/skills/ref-patterns/SKILL.md +284 -0
- package/marketplace/skills/refactor/SKILL.md +65 -0
- package/marketplace/skills/rendering-models/SKILL.md +142 -0
- package/marketplace/skills/replication-patterns/SKILL.md +110 -0
- package/marketplace/skills/research-synthesis/SKILL.md +41 -0
- package/marketplace/skills/route-handler-design/SKILL.md +347 -0
- package/marketplace/skills/schema-evolution/SKILL.md +140 -0
- package/marketplace/skills/security-fundamentals/SKILL.md +139 -0
- package/marketplace/skills/semantic-center/SKILL.md +194 -0
- package/marketplace/skills/semantic-relations/SKILL.md +250 -0
- package/marketplace/skills/semantics/SKILL.md +366 -0
- package/marketplace/skills/semiotics/SKILL.md +230 -0
- package/marketplace/skills/seo-strategy/SKILL.md +260 -0
- package/marketplace/skills/server-actions-design/SKILL.md +243 -0
- package/marketplace/skills/server-components-design/SKILL.md +190 -0
- package/marketplace/skills/sharding-strategy/SKILL.md +123 -0
- package/marketplace/skills/shopify/SKILL.md +42 -0
- package/marketplace/skills/skill-infrastructure/SKILL.md +320 -0
- package/marketplace/skills/skill-router/SKILL.md +71 -0
- package/marketplace/skills/skill-scaffold/SKILL.md +105 -0
- package/marketplace/skills/snapshot-testing/SKILL.md +120 -0
- package/marketplace/skills/spec-driven-development/SKILL.md +148 -0
- package/marketplace/skills/state-machine-modeling/SKILL.md +56 -0
- package/marketplace/skills/state-management/SKILL.md +134 -0
- package/marketplace/skills/streaming-architecture/SKILL.md +194 -0
- package/marketplace/skills/summarization/SKILL.md +156 -0
- package/marketplace/skills/suspense-patterns/SKILL.md +265 -0
- package/marketplace/skills/system-interface-contracts/SKILL.md +59 -0
- package/marketplace/skills/task-analysis/SKILL.md +201 -0
- package/marketplace/skills/taxonomy-design/SKILL.md +66 -0
- package/marketplace/skills/test-coverage-strategy/SKILL.md +108 -0
- package/marketplace/skills/test-doubles-design/SKILL.md +98 -0
- package/marketplace/skills/test-driven-development/SKILL.md +96 -0
- package/marketplace/skills/testing-strategy/SKILL.md +67 -0
- package/marketplace/skills/theme-system-design/SKILL.md +43 -0
- package/marketplace/skills/tool-call-flow/SKILL.md +229 -0
- package/marketplace/skills/tool-call-strategy/SKILL.md +292 -0
- package/marketplace/skills/transaction-isolation/SKILL.md +98 -0
- package/marketplace/skills/type-safety/SKILL.md +177 -0
- package/marketplace/skills/typography-system/SKILL.md +43 -0
- package/marketplace/skills/usability-testing/SKILL.md +43 -0
- package/marketplace/skills/user-research/SKILL.md +43 -0
- package/marketplace/skills/vercel-composition-patterns/SKILL.md +157 -0
- package/marketplace/skills/version-control/SKILL.md +233 -0
- package/marketplace/skills/visual-design-foundations/SKILL.md +59 -0
- package/marketplace/skills/visual-hierarchy/SKILL.md +43 -0
- package/marketplace/skills/webhook-integration/SKILL.md +331 -0
- package/marketplace/skills/writing-humanizer/SKILL.md +380 -0
- package/package.json +67 -0
- package/schemas/manifest.schema.json +811 -0
- package/schemas/manifest.v2.schema.json +164 -0
- package/schemas/manifest.v3.schema.json +758 -0
- package/schemas/manifest.v4.schema.json +755 -0
- package/schemas/manifest.v5.schema.json +755 -0
- package/schemas/manifest.v6.schema.json +811 -0
- package/schemas/skill.context.jsonld +279 -0
- package/schemas/skill.schema.json +919 -0
- package/schemas/skill.v2.schema.json +201 -0
- package/schemas/skill.v3.schema.json +827 -0
- package/schemas/skill.v4.schema.json +822 -0
- package/schemas/skill.v5.schema.json +830 -0
- package/schemas/skill.v6.schema.json +946 -0
- package/schemas/vocabulary/keywords.json +180 -0
- package/schemas/vocabulary/workspace_tags.json +23 -0
- package/scripts/__tests__/migrate-skill-v2-to-v3.test.js +161 -0
- package/scripts/__tests__/migrate-skill-v3-to-v4.test.js +158 -0
- package/scripts/__tests__/test-export-parser-drift.js +149 -0
- package/scripts/__tests__/test-marketplace-export.js +114 -0
- package/scripts/__tests__/test-router-paths.js +82 -0
- package/scripts/__tests__/test-stability-promotion.js +244 -0
- package/scripts/__tests__/test-v3-1-alias-contract.js +109 -0
- package/scripts/__tests__/test-v3-1-skos-runtime.js +116 -0
- package/scripts/backfill-schema-version.js +198 -0
- package/scripts/build-field-reference.js +160 -0
- package/scripts/build-retrieval-baseline.js +511 -0
- package/scripts/check-markdown-links.js +211 -0
- package/scripts/check-protocol-consistency.js +979 -0
- package/scripts/export-marketplace-skills.js +610 -0
- package/scripts/export-skill.js +374 -0
- package/scripts/generate-manifest.js +787 -0
- package/scripts/lib/alias-contract.js +83 -0
- package/scripts/lib/audit-prompt-builder.js +771 -0
- package/scripts/lib/mock-grader.js +134 -0
- package/scripts/lib/parse-frontmatter.js +429 -0
- package/scripts/lib/roots.js +119 -0
- package/scripts/lint/check-archetype-sections.js +185 -0
- package/scripts/lint/check-category-enum.js +83 -0
- package/scripts/lint/check-routing-eval.js +146 -0
- package/scripts/lint/check-routing-quality.js +211 -0
- package/scripts/lint/check-stability-promotion.js +220 -0
- package/scripts/lint/format-code-frame.js +206 -0
- package/scripts/marketplace-install.js +125 -0
- package/scripts/migrate-category-to-enum.js +169 -0
- package/scripts/migrate-skill-v2-to-v3.js +424 -0
- package/scripts/migrate-skill-v3-to-v4.js +200 -0
- package/scripts/migrate-skill-v5-to-v6.js +304 -0
- package/scripts/restructure-by-category.js +85 -0
- package/scripts/seed-publication-classification.js +282 -0
- package/scripts/skill-audit.js +893 -0
- package/scripts/skill-graph-drift.js +483 -0
- package/scripts/skill-graph-route.js +766 -0
- package/scripts/skill-graph-routing-eval.js +393 -0
- package/scripts/skill-lint.js +1317 -0
- package/scripts/skill-overlap.js +213 -0
- 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.
|