sediment-core 0.1.0__py3-none-any.whl

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.
@@ -0,0 +1,144 @@
1
+ # SPDX-License-Identifier: AGPL-3.0-or-later
2
+ """Sediment core: fact models and the PostgreSQL fact store."""
3
+
4
+ from .models import (
5
+ BranchName,
6
+ CIOutcome,
7
+ CIReason,
8
+ CI_REASON_MAX_LENGTH,
9
+ CIProvider,
10
+ CIResult,
11
+ CommitSha,
12
+ AgentHarness,
13
+ DeveloperDecision,
14
+ EditObservation,
15
+ RejectedEdit,
16
+ RetryLinkage,
17
+ InteractionMode,
18
+ FactTable,
19
+ ForgeProvider,
20
+ ForgeHost,
21
+ ProviderRepositoryId,
22
+ RepositoryRename,
23
+ GatewayProvider,
24
+ InferenceCall,
25
+ InferenceMessage,
26
+ InferenceMessagePart,
27
+ ModelName,
28
+ NonEmptyId,
29
+ NonEmptyContent,
30
+ ScalarIdentity,
31
+ OrgId,
32
+ Push,
33
+ SessionCommitObservation,
34
+ PullRequestMerge,
35
+ PullRequestRevision,
36
+ QuarantineAction,
37
+ QuarantineRecord,
38
+ ReasoningPart,
39
+ RepoSlug,
40
+ RequiredRepoSlug,
41
+ ToolCallPart,
42
+ ToolCallResponsePart,
43
+ TextPart,
44
+ UNKNOWN_USER_ID,
45
+ WorkflowName,
46
+ WorkflowPath,
47
+ normalize_commit_sha,
48
+ normalize_repo_slug,
49
+ )
50
+ from .org import normalize_org_id
51
+ from .redaction import REDACTION_MARKER, RedactionReason, redact_fact
52
+ from .store import (
53
+ COMPOSITE_FILTER_KEY_LIMIT,
54
+ FactStore,
55
+ InferenceCallIdentityConflict,
56
+ InferenceCallReceipt,
57
+ RepositoryFactReceipt,
58
+ RepositoryIdentityConflict,
59
+ RepositoryIdentityEvidence,
60
+ RepositoryReadAmbiguous,
61
+ RepositoryReadKey,
62
+ RepositoryCommitReadKey,
63
+ RepositoryPRReadKey,
64
+ REPOSITORY_FILTER_KEY_LIMIT,
65
+ REPOSITORY_IDENTITY_LIMIT,
66
+ OperationalReportLimitExceeded,
67
+ ReportInferenceCall,
68
+ INFERENCE_CALL_ROW_BYTES_LIMIT,
69
+ INFERENCE_REPORT_ROW_LIMIT,
70
+ INFERENCE_SESSION_BYTES_LIMIT,
71
+ INFERENCE_PROJECTION_BYTES_LIMIT,
72
+ validate_composite_filter_keys,
73
+ )
74
+
75
+ __all__ = [
76
+ "BranchName",
77
+ "CIOutcome",
78
+ "CIReason",
79
+ "CI_REASON_MAX_LENGTH",
80
+ "CIProvider",
81
+ "CIResult",
82
+ "COMPOSITE_FILTER_KEY_LIMIT",
83
+ "CommitSha",
84
+ "AgentHarness",
85
+ "DeveloperDecision",
86
+ "EditObservation",
87
+ "RejectedEdit",
88
+ "RetryLinkage",
89
+ "InteractionMode",
90
+ "FactStore",
91
+ "FactTable",
92
+ "ForgeProvider",
93
+ "ForgeHost",
94
+ "ProviderRepositoryId",
95
+ "RepositoryRename",
96
+ "GatewayProvider",
97
+ "InferenceCall",
98
+ "InferenceCallIdentityConflict",
99
+ "InferenceCallReceipt",
100
+ "RepositoryFactReceipt",
101
+ "RepositoryIdentityConflict",
102
+ "RepositoryIdentityEvidence",
103
+ "RepositoryReadAmbiguous",
104
+ "RepositoryReadKey",
105
+ "RepositoryCommitReadKey",
106
+ "RepositoryPRReadKey",
107
+ "REPOSITORY_FILTER_KEY_LIMIT",
108
+ "REPOSITORY_IDENTITY_LIMIT",
109
+ "InferenceMessage",
110
+ "InferenceMessagePart",
111
+ "ModelName",
112
+ "NonEmptyId",
113
+ "NonEmptyContent",
114
+ "ScalarIdentity",
115
+ "OperationalReportLimitExceeded",
116
+ "ReportInferenceCall",
117
+ "INFERENCE_CALL_ROW_BYTES_LIMIT",
118
+ "INFERENCE_REPORT_ROW_LIMIT",
119
+ "INFERENCE_SESSION_BYTES_LIMIT",
120
+ "INFERENCE_PROJECTION_BYTES_LIMIT",
121
+ "OrgId",
122
+ "Push",
123
+ "SessionCommitObservation",
124
+ "PullRequestMerge",
125
+ "PullRequestRevision",
126
+ "QuarantineAction",
127
+ "QuarantineRecord",
128
+ "REDACTION_MARKER",
129
+ "RedactionReason",
130
+ "ReasoningPart",
131
+ "RepoSlug",
132
+ "RequiredRepoSlug",
133
+ "ToolCallPart",
134
+ "ToolCallResponsePart",
135
+ "TextPart",
136
+ "UNKNOWN_USER_ID",
137
+ "WorkflowName",
138
+ "WorkflowPath",
139
+ "normalize_commit_sha",
140
+ "normalize_org_id",
141
+ "normalize_repo_slug",
142
+ "redact_fact",
143
+ "validate_composite_filter_keys",
144
+ ]
@@ -0,0 +1,2 @@
1
+ # SPDX-License-Identifier: AGPL-3.0-or-later
2
+ """Alembic migration environment for the PostgreSQL fact store."""
@@ -0,0 +1,25 @@
1
+ # SPDX-License-Identifier: AGPL-3.0-or-later
2
+ """Run Alembic migrations on the connection supplied by Sediment."""
3
+
4
+ from __future__ import annotations
5
+
6
+ from alembic import context
7
+
8
+ from sediment_core.postgres_schema import metadata
9
+
10
+
11
+ def run_migrations() -> None:
12
+ connection = context.config.attributes.get("connection")
13
+ if connection is None:
14
+ raise RuntimeError("Sediment must supply an Alembic connection")
15
+ context.configure(
16
+ connection=connection,
17
+ target_metadata=metadata,
18
+ compare_type=True,
19
+ transaction_per_migration=True,
20
+ )
21
+ with context.begin_transaction():
22
+ context.run_migrations()
23
+
24
+
25
+ run_migrations()
@@ -0,0 +1,500 @@
1
+ # SPDX-License-Identifier: AGPL-3.0-or-later
2
+ """Create the frozen PostgreSQL fact-store baseline.
3
+
4
+ Revision ID: 0001_postgresql_baseline
5
+ Revises:
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ from alembic import op
11
+ import sqlalchemy as sa
12
+
13
+ revision = "0001_postgresql_baseline"
14
+ down_revision = None
15
+ branch_labels = None
16
+ depends_on = None
17
+
18
+
19
+ def upgrade() -> None:
20
+ # ### commands auto generated by Alembic - please adjust! ###
21
+ op.create_table(
22
+ "ci_outcomes",
23
+ sa.Column("schema_version", sa.BigInteger(), nullable=False),
24
+ sa.Column("outcome_id", sa.Text(), nullable=False),
25
+ sa.Column("org_id", sa.Text(), nullable=False),
26
+ sa.Column("provider", sa.Text(), nullable=False),
27
+ sa.Column("run_id", sa.Text(), nullable=False),
28
+ sa.Column("run_attempt", sa.BigInteger(), nullable=True),
29
+ sa.Column("repo", sa.Text(), nullable=False),
30
+ sa.Column("commit_sha", sa.Text(), nullable=False),
31
+ sa.Column("branch", sa.Text(), nullable=False),
32
+ sa.Column("result", sa.Text(), nullable=False),
33
+ sa.Column("workflow_name", sa.Text(), nullable=False),
34
+ sa.Column("workflow_id", sa.Text(), nullable=True),
35
+ sa.Column("workflow_path", sa.Text(), nullable=True),
36
+ sa.Column("run_url", sa.Text(), nullable=True),
37
+ sa.Column("provider_result", sa.Text(), nullable=True),
38
+ sa.Column("error_type", sa.Text(), nullable=True),
39
+ sa.Column("reason", sa.Text(), nullable=True),
40
+ sa.Column("source_event_type", sa.Text(), nullable=True),
41
+ sa.Column("source_spec_version", sa.Text(), nullable=True),
42
+ sa.Column("source_event_id", sa.Text(), nullable=True),
43
+ sa.Column("pr_number", sa.BigInteger(), nullable=True),
44
+ sa.Column("captured_at", sa.DateTime(timezone=True), nullable=False),
45
+ sa.Column("raw", sa.Text(), nullable=False),
46
+ sa.CheckConstraint(
47
+ "branch NOT LIKE 'refs/heads/%' AND branch = btrim(branch, E' \\t\\n\\r')",
48
+ name="ck_ci_outcomes_branch",
49
+ ),
50
+ sa.CheckConstraint(
51
+ "commit_sha ~ '^(?:[0-9a-f]{40}|[0-9a-f]{64})$'",
52
+ name="ck_ci_outcomes_commit_sha",
53
+ ),
54
+ sa.CheckConstraint(
55
+ "error_type IS NULL OR (error_type <> '' AND error_type = btrim(error_type, E' \\t\\n\\r'))",
56
+ name="ck_ci_outcomes_error_type",
57
+ ),
58
+ sa.CheckConstraint(
59
+ "org_id ~ '^[a-z0-9][a-z0-9._-]{0,63}$'", name="ck_ci_outcomes_org_id"
60
+ ),
61
+ sa.CheckConstraint(
62
+ "outcome_id <> '' AND outcome_id = btrim(outcome_id, E' \\t\\n\\r')",
63
+ name="ck_ci_outcomes_outcome_id",
64
+ ),
65
+ sa.CheckConstraint(
66
+ "provider IN ('github_actions', 'jenkins', 'gitlab_ci', 'circleci', 'buildkite', 'other')",
67
+ name="ck_ci_outcomes_provider",
68
+ ),
69
+ sa.CheckConstraint(
70
+ "provider_result IS NULL OR (provider_result <> '' AND provider_result = btrim(provider_result, E' \\t\\n\\r'))",
71
+ name="ck_ci_outcomes_provider_result",
72
+ ),
73
+ sa.CheckConstraint(
74
+ "repo = '' OR (repo = lower(repo) AND repo ~ '^[^/]+/[^/]+$')",
75
+ name="ck_ci_outcomes_repo",
76
+ ),
77
+ sa.CheckConstraint(
78
+ "result IN ('passed', 'failed', 'error', 'timed_out', 'cancelled', 'skipped', 'neutral', 'unknown')",
79
+ name="ck_ci_outcomes_result",
80
+ ),
81
+ sa.CheckConstraint(
82
+ "run_id <> '' AND run_id = btrim(run_id, E' \\t\\n\\r')",
83
+ name="ck_ci_outcomes_run_id",
84
+ ),
85
+ sa.CheckConstraint(
86
+ "run_url IS NULL OR (run_url <> '' AND run_url = btrim(run_url, E' \\t\\n\\r'))",
87
+ name="ck_ci_outcomes_run_url",
88
+ ),
89
+ sa.CheckConstraint(
90
+ "source_event_id IS NULL OR (source_event_id <> '' AND source_event_id = btrim(source_event_id, E' \\t\\n\\r'))",
91
+ name="ck_ci_outcomes_source_event_id",
92
+ ),
93
+ sa.CheckConstraint(
94
+ "source_event_type IS NULL OR (source_event_type <> '' AND source_event_type = btrim(source_event_type, E' \\t\\n\\r'))",
95
+ name="ck_ci_outcomes_source_event_type",
96
+ ),
97
+ sa.CheckConstraint(
98
+ "source_spec_version IS NULL OR (source_spec_version <> '' AND source_spec_version = btrim(source_spec_version, E' \\t\\n\\r'))",
99
+ name="ck_ci_outcomes_source_spec_version",
100
+ ),
101
+ sa.CheckConstraint(
102
+ "workflow_id IS NULL OR (workflow_id <> '' AND workflow_id = btrim(workflow_id, E' \\t\\n\\r'))",
103
+ name="ck_ci_outcomes_workflow_id",
104
+ ),
105
+ sa.CheckConstraint(
106
+ "pr_number IS NULL OR pr_number BETWEEN 1 AND 9223372036854775807",
107
+ name="ck_ci_outcomes_pr_number",
108
+ ),
109
+ sa.CheckConstraint(
110
+ "reason IS NULL OR char_length(reason) <= 4096",
111
+ name="ck_ci_outcomes_reason",
112
+ ),
113
+ sa.CheckConstraint(
114
+ "run_attempt IS NULL OR run_attempt BETWEEN 1 AND 9223372036854775807",
115
+ name="ck_ci_outcomes_run_attempt",
116
+ ),
117
+ sa.CheckConstraint("schema_version = 1", name="ck_ci_outcomes_schema_version"),
118
+ sa.PrimaryKeyConstraint("outcome_id"),
119
+ )
120
+ op.create_index(
121
+ "ix_ci_org_commit", "ci_outcomes", ["org_id", "commit_sha"], unique=False
122
+ )
123
+ op.create_index(
124
+ "uq_ci_run",
125
+ "ci_outcomes",
126
+ ["org_id", "provider", "run_id", sa.literal_column("coalesce(run_attempt, 0)")],
127
+ unique=True,
128
+ )
129
+ op.create_table(
130
+ "developer_decisions",
131
+ sa.Column("decision_id", sa.Text(), nullable=False),
132
+ sa.Column("org_id", sa.Text(), nullable=False),
133
+ sa.Column("session_id", sa.Text(), nullable=False),
134
+ sa.Column("user_id", sa.Text(), nullable=True),
135
+ sa.Column("agent_harness", sa.Text(), nullable=False),
136
+ sa.Column("file_path", sa.Text(), nullable=False),
137
+ sa.Column("accepted", sa.Boolean(), nullable=False),
138
+ sa.Column("explicit", sa.Boolean(), nullable=False),
139
+ sa.Column("interaction_mode", sa.Text(), nullable=False),
140
+ sa.Column("commit_sha", sa.Text(), nullable=True),
141
+ sa.Column("call_id", sa.Text(), nullable=True),
142
+ sa.Column("edit_retention_score", sa.Float(), nullable=True),
143
+ sa.Column("observation_delay_ms", sa.BigInteger(), nullable=True),
144
+ sa.Column("occurred_at", sa.DateTime(timezone=True), nullable=False),
145
+ sa.Column("captured_at", sa.DateTime(timezone=True), nullable=False),
146
+ sa.Column("raw", sa.Text(), nullable=False),
147
+ sa.CheckConstraint(
148
+ "agent_harness IN ('claude-code', 'copilot', 'codex', 'pi')",
149
+ name="ck_decisions_agent_harness",
150
+ ),
151
+ sa.CheckConstraint(
152
+ "call_id IS NULL OR (call_id <> '' AND call_id = btrim(call_id, E' \\t\\n\\r'))",
153
+ name="ck_decisions_call_id",
154
+ ),
155
+ sa.CheckConstraint(
156
+ "commit_sha IS NULL OR (commit_sha ~ '^(?:[0-9a-f]{40}|[0-9a-f]{64})$')",
157
+ name="ck_decisions_commit_sha",
158
+ ),
159
+ sa.CheckConstraint(
160
+ "decision_id <> '' AND decision_id = btrim(decision_id, E' \\t\\n\\r')",
161
+ name="ck_decisions_decision_id",
162
+ ),
163
+ sa.CheckConstraint(
164
+ "interaction_mode IN ('agent', 'inline')",
165
+ name="ck_decisions_interaction_mode",
166
+ ),
167
+ sa.CheckConstraint(
168
+ "org_id ~ '^[a-z0-9][a-z0-9._-]{0,63}$'", name="ck_decisions_org_id"
169
+ ),
170
+ sa.CheckConstraint(
171
+ "session_id <> '' AND session_id = btrim(session_id, E' \\t\\n\\r')",
172
+ name="ck_decisions_session_id",
173
+ ),
174
+ sa.CheckConstraint(
175
+ "user_id IS NULL OR (user_id <> '' AND user_id = btrim(user_id, E' \\t\\n\\r'))",
176
+ name="ck_decisions_user_id",
177
+ ),
178
+ sa.CheckConstraint(
179
+ "edit_retention_score IS NULL OR edit_retention_score BETWEEN 0.0 AND 1.0",
180
+ name="ck_decisions_edit_retention_score",
181
+ ),
182
+ sa.CheckConstraint(
183
+ "observation_delay_ms IS NULL OR observation_delay_ms >= 0",
184
+ name="ck_decisions_observation_delay_ms",
185
+ ),
186
+ sa.PrimaryKeyConstraint("decision_id"),
187
+ )
188
+ op.create_index(
189
+ "uq_decisions_keyed",
190
+ "developer_decisions",
191
+ [
192
+ "org_id",
193
+ "agent_harness",
194
+ "session_id",
195
+ "accepted",
196
+ "explicit",
197
+ "interaction_mode",
198
+ "occurred_at",
199
+ "call_id",
200
+ ],
201
+ unique=True,
202
+ postgresql_where=sa.text(
203
+ "call_id IS NOT NULL AND agent_harness = 'claude-code'"
204
+ ),
205
+ )
206
+ op.create_index(
207
+ "uq_decisions_natural",
208
+ "developer_decisions",
209
+ [
210
+ "org_id",
211
+ "agent_harness",
212
+ "session_id",
213
+ "file_path",
214
+ "accepted",
215
+ "explicit",
216
+ "interaction_mode",
217
+ "occurred_at",
218
+ sa.literal_column("coalesce(call_id, '')"),
219
+ sa.literal_column("coalesce(observation_delay_ms, -1)"),
220
+ ],
221
+ unique=True,
222
+ postgresql_where=sa.text("call_id IS NULL OR agent_harness != 'claude-code'"),
223
+ )
224
+ op.create_table(
225
+ "edit_observations",
226
+ sa.Column("observation_id", sa.Text(), nullable=False),
227
+ sa.Column("org_id", sa.Text(), nullable=False),
228
+ sa.Column("session_id", sa.Text(), nullable=False),
229
+ sa.Column("user_id", sa.Text(), nullable=True),
230
+ sa.Column("agent_harness", sa.Text(), nullable=False),
231
+ sa.Column("file_path", sa.Text(), nullable=False),
232
+ sa.Column("call_id", sa.Text(), nullable=False),
233
+ sa.Column("applied_text", sa.Text(), nullable=False),
234
+ sa.Column("observed_file_text", sa.Text(), nullable=False),
235
+ sa.Column("external_lines_added", sa.BigInteger(), nullable=True),
236
+ sa.Column("external_lines_removed", sa.BigInteger(), nullable=True),
237
+ sa.Column("occurred_at", sa.DateTime(timezone=True), nullable=False),
238
+ sa.Column("captured_at", sa.DateTime(timezone=True), nullable=False),
239
+ sa.Column("raw", sa.Text(), nullable=False),
240
+ sa.CheckConstraint(
241
+ "agent_harness IN ('claude-code', 'copilot', 'codex', 'pi')",
242
+ name="ck_edit_observations_agent_harness",
243
+ ),
244
+ sa.CheckConstraint(
245
+ "call_id <> '' AND call_id = btrim(call_id, E' \\t\\n\\r')",
246
+ name="ck_edit_observations_call_id",
247
+ ),
248
+ sa.CheckConstraint(
249
+ "observation_id <> '' AND observation_id = btrim(observation_id, E' \\t\\n\\r')",
250
+ name="ck_edit_observations_observation_id",
251
+ ),
252
+ sa.CheckConstraint(
253
+ "org_id ~ '^[a-z0-9][a-z0-9._-]{0,63}$'", name="ck_edit_observations_org_id"
254
+ ),
255
+ sa.CheckConstraint(
256
+ "session_id <> '' AND session_id = btrim(session_id, E' \\t\\n\\r')",
257
+ name="ck_edit_observations_session_id",
258
+ ),
259
+ sa.CheckConstraint(
260
+ "user_id IS NULL OR (user_id <> '' AND user_id = btrim(user_id, E' \\t\\n\\r'))",
261
+ name="ck_edit_observations_user_id",
262
+ ),
263
+ sa.CheckConstraint(
264
+ "(external_lines_added IS NULL OR external_lines_added >= 0) AND (external_lines_removed IS NULL OR external_lines_removed >= 0)",
265
+ name="ck_edit_observations_external_counts",
266
+ ),
267
+ sa.PrimaryKeyConstraint("observation_id"),
268
+ )
269
+ op.create_index(
270
+ "uq_edit_observations_call",
271
+ "edit_observations",
272
+ ["org_id", "agent_harness", "session_id", "call_id"],
273
+ unique=True,
274
+ )
275
+ op.create_table(
276
+ "fact_quarantine",
277
+ sa.Column(
278
+ "quarantine_revision",
279
+ sa.BigInteger(),
280
+ sa.Identity(always=True),
281
+ nullable=False,
282
+ ),
283
+ sa.Column("quarantine_id", sa.Text(), nullable=False),
284
+ sa.Column("org_id", sa.Text(), nullable=False),
285
+ sa.Column("fact_table", sa.Text(), nullable=False),
286
+ sa.Column("fact_id", sa.Text(), nullable=False),
287
+ sa.Column("action", sa.Text(), nullable=False),
288
+ sa.Column("reason", sa.Text(), nullable=False),
289
+ sa.Column("recorded_at", sa.DateTime(timezone=True), nullable=False),
290
+ sa.CheckConstraint(
291
+ "action IN ('quarantine', 'release')", name="ck_quarantine_action"
292
+ ),
293
+ sa.CheckConstraint(
294
+ "btrim(reason, E' \\t\\n\\r') <> ''", name="ck_quarantine_reason"
295
+ ),
296
+ sa.CheckConstraint(
297
+ "fact_id <> '' AND fact_id = btrim(fact_id, E' \\t\\n\\r')",
298
+ name="ck_quarantine_fact_id",
299
+ ),
300
+ sa.CheckConstraint(
301
+ "fact_table IN ('inference_calls', 'developer_decisions', 'ci_outcomes', 'pushes', 'edit_observations', 'rejected_edits')",
302
+ name="ck_quarantine_table",
303
+ ),
304
+ sa.CheckConstraint(
305
+ "org_id ~ '^[a-z0-9][a-z0-9._-]{0,63}$'", name="ck_quarantine_org_id"
306
+ ),
307
+ sa.CheckConstraint(
308
+ "quarantine_id <> '' AND quarantine_id = btrim(quarantine_id, E' \\t\\n\\r')",
309
+ name="ck_quarantine_quarantine_id",
310
+ ),
311
+ sa.PrimaryKeyConstraint("quarantine_revision"),
312
+ sa.UniqueConstraint("quarantine_id", name="uq_quarantine_id"),
313
+ )
314
+ op.create_index(
315
+ "ix_quarantine_fact",
316
+ "fact_quarantine",
317
+ ["org_id", "fact_table", "fact_id", "quarantine_revision"],
318
+ unique=False,
319
+ )
320
+ op.create_table(
321
+ "inference_calls",
322
+ sa.Column("schema_version", sa.BigInteger(), nullable=False),
323
+ sa.Column("inference_call_id", sa.Text(), nullable=False),
324
+ sa.Column("org_id", sa.Text(), nullable=False),
325
+ sa.Column("session_id", sa.Text(), nullable=False),
326
+ sa.Column("user_id", sa.Text(), nullable=True),
327
+ sa.Column("gateway_provider", sa.Text(), nullable=False),
328
+ sa.Column("model_provider", sa.Text(), nullable=True),
329
+ sa.Column("model", sa.Text(), nullable=True),
330
+ sa.Column("input_messages", sa.Text(), nullable=False),
331
+ sa.Column("output_messages", sa.Text(), nullable=False),
332
+ sa.Column("input_tokens", sa.BigInteger(), nullable=True),
333
+ sa.Column("output_tokens", sa.BigInteger(), nullable=True),
334
+ sa.Column("duration_ms", sa.BigInteger(), nullable=True),
335
+ sa.Column("model_call_id", sa.Text(), nullable=True),
336
+ sa.Column("observed_at", sa.DateTime(timezone=True), nullable=False),
337
+ sa.Column("raw", sa.Text(), nullable=False),
338
+ sa.CheckConstraint(
339
+ "gateway_provider IN ('litellm', 'portkey', 'helicone', 'unknown')",
340
+ name="ck_inference_calls_gateway_provider",
341
+ ),
342
+ sa.CheckConstraint(
343
+ "inference_call_id <> '' AND inference_call_id = btrim(inference_call_id, E' \\t\\n\\r')",
344
+ name="ck_inference_calls_inference_call_id",
345
+ ),
346
+ sa.CheckConstraint(
347
+ "model_call_id IS NULL OR (model_call_id <> '' AND model_call_id = btrim(model_call_id, E' \\t\\n\\r'))",
348
+ name="ck_inference_calls_model_call_id",
349
+ ),
350
+ sa.CheckConstraint(
351
+ "org_id ~ '^[a-z0-9][a-z0-9._-]{0,63}$'", name="ck_inference_calls_org_id"
352
+ ),
353
+ sa.CheckConstraint(
354
+ "session_id <> '' AND session_id = btrim(session_id, E' \\t\\n\\r')",
355
+ name="ck_inference_calls_session_id",
356
+ ),
357
+ sa.CheckConstraint(
358
+ "user_id IS NULL OR (user_id <> '' AND user_id = btrim(user_id, E' \\t\\n\\r'))",
359
+ name="ck_inference_calls_user_id",
360
+ ),
361
+ sa.CheckConstraint(
362
+ "(input_tokens IS NULL OR input_tokens >= 0) AND (output_tokens IS NULL OR output_tokens >= 0) AND (duration_ms IS NULL OR duration_ms >= 0)",
363
+ name="ck_inference_calls_usage",
364
+ ),
365
+ sa.CheckConstraint(
366
+ "schema_version = 1", name="ck_inference_calls_schema_version"
367
+ ),
368
+ sa.PrimaryKeyConstraint("inference_call_id"),
369
+ )
370
+ op.create_index(
371
+ "ix_inference_calls_org_time",
372
+ "inference_calls",
373
+ ["org_id", "observed_at"],
374
+ unique=False,
375
+ )
376
+ op.create_index(
377
+ "uq_inference_calls_model_call",
378
+ "inference_calls",
379
+ ["org_id", "gateway_provider", "model_call_id"],
380
+ unique=True,
381
+ postgresql_where=sa.text("model_call_id IS NOT NULL"),
382
+ )
383
+ op.create_table(
384
+ "pushes",
385
+ sa.Column("push_id", sa.Text(), nullable=False),
386
+ sa.Column("org_id", sa.Text(), nullable=False),
387
+ sa.Column("provider", sa.Text(), nullable=False),
388
+ sa.Column("repo", sa.Text(), nullable=False),
389
+ sa.Column("clone_url", sa.Text(), nullable=False),
390
+ sa.Column("ref", sa.Text(), nullable=False),
391
+ sa.Column("before_sha", sa.Text(), nullable=False),
392
+ sa.Column("after_sha", sa.Text(), nullable=False),
393
+ sa.Column("forced", sa.Boolean(), nullable=False),
394
+ sa.Column("captured_at", sa.DateTime(timezone=True), nullable=False),
395
+ sa.CheckConstraint(
396
+ "after_sha ~ '^(?:[0-9a-f]{40}|[0-9a-f]{64})$'", name="ck_pushes_after_sha"
397
+ ),
398
+ sa.CheckConstraint(
399
+ "before_sha ~ '^(?:[0-9a-f]{40}|[0-9a-f]{64})$'",
400
+ name="ck_pushes_before_sha",
401
+ ),
402
+ sa.CheckConstraint(
403
+ "org_id ~ '^[a-z0-9][a-z0-9._-]{0,63}$'", name="ck_pushes_org_id"
404
+ ),
405
+ sa.CheckConstraint("provider IN ('github')", name="ck_pushes_provider"),
406
+ sa.CheckConstraint(
407
+ "push_id <> '' AND push_id = btrim(push_id, E' \\t\\n\\r')",
408
+ name="ck_pushes_push_id",
409
+ ),
410
+ sa.CheckConstraint(
411
+ "ref <> '' AND ref = btrim(ref, E' \\t\\n\\r')", name="ck_pushes_ref"
412
+ ),
413
+ sa.CheckConstraint(
414
+ "repo = '' OR (repo = lower(repo) AND repo ~ '^[^/]+/[^/]+$')",
415
+ name="ck_pushes_repo",
416
+ ),
417
+ sa.PrimaryKeyConstraint("push_id"),
418
+ )
419
+ op.create_index(
420
+ "uq_pushes_natural",
421
+ "pushes",
422
+ ["org_id", "repo", "ref", "before_sha", "after_sha"],
423
+ unique=True,
424
+ )
425
+ op.create_table(
426
+ "rejected_edits",
427
+ sa.Column("rejection_id", sa.Text(), nullable=False),
428
+ sa.Column("org_id", sa.Text(), nullable=False),
429
+ sa.Column("session_id", sa.Text(), nullable=False),
430
+ sa.Column("user_id", sa.Text(), nullable=True),
431
+ sa.Column("agent_harness", sa.Text(), nullable=False),
432
+ sa.Column("file_path", sa.Text(), nullable=False),
433
+ sa.Column("call_id", sa.Text(), nullable=False),
434
+ sa.Column("proposed", sa.Text(), nullable=False),
435
+ sa.Column("occurred_at", sa.DateTime(timezone=True), nullable=False),
436
+ sa.Column("captured_at", sa.DateTime(timezone=True), nullable=False),
437
+ sa.Column("raw", sa.Text(), nullable=False),
438
+ sa.CheckConstraint(
439
+ "agent_harness IN ('claude-code', 'copilot', 'codex', 'pi')",
440
+ name="ck_rejected_edits_agent_harness",
441
+ ),
442
+ sa.CheckConstraint(
443
+ "call_id <> '' AND call_id = btrim(call_id, E' \\t\\n\\r')",
444
+ name="ck_rejected_edits_call_id",
445
+ ),
446
+ sa.CheckConstraint(
447
+ "org_id ~ '^[a-z0-9][a-z0-9._-]{0,63}$'", name="ck_rejected_edits_org_id"
448
+ ),
449
+ sa.CheckConstraint(
450
+ "rejection_id <> '' AND rejection_id = btrim(rejection_id, E' \\t\\n\\r')",
451
+ name="ck_rejected_edits_rejection_id",
452
+ ),
453
+ sa.CheckConstraint(
454
+ "session_id <> '' AND session_id = btrim(session_id, E' \\t\\n\\r')",
455
+ name="ck_rejected_edits_session_id",
456
+ ),
457
+ sa.CheckConstraint(
458
+ "user_id IS NULL OR (user_id <> '' AND user_id = btrim(user_id, E' \\t\\n\\r'))",
459
+ name="ck_rejected_edits_user_id",
460
+ ),
461
+ sa.PrimaryKeyConstraint("rejection_id"),
462
+ )
463
+ op.create_index(
464
+ "uq_rejected_edits_call",
465
+ "rejected_edits",
466
+ ["org_id", "agent_harness", "session_id", "call_id"],
467
+ unique=True,
468
+ )
469
+ op.create_table(
470
+ "sessions",
471
+ sa.Column("org_id", sa.Text(), nullable=False),
472
+ sa.Column("session_id", sa.Text(), nullable=False),
473
+ sa.Column("user_id", sa.Text(), nullable=True),
474
+ sa.Column(
475
+ "user_id_conflict", sa.Boolean(), server_default="false", nullable=False
476
+ ),
477
+ sa.Column("first_observed_at", sa.DateTime(timezone=True), nullable=False),
478
+ sa.Column("last_observed_at", sa.DateTime(timezone=True), nullable=False),
479
+ sa.CheckConstraint(
480
+ "org_id ~ '^[a-z0-9][a-z0-9._-]{0,63}$'", name="ck_sessions_org_id"
481
+ ),
482
+ sa.CheckConstraint(
483
+ "session_id <> '' AND session_id = btrim(session_id, E' \\t\\n\\r')",
484
+ name="ck_sessions_session_id",
485
+ ),
486
+ sa.CheckConstraint(
487
+ "user_id IS NULL OR (user_id <> '' AND user_id = btrim(user_id, E' \\t\\n\\r'))",
488
+ name="ck_sessions_user_id",
489
+ ),
490
+ sa.CheckConstraint(
491
+ "first_observed_at <= last_observed_at",
492
+ name="ck_sessions_observation_bounds",
493
+ ),
494
+ sa.PrimaryKeyConstraint("org_id", "session_id", name="pk_sessions"),
495
+ )
496
+ # ### end Alembic commands ###
497
+
498
+
499
+ def downgrade() -> None:
500
+ raise NotImplementedError("Sediment migrations are forward-only")