aios-core 2.2.2 → 2.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (105) hide show
  1. package/.aios-core/.session/current-session.json +14 -14
  2. package/.aios-core/cli/commands/migrate/validate.js +1 -1
  3. package/.aios-core/core/docs/session-update-pattern.md +17 -10
  4. package/.aios-core/core/elicitation/elicitation-engine.js +11 -6
  5. package/.aios-core/core/elicitation/session-manager.js +2 -1
  6. package/.aios-core/core/registry/registry-schema.json +166 -166
  7. package/.aios-core/core/registry/service-registry.json +6585 -6585
  8. package/.aios-core/core-config.yaml +12 -1
  9. package/.aios-core/data/agent-config-requirements.yaml +5 -5
  10. package/.aios-core/development/agents/devops.md +12 -0
  11. package/.aios-core/development/scripts/squad/README.md +112 -0
  12. package/.aios-core/development/scripts/squad/index.js +41 -0
  13. package/.aios-core/development/scripts/squad/squad-loader.js +359 -0
  14. package/.aios-core/development/scripts/squad/squad-validator.js +685 -0
  15. package/.aios-core/development/tasks/add-mcp.md +11 -5
  16. package/.aios-core/development/tasks/search-mcp.md +309 -0
  17. package/.aios-core/development/tasks/setup-mcp-docker.md +11 -8
  18. package/.aios-core/development/tasks/squad-creator-validate.md +151 -0
  19. package/.aios-core/docs/standards/AGENT-PERSONALIZATION-STANDARD-V1.md +3 -3
  20. package/.aios-core/index.d.ts +7 -7
  21. package/.aios-core/index.js +1 -1
  22. package/.aios-core/infrastructure/scripts/batch-creator.js +1 -1
  23. package/.aios-core/infrastructure/scripts/component-generator.js +1 -1
  24. package/.aios-core/infrastructure/templates/coderabbit.yaml.template +279 -279
  25. package/.aios-core/infrastructure/templates/github-workflows/ci.yml.template +169 -169
  26. package/.aios-core/infrastructure/templates/github-workflows/pr-automation.yml.template +330 -330
  27. package/.aios-core/infrastructure/templates/github-workflows/release.yml.template +196 -196
  28. package/.aios-core/infrastructure/templates/gitignore/gitignore-aios-base.tmpl +63 -63
  29. package/.aios-core/infrastructure/templates/gitignore/gitignore-brownfield-merge.tmpl +18 -18
  30. package/.aios-core/infrastructure/templates/gitignore/gitignore-node.tmpl +85 -85
  31. package/.aios-core/infrastructure/templates/gitignore/gitignore-python.tmpl +145 -145
  32. package/.aios-core/infrastructure/tests/utilities-audit-results.json +500 -500
  33. package/.aios-core/infrastructure/tools/README.md +1 -1
  34. package/.aios-core/install-manifest.yaml +4 -1
  35. package/.aios-core/manifests/schema/manifest-schema.json +190 -190
  36. package/.aios-core/manifests/workers.csv +203 -203
  37. package/.aios-core/package.json +102 -102
  38. package/.aios-core/product/templates/activation-instructions-template.md +7 -7
  39. package/.aios-core/product/templates/adr.hbs +125 -125
  40. package/.aios-core/product/templates/component-react-tmpl.tsx +98 -98
  41. package/.aios-core/product/templates/dbdr.hbs +241 -241
  42. package/.aios-core/product/templates/engine/schemas/adr.schema.json +102 -102
  43. package/.aios-core/product/templates/engine/schemas/dbdr.schema.json +205 -205
  44. package/.aios-core/product/templates/engine/schemas/epic.schema.json +175 -175
  45. package/.aios-core/product/templates/engine/schemas/pmdr.schema.json +175 -175
  46. package/.aios-core/product/templates/engine/schemas/prd-v2.schema.json +300 -300
  47. package/.aios-core/product/templates/engine/schemas/prd.schema.json +152 -152
  48. package/.aios-core/product/templates/engine/schemas/story.schema.json +222 -222
  49. package/.aios-core/product/templates/engine/schemas/task.schema.json +154 -154
  50. package/.aios-core/product/templates/epic.hbs +212 -212
  51. package/.aios-core/product/templates/eslintrc-security.json +32 -32
  52. package/.aios-core/product/templates/github-actions-cd.yml +212 -212
  53. package/.aios-core/product/templates/github-actions-ci.yml +172 -172
  54. package/.aios-core/product/templates/pmdr.hbs +186 -186
  55. package/.aios-core/product/templates/prd-v2.0.hbs +216 -216
  56. package/.aios-core/product/templates/prd.hbs +201 -201
  57. package/.aios-core/product/templates/shock-report-tmpl.html +502 -502
  58. package/.aios-core/product/templates/story.hbs +263 -263
  59. package/.aios-core/product/templates/task.hbs +170 -170
  60. package/.aios-core/product/templates/tmpl-comment-on-examples.sql +158 -158
  61. package/.aios-core/product/templates/tmpl-migration-script.sql +91 -91
  62. package/.aios-core/product/templates/tmpl-rls-granular-policies.sql +104 -104
  63. package/.aios-core/product/templates/tmpl-rls-kiss-policy.sql +10 -10
  64. package/.aios-core/product/templates/tmpl-rls-roles.sql +135 -135
  65. package/.aios-core/product/templates/tmpl-rls-simple.sql +77 -77
  66. package/.aios-core/product/templates/tmpl-rls-tenant.sql +152 -152
  67. package/.aios-core/product/templates/tmpl-rollback-script.sql +77 -77
  68. package/.aios-core/product/templates/tmpl-seed-data.sql +140 -140
  69. package/.aios-core/product/templates/tmpl-smoke-test.sql +16 -16
  70. package/.aios-core/product/templates/tmpl-staging-copy-merge.sql +139 -139
  71. package/.aios-core/product/templates/tmpl-stored-proc.sql +140 -140
  72. package/.aios-core/product/templates/tmpl-trigger.sql +152 -152
  73. package/.aios-core/product/templates/tmpl-view-materialized.sql +133 -133
  74. package/.aios-core/product/templates/tmpl-view.sql +177 -177
  75. package/.aios-core/product/templates/token-exports-css-tmpl.css +240 -240
  76. package/.aios-core/quality/schemas/quality-metrics.schema.json +233 -233
  77. package/.aios-core/schemas/squad-schema.json +185 -0
  78. package/.aios-core/scripts/README.md +90 -322
  79. package/.aios-core/scripts/migrate-framework-docs.sh +300 -300
  80. package/.claude/rules/mcp-usage.md +116 -100
  81. package/LICENSE +48 -48
  82. package/README.md +3 -4
  83. package/bin/aios.js +2 -1
  84. package/package.json +1 -3
  85. package/packages/installer/package.json +39 -39
  86. package/templates/squad/LICENSE +21 -21
  87. package/templates/squad/README.md +37 -37
  88. package/templates/squad/agents/example-agent.yaml +36 -36
  89. package/templates/squad/package.json +19 -19
  90. package/templates/squad/squad.yaml +25 -25
  91. package/templates/squad/tasks/example-task.yaml +46 -46
  92. package/templates/squad/templates/example-template.md +24 -24
  93. package/templates/squad/tests/example-agent.test.js +53 -53
  94. package/templates/squad/workflows/example-workflow.yaml +54 -54
  95. package/tools/diagnose-npx-issue.ps1 +96 -96
  96. package/tools/quick-diagnose.cmd +85 -85
  97. package/tools/quick-diagnose.ps1 +117 -117
  98. package/.aios-core/core/data/agent-config-requirements.yaml +0 -368
  99. package/.aios-core/core/data/aios-kb.md +0 -924
  100. package/.aios-core/core/data/workflow-patterns.yaml +0 -267
  101. package/.aios-core/product/templates/1mcp-config.yaml +0 -225
  102. package/.aios-core/scripts/context-detector.js +0 -226
  103. package/.aios-core/scripts/elicitation-engine.js +0 -385
  104. package/.aios-core/scripts/elicitation-session-manager.js +0 -300
  105. package/.claude/CLAUDE.md +0 -221
@@ -1,140 +1,140 @@
1
- -- Seed Data Template
2
- -- Purpose: Idempotent seed data for development and testing
3
- -- Created: :created_date
4
- -- Author: :author
5
- --
6
- -- IMPORTANT: All seed operations should be idempotent (safe to run multiple times)
7
-
8
- -- =============================================================================
9
- -- SEED CONFIGURATION
10
- -- =============================================================================
11
-
12
- -- Disable triggers during seeding (optional, speeds up bulk insert)
13
- -- SET session_replication_role = replica;
14
-
15
- BEGIN;
16
-
17
- -- =============================================================================
18
- -- SEED: REFERENCE DATA (Lookup tables, static data)
19
- -- =============================================================================
20
-
21
- -- Example: Status types
22
- INSERT INTO status_types (id, name, description, sort_order)
23
- VALUES
24
- ('11111111-1111-1111-1111-111111111001', 'draft', 'Initial draft state', 1),
25
- ('11111111-1111-1111-1111-111111111002', 'pending', 'Awaiting action', 2),
26
- ('11111111-1111-1111-1111-111111111003', 'active', 'Currently active', 3),
27
- ('11111111-1111-1111-1111-111111111004', 'completed', 'Work completed', 4),
28
- ('11111111-1111-1111-1111-111111111005', 'archived', 'No longer active', 5)
29
- ON CONFLICT (id) DO UPDATE SET
30
- name = EXCLUDED.name,
31
- description = EXCLUDED.description,
32
- sort_order = EXCLUDED.sort_order;
33
-
34
- -- Example: Categories
35
- INSERT INTO categories (id, name, slug, parent_id)
36
- VALUES
37
- ('22222222-2222-2222-2222-222222222001', 'General', 'general', NULL),
38
- ('22222222-2222-2222-2222-222222222002', 'Development', 'development', NULL),
39
- ('22222222-2222-2222-2222-222222222003', 'Frontend', 'frontend', '22222222-2222-2222-2222-222222222002'),
40
- ('22222222-2222-2222-2222-222222222004', 'Backend', 'backend', '22222222-2222-2222-2222-222222222002')
41
- ON CONFLICT (id) DO UPDATE SET
42
- name = EXCLUDED.name,
43
- slug = EXCLUDED.slug,
44
- parent_id = EXCLUDED.parent_id;
45
-
46
- -- =============================================================================
47
- -- SEED: ROLES AND PERMISSIONS
48
- -- =============================================================================
49
-
50
- INSERT INTO roles (id, name, description, permissions)
51
- VALUES
52
- ('33333333-3333-3333-3333-333333333001', 'admin', 'Full system access', '["*"]'::JSONB),
53
- ('33333333-3333-3333-3333-333333333002', 'editor', 'Can edit content', '["read", "write", "update"]'::JSONB),
54
- ('33333333-3333-3333-3333-333333333003', 'viewer', 'Read-only access', '["read"]'::JSONB)
55
- ON CONFLICT (id) DO UPDATE SET
56
- description = EXCLUDED.description,
57
- permissions = EXCLUDED.permissions;
58
-
59
- -- =============================================================================
60
- -- SEED: TEST USERS (Development only!)
61
- -- =============================================================================
62
-
63
- -- NOTE: Only run in development environment
64
- -- These are fake users for testing - DO NOT use in production
65
-
66
- -- Check environment before inserting test data
67
- DO $$
68
- BEGIN
69
- -- Only seed test users if this appears to be a dev environment
70
- IF current_database() LIKE '%dev%' OR current_database() LIKE '%test%' THEN
71
- -- Test Admin User
72
- INSERT INTO auth.users (
73
- id,
74
- email,
75
- encrypted_password,
76
- email_confirmed_at,
77
- raw_user_meta_data
78
- ) VALUES (
79
- '00000000-0000-0000-0000-000000000001',
80
- 'admin@test.local',
81
- crypt('testpassword123', gen_salt('bf')),
82
- NOW(),
83
- '{"full_name": "Test Admin"}'::JSONB
84
- ) ON CONFLICT (id) DO NOTHING;
85
-
86
- -- Test Regular User
87
- INSERT INTO auth.users (
88
- id,
89
- email,
90
- encrypted_password,
91
- email_confirmed_at,
92
- raw_user_meta_data
93
- ) VALUES (
94
- '00000000-0000-0000-0000-000000000002',
95
- 'user@test.local',
96
- crypt('testpassword123', gen_salt('bf')),
97
- NOW(),
98
- '{"full_name": "Test User"}'::JSONB
99
- ) ON CONFLICT (id) DO NOTHING;
100
-
101
- RAISE NOTICE 'Test users seeded successfully';
102
- ELSE
103
- RAISE NOTICE 'Skipping test user seeding - not a dev/test environment';
104
- END IF;
105
- END $$;
106
-
107
- -- =============================================================================
108
- -- SEED: SAMPLE DATA (Development only!)
109
- -- =============================================================================
110
-
111
- -- Example: Sample projects for testing
112
- INSERT INTO :table_name (id, name, description, user_id, status, created_at)
113
- VALUES
114
- ('44444444-4444-4444-4444-444444444001', 'Sample Project 1', 'First sample project for testing', '00000000-0000-0000-0000-000000000001', 'active', NOW()),
115
- ('44444444-4444-4444-4444-444444444002', 'Sample Project 2', 'Second sample project for testing', '00000000-0000-0000-0000-000000000002', 'draft', NOW())
116
- ON CONFLICT (id) DO UPDATE SET
117
- name = EXCLUDED.name,
118
- description = EXCLUDED.description,
119
- updated_at = NOW();
120
-
121
- COMMIT;
122
-
123
- -- =============================================================================
124
- -- RE-ENABLE TRIGGERS
125
- -- =============================================================================
126
-
127
- -- SET session_replication_role = DEFAULT;
128
-
129
- -- =============================================================================
130
- -- VERIFICATION
131
- -- =============================================================================
132
-
133
- SELECT
134
- 'status_types' as table_name,
135
- COUNT(*) as row_count
136
- FROM status_types
137
- UNION ALL
138
- SELECT 'categories', COUNT(*) FROM categories
139
- UNION ALL
140
- SELECT 'roles', COUNT(*) FROM roles;
1
+ -- Seed Data Template
2
+ -- Purpose: Idempotent seed data for development and testing
3
+ -- Created: :created_date
4
+ -- Author: :author
5
+ --
6
+ -- IMPORTANT: All seed operations should be idempotent (safe to run multiple times)
7
+
8
+ -- =============================================================================
9
+ -- SEED CONFIGURATION
10
+ -- =============================================================================
11
+
12
+ -- Disable triggers during seeding (optional, speeds up bulk insert)
13
+ -- SET session_replication_role = replica;
14
+
15
+ BEGIN;
16
+
17
+ -- =============================================================================
18
+ -- SEED: REFERENCE DATA (Lookup tables, static data)
19
+ -- =============================================================================
20
+
21
+ -- Example: Status types
22
+ INSERT INTO status_types (id, name, description, sort_order)
23
+ VALUES
24
+ ('11111111-1111-1111-1111-111111111001', 'draft', 'Initial draft state', 1),
25
+ ('11111111-1111-1111-1111-111111111002', 'pending', 'Awaiting action', 2),
26
+ ('11111111-1111-1111-1111-111111111003', 'active', 'Currently active', 3),
27
+ ('11111111-1111-1111-1111-111111111004', 'completed', 'Work completed', 4),
28
+ ('11111111-1111-1111-1111-111111111005', 'archived', 'No longer active', 5)
29
+ ON CONFLICT (id) DO UPDATE SET
30
+ name = EXCLUDED.name,
31
+ description = EXCLUDED.description,
32
+ sort_order = EXCLUDED.sort_order;
33
+
34
+ -- Example: Categories
35
+ INSERT INTO categories (id, name, slug, parent_id)
36
+ VALUES
37
+ ('22222222-2222-2222-2222-222222222001', 'General', 'general', NULL),
38
+ ('22222222-2222-2222-2222-222222222002', 'Development', 'development', NULL),
39
+ ('22222222-2222-2222-2222-222222222003', 'Frontend', 'frontend', '22222222-2222-2222-2222-222222222002'),
40
+ ('22222222-2222-2222-2222-222222222004', 'Backend', 'backend', '22222222-2222-2222-2222-222222222002')
41
+ ON CONFLICT (id) DO UPDATE SET
42
+ name = EXCLUDED.name,
43
+ slug = EXCLUDED.slug,
44
+ parent_id = EXCLUDED.parent_id;
45
+
46
+ -- =============================================================================
47
+ -- SEED: ROLES AND PERMISSIONS
48
+ -- =============================================================================
49
+
50
+ INSERT INTO roles (id, name, description, permissions)
51
+ VALUES
52
+ ('33333333-3333-3333-3333-333333333001', 'admin', 'Full system access', '["*"]'::JSONB),
53
+ ('33333333-3333-3333-3333-333333333002', 'editor', 'Can edit content', '["read", "write", "update"]'::JSONB),
54
+ ('33333333-3333-3333-3333-333333333003', 'viewer', 'Read-only access', '["read"]'::JSONB)
55
+ ON CONFLICT (id) DO UPDATE SET
56
+ description = EXCLUDED.description,
57
+ permissions = EXCLUDED.permissions;
58
+
59
+ -- =============================================================================
60
+ -- SEED: TEST USERS (Development only!)
61
+ -- =============================================================================
62
+
63
+ -- NOTE: Only run in development environment
64
+ -- These are fake users for testing - DO NOT use in production
65
+
66
+ -- Check environment before inserting test data
67
+ DO $$
68
+ BEGIN
69
+ -- Only seed test users if this appears to be a dev environment
70
+ IF current_database() LIKE '%dev%' OR current_database() LIKE '%test%' THEN
71
+ -- Test Admin User
72
+ INSERT INTO auth.users (
73
+ id,
74
+ email,
75
+ encrypted_password,
76
+ email_confirmed_at,
77
+ raw_user_meta_data
78
+ ) VALUES (
79
+ '00000000-0000-0000-0000-000000000001',
80
+ 'admin@test.local',
81
+ crypt('testpassword123', gen_salt('bf')),
82
+ NOW(),
83
+ '{"full_name": "Test Admin"}'::JSONB
84
+ ) ON CONFLICT (id) DO NOTHING;
85
+
86
+ -- Test Regular User
87
+ INSERT INTO auth.users (
88
+ id,
89
+ email,
90
+ encrypted_password,
91
+ email_confirmed_at,
92
+ raw_user_meta_data
93
+ ) VALUES (
94
+ '00000000-0000-0000-0000-000000000002',
95
+ 'user@test.local',
96
+ crypt('testpassword123', gen_salt('bf')),
97
+ NOW(),
98
+ '{"full_name": "Test User"}'::JSONB
99
+ ) ON CONFLICT (id) DO NOTHING;
100
+
101
+ RAISE NOTICE 'Test users seeded successfully';
102
+ ELSE
103
+ RAISE NOTICE 'Skipping test user seeding - not a dev/test environment';
104
+ END IF;
105
+ END $$;
106
+
107
+ -- =============================================================================
108
+ -- SEED: SAMPLE DATA (Development only!)
109
+ -- =============================================================================
110
+
111
+ -- Example: Sample projects for testing
112
+ INSERT INTO :table_name (id, name, description, user_id, status, created_at)
113
+ VALUES
114
+ ('44444444-4444-4444-4444-444444444001', 'Sample Project 1', 'First sample project for testing', '00000000-0000-0000-0000-000000000001', 'active', NOW()),
115
+ ('44444444-4444-4444-4444-444444444002', 'Sample Project 2', 'Second sample project for testing', '00000000-0000-0000-0000-000000000002', 'draft', NOW())
116
+ ON CONFLICT (id) DO UPDATE SET
117
+ name = EXCLUDED.name,
118
+ description = EXCLUDED.description,
119
+ updated_at = NOW();
120
+
121
+ COMMIT;
122
+
123
+ -- =============================================================================
124
+ -- RE-ENABLE TRIGGERS
125
+ -- =============================================================================
126
+
127
+ -- SET session_replication_role = DEFAULT;
128
+
129
+ -- =============================================================================
130
+ -- VERIFICATION
131
+ -- =============================================================================
132
+
133
+ SELECT
134
+ 'status_types' as table_name,
135
+ COUNT(*) as row_count
136
+ FROM status_types
137
+ UNION ALL
138
+ SELECT 'categories', COUNT(*) FROM categories
139
+ UNION ALL
140
+ SELECT 'roles', COUNT(*) FROM roles;
@@ -1,16 +1,16 @@
1
- -- Basic post-migration checks (fill placeholders)
2
- SET client_min_messages = warning;
3
-
4
- -- Count tables/policies/functions expected
5
- SELECT COUNT(*) AS tables FROM information_schema.tables WHERE table_schema='public';
6
- SELECT COUNT(*) AS policies FROM pg_policies WHERE schemaname='public';
7
- SELECT proname FROM pg_proc WHERE pronamespace = 'public'::regnamespace
8
- AND proname IN ('current_mind_id','provision_user_profile','set_fragment_mind_id');
9
-
10
- -- Sanity queries (examples)
11
- -- SELECT * FROM categories LIMIT 5;
12
- -- SELECT * FROM minds LIMIT 5;
13
-
14
- -- RLS sanity (should not error even if it returns 0)
15
- -- SET LOCAL request.jwt.claims = '{"sub":"<user-uuid>","role":"authenticated"}';
16
- -- SELECT 1 FROM fragments WHERE false;
1
+ -- Basic post-migration checks (fill placeholders)
2
+ SET client_min_messages = warning;
3
+
4
+ -- Count tables/policies/functions expected
5
+ SELECT COUNT(*) AS tables FROM information_schema.tables WHERE table_schema='public';
6
+ SELECT COUNT(*) AS policies FROM pg_policies WHERE schemaname='public';
7
+ SELECT proname FROM pg_proc WHERE pronamespace = 'public'::regnamespace
8
+ AND proname IN ('current_mind_id','provision_user_profile','set_fragment_mind_id');
9
+
10
+ -- Sanity queries (examples)
11
+ -- SELECT * FROM categories LIMIT 5;
12
+ -- SELECT * FROM minds LIMIT 5;
13
+
14
+ -- RLS sanity (should not error even if it returns 0)
15
+ -- SET LOCAL request.jwt.claims = '{"sub":"<user-uuid>","role":"authenticated"}';
16
+ -- SELECT 1 FROM fragments WHERE false;
@@ -1,139 +1,139 @@
1
- -- Staging Copy Merge Template
2
- -- Purpose: Safely load external data through staging table and merge to target
3
- -- Created: :created_date
4
- -- Author: :author
5
- --
6
- -- This pattern prevents data corruption by validating in staging before merge
7
-
8
- -- =============================================================================
9
- -- STEP 1: CREATE STAGING TABLE (temporary)
10
- -- =============================================================================
11
-
12
- -- Create staging table mirroring target structure
13
- CREATE TEMP TABLE IF NOT EXISTS :staging_table (
14
- -- Match target table columns
15
- id UUID,
16
- :column1 :type1,
17
- :column2 :type2,
18
- -- Metadata for import tracking
19
- _row_number INTEGER,
20
- _import_status TEXT DEFAULT 'pending',
21
- _import_error TEXT
22
- );
23
-
24
- -- =============================================================================
25
- -- STEP 2: COPY DATA TO STAGING
26
- -- =============================================================================
27
-
28
- -- Option A: From CSV file
29
- -- COPY :staging_table (id, :column1, :column2)
30
- -- FROM '/path/to/file.csv'
31
- -- WITH (FORMAT csv, HEADER true);
32
-
33
- -- Option B: From STDIN (programmatic)
34
- -- COPY :staging_table (id, :column1, :column2) FROM STDIN;
35
-
36
- -- Option C: Insert from another query
37
- -- INSERT INTO :staging_table (id, :column1, :column2)
38
- -- SELECT id, col1, col2 FROM external_source;
39
-
40
- -- =============================================================================
41
- -- STEP 3: VALIDATE STAGING DATA
42
- -- =============================================================================
43
-
44
- -- Mark rows with validation errors
45
- UPDATE :staging_table
46
- SET
47
- _import_status = 'error',
48
- _import_error = 'Missing required field'
49
- WHERE :column1 IS NULL;
50
-
51
- -- Check for duplicates
52
- UPDATE :staging_table s
53
- SET
54
- _import_status = 'duplicate',
55
- _import_error = 'Duplicate ID found'
56
- WHERE EXISTS (
57
- SELECT 1 FROM :staging_table s2
58
- WHERE s2.id = s.id
59
- AND s2._row_number < s._row_number
60
- );
61
-
62
- -- Check for existing records (will update instead of insert)
63
- UPDATE :staging_table s
64
- SET _import_status = 'update'
65
- WHERE EXISTS (
66
- SELECT 1 FROM :target_table t
67
- WHERE t.id = s.id
68
- )
69
- AND s._import_status = 'pending';
70
-
71
- -- Mark remaining as new inserts
72
- UPDATE :staging_table
73
- SET _import_status = 'insert'
74
- WHERE _import_status = 'pending';
75
-
76
- -- =============================================================================
77
- -- STEP 4: REPORT VALIDATION RESULTS
78
- -- =============================================================================
79
-
80
- -- Get summary of staging data
81
- SELECT
82
- _import_status,
83
- COUNT(*) as count,
84
- STRING_AGG(DISTINCT _import_error, ', ') as errors
85
- FROM :staging_table
86
- GROUP BY _import_status
87
- ORDER BY _import_status;
88
-
89
- -- =============================================================================
90
- -- STEP 5: MERGE TO TARGET (UPSERT)
91
- -- =============================================================================
92
-
93
- BEGIN;
94
-
95
- -- Insert new records
96
- INSERT INTO :target_table (id, :column1, :column2, created_at)
97
- SELECT id, :column1, :column2, NOW()
98
- FROM :staging_table
99
- WHERE _import_status = 'insert';
100
-
101
- -- Update existing records
102
- UPDATE :target_table t
103
- SET
104
- :column1 = s.:column1,
105
- :column2 = s.:column2,
106
- updated_at = NOW()
107
- FROM :staging_table s
108
- WHERE t.id = s.id
109
- AND s._import_status = 'update';
110
-
111
- COMMIT;
112
-
113
- -- =============================================================================
114
- -- STEP 6: VERIFY AND CLEANUP
115
- -- =============================================================================
116
-
117
- -- Verify merge results
118
- SELECT
119
- 'inserted' as operation,
120
- COUNT(*) as count
121
- FROM :staging_table WHERE _import_status = 'insert'
122
- UNION ALL
123
- SELECT
124
- 'updated' as operation,
125
- COUNT(*) as count
126
- FROM :staging_table WHERE _import_status = 'update'
127
- UNION ALL
128
- SELECT
129
- 'errors' as operation,
130
- COUNT(*) as count
131
- FROM :staging_table WHERE _import_status = 'error';
132
-
133
- -- Get error details for review
134
- SELECT id, :column1, _import_error
135
- FROM :staging_table
136
- WHERE _import_status = 'error';
137
-
138
- -- Drop staging table (automatic for TEMP, but explicit is clearer)
139
- DROP TABLE IF EXISTS :staging_table;
1
+ -- Staging Copy Merge Template
2
+ -- Purpose: Safely load external data through staging table and merge to target
3
+ -- Created: :created_date
4
+ -- Author: :author
5
+ --
6
+ -- This pattern prevents data corruption by validating in staging before merge
7
+
8
+ -- =============================================================================
9
+ -- STEP 1: CREATE STAGING TABLE (temporary)
10
+ -- =============================================================================
11
+
12
+ -- Create staging table mirroring target structure
13
+ CREATE TEMP TABLE IF NOT EXISTS :staging_table (
14
+ -- Match target table columns
15
+ id UUID,
16
+ :column1 :type1,
17
+ :column2 :type2,
18
+ -- Metadata for import tracking
19
+ _row_number INTEGER,
20
+ _import_status TEXT DEFAULT 'pending',
21
+ _import_error TEXT
22
+ );
23
+
24
+ -- =============================================================================
25
+ -- STEP 2: COPY DATA TO STAGING
26
+ -- =============================================================================
27
+
28
+ -- Option A: From CSV file
29
+ -- COPY :staging_table (id, :column1, :column2)
30
+ -- FROM '/path/to/file.csv'
31
+ -- WITH (FORMAT csv, HEADER true);
32
+
33
+ -- Option B: From STDIN (programmatic)
34
+ -- COPY :staging_table (id, :column1, :column2) FROM STDIN;
35
+
36
+ -- Option C: Insert from another query
37
+ -- INSERT INTO :staging_table (id, :column1, :column2)
38
+ -- SELECT id, col1, col2 FROM external_source;
39
+
40
+ -- =============================================================================
41
+ -- STEP 3: VALIDATE STAGING DATA
42
+ -- =============================================================================
43
+
44
+ -- Mark rows with validation errors
45
+ UPDATE :staging_table
46
+ SET
47
+ _import_status = 'error',
48
+ _import_error = 'Missing required field'
49
+ WHERE :column1 IS NULL;
50
+
51
+ -- Check for duplicates
52
+ UPDATE :staging_table s
53
+ SET
54
+ _import_status = 'duplicate',
55
+ _import_error = 'Duplicate ID found'
56
+ WHERE EXISTS (
57
+ SELECT 1 FROM :staging_table s2
58
+ WHERE s2.id = s.id
59
+ AND s2._row_number < s._row_number
60
+ );
61
+
62
+ -- Check for existing records (will update instead of insert)
63
+ UPDATE :staging_table s
64
+ SET _import_status = 'update'
65
+ WHERE EXISTS (
66
+ SELECT 1 FROM :target_table t
67
+ WHERE t.id = s.id
68
+ )
69
+ AND s._import_status = 'pending';
70
+
71
+ -- Mark remaining as new inserts
72
+ UPDATE :staging_table
73
+ SET _import_status = 'insert'
74
+ WHERE _import_status = 'pending';
75
+
76
+ -- =============================================================================
77
+ -- STEP 4: REPORT VALIDATION RESULTS
78
+ -- =============================================================================
79
+
80
+ -- Get summary of staging data
81
+ SELECT
82
+ _import_status,
83
+ COUNT(*) as count,
84
+ STRING_AGG(DISTINCT _import_error, ', ') as errors
85
+ FROM :staging_table
86
+ GROUP BY _import_status
87
+ ORDER BY _import_status;
88
+
89
+ -- =============================================================================
90
+ -- STEP 5: MERGE TO TARGET (UPSERT)
91
+ -- =============================================================================
92
+
93
+ BEGIN;
94
+
95
+ -- Insert new records
96
+ INSERT INTO :target_table (id, :column1, :column2, created_at)
97
+ SELECT id, :column1, :column2, NOW()
98
+ FROM :staging_table
99
+ WHERE _import_status = 'insert';
100
+
101
+ -- Update existing records
102
+ UPDATE :target_table t
103
+ SET
104
+ :column1 = s.:column1,
105
+ :column2 = s.:column2,
106
+ updated_at = NOW()
107
+ FROM :staging_table s
108
+ WHERE t.id = s.id
109
+ AND s._import_status = 'update';
110
+
111
+ COMMIT;
112
+
113
+ -- =============================================================================
114
+ -- STEP 6: VERIFY AND CLEANUP
115
+ -- =============================================================================
116
+
117
+ -- Verify merge results
118
+ SELECT
119
+ 'inserted' as operation,
120
+ COUNT(*) as count
121
+ FROM :staging_table WHERE _import_status = 'insert'
122
+ UNION ALL
123
+ SELECT
124
+ 'updated' as operation,
125
+ COUNT(*) as count
126
+ FROM :staging_table WHERE _import_status = 'update'
127
+ UNION ALL
128
+ SELECT
129
+ 'errors' as operation,
130
+ COUNT(*) as count
131
+ FROM :staging_table WHERE _import_status = 'error';
132
+
133
+ -- Get error details for review
134
+ SELECT id, :column1, _import_error
135
+ FROM :staging_table
136
+ WHERE _import_status = 'error';
137
+
138
+ -- Drop staging table (automatic for TEMP, but explicit is clearer)
139
+ DROP TABLE IF EXISTS :staging_table;