aios-core 2.1.6 → 2.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.aios-core/core/README.md +229 -229
- package/.aios-core/core/data/agent-config-requirements.yaml +368 -368
- package/.aios-core/core/data/aios-kb.md +923 -923
- package/.aios-core/core/data/workflow-patterns.yaml +267 -267
- package/.aios-core/core/docs/SHARD-TRANSLATION-GUIDE.md +335 -335
- package/.aios-core/core/docs/component-creation-guide.md +457 -457
- package/.aios-core/core/docs/session-update-pattern.md +307 -307
- package/.aios-core/core/docs/template-syntax.md +266 -266
- package/.aios-core/core/docs/troubleshooting-guide.md +624 -624
- package/.aios-core/core/elicitation/elicitation-engine.js +1 -1
- package/.aios-core/core/index.esm.js +42 -42
- package/.aios-core/core/index.js +1 -1
- package/.aios-core/core/migration/migration-config.yaml +83 -83
- package/.aios-core/core/migration/module-mapping.yaml +89 -89
- package/.aios-core/core/quality-gates/layer2-pr-automation.js +1 -1
- package/.aios-core/core/quality-gates/quality-gate-config.yaml +86 -86
- package/.aios-core/core/registry/README.md +179 -179
- package/.aios-core/core/utils/security-utils.js +1 -1
- package/.aios-core/core-config.yaml +391 -382
- package/.aios-core/data/agent-config-requirements.yaml +368 -368
- package/.aios-core/data/aios-kb.md +923 -923
- package/.aios-core/data/technical-preferences.md +3 -3
- package/.aios-core/data/workflow-patterns.yaml +267 -267
- package/.aios-core/development/README.md +142 -142
- package/.aios-core/development/agent-teams/team-all.yaml +15 -15
- package/.aios-core/development/agent-teams/team-fullstack.yaml +18 -18
- package/.aios-core/development/agent-teams/team-ide-minimal.yaml +10 -10
- package/.aios-core/development/agent-teams/team-no-ui.yaml +13 -13
- package/.aios-core/development/agent-teams/team-qa-focused.yaml +155 -155
- package/.aios-core/development/agents/aios-master.md +339 -339
- package/.aios-core/development/agents/analyst.md +195 -195
- package/.aios-core/development/agents/architect.md +359 -359
- package/.aios-core/development/agents/data-engineer.md +468 -468
- package/.aios-core/development/agents/dev.md +390 -390
- package/.aios-core/development/agents/devops.md +398 -398
- package/.aios-core/development/agents/pm.md +198 -198
- package/.aios-core/development/agents/po.md +256 -256
- package/.aios-core/development/agents/qa.md +312 -312
- package/.aios-core/development/agents/sm.md +220 -220
- package/.aios-core/development/agents/ux-design-expert.md +451 -451
- package/.aios-core/development/scripts/greeting-config-cli.js +85 -85
- package/.aios-core/development/tasks/add-mcp.md +319 -319
- package/.aios-core/development/tasks/advanced-elicitation.md +318 -318
- package/.aios-core/development/tasks/analyst-facilitate-brainstorming.md +341 -341
- package/.aios-core/development/tasks/analyze-framework.md +696 -696
- package/.aios-core/development/tasks/analyze-performance.md +637 -637
- package/.aios-core/development/tasks/apply-qa-fixes.md +340 -340
- package/.aios-core/development/tasks/architect-analyze-impact.md +826 -826
- package/.aios-core/development/tasks/audit-codebase.md +429 -429
- package/.aios-core/development/tasks/audit-tailwind-config.md +270 -270
- package/.aios-core/development/tasks/audit-utilities.md +358 -358
- package/.aios-core/development/tasks/bootstrap-shadcn-library.md +286 -286
- package/.aios-core/development/tasks/brownfield-create-epic.md +485 -485
- package/.aios-core/development/tasks/brownfield-create-story.md +356 -356
- package/.aios-core/development/tasks/build-component.md +478 -478
- package/.aios-core/development/tasks/calculate-roi.md +455 -455
- package/.aios-core/development/tasks/ci-cd-configuration.md +764 -764
- package/.aios-core/development/tasks/cleanup-utilities.md +670 -670
- package/.aios-core/development/tasks/collaborative-edit.md +1108 -1108
- package/.aios-core/development/tasks/compose-molecule.md +284 -284
- package/.aios-core/development/tasks/consolidate-patterns.md +414 -414
- package/.aios-core/development/tasks/correct-course.md +279 -279
- package/.aios-core/development/tasks/create-agent.md +321 -321
- package/.aios-core/development/tasks/create-brownfield-story.md +726 -726
- package/.aios-core/development/tasks/create-deep-research-prompt.md +498 -498
- package/.aios-core/development/tasks/create-doc.md +316 -316
- package/.aios-core/development/tasks/create-next-story.md +774 -774
- package/.aios-core/development/tasks/create-suite.md +283 -283
- package/.aios-core/development/tasks/create-task.md +371 -371
- package/.aios-core/development/tasks/create-workflow.md +370 -370
- package/.aios-core/development/tasks/db-analyze-hotpaths.md +572 -572
- package/.aios-core/development/tasks/db-apply-migration.md +381 -381
- package/.aios-core/development/tasks/db-bootstrap.md +642 -642
- package/.aios-core/development/tasks/db-domain-modeling.md +693 -693
- package/.aios-core/development/tasks/db-dry-run.md +293 -293
- package/.aios-core/development/tasks/db-env-check.md +260 -260
- package/.aios-core/development/tasks/db-expansion-pack-integration.md +663 -663
- package/.aios-core/development/tasks/db-explain.md +631 -631
- package/.aios-core/development/tasks/db-impersonate.md +495 -495
- package/.aios-core/development/tasks/db-load-csv.md +593 -593
- package/.aios-core/development/tasks/db-policy-apply.md +653 -653
- package/.aios-core/development/tasks/db-rls-audit.md +411 -411
- package/.aios-core/development/tasks/db-rollback.md +739 -739
- package/.aios-core/development/tasks/db-run-sql.md +613 -613
- package/.aios-core/development/tasks/db-schema-audit.md +1011 -1011
- package/.aios-core/development/tasks/db-seed.md +390 -390
- package/.aios-core/development/tasks/db-smoke-test.md +351 -351
- package/.aios-core/development/tasks/db-snapshot.md +569 -569
- package/.aios-core/development/tasks/db-supabase-setup.md +712 -712
- package/.aios-core/development/tasks/db-verify-order.md +515 -515
- package/.aios-core/development/tasks/deprecate-component.md +956 -956
- package/.aios-core/development/tasks/dev-apply-qa-fixes.md +318 -318
- package/.aios-core/development/tasks/dev-backlog-debt.md +469 -469
- package/.aios-core/development/tasks/dev-develop-story.md +846 -846
- package/.aios-core/development/tasks/dev-improve-code-quality.md +872 -872
- package/.aios-core/development/tasks/dev-optimize-performance.md +1033 -1033
- package/.aios-core/development/tasks/dev-suggest-refactoring.md +870 -870
- package/.aios-core/development/tasks/dev-validate-next-story.md +348 -348
- package/.aios-core/development/tasks/document-project.md +552 -552
- package/.aios-core/development/tasks/environment-bootstrap.md +1311 -1311
- package/.aios-core/development/tasks/execute-checklist.md +301 -301
- package/.aios-core/development/tasks/export-design-tokens-dtcg.md +274 -274
- package/.aios-core/development/tasks/extend-pattern.md +269 -269
- package/.aios-core/development/tasks/extract-tokens.md +467 -467
- package/.aios-core/development/tasks/facilitate-brainstorming-session.md +518 -518
- package/.aios-core/development/tasks/generate-ai-frontend-prompt.md +260 -260
- package/.aios-core/development/tasks/generate-documentation.md +284 -284
- package/.aios-core/development/tasks/generate-migration-strategy.md +522 -522
- package/.aios-core/development/tasks/generate-shock-report.md +501 -501
- package/.aios-core/development/tasks/github-devops-github-pr-automation.md +427 -427
- package/.aios-core/development/tasks/github-devops-pre-push-quality-gate.md +733 -733
- package/.aios-core/development/tasks/github-devops-repository-cleanup.md +374 -374
- package/.aios-core/development/tasks/github-devops-version-management.md +483 -483
- package/.aios-core/development/tasks/improve-self.md +822 -822
- package/.aios-core/development/tasks/index-docs.md +387 -387
- package/.aios-core/development/tasks/init-project-status.md +506 -506
- package/.aios-core/development/tasks/integrate-expansion-pack.md +314 -314
- package/.aios-core/development/tasks/kb-mode-interaction.md +283 -283
- package/.aios-core/development/tasks/learn-patterns.md +900 -900
- package/.aios-core/development/tasks/mcp-workflow.md +437 -437
- package/.aios-core/development/tasks/modify-agent.md +381 -381
- package/.aios-core/development/tasks/modify-task.md +424 -424
- package/.aios-core/development/tasks/modify-workflow.md +465 -465
- package/.aios-core/development/tasks/po-backlog-add.md +370 -370
- package/.aios-core/development/tasks/po-manage-story-backlog.md +523 -523
- package/.aios-core/development/tasks/po-pull-story-from-clickup.md +540 -540
- package/.aios-core/development/tasks/po-pull-story.md +316 -316
- package/.aios-core/development/tasks/po-stories-index.md +351 -351
- package/.aios-core/development/tasks/po-sync-story-to-clickup.md +457 -457
- package/.aios-core/development/tasks/po-sync-story.md +303 -303
- package/.aios-core/development/tasks/pr-automation.md +701 -701
- package/.aios-core/development/tasks/propose-modification.md +842 -842
- package/.aios-core/development/tasks/qa-backlog-add-followup.md +425 -425
- package/.aios-core/development/tasks/qa-gate.md +373 -373
- package/.aios-core/development/tasks/qa-generate-tests.md +1174 -1174
- package/.aios-core/development/tasks/qa-nfr-assess.md +557 -557
- package/.aios-core/development/tasks/qa-review-proposal.md +1157 -1157
- package/.aios-core/development/tasks/qa-review-story.md +682 -682
- package/.aios-core/development/tasks/qa-risk-profile.md +566 -566
- package/.aios-core/development/tasks/qa-run-tests.md +277 -277
- package/.aios-core/development/tasks/qa-test-design.md +387 -387
- package/.aios-core/development/tasks/qa-trace-requirements.md +476 -476
- package/.aios-core/development/tasks/release-management.md +723 -723
- package/.aios-core/development/tasks/security-audit.md +554 -554
- package/.aios-core/development/tasks/security-scan.md +790 -790
- package/.aios-core/development/tasks/setup-database.md +741 -741
- package/.aios-core/development/tasks/setup-design-system.md +462 -462
- package/.aios-core/development/tasks/setup-github.md +874 -874
- package/.aios-core/development/tasks/setup-llm-routing.md +1 -1
- package/.aios-core/development/tasks/setup-mcp-docker.md +584 -584
- package/.aios-core/development/tasks/setup-project-docs.md +1 -1
- package/.aios-core/development/tasks/shard-doc.md +537 -537
- package/.aios-core/development/tasks/sm-create-next-story.md +480 -480
- package/.aios-core/development/tasks/sync-documentation.md +864 -864
- package/.aios-core/development/tasks/tailwind-upgrade.md +294 -294
- package/.aios-core/development/tasks/test-as-user.md +621 -621
- package/.aios-core/development/tasks/test-validation-task.md +171 -171
- package/.aios-core/development/tasks/undo-last.md +346 -346
- package/.aios-core/development/tasks/update-manifest.md +409 -409
- package/.aios-core/development/tasks/ux-create-wireframe.md +617 -617
- package/.aios-core/development/tasks/ux-ds-scan-artifact.md +672 -672
- package/.aios-core/development/tasks/ux-user-research.md +559 -559
- package/.aios-core/development/tasks/validate-next-story.md +422 -422
- package/.aios-core/development/workflows/README.md +83 -83
- package/.aios-core/development/workflows/brownfield-fullstack.yaml +297 -297
- package/.aios-core/development/workflows/brownfield-service.yaml +187 -187
- package/.aios-core/development/workflows/brownfield-ui.yaml +197 -197
- package/.aios-core/development/workflows/greenfield-fullstack.yaml +333 -333
- package/.aios-core/development/workflows/greenfield-service.yaml +206 -206
- package/.aios-core/development/workflows/greenfield-ui.yaml +235 -235
- package/.aios-core/docs/SHARD-TRANSLATION-GUIDE.md +335 -335
- package/.aios-core/docs/component-creation-guide.md +457 -457
- package/.aios-core/docs/session-update-pattern.md +307 -307
- package/.aios-core/docs/standards/AGENT-PERSONALIZATION-STANDARD-V1.md +572 -572
- package/.aios-core/docs/standards/AIOS-COLOR-PALETTE-QUICK-REFERENCE.md +185 -185
- package/.aios-core/docs/standards/AIOS-COLOR-PALETTE-V2.1.md +354 -354
- package/.aios-core/docs/standards/AIOS-FRAMEWORK-MASTER.md +1963 -1963
- package/.aios-core/docs/standards/AIOS-LIVRO-DE-OURO-V2.1-COMPLETE.md +821 -821
- package/.aios-core/docs/standards/AIOS-LIVRO-DE-OURO-V2.1-SUMMARY.md +1190 -1190
- package/.aios-core/docs/standards/AIOS-LIVRO-DE-OURO-V2.1.md +439 -439
- package/.aios-core/docs/standards/AIOS-LIVRO-DE-OURO-V2.2-SUMMARY.md +1339 -1339
- package/.aios-core/docs/standards/AIOS-LIVRO-DE-OURO.md +5398 -5398
- package/.aios-core/docs/standards/EXECUTOR-DECISION-TREE.md +697 -697
- package/.aios-core/docs/standards/OPEN-SOURCE-VS-SERVICE-DIFFERENCES.md +511 -511
- package/.aios-core/docs/standards/QUALITY-GATES-SPECIFICATION.md +556 -556
- package/.aios-core/docs/standards/STANDARDS-INDEX.md +210 -210
- package/.aios-core/docs/standards/STORY-TEMPLATE-V2-SPECIFICATION.md +550 -550
- package/.aios-core/docs/standards/TASK-FORMAT-SPECIFICATION-V1.md +1414 -1414
- package/.aios-core/docs/standards/V3-ARCHITECTURAL-DECISIONS.md +523 -523
- package/.aios-core/docs/template-syntax.md +266 -266
- package/.aios-core/docs/troubleshooting-guide.md +624 -624
- package/.aios-core/index.esm.js +15 -15
- package/.aios-core/index.js +1 -1
- package/.aios-core/infrastructure/README.md +126 -126
- package/.aios-core/infrastructure/integrations/pm-adapters/README.md +59 -59
- package/.aios-core/infrastructure/scripts/approval-workflow.js +1 -1
- package/.aios-core/infrastructure/scripts/batch-creator.js +1 -1
- package/.aios-core/infrastructure/scripts/component-generator.js +3 -3
- package/.aios-core/infrastructure/scripts/component-metadata.js +1 -1
- package/.aios-core/infrastructure/scripts/component-search.js +1 -1
- package/.aios-core/infrastructure/scripts/coverage-analyzer.js +1 -1
- package/.aios-core/infrastructure/scripts/dependency-analyzer.js +1 -1
- package/.aios-core/infrastructure/scripts/dependency-impact-analyzer.js +1 -1
- package/.aios-core/infrastructure/scripts/framework-analyzer.js +1 -1
- package/.aios-core/infrastructure/scripts/improvement-engine.js +1 -1
- package/.aios-core/infrastructure/scripts/llm-routing/install-llm-routing.js +26 -13
- package/.aios-core/infrastructure/scripts/llm-routing/templates/claude-free-tracked.cmd +127 -0
- package/.aios-core/infrastructure/scripts/llm-routing/templates/claude-free-tracked.sh +108 -0
- package/.aios-core/infrastructure/scripts/llm-routing/templates/deepseek-proxy.cmd +71 -0
- package/.aios-core/infrastructure/scripts/llm-routing/templates/deepseek-proxy.sh +65 -0
- package/.aios-core/infrastructure/scripts/llm-routing/templates/deepseek-usage.cmd +51 -0
- package/.aios-core/infrastructure/scripts/llm-routing/templates/deepseek-usage.sh +16 -0
- package/.aios-core/infrastructure/scripts/llm-routing/usage-tracker/index.js +549 -0
- package/.aios-core/infrastructure/scripts/modification-risk-assessment.js +1 -1
- package/.aios-core/infrastructure/scripts/performance-analyzer.js +1 -1
- package/.aios-core/infrastructure/scripts/pm-adapter.js +134 -134
- package/.aios-core/infrastructure/scripts/repository-detector.js +3 -3
- package/.aios-core/infrastructure/scripts/template-engine.js +1 -1
- package/.aios-core/infrastructure/scripts/template-validator.js +1 -1
- package/.aios-core/infrastructure/scripts/test-generator.js +1 -1
- package/.aios-core/infrastructure/scripts/test-quality-assessment.js +1 -1
- package/.aios-core/infrastructure/scripts/transaction-manager.js +1 -1
- package/.aios-core/infrastructure/scripts/usage-analytics.js +1 -1
- package/.aios-core/infrastructure/scripts/visual-impact-generator.js +2 -2
- package/.aios-core/infrastructure/templates/github-workflows/README.md +109 -109
- package/.aios-core/infrastructure/tests/regression-suite-v2.md +621 -621
- package/.aios-core/infrastructure/tools/README.md +222 -222
- package/.aios-core/infrastructure/tools/cli/github-cli.yaml +200 -200
- package/.aios-core/infrastructure/tools/cli/railway-cli.yaml +260 -260
- package/.aios-core/infrastructure/tools/cli/supabase-cli.yaml +224 -224
- package/.aios-core/infrastructure/tools/local/ffmpeg.yaml +261 -261
- package/.aios-core/infrastructure/tools/mcp/21st-dev-magic.yaml +127 -127
- package/.aios-core/infrastructure/tools/mcp/browser.yaml +103 -103
- package/.aios-core/infrastructure/tools/mcp/clickup.yaml +534 -534
- package/.aios-core/infrastructure/tools/mcp/context7.yaml +78 -78
- package/.aios-core/infrastructure/tools/mcp/desktop-commander.yaml +180 -180
- package/.aios-core/infrastructure/tools/mcp/exa.yaml +103 -103
- package/.aios-core/infrastructure/tools/mcp/google-workspace.yaml +930 -930
- package/.aios-core/infrastructure/tools/mcp/n8n.yaml +551 -551
- package/.aios-core/infrastructure/tools/mcp/supabase.yaml +808 -808
- package/.aios-core/install-manifest.yaml +347 -347
- package/.aios-core/product/README.md +56 -56
- package/.aios-core/product/checklists/accessibility-wcag-checklist.md +80 -0
- package/.aios-core/product/checklists/architect-checklist.md +443 -443
- package/.aios-core/product/checklists/change-checklist.md +182 -182
- package/.aios-core/product/checklists/component-quality-checklist.md +74 -0
- package/.aios-core/product/checklists/database-design-checklist.md +119 -119
- package/.aios-core/product/checklists/dba-predeploy-checklist.md +97 -97
- package/.aios-core/product/checklists/dba-rollback-checklist.md +99 -99
- package/.aios-core/product/checklists/migration-readiness-checklist.md +75 -0
- package/.aios-core/product/checklists/pattern-audit-checklist.md +88 -0
- package/.aios-core/product/checklists/pm-checklist.md +375 -375
- package/.aios-core/product/checklists/po-master-checklist.md +441 -441
- package/.aios-core/product/checklists/pre-push-checklist.md +108 -108
- package/.aios-core/product/checklists/release-checklist.md +122 -122
- package/.aios-core/product/checklists/story-dod-checklist.md +101 -101
- package/.aios-core/product/checklists/story-draft-checklist.md +215 -215
- package/.aios-core/product/data/atomic-design-principles.md +108 -0
- package/.aios-core/product/data/brainstorming-techniques.md +36 -36
- package/.aios-core/product/data/consolidation-algorithms.md +142 -0
- package/.aios-core/product/data/database-best-practices.md +182 -0
- package/.aios-core/product/data/design-token-best-practices.md +107 -0
- package/.aios-core/product/data/elicitation-methods.md +134 -134
- package/.aios-core/product/data/integration-patterns.md +207 -0
- package/.aios-core/product/data/migration-safety-guide.md +329 -0
- package/.aios-core/product/data/mode-selection-best-practices.md +471 -471
- package/.aios-core/product/data/postgres-tuning-guide.md +300 -0
- package/.aios-core/product/data/rls-security-patterns.md +333 -0
- package/.aios-core/product/data/roi-calculation-guide.md +142 -0
- package/.aios-core/product/data/supabase-patterns.md +330 -0
- package/.aios-core/product/data/test-levels-framework.md +148 -148
- package/.aios-core/product/data/test-priorities-matrix.md +174 -174
- package/.aios-core/product/data/wcag-compliance-guide.md +267 -0
- package/.aios-core/product/templates/1mcp-config.yaml +225 -225
- package/.aios-core/product/templates/activation-instructions-inline-greeting.yaml +63 -63
- package/.aios-core/product/templates/activation-instructions-template.md +258 -258
- package/.aios-core/product/templates/agent-template.yaml +120 -120
- package/.aios-core/product/templates/architecture-tmpl.yaml +650 -650
- package/.aios-core/product/templates/brainstorming-output-tmpl.yaml +155 -155
- package/.aios-core/product/templates/brownfield-architecture-tmpl.yaml +475 -475
- package/.aios-core/product/templates/brownfield-prd-tmpl.yaml +279 -279
- package/.aios-core/product/templates/changelog-template.md +134 -134
- package/.aios-core/product/templates/command-rationalization-matrix.md +152 -152
- package/.aios-core/product/templates/competitor-analysis-tmpl.yaml +292 -292
- package/.aios-core/product/templates/design-story-tmpl.yaml +587 -587
- package/.aios-core/product/templates/ds-artifact-analysis.md +70 -70
- package/.aios-core/product/templates/front-end-architecture-tmpl.yaml +205 -205
- package/.aios-core/product/templates/front-end-spec-tmpl.yaml +348 -348
- package/.aios-core/product/templates/fullstack-architecture-tmpl.yaml +804 -804
- package/.aios-core/product/templates/github-pr-template.md +67 -67
- package/.aios-core/product/templates/gordon-mcp.yaml +140 -140
- package/.aios-core/product/templates/ide-rules/antigravity-rules.md +115 -115
- package/.aios-core/product/templates/ide-rules/claude-rules.md +221 -221
- package/.aios-core/product/templates/ide-rules/cline-rules.md +84 -84
- package/.aios-core/product/templates/ide-rules/copilot-rules.md +92 -92
- package/.aios-core/product/templates/ide-rules/cursor-rules.md +115 -115
- package/.aios-core/product/templates/ide-rules/gemini-rules.md +85 -85
- package/.aios-core/product/templates/ide-rules/roo-rules.md +86 -86
- package/.aios-core/product/templates/ide-rules/trae-rules.md +104 -104
- package/.aios-core/product/templates/ide-rules/windsurf-rules.md +80 -80
- package/.aios-core/product/templates/index-strategy-tmpl.yaml +53 -53
- package/.aios-core/product/templates/market-research-tmpl.yaml +251 -251
- package/.aios-core/product/templates/mcp-workflow.js +271 -271
- package/.aios-core/product/templates/migration-plan-tmpl.yaml +1022 -1022
- package/.aios-core/product/templates/migration-strategy-tmpl.md +524 -524
- package/.aios-core/product/templates/personalized-agent-template.md +258 -258
- package/.aios-core/product/templates/personalized-checklist-template.md +340 -340
- package/.aios-core/product/templates/personalized-task-template-v2.md +905 -905
- package/.aios-core/product/templates/personalized-task-template.md +344 -344
- package/.aios-core/product/templates/personalized-template-file.yaml +322 -322
- package/.aios-core/product/templates/personalized-workflow-template.yaml +460 -460
- package/.aios-core/product/templates/prd-tmpl.yaml +201 -201
- package/.aios-core/product/templates/project-brief-tmpl.yaml +220 -220
- package/.aios-core/product/templates/qa-gate-tmpl.yaml +240 -240
- package/.aios-core/product/templates/rls-policies-tmpl.yaml +1203 -1203
- package/.aios-core/product/templates/schema-design-tmpl.yaml +428 -428
- package/.aios-core/product/templates/state-persistence-tmpl.yaml +219 -219
- package/.aios-core/product/templates/story-tmpl.yaml +331 -331
- package/.aios-core/product/templates/task-execution-report.md +495 -495
- package/.aios-core/product/templates/task-template.md +122 -122
- package/.aios-core/product/templates/token-exports-tailwind-tmpl.js +395 -395
- package/.aios-core/product/templates/tokens-schema-tmpl.yaml +305 -305
- package/.aios-core/product/templates/workflow-template.yaml +133 -133
- package/.aios-core/scripts/README.md +354 -354
- package/.aios-core/scripts/aios-doc-template.md +325 -325
- package/.aios-core/scripts/elicitation-engine.js +1 -1
- package/.aios-core/scripts/test-template-system.js +1 -1
- package/.aios-core/scripts/workflow-management.md +69 -69
- package/.aios-core/user-guide.md +1413 -1413
- package/.aios-core/working-in-the-brownfield.md +361 -361
- package/LICENSE +1 -1
- package/README.md +704 -703
- package/bin/aios-init-old.js +3 -3
- package/bin/aios-init-v4.js +1 -1
- package/bin/aios-init.backup-v1.1.4.js +1 -1
- package/bin/aios-init.js +3 -3
- package/bin/aios.js +279 -279
- package/bin/utils/install-errors.js +339 -339
- package/bin/utils/install-transaction.js +445 -445
- package/index.d.ts +18 -18
- package/index.esm.js +20 -20
- package/index.js +6 -6
- package/package.json +8 -11
- package/packages/installer/src/config/templates/env-template.js +27 -4
- package/packages/installer/src/detection/detect-project-type.js +81 -81
- package/packages/installer/tests/integration/wizard-detection.test.js +8 -6
- package/packages/installer/tests/unit/env-template.test.js +8 -8
- package/src/config/ide-configs.js +1 -1
- package/src/wizard/feedback.js +2 -2
- package/src/wizard/index.js +1 -1
- package/src/wizard/validation/report-generator.js +1 -1
- package/src/wizard/validation/troubleshooting-system.js +13 -13
- package/tools/diagnose-installation.js +266 -0
- package/tools/diagnose-npx-issue.ps1 +96 -0
- package/tools/quick-diagnose.cmd +85 -0
- package/tools/quick-diagnose.ps1 +117 -0
- package/.aios-core/infrastructure/scripts/_archived/final-todo-count.js +0 -122
- package/.aios-core/infrastructure/scripts/_archived/fix-yaml-formatting.js +0 -89
- package/.aios-core/infrastructure/scripts/_archived/migration-generator.js +0 -780
- package/.aios-core/infrastructure/scripts/_archived/migration-path-generator.js +0 -950
- package/.aios-core/infrastructure/scripts/_archived/phase2-entrada-saida-errors.js +0 -425
- package/.aios-core/infrastructure/scripts/_archived/phase2-spot-check.js +0 -132
- package/.aios-core/infrastructure/scripts/_archived/phase3-tools-scripts-validation.js +0 -381
- package/.aios-core/infrastructure/scripts/_archived/phase4-metadata-performance.js +0 -203
- package/.aios-core/infrastructure/scripts/_archived/test-yaml-parsing.js +0 -24
- package/.aios-core/infrastructure/scripts/_archived/verify-yaml-fix.js +0 -51
- package/.aios-core/tasks/find-component.md.legacy +0 -391
- package/.aios-core/tasks/generate-commit-message.md.legacy +0 -426
- package/.aios-core/tasks/generate-migration.md.legacy +0 -382
- package/.aios-core/tasks/rollback-modification.md.legacy +0 -307
- package/.aios-core/tasks/update-tests.md.legacy +0 -283
|
@@ -1,1011 +1,1011 @@
|
|
|
1
|
-
# Task: Schema Audit
|
|
2
|
-
|
|
3
|
-
**Purpose**: Comprehensive audit of database schema quality and best practices
|
|
4
|
-
|
|
5
|
-
**Elicit**: false
|
|
6
|
-
|
|
7
|
-
---
|
|
8
|
-
|
|
9
|
-
## Execution Modes
|
|
10
|
-
|
|
11
|
-
**Choose your execution mode:**
|
|
12
|
-
|
|
13
|
-
### 1. YOLO Mode - Fast, Autonomous (0-1 prompts)
|
|
14
|
-
- Autonomous decision making with logging
|
|
15
|
-
- Minimal user interaction
|
|
16
|
-
- **Best for:** Simple, deterministic tasks
|
|
17
|
-
|
|
18
|
-
### 2. Interactive Mode - Balanced, Educational (5-10 prompts) **[DEFAULT]**
|
|
19
|
-
- Explicit decision checkpoints
|
|
20
|
-
- Educational explanations
|
|
21
|
-
- **Best for:** Learning, complex decisions
|
|
22
|
-
|
|
23
|
-
### 3. Pre-Flight Planning - Comprehensive Upfront Planning
|
|
24
|
-
- Task analysis phase (identify all ambiguities)
|
|
25
|
-
- Zero ambiguity execution
|
|
26
|
-
- **Best for:** Ambiguous requirements, critical work
|
|
27
|
-
|
|
28
|
-
**Parameter:** `mode` (optional, default: `interactive`)
|
|
29
|
-
|
|
30
|
-
---
|
|
31
|
-
|
|
32
|
-
## Task Definition (AIOS Task Format V1.0)
|
|
33
|
-
|
|
34
|
-
```yaml
|
|
35
|
-
task: dbSchemaAudit()
|
|
36
|
-
responsável: Dara (Sage)
|
|
37
|
-
responsavel_type: Agente
|
|
38
|
-
atomic_layer: Strategy
|
|
39
|
-
|
|
40
|
-
**Entrada:**
|
|
41
|
-
- campo: query
|
|
42
|
-
tipo: string
|
|
43
|
-
origem: User Input
|
|
44
|
-
obrigatório: true
|
|
45
|
-
validação: Valid SQL query
|
|
46
|
-
|
|
47
|
-
- campo: params
|
|
48
|
-
tipo: object
|
|
49
|
-
origem: User Input
|
|
50
|
-
obrigatório: false
|
|
51
|
-
validação: Query parameters
|
|
52
|
-
|
|
53
|
-
- campo: connection
|
|
54
|
-
tipo: object
|
|
55
|
-
origem: config
|
|
56
|
-
obrigatório: true
|
|
57
|
-
validação: Valid PostgreSQL connection via Supabase
|
|
58
|
-
|
|
59
|
-
**Saída:**
|
|
60
|
-
- campo: query_result
|
|
61
|
-
tipo: array
|
|
62
|
-
destino: Memory
|
|
63
|
-
persistido: false
|
|
64
|
-
|
|
65
|
-
- campo: records_affected
|
|
66
|
-
tipo: number
|
|
67
|
-
destino: Return value
|
|
68
|
-
persistido: false
|
|
69
|
-
|
|
70
|
-
- campo: execution_time
|
|
71
|
-
tipo: number
|
|
72
|
-
destino: Memory
|
|
73
|
-
persistido: false
|
|
74
|
-
```
|
|
75
|
-
|
|
76
|
-
---
|
|
77
|
-
|
|
78
|
-
## Pre-Conditions
|
|
79
|
-
|
|
80
|
-
**Purpose:** Validate prerequisites BEFORE task execution (blocking)
|
|
81
|
-
|
|
82
|
-
**Checklist:**
|
|
83
|
-
|
|
84
|
-
```yaml
|
|
85
|
-
pre-conditions:
|
|
86
|
-
- [ ] Database connection established; query syntax valid
|
|
87
|
-
tipo: pre-condition
|
|
88
|
-
blocker: true
|
|
89
|
-
validação: |
|
|
90
|
-
Check database connection established; query syntax valid
|
|
91
|
-
error_message: "Pre-condition failed: Database connection established; query syntax valid"
|
|
92
|
-
```
|
|
93
|
-
|
|
94
|
-
---
|
|
95
|
-
|
|
96
|
-
## Post-Conditions
|
|
97
|
-
|
|
98
|
-
**Purpose:** Validate execution success AFTER task completes
|
|
99
|
-
|
|
100
|
-
**Checklist:**
|
|
101
|
-
|
|
102
|
-
```yaml
|
|
103
|
-
post-conditions:
|
|
104
|
-
- [ ] Query executed; results returned; transaction committed
|
|
105
|
-
tipo: post-condition
|
|
106
|
-
blocker: true
|
|
107
|
-
validação: |
|
|
108
|
-
Verify query executed; results returned; transaction committed
|
|
109
|
-
error_message: "Post-condition failed: Query executed; results returned; transaction committed"
|
|
110
|
-
```
|
|
111
|
-
|
|
112
|
-
---
|
|
113
|
-
|
|
114
|
-
## Acceptance Criteria
|
|
115
|
-
|
|
116
|
-
**Purpose:** Definitive pass/fail criteria for task completion
|
|
117
|
-
|
|
118
|
-
**Checklist:**
|
|
119
|
-
|
|
120
|
-
```yaml
|
|
121
|
-
acceptance-criteria:
|
|
122
|
-
- [ ] Data persisted correctly; constraints respected; no orphaned data
|
|
123
|
-
tipo: acceptance-criterion
|
|
124
|
-
blocker: true
|
|
125
|
-
validação: |
|
|
126
|
-
Assert data persisted correctly; constraints respected; no orphaned data
|
|
127
|
-
error_message: "Acceptance criterion not met: Data persisted correctly; constraints respected; no orphaned data"
|
|
128
|
-
```
|
|
129
|
-
|
|
130
|
-
---
|
|
131
|
-
|
|
132
|
-
## Tools
|
|
133
|
-
|
|
134
|
-
**External/shared resources used by this task:**
|
|
135
|
-
|
|
136
|
-
- **Tool:** neo4j-driver
|
|
137
|
-
- **Purpose:** Neo4j database connection and query execution
|
|
138
|
-
- **Source:** npm: neo4j-driver
|
|
139
|
-
|
|
140
|
-
- **Tool:** query-validator
|
|
141
|
-
- **Purpose:** Cypher query syntax validation
|
|
142
|
-
- **Source:** .aios-core/utils/db-query-validator.js
|
|
143
|
-
|
|
144
|
-
---
|
|
145
|
-
|
|
146
|
-
## Scripts
|
|
147
|
-
|
|
148
|
-
**Agent-specific code for this task:**
|
|
149
|
-
|
|
150
|
-
- **Script:** db-query.js
|
|
151
|
-
- **Purpose:** Execute Neo4j queries with error handling
|
|
152
|
-
- **Language:** JavaScript
|
|
153
|
-
- **Location:** .aios-core/scripts/db-query.js
|
|
154
|
-
|
|
155
|
-
---
|
|
156
|
-
|
|
157
|
-
## Error Handling
|
|
158
|
-
|
|
159
|
-
**Strategy:** fallback
|
|
160
|
-
|
|
161
|
-
**Common Errors:**
|
|
162
|
-
|
|
163
|
-
1. **Error:** Connection Failed
|
|
164
|
-
- **Cause:** Unable to connect to Neo4j database
|
|
165
|
-
- **Resolution:** Check connection string, credentials, network
|
|
166
|
-
- **Recovery:** Retry with exponential backoff (max 3 attempts)
|
|
167
|
-
|
|
168
|
-
2. **Error:** Query Syntax Error
|
|
169
|
-
- **Cause:** Invalid Cypher query syntax
|
|
170
|
-
- **Resolution:** Validate query syntax before execution
|
|
171
|
-
- **Recovery:** Return detailed syntax error, suggest fix
|
|
172
|
-
|
|
173
|
-
3. **Error:** Transaction Rollback
|
|
174
|
-
- **Cause:** Query violates constraints or timeout
|
|
175
|
-
- **Resolution:** Review query logic and constraints
|
|
176
|
-
- **Recovery:** Automatic rollback, preserve data integrity
|
|
177
|
-
|
|
178
|
-
---
|
|
179
|
-
|
|
180
|
-
## Performance
|
|
181
|
-
|
|
182
|
-
**Expected Metrics:**
|
|
183
|
-
|
|
184
|
-
```yaml
|
|
185
|
-
duration_expected: 5-20 min (estimated)
|
|
186
|
-
cost_estimated: $0.003-0.015
|
|
187
|
-
token_usage: ~2,000-8,000 tokens
|
|
188
|
-
```
|
|
189
|
-
|
|
190
|
-
**Optimization Notes:**
|
|
191
|
-
- Iterative analysis with depth limits; cache intermediate results; batch similar operations
|
|
192
|
-
|
|
193
|
-
---
|
|
194
|
-
|
|
195
|
-
## Metadata
|
|
196
|
-
|
|
197
|
-
```yaml
|
|
198
|
-
story: N/A
|
|
199
|
-
version: 1.0.0
|
|
200
|
-
dependencies:
|
|
201
|
-
- N/A
|
|
202
|
-
tags:
|
|
203
|
-
- database
|
|
204
|
-
- infrastructure
|
|
205
|
-
updated_at: 2025-11-17
|
|
206
|
-
```
|
|
207
|
-
|
|
208
|
-
---
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
## Overview
|
|
212
|
-
|
|
213
|
-
This task performs a thorough audit of your database schema, checking for:
|
|
214
|
-
- Design best practices
|
|
215
|
-
- Performance issues
|
|
216
|
-
- Security gaps
|
|
217
|
-
- Data integrity risks
|
|
218
|
-
- Missing indexes
|
|
219
|
-
- Naming conventions
|
|
220
|
-
|
|
221
|
-
---
|
|
222
|
-
|
|
223
|
-
## Process
|
|
224
|
-
|
|
225
|
-
### 1. Collect Schema Metadata
|
|
226
|
-
|
|
227
|
-
Gather comprehensive schema information:
|
|
228
|
-
|
|
229
|
-
```bash
|
|
230
|
-
echo "Collecting schema metadata..."
|
|
231
|
-
|
|
232
|
-
psql "$SUPABASE_DB_URL" << 'EOF'
|
|
233
|
-
-- Save to temp tables for analysis
|
|
234
|
-
|
|
235
|
-
-- Tables
|
|
236
|
-
CREATE TEMP TABLE audit_tables AS
|
|
237
|
-
SELECT
|
|
238
|
-
schemaname,
|
|
239
|
-
tablename,
|
|
240
|
-
pg_total_relation_size(schemaname||'.'||tablename) AS total_size
|
|
241
|
-
FROM pg_tables
|
|
242
|
-
WHERE schemaname = 'public';
|
|
243
|
-
|
|
244
|
-
-- Columns
|
|
245
|
-
CREATE TEMP TABLE audit_columns AS
|
|
246
|
-
SELECT
|
|
247
|
-
table_schema,
|
|
248
|
-
table_name,
|
|
249
|
-
column_name,
|
|
250
|
-
data_type,
|
|
251
|
-
is_nullable,
|
|
252
|
-
column_default
|
|
253
|
-
FROM information_schema.columns
|
|
254
|
-
WHERE table_schema = 'public';
|
|
255
|
-
|
|
256
|
-
-- Indexes
|
|
257
|
-
CREATE TEMP TABLE audit_indexes AS
|
|
258
|
-
SELECT
|
|
259
|
-
schemaname,
|
|
260
|
-
tablename,
|
|
261
|
-
indexname,
|
|
262
|
-
indexdef,
|
|
263
|
-
pg_relation_size(indexrelid) AS index_size
|
|
264
|
-
FROM pg_indexes
|
|
265
|
-
WHERE schemaname = 'public';
|
|
266
|
-
|
|
267
|
-
-- Foreign Keys
|
|
268
|
-
CREATE TEMP TABLE audit_fks AS
|
|
269
|
-
SELECT
|
|
270
|
-
tc.table_name,
|
|
271
|
-
kcu.column_name,
|
|
272
|
-
ccu.table_name AS foreign_table,
|
|
273
|
-
ccu.column_name AS foreign_column
|
|
274
|
-
FROM information_schema.table_constraints tc
|
|
275
|
-
JOIN information_schema.key_column_usage kcu
|
|
276
|
-
ON tc.constraint_name = kcu.constraint_name
|
|
277
|
-
JOIN information_schema.constraint_column_usage ccu
|
|
278
|
-
ON ccu.constraint_name = tc.constraint_name
|
|
279
|
-
WHERE tc.constraint_type = 'FOREIGN KEY'
|
|
280
|
-
AND tc.table_schema = 'public';
|
|
281
|
-
|
|
282
|
-
SELECT '✓ Metadata collected' AS status;
|
|
283
|
-
EOF
|
|
284
|
-
```
|
|
285
|
-
|
|
286
|
-
### 2. Check Design Best Practices
|
|
287
|
-
|
|
288
|
-
Run design checks:
|
|
289
|
-
|
|
290
|
-
```bash
|
|
291
|
-
psql "$SUPABASE_DB_URL" << 'EOF'
|
|
292
|
-
\echo '=========================================='
|
|
293
|
-
\echo '🔍 DESIGN BEST PRACTICES AUDIT'
|
|
294
|
-
\echo '=========================================='
|
|
295
|
-
\echo ''
|
|
296
|
-
|
|
297
|
-
-- Check 1: Tables without primary keys
|
|
298
|
-
\echo '1. Tables without PRIMARY KEY:'
|
|
299
|
-
SELECT table_name
|
|
300
|
-
FROM information_schema.tables t
|
|
301
|
-
WHERE table_schema = 'public'
|
|
302
|
-
AND table_type = 'BASE TABLE'
|
|
303
|
-
AND NOT EXISTS (
|
|
304
|
-
SELECT 1
|
|
305
|
-
FROM information_schema.table_constraints
|
|
306
|
-
WHERE table_schema = t.table_schema
|
|
307
|
-
AND table_name = t.table_name
|
|
308
|
-
AND constraint_type = 'PRIMARY KEY'
|
|
309
|
-
);
|
|
310
|
-
\echo ''
|
|
311
|
-
|
|
312
|
-
-- Check 2: Tables without created_at
|
|
313
|
-
\echo '2. Tables without created_at timestamp:'
|
|
314
|
-
SELECT table_name
|
|
315
|
-
FROM information_schema.tables t
|
|
316
|
-
WHERE table_schema = 'public'
|
|
317
|
-
AND table_type = 'BASE TABLE'
|
|
318
|
-
AND NOT EXISTS (
|
|
319
|
-
SELECT 1
|
|
320
|
-
FROM information_schema.columns
|
|
321
|
-
WHERE table_schema = t.table_schema
|
|
322
|
-
AND table_name = t.table_name
|
|
323
|
-
AND column_name IN ('created_at', 'createdat')
|
|
324
|
-
);
|
|
325
|
-
\echo ''
|
|
326
|
-
|
|
327
|
-
-- Check 3: Tables without updated_at
|
|
328
|
-
\echo '3. Tables without updated_at timestamp:'
|
|
329
|
-
SELECT table_name
|
|
330
|
-
FROM information_schema.tables t
|
|
331
|
-
WHERE table_schema = 'public'
|
|
332
|
-
AND table_type = 'BASE TABLE'
|
|
333
|
-
AND NOT EXISTS (
|
|
334
|
-
SELECT 1
|
|
335
|
-
FROM information_schema.columns
|
|
336
|
-
WHERE table_schema = t.table_schema
|
|
337
|
-
AND table_name = t.table_name
|
|
338
|
-
AND column_name IN ('updated_at', 'updatedat')
|
|
339
|
-
);
|
|
340
|
-
\echo ''
|
|
341
|
-
|
|
342
|
-
-- Check 4: Foreign keys without indexes
|
|
343
|
-
\echo '4. Foreign keys without indexes (performance issue):'
|
|
344
|
-
SELECT
|
|
345
|
-
fk.table_name,
|
|
346
|
-
fk.column_name,
|
|
347
|
-
fk.foreign_table
|
|
348
|
-
FROM audit_fks fk
|
|
349
|
-
WHERE NOT EXISTS (
|
|
350
|
-
SELECT 1
|
|
351
|
-
FROM pg_indexes idx
|
|
352
|
-
WHERE idx.tablename = fk.table_name
|
|
353
|
-
AND idx.indexdef LIKE '%' || fk.column_name || '%'
|
|
354
|
-
);
|
|
355
|
-
\echo ''
|
|
356
|
-
|
|
357
|
-
-- Check 5: Nullable columns that should be NOT NULL
|
|
358
|
-
\echo '5. Suspicious nullable columns (id, *_id, email, created_at):'
|
|
359
|
-
SELECT
|
|
360
|
-
table_name,
|
|
361
|
-
column_name,
|
|
362
|
-
data_type
|
|
363
|
-
FROM information_schema.columns
|
|
364
|
-
WHERE table_schema = 'public'
|
|
365
|
-
AND is_nullable = 'YES'
|
|
366
|
-
AND (
|
|
367
|
-
column_name = 'id'
|
|
368
|
-
OR column_name = 'email'
|
|
369
|
-
OR column_name = 'created_at'
|
|
370
|
-
OR column_name LIKE '%_id'
|
|
371
|
-
);
|
|
372
|
-
\echo ''
|
|
373
|
-
|
|
374
|
-
EOF
|
|
375
|
-
```
|
|
376
|
-
|
|
377
|
-
### 3. Check Performance Issues
|
|
378
|
-
|
|
379
|
-
Identify performance problems:
|
|
380
|
-
|
|
381
|
-
```bash
|
|
382
|
-
psql "$SUPABASE_DB_URL" << 'EOF'
|
|
383
|
-
\echo '=========================================='
|
|
384
|
-
\echo '⚡ PERFORMANCE ISSUES AUDIT'
|
|
385
|
-
\echo '=========================================='
|
|
386
|
-
\echo ''
|
|
387
|
-
|
|
388
|
-
-- Check 1: Missing indexes on foreign keys
|
|
389
|
-
\echo '1. Foreign keys without indexes:'
|
|
390
|
-
[Same as Check 4 above]
|
|
391
|
-
\echo ''
|
|
392
|
-
|
|
393
|
-
-- Check 2: Tables without indexes (except very small tables)
|
|
394
|
-
\echo '2. Tables without any indexes (excluding tiny tables):'
|
|
395
|
-
SELECT
|
|
396
|
-
t.tablename,
|
|
397
|
-
pg_size_pretty(pg_total_relation_size('public.' || t.tablename)) AS size
|
|
398
|
-
FROM pg_tables t
|
|
399
|
-
WHERE t.schemaname = 'public'
|
|
400
|
-
AND NOT EXISTS (
|
|
401
|
-
SELECT 1
|
|
402
|
-
FROM pg_indexes idx
|
|
403
|
-
WHERE idx.tablename = t.tablename
|
|
404
|
-
AND idx.schemaname = t.schemaname
|
|
405
|
-
)
|
|
406
|
-
AND pg_total_relation_size('public.' || t.tablename) > 8192; -- > 8KB
|
|
407
|
-
\echo ''
|
|
408
|
-
|
|
409
|
-
-- Check 3: Unused indexes
|
|
410
|
-
\echo '3. Unused indexes (0 scans, size > 1MB):'
|
|
411
|
-
SELECT
|
|
412
|
-
schemaname,
|
|
413
|
-
tablename,
|
|
414
|
-
indexname,
|
|
415
|
-
pg_size_pretty(pg_relation_size(indexrelid)) AS size,
|
|
416
|
-
idx_scan
|
|
417
|
-
FROM pg_stat_user_indexes
|
|
418
|
-
WHERE schemaname = 'public'
|
|
419
|
-
AND idx_scan = 0
|
|
420
|
-
AND indexname NOT LIKE '%_pkey' -- Exclude primary keys
|
|
421
|
-
AND pg_relation_size(indexrelid) > 1024*1024; -- > 1MB
|
|
422
|
-
\echo ''
|
|
423
|
-
|
|
424
|
-
-- Check 4: Duplicate indexes
|
|
425
|
-
\echo '4. Potential duplicate indexes:'
|
|
426
|
-
SELECT
|
|
427
|
-
a.tablename,
|
|
428
|
-
a.indexname AS index1,
|
|
429
|
-
b.indexname AS index2
|
|
430
|
-
FROM pg_indexes a
|
|
431
|
-
JOIN pg_indexes b
|
|
432
|
-
ON a.tablename = b.tablename
|
|
433
|
-
AND a.indexname < b.indexname
|
|
434
|
-
AND a.indexdef = b.indexdef
|
|
435
|
-
WHERE a.schemaname = 'public';
|
|
436
|
-
\echo ''
|
|
437
|
-
|
|
438
|
-
-- Check 5: Large tables without partitioning
|
|
439
|
-
\echo '5. Large tables (>1GB) that might benefit from partitioning:'
|
|
440
|
-
SELECT
|
|
441
|
-
tablename,
|
|
442
|
-
pg_size_pretty(pg_total_relation_size('public.' || tablename)) AS size
|
|
443
|
-
FROM pg_tables
|
|
444
|
-
WHERE schemaname = 'public'
|
|
445
|
-
AND pg_total_relation_size('public.' || tablename) > 1024*1024*1024
|
|
446
|
-
ORDER BY pg_total_relation_size('public.' || tablename) DESC;
|
|
447
|
-
\echo ''
|
|
448
|
-
|
|
449
|
-
EOF
|
|
450
|
-
```
|
|
451
|
-
|
|
452
|
-
### 4. Check Security
|
|
453
|
-
|
|
454
|
-
Audit security configuration:
|
|
455
|
-
|
|
456
|
-
```bash
|
|
457
|
-
psql "$SUPABASE_DB_URL" << 'EOF'
|
|
458
|
-
\echo '=========================================='
|
|
459
|
-
\echo '🔒 SECURITY AUDIT'
|
|
460
|
-
\echo '=========================================='
|
|
461
|
-
\echo ''
|
|
462
|
-
|
|
463
|
-
-- Check 1: Tables without RLS
|
|
464
|
-
\echo '1. Tables without Row Level Security enabled:'
|
|
465
|
-
SELECT
|
|
466
|
-
schemaname,
|
|
467
|
-
tablename,
|
|
468
|
-
rowsecurity
|
|
469
|
-
FROM pg_tables
|
|
470
|
-
WHERE schemaname = 'public'
|
|
471
|
-
AND rowsecurity = false;
|
|
472
|
-
\echo ''
|
|
473
|
-
|
|
474
|
-
-- Check 2: Tables with RLS but no policies
|
|
475
|
-
\echo '2. Tables with RLS enabled but no policies:'
|
|
476
|
-
SELECT
|
|
477
|
-
t.schemaname,
|
|
478
|
-
t.tablename
|
|
479
|
-
FROM pg_tables t
|
|
480
|
-
WHERE t.schemaname = 'public'
|
|
481
|
-
AND t.rowsecurity = true
|
|
482
|
-
AND NOT EXISTS (
|
|
483
|
-
SELECT 1
|
|
484
|
-
FROM pg_policies p
|
|
485
|
-
WHERE p.schemaname = t.schemaname
|
|
486
|
-
AND p.tablename = t.tablename
|
|
487
|
-
);
|
|
488
|
-
\echo ''
|
|
489
|
-
|
|
490
|
-
-- Check 3: RLS policy coverage
|
|
491
|
-
\echo '3. RLS policy coverage by table:'
|
|
492
|
-
SELECT
|
|
493
|
-
t.tablename,
|
|
494
|
-
t.rowsecurity AS rls_enabled,
|
|
495
|
-
COUNT(p.policyname) AS policy_count,
|
|
496
|
-
STRING_AGG(DISTINCT p.cmd, ', ') AS operations
|
|
497
|
-
FROM pg_tables t
|
|
498
|
-
LEFT JOIN pg_policies p
|
|
499
|
-
ON t.tablename = p.tablename
|
|
500
|
-
AND t.schemaname = p.schemaname
|
|
501
|
-
WHERE t.schemaname = 'public'
|
|
502
|
-
GROUP BY t.tablename, t.rowsecurity
|
|
503
|
-
ORDER BY t.tablename;
|
|
504
|
-
\echo ''
|
|
505
|
-
|
|
506
|
-
-- Check 4: Columns that might contain PII without encryption
|
|
507
|
-
\echo '4. Potential PII columns (consider encryption/hashing):'
|
|
508
|
-
SELECT
|
|
509
|
-
table_name,
|
|
510
|
-
column_name,
|
|
511
|
-
data_type
|
|
512
|
-
FROM information_schema.columns
|
|
513
|
-
WHERE table_schema = 'public'
|
|
514
|
-
AND (
|
|
515
|
-
column_name ILIKE '%ssn%'
|
|
516
|
-
OR column_name ILIKE '%tax_id%'
|
|
517
|
-
OR column_name ILIKE '%passport%'
|
|
518
|
-
OR column_name ILIKE '%credit_card%'
|
|
519
|
-
OR column_name ILIKE '%password%'
|
|
520
|
-
);
|
|
521
|
-
\echo ''
|
|
522
|
-
|
|
523
|
-
EOF
|
|
524
|
-
```
|
|
525
|
-
|
|
526
|
-
### 5. Check Data Integrity
|
|
527
|
-
|
|
528
|
-
Verify constraints and relationships:
|
|
529
|
-
|
|
530
|
-
```bash
|
|
531
|
-
psql "$SUPABASE_DB_URL" << 'EOF'
|
|
532
|
-
\echo '=========================================='
|
|
533
|
-
\echo '✅ DATA INTEGRITY AUDIT'
|
|
534
|
-
\echo '=========================================='
|
|
535
|
-
\echo ''
|
|
536
|
-
|
|
537
|
-
-- Check 1: Foreign key relationships count
|
|
538
|
-
\echo '1. Foreign key relationship summary:'
|
|
539
|
-
SELECT
|
|
540
|
-
COUNT(*) AS total_fk_constraints,
|
|
541
|
-
COUNT(DISTINCT table_name) AS tables_with_fks
|
|
542
|
-
FROM audit_fks;
|
|
543
|
-
\echo ''
|
|
544
|
-
|
|
545
|
-
-- Check 2: Check constraints count
|
|
546
|
-
\echo '2. CHECK constraints summary:'
|
|
547
|
-
SELECT
|
|
548
|
-
COUNT(*) AS total_check_constraints
|
|
549
|
-
FROM information_schema.check_constraints
|
|
550
|
-
WHERE constraint_schema = 'public';
|
|
551
|
-
\echo ''
|
|
552
|
-
|
|
553
|
-
-- Check 3: Unique constraints count
|
|
554
|
-
\echo '3. UNIQUE constraints summary:'
|
|
555
|
-
SELECT
|
|
556
|
-
COUNT(*) AS total_unique_constraints
|
|
557
|
-
FROM information_schema.table_constraints
|
|
558
|
-
WHERE constraint_schema = 'public'
|
|
559
|
-
AND constraint_type = 'UNIQUE';
|
|
560
|
-
\echo ''
|
|
561
|
-
|
|
562
|
-
-- Check 4: Tables without any constraints (red flag)
|
|
563
|
-
\echo '4. Tables without constraints (potential issues):'
|
|
564
|
-
SELECT table_name
|
|
565
|
-
FROM information_schema.tables t
|
|
566
|
-
WHERE table_schema = 'public'
|
|
567
|
-
AND table_type = 'BASE TABLE'
|
|
568
|
-
AND NOT EXISTS (
|
|
569
|
-
SELECT 1
|
|
570
|
-
FROM information_schema.table_constraints
|
|
571
|
-
WHERE table_schema = t.table_schema
|
|
572
|
-
AND table_name = t.table_name
|
|
573
|
-
);
|
|
574
|
-
\echo ''
|
|
575
|
-
|
|
576
|
-
-- Check 5: Orphaned records (FK points to non-existent record)
|
|
577
|
-
\echo '5. Checking for orphaned records...'
|
|
578
|
-
\echo ' (This check requires custom queries per table)'
|
|
579
|
-
\echo ' Example:'
|
|
580
|
-
\echo ' SELECT COUNT(*) FROM posts p'
|
|
581
|
-
\echo ' WHERE NOT EXISTS (SELECT 1 FROM users u WHERE u.id = p.user_id);'
|
|
582
|
-
\echo ''
|
|
583
|
-
|
|
584
|
-
EOF
|
|
585
|
-
```
|
|
586
|
-
|
|
587
|
-
### 6. Generate Audit Report
|
|
588
|
-
|
|
589
|
-
Create comprehensive report:
|
|
590
|
-
|
|
591
|
-
```bash
|
|
592
|
-
REPORT_FILE="supabase/docs/schema-audit-$(date +%Y%m%d%H%M%S).md"
|
|
593
|
-
mkdir -p supabase/docs
|
|
594
|
-
|
|
595
|
-
cat > "$REPORT_FILE" << 'MDEOF'
|
|
596
|
-
# Database Schema Audit Report
|
|
597
|
-
|
|
598
|
-
**Date**: $(date -u +"%Y-%m-%d %H:%M:%S UTC")
|
|
599
|
-
**Database**: [redacted]
|
|
600
|
-
**Auditor**: DB Sage
|
|
601
|
-
|
|
602
|
-
---
|
|
603
|
-
|
|
604
|
-
## Executive Summary
|
|
605
|
-
|
|
606
|
-
- Tables audited: {count}
|
|
607
|
-
- Total database size: {size}
|
|
608
|
-
- Critical issues: {critical_count}
|
|
609
|
-
- Warnings: {warning_count}
|
|
610
|
-
- Recommendations: {rec_count}
|
|
611
|
-
|
|
612
|
-
**Overall Score**: {score}/100
|
|
613
|
-
|
|
614
|
-
---
|
|
615
|
-
|
|
616
|
-
## Critical Issues 🔴
|
|
617
|
-
|
|
618
|
-
### 1. Tables without Primary Keys
|
|
619
|
-
{list_of_tables}
|
|
620
|
-
|
|
621
|
-
**Impact**: Cannot uniquely identify rows, replication issues
|
|
622
|
-
**Fix**: Add UUID or SERIAL primary key
|
|
623
|
-
|
|
624
|
-
---
|
|
625
|
-
|
|
626
|
-
### 2. Foreign Keys without Indexes
|
|
627
|
-
{list_of_fks}
|
|
628
|
-
|
|
629
|
-
**Impact**: Slow JOIN queries, slow ON DELETE CASCADE
|
|
630
|
-
**Fix**: Create indexes on FK columns
|
|
631
|
-
|
|
632
|
-
---
|
|
633
|
-
|
|
634
|
-
## Warnings ⚠️
|
|
635
|
-
|
|
636
|
-
### 3. Missing Timestamps
|
|
637
|
-
{list_of_tables_without_timestamps}
|
|
638
|
-
|
|
639
|
-
**Impact**: No audit trail, cannot track record creation/modification
|
|
640
|
-
**Fix**: Add created_at, updated_at columns
|
|
641
|
-
|
|
642
|
-
---
|
|
643
|
-
|
|
644
|
-
### 4. Tables without RLS
|
|
645
|
-
{list_of_tables_without_rls}
|
|
646
|
-
|
|
647
|
-
**Impact**: Security risk in multi-tenant applications
|
|
648
|
-
**Fix**: Enable RLS and create policies
|
|
649
|
-
|
|
650
|
-
---
|
|
651
|
-
|
|
652
|
-
## Recommendations 💡
|
|
653
|
-
|
|
654
|
-
### 5. Performance Optimizations
|
|
655
|
-
- Add indexes on frequently queried columns
|
|
656
|
-
- Consider partitioning for tables > 1GB
|
|
657
|
-
- Remove unused indexes (saves space, improves write performance)
|
|
658
|
-
|
|
659
|
-
### 6. Security Hardening
|
|
660
|
-
- Encrypt PII columns
|
|
661
|
-
- Implement RLS on all user-facing tables
|
|
662
|
-
- Add check constraints for data validation
|
|
663
|
-
|
|
664
|
-
### 7. Naming Conventions
|
|
665
|
-
- Use snake_case consistently
|
|
666
|
-
- Prefix foreign keys with table name (e.g., user_id not uid)
|
|
667
|
-
- Use plural for table names (e.g., users not user)
|
|
668
|
-
|
|
669
|
-
---
|
|
670
|
-
|
|
671
|
-
## Detailed Findings
|
|
672
|
-
|
|
673
|
-
[Include full output from all checks above]
|
|
674
|
-
|
|
675
|
-
---
|
|
676
|
-
|
|
677
|
-
## Action Items
|
|
678
|
-
|
|
679
|
-
Priority | Action | Estimated Effort
|
|
680
|
-
---------|--------|------------------
|
|
681
|
-
P0 | Add primary keys to {tables} | 1 hour
|
|
682
|
-
P0 | Index foreign keys | 2 hours
|
|
683
|
-
P1 | Enable RLS on {tables} | 4 hours
|
|
684
|
-
P1 | Add timestamps | 2 hours
|
|
685
|
-
P2 | Optimize indexes | 4 hours
|
|
686
|
-
|
|
687
|
-
---
|
|
688
|
-
|
|
689
|
-
## SQL Fixes
|
|
690
|
-
|
|
691
|
-
```sql
|
|
692
|
-
-- Fix 1: Add primary keys
|
|
693
|
-
ALTER TABLE {table} ADD COLUMN id UUID PRIMARY KEY DEFAULT gen_random_uuid();
|
|
694
|
-
|
|
695
|
-
-- Fix 2: Index foreign keys
|
|
696
|
-
CREATE INDEX CONCURRENTLY idx_{table}_{fk} ON {table}({fk_column});
|
|
697
|
-
|
|
698
|
-
-- Fix 3: Enable RLS
|
|
699
|
-
ALTER TABLE {table} ENABLE ROW LEVEL SECURITY;
|
|
700
|
-
CREATE POLICY "{table}_policy" ON {table} FOR ALL TO authenticated
|
|
701
|
-
USING (auth.uid() = user_id);
|
|
702
|
-
|
|
703
|
-
-- Fix 4: Add timestamps
|
|
704
|
-
ALTER TABLE {table} ADD COLUMN created_at TIMESTAMPTZ DEFAULT NOW();
|
|
705
|
-
ALTER TABLE {table} ADD COLUMN updated_at TIMESTAMPTZ;
|
|
706
|
-
```
|
|
707
|
-
|
|
708
|
-
MDEOF
|
|
709
|
-
|
|
710
|
-
echo "✓ Audit report: $REPORT_FILE"
|
|
711
|
-
```
|
|
712
|
-
|
|
713
|
-
---
|
|
714
|
-
|
|
715
|
-
## Output
|
|
716
|
-
|
|
717
|
-
Display audit summary:
|
|
718
|
-
|
|
719
|
-
```
|
|
720
|
-
✅ SCHEMA AUDIT COMPLETE
|
|
721
|
-
|
|
722
|
-
Database: [redacted]
|
|
723
|
-
Tables: {count}
|
|
724
|
-
Size: {size}
|
|
725
|
-
|
|
726
|
-
Critical Issues: {count} 🔴
|
|
727
|
-
Warnings: {count} ⚠️
|
|
728
|
-
Recommendations: {count} 💡
|
|
729
|
-
|
|
730
|
-
Overall Score: {score}/100
|
|
731
|
-
|
|
732
|
-
Report: supabase/docs/schema-audit-{timestamp}.md
|
|
733
|
-
|
|
734
|
-
Top Issues:
|
|
735
|
-
1. {issue_1}
|
|
736
|
-
2. {issue_2}
|
|
737
|
-
3. {issue_3}
|
|
738
|
-
|
|
739
|
-
Next Steps:
|
|
740
|
-
1. Review full report: cat {report_file}
|
|
741
|
-
2. Prioritize fixes
|
|
742
|
-
3. Create migrations for P0 issues
|
|
743
|
-
4. Re-run audit after fixes
|
|
744
|
-
```
|
|
745
|
-
|
|
746
|
-
---
|
|
747
|
-
|
|
748
|
-
## Scoring Rubric
|
|
749
|
-
|
|
750
|
-
- **100**: Perfect schema (rare!)
|
|
751
|
-
- **90-99**: Excellent, minor improvements
|
|
752
|
-
- **80-89**: Good, some best practices missed
|
|
753
|
-
- **70-79**: Fair, several issues to address
|
|
754
|
-
- **60-69**: Needs work, security or performance risks
|
|
755
|
-
- **<60**: Critical issues, not production-ready
|
|
756
|
-
|
|
757
|
-
---
|
|
758
|
-
|
|
759
|
-
## Advanced Auditing Tools
|
|
760
|
-
|
|
761
|
-
### 1. Audit Triggers (Change Tracking)
|
|
762
|
-
|
|
763
|
-
**Purpose:** Track all changes (INSERT, UPDATE, DELETE) with who, when, what changed
|
|
764
|
-
|
|
765
|
-
**Implementation:**
|
|
766
|
-
```sql
|
|
767
|
-
-- Create audit log schema
|
|
768
|
-
CREATE SCHEMA IF NOT EXISTS audit;
|
|
769
|
-
|
|
770
|
-
-- Audit log table
|
|
771
|
-
CREATE TABLE audit.logged_actions (
|
|
772
|
-
event_id BIGSERIAL PRIMARY KEY,
|
|
773
|
-
schema_name TEXT NOT NULL,
|
|
774
|
-
table_name TEXT NOT NULL,
|
|
775
|
-
relid OID NOT NULL,
|
|
776
|
-
session_user_name TEXT,
|
|
777
|
-
action_tstamp_tx TIMESTAMPTZ NOT NULL DEFAULT transaction_timestamp(),
|
|
778
|
-
action_tstamp_stm TIMESTAMPTZ NOT NULL DEFAULT statement_timestamp(),
|
|
779
|
-
action_tstamp_clk TIMESTAMPTZ NOT NULL DEFAULT clock_timestamp(),
|
|
780
|
-
transaction_id BIGINT,
|
|
781
|
-
application_name TEXT,
|
|
782
|
-
client_addr INET,
|
|
783
|
-
client_port INTEGER,
|
|
784
|
-
client_query TEXT,
|
|
785
|
-
action TEXT NOT NULL CHECK (action IN ('I','D','U', 'T')),
|
|
786
|
-
row_data JSONB,
|
|
787
|
-
changed_fields JSONB,
|
|
788
|
-
statement_only BOOLEAN NOT NULL DEFAULT false
|
|
789
|
-
);
|
|
790
|
-
|
|
791
|
-
CREATE INDEX idx_audit_relid ON audit.logged_actions(relid);
|
|
792
|
-
CREATE INDEX idx_audit_action_tstamp ON audit.logged_actions(action_tstamp_tx);
|
|
793
|
-
CREATE INDEX idx_audit_table_name ON audit.logged_actions(table_name);
|
|
794
|
-
|
|
795
|
-
-- Generic audit trigger function
|
|
796
|
-
CREATE OR REPLACE FUNCTION audit.if_modified_func()
|
|
797
|
-
RETURNS TRIGGER AS $$
|
|
798
|
-
DECLARE
|
|
799
|
-
audit_row audit.logged_actions;
|
|
800
|
-
excluded_cols TEXT[] = ARRAY[]::TEXT[];
|
|
801
|
-
BEGIN
|
|
802
|
-
IF TG_WHEN <> 'AFTER' THEN
|
|
803
|
-
RAISE EXCEPTION 'audit.if_modified_func() may only run as an AFTER trigger';
|
|
804
|
-
END IF;
|
|
805
|
-
|
|
806
|
-
audit_row = ROW(
|
|
807
|
-
nextval('audit.logged_actions_event_id_seq'), -- event_id
|
|
808
|
-
TG_TABLE_SCHEMA::TEXT, -- schema_name
|
|
809
|
-
TG_TABLE_NAME::TEXT, -- table_name
|
|
810
|
-
TG_RELID, -- relid
|
|
811
|
-
session_user::TEXT, -- session_user_name
|
|
812
|
-
current_timestamp, -- action_tstamp_tx
|
|
813
|
-
statement_timestamp(), -- action_tstamp_stm
|
|
814
|
-
clock_timestamp(), -- action_tstamp_clk
|
|
815
|
-
txid_current(), -- transaction_id
|
|
816
|
-
current_setting('application_name'), -- application_name
|
|
817
|
-
inet_client_addr(), -- client_addr
|
|
818
|
-
inet_client_port(), -- client_port
|
|
819
|
-
current_query(), -- client_query
|
|
820
|
-
substring(TG_OP,1,1), -- action
|
|
821
|
-
NULL, -- row_data (set below)
|
|
822
|
-
NULL, -- changed_fields (set below)
|
|
823
|
-
false -- statement_only
|
|
824
|
-
);
|
|
825
|
-
|
|
826
|
-
IF TG_OP = 'UPDATE' AND TG_LEVEL = 'ROW' THEN
|
|
827
|
-
audit_row.row_data = to_jsonb(OLD);
|
|
828
|
-
audit_row.changed_fields = jsonb_build_object(
|
|
829
|
-
'old', to_jsonb(OLD),
|
|
830
|
-
'new', to_jsonb(NEW)
|
|
831
|
-
);
|
|
832
|
-
ELSIF TG_OP = 'DELETE' AND TG_LEVEL = 'ROW' THEN
|
|
833
|
-
audit_row.row_data = to_jsonb(OLD);
|
|
834
|
-
ELSIF TG_OP = 'INSERT' AND TG_LEVEL = 'ROW' THEN
|
|
835
|
-
audit_row.row_data = to_jsonb(NEW);
|
|
836
|
-
ELSE
|
|
837
|
-
RAISE EXCEPTION '[audit.if_modified_func] - Trigger func added as trigger for unhandled case: %, %',TG_OP, TG_LEVEL;
|
|
838
|
-
RETURN NULL;
|
|
839
|
-
END IF;
|
|
840
|
-
|
|
841
|
-
INSERT INTO audit.logged_actions VALUES (audit_row.*);
|
|
842
|
-
RETURN NULL;
|
|
843
|
-
END;
|
|
844
|
-
$$ LANGUAGE plpgsql SECURITY DEFINER;
|
|
845
|
-
|
|
846
|
-
-- Apply to tables (example)
|
|
847
|
-
CREATE TRIGGER audit_trigger_row
|
|
848
|
-
AFTER INSERT OR UPDATE OR DELETE ON users
|
|
849
|
-
FOR EACH ROW EXECUTE FUNCTION audit.if_modified_func();
|
|
850
|
-
```
|
|
851
|
-
|
|
852
|
-
**Benefits:**
|
|
853
|
-
- Complete audit trail of all changes
|
|
854
|
-
- Forensic analysis capabilities
|
|
855
|
-
- Compliance requirements (GDPR, SOX, HIPAA)
|
|
856
|
-
- Debugging production issues
|
|
857
|
-
|
|
858
|
-
### 2. pgAudit Extension (PostgreSQL Auditing)
|
|
859
|
-
|
|
860
|
-
**Purpose:** Comprehensive session and object audit logging
|
|
861
|
-
|
|
862
|
-
```sql
|
|
863
|
-
-- Install extension
|
|
864
|
-
CREATE EXTENSION IF NOT EXISTS pgaudit;
|
|
865
|
-
|
|
866
|
-
-- Configure (in postgresql.conf or ALTER SYSTEM)
|
|
867
|
-
ALTER SYSTEM SET pgaudit.log = 'write'; -- Log all writes
|
|
868
|
-
ALTER SYSTEM SET pgaudit.log_catalog = off; -- Don't log catalog queries
|
|
869
|
-
ALTER SYSTEM SET pgaudit.log_parameter = on; -- Include parameter values
|
|
870
|
-
ALTER SYSTEM SET pgaudit.log_relation = on; -- Include table names
|
|
871
|
-
ALTER SYSTEM SET pgaudit.log_statement_once = off; -- Log each statement
|
|
872
|
-
|
|
873
|
-
-- Reload configuration
|
|
874
|
-
SELECT pg_reload_conf();
|
|
875
|
-
|
|
876
|
-
-- Example: Audit specific table
|
|
877
|
-
CREATE ROLE auditor;
|
|
878
|
-
GRANT SELECT, INSERT, UPDATE, DELETE ON users TO auditor;
|
|
879
|
-
ALTER ROLE auditor SET pgaudit.log = 'write';
|
|
880
|
-
```
|
|
881
|
-
|
|
882
|
-
**What gets logged:**
|
|
883
|
-
- All DDL operations (CREATE, ALTER, DROP)
|
|
884
|
-
- All DML operations (INSERT, UPDATE, DELETE) based on config
|
|
885
|
-
- Parameter values (for forensics)
|
|
886
|
-
- Session information
|
|
887
|
-
|
|
888
|
-
### 3. pgTAP Extension (Database Testing)
|
|
889
|
-
|
|
890
|
-
**Purpose:** Unit tests for database schema, constraints, and data
|
|
891
|
-
|
|
892
|
-
**Installation:**
|
|
893
|
-
```sql
|
|
894
|
-
CREATE EXTENSION IF NOT EXISTS pgtap;
|
|
895
|
-
```
|
|
896
|
-
|
|
897
|
-
**Example test suite:**
|
|
898
|
-
```sql
|
|
899
|
-
-- File: tests/schema_tests.sql
|
|
900
|
-
BEGIN;
|
|
901
|
-
SELECT plan(10); -- Number of tests
|
|
902
|
-
|
|
903
|
-
-- Test 1: Check table exists
|
|
904
|
-
SELECT has_table('public', 'users', 'users table exists');
|
|
905
|
-
|
|
906
|
-
-- Test 2: Check primary key
|
|
907
|
-
SELECT has_pk('public', 'users', 'users has primary key');
|
|
908
|
-
|
|
909
|
-
-- Test 3: Check specific columns
|
|
910
|
-
SELECT has_column('public', 'users', 'id', 'users.id exists');
|
|
911
|
-
SELECT has_column('public', 'users', 'email', 'users.email exists');
|
|
912
|
-
SELECT has_column('public', 'users', 'created_at', 'users.created_at exists');
|
|
913
|
-
|
|
914
|
-
-- Test 4: Check column types
|
|
915
|
-
SELECT col_type_is('public', 'users', 'id', 'uuid', 'users.id is UUID');
|
|
916
|
-
SELECT col_type_is('public', 'users', 'email', 'text', 'users.email is TEXT');
|
|
917
|
-
|
|
918
|
-
-- Test 5: Check NOT NULL constraints
|
|
919
|
-
SELECT col_not_null('public', 'users', 'email', 'users.email is NOT NULL');
|
|
920
|
-
|
|
921
|
-
-- Test 6: Check foreign keys
|
|
922
|
-
SELECT has_fk('public', 'posts', 'posts has foreign key');
|
|
923
|
-
|
|
924
|
-
-- Test 7: Check indexes
|
|
925
|
-
SELECT has_index('public', 'users', 'idx_users_email', 'email index exists');
|
|
926
|
-
|
|
927
|
-
SELECT * FROM finish();
|
|
928
|
-
ROLLBACK;
|
|
929
|
-
```
|
|
930
|
-
|
|
931
|
-
**Run tests:**
|
|
932
|
-
```bash
|
|
933
|
-
psql "$DB_URL" -f tests/schema_tests.sql
|
|
934
|
-
```
|
|
935
|
-
|
|
936
|
-
**CI/CD Integration:**
|
|
937
|
-
```yaml
|
|
938
|
-
# .github/workflows/test.yml
|
|
939
|
-
- name: Run pgTAP tests
|
|
940
|
-
run: |
|
|
941
|
-
pg_prove --dbname "$DB_URL" tests/*.sql
|
|
942
|
-
```
|
|
943
|
-
|
|
944
|
-
### 4. Named Constraints (Best Practice)
|
|
945
|
-
|
|
946
|
-
**Why naming matters:**
|
|
947
|
-
- Error messages become informative
|
|
948
|
-
- Easier to troubleshoot constraint violations
|
|
949
|
-
- Explicit documentation of business rules
|
|
950
|
-
|
|
951
|
-
**Examples:**
|
|
952
|
-
```sql
|
|
953
|
-
-- ❌ BAD: Unnamed constraints
|
|
954
|
-
CREATE TABLE users (
|
|
955
|
-
id UUID PRIMARY KEY,
|
|
956
|
-
email TEXT UNIQUE,
|
|
957
|
-
age INTEGER CHECK (age >= 18)
|
|
958
|
-
);
|
|
959
|
-
-- Error: "violates check constraint users_age_check" (cryptic!)
|
|
960
|
-
|
|
961
|
-
-- ✅ GOOD: Named constraints with descriptive names
|
|
962
|
-
CREATE TABLE users (
|
|
963
|
-
id UUID CONSTRAINT users_pkey PRIMARY KEY,
|
|
964
|
-
email TEXT CONSTRAINT users_email_unique UNIQUE,
|
|
965
|
-
age INTEGER CONSTRAINT users_age_must_be_adult CHECK (age >= 18),
|
|
966
|
-
created_at TIMESTAMPTZ CONSTRAINT users_created_at_required NOT NULL,
|
|
967
|
-
status TEXT CONSTRAINT users_status_valid CHECK (status IN ('active', 'suspended', 'deleted'))
|
|
968
|
-
);
|
|
969
|
-
-- Error: "violates check constraint users_age_must_be_adult" (clear!)
|
|
970
|
-
```
|
|
971
|
-
|
|
972
|
-
**Naming conventions:**
|
|
973
|
-
```
|
|
974
|
-
{table}_{column}_{type}
|
|
975
|
-
{table}_{columns}_{type}
|
|
976
|
-
|
|
977
|
-
Types:
|
|
978
|
-
- pkey: Primary key
|
|
979
|
-
- fkey: Foreign key
|
|
980
|
-
- unique: Unique constraint
|
|
981
|
-
- check: Check constraint
|
|
982
|
-
- idx: Index
|
|
983
|
-
```
|
|
984
|
-
|
|
985
|
-
**Audit query for unnamed constraints:**
|
|
986
|
-
```sql
|
|
987
|
-
-- Find constraints without descriptive names
|
|
988
|
-
SELECT
|
|
989
|
-
conname AS constraint_name,
|
|
990
|
-
conrelid::regclass AS table_name,
|
|
991
|
-
contype AS constraint_type
|
|
992
|
-
FROM pg_constraint
|
|
993
|
-
WHERE connamespace = 'public'::regnamespace
|
|
994
|
-
AND (
|
|
995
|
-
-- Auto-generated names (PostgreSQL pattern)
|
|
996
|
-
conname ~ '_pkey$|_key$|_fkey$|_check$|_not_null$'
|
|
997
|
-
AND NOT conname ~ '^[a-z]+_[a-z_]+_(pkey|fkey|unique|check|required|valid)'
|
|
998
|
-
)
|
|
999
|
-
ORDER BY conrelid::regclass::TEXT, conname;
|
|
1000
|
-
```
|
|
1001
|
-
|
|
1002
|
-
---
|
|
1003
|
-
|
|
1004
|
-
## References
|
|
1005
|
-
|
|
1006
|
-
- [PostgreSQL Best Practices](https://wiki.postgresql.org/wiki/Don't_Do_This)
|
|
1007
|
-
- [Supabase RLS Best Practices](https://supabase.com/docs/guides/auth/row-level-security)
|
|
1008
|
-
- [Database Design Best Practices](https://www.postgresql.org/docs/current/ddl.html)
|
|
1009
|
-
- [PostgreSQL Audit Trigger](https://wiki.postgresql.org/wiki/Audit_trigger)
|
|
1010
|
-
- [pgAudit Extension](https://www.pgaudit.org/)
|
|
1011
|
-
- [pgTAP Documentation](https://pgtap.org/)
|
|
1
|
+
# Task: Schema Audit
|
|
2
|
+
|
|
3
|
+
**Purpose**: Comprehensive audit of database schema quality and best practices
|
|
4
|
+
|
|
5
|
+
**Elicit**: false
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Execution Modes
|
|
10
|
+
|
|
11
|
+
**Choose your execution mode:**
|
|
12
|
+
|
|
13
|
+
### 1. YOLO Mode - Fast, Autonomous (0-1 prompts)
|
|
14
|
+
- Autonomous decision making with logging
|
|
15
|
+
- Minimal user interaction
|
|
16
|
+
- **Best for:** Simple, deterministic tasks
|
|
17
|
+
|
|
18
|
+
### 2. Interactive Mode - Balanced, Educational (5-10 prompts) **[DEFAULT]**
|
|
19
|
+
- Explicit decision checkpoints
|
|
20
|
+
- Educational explanations
|
|
21
|
+
- **Best for:** Learning, complex decisions
|
|
22
|
+
|
|
23
|
+
### 3. Pre-Flight Planning - Comprehensive Upfront Planning
|
|
24
|
+
- Task analysis phase (identify all ambiguities)
|
|
25
|
+
- Zero ambiguity execution
|
|
26
|
+
- **Best for:** Ambiguous requirements, critical work
|
|
27
|
+
|
|
28
|
+
**Parameter:** `mode` (optional, default: `interactive`)
|
|
29
|
+
|
|
30
|
+
---
|
|
31
|
+
|
|
32
|
+
## Task Definition (AIOS Task Format V1.0)
|
|
33
|
+
|
|
34
|
+
```yaml
|
|
35
|
+
task: dbSchemaAudit()
|
|
36
|
+
responsável: Dara (Sage)
|
|
37
|
+
responsavel_type: Agente
|
|
38
|
+
atomic_layer: Strategy
|
|
39
|
+
|
|
40
|
+
**Entrada:**
|
|
41
|
+
- campo: query
|
|
42
|
+
tipo: string
|
|
43
|
+
origem: User Input
|
|
44
|
+
obrigatório: true
|
|
45
|
+
validação: Valid SQL query
|
|
46
|
+
|
|
47
|
+
- campo: params
|
|
48
|
+
tipo: object
|
|
49
|
+
origem: User Input
|
|
50
|
+
obrigatório: false
|
|
51
|
+
validação: Query parameters
|
|
52
|
+
|
|
53
|
+
- campo: connection
|
|
54
|
+
tipo: object
|
|
55
|
+
origem: config
|
|
56
|
+
obrigatório: true
|
|
57
|
+
validação: Valid PostgreSQL connection via Supabase
|
|
58
|
+
|
|
59
|
+
**Saída:**
|
|
60
|
+
- campo: query_result
|
|
61
|
+
tipo: array
|
|
62
|
+
destino: Memory
|
|
63
|
+
persistido: false
|
|
64
|
+
|
|
65
|
+
- campo: records_affected
|
|
66
|
+
tipo: number
|
|
67
|
+
destino: Return value
|
|
68
|
+
persistido: false
|
|
69
|
+
|
|
70
|
+
- campo: execution_time
|
|
71
|
+
tipo: number
|
|
72
|
+
destino: Memory
|
|
73
|
+
persistido: false
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
---
|
|
77
|
+
|
|
78
|
+
## Pre-Conditions
|
|
79
|
+
|
|
80
|
+
**Purpose:** Validate prerequisites BEFORE task execution (blocking)
|
|
81
|
+
|
|
82
|
+
**Checklist:**
|
|
83
|
+
|
|
84
|
+
```yaml
|
|
85
|
+
pre-conditions:
|
|
86
|
+
- [ ] Database connection established; query syntax valid
|
|
87
|
+
tipo: pre-condition
|
|
88
|
+
blocker: true
|
|
89
|
+
validação: |
|
|
90
|
+
Check database connection established; query syntax valid
|
|
91
|
+
error_message: "Pre-condition failed: Database connection established; query syntax valid"
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
---
|
|
95
|
+
|
|
96
|
+
## Post-Conditions
|
|
97
|
+
|
|
98
|
+
**Purpose:** Validate execution success AFTER task completes
|
|
99
|
+
|
|
100
|
+
**Checklist:**
|
|
101
|
+
|
|
102
|
+
```yaml
|
|
103
|
+
post-conditions:
|
|
104
|
+
- [ ] Query executed; results returned; transaction committed
|
|
105
|
+
tipo: post-condition
|
|
106
|
+
blocker: true
|
|
107
|
+
validação: |
|
|
108
|
+
Verify query executed; results returned; transaction committed
|
|
109
|
+
error_message: "Post-condition failed: Query executed; results returned; transaction committed"
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
---
|
|
113
|
+
|
|
114
|
+
## Acceptance Criteria
|
|
115
|
+
|
|
116
|
+
**Purpose:** Definitive pass/fail criteria for task completion
|
|
117
|
+
|
|
118
|
+
**Checklist:**
|
|
119
|
+
|
|
120
|
+
```yaml
|
|
121
|
+
acceptance-criteria:
|
|
122
|
+
- [ ] Data persisted correctly; constraints respected; no orphaned data
|
|
123
|
+
tipo: acceptance-criterion
|
|
124
|
+
blocker: true
|
|
125
|
+
validação: |
|
|
126
|
+
Assert data persisted correctly; constraints respected; no orphaned data
|
|
127
|
+
error_message: "Acceptance criterion not met: Data persisted correctly; constraints respected; no orphaned data"
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
---
|
|
131
|
+
|
|
132
|
+
## Tools
|
|
133
|
+
|
|
134
|
+
**External/shared resources used by this task:**
|
|
135
|
+
|
|
136
|
+
- **Tool:** neo4j-driver
|
|
137
|
+
- **Purpose:** Neo4j database connection and query execution
|
|
138
|
+
- **Source:** npm: neo4j-driver
|
|
139
|
+
|
|
140
|
+
- **Tool:** query-validator
|
|
141
|
+
- **Purpose:** Cypher query syntax validation
|
|
142
|
+
- **Source:** .aios-core/utils/db-query-validator.js
|
|
143
|
+
|
|
144
|
+
---
|
|
145
|
+
|
|
146
|
+
## Scripts
|
|
147
|
+
|
|
148
|
+
**Agent-specific code for this task:**
|
|
149
|
+
|
|
150
|
+
- **Script:** db-query.js
|
|
151
|
+
- **Purpose:** Execute Neo4j queries with error handling
|
|
152
|
+
- **Language:** JavaScript
|
|
153
|
+
- **Location:** .aios-core/scripts/db-query.js
|
|
154
|
+
|
|
155
|
+
---
|
|
156
|
+
|
|
157
|
+
## Error Handling
|
|
158
|
+
|
|
159
|
+
**Strategy:** fallback
|
|
160
|
+
|
|
161
|
+
**Common Errors:**
|
|
162
|
+
|
|
163
|
+
1. **Error:** Connection Failed
|
|
164
|
+
- **Cause:** Unable to connect to Neo4j database
|
|
165
|
+
- **Resolution:** Check connection string, credentials, network
|
|
166
|
+
- **Recovery:** Retry with exponential backoff (max 3 attempts)
|
|
167
|
+
|
|
168
|
+
2. **Error:** Query Syntax Error
|
|
169
|
+
- **Cause:** Invalid Cypher query syntax
|
|
170
|
+
- **Resolution:** Validate query syntax before execution
|
|
171
|
+
- **Recovery:** Return detailed syntax error, suggest fix
|
|
172
|
+
|
|
173
|
+
3. **Error:** Transaction Rollback
|
|
174
|
+
- **Cause:** Query violates constraints or timeout
|
|
175
|
+
- **Resolution:** Review query logic and constraints
|
|
176
|
+
- **Recovery:** Automatic rollback, preserve data integrity
|
|
177
|
+
|
|
178
|
+
---
|
|
179
|
+
|
|
180
|
+
## Performance
|
|
181
|
+
|
|
182
|
+
**Expected Metrics:**
|
|
183
|
+
|
|
184
|
+
```yaml
|
|
185
|
+
duration_expected: 5-20 min (estimated)
|
|
186
|
+
cost_estimated: $0.003-0.015
|
|
187
|
+
token_usage: ~2,000-8,000 tokens
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
**Optimization Notes:**
|
|
191
|
+
- Iterative analysis with depth limits; cache intermediate results; batch similar operations
|
|
192
|
+
|
|
193
|
+
---
|
|
194
|
+
|
|
195
|
+
## Metadata
|
|
196
|
+
|
|
197
|
+
```yaml
|
|
198
|
+
story: N/A
|
|
199
|
+
version: 1.0.0
|
|
200
|
+
dependencies:
|
|
201
|
+
- N/A
|
|
202
|
+
tags:
|
|
203
|
+
- database
|
|
204
|
+
- infrastructure
|
|
205
|
+
updated_at: 2025-11-17
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
---
|
|
209
|
+
|
|
210
|
+
|
|
211
|
+
## Overview
|
|
212
|
+
|
|
213
|
+
This task performs a thorough audit of your database schema, checking for:
|
|
214
|
+
- Design best practices
|
|
215
|
+
- Performance issues
|
|
216
|
+
- Security gaps
|
|
217
|
+
- Data integrity risks
|
|
218
|
+
- Missing indexes
|
|
219
|
+
- Naming conventions
|
|
220
|
+
|
|
221
|
+
---
|
|
222
|
+
|
|
223
|
+
## Process
|
|
224
|
+
|
|
225
|
+
### 1. Collect Schema Metadata
|
|
226
|
+
|
|
227
|
+
Gather comprehensive schema information:
|
|
228
|
+
|
|
229
|
+
```bash
|
|
230
|
+
echo "Collecting schema metadata..."
|
|
231
|
+
|
|
232
|
+
psql "$SUPABASE_DB_URL" << 'EOF'
|
|
233
|
+
-- Save to temp tables for analysis
|
|
234
|
+
|
|
235
|
+
-- Tables
|
|
236
|
+
CREATE TEMP TABLE audit_tables AS
|
|
237
|
+
SELECT
|
|
238
|
+
schemaname,
|
|
239
|
+
tablename,
|
|
240
|
+
pg_total_relation_size(schemaname||'.'||tablename) AS total_size
|
|
241
|
+
FROM pg_tables
|
|
242
|
+
WHERE schemaname = 'public';
|
|
243
|
+
|
|
244
|
+
-- Columns
|
|
245
|
+
CREATE TEMP TABLE audit_columns AS
|
|
246
|
+
SELECT
|
|
247
|
+
table_schema,
|
|
248
|
+
table_name,
|
|
249
|
+
column_name,
|
|
250
|
+
data_type,
|
|
251
|
+
is_nullable,
|
|
252
|
+
column_default
|
|
253
|
+
FROM information_schema.columns
|
|
254
|
+
WHERE table_schema = 'public';
|
|
255
|
+
|
|
256
|
+
-- Indexes
|
|
257
|
+
CREATE TEMP TABLE audit_indexes AS
|
|
258
|
+
SELECT
|
|
259
|
+
schemaname,
|
|
260
|
+
tablename,
|
|
261
|
+
indexname,
|
|
262
|
+
indexdef,
|
|
263
|
+
pg_relation_size(indexrelid) AS index_size
|
|
264
|
+
FROM pg_indexes
|
|
265
|
+
WHERE schemaname = 'public';
|
|
266
|
+
|
|
267
|
+
-- Foreign Keys
|
|
268
|
+
CREATE TEMP TABLE audit_fks AS
|
|
269
|
+
SELECT
|
|
270
|
+
tc.table_name,
|
|
271
|
+
kcu.column_name,
|
|
272
|
+
ccu.table_name AS foreign_table,
|
|
273
|
+
ccu.column_name AS foreign_column
|
|
274
|
+
FROM information_schema.table_constraints tc
|
|
275
|
+
JOIN information_schema.key_column_usage kcu
|
|
276
|
+
ON tc.constraint_name = kcu.constraint_name
|
|
277
|
+
JOIN information_schema.constraint_column_usage ccu
|
|
278
|
+
ON ccu.constraint_name = tc.constraint_name
|
|
279
|
+
WHERE tc.constraint_type = 'FOREIGN KEY'
|
|
280
|
+
AND tc.table_schema = 'public';
|
|
281
|
+
|
|
282
|
+
SELECT '✓ Metadata collected' AS status;
|
|
283
|
+
EOF
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
### 2. Check Design Best Practices
|
|
287
|
+
|
|
288
|
+
Run design checks:
|
|
289
|
+
|
|
290
|
+
```bash
|
|
291
|
+
psql "$SUPABASE_DB_URL" << 'EOF'
|
|
292
|
+
\echo '=========================================='
|
|
293
|
+
\echo '🔍 DESIGN BEST PRACTICES AUDIT'
|
|
294
|
+
\echo '=========================================='
|
|
295
|
+
\echo ''
|
|
296
|
+
|
|
297
|
+
-- Check 1: Tables without primary keys
|
|
298
|
+
\echo '1. Tables without PRIMARY KEY:'
|
|
299
|
+
SELECT table_name
|
|
300
|
+
FROM information_schema.tables t
|
|
301
|
+
WHERE table_schema = 'public'
|
|
302
|
+
AND table_type = 'BASE TABLE'
|
|
303
|
+
AND NOT EXISTS (
|
|
304
|
+
SELECT 1
|
|
305
|
+
FROM information_schema.table_constraints
|
|
306
|
+
WHERE table_schema = t.table_schema
|
|
307
|
+
AND table_name = t.table_name
|
|
308
|
+
AND constraint_type = 'PRIMARY KEY'
|
|
309
|
+
);
|
|
310
|
+
\echo ''
|
|
311
|
+
|
|
312
|
+
-- Check 2: Tables without created_at
|
|
313
|
+
\echo '2. Tables without created_at timestamp:'
|
|
314
|
+
SELECT table_name
|
|
315
|
+
FROM information_schema.tables t
|
|
316
|
+
WHERE table_schema = 'public'
|
|
317
|
+
AND table_type = 'BASE TABLE'
|
|
318
|
+
AND NOT EXISTS (
|
|
319
|
+
SELECT 1
|
|
320
|
+
FROM information_schema.columns
|
|
321
|
+
WHERE table_schema = t.table_schema
|
|
322
|
+
AND table_name = t.table_name
|
|
323
|
+
AND column_name IN ('created_at', 'createdat')
|
|
324
|
+
);
|
|
325
|
+
\echo ''
|
|
326
|
+
|
|
327
|
+
-- Check 3: Tables without updated_at
|
|
328
|
+
\echo '3. Tables without updated_at timestamp:'
|
|
329
|
+
SELECT table_name
|
|
330
|
+
FROM information_schema.tables t
|
|
331
|
+
WHERE table_schema = 'public'
|
|
332
|
+
AND table_type = 'BASE TABLE'
|
|
333
|
+
AND NOT EXISTS (
|
|
334
|
+
SELECT 1
|
|
335
|
+
FROM information_schema.columns
|
|
336
|
+
WHERE table_schema = t.table_schema
|
|
337
|
+
AND table_name = t.table_name
|
|
338
|
+
AND column_name IN ('updated_at', 'updatedat')
|
|
339
|
+
);
|
|
340
|
+
\echo ''
|
|
341
|
+
|
|
342
|
+
-- Check 4: Foreign keys without indexes
|
|
343
|
+
\echo '4. Foreign keys without indexes (performance issue):'
|
|
344
|
+
SELECT
|
|
345
|
+
fk.table_name,
|
|
346
|
+
fk.column_name,
|
|
347
|
+
fk.foreign_table
|
|
348
|
+
FROM audit_fks fk
|
|
349
|
+
WHERE NOT EXISTS (
|
|
350
|
+
SELECT 1
|
|
351
|
+
FROM pg_indexes idx
|
|
352
|
+
WHERE idx.tablename = fk.table_name
|
|
353
|
+
AND idx.indexdef LIKE '%' || fk.column_name || '%'
|
|
354
|
+
);
|
|
355
|
+
\echo ''
|
|
356
|
+
|
|
357
|
+
-- Check 5: Nullable columns that should be NOT NULL
|
|
358
|
+
\echo '5. Suspicious nullable columns (id, *_id, email, created_at):'
|
|
359
|
+
SELECT
|
|
360
|
+
table_name,
|
|
361
|
+
column_name,
|
|
362
|
+
data_type
|
|
363
|
+
FROM information_schema.columns
|
|
364
|
+
WHERE table_schema = 'public'
|
|
365
|
+
AND is_nullable = 'YES'
|
|
366
|
+
AND (
|
|
367
|
+
column_name = 'id'
|
|
368
|
+
OR column_name = 'email'
|
|
369
|
+
OR column_name = 'created_at'
|
|
370
|
+
OR column_name LIKE '%_id'
|
|
371
|
+
);
|
|
372
|
+
\echo ''
|
|
373
|
+
|
|
374
|
+
EOF
|
|
375
|
+
```
|
|
376
|
+
|
|
377
|
+
### 3. Check Performance Issues
|
|
378
|
+
|
|
379
|
+
Identify performance problems:
|
|
380
|
+
|
|
381
|
+
```bash
|
|
382
|
+
psql "$SUPABASE_DB_URL" << 'EOF'
|
|
383
|
+
\echo '=========================================='
|
|
384
|
+
\echo '⚡ PERFORMANCE ISSUES AUDIT'
|
|
385
|
+
\echo '=========================================='
|
|
386
|
+
\echo ''
|
|
387
|
+
|
|
388
|
+
-- Check 1: Missing indexes on foreign keys
|
|
389
|
+
\echo '1. Foreign keys without indexes:'
|
|
390
|
+
[Same as Check 4 above]
|
|
391
|
+
\echo ''
|
|
392
|
+
|
|
393
|
+
-- Check 2: Tables without indexes (except very small tables)
|
|
394
|
+
\echo '2. Tables without any indexes (excluding tiny tables):'
|
|
395
|
+
SELECT
|
|
396
|
+
t.tablename,
|
|
397
|
+
pg_size_pretty(pg_total_relation_size('public.' || t.tablename)) AS size
|
|
398
|
+
FROM pg_tables t
|
|
399
|
+
WHERE t.schemaname = 'public'
|
|
400
|
+
AND NOT EXISTS (
|
|
401
|
+
SELECT 1
|
|
402
|
+
FROM pg_indexes idx
|
|
403
|
+
WHERE idx.tablename = t.tablename
|
|
404
|
+
AND idx.schemaname = t.schemaname
|
|
405
|
+
)
|
|
406
|
+
AND pg_total_relation_size('public.' || t.tablename) > 8192; -- > 8KB
|
|
407
|
+
\echo ''
|
|
408
|
+
|
|
409
|
+
-- Check 3: Unused indexes
|
|
410
|
+
\echo '3. Unused indexes (0 scans, size > 1MB):'
|
|
411
|
+
SELECT
|
|
412
|
+
schemaname,
|
|
413
|
+
tablename,
|
|
414
|
+
indexname,
|
|
415
|
+
pg_size_pretty(pg_relation_size(indexrelid)) AS size,
|
|
416
|
+
idx_scan
|
|
417
|
+
FROM pg_stat_user_indexes
|
|
418
|
+
WHERE schemaname = 'public'
|
|
419
|
+
AND idx_scan = 0
|
|
420
|
+
AND indexname NOT LIKE '%_pkey' -- Exclude primary keys
|
|
421
|
+
AND pg_relation_size(indexrelid) > 1024*1024; -- > 1MB
|
|
422
|
+
\echo ''
|
|
423
|
+
|
|
424
|
+
-- Check 4: Duplicate indexes
|
|
425
|
+
\echo '4. Potential duplicate indexes:'
|
|
426
|
+
SELECT
|
|
427
|
+
a.tablename,
|
|
428
|
+
a.indexname AS index1,
|
|
429
|
+
b.indexname AS index2
|
|
430
|
+
FROM pg_indexes a
|
|
431
|
+
JOIN pg_indexes b
|
|
432
|
+
ON a.tablename = b.tablename
|
|
433
|
+
AND a.indexname < b.indexname
|
|
434
|
+
AND a.indexdef = b.indexdef
|
|
435
|
+
WHERE a.schemaname = 'public';
|
|
436
|
+
\echo ''
|
|
437
|
+
|
|
438
|
+
-- Check 5: Large tables without partitioning
|
|
439
|
+
\echo '5. Large tables (>1GB) that might benefit from partitioning:'
|
|
440
|
+
SELECT
|
|
441
|
+
tablename,
|
|
442
|
+
pg_size_pretty(pg_total_relation_size('public.' || tablename)) AS size
|
|
443
|
+
FROM pg_tables
|
|
444
|
+
WHERE schemaname = 'public'
|
|
445
|
+
AND pg_total_relation_size('public.' || tablename) > 1024*1024*1024
|
|
446
|
+
ORDER BY pg_total_relation_size('public.' || tablename) DESC;
|
|
447
|
+
\echo ''
|
|
448
|
+
|
|
449
|
+
EOF
|
|
450
|
+
```
|
|
451
|
+
|
|
452
|
+
### 4. Check Security
|
|
453
|
+
|
|
454
|
+
Audit security configuration:
|
|
455
|
+
|
|
456
|
+
```bash
|
|
457
|
+
psql "$SUPABASE_DB_URL" << 'EOF'
|
|
458
|
+
\echo '=========================================='
|
|
459
|
+
\echo '🔒 SECURITY AUDIT'
|
|
460
|
+
\echo '=========================================='
|
|
461
|
+
\echo ''
|
|
462
|
+
|
|
463
|
+
-- Check 1: Tables without RLS
|
|
464
|
+
\echo '1. Tables without Row Level Security enabled:'
|
|
465
|
+
SELECT
|
|
466
|
+
schemaname,
|
|
467
|
+
tablename,
|
|
468
|
+
rowsecurity
|
|
469
|
+
FROM pg_tables
|
|
470
|
+
WHERE schemaname = 'public'
|
|
471
|
+
AND rowsecurity = false;
|
|
472
|
+
\echo ''
|
|
473
|
+
|
|
474
|
+
-- Check 2: Tables with RLS but no policies
|
|
475
|
+
\echo '2. Tables with RLS enabled but no policies:'
|
|
476
|
+
SELECT
|
|
477
|
+
t.schemaname,
|
|
478
|
+
t.tablename
|
|
479
|
+
FROM pg_tables t
|
|
480
|
+
WHERE t.schemaname = 'public'
|
|
481
|
+
AND t.rowsecurity = true
|
|
482
|
+
AND NOT EXISTS (
|
|
483
|
+
SELECT 1
|
|
484
|
+
FROM pg_policies p
|
|
485
|
+
WHERE p.schemaname = t.schemaname
|
|
486
|
+
AND p.tablename = t.tablename
|
|
487
|
+
);
|
|
488
|
+
\echo ''
|
|
489
|
+
|
|
490
|
+
-- Check 3: RLS policy coverage
|
|
491
|
+
\echo '3. RLS policy coverage by table:'
|
|
492
|
+
SELECT
|
|
493
|
+
t.tablename,
|
|
494
|
+
t.rowsecurity AS rls_enabled,
|
|
495
|
+
COUNT(p.policyname) AS policy_count,
|
|
496
|
+
STRING_AGG(DISTINCT p.cmd, ', ') AS operations
|
|
497
|
+
FROM pg_tables t
|
|
498
|
+
LEFT JOIN pg_policies p
|
|
499
|
+
ON t.tablename = p.tablename
|
|
500
|
+
AND t.schemaname = p.schemaname
|
|
501
|
+
WHERE t.schemaname = 'public'
|
|
502
|
+
GROUP BY t.tablename, t.rowsecurity
|
|
503
|
+
ORDER BY t.tablename;
|
|
504
|
+
\echo ''
|
|
505
|
+
|
|
506
|
+
-- Check 4: Columns that might contain PII without encryption
|
|
507
|
+
\echo '4. Potential PII columns (consider encryption/hashing):'
|
|
508
|
+
SELECT
|
|
509
|
+
table_name,
|
|
510
|
+
column_name,
|
|
511
|
+
data_type
|
|
512
|
+
FROM information_schema.columns
|
|
513
|
+
WHERE table_schema = 'public'
|
|
514
|
+
AND (
|
|
515
|
+
column_name ILIKE '%ssn%'
|
|
516
|
+
OR column_name ILIKE '%tax_id%'
|
|
517
|
+
OR column_name ILIKE '%passport%'
|
|
518
|
+
OR column_name ILIKE '%credit_card%'
|
|
519
|
+
OR column_name ILIKE '%password%'
|
|
520
|
+
);
|
|
521
|
+
\echo ''
|
|
522
|
+
|
|
523
|
+
EOF
|
|
524
|
+
```
|
|
525
|
+
|
|
526
|
+
### 5. Check Data Integrity
|
|
527
|
+
|
|
528
|
+
Verify constraints and relationships:
|
|
529
|
+
|
|
530
|
+
```bash
|
|
531
|
+
psql "$SUPABASE_DB_URL" << 'EOF'
|
|
532
|
+
\echo '=========================================='
|
|
533
|
+
\echo '✅ DATA INTEGRITY AUDIT'
|
|
534
|
+
\echo '=========================================='
|
|
535
|
+
\echo ''
|
|
536
|
+
|
|
537
|
+
-- Check 1: Foreign key relationships count
|
|
538
|
+
\echo '1. Foreign key relationship summary:'
|
|
539
|
+
SELECT
|
|
540
|
+
COUNT(*) AS total_fk_constraints,
|
|
541
|
+
COUNT(DISTINCT table_name) AS tables_with_fks
|
|
542
|
+
FROM audit_fks;
|
|
543
|
+
\echo ''
|
|
544
|
+
|
|
545
|
+
-- Check 2: Check constraints count
|
|
546
|
+
\echo '2. CHECK constraints summary:'
|
|
547
|
+
SELECT
|
|
548
|
+
COUNT(*) AS total_check_constraints
|
|
549
|
+
FROM information_schema.check_constraints
|
|
550
|
+
WHERE constraint_schema = 'public';
|
|
551
|
+
\echo ''
|
|
552
|
+
|
|
553
|
+
-- Check 3: Unique constraints count
|
|
554
|
+
\echo '3. UNIQUE constraints summary:'
|
|
555
|
+
SELECT
|
|
556
|
+
COUNT(*) AS total_unique_constraints
|
|
557
|
+
FROM information_schema.table_constraints
|
|
558
|
+
WHERE constraint_schema = 'public'
|
|
559
|
+
AND constraint_type = 'UNIQUE';
|
|
560
|
+
\echo ''
|
|
561
|
+
|
|
562
|
+
-- Check 4: Tables without any constraints (red flag)
|
|
563
|
+
\echo '4. Tables without constraints (potential issues):'
|
|
564
|
+
SELECT table_name
|
|
565
|
+
FROM information_schema.tables t
|
|
566
|
+
WHERE table_schema = 'public'
|
|
567
|
+
AND table_type = 'BASE TABLE'
|
|
568
|
+
AND NOT EXISTS (
|
|
569
|
+
SELECT 1
|
|
570
|
+
FROM information_schema.table_constraints
|
|
571
|
+
WHERE table_schema = t.table_schema
|
|
572
|
+
AND table_name = t.table_name
|
|
573
|
+
);
|
|
574
|
+
\echo ''
|
|
575
|
+
|
|
576
|
+
-- Check 5: Orphaned records (FK points to non-existent record)
|
|
577
|
+
\echo '5. Checking for orphaned records...'
|
|
578
|
+
\echo ' (This check requires custom queries per table)'
|
|
579
|
+
\echo ' Example:'
|
|
580
|
+
\echo ' SELECT COUNT(*) FROM posts p'
|
|
581
|
+
\echo ' WHERE NOT EXISTS (SELECT 1 FROM users u WHERE u.id = p.user_id);'
|
|
582
|
+
\echo ''
|
|
583
|
+
|
|
584
|
+
EOF
|
|
585
|
+
```
|
|
586
|
+
|
|
587
|
+
### 6. Generate Audit Report
|
|
588
|
+
|
|
589
|
+
Create comprehensive report:
|
|
590
|
+
|
|
591
|
+
```bash
|
|
592
|
+
REPORT_FILE="supabase/docs/schema-audit-$(date +%Y%m%d%H%M%S).md"
|
|
593
|
+
mkdir -p supabase/docs
|
|
594
|
+
|
|
595
|
+
cat > "$REPORT_FILE" << 'MDEOF'
|
|
596
|
+
# Database Schema Audit Report
|
|
597
|
+
|
|
598
|
+
**Date**: $(date -u +"%Y-%m-%d %H:%M:%S UTC")
|
|
599
|
+
**Database**: [redacted]
|
|
600
|
+
**Auditor**: DB Sage
|
|
601
|
+
|
|
602
|
+
---
|
|
603
|
+
|
|
604
|
+
## Executive Summary
|
|
605
|
+
|
|
606
|
+
- Tables audited: {count}
|
|
607
|
+
- Total database size: {size}
|
|
608
|
+
- Critical issues: {critical_count}
|
|
609
|
+
- Warnings: {warning_count}
|
|
610
|
+
- Recommendations: {rec_count}
|
|
611
|
+
|
|
612
|
+
**Overall Score**: {score}/100
|
|
613
|
+
|
|
614
|
+
---
|
|
615
|
+
|
|
616
|
+
## Critical Issues 🔴
|
|
617
|
+
|
|
618
|
+
### 1. Tables without Primary Keys
|
|
619
|
+
{list_of_tables}
|
|
620
|
+
|
|
621
|
+
**Impact**: Cannot uniquely identify rows, replication issues
|
|
622
|
+
**Fix**: Add UUID or SERIAL primary key
|
|
623
|
+
|
|
624
|
+
---
|
|
625
|
+
|
|
626
|
+
### 2. Foreign Keys without Indexes
|
|
627
|
+
{list_of_fks}
|
|
628
|
+
|
|
629
|
+
**Impact**: Slow JOIN queries, slow ON DELETE CASCADE
|
|
630
|
+
**Fix**: Create indexes on FK columns
|
|
631
|
+
|
|
632
|
+
---
|
|
633
|
+
|
|
634
|
+
## Warnings ⚠️
|
|
635
|
+
|
|
636
|
+
### 3. Missing Timestamps
|
|
637
|
+
{list_of_tables_without_timestamps}
|
|
638
|
+
|
|
639
|
+
**Impact**: No audit trail, cannot track record creation/modification
|
|
640
|
+
**Fix**: Add created_at, updated_at columns
|
|
641
|
+
|
|
642
|
+
---
|
|
643
|
+
|
|
644
|
+
### 4. Tables without RLS
|
|
645
|
+
{list_of_tables_without_rls}
|
|
646
|
+
|
|
647
|
+
**Impact**: Security risk in multi-tenant applications
|
|
648
|
+
**Fix**: Enable RLS and create policies
|
|
649
|
+
|
|
650
|
+
---
|
|
651
|
+
|
|
652
|
+
## Recommendations 💡
|
|
653
|
+
|
|
654
|
+
### 5. Performance Optimizations
|
|
655
|
+
- Add indexes on frequently queried columns
|
|
656
|
+
- Consider partitioning for tables > 1GB
|
|
657
|
+
- Remove unused indexes (saves space, improves write performance)
|
|
658
|
+
|
|
659
|
+
### 6. Security Hardening
|
|
660
|
+
- Encrypt PII columns
|
|
661
|
+
- Implement RLS on all user-facing tables
|
|
662
|
+
- Add check constraints for data validation
|
|
663
|
+
|
|
664
|
+
### 7. Naming Conventions
|
|
665
|
+
- Use snake_case consistently
|
|
666
|
+
- Prefix foreign keys with table name (e.g., user_id not uid)
|
|
667
|
+
- Use plural for table names (e.g., users not user)
|
|
668
|
+
|
|
669
|
+
---
|
|
670
|
+
|
|
671
|
+
## Detailed Findings
|
|
672
|
+
|
|
673
|
+
[Include full output from all checks above]
|
|
674
|
+
|
|
675
|
+
---
|
|
676
|
+
|
|
677
|
+
## Action Items
|
|
678
|
+
|
|
679
|
+
Priority | Action | Estimated Effort
|
|
680
|
+
---------|--------|------------------
|
|
681
|
+
P0 | Add primary keys to {tables} | 1 hour
|
|
682
|
+
P0 | Index foreign keys | 2 hours
|
|
683
|
+
P1 | Enable RLS on {tables} | 4 hours
|
|
684
|
+
P1 | Add timestamps | 2 hours
|
|
685
|
+
P2 | Optimize indexes | 4 hours
|
|
686
|
+
|
|
687
|
+
---
|
|
688
|
+
|
|
689
|
+
## SQL Fixes
|
|
690
|
+
|
|
691
|
+
```sql
|
|
692
|
+
-- Fix 1: Add primary keys
|
|
693
|
+
ALTER TABLE {table} ADD COLUMN id UUID PRIMARY KEY DEFAULT gen_random_uuid();
|
|
694
|
+
|
|
695
|
+
-- Fix 2: Index foreign keys
|
|
696
|
+
CREATE INDEX CONCURRENTLY idx_{table}_{fk} ON {table}({fk_column});
|
|
697
|
+
|
|
698
|
+
-- Fix 3: Enable RLS
|
|
699
|
+
ALTER TABLE {table} ENABLE ROW LEVEL SECURITY;
|
|
700
|
+
CREATE POLICY "{table}_policy" ON {table} FOR ALL TO authenticated
|
|
701
|
+
USING (auth.uid() = user_id);
|
|
702
|
+
|
|
703
|
+
-- Fix 4: Add timestamps
|
|
704
|
+
ALTER TABLE {table} ADD COLUMN created_at TIMESTAMPTZ DEFAULT NOW();
|
|
705
|
+
ALTER TABLE {table} ADD COLUMN updated_at TIMESTAMPTZ;
|
|
706
|
+
```
|
|
707
|
+
|
|
708
|
+
MDEOF
|
|
709
|
+
|
|
710
|
+
echo "✓ Audit report: $REPORT_FILE"
|
|
711
|
+
```
|
|
712
|
+
|
|
713
|
+
---
|
|
714
|
+
|
|
715
|
+
## Output
|
|
716
|
+
|
|
717
|
+
Display audit summary:
|
|
718
|
+
|
|
719
|
+
```
|
|
720
|
+
✅ SCHEMA AUDIT COMPLETE
|
|
721
|
+
|
|
722
|
+
Database: [redacted]
|
|
723
|
+
Tables: {count}
|
|
724
|
+
Size: {size}
|
|
725
|
+
|
|
726
|
+
Critical Issues: {count} 🔴
|
|
727
|
+
Warnings: {count} ⚠️
|
|
728
|
+
Recommendations: {count} 💡
|
|
729
|
+
|
|
730
|
+
Overall Score: {score}/100
|
|
731
|
+
|
|
732
|
+
Report: supabase/docs/schema-audit-{timestamp}.md
|
|
733
|
+
|
|
734
|
+
Top Issues:
|
|
735
|
+
1. {issue_1}
|
|
736
|
+
2. {issue_2}
|
|
737
|
+
3. {issue_3}
|
|
738
|
+
|
|
739
|
+
Next Steps:
|
|
740
|
+
1. Review full report: cat {report_file}
|
|
741
|
+
2. Prioritize fixes
|
|
742
|
+
3. Create migrations for P0 issues
|
|
743
|
+
4. Re-run audit after fixes
|
|
744
|
+
```
|
|
745
|
+
|
|
746
|
+
---
|
|
747
|
+
|
|
748
|
+
## Scoring Rubric
|
|
749
|
+
|
|
750
|
+
- **100**: Perfect schema (rare!)
|
|
751
|
+
- **90-99**: Excellent, minor improvements
|
|
752
|
+
- **80-89**: Good, some best practices missed
|
|
753
|
+
- **70-79**: Fair, several issues to address
|
|
754
|
+
- **60-69**: Needs work, security or performance risks
|
|
755
|
+
- **<60**: Critical issues, not production-ready
|
|
756
|
+
|
|
757
|
+
---
|
|
758
|
+
|
|
759
|
+
## Advanced Auditing Tools
|
|
760
|
+
|
|
761
|
+
### 1. Audit Triggers (Change Tracking)
|
|
762
|
+
|
|
763
|
+
**Purpose:** Track all changes (INSERT, UPDATE, DELETE) with who, when, what changed
|
|
764
|
+
|
|
765
|
+
**Implementation:**
|
|
766
|
+
```sql
|
|
767
|
+
-- Create audit log schema
|
|
768
|
+
CREATE SCHEMA IF NOT EXISTS audit;
|
|
769
|
+
|
|
770
|
+
-- Audit log table
|
|
771
|
+
CREATE TABLE audit.logged_actions (
|
|
772
|
+
event_id BIGSERIAL PRIMARY KEY,
|
|
773
|
+
schema_name TEXT NOT NULL,
|
|
774
|
+
table_name TEXT NOT NULL,
|
|
775
|
+
relid OID NOT NULL,
|
|
776
|
+
session_user_name TEXT,
|
|
777
|
+
action_tstamp_tx TIMESTAMPTZ NOT NULL DEFAULT transaction_timestamp(),
|
|
778
|
+
action_tstamp_stm TIMESTAMPTZ NOT NULL DEFAULT statement_timestamp(),
|
|
779
|
+
action_tstamp_clk TIMESTAMPTZ NOT NULL DEFAULT clock_timestamp(),
|
|
780
|
+
transaction_id BIGINT,
|
|
781
|
+
application_name TEXT,
|
|
782
|
+
client_addr INET,
|
|
783
|
+
client_port INTEGER,
|
|
784
|
+
client_query TEXT,
|
|
785
|
+
action TEXT NOT NULL CHECK (action IN ('I','D','U', 'T')),
|
|
786
|
+
row_data JSONB,
|
|
787
|
+
changed_fields JSONB,
|
|
788
|
+
statement_only BOOLEAN NOT NULL DEFAULT false
|
|
789
|
+
);
|
|
790
|
+
|
|
791
|
+
CREATE INDEX idx_audit_relid ON audit.logged_actions(relid);
|
|
792
|
+
CREATE INDEX idx_audit_action_tstamp ON audit.logged_actions(action_tstamp_tx);
|
|
793
|
+
CREATE INDEX idx_audit_table_name ON audit.logged_actions(table_name);
|
|
794
|
+
|
|
795
|
+
-- Generic audit trigger function
|
|
796
|
+
CREATE OR REPLACE FUNCTION audit.if_modified_func()
|
|
797
|
+
RETURNS TRIGGER AS $$
|
|
798
|
+
DECLARE
|
|
799
|
+
audit_row audit.logged_actions;
|
|
800
|
+
excluded_cols TEXT[] = ARRAY[]::TEXT[];
|
|
801
|
+
BEGIN
|
|
802
|
+
IF TG_WHEN <> 'AFTER' THEN
|
|
803
|
+
RAISE EXCEPTION 'audit.if_modified_func() may only run as an AFTER trigger';
|
|
804
|
+
END IF;
|
|
805
|
+
|
|
806
|
+
audit_row = ROW(
|
|
807
|
+
nextval('audit.logged_actions_event_id_seq'), -- event_id
|
|
808
|
+
TG_TABLE_SCHEMA::TEXT, -- schema_name
|
|
809
|
+
TG_TABLE_NAME::TEXT, -- table_name
|
|
810
|
+
TG_RELID, -- relid
|
|
811
|
+
session_user::TEXT, -- session_user_name
|
|
812
|
+
current_timestamp, -- action_tstamp_tx
|
|
813
|
+
statement_timestamp(), -- action_tstamp_stm
|
|
814
|
+
clock_timestamp(), -- action_tstamp_clk
|
|
815
|
+
txid_current(), -- transaction_id
|
|
816
|
+
current_setting('application_name'), -- application_name
|
|
817
|
+
inet_client_addr(), -- client_addr
|
|
818
|
+
inet_client_port(), -- client_port
|
|
819
|
+
current_query(), -- client_query
|
|
820
|
+
substring(TG_OP,1,1), -- action
|
|
821
|
+
NULL, -- row_data (set below)
|
|
822
|
+
NULL, -- changed_fields (set below)
|
|
823
|
+
false -- statement_only
|
|
824
|
+
);
|
|
825
|
+
|
|
826
|
+
IF TG_OP = 'UPDATE' AND TG_LEVEL = 'ROW' THEN
|
|
827
|
+
audit_row.row_data = to_jsonb(OLD);
|
|
828
|
+
audit_row.changed_fields = jsonb_build_object(
|
|
829
|
+
'old', to_jsonb(OLD),
|
|
830
|
+
'new', to_jsonb(NEW)
|
|
831
|
+
);
|
|
832
|
+
ELSIF TG_OP = 'DELETE' AND TG_LEVEL = 'ROW' THEN
|
|
833
|
+
audit_row.row_data = to_jsonb(OLD);
|
|
834
|
+
ELSIF TG_OP = 'INSERT' AND TG_LEVEL = 'ROW' THEN
|
|
835
|
+
audit_row.row_data = to_jsonb(NEW);
|
|
836
|
+
ELSE
|
|
837
|
+
RAISE EXCEPTION '[audit.if_modified_func] - Trigger func added as trigger for unhandled case: %, %',TG_OP, TG_LEVEL;
|
|
838
|
+
RETURN NULL;
|
|
839
|
+
END IF;
|
|
840
|
+
|
|
841
|
+
INSERT INTO audit.logged_actions VALUES (audit_row.*);
|
|
842
|
+
RETURN NULL;
|
|
843
|
+
END;
|
|
844
|
+
$$ LANGUAGE plpgsql SECURITY DEFINER;
|
|
845
|
+
|
|
846
|
+
-- Apply to tables (example)
|
|
847
|
+
CREATE TRIGGER audit_trigger_row
|
|
848
|
+
AFTER INSERT OR UPDATE OR DELETE ON users
|
|
849
|
+
FOR EACH ROW EXECUTE FUNCTION audit.if_modified_func();
|
|
850
|
+
```
|
|
851
|
+
|
|
852
|
+
**Benefits:**
|
|
853
|
+
- Complete audit trail of all changes
|
|
854
|
+
- Forensic analysis capabilities
|
|
855
|
+
- Compliance requirements (GDPR, SOX, HIPAA)
|
|
856
|
+
- Debugging production issues
|
|
857
|
+
|
|
858
|
+
### 2. pgAudit Extension (PostgreSQL Auditing)
|
|
859
|
+
|
|
860
|
+
**Purpose:** Comprehensive session and object audit logging
|
|
861
|
+
|
|
862
|
+
```sql
|
|
863
|
+
-- Install extension
|
|
864
|
+
CREATE EXTENSION IF NOT EXISTS pgaudit;
|
|
865
|
+
|
|
866
|
+
-- Configure (in postgresql.conf or ALTER SYSTEM)
|
|
867
|
+
ALTER SYSTEM SET pgaudit.log = 'write'; -- Log all writes
|
|
868
|
+
ALTER SYSTEM SET pgaudit.log_catalog = off; -- Don't log catalog queries
|
|
869
|
+
ALTER SYSTEM SET pgaudit.log_parameter = on; -- Include parameter values
|
|
870
|
+
ALTER SYSTEM SET pgaudit.log_relation = on; -- Include table names
|
|
871
|
+
ALTER SYSTEM SET pgaudit.log_statement_once = off; -- Log each statement
|
|
872
|
+
|
|
873
|
+
-- Reload configuration
|
|
874
|
+
SELECT pg_reload_conf();
|
|
875
|
+
|
|
876
|
+
-- Example: Audit specific table
|
|
877
|
+
CREATE ROLE auditor;
|
|
878
|
+
GRANT SELECT, INSERT, UPDATE, DELETE ON users TO auditor;
|
|
879
|
+
ALTER ROLE auditor SET pgaudit.log = 'write';
|
|
880
|
+
```
|
|
881
|
+
|
|
882
|
+
**What gets logged:**
|
|
883
|
+
- All DDL operations (CREATE, ALTER, DROP)
|
|
884
|
+
- All DML operations (INSERT, UPDATE, DELETE) based on config
|
|
885
|
+
- Parameter values (for forensics)
|
|
886
|
+
- Session information
|
|
887
|
+
|
|
888
|
+
### 3. pgTAP Extension (Database Testing)
|
|
889
|
+
|
|
890
|
+
**Purpose:** Unit tests for database schema, constraints, and data
|
|
891
|
+
|
|
892
|
+
**Installation:**
|
|
893
|
+
```sql
|
|
894
|
+
CREATE EXTENSION IF NOT EXISTS pgtap;
|
|
895
|
+
```
|
|
896
|
+
|
|
897
|
+
**Example test suite:**
|
|
898
|
+
```sql
|
|
899
|
+
-- File: tests/schema_tests.sql
|
|
900
|
+
BEGIN;
|
|
901
|
+
SELECT plan(10); -- Number of tests
|
|
902
|
+
|
|
903
|
+
-- Test 1: Check table exists
|
|
904
|
+
SELECT has_table('public', 'users', 'users table exists');
|
|
905
|
+
|
|
906
|
+
-- Test 2: Check primary key
|
|
907
|
+
SELECT has_pk('public', 'users', 'users has primary key');
|
|
908
|
+
|
|
909
|
+
-- Test 3: Check specific columns
|
|
910
|
+
SELECT has_column('public', 'users', 'id', 'users.id exists');
|
|
911
|
+
SELECT has_column('public', 'users', 'email', 'users.email exists');
|
|
912
|
+
SELECT has_column('public', 'users', 'created_at', 'users.created_at exists');
|
|
913
|
+
|
|
914
|
+
-- Test 4: Check column types
|
|
915
|
+
SELECT col_type_is('public', 'users', 'id', 'uuid', 'users.id is UUID');
|
|
916
|
+
SELECT col_type_is('public', 'users', 'email', 'text', 'users.email is TEXT');
|
|
917
|
+
|
|
918
|
+
-- Test 5: Check NOT NULL constraints
|
|
919
|
+
SELECT col_not_null('public', 'users', 'email', 'users.email is NOT NULL');
|
|
920
|
+
|
|
921
|
+
-- Test 6: Check foreign keys
|
|
922
|
+
SELECT has_fk('public', 'posts', 'posts has foreign key');
|
|
923
|
+
|
|
924
|
+
-- Test 7: Check indexes
|
|
925
|
+
SELECT has_index('public', 'users', 'idx_users_email', 'email index exists');
|
|
926
|
+
|
|
927
|
+
SELECT * FROM finish();
|
|
928
|
+
ROLLBACK;
|
|
929
|
+
```
|
|
930
|
+
|
|
931
|
+
**Run tests:**
|
|
932
|
+
```bash
|
|
933
|
+
psql "$DB_URL" -f tests/schema_tests.sql
|
|
934
|
+
```
|
|
935
|
+
|
|
936
|
+
**CI/CD Integration:**
|
|
937
|
+
```yaml
|
|
938
|
+
# .github/workflows/test.yml
|
|
939
|
+
- name: Run pgTAP tests
|
|
940
|
+
run: |
|
|
941
|
+
pg_prove --dbname "$DB_URL" tests/*.sql
|
|
942
|
+
```
|
|
943
|
+
|
|
944
|
+
### 4. Named Constraints (Best Practice)
|
|
945
|
+
|
|
946
|
+
**Why naming matters:**
|
|
947
|
+
- Error messages become informative
|
|
948
|
+
- Easier to troubleshoot constraint violations
|
|
949
|
+
- Explicit documentation of business rules
|
|
950
|
+
|
|
951
|
+
**Examples:**
|
|
952
|
+
```sql
|
|
953
|
+
-- ❌ BAD: Unnamed constraints
|
|
954
|
+
CREATE TABLE users (
|
|
955
|
+
id UUID PRIMARY KEY,
|
|
956
|
+
email TEXT UNIQUE,
|
|
957
|
+
age INTEGER CHECK (age >= 18)
|
|
958
|
+
);
|
|
959
|
+
-- Error: "violates check constraint users_age_check" (cryptic!)
|
|
960
|
+
|
|
961
|
+
-- ✅ GOOD: Named constraints with descriptive names
|
|
962
|
+
CREATE TABLE users (
|
|
963
|
+
id UUID CONSTRAINT users_pkey PRIMARY KEY,
|
|
964
|
+
email TEXT CONSTRAINT users_email_unique UNIQUE,
|
|
965
|
+
age INTEGER CONSTRAINT users_age_must_be_adult CHECK (age >= 18),
|
|
966
|
+
created_at TIMESTAMPTZ CONSTRAINT users_created_at_required NOT NULL,
|
|
967
|
+
status TEXT CONSTRAINT users_status_valid CHECK (status IN ('active', 'suspended', 'deleted'))
|
|
968
|
+
);
|
|
969
|
+
-- Error: "violates check constraint users_age_must_be_adult" (clear!)
|
|
970
|
+
```
|
|
971
|
+
|
|
972
|
+
**Naming conventions:**
|
|
973
|
+
```
|
|
974
|
+
{table}_{column}_{type}
|
|
975
|
+
{table}_{columns}_{type}
|
|
976
|
+
|
|
977
|
+
Types:
|
|
978
|
+
- pkey: Primary key
|
|
979
|
+
- fkey: Foreign key
|
|
980
|
+
- unique: Unique constraint
|
|
981
|
+
- check: Check constraint
|
|
982
|
+
- idx: Index
|
|
983
|
+
```
|
|
984
|
+
|
|
985
|
+
**Audit query for unnamed constraints:**
|
|
986
|
+
```sql
|
|
987
|
+
-- Find constraints without descriptive names
|
|
988
|
+
SELECT
|
|
989
|
+
conname AS constraint_name,
|
|
990
|
+
conrelid::regclass AS table_name,
|
|
991
|
+
contype AS constraint_type
|
|
992
|
+
FROM pg_constraint
|
|
993
|
+
WHERE connamespace = 'public'::regnamespace
|
|
994
|
+
AND (
|
|
995
|
+
-- Auto-generated names (PostgreSQL pattern)
|
|
996
|
+
conname ~ '_pkey$|_key$|_fkey$|_check$|_not_null$'
|
|
997
|
+
AND NOT conname ~ '^[a-z]+_[a-z_]+_(pkey|fkey|unique|check|required|valid)'
|
|
998
|
+
)
|
|
999
|
+
ORDER BY conrelid::regclass::TEXT, conname;
|
|
1000
|
+
```
|
|
1001
|
+
|
|
1002
|
+
---
|
|
1003
|
+
|
|
1004
|
+
## References
|
|
1005
|
+
|
|
1006
|
+
- [PostgreSQL Best Practices](https://wiki.postgresql.org/wiki/Don't_Do_This)
|
|
1007
|
+
- [Supabase RLS Best Practices](https://supabase.com/docs/guides/auth/row-level-security)
|
|
1008
|
+
- [Database Design Best Practices](https://www.postgresql.org/docs/current/ddl.html)
|
|
1009
|
+
- [PostgreSQL Audit Trigger](https://wiki.postgresql.org/wiki/Audit_trigger)
|
|
1010
|
+
- [pgAudit Extension](https://www.pgaudit.org/)
|
|
1011
|
+
- [pgTAP Documentation](https://pgtap.org/)
|