@sixb/pg 0.1.1 → 0.1.2

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 (97) hide show
  1. package/README.md +7 -6
  2. package/dist/agents/agent-storage.d.ts +2 -1
  3. package/dist/agents/agent-storage.d.ts.map +1 -1
  4. package/dist/agents/rows.d.ts +4 -11
  5. package/dist/agents/rows.d.ts.map +1 -1
  6. package/dist/agents/runs.d.ts +3 -2
  7. package/dist/agents/runs.d.ts.map +1 -1
  8. package/dist/auth-storage/service-accounts.d.ts.map +1 -1
  9. package/dist/auth-storage/shared.d.ts +0 -4
  10. package/dist/auth-storage/shared.d.ts.map +1 -1
  11. package/dist/connector-connection-storage.d.ts +9 -0
  12. package/dist/connector-connection-storage.d.ts.map +1 -0
  13. package/dist/index.d.ts +8 -5
  14. package/dist/index.d.ts.map +1 -1
  15. package/dist/index.js +3086 -730
  16. package/dist/index.js.map +28 -25
  17. package/dist/latest-run-query.d.ts.map +1 -1
  18. package/dist/migrations.d.ts.map +1 -1
  19. package/dist/ontology-storage/materialization-session.d.ts.map +1 -1
  20. package/dist/ontology-storage/materialization-state.d.ts +0 -2
  21. package/dist/ontology-storage/materialization-state.d.ts.map +1 -1
  22. package/dist/ontology-storage/materialization-writer.d.ts.map +1 -1
  23. package/dist/ontology-storage/materializations.d.ts.map +1 -1
  24. package/dist/ontology-storage/outbox.d.ts.map +1 -1
  25. package/dist/ontology-storage/shared.d.ts +3 -6
  26. package/dist/ontology-storage/shared.d.ts.map +1 -1
  27. package/dist/pg-action-run-storage.d.ts +3 -2
  28. package/dist/pg-action-run-storage.d.ts.map +1 -1
  29. package/dist/pg-ai-usage-storage.d.ts +16 -0
  30. package/dist/pg-ai-usage-storage.d.ts.map +1 -0
  31. package/dist/pg-execution-storage.d.ts +14 -0
  32. package/dist/pg-execution-storage.d.ts.map +1 -0
  33. package/dist/pg-pipeline-run-storage.d.ts +5 -2
  34. package/dist/pg-pipeline-run-storage.d.ts.map +1 -1
  35. package/dist/pg-projection-run-storage.d.ts +5 -2
  36. package/dist/pg-projection-run-storage.d.ts.map +1 -1
  37. package/dist/pg-sync-run-storage.d.ts +5 -2
  38. package/dist/pg-sync-run-storage.d.ts.map +1 -1
  39. package/dist/pg-webhook-run-storage.d.ts +5 -2
  40. package/dist/pg-webhook-run-storage.d.ts.map +1 -1
  41. package/dist/pg-workflow-run-storage.d.ts +5 -3
  42. package/dist/pg-workflow-run-storage.d.ts.map +1 -1
  43. package/dist/run-list-query.d.ts +1 -1
  44. package/dist/run-list-query.d.ts.map +1 -1
  45. package/dist/testing.d.ts +6 -0
  46. package/dist/testing.d.ts.map +1 -0
  47. package/package.json +3 -3
  48. package/src/agents/agent-storage.ts +3 -2
  49. package/src/agents/rows.ts +12 -43
  50. package/src/agents/runs.ts +29 -15
  51. package/src/auth-storage/service-accounts.ts +14 -15
  52. package/src/auth-storage/shared.ts +0 -8
  53. package/src/connector-connection-storage.ts +760 -0
  54. package/src/index.ts +32 -15
  55. package/src/latest-run-query.ts +7 -1
  56. package/src/migrations/004-executions.sql +140 -0
  57. package/src/migrations/005-workflow-executions.sql +10 -0
  58. package/src/migrations/006-narrow-ontology-source-root-index.sql +4 -0
  59. package/src/migrations/007-split-overrides.sql +153 -0
  60. package/src/migrations/008-action-executions.sql +7 -0
  61. package/src/migrations/009-agent-executions.sql +21 -0
  62. package/src/migrations/010-ai-usage-accounting-foundation.sql +76 -0
  63. package/src/migrations/011-sync-failure-record.sql +23 -0
  64. package/src/migrations/012-pipeline-failure-record.sql +47 -0
  65. package/src/migrations/013-workflow-failure-record.sql +45 -0
  66. package/src/migrations/014-agent-failure-record.sql +56 -0
  67. package/src/migrations/015-projection-failure-record.sql +20 -0
  68. package/src/migrations/016-webhook-run-failure-record.sql +19 -0
  69. package/src/migrations/017-action-failure-record.sql +65 -0
  70. package/src/migrations/018-ontology-outbox-failure-record.sql +29 -0
  71. package/src/migrations/019-webhook-delivery-failure-record.sql +27 -0
  72. package/src/migrations/020-drop-run-usage-projections.sql +8 -0
  73. package/src/migrations/021-sync-pipeline-executions.sql +50 -0
  74. package/src/migrations/022-projection-executions.sql +115 -0
  75. package/src/migrations/023-webhook-executions.sql +30 -0
  76. package/src/migrations/024-ontology-commit-executions.sql +12 -0
  77. package/src/migrations/025-connector-connections.sql +231 -0
  78. package/src/migrations.ts +72 -0
  79. package/src/ontology-storage/materialization-session.ts +25 -28
  80. package/src/ontology-storage/materialization-state.ts +159 -43
  81. package/src/ontology-storage/materialization-writer.ts +146 -75
  82. package/src/ontology-storage/materializations.ts +3 -1
  83. package/src/ontology-storage/outbox.ts +9 -2
  84. package/src/ontology-storage/shared.ts +10 -20
  85. package/src/pg-action-run-storage.ts +43 -67
  86. package/src/pg-ai-usage-storage.ts +290 -0
  87. package/src/pg-execution-storage.ts +147 -0
  88. package/src/pg-pipeline-run-storage.ts +107 -41
  89. package/src/pg-projection-run-storage.ts +159 -91
  90. package/src/pg-sync-run-storage.ts +100 -28
  91. package/src/pg-webhook-run-storage.ts +89 -57
  92. package/src/pg-workflow-run-storage.ts +97 -130
  93. package/src/run-list-query.ts +9 -4
  94. package/src/testing.ts +18 -0
  95. package/dist/pg-webhook-delivery-storage.d.ts +0 -17
  96. package/dist/pg-webhook-delivery-storage.d.ts.map +0 -1
  97. package/src/pg-webhook-delivery-storage.ts +0 -180
@@ -0,0 +1,45 @@
1
+ ALTER TABLE workflow_runs RENAME COLUMN error TO legacy_error;
2
+ ALTER TABLE workflow_runs ADD COLUMN error JSONB;
3
+
4
+ UPDATE workflow_runs
5
+ SET error = jsonb_build_object(
6
+ 'code', CASE WHEN status = 'cancelled' THEN 'runtime.cancelled' ELSE 'internal.unexpected' END,
7
+ 'message', CASE
8
+ WHEN status = 'cancelled' THEN 'Execution was cancelled.'
9
+ ELSE 'An unexpected internal error occurred.'
10
+ END,
11
+ 'retryable', false,
12
+ 'at', to_char(COALESCE(finished_at, started_at) AT TIME ZONE 'UTC', 'YYYY-MM-DD"T"HH24:MI:SS.MS"Z"'),
13
+ 'details', jsonb_build_object(
14
+ 'workflowId', workflow_id,
15
+ 'workflowRunId', id,
16
+ 'migratedFromLegacyError', true
17
+ )
18
+ )
19
+ WHERE legacy_error IS NOT NULL;
20
+
21
+ ALTER TABLE workflow_runs DROP COLUMN legacy_error;
22
+
23
+ ALTER TABLE workflow_node_runs RENAME COLUMN error TO legacy_error;
24
+ ALTER TABLE workflow_node_runs ADD COLUMN error JSONB;
25
+
26
+ UPDATE workflow_node_runs
27
+ SET error = jsonb_build_object(
28
+ 'code', CASE WHEN status = 'cancelled' THEN 'runtime.cancelled' ELSE 'internal.unexpected' END,
29
+ 'message', CASE
30
+ WHEN status = 'cancelled' THEN 'Execution was cancelled.'
31
+ ELSE 'An unexpected internal error occurred.'
32
+ END,
33
+ 'retryable', false,
34
+ 'at', to_char(COALESCE(finished_at, started_at) AT TIME ZONE 'UTC', 'YYYY-MM-DD"T"HH24:MI:SS.MS"Z"'),
35
+ 'details', jsonb_build_object(
36
+ 'workflowId', workflow_id,
37
+ 'workflowRunId', workflow_run_id,
38
+ 'nodeId', node_id,
39
+ 'nodeRunId', id,
40
+ 'migratedFromLegacyError', true
41
+ )
42
+ )
43
+ WHERE legacy_error IS NOT NULL;
44
+
45
+ ALTER TABLE workflow_node_runs DROP COLUMN legacy_error;
@@ -0,0 +1,56 @@
1
+ ALTER TABLE agent_runs RENAME COLUMN error TO legacy_error;
2
+ ALTER TABLE agent_runs ADD COLUMN error JSONB;
3
+
4
+ UPDATE agent_runs
5
+ SET error = jsonb_build_object(
6
+ 'code', CASE WHEN status = 'cancelled' THEN 'runtime.cancelled' ELSE 'internal.unexpected' END,
7
+ 'message', CASE
8
+ WHEN status = 'cancelled' THEN 'Execution was cancelled.'
9
+ ELSE 'An unexpected internal error occurred.'
10
+ END,
11
+ 'retryable', false,
12
+ 'at', to_char(COALESCE(completed_at, started_at, created_at) AT TIME ZONE 'UTC', 'YYYY-MM-DD"T"HH24:MI:SS.MS"Z"'),
13
+ 'details', jsonb_build_object(
14
+ 'agentId', agent_id,
15
+ 'runId', id,
16
+ 'threadId', thread_id,
17
+ 'migratedFromLegacyError', true
18
+ )
19
+ )
20
+ WHERE legacy_error IS NOT NULL;
21
+
22
+ ALTER TABLE agent_runs DROP COLUMN legacy_error;
23
+
24
+ ALTER TABLE workflow_agent_node_runs RENAME COLUMN error TO legacy_error;
25
+ ALTER TABLE workflow_agent_node_runs ADD COLUMN error JSONB;
26
+
27
+ UPDATE workflow_agent_node_runs AS agent_node
28
+ SET error = jsonb_build_object(
29
+ 'code', CASE
30
+ WHEN agent_node.status = 'cancelled' THEN 'runtime.cancelled'
31
+ ELSE 'internal.unexpected'
32
+ END,
33
+ 'message', CASE
34
+ WHEN agent_node.status = 'cancelled' THEN 'Execution was cancelled.'
35
+ ELSE 'An unexpected internal error occurred.'
36
+ END,
37
+ 'retryable', false,
38
+ 'at', to_char(
39
+ COALESCE(agent_node.completed_at, agent_node.started_at, agent_node.created_at) AT TIME ZONE 'UTC',
40
+ 'YYYY-MM-DD"T"HH24:MI:SS.MS"Z"'
41
+ ),
42
+ 'details', jsonb_build_object(
43
+ 'agentId', agent_node.agent_id,
44
+ 'workflowId', node.workflow_id,
45
+ 'workflowRunId', node.workflow_run_id,
46
+ 'nodeId', node.node_id,
47
+ 'nodeRunId', agent_node.node_run_id,
48
+ 'migratedFromLegacyError', true
49
+ )
50
+ )
51
+ FROM workflow_node_runs AS node
52
+ WHERE node.project_id = agent_node.project_id
53
+ AND node.id = agent_node.node_run_id
54
+ AND agent_node.legacy_error IS NOT NULL;
55
+
56
+ ALTER TABLE workflow_agent_node_runs DROP COLUMN legacy_error;
@@ -0,0 +1,20 @@
1
+ ALTER TABLE projection_runs ADD COLUMN error JSONB;
2
+
3
+ UPDATE projection_runs
4
+ SET error = jsonb_build_object(
5
+ 'code', CASE WHEN status = 'cancelled' THEN 'runtime.cancelled' ELSE 'internal.unexpected' END,
6
+ 'message', CASE
7
+ WHEN status = 'cancelled' THEN 'Execution was cancelled.'
8
+ ELSE 'An unexpected internal error occurred.'
9
+ END,
10
+ 'retryable', false,
11
+ 'at', to_char(COALESCE(finished_at, started_at) AT TIME ZONE 'UTC', 'YYYY-MM-DD"T"HH24:MI:SS.MS"Z"'),
12
+ 'details', jsonb_build_object(
13
+ 'projectionId', projection_id,
14
+ 'runId', id,
15
+ 'migratedFromLegacyError', true
16
+ )
17
+ )
18
+ WHERE error_message IS NOT NULL;
19
+
20
+ ALTER TABLE projection_runs DROP COLUMN error_message;
@@ -0,0 +1,19 @@
1
+ ALTER TABLE webhook_runs RENAME COLUMN error TO legacy_error;
2
+ ALTER TABLE webhook_runs ADD COLUMN error JSONB;
3
+
4
+ UPDATE webhook_runs
5
+ SET error = jsonb_build_object(
6
+ 'code', 'internal.unexpected',
7
+ 'message', 'An unexpected internal error occurred.',
8
+ 'retryable', false,
9
+ 'at', to_char(COALESCE(finished_at, started_at) AT TIME ZONE 'UTC', 'YYYY-MM-DD"T"HH24:MI:SS.MS"Z"'),
10
+ 'details', jsonb_build_object(
11
+ 'connectorId', connector_id,
12
+ 'webhookId', webhook_id,
13
+ 'runId', id,
14
+ 'migratedFromLegacyError', true
15
+ )
16
+ )
17
+ WHERE legacy_error IS NOT NULL;
18
+
19
+ ALTER TABLE webhook_runs DROP COLUMN legacy_error;
@@ -0,0 +1,65 @@
1
+ ALTER TABLE action_runs
2
+ ADD COLUMN writeback_error JSONB,
3
+ ADD COLUMN effects_error JSONB,
4
+ ADD COLUMN error JSONB;
5
+
6
+ UPDATE action_runs
7
+ SET writeback_error = jsonb_build_object(
8
+ 'code', 'internal.unexpected',
9
+ 'message', 'An unexpected internal error occurred.',
10
+ 'retryable', false,
11
+ 'at', to_char(COALESCE(writeback_completed_at, finished_at, queued_at) AT TIME ZONE 'UTC', 'YYYY-MM-DD"T"HH24:MI:SS.MS"Z"'),
12
+ 'details', jsonb_build_object(
13
+ 'actionId', action_id,
14
+ 'runId', id,
15
+ 'phase', 'writeback'
16
+ )
17
+ )
18
+ WHERE writeback_error_name IS NOT NULL OR writeback_error_message IS NOT NULL;
19
+
20
+ UPDATE action_runs
21
+ SET effects_error = jsonb_build_object(
22
+ 'code', 'internal.unexpected',
23
+ 'message', 'An unexpected internal error occurred.',
24
+ 'retryable', false,
25
+ 'at', to_char(COALESCE(effects_completed_at, finished_at, queued_at) AT TIME ZONE 'UTC', 'YYYY-MM-DD"T"HH24:MI:SS.MS"Z"'),
26
+ 'details', jsonb_build_object(
27
+ 'actionId', action_id,
28
+ 'runId', id,
29
+ 'phase', 'effects'
30
+ )
31
+ )
32
+ WHERE effects_error_name IS NOT NULL OR effects_error_message IS NOT NULL;
33
+
34
+ UPDATE action_runs
35
+ SET error = jsonb_build_object(
36
+ 'code', CASE
37
+ WHEN error_phase = 'enqueue' THEN 'queue.enqueue_failed'
38
+ WHEN error_phase = 'cancelled' OR status = 'cancelled' THEN 'runtime.cancelled'
39
+ ELSE 'internal.unexpected'
40
+ END,
41
+ 'message', CASE
42
+ WHEN error_phase = 'enqueue' THEN 'The job could not be enqueued.'
43
+ WHEN error_phase = 'cancelled' OR status = 'cancelled' THEN 'Execution was cancelled.'
44
+ ELSE 'An unexpected internal error occurred.'
45
+ END,
46
+ 'retryable', COALESCE(error_phase = 'enqueue', false),
47
+ 'at', to_char(COALESCE(finished_at, queued_at) AT TIME ZONE 'UTC', 'YYYY-MM-DD"T"HH24:MI:SS.MS"Z"'),
48
+ 'details', jsonb_build_object(
49
+ 'actionId', action_id,
50
+ 'runId', id,
51
+ 'phase', COALESCE(error_phase, phase, 'request')
52
+ )
53
+ )
54
+ WHERE error_name IS NOT NULL OR error_message IS NOT NULL;
55
+
56
+ ALTER TABLE action_runs
57
+ DROP COLUMN writeback_error_name,
58
+ DROP COLUMN writeback_error_message,
59
+ DROP COLUMN writeback_error_phase,
60
+ DROP COLUMN effects_error_name,
61
+ DROP COLUMN effects_error_message,
62
+ DROP COLUMN effects_error_phase,
63
+ DROP COLUMN error_name,
64
+ DROP COLUMN error_message,
65
+ DROP COLUMN error_phase;
@@ -0,0 +1,29 @@
1
+ -- The legacy record has no failure timestamp. Use its next availability time as the closest
2
+ -- durable timestamp and mark that approximation explicitly in details.
3
+ ALTER TABLE ontology_outbox RENAME COLUMN last_error TO last_failure;
4
+
5
+ ALTER TABLE ontology_outbox
6
+ ALTER COLUMN last_failure TYPE JSONB
7
+ USING (
8
+ CASE
9
+ WHEN last_failure IS NULL
10
+ OR last_failure = 'Outbox dispatcher stopped before publication completed.'
11
+ THEN NULL
12
+ ELSE jsonb_build_object(
13
+ 'code', 'event.delivery_failed',
14
+ 'message', 'Event delivery failed.',
15
+ 'retryable', true,
16
+ 'at', to_char(
17
+ available_at AT TIME ZONE 'UTC',
18
+ 'YYYY-MM-DD"T"HH24:MI:SS.MS"Z"'
19
+ ),
20
+ 'details', jsonb_build_object(
21
+ 'attempts', attempts,
22
+ 'eventIds', jsonb_build_array(id),
23
+ 'eventTypes', jsonb_build_array(envelope->>'type'),
24
+ 'migratedFromLegacyLastError', true,
25
+ 'timestampSource', 'availableAt'
26
+ )
27
+ )
28
+ END
29
+ );
@@ -0,0 +1,27 @@
1
+ -- Legacy deliveries normally have an exact failure timestamp. Fall back to the receipt timestamp
2
+ -- only for inconsistent historical rows and record the source so the approximation stays visible.
3
+ ALTER TABLE webhook_deliveries RENAME COLUMN error TO failure;
4
+
5
+ ALTER TABLE webhook_deliveries
6
+ ALTER COLUMN failure TYPE JSONB
7
+ USING (
8
+ CASE
9
+ WHEN failure IS NULL THEN NULL
10
+ ELSE jsonb_build_object(
11
+ 'code', 'webhook.delivery_failed',
12
+ 'message', 'Webhook delivery failed.',
13
+ 'retryable', true,
14
+ 'at', to_char(
15
+ COALESCE(failed_at, received_at) AT TIME ZONE 'UTC',
16
+ 'YYYY-MM-DD"T"HH24:MI:SS.MS"Z"'
17
+ ),
18
+ 'details', jsonb_build_object(
19
+ 'connectorId', connector_id,
20
+ 'webhookId', webhook_id,
21
+ 'idempotencyKey', idempotency_key,
22
+ 'migratedFromLegacyError', true,
23
+ 'timestampSource', CASE WHEN failed_at IS NULL THEN 'receivedAt' ELSE 'failedAt' END
24
+ )
25
+ )
26
+ END
27
+ );
@@ -0,0 +1,8 @@
1
+ ALTER TABLE agent_runs
2
+ DROP COLUMN usage_input_tokens,
3
+ DROP COLUMN usage_output_tokens,
4
+ DROP COLUMN usage_total_tokens,
5
+ DROP COLUMN usage_reasoning_tokens,
6
+ DROP COLUMN usage_cached_input_tokens;
7
+
8
+ ALTER TABLE workflow_agent_node_runs DROP COLUMN usage;
@@ -0,0 +1,50 @@
1
+ ALTER TABLE sync_runs
2
+ DROP CONSTRAINT sync_runs_status_check,
3
+ ADD COLUMN execution_id TEXT NOT NULL,
4
+ ADD COLUMN queued_at TIMESTAMPTZ NOT NULL,
5
+ ALTER COLUMN started_at DROP NOT NULL,
6
+ ADD CONSTRAINT sync_runs_status_check
7
+ CHECK (status IN ('queued', 'running', 'succeeded', 'failed', 'cancelled')),
8
+ ADD CONSTRAINT fk_sync_runs_execution
9
+ FOREIGN KEY (project_id, execution_id)
10
+ REFERENCES executions (project_id, id)
11
+ ON DELETE RESTRICT,
12
+ ADD CONSTRAINT uq_sync_runs_execution UNIQUE (project_id, execution_id);
13
+
14
+ DROP INDEX idx_sync_runs_project_started;
15
+ DROP INDEX idx_sync_runs_project_dataset_started;
16
+ DROP INDEX idx_sync_runs_project_sync_started;
17
+ DROP INDEX idx_sync_runs_project_status_started;
18
+
19
+ CREATE INDEX idx_sync_runs_project_started
20
+ ON sync_runs (project_id, COALESCE(started_at, queued_at) DESC);
21
+ CREATE INDEX idx_sync_runs_project_dataset_started
22
+ ON sync_runs (project_id, dataset_id, COALESCE(started_at, queued_at) DESC);
23
+ CREATE INDEX idx_sync_runs_project_sync_started
24
+ ON sync_runs (project_id, sync_id, COALESCE(started_at, queued_at) DESC);
25
+ CREATE INDEX idx_sync_runs_project_status_started
26
+ ON sync_runs (project_id, status, COALESCE(started_at, queued_at) DESC);
27
+
28
+ ALTER TABLE pipeline_runs
29
+ DROP CONSTRAINT pipeline_runs_status_check,
30
+ ADD COLUMN execution_id TEXT NOT NULL,
31
+ ADD COLUMN queued_at TIMESTAMPTZ NOT NULL,
32
+ ALTER COLUMN started_at DROP NOT NULL,
33
+ ADD CONSTRAINT pipeline_runs_status_check
34
+ CHECK (status IN ('queued', 'running', 'succeeded', 'failed', 'cancelled')),
35
+ ADD CONSTRAINT fk_pipeline_runs_execution
36
+ FOREIGN KEY (project_id, execution_id)
37
+ REFERENCES executions (project_id, id)
38
+ ON DELETE RESTRICT,
39
+ ADD CONSTRAINT uq_pipeline_runs_execution UNIQUE (project_id, execution_id);
40
+
41
+ DROP INDEX idx_pipeline_runs_project_started;
42
+ DROP INDEX idx_pipeline_runs_project_pipeline_started;
43
+ DROP INDEX idx_pipeline_runs_project_status_started;
44
+
45
+ CREATE INDEX idx_pipeline_runs_project_started
46
+ ON pipeline_runs (project_id, COALESCE(started_at, queued_at) DESC);
47
+ CREATE INDEX idx_pipeline_runs_project_pipeline_started
48
+ ON pipeline_runs (project_id, pipeline_id, COALESCE(started_at, queued_at) DESC);
49
+ CREATE INDEX idx_pipeline_runs_project_status_started
50
+ ON pipeline_runs (project_id, status, COALESCE(started_at, queued_at) DESC);
@@ -0,0 +1,115 @@
1
+ ALTER TABLE executions
2
+ DROP CONSTRAINT executions_source_kind_check,
3
+ ADD CONSTRAINT executions_source_kind_check CHECK (
4
+ source_kind IN ('http', 'webhook', 'schedule', 'event', 'datasetVersion', 'execution')
5
+ );
6
+
7
+ -- Existing Projection rows cannot be assigned honest authority. Refuse them explicitly before
8
+ -- rebuilding anything; this guard is migration-local and leaves no function or trigger behind.
9
+ CREATE TEMP TABLE projection_execution_migration_guard (
10
+ must_be_empty BOOLEAN NOT NULL CHECK (must_be_empty = FALSE)
11
+ ) ON COMMIT DROP;
12
+ INSERT INTO projection_execution_migration_guard (must_be_empty)
13
+ SELECT TRUE FROM projection_runs LIMIT 1;
14
+ DROP TABLE projection_execution_migration_guard;
15
+
16
+ DROP TABLE projection_runs;
17
+
18
+ CREATE TABLE projection_runs (
19
+ project_id TEXT NOT NULL,
20
+ id TEXT NOT NULL,
21
+ execution_id TEXT NOT NULL,
22
+ projection_id TEXT NOT NULL,
23
+ projection_kind TEXT NOT NULL CHECK (projection_kind IN ('object', 'link', 'telemetry')),
24
+ materialization_protocol TEXT NOT NULL CHECK (
25
+ materialization_protocol IN ('replacement', 'telemetry')
26
+ ),
27
+ dataset_id TEXT NOT NULL,
28
+ dataset_version_id TEXT NOT NULL,
29
+ dataset_version_created_at TEXT NOT NULL,
30
+ ontology_revision TEXT NOT NULL,
31
+ projection_revision TEXT NOT NULL,
32
+ ownership_hash TEXT NOT NULL,
33
+ object_type_id TEXT,
34
+ source_object_type_id TEXT,
35
+ target_object_type_id TEXT,
36
+ status TEXT NOT NULL CHECK (status IN ('queued', 'running', 'succeeded', 'failed', 'cancelled')),
37
+ queued_at TIMESTAMPTZ NOT NULL,
38
+ started_at TIMESTAMPTZ,
39
+ finished_at TIMESTAMPTZ,
40
+ attempt BIGINT NOT NULL CHECK (attempt >= 0),
41
+ execution_token TEXT,
42
+ fixed_batch_size BIGINT CHECK (fixed_batch_size IS NULL OR fixed_batch_size > 0),
43
+ next_batch_ordinal BIGINT CHECK (next_batch_ordinal IS NULL OR next_batch_ordinal >= 0),
44
+ next_row_offset BIGINT CHECK (next_row_offset IS NULL OR next_row_offset >= 0),
45
+ input_exhausted BOOLEAN,
46
+ missing_target_object_type_id TEXT,
47
+ missing_target_object_id TEXT,
48
+ missing_target_batch_ordinal BIGINT CHECK (
49
+ missing_target_batch_ordinal IS NULL OR missing_target_batch_ordinal >= 0
50
+ ),
51
+ missing_target_first_seen_at TIMESTAMPTZ,
52
+ source_rows_read BIGINT NOT NULL DEFAULT 0 CHECK (source_rows_read >= 0),
53
+ source_rows_skipped BIGINT NOT NULL DEFAULT 0 CHECK (source_rows_skipped >= 0),
54
+ error JSONB,
55
+ PRIMARY KEY (project_id, id),
56
+ UNIQUE (project_id, execution_id),
57
+ FOREIGN KEY (project_id, execution_id)
58
+ REFERENCES executions (project_id, id)
59
+ ON DELETE RESTRICT,
60
+ CHECK (source_rows_skipped <= source_rows_read),
61
+ CHECK ((status = 'running') = (execution_token IS NOT NULL)),
62
+ CHECK (
63
+ (status = 'queued' AND attempt = 0 AND started_at IS NULL AND finished_at IS NULL
64
+ AND error IS NULL)
65
+ OR (status = 'running' AND attempt >= 1 AND started_at IS NOT NULL AND finished_at IS NULL
66
+ AND error IS NULL)
67
+ OR (
68
+ status IN ('succeeded', 'failed', 'cancelled')
69
+ AND finished_at IS NOT NULL
70
+ AND (
71
+ (error->>'code' = 'queue.enqueue_failed'
72
+ AND status = 'failed' AND attempt = 0 AND started_at IS NULL)
73
+ OR (COALESCE(error->>'code', '') != 'queue.enqueue_failed'
74
+ AND attempt >= 1 AND started_at IS NOT NULL)
75
+ )
76
+ )
77
+ ),
78
+ CHECK (status != 'succeeded' OR error IS NULL),
79
+ CHECK ((projection_kind = 'telemetry') = (materialization_protocol = 'telemetry')),
80
+ CHECK (
81
+ (projection_kind = 'link' AND object_type_id IS NULL
82
+ AND source_object_type_id IS NOT NULL AND target_object_type_id IS NOT NULL)
83
+ OR (projection_kind IN ('object', 'telemetry') AND object_type_id IS NOT NULL
84
+ AND source_object_type_id IS NULL AND target_object_type_id IS NULL)
85
+ ),
86
+ CHECK (
87
+ (materialization_protocol = 'replacement' AND fixed_batch_size IS NULL
88
+ AND next_batch_ordinal IS NULL AND next_row_offset IS NULL AND input_exhausted IS NULL)
89
+ OR (materialization_protocol = 'telemetry' AND fixed_batch_size IS NOT NULL
90
+ AND next_batch_ordinal IS NOT NULL AND next_row_offset IS NOT NULL
91
+ AND input_exhausted IS NOT NULL)
92
+ ),
93
+ CHECK (
94
+ (missing_target_object_type_id IS NULL AND missing_target_object_id IS NULL
95
+ AND missing_target_batch_ordinal IS NULL AND missing_target_first_seen_at IS NULL)
96
+ OR (projection_kind = 'telemetry' AND missing_target_object_type_id = object_type_id
97
+ AND missing_target_object_id IS NOT NULL
98
+ AND missing_target_batch_ordinal = next_batch_ordinal
99
+ AND missing_target_first_seen_at IS NOT NULL)
100
+ ),
101
+ CHECK (projection_kind != 'telemetry' OR status != 'succeeded' OR input_exhausted = TRUE)
102
+ );
103
+
104
+ CREATE INDEX idx_projection_runs_project_started
105
+ ON projection_runs (project_id, COALESCE(started_at, queued_at) DESC);
106
+ CREATE INDEX idx_projection_runs_project_projection_started
107
+ ON projection_runs (project_id, projection_id, COALESCE(started_at, queued_at) DESC);
108
+ CREATE INDEX idx_projection_runs_project_dataset_started
109
+ ON projection_runs (project_id, dataset_id, COALESCE(started_at, queued_at) DESC);
110
+ CREATE INDEX idx_projection_runs_project_version_started
111
+ ON projection_runs (project_id, dataset_version_id, COALESCE(started_at, queued_at) DESC);
112
+ CREATE INDEX idx_projection_runs_project_status_started
113
+ ON projection_runs (project_id, status, COALESCE(started_at, queued_at) DESC);
114
+ CREATE INDEX idx_projection_runs_project_object_type_started
115
+ ON projection_runs (project_id, object_type_id, COALESCE(started_at, queued_at) DESC);
@@ -0,0 +1,30 @@
1
+ -- Existing Webhook runs and delivery claims cannot be assigned honest authority retroactively.
2
+ -- Fail before changing the schema instead of inventing execution provenance or discarding claims.
3
+ SELECT 1 / CASE
4
+ WHEN EXISTS (SELECT 1 FROM webhook_runs) OR EXISTS (SELECT 1 FROM webhook_deliveries)
5
+ THEN 0
6
+ ELSE 1
7
+ END;
8
+
9
+ ALTER TABLE webhook_runs
10
+ DROP CONSTRAINT webhook_runs_status_check,
11
+ ADD COLUMN execution_id TEXT NOT NULL,
12
+ ADD COLUMN request_body_sha256 TEXT NOT NULL CHECK (length(request_body_sha256) = 64),
13
+ ALTER COLUMN request_body_bytes SET NOT NULL,
14
+ DROP COLUMN delivery_claim_result,
15
+ ADD CONSTRAINT webhook_runs_status_check
16
+ CHECK (status IN ('running', 'succeeded', 'failed')),
17
+ ADD CONSTRAINT webhook_runs_lifecycle_check CHECK (
18
+ (status = 'running' AND finished_at IS NULL AND response_status IS NULL AND error IS NULL)
19
+ OR (status = 'succeeded' AND finished_at IS NOT NULL AND error IS NULL)
20
+ OR (status = 'failed' AND finished_at IS NOT NULL AND error IS NOT NULL)
21
+ ),
22
+ ADD CONSTRAINT fk_webhook_runs_execution
23
+ FOREIGN KEY (project_id, execution_id)
24
+ REFERENCES executions (project_id, id)
25
+ ON DELETE RESTRICT,
26
+ ADD CONSTRAINT uq_webhook_runs_execution UNIQUE (project_id, execution_id),
27
+ ADD CONSTRAINT uq_webhook_runs_delivery
28
+ UNIQUE (project_id, connector_id, webhook_id, idempotency_key);
29
+
30
+ DROP TABLE webhook_deliveries;
@@ -0,0 +1,12 @@
1
+ -- Existing commits cannot be assigned honest execution authority retroactively.
2
+ SELECT 1 / CASE WHEN EXISTS (SELECT 1 FROM ontology_commits) THEN 0 ELSE 1 END;
3
+
4
+ ALTER TABLE ontology_commits
5
+ ADD COLUMN execution_id TEXT NOT NULL,
6
+ ADD CONSTRAINT fk_ontology_commits_execution
7
+ FOREIGN KEY (project_id, execution_id)
8
+ REFERENCES executions (project_id, id)
9
+ ON DELETE RESTRICT;
10
+
11
+ CREATE INDEX idx_ontology_commits_execution
12
+ ON ontology_commits (project_id, execution_id);