loomrail 0.1.0-alpha.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/LICENSE +202 -0
- package/NOTICE +4 -0
- package/README.md +266 -0
- package/apps/cli/dist/index.js +11154 -0
- package/apps/cli/migrations/0001_initial.sql +99 -0
- package/apps/cli/migrations/0002_mock_workflow.sql +169 -0
- package/apps/cli/migrations/0003_budget_pause_recovery.sql +154 -0
- package/apps/cli/migrations/0004_pipeline_started_budget_backfill.sql +72 -0
- package/apps/cli/migrations/0005_acceptance_evidence.sql +129 -0
- package/apps/cli/migrations/0006_session_handoff.sql +166 -0
- package/apps/cli/migrations/0007_pack_share_backoff.sql +11 -0
- package/apps/cli/migrations/0008_stage_attempt_counter_backfill.sql +169 -0
- package/apps/cli/migrations/0009_session_window_occupancy.sql +44 -0
- package/apps/cli/migrations/0010_provider_session_pid.sql +5 -0
- package/apps/cli/migrations/0011_work_item_workspaces.sql +94 -0
- package/apps/cli/migrations/0012_registered_repository_projects.sql +54 -0
- package/apps/cli/migrations/0013_stage_attempt_result_tree.sql +166 -0
- package/apps/cli/migrations/0014_live_evidence_provider.sql +49 -0
- package/apps/web/dist/assets/index-DjwvCSSY.js +5 -0
- package/apps/web/dist/assets/index-DjwvCSSY.js.map +1 -0
- package/apps/web/dist/assets/index-Z88TqCIf.css +1 -0
- package/apps/web/dist/assets/radix-vendor-CRdHXIPR.js +42 -0
- package/apps/web/dist/assets/radix-vendor-CRdHXIPR.js.map +1 -0
- package/apps/web/dist/assets/react-vendor-BicOiEU0.js +10 -0
- package/apps/web/dist/assets/react-vendor-BicOiEU0.js.map +1 -0
- package/apps/web/dist/assets/rolldown-runtime-CbXtAM7H.js +1 -0
- package/apps/web/dist/assets/tanstack-vendor-ec08LVZ_.js +2 -0
- package/apps/web/dist/assets/tanstack-vendor-ec08LVZ_.js.map +1 -0
- package/apps/web/dist/assets/validation-vendor-CGrOSWd3.js +65 -0
- package/apps/web/dist/assets/validation-vendor-CGrOSWd3.js.map +1 -0
- package/apps/web/dist/favicon.svg +5 -0
- package/apps/web/dist/index.html +60 -0
- package/apps/web/dist/theme-bootstrap.js +46 -0
- package/fixtures/projects/api-service-b/README.md +3 -0
- package/fixtures/projects/api-service-b/loomrail-fixture.json +6 -0
- package/fixtures/projects/web-app-a/README.md +3 -0
- package/fixtures/projects/web-app-a/loomrail-fixture.json +6 -0
- package/package.json +46 -0
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
CREATE TABLE workspaces (
|
|
2
|
+
id TEXT PRIMARY KEY,
|
|
3
|
+
name TEXT NOT NULL CHECK (length(name) BETWEEN 1 AND 200),
|
|
4
|
+
created_at TEXT NOT NULL,
|
|
5
|
+
updated_at TEXT NOT NULL
|
|
6
|
+
) STRICT;
|
|
7
|
+
|
|
8
|
+
CREATE TABLE projects (
|
|
9
|
+
id TEXT PRIMARY KEY,
|
|
10
|
+
workspace_id TEXT NOT NULL REFERENCES workspaces(id) ON DELETE RESTRICT,
|
|
11
|
+
fixture_id TEXT NOT NULL UNIQUE CHECK (fixture_id IN ('web-app-a', 'api-service-b')),
|
|
12
|
+
name TEXT NOT NULL CHECK (length(name) BETWEEN 1 AND 200),
|
|
13
|
+
repository_path TEXT NOT NULL UNIQUE,
|
|
14
|
+
status TEXT NOT NULL CHECK (status IN ('ACTIVE', 'ARCHIVED')),
|
|
15
|
+
version INTEGER NOT NULL CHECK (version > 0),
|
|
16
|
+
created_at TEXT NOT NULL,
|
|
17
|
+
updated_at TEXT NOT NULL
|
|
18
|
+
) STRICT;
|
|
19
|
+
|
|
20
|
+
CREATE TABLE work_items (
|
|
21
|
+
id TEXT PRIMARY KEY,
|
|
22
|
+
project_id TEXT NOT NULL REFERENCES projects(id) ON DELETE RESTRICT,
|
|
23
|
+
parent_id TEXT,
|
|
24
|
+
type TEXT NOT NULL CHECK (type IN ('EPIC', 'FEATURE', 'TASK', 'BUG', 'SPIKE', 'SUBTASK')),
|
|
25
|
+
title TEXT NOT NULL CHECK (length(title) BETWEEN 1 AND 200),
|
|
26
|
+
description TEXT NOT NULL CHECK (length(description) <= 20000),
|
|
27
|
+
state TEXT NOT NULL CHECK (state IN ('BACKLOG', 'READY', 'IN_PROGRESS', 'BLOCKED', 'DONE', 'CANCELLED')),
|
|
28
|
+
current_stage TEXT CHECK (
|
|
29
|
+
current_stage IS NULL OR current_stage IN ('DISCOVERY', 'PLAN', 'IMPLEMENT', 'REVIEW', 'QA', 'ACCEPTANCE')
|
|
30
|
+
),
|
|
31
|
+
priority TEXT NOT NULL CHECK (priority IN ('LOW', 'MEDIUM', 'HIGH', 'URGENT')),
|
|
32
|
+
risk TEXT NOT NULL CHECK (risk IN ('LOW', 'MEDIUM', 'HIGH', 'CRITICAL')),
|
|
33
|
+
version INTEGER NOT NULL CHECK (version > 0),
|
|
34
|
+
created_at TEXT NOT NULL,
|
|
35
|
+
updated_at TEXT NOT NULL,
|
|
36
|
+
UNIQUE (id, project_id),
|
|
37
|
+
FOREIGN KEY (parent_id, project_id) REFERENCES work_items(id, project_id) ON DELETE RESTRICT
|
|
38
|
+
) STRICT;
|
|
39
|
+
|
|
40
|
+
CREATE TABLE work_item_acceptance_criteria (
|
|
41
|
+
work_item_id TEXT NOT NULL REFERENCES work_items(id) ON DELETE CASCADE,
|
|
42
|
+
ordinal INTEGER NOT NULL CHECK (ordinal >= 0),
|
|
43
|
+
criterion TEXT NOT NULL CHECK (length(criterion) BETWEEN 1 AND 500),
|
|
44
|
+
PRIMARY KEY (work_item_id, ordinal)
|
|
45
|
+
) STRICT;
|
|
46
|
+
|
|
47
|
+
CREATE TABLE events (
|
|
48
|
+
sequence INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
49
|
+
id TEXT NOT NULL UNIQUE,
|
|
50
|
+
schema_version INTEGER NOT NULL CHECK (schema_version = 1),
|
|
51
|
+
type TEXT NOT NULL CHECK (
|
|
52
|
+
type IN ('PROJECT_REGISTERED', 'WORK_ITEM_CREATED', 'WORK_ITEM_UPDATED', 'WORK_ITEM_STATE_CHANGED')
|
|
53
|
+
),
|
|
54
|
+
aggregate_type TEXT NOT NULL CHECK (aggregate_type IN ('PROJECT', 'WORK_ITEM')),
|
|
55
|
+
aggregate_id TEXT NOT NULL,
|
|
56
|
+
project_id TEXT NOT NULL REFERENCES projects(id) ON DELETE RESTRICT,
|
|
57
|
+
actor_type TEXT NOT NULL CHECK (actor_type IN ('HUMAN', 'SYSTEM')),
|
|
58
|
+
actor_id TEXT NOT NULL,
|
|
59
|
+
occurred_at TEXT NOT NULL,
|
|
60
|
+
correlation_id TEXT NOT NULL,
|
|
61
|
+
data_json TEXT NOT NULL CHECK (json_valid(data_json))
|
|
62
|
+
) STRICT;
|
|
63
|
+
|
|
64
|
+
CREATE TABLE commands (
|
|
65
|
+
command_id TEXT PRIMARY KEY,
|
|
66
|
+
command_type TEXT NOT NULL,
|
|
67
|
+
input_hash TEXT NOT NULL CHECK (length(input_hash) = 64),
|
|
68
|
+
result_json TEXT NOT NULL CHECK (json_valid(result_json)),
|
|
69
|
+
created_at TEXT NOT NULL
|
|
70
|
+
) STRICT;
|
|
71
|
+
|
|
72
|
+
CREATE INDEX work_items_project_state_idx ON work_items(project_id, state, created_at, id);
|
|
73
|
+
CREATE INDEX work_items_parent_idx ON work_items(parent_id, created_at, id);
|
|
74
|
+
CREATE INDEX events_project_sequence_idx ON events(project_id, sequence);
|
|
75
|
+
CREATE INDEX events_aggregate_sequence_idx ON events(aggregate_id, sequence);
|
|
76
|
+
|
|
77
|
+
CREATE TRIGGER events_are_append_only_update
|
|
78
|
+
BEFORE UPDATE ON events
|
|
79
|
+
BEGIN
|
|
80
|
+
SELECT RAISE(ABORT, 'events are append-only');
|
|
81
|
+
END;
|
|
82
|
+
|
|
83
|
+
CREATE TRIGGER events_are_append_only_delete
|
|
84
|
+
BEFORE DELETE ON events
|
|
85
|
+
BEGIN
|
|
86
|
+
SELECT RAISE(ABORT, 'events are append-only');
|
|
87
|
+
END;
|
|
88
|
+
|
|
89
|
+
CREATE TRIGGER commands_are_append_only_update
|
|
90
|
+
BEFORE UPDATE ON commands
|
|
91
|
+
BEGIN
|
|
92
|
+
SELECT RAISE(ABORT, 'command receipts are append-only');
|
|
93
|
+
END;
|
|
94
|
+
|
|
95
|
+
CREATE TRIGGER commands_are_append_only_delete
|
|
96
|
+
BEFORE DELETE ON commands
|
|
97
|
+
BEGIN
|
|
98
|
+
SELECT RAISE(ABORT, 'command receipts are append-only');
|
|
99
|
+
END;
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
CREATE TABLE workflow_templates (
|
|
2
|
+
id TEXT NOT NULL,
|
|
3
|
+
version INTEGER NOT NULL CHECK (version > 0),
|
|
4
|
+
schema_version INTEGER NOT NULL CHECK (schema_version = 1),
|
|
5
|
+
name TEXT NOT NULL CHECK (length(name) BETWEEN 1 AND 200),
|
|
6
|
+
template_json TEXT NOT NULL CHECK (json_valid(template_json)),
|
|
7
|
+
created_at TEXT NOT NULL,
|
|
8
|
+
PRIMARY KEY (id, version)
|
|
9
|
+
) STRICT;
|
|
10
|
+
|
|
11
|
+
CREATE TABLE pipeline_runs (
|
|
12
|
+
id TEXT PRIMARY KEY,
|
|
13
|
+
project_id TEXT NOT NULL REFERENCES projects(id) ON DELETE RESTRICT,
|
|
14
|
+
work_item_id TEXT NOT NULL REFERENCES work_items(id) ON DELETE RESTRICT,
|
|
15
|
+
workflow_template_id TEXT NOT NULL,
|
|
16
|
+
workflow_version INTEGER NOT NULL CHECK (workflow_version > 0),
|
|
17
|
+
status TEXT NOT NULL CHECK (status IN ('RUNNING', 'WAITING_HUMAN', 'SUCCEEDED', 'FAILED', 'CANCELLED')),
|
|
18
|
+
current_stage_attempt_id TEXT NOT NULL,
|
|
19
|
+
version INTEGER NOT NULL CHECK (version > 0),
|
|
20
|
+
created_at TEXT NOT NULL,
|
|
21
|
+
updated_at TEXT NOT NULL,
|
|
22
|
+
finished_at TEXT,
|
|
23
|
+
FOREIGN KEY (workflow_template_id, workflow_version)
|
|
24
|
+
REFERENCES workflow_templates(id, version) ON DELETE RESTRICT
|
|
25
|
+
) STRICT;
|
|
26
|
+
|
|
27
|
+
CREATE TABLE stage_attempts (
|
|
28
|
+
id TEXT PRIMARY KEY,
|
|
29
|
+
pipeline_run_id TEXT NOT NULL REFERENCES pipeline_runs(id) ON DELETE RESTRICT,
|
|
30
|
+
project_id TEXT NOT NULL REFERENCES projects(id) ON DELETE RESTRICT,
|
|
31
|
+
work_item_id TEXT NOT NULL REFERENCES work_items(id) ON DELETE RESTRICT,
|
|
32
|
+
stage TEXT NOT NULL CHECK (stage IN ('DISCOVERY', 'PLAN', 'IMPLEMENT', 'REVIEW', 'QA', 'ACCEPTANCE')),
|
|
33
|
+
attempt INTEGER NOT NULL CHECK (attempt > 0),
|
|
34
|
+
status TEXT NOT NULL CHECK (
|
|
35
|
+
status IN (
|
|
36
|
+
'PENDING', 'QUEUED', 'RUNNING', 'WAITING_HUMAN', 'SOFT_PAUSED', 'HARD_PAUSED',
|
|
37
|
+
'SUCCEEDED', 'FAILED', 'CANCELLED', 'INTERRUPTED', 'RECOVERING', 'STALE'
|
|
38
|
+
)
|
|
39
|
+
),
|
|
40
|
+
version INTEGER NOT NULL CHECK (version > 0),
|
|
41
|
+
started_at TEXT,
|
|
42
|
+
finished_at TEXT,
|
|
43
|
+
failure_code TEXT,
|
|
44
|
+
UNIQUE (pipeline_run_id, stage, attempt)
|
|
45
|
+
) STRICT;
|
|
46
|
+
|
|
47
|
+
CREATE TABLE human_requests (
|
|
48
|
+
id TEXT PRIMARY KEY,
|
|
49
|
+
project_id TEXT NOT NULL REFERENCES projects(id) ON DELETE RESTRICT,
|
|
50
|
+
work_item_id TEXT NOT NULL REFERENCES work_items(id) ON DELETE RESTRICT,
|
|
51
|
+
stage_attempt_id TEXT NOT NULL REFERENCES stage_attempts(id) ON DELETE RESTRICT,
|
|
52
|
+
kind TEXT NOT NULL CHECK (kind IN ('SINGLE_CHOICE', 'MULTIPLE_CHOICE', 'CONFIRMATION', 'FREE_TEXT')),
|
|
53
|
+
blocking INTEGER NOT NULL CHECK (blocking IN (0, 1)),
|
|
54
|
+
title TEXT NOT NULL CHECK (length(title) BETWEEN 1 AND 200),
|
|
55
|
+
context TEXT NOT NULL CHECK (length(context) BETWEEN 1 AND 4000),
|
|
56
|
+
recommendation TEXT,
|
|
57
|
+
allow_other INTEGER NOT NULL CHECK (allow_other IN (0, 1)),
|
|
58
|
+
status TEXT NOT NULL CHECK (status IN ('OPEN', 'CLAIMED', 'SNOOZED', 'RESOLVED', 'EXPIRED', 'CANCELLED')),
|
|
59
|
+
version INTEGER NOT NULL CHECK (version > 0),
|
|
60
|
+
created_at TEXT NOT NULL,
|
|
61
|
+
resolved_at TEXT
|
|
62
|
+
) STRICT;
|
|
63
|
+
|
|
64
|
+
CREATE TABLE human_request_options (
|
|
65
|
+
human_request_id TEXT NOT NULL REFERENCES human_requests(id) ON DELETE CASCADE,
|
|
66
|
+
ordinal INTEGER NOT NULL CHECK (ordinal >= 0),
|
|
67
|
+
id TEXT NOT NULL,
|
|
68
|
+
label TEXT NOT NULL CHECK (length(label) BETWEEN 1 AND 200),
|
|
69
|
+
consequence TEXT NOT NULL CHECK (length(consequence) BETWEEN 1 AND 4000),
|
|
70
|
+
recommended INTEGER NOT NULL CHECK (recommended IN (0, 1)),
|
|
71
|
+
PRIMARY KEY (human_request_id, ordinal),
|
|
72
|
+
UNIQUE (human_request_id, id)
|
|
73
|
+
) STRICT;
|
|
74
|
+
|
|
75
|
+
CREATE TABLE decisions (
|
|
76
|
+
id TEXT PRIMARY KEY,
|
|
77
|
+
schema_version INTEGER NOT NULL CHECK (schema_version = 1),
|
|
78
|
+
project_id TEXT NOT NULL REFERENCES projects(id) ON DELETE RESTRICT,
|
|
79
|
+
work_item_id TEXT NOT NULL REFERENCES work_items(id) ON DELETE RESTRICT,
|
|
80
|
+
human_request_id TEXT NOT NULL UNIQUE REFERENCES human_requests(id) ON DELETE RESTRICT,
|
|
81
|
+
answer_json TEXT NOT NULL CHECK (json_valid(answer_json)),
|
|
82
|
+
actor_type TEXT NOT NULL CHECK (actor_type IN ('HUMAN', 'SYSTEM')),
|
|
83
|
+
actor_id TEXT NOT NULL,
|
|
84
|
+
reason TEXT,
|
|
85
|
+
created_at TEXT NOT NULL
|
|
86
|
+
) STRICT;
|
|
87
|
+
|
|
88
|
+
CREATE TABLE workflow_dispatches (
|
|
89
|
+
id TEXT PRIMARY KEY,
|
|
90
|
+
project_id TEXT NOT NULL REFERENCES projects(id) ON DELETE RESTRICT,
|
|
91
|
+
work_item_id TEXT NOT NULL REFERENCES work_items(id) ON DELETE RESTRICT,
|
|
92
|
+
pipeline_run_id TEXT NOT NULL REFERENCES pipeline_runs(id) ON DELETE RESTRICT,
|
|
93
|
+
stage_attempt_id TEXT NOT NULL REFERENCES stage_attempts(id) ON DELETE RESTRICT,
|
|
94
|
+
mode TEXT NOT NULL CHECK (mode IN ('START', 'RESUME')),
|
|
95
|
+
status TEXT NOT NULL CHECK (status IN ('PENDING', 'COMPLETED', 'FAILED')),
|
|
96
|
+
created_at TEXT NOT NULL,
|
|
97
|
+
completed_at TEXT
|
|
98
|
+
) STRICT;
|
|
99
|
+
|
|
100
|
+
DROP TRIGGER events_are_append_only_update;
|
|
101
|
+
DROP TRIGGER events_are_append_only_delete;
|
|
102
|
+
DROP INDEX events_project_sequence_idx;
|
|
103
|
+
DROP INDEX events_aggregate_sequence_idx;
|
|
104
|
+
ALTER TABLE events RENAME TO events_v1;
|
|
105
|
+
|
|
106
|
+
CREATE TABLE events (
|
|
107
|
+
sequence INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
108
|
+
id TEXT NOT NULL UNIQUE,
|
|
109
|
+
schema_version INTEGER NOT NULL CHECK (schema_version = 1),
|
|
110
|
+
type TEXT NOT NULL CHECK (
|
|
111
|
+
type IN (
|
|
112
|
+
'PROJECT_REGISTERED', 'WORK_ITEM_CREATED', 'WORK_ITEM_UPDATED', 'WORK_ITEM_STATE_CHANGED',
|
|
113
|
+
'PIPELINE_STARTED', 'STAGE_ATTEMPT_CHANGED', 'HUMAN_REQUEST_OPENED',
|
|
114
|
+
'HUMAN_REQUEST_RESOLVED', 'PIPELINE_COMPLETED'
|
|
115
|
+
)
|
|
116
|
+
),
|
|
117
|
+
aggregate_type TEXT NOT NULL CHECK (aggregate_type IN ('PROJECT', 'WORK_ITEM')),
|
|
118
|
+
aggregate_id TEXT NOT NULL,
|
|
119
|
+
project_id TEXT NOT NULL REFERENCES projects(id) ON DELETE RESTRICT,
|
|
120
|
+
actor_type TEXT NOT NULL CHECK (actor_type IN ('HUMAN', 'SYSTEM')),
|
|
121
|
+
actor_id TEXT NOT NULL,
|
|
122
|
+
occurred_at TEXT NOT NULL,
|
|
123
|
+
correlation_id TEXT NOT NULL,
|
|
124
|
+
data_json TEXT NOT NULL CHECK (json_valid(data_json))
|
|
125
|
+
) STRICT;
|
|
126
|
+
|
|
127
|
+
INSERT INTO events (
|
|
128
|
+
sequence, id, schema_version, type, aggregate_type, aggregate_id, project_id,
|
|
129
|
+
actor_type, actor_id, occurred_at, correlation_id, data_json
|
|
130
|
+
)
|
|
131
|
+
SELECT
|
|
132
|
+
sequence, id, schema_version, type, aggregate_type, aggregate_id, project_id,
|
|
133
|
+
actor_type, actor_id, occurred_at, correlation_id, data_json
|
|
134
|
+
FROM events_v1;
|
|
135
|
+
|
|
136
|
+
DROP TABLE events_v1;
|
|
137
|
+
|
|
138
|
+
CREATE INDEX pipeline_runs_work_item_created_idx ON pipeline_runs(work_item_id, created_at DESC, id);
|
|
139
|
+
CREATE INDEX stage_attempts_run_idx ON stage_attempts(pipeline_run_id, attempt, id);
|
|
140
|
+
CREATE INDEX human_requests_project_status_idx ON human_requests(project_id, status, created_at, id);
|
|
141
|
+
CREATE INDEX human_requests_work_item_idx ON human_requests(work_item_id, created_at, id);
|
|
142
|
+
CREATE INDEX decisions_work_item_idx ON decisions(work_item_id, created_at, id);
|
|
143
|
+
CREATE INDEX workflow_dispatches_pending_idx ON workflow_dispatches(status, created_at, id);
|
|
144
|
+
CREATE INDEX events_project_sequence_idx ON events(project_id, sequence);
|
|
145
|
+
CREATE INDEX events_aggregate_sequence_idx ON events(aggregate_id, sequence);
|
|
146
|
+
|
|
147
|
+
CREATE TRIGGER events_are_append_only_update
|
|
148
|
+
BEFORE UPDATE ON events
|
|
149
|
+
BEGIN
|
|
150
|
+
SELECT RAISE(ABORT, 'events are append-only');
|
|
151
|
+
END;
|
|
152
|
+
|
|
153
|
+
CREATE TRIGGER events_are_append_only_delete
|
|
154
|
+
BEFORE DELETE ON events
|
|
155
|
+
BEGIN
|
|
156
|
+
SELECT RAISE(ABORT, 'events are append-only');
|
|
157
|
+
END;
|
|
158
|
+
|
|
159
|
+
CREATE TRIGGER decisions_are_append_only_update
|
|
160
|
+
BEFORE UPDATE ON decisions
|
|
161
|
+
BEGIN
|
|
162
|
+
SELECT RAISE(ABORT, 'decisions are append-only');
|
|
163
|
+
END;
|
|
164
|
+
|
|
165
|
+
CREATE TRIGGER decisions_are_append_only_delete
|
|
166
|
+
BEFORE DELETE ON decisions
|
|
167
|
+
BEGIN
|
|
168
|
+
SELECT RAISE(ABORT, 'decisions are append-only');
|
|
169
|
+
END;
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
ALTER TABLE pipeline_runs ADD COLUMN orchestration_status TEXT CHECK (
|
|
2
|
+
orchestration_status IS NULL OR orchestration_status IN (
|
|
3
|
+
'RUNNING', 'WAITING_HUMAN', 'SOFT_PAUSED', 'HARD_PAUSED', 'INTERRUPTED',
|
|
4
|
+
'SUCCEEDED', 'FAILED', 'CANCELLED'
|
|
5
|
+
)
|
|
6
|
+
);
|
|
7
|
+
|
|
8
|
+
UPDATE pipeline_runs SET orchestration_status = status WHERE orchestration_status IS NULL;
|
|
9
|
+
|
|
10
|
+
CREATE TABLE budget_policies (
|
|
11
|
+
id TEXT PRIMARY KEY,
|
|
12
|
+
schema_version INTEGER NOT NULL CHECK (schema_version = 1),
|
|
13
|
+
project_id TEXT NOT NULL REFERENCES projects(id) ON DELETE RESTRICT,
|
|
14
|
+
work_item_id TEXT NOT NULL REFERENCES work_items(id) ON DELETE RESTRICT,
|
|
15
|
+
pipeline_run_id TEXT NOT NULL REFERENCES pipeline_runs(id) ON DELETE RESTRICT,
|
|
16
|
+
revision INTEGER NOT NULL CHECK (revision > 0),
|
|
17
|
+
max_estimated_tokens INTEGER NOT NULL CHECK (max_estimated_tokens > 0),
|
|
18
|
+
warning_thresholds_json TEXT NOT NULL CHECK (json_valid(warning_thresholds_json)),
|
|
19
|
+
actor_type TEXT NOT NULL CHECK (actor_type IN ('HUMAN', 'SYSTEM')),
|
|
20
|
+
actor_id TEXT NOT NULL,
|
|
21
|
+
created_at TEXT NOT NULL,
|
|
22
|
+
UNIQUE (pipeline_run_id, revision)
|
|
23
|
+
) STRICT;
|
|
24
|
+
|
|
25
|
+
INSERT INTO budget_policies (
|
|
26
|
+
id, schema_version, project_id, work_item_id, pipeline_run_id, revision,
|
|
27
|
+
max_estimated_tokens, warning_thresholds_json, actor_type, actor_id, created_at
|
|
28
|
+
)
|
|
29
|
+
SELECT
|
|
30
|
+
'budget-migrated-' || id, 1, project_id, work_item_id, id, 1,
|
|
31
|
+
100, '[0.5,0.8,0.95]', 'SYSTEM', 'migration-0003', created_at
|
|
32
|
+
FROM pipeline_runs;
|
|
33
|
+
|
|
34
|
+
CREATE TABLE usage_records (
|
|
35
|
+
id TEXT PRIMARY KEY,
|
|
36
|
+
schema_version INTEGER NOT NULL CHECK (schema_version = 1),
|
|
37
|
+
project_id TEXT NOT NULL REFERENCES projects(id) ON DELETE RESTRICT,
|
|
38
|
+
work_item_id TEXT NOT NULL REFERENCES work_items(id) ON DELETE RESTRICT,
|
|
39
|
+
pipeline_run_id TEXT NOT NULL REFERENCES pipeline_runs(id) ON DELETE RESTRICT,
|
|
40
|
+
stage_attempt_id TEXT NOT NULL REFERENCES stage_attempts(id) ON DELETE RESTRICT,
|
|
41
|
+
budget_policy_id TEXT NOT NULL REFERENCES budget_policies(id) ON DELETE RESTRICT,
|
|
42
|
+
kind TEXT NOT NULL CHECK (kind = 'ESTIMATED_TOKENS'),
|
|
43
|
+
amount INTEGER NOT NULL CHECK (amount > 0),
|
|
44
|
+
quality TEXT NOT NULL CHECK (quality IN ('ACTUAL', 'PROVIDER_ESTIMATE', 'LOOMRAIL_ESTIMATE')),
|
|
45
|
+
recorded_at TEXT NOT NULL
|
|
46
|
+
) STRICT;
|
|
47
|
+
|
|
48
|
+
CREATE TABLE recovery_reports (
|
|
49
|
+
id TEXT PRIMARY KEY,
|
|
50
|
+
schema_version INTEGER NOT NULL CHECK (schema_version = 1),
|
|
51
|
+
project_id TEXT NOT NULL REFERENCES projects(id) ON DELETE RESTRICT,
|
|
52
|
+
work_item_id TEXT NOT NULL REFERENCES work_items(id) ON DELETE RESTRICT,
|
|
53
|
+
pipeline_run_id TEXT NOT NULL REFERENCES pipeline_runs(id) ON DELETE RESTRICT,
|
|
54
|
+
stage_attempt_id TEXT NOT NULL REFERENCES stage_attempts(id) ON DELETE RESTRICT,
|
|
55
|
+
previous_status TEXT NOT NULL CHECK (previous_status = 'RUNNING'),
|
|
56
|
+
recovered_status TEXT NOT NULL CHECK (recovered_status = 'INTERRUPTED'),
|
|
57
|
+
reason TEXT NOT NULL CHECK (reason = 'DAEMON_RESTART'),
|
|
58
|
+
created_at TEXT NOT NULL,
|
|
59
|
+
UNIQUE (stage_attempt_id, reason)
|
|
60
|
+
) STRICT;
|
|
61
|
+
|
|
62
|
+
DROP TRIGGER events_are_append_only_update;
|
|
63
|
+
DROP TRIGGER events_are_append_only_delete;
|
|
64
|
+
DROP INDEX events_project_sequence_idx;
|
|
65
|
+
DROP INDEX events_aggregate_sequence_idx;
|
|
66
|
+
ALTER TABLE events RENAME TO events_v2;
|
|
67
|
+
|
|
68
|
+
CREATE TABLE events (
|
|
69
|
+
sequence INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
70
|
+
id TEXT NOT NULL UNIQUE,
|
|
71
|
+
schema_version INTEGER NOT NULL CHECK (schema_version = 1),
|
|
72
|
+
type TEXT NOT NULL CHECK (
|
|
73
|
+
type IN (
|
|
74
|
+
'PROJECT_REGISTERED', 'WORK_ITEM_CREATED', 'WORK_ITEM_UPDATED', 'WORK_ITEM_STATE_CHANGED',
|
|
75
|
+
'PIPELINE_STARTED', 'STAGE_ATTEMPT_CHANGED', 'HUMAN_REQUEST_OPENED',
|
|
76
|
+
'HUMAN_REQUEST_RESOLVED', 'USAGE_RECORDED', 'BUDGET_THRESHOLD_REACHED',
|
|
77
|
+
'PIPELINE_PAUSED', 'PIPELINE_RESUMED', 'PIPELINE_CANCELLED',
|
|
78
|
+
'BUDGET_OVERRIDE_APPROVED', 'RECOVERY_REPORT_CREATED', 'PIPELINE_COMPLETED'
|
|
79
|
+
)
|
|
80
|
+
),
|
|
81
|
+
aggregate_type TEXT NOT NULL CHECK (aggregate_type IN ('PROJECT', 'WORK_ITEM')),
|
|
82
|
+
aggregate_id TEXT NOT NULL,
|
|
83
|
+
project_id TEXT NOT NULL REFERENCES projects(id) ON DELETE RESTRICT,
|
|
84
|
+
actor_type TEXT NOT NULL CHECK (actor_type IN ('HUMAN', 'SYSTEM')),
|
|
85
|
+
actor_id TEXT NOT NULL,
|
|
86
|
+
occurred_at TEXT NOT NULL,
|
|
87
|
+
correlation_id TEXT NOT NULL,
|
|
88
|
+
data_json TEXT NOT NULL CHECK (json_valid(data_json))
|
|
89
|
+
) STRICT;
|
|
90
|
+
|
|
91
|
+
INSERT INTO events (
|
|
92
|
+
sequence, id, schema_version, type, aggregate_type, aggregate_id, project_id,
|
|
93
|
+
actor_type, actor_id, occurred_at, correlation_id, data_json
|
|
94
|
+
)
|
|
95
|
+
SELECT
|
|
96
|
+
sequence, id, schema_version, type, aggregate_type, aggregate_id, project_id,
|
|
97
|
+
actor_type, actor_id, occurred_at, correlation_id, data_json
|
|
98
|
+
FROM events_v2;
|
|
99
|
+
|
|
100
|
+
DROP TABLE events_v2;
|
|
101
|
+
|
|
102
|
+
CREATE INDEX budget_policies_run_revision_idx ON budget_policies(pipeline_run_id, revision DESC);
|
|
103
|
+
CREATE INDEX usage_records_run_recorded_idx ON usage_records(pipeline_run_id, recorded_at, id);
|
|
104
|
+
CREATE INDEX recovery_reports_work_item_created_idx ON recovery_reports(work_item_id, created_at, id);
|
|
105
|
+
CREATE INDEX events_project_sequence_idx ON events(project_id, sequence);
|
|
106
|
+
CREATE INDEX events_aggregate_sequence_idx ON events(aggregate_id, sequence);
|
|
107
|
+
|
|
108
|
+
CREATE TRIGGER events_are_append_only_update
|
|
109
|
+
BEFORE UPDATE ON events
|
|
110
|
+
BEGIN
|
|
111
|
+
SELECT RAISE(ABORT, 'events are append-only');
|
|
112
|
+
END;
|
|
113
|
+
|
|
114
|
+
CREATE TRIGGER events_are_append_only_delete
|
|
115
|
+
BEFORE DELETE ON events
|
|
116
|
+
BEGIN
|
|
117
|
+
SELECT RAISE(ABORT, 'events are append-only');
|
|
118
|
+
END;
|
|
119
|
+
|
|
120
|
+
CREATE TRIGGER budget_policies_are_append_only_update
|
|
121
|
+
BEFORE UPDATE ON budget_policies
|
|
122
|
+
BEGIN
|
|
123
|
+
SELECT RAISE(ABORT, 'budget policies are append-only');
|
|
124
|
+
END;
|
|
125
|
+
|
|
126
|
+
CREATE TRIGGER budget_policies_are_append_only_delete
|
|
127
|
+
BEFORE DELETE ON budget_policies
|
|
128
|
+
BEGIN
|
|
129
|
+
SELECT RAISE(ABORT, 'budget policies are append-only');
|
|
130
|
+
END;
|
|
131
|
+
|
|
132
|
+
CREATE TRIGGER usage_records_are_append_only_update
|
|
133
|
+
BEFORE UPDATE ON usage_records
|
|
134
|
+
BEGIN
|
|
135
|
+
SELECT RAISE(ABORT, 'usage records are append-only');
|
|
136
|
+
END;
|
|
137
|
+
|
|
138
|
+
CREATE TRIGGER usage_records_are_append_only_delete
|
|
139
|
+
BEFORE DELETE ON usage_records
|
|
140
|
+
BEGIN
|
|
141
|
+
SELECT RAISE(ABORT, 'usage records are append-only');
|
|
142
|
+
END;
|
|
143
|
+
|
|
144
|
+
CREATE TRIGGER recovery_reports_are_append_only_update
|
|
145
|
+
BEFORE UPDATE ON recovery_reports
|
|
146
|
+
BEGIN
|
|
147
|
+
SELECT RAISE(ABORT, 'recovery reports are append-only');
|
|
148
|
+
END;
|
|
149
|
+
|
|
150
|
+
CREATE TRIGGER recovery_reports_are_append_only_delete
|
|
151
|
+
BEFORE DELETE ON recovery_reports
|
|
152
|
+
BEGIN
|
|
153
|
+
SELECT RAISE(ABORT, 'recovery reports are append-only');
|
|
154
|
+
END;
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
DROP TRIGGER events_are_append_only_update;
|
|
2
|
+
DROP TRIGGER commands_are_append_only_update;
|
|
3
|
+
|
|
4
|
+
UPDATE events
|
|
5
|
+
SET data_json = json_set(
|
|
6
|
+
data_json,
|
|
7
|
+
'$.budgetPolicy',
|
|
8
|
+
json_object(
|
|
9
|
+
'schemaVersion', 1,
|
|
10
|
+
'id', 'budget-migrated-' || json_extract(data_json, '$.run.id'),
|
|
11
|
+
'projectId', json_extract(data_json, '$.run.projectId'),
|
|
12
|
+
'workItemId', json_extract(data_json, '$.run.workItemId'),
|
|
13
|
+
'pipelineRunId', json_extract(data_json, '$.run.id'),
|
|
14
|
+
'revision', 1,
|
|
15
|
+
'maxEstimatedTokens', 100,
|
|
16
|
+
'warningThresholds', json('[0.5,0.8,0.95]'),
|
|
17
|
+
'createdBy', json_object('type', 'SYSTEM', 'id', 'migration-0003'),
|
|
18
|
+
'createdAt', json_extract(data_json, '$.run.createdAt')
|
|
19
|
+
)
|
|
20
|
+
)
|
|
21
|
+
WHERE type = 'PIPELINE_STARTED'
|
|
22
|
+
AND json_type(data_json, '$.budgetPolicy') IS NULL;
|
|
23
|
+
|
|
24
|
+
UPDATE commands
|
|
25
|
+
SET result_json = json_set(
|
|
26
|
+
result_json,
|
|
27
|
+
'$.budgetPolicy',
|
|
28
|
+
json_object(
|
|
29
|
+
'schemaVersion', 1,
|
|
30
|
+
'id', 'budget-migrated-' || json_extract(result_json, '$.run.id'),
|
|
31
|
+
'projectId', json_extract(result_json, '$.run.projectId'),
|
|
32
|
+
'workItemId', json_extract(result_json, '$.run.workItemId'),
|
|
33
|
+
'pipelineRunId', json_extract(result_json, '$.run.id'),
|
|
34
|
+
'revision', 1,
|
|
35
|
+
'maxEstimatedTokens', 100,
|
|
36
|
+
'warningThresholds', json('[0.5,0.8,0.95]'),
|
|
37
|
+
'createdBy', json_object('type', 'SYSTEM', 'id', 'migration-0003'),
|
|
38
|
+
'createdAt', json_extract(result_json, '$.run.createdAt')
|
|
39
|
+
),
|
|
40
|
+
'$.events[0].data.budgetPolicy',
|
|
41
|
+
json_object(
|
|
42
|
+
'schemaVersion', 1,
|
|
43
|
+
'id', 'budget-migrated-' || json_extract(result_json, '$.run.id'),
|
|
44
|
+
'projectId', json_extract(result_json, '$.run.projectId'),
|
|
45
|
+
'workItemId', json_extract(result_json, '$.run.workItemId'),
|
|
46
|
+
'pipelineRunId', json_extract(result_json, '$.run.id'),
|
|
47
|
+
'revision', 1,
|
|
48
|
+
'maxEstimatedTokens', 100,
|
|
49
|
+
'warningThresholds', json('[0.5,0.8,0.95]'),
|
|
50
|
+
'createdBy', json_object('type', 'SYSTEM', 'id', 'migration-0003'),
|
|
51
|
+
'createdAt', json_extract(result_json, '$.run.createdAt')
|
|
52
|
+
)
|
|
53
|
+
)
|
|
54
|
+
WHERE command_type = 'START_MOCK_PIPELINE'
|
|
55
|
+
AND json_type(result_json, '$.budgetPolicy') IS NULL;
|
|
56
|
+
|
|
57
|
+
UPDATE commands
|
|
58
|
+
SET result_json = json_set(result_json, '$.usageRecords', json('[]'))
|
|
59
|
+
WHERE command_type = 'APPLY_MOCK_PROVIDER_OUTCOME'
|
|
60
|
+
AND json_type(result_json, '$.usageRecords') IS NULL;
|
|
61
|
+
|
|
62
|
+
CREATE TRIGGER events_are_append_only_update
|
|
63
|
+
BEFORE UPDATE ON events
|
|
64
|
+
BEGIN
|
|
65
|
+
SELECT RAISE(ABORT, 'events are append-only');
|
|
66
|
+
END;
|
|
67
|
+
|
|
68
|
+
CREATE TRIGGER commands_are_append_only_update
|
|
69
|
+
BEFORE UPDATE ON commands
|
|
70
|
+
BEGIN
|
|
71
|
+
SELECT RAISE(ABORT, 'commands are append-only');
|
|
72
|
+
END;
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
CREATE TABLE evidence_artifacts (
|
|
2
|
+
id TEXT PRIMARY KEY,
|
|
3
|
+
schema_version INTEGER NOT NULL CHECK (schema_version = 1),
|
|
4
|
+
project_id TEXT NOT NULL REFERENCES projects(id) ON DELETE RESTRICT,
|
|
5
|
+
work_item_id TEXT NOT NULL REFERENCES work_items(id) ON DELETE RESTRICT,
|
|
6
|
+
pipeline_run_id TEXT NOT NULL REFERENCES pipeline_runs(id) ON DELETE RESTRICT,
|
|
7
|
+
stage_attempt_id TEXT NOT NULL REFERENCES stage_attempts(id) ON DELETE RESTRICT,
|
|
8
|
+
stage TEXT NOT NULL CHECK (stage IN ('REVIEW', 'QA')),
|
|
9
|
+
kind TEXT NOT NULL CHECK (kind IN ('REVIEW_REPORT', 'QA_REPORT')),
|
|
10
|
+
status TEXT NOT NULL CHECK (status = 'PASSED'),
|
|
11
|
+
provider TEXT NOT NULL CHECK (provider = 'MOCK'),
|
|
12
|
+
title TEXT NOT NULL CHECK (length(title) BETWEEN 1 AND 200),
|
|
13
|
+
summary TEXT NOT NULL CHECK (length(summary) BETWEEN 1 AND 4000),
|
|
14
|
+
checks_json TEXT NOT NULL CHECK (json_valid(checks_json)),
|
|
15
|
+
created_at TEXT NOT NULL,
|
|
16
|
+
UNIQUE (pipeline_run_id, kind)
|
|
17
|
+
) STRICT;
|
|
18
|
+
|
|
19
|
+
CREATE TABLE acceptance_packages (
|
|
20
|
+
id TEXT PRIMARY KEY,
|
|
21
|
+
schema_version INTEGER NOT NULL CHECK (schema_version = 1),
|
|
22
|
+
project_id TEXT NOT NULL REFERENCES projects(id) ON DELETE RESTRICT,
|
|
23
|
+
work_item_id TEXT NOT NULL REFERENCES work_items(id) ON DELETE RESTRICT,
|
|
24
|
+
pipeline_run_id TEXT NOT NULL UNIQUE REFERENCES pipeline_runs(id) ON DELETE RESTRICT,
|
|
25
|
+
stage_attempt_id TEXT NOT NULL REFERENCES stage_attempts(id) ON DELETE RESTRICT,
|
|
26
|
+
human_request_id TEXT NOT NULL UNIQUE REFERENCES human_requests(id) ON DELETE RESTRICT,
|
|
27
|
+
status TEXT NOT NULL CHECK (status IN ('PENDING', 'ACCEPTED', 'RETURNED', 'REJECTED')),
|
|
28
|
+
criteria_json TEXT NOT NULL CHECK (json_valid(criteria_json)),
|
|
29
|
+
artifact_ids_json TEXT NOT NULL CHECK (json_valid(artifact_ids_json)),
|
|
30
|
+
release_note TEXT NOT NULL CHECK (length(release_note) BETWEEN 1 AND 4000),
|
|
31
|
+
verify_instructions_json TEXT NOT NULL CHECK (json_valid(verify_instructions_json)),
|
|
32
|
+
version INTEGER NOT NULL CHECK (version > 0),
|
|
33
|
+
created_at TEXT NOT NULL,
|
|
34
|
+
resolved_at TEXT,
|
|
35
|
+
resolved_by_type TEXT CHECK (resolved_by_type IS NULL OR resolved_by_type IN ('HUMAN', 'SYSTEM')),
|
|
36
|
+
resolved_by_id TEXT,
|
|
37
|
+
resolution_reason TEXT,
|
|
38
|
+
CHECK ((resolved_by_type IS NULL) = (resolved_by_id IS NULL))
|
|
39
|
+
) STRICT;
|
|
40
|
+
|
|
41
|
+
CREATE INDEX evidence_artifacts_run_created_idx
|
|
42
|
+
ON evidence_artifacts(pipeline_run_id, created_at, id);
|
|
43
|
+
CREATE INDEX acceptance_packages_work_item_created_idx
|
|
44
|
+
ON acceptance_packages(work_item_id, created_at DESC, id);
|
|
45
|
+
|
|
46
|
+
CREATE TRIGGER evidence_artifacts_are_append_only_update
|
|
47
|
+
BEFORE UPDATE ON evidence_artifacts
|
|
48
|
+
BEGIN
|
|
49
|
+
SELECT RAISE(ABORT, 'evidence artifacts are append-only');
|
|
50
|
+
END;
|
|
51
|
+
|
|
52
|
+
CREATE TRIGGER evidence_artifacts_are_append_only_delete
|
|
53
|
+
BEFORE DELETE ON evidence_artifacts
|
|
54
|
+
BEGIN
|
|
55
|
+
SELECT RAISE(ABORT, 'evidence artifacts are append-only');
|
|
56
|
+
END;
|
|
57
|
+
|
|
58
|
+
DROP TRIGGER commands_are_append_only_update;
|
|
59
|
+
|
|
60
|
+
UPDATE commands
|
|
61
|
+
SET result_json = json_set(
|
|
62
|
+
result_json,
|
|
63
|
+
'$.artifacts', json('[]'),
|
|
64
|
+
'$.acceptancePackage', json('null')
|
|
65
|
+
)
|
|
66
|
+
WHERE command_type = 'APPLY_MOCK_PROVIDER_OUTCOME'
|
|
67
|
+
AND json_type(result_json, '$.artifacts') IS NULL;
|
|
68
|
+
|
|
69
|
+
CREATE TRIGGER commands_are_append_only_update
|
|
70
|
+
BEFORE UPDATE ON commands
|
|
71
|
+
BEGIN
|
|
72
|
+
SELECT RAISE(ABORT, 'commands are append-only');
|
|
73
|
+
END;
|
|
74
|
+
|
|
75
|
+
DROP TRIGGER events_are_append_only_update;
|
|
76
|
+
DROP TRIGGER events_are_append_only_delete;
|
|
77
|
+
DROP INDEX events_project_sequence_idx;
|
|
78
|
+
DROP INDEX events_aggregate_sequence_idx;
|
|
79
|
+
ALTER TABLE events RENAME TO events_v4;
|
|
80
|
+
|
|
81
|
+
CREATE TABLE events (
|
|
82
|
+
sequence INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
83
|
+
id TEXT NOT NULL UNIQUE,
|
|
84
|
+
schema_version INTEGER NOT NULL CHECK (schema_version = 1),
|
|
85
|
+
type TEXT NOT NULL CHECK (
|
|
86
|
+
type IN (
|
|
87
|
+
'PROJECT_REGISTERED', 'WORK_ITEM_CREATED', 'WORK_ITEM_UPDATED', 'WORK_ITEM_STATE_CHANGED',
|
|
88
|
+
'PIPELINE_STARTED', 'STAGE_ATTEMPT_CHANGED', 'HUMAN_REQUEST_OPENED',
|
|
89
|
+
'HUMAN_REQUEST_RESOLVED', 'USAGE_RECORDED', 'BUDGET_THRESHOLD_REACHED',
|
|
90
|
+
'PIPELINE_PAUSED', 'PIPELINE_RESUMED', 'PIPELINE_CANCELLED',
|
|
91
|
+
'BUDGET_OVERRIDE_APPROVED', 'RECOVERY_REPORT_CREATED', 'PIPELINE_COMPLETED',
|
|
92
|
+
'EVIDENCE_ARTIFACT_RECORDED', 'ACCEPTANCE_REQUESTED', 'ACCEPTANCE_RESOLVED'
|
|
93
|
+
)
|
|
94
|
+
),
|
|
95
|
+
aggregate_type TEXT NOT NULL CHECK (aggregate_type IN ('PROJECT', 'WORK_ITEM')),
|
|
96
|
+
aggregate_id TEXT NOT NULL,
|
|
97
|
+
project_id TEXT NOT NULL REFERENCES projects(id) ON DELETE RESTRICT,
|
|
98
|
+
actor_type TEXT NOT NULL CHECK (actor_type IN ('HUMAN', 'SYSTEM')),
|
|
99
|
+
actor_id TEXT NOT NULL,
|
|
100
|
+
occurred_at TEXT NOT NULL,
|
|
101
|
+
correlation_id TEXT NOT NULL,
|
|
102
|
+
data_json TEXT NOT NULL CHECK (json_valid(data_json))
|
|
103
|
+
) STRICT;
|
|
104
|
+
|
|
105
|
+
INSERT INTO events (
|
|
106
|
+
sequence, id, schema_version, type, aggregate_type, aggregate_id, project_id,
|
|
107
|
+
actor_type, actor_id, occurred_at, correlation_id, data_json
|
|
108
|
+
)
|
|
109
|
+
SELECT
|
|
110
|
+
sequence, id, schema_version, type, aggregate_type, aggregate_id, project_id,
|
|
111
|
+
actor_type, actor_id, occurred_at, correlation_id, data_json
|
|
112
|
+
FROM events_v4;
|
|
113
|
+
|
|
114
|
+
DROP TABLE events_v4;
|
|
115
|
+
|
|
116
|
+
CREATE INDEX events_project_sequence_idx ON events(project_id, sequence);
|
|
117
|
+
CREATE INDEX events_aggregate_sequence_idx ON events(aggregate_id, sequence);
|
|
118
|
+
|
|
119
|
+
CREATE TRIGGER events_are_append_only_update
|
|
120
|
+
BEFORE UPDATE ON events
|
|
121
|
+
BEGIN
|
|
122
|
+
SELECT RAISE(ABORT, 'events are append-only');
|
|
123
|
+
END;
|
|
124
|
+
|
|
125
|
+
CREATE TRIGGER events_are_append_only_delete
|
|
126
|
+
BEFORE DELETE ON events
|
|
127
|
+
BEGIN
|
|
128
|
+
SELECT RAISE(ABORT, 'events are append-only');
|
|
129
|
+
END;
|