@zq-silk/yui 0.5.3 → 0.6.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/README.md +5 -5
- package/dist/agent/managedRuntimeEnvironment.js +2 -1
- package/dist/cli/agentConfigurationPicker.js +1 -1
- package/dist/cli/commandCatalog.js +251 -13
- package/dist/cli/updateOrchestrator.js +8 -0
- package/dist/cli/updatePorts.js +76 -22
- package/dist/cli.js +264 -20
- package/dist/commands/configCommands.js +83 -9
- package/dist/commands/controllerCommands.js +103 -0
- package/dist/commands/deliveryGuardPreflight.js +35 -0
- package/dist/commands/durableJobCommands.js +231 -0
- package/dist/commands/executionAuditCommands.js +193 -0
- package/dist/commands/grantCommands.js +374 -0
- package/dist/commands/projectCommands.js +119 -81
- package/dist/commands/releaseCommands.js +444 -0
- package/dist/commands/resourcesCommands.js +274 -0
- package/dist/commands/sessionCommands.js +104 -0
- package/dist/commands/taskActor.js +117 -0
- package/dist/commands/taskChangeSetCommands.js +60 -0
- package/dist/commands/taskCommands.js +610 -201
- package/dist/commands/taskCompletionGate.js +78 -1
- package/dist/commands/taskContextCommand.js +24 -6
- package/dist/commands/taskInputCommands.js +1 -1
- package/dist/commands/taskIntegrationCommands.js +136 -33
- package/dist/commands/taskIntegrationQueueCommands.js +228 -0
- package/dist/commands/taskNextActionCommand.js +85 -0
- package/dist/commands/taskOverlapCommands.js +120 -0
- package/dist/commands/taskOverviewCommand.js +36 -8
- package/dist/commands/telemetryCommands.js +330 -0
- package/dist/commands/workflowCommands.js +415 -0
- package/dist/config/yuiConfig.js +60 -0
- package/dist/controller/clientRuntime.js +42 -1
- package/dist/controller/controller.js +413 -61
- package/dist/controller/controllerMain.js +25 -2
- package/dist/controller/domainIdentity.js +16 -8
- package/dist/controller/ephemeralResourceReaper.js +2 -1
- package/dist/controller/fileSchedulerStoreAdapter.js +423 -31
- package/dist/controller/handoverCandidate.js +168 -0
- package/dist/controller/jobClient.js +102 -0
- package/dist/controller/jobControl.js +613 -0
- package/dist/controller/jobSupervisor.js +498 -0
- package/dist/controller/providerHookRunFence.js +34 -5
- package/dist/controller/resourceCleanupLinux.js +18 -9
- package/dist/controller/resourceInventoryLinux.js +90 -39
- package/dist/controller/resourceInventoryRpc.js +85 -0
- package/dist/controller/resourceInventoryWorker.js +50 -0
- package/dist/controller/runtime.js +238 -22
- package/dist/controller/runtimeEventInbox.js +234 -57
- package/dist/controller/runtimeEventProcessor.js +549 -42
- package/dist/controller/sessionOwnerReconciliation.js +321 -0
- package/dist/core/boundedRpc.js +475 -0
- package/dist/core/controllerServer.js +416 -27
- package/dist/core/controllerTelemetry.js +167 -0
- package/dist/doctor/doctor.js +113 -16
- package/dist/domain/validation.js +9 -0
- package/dist/execution/executionGroup.js +40 -3
- package/dist/executor/agentExecutor.js +6 -3
- package/dist/executor/effectiveLaunch.js +52 -0
- package/dist/executor/executorRegistry.js +50 -0
- package/dist/executor/fileRoleLaunchPlanner.js +61 -6
- package/dist/grant/capabilityGrant.js +282 -0
- package/dist/integration/changeSet.js +16 -3
- package/dist/integration/changeSetManifest.js +46 -0
- package/dist/integration/gitIntegrationService.js +528 -147
- package/dist/integration/integrationAttempt.js +54 -5
- package/dist/integration/integrationQueueEntry.js +221 -0
- package/dist/integration/integrationQueueService.js +955 -0
- package/dist/integration/manifestTags.js +99 -0
- package/dist/integration/overlapDiagnostics.js +211 -0
- package/dist/job/durableJob.js +449 -0
- package/dist/job/jobRunner.js +350 -0
- package/dist/lifecycle/exactRunTerminalization.js +24 -2
- package/dist/lifecycle/providerErrorClass.js +126 -0
- package/dist/message/message.js +16 -3
- package/dist/observability/executionAudit.js +545 -0
- package/dist/observability/faultClassification.js +160 -0
- package/dist/observability/runtimeIdentity.js +367 -0
- package/dist/release/fakeReleasePorts.js +55 -0
- package/dist/release/releaseHandover.js +475 -0
- package/dist/release/releaseIdempotencyStore.js +165 -0
- package/dist/release/releaseWorkflow.js +459 -0
- package/dist/release/releaseWorkflowEngine.js +688 -0
- package/dist/release/releaseWorkflowPorts.js +1720 -0
- package/dist/release/runtimeRelease.js +495 -0
- package/dist/release/workflowFileLock.js +218 -0
- package/dist/repository/gitWorkspace.js +177 -1
- package/dist/repository/projectMaintenanceLock.js +315 -0
- package/dist/repository/taskWorkspaceCoordinator.js +87 -17
- package/dist/repository/taskWorkspacePreparer.js +1091 -517
- package/dist/resources/autoResourceGc.js +116 -0
- package/dist/resources/liveReferences.js +574 -0
- package/dist/resources/resourceDiscovery.js +477 -0
- package/dist/resources/resourceGc.js +645 -0
- package/dist/resources/resourceRegistrar.js +256 -0
- package/dist/resources/resourceRegistry.js +150 -0
- package/dist/resources/resourceRegistryStore.js +41 -0
- package/dist/resources/resourceTypes.js +42 -0
- package/dist/resources/sqliteResourceRegistry.js +111 -0
- package/dist/review/reviewConfig.js +10 -0
- package/dist/review/reviewFinding.js +240 -0
- package/dist/review/reviewFindingLedger.js +545 -0
- package/dist/review/reviewOutcomeClassifier.js +61 -0
- package/dist/review/reviewRound.js +56 -4
- package/dist/run/agentRun.js +80 -4
- package/dist/run/providerRetry.js +84 -0
- package/dist/run/providerRetryConfig.js +63 -0
- package/dist/run/yieldReceipt.js +65 -0
- package/dist/runtime/exactControlPlane.js +79 -2
- package/dist/runtime/index.js +4 -0
- package/dist/runtime/sessionOwnerIdentity.js +269 -0
- package/dist/runtime/sessionOwnerRegistry.js +132 -0
- package/dist/runtime/sessionReconciliation.js +93 -0
- package/dist/runtime/sessionTerminationGuard.js +211 -0
- package/dist/runtime/taskRuntimeIsolation.js +13 -0
- package/dist/runtime/tmuxAdapters.js +34 -1
- package/dist/scheduler/actionability.js +155 -0
- package/dist/scheduler/activeRoleRunDelivery.js +14 -5
- package/dist/scheduler/activeTaskProgress.js +60 -0
- package/dist/scheduler/leaderWakeupProcessor.js +22 -11
- package/dist/scheduler/roleRunStall.js +135 -29
- package/dist/scheduler/taskExecutionProjection.js +11 -0
- package/dist/setup/setupCommand.js +27 -4
- package/dist/storage/compatibleTaskStore.js +112 -5
- package/dist/storage/migration/productionRegistry.js +769 -1
- package/dist/storage/persistenceWorker.js +194 -0
- package/dist/storage/sqliteSchema.js +705 -0
- package/dist/storage/sqliteStore.js +1695 -0
- package/dist/storage/storageVersions.js +9 -2
- package/dist/storage/storeRpc.js +298 -0
- package/dist/storage/taskStore.js +982 -21
- package/dist/storage/upgrade/homeClassification.js +157 -12
- package/dist/storage/upgrade/migrationReceipt.js +67 -0
- package/dist/storage/upgrade/pseudoLayoutRepair.js +241 -0
- package/dist/storage/upgrade/recordVersions.js +10 -1
- package/dist/storage/upgrade/sqliteMigrationTarget.js +351 -0
- package/dist/storage/upgrade/sqliteRecordMigrationTarget.js +290 -0
- package/dist/storage/upgrade/sqliteStateMigration.js +713 -0
- package/dist/storage/upgrade/upgradeOrchestrator.js +510 -18
- package/dist/task/deliveryGuard.js +226 -0
- package/dist/task/nextAction.js +343 -0
- package/dist/task/repairWave.js +137 -0
- package/dist/task/taskRecordReference.js +6 -1
- package/dist/telemetry/sqliteTelemetryStore.js +387 -0
- package/dist/telemetry/telemetryCompaction.js +251 -0
- package/dist/telemetry/telemetryConfig.js +64 -0
- package/dist/telemetry/telemetryRouter.js +32 -0
- package/dist/telemetry/telemetryStore.js +19 -0
- package/dist/telemetry/telemetryWiring.js +33 -0
- package/dist/tmux/tmuxManager.js +20 -1
- package/dist/tmux/tmuxSocketEndpoint.js +20 -0
- package/dist/verification/gateArtifact.js +216 -0
- package/dist/verification/gateArtifactStore.js +87 -0
- package/dist/verification/verificationGateService.js +414 -0
- package/dist/verification/verificationPlan.js +308 -0
- package/dist/workspace/gitChangeSetCapture.js +12 -2
- package/dist/workspace/workItemChangeSetManager.js +60 -3
- package/package.json +2 -1
|
@@ -0,0 +1,705 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SQLite WAL control-plane schema and migration runner (task-21, work-item-3).
|
|
3
|
+
*
|
|
4
|
+
* This is the normalized, `task_id`-partitioned schema that replaces the single
|
|
5
|
+
* aggregate `state.json` document. It implements the logical schema from
|
|
6
|
+
* `docs/sqlite-control-plane-design.md` §4 (31 tables) plus the cross-task
|
|
7
|
+
* coordination tables the design references in §5:
|
|
8
|
+
*
|
|
9
|
+
* - `global_sequences` (§5.3): global record ID high-water marks.
|
|
10
|
+
* - `outbox` (§5.4): durable outbox with `UNIQUE(request_id)` for exactly-once.
|
|
11
|
+
* - `config`: the `YuiConfig` singleton (the design keeps `home_meta` for
|
|
12
|
+
* identity/revision/versions; config is a separate singleton).
|
|
13
|
+
*
|
|
14
|
+
* Record payloads are stored two ways, per §4: typed columns for fields that are
|
|
15
|
+
* queried/filtered/used-for-CAS, and a `payload` JSON column holding the full
|
|
16
|
+
* versioned record (including its family `schemaVersion`). The record axis of
|
|
17
|
+
* the three-axis versioning is therefore unchanged.
|
|
18
|
+
*
|
|
19
|
+
* The migration runner is idempotent: it records applied versions in
|
|
20
|
+
* `schema_migrations` and uses `CREATE TABLE IF NOT EXISTS`, so re-running on an
|
|
21
|
+
* already-current database is a no-op and a crash mid-migration is rolled back
|
|
22
|
+
* by SQLite (DDL is transactional) and re-applied on the next open.
|
|
23
|
+
*/
|
|
24
|
+
import { createHash } from "node:crypto";
|
|
25
|
+
/** The SQLite layout version (state.json layout 6 -> SQLite layout 7, per §8.1). */
|
|
26
|
+
export const SQLITE_LAYOUT_VERSION = 7;
|
|
27
|
+
/** The aggregate version of the normalized SQLite schema. */
|
|
28
|
+
export const SQLITE_AGGREGATE_VERSION = 1;
|
|
29
|
+
/** The current schema migration version. */
|
|
30
|
+
export const SQLITE_SCHEMA_VERSION = 9;
|
|
31
|
+
/** Telemetry retention bounds (§4.4). Open question 3 in §11; defaults from the design. */
|
|
32
|
+
export const TELEMETRY_KEEP_PER_GENERATION = 200;
|
|
33
|
+
export const TELEMETRY_RUN_CAP = 50_000;
|
|
34
|
+
/**
|
|
35
|
+
* Version 1 migration: creates every table and index.
|
|
36
|
+
*
|
|
37
|
+
* `synchronous`/`foreign_keys`/`busy_timeout` are per-connection PRAGMAs set by
|
|
38
|
+
* the store; `journal_mode=WAL` is a persistent database property set on open.
|
|
39
|
+
* The migration itself only contains schema objects.
|
|
40
|
+
*/
|
|
41
|
+
const MIGRATION_1_SQL = `
|
|
42
|
+
-- Global catalog and coordination (§4.1) -------------------------------------
|
|
43
|
+
|
|
44
|
+
CREATE TABLE IF NOT EXISTS home_meta (
|
|
45
|
+
id INTEGER PRIMARY KEY CHECK (id = 1),
|
|
46
|
+
home_identity TEXT NOT NULL,
|
|
47
|
+
revision INTEGER NOT NULL,
|
|
48
|
+
layout_version INTEGER NOT NULL,
|
|
49
|
+
aggregate_version INTEGER NOT NULL,
|
|
50
|
+
created_at TEXT NOT NULL,
|
|
51
|
+
updated_at TEXT NOT NULL
|
|
52
|
+
);
|
|
53
|
+
|
|
54
|
+
CREATE TABLE IF NOT EXISTS config (
|
|
55
|
+
id INTEGER PRIMARY KEY CHECK (id = 1),
|
|
56
|
+
payload TEXT NOT NULL,
|
|
57
|
+
updated_at TEXT NOT NULL
|
|
58
|
+
);
|
|
59
|
+
|
|
60
|
+
CREATE TABLE IF NOT EXISTS configured_agents (
|
|
61
|
+
id TEXT PRIMARY KEY,
|
|
62
|
+
payload TEXT NOT NULL,
|
|
63
|
+
updated_at TEXT NOT NULL
|
|
64
|
+
);
|
|
65
|
+
|
|
66
|
+
CREATE TABLE IF NOT EXISTS agent_profiles (
|
|
67
|
+
id TEXT PRIMARY KEY,
|
|
68
|
+
payload TEXT NOT NULL,
|
|
69
|
+
updated_at TEXT NOT NULL
|
|
70
|
+
);
|
|
71
|
+
|
|
72
|
+
CREATE TABLE IF NOT EXISTS projects (
|
|
73
|
+
id TEXT PRIMARY KEY,
|
|
74
|
+
name TEXT NOT NULL,
|
|
75
|
+
path TEXT NOT NULL,
|
|
76
|
+
payload TEXT NOT NULL,
|
|
77
|
+
created_at TEXT NOT NULL,
|
|
78
|
+
updated_at TEXT NOT NULL
|
|
79
|
+
);
|
|
80
|
+
|
|
81
|
+
CREATE TABLE IF NOT EXISTS global_roles (
|
|
82
|
+
name TEXT PRIMARY KEY,
|
|
83
|
+
payload TEXT NOT NULL,
|
|
84
|
+
updated_at TEXT NOT NULL
|
|
85
|
+
);
|
|
86
|
+
|
|
87
|
+
CREATE TABLE IF NOT EXISTS global_role_session_sets (
|
|
88
|
+
name TEXT PRIMARY KEY,
|
|
89
|
+
payload TEXT NOT NULL,
|
|
90
|
+
updated_at TEXT NOT NULL,
|
|
91
|
+
FOREIGN KEY (name) REFERENCES global_roles(name)
|
|
92
|
+
);
|
|
93
|
+
|
|
94
|
+
-- Global record ID high-water marks (§5.3).
|
|
95
|
+
CREATE TABLE IF NOT EXISTS global_sequences (
|
|
96
|
+
name TEXT PRIMARY KEY,
|
|
97
|
+
high_water INTEGER NOT NULL
|
|
98
|
+
);
|
|
99
|
+
|
|
100
|
+
-- Task catalog: the global active index and lifecycle lookup.
|
|
101
|
+
CREATE TABLE IF NOT EXISTS tasks_catalog (
|
|
102
|
+
task_id TEXT PRIMARY KEY,
|
|
103
|
+
status TEXT NOT NULL,
|
|
104
|
+
lifecycle TEXT NOT NULL,
|
|
105
|
+
is_active INTEGER NOT NULL CHECK (is_active IN (0,1)),
|
|
106
|
+
created_at TEXT NOT NULL,
|
|
107
|
+
updated_at TEXT NOT NULL
|
|
108
|
+
);
|
|
109
|
+
CREATE INDEX IF NOT EXISTS idx_tasks_active ON tasks_catalog(is_active) WHERE is_active = 1;
|
|
110
|
+
|
|
111
|
+
-- Session/Workspace ownership is global (workspaces outlive task activity).
|
|
112
|
+
CREATE TABLE IF NOT EXISTS managed_workspaces (
|
|
113
|
+
owner_kind TEXT NOT NULL CHECK (owner_kind IN
|
|
114
|
+
('task','work-item','review-round','integration-attempt','execution-lane')),
|
|
115
|
+
owner_id TEXT NOT NULL,
|
|
116
|
+
task_id TEXT,
|
|
117
|
+
path TEXT NOT NULL,
|
|
118
|
+
payload TEXT NOT NULL,
|
|
119
|
+
status TEXT NOT NULL,
|
|
120
|
+
created_at TEXT NOT NULL,
|
|
121
|
+
updated_at TEXT NOT NULL,
|
|
122
|
+
PRIMARY KEY (owner_kind, owner_id)
|
|
123
|
+
);
|
|
124
|
+
CREATE INDEX IF NOT EXISTS idx_workspaces_task ON managed_workspaces(task_id);
|
|
125
|
+
|
|
126
|
+
-- Per-task record ID high-water marks (replaces StoredTask.idHighWaterMarks).
|
|
127
|
+
CREATE TABLE IF NOT EXISTS id_sequences (
|
|
128
|
+
task_id TEXT NOT NULL,
|
|
129
|
+
kind TEXT NOT NULL,
|
|
130
|
+
high_water INTEGER NOT NULL,
|
|
131
|
+
PRIMARY KEY (task_id, kind)
|
|
132
|
+
);
|
|
133
|
+
|
|
134
|
+
-- Cross-task coordination: Project locks and the Integration queue.
|
|
135
|
+
CREATE TABLE IF NOT EXISTS coordination_locks (
|
|
136
|
+
lock_key TEXT PRIMARY KEY,
|
|
137
|
+
holder_task TEXT NOT NULL,
|
|
138
|
+
holder_ref TEXT NOT NULL,
|
|
139
|
+
acquired_at TEXT NOT NULL,
|
|
140
|
+
expires_at TEXT
|
|
141
|
+
);
|
|
142
|
+
|
|
143
|
+
CREATE TABLE IF NOT EXISTS integration_queue (
|
|
144
|
+
queue_id TEXT PRIMARY KEY,
|
|
145
|
+
task_id TEXT NOT NULL,
|
|
146
|
+
project_id TEXT NOT NULL,
|
|
147
|
+
change_set TEXT NOT NULL,
|
|
148
|
+
status TEXT NOT NULL,
|
|
149
|
+
payload TEXT NOT NULL,
|
|
150
|
+
created_at TEXT NOT NULL,
|
|
151
|
+
updated_at TEXT NOT NULL
|
|
152
|
+
);
|
|
153
|
+
CREATE INDEX IF NOT EXISTS idx_integration_queue_status ON integration_queue(status, created_at);
|
|
154
|
+
|
|
155
|
+
-- Durable outbox (§5.4): UNIQUE(request_id) makes cross-task effects exactly-once.
|
|
156
|
+
CREATE TABLE IF NOT EXISTS outbox (
|
|
157
|
+
outbox_id INTEGER PRIMARY KEY,
|
|
158
|
+
request_id TEXT NOT NULL UNIQUE,
|
|
159
|
+
command TEXT NOT NULL,
|
|
160
|
+
state TEXT NOT NULL DEFAULT 'pending',
|
|
161
|
+
created_at TEXT NOT NULL,
|
|
162
|
+
applied_at TEXT
|
|
163
|
+
);
|
|
164
|
+
CREATE INDEX IF NOT EXISTS idx_outbox_state ON outbox(state, created_at);
|
|
165
|
+
|
|
166
|
+
-- Mailboxes (§4.2): per-target ordering and exactly-once signals. -------------
|
|
167
|
+
|
|
168
|
+
CREATE TABLE IF NOT EXISTS mailboxes (
|
|
169
|
+
mailbox_id INTEGER PRIMARY KEY,
|
|
170
|
+
target_kind TEXT NOT NULL CHECK (target_kind IN
|
|
171
|
+
('task','role','role-runtime','global-role-runtime','operator')),
|
|
172
|
+
task_id TEXT,
|
|
173
|
+
role_name TEXT,
|
|
174
|
+
-- The stable mailboxTargetKey string carries the real uniqueness: the column
|
|
175
|
+
-- UNIQUE below cannot, because NULL task_id/role_name (the 'operator' and
|
|
176
|
+
-- 'global-role-runtime' targets) are distinct under SQL NULL semantics.
|
|
177
|
+
target_key TEXT NOT NULL,
|
|
178
|
+
next_sequence INTEGER NOT NULL,
|
|
179
|
+
processing TEXT,
|
|
180
|
+
pending TEXT,
|
|
181
|
+
UNIQUE (target_kind, task_id, role_name)
|
|
182
|
+
);
|
|
183
|
+
CREATE UNIQUE INDEX IF NOT EXISTS idx_mailboxes_target_key
|
|
184
|
+
ON mailboxes(target_key);
|
|
185
|
+
|
|
186
|
+
-- Signals are the durable append log; (mailbox, sequence) is the exactly-once key.
|
|
187
|
+
CREATE TABLE IF NOT EXISTS mailbox_signals (
|
|
188
|
+
mailbox_id INTEGER NOT NULL,
|
|
189
|
+
sequence INTEGER NOT NULL,
|
|
190
|
+
reason TEXT NOT NULL,
|
|
191
|
+
ref_type TEXT,
|
|
192
|
+
ref_task_id TEXT,
|
|
193
|
+
ref_id TEXT,
|
|
194
|
+
occurred_at TEXT NOT NULL,
|
|
195
|
+
request_id TEXT NOT NULL,
|
|
196
|
+
PRIMARY KEY (mailbox_id, sequence),
|
|
197
|
+
FOREIGN KEY (mailbox_id) REFERENCES mailboxes(mailbox_id)
|
|
198
|
+
);
|
|
199
|
+
CREATE INDEX IF NOT EXISTS idx_mailbox_signals_request ON mailbox_signals(request_id);
|
|
200
|
+
|
|
201
|
+
-- Task-partitioned tables (§4.3). Every task-local read constrains task_id. ----
|
|
202
|
+
|
|
203
|
+
CREATE TABLE IF NOT EXISTS task_records (
|
|
204
|
+
task_id TEXT PRIMARY KEY,
|
|
205
|
+
payload TEXT NOT NULL,
|
|
206
|
+
brief TEXT,
|
|
207
|
+
updated_at TEXT NOT NULL,
|
|
208
|
+
FOREIGN KEY (task_id) REFERENCES tasks_catalog(task_id)
|
|
209
|
+
);
|
|
210
|
+
|
|
211
|
+
CREATE TABLE IF NOT EXISTS task_roles (
|
|
212
|
+
task_id TEXT NOT NULL,
|
|
213
|
+
role_name TEXT NOT NULL,
|
|
214
|
+
payload TEXT NOT NULL,
|
|
215
|
+
updated_at TEXT NOT NULL,
|
|
216
|
+
PRIMARY KEY (task_id, role_name)
|
|
217
|
+
);
|
|
218
|
+
|
|
219
|
+
CREATE TABLE IF NOT EXISTS role_session_sets (
|
|
220
|
+
task_id TEXT NOT NULL,
|
|
221
|
+
role_name TEXT NOT NULL,
|
|
222
|
+
payload TEXT NOT NULL,
|
|
223
|
+
updated_at TEXT NOT NULL,
|
|
224
|
+
PRIMARY KEY (task_id, role_name)
|
|
225
|
+
);
|
|
226
|
+
|
|
227
|
+
CREATE TABLE IF NOT EXISTS work_items (
|
|
228
|
+
task_id TEXT NOT NULL,
|
|
229
|
+
work_item_id TEXT NOT NULL,
|
|
230
|
+
status TEXT NOT NULL,
|
|
231
|
+
payload TEXT NOT NULL,
|
|
232
|
+
updated_at TEXT NOT NULL,
|
|
233
|
+
PRIMARY KEY (task_id, work_item_id)
|
|
234
|
+
);
|
|
235
|
+
CREATE INDEX IF NOT EXISTS idx_work_items_status ON work_items(task_id, status);
|
|
236
|
+
|
|
237
|
+
CREATE TABLE IF NOT EXISTS work_item_candidates (
|
|
238
|
+
task_id TEXT NOT NULL,
|
|
239
|
+
candidate_id TEXT NOT NULL,
|
|
240
|
+
work_item_id TEXT NOT NULL,
|
|
241
|
+
payload TEXT NOT NULL,
|
|
242
|
+
created_at TEXT NOT NULL,
|
|
243
|
+
PRIMARY KEY (task_id, candidate_id)
|
|
244
|
+
);
|
|
245
|
+
|
|
246
|
+
CREATE TABLE IF NOT EXISTS agent_runs (
|
|
247
|
+
task_id TEXT NOT NULL,
|
|
248
|
+
run_id TEXT NOT NULL,
|
|
249
|
+
role_name TEXT NOT NULL,
|
|
250
|
+
status TEXT NOT NULL,
|
|
251
|
+
payload TEXT NOT NULL,
|
|
252
|
+
updated_at TEXT NOT NULL,
|
|
253
|
+
PRIMARY KEY (task_id, run_id)
|
|
254
|
+
);
|
|
255
|
+
CREATE INDEX IF NOT EXISTS idx_agent_runs_role_status ON agent_runs(task_id, role_name, status);
|
|
256
|
+
|
|
257
|
+
-- Active-run pointers (getActiveAgentRun / execution-lane runs).
|
|
258
|
+
CREATE TABLE IF NOT EXISTS active_runs (
|
|
259
|
+
task_id TEXT NOT NULL,
|
|
260
|
+
pointer TEXT NOT NULL,
|
|
261
|
+
run_id TEXT NOT NULL,
|
|
262
|
+
payload TEXT NOT NULL,
|
|
263
|
+
updated_at TEXT NOT NULL,
|
|
264
|
+
PRIMARY KEY (task_id, pointer)
|
|
265
|
+
);
|
|
266
|
+
|
|
267
|
+
CREATE TABLE IF NOT EXISTS review_rounds (
|
|
268
|
+
task_id TEXT NOT NULL,
|
|
269
|
+
review_round_id TEXT NOT NULL,
|
|
270
|
+
status TEXT NOT NULL,
|
|
271
|
+
payload TEXT NOT NULL,
|
|
272
|
+
updated_at TEXT NOT NULL,
|
|
273
|
+
PRIMARY KEY (task_id, review_round_id)
|
|
274
|
+
);
|
|
275
|
+
|
|
276
|
+
CREATE TABLE IF NOT EXISTS change_sets (
|
|
277
|
+
task_id TEXT NOT NULL,
|
|
278
|
+
change_set_id TEXT NOT NULL,
|
|
279
|
+
project_id TEXT NOT NULL,
|
|
280
|
+
head_sha TEXT NOT NULL,
|
|
281
|
+
payload TEXT NOT NULL,
|
|
282
|
+
created_at TEXT NOT NULL,
|
|
283
|
+
PRIMARY KEY (task_id, change_set_id)
|
|
284
|
+
);
|
|
285
|
+
CREATE INDEX IF NOT EXISTS idx_change_sets_project ON change_sets(task_id, project_id);
|
|
286
|
+
|
|
287
|
+
CREATE TABLE IF NOT EXISTS integration_attempts (
|
|
288
|
+
task_id TEXT NOT NULL,
|
|
289
|
+
integration_id TEXT NOT NULL,
|
|
290
|
+
status TEXT NOT NULL,
|
|
291
|
+
payload TEXT NOT NULL,
|
|
292
|
+
updated_at TEXT NOT NULL,
|
|
293
|
+
PRIMARY KEY (task_id, integration_id)
|
|
294
|
+
);
|
|
295
|
+
|
|
296
|
+
CREATE TABLE IF NOT EXISTS messages (
|
|
297
|
+
task_id TEXT NOT NULL,
|
|
298
|
+
message_id TEXT NOT NULL,
|
|
299
|
+
seq INTEGER NOT NULL,
|
|
300
|
+
payload TEXT NOT NULL,
|
|
301
|
+
created_at TEXT NOT NULL,
|
|
302
|
+
PRIMARY KEY (task_id, message_id)
|
|
303
|
+
);
|
|
304
|
+
CREATE INDEX IF NOT EXISTS idx_messages_seq ON messages(task_id, seq);
|
|
305
|
+
|
|
306
|
+
CREATE TABLE IF NOT EXISTS input_requests (
|
|
307
|
+
task_id TEXT NOT NULL,
|
|
308
|
+
input_id TEXT NOT NULL,
|
|
309
|
+
status TEXT NOT NULL,
|
|
310
|
+
blocks TEXT,
|
|
311
|
+
payload TEXT NOT NULL,
|
|
312
|
+
created_at TEXT NOT NULL,
|
|
313
|
+
updated_at TEXT NOT NULL,
|
|
314
|
+
PRIMARY KEY (task_id, input_id)
|
|
315
|
+
);
|
|
316
|
+
CREATE INDEX IF NOT EXISTS idx_input_open ON input_requests(task_id, status) WHERE status <> 'resolved';
|
|
317
|
+
|
|
318
|
+
CREATE TABLE IF NOT EXISTS decisions (
|
|
319
|
+
task_id TEXT NOT NULL,
|
|
320
|
+
decision_id TEXT NOT NULL,
|
|
321
|
+
payload TEXT NOT NULL,
|
|
322
|
+
created_at TEXT NOT NULL,
|
|
323
|
+
PRIMARY KEY (task_id, decision_id)
|
|
324
|
+
);
|
|
325
|
+
|
|
326
|
+
CREATE TABLE IF NOT EXISTS milestones (
|
|
327
|
+
task_id TEXT NOT NULL,
|
|
328
|
+
milestone_id TEXT NOT NULL,
|
|
329
|
+
payload TEXT NOT NULL,
|
|
330
|
+
created_at TEXT NOT NULL,
|
|
331
|
+
PRIMARY KEY (task_id, milestone_id)
|
|
332
|
+
);
|
|
333
|
+
|
|
334
|
+
-- Terminal/semantic events: retained individually, never pruned.
|
|
335
|
+
CREATE TABLE IF NOT EXISTS events (
|
|
336
|
+
task_id TEXT NOT NULL,
|
|
337
|
+
event_id TEXT NOT NULL,
|
|
338
|
+
type TEXT NOT NULL,
|
|
339
|
+
occurred_at TEXT NOT NULL,
|
|
340
|
+
payload TEXT NOT NULL,
|
|
341
|
+
PRIMARY KEY (task_id, event_id)
|
|
342
|
+
);
|
|
343
|
+
CREATE INDEX IF NOT EXISTS idx_events_type_time ON events(task_id, type, occurred_at);
|
|
344
|
+
|
|
345
|
+
-- Per-task scheduler projections (leaderFailure, operatorNotification).
|
|
346
|
+
CREATE TABLE IF NOT EXISTS task_projections (
|
|
347
|
+
task_id TEXT NOT NULL,
|
|
348
|
+
kind TEXT NOT NULL CHECK (kind IN ('leader-failure','operator-notification')),
|
|
349
|
+
payload TEXT,
|
|
350
|
+
updated_at TEXT NOT NULL,
|
|
351
|
+
PRIMARY KEY (task_id, kind)
|
|
352
|
+
);
|
|
353
|
+
|
|
354
|
+
-- Telemetry (§4.4): bounded, latest-per-key. WITHOUT ROWID, PK is the key.
|
|
355
|
+
CREATE TABLE IF NOT EXISTS telemetry (
|
|
356
|
+
task_id TEXT NOT NULL,
|
|
357
|
+
role_name TEXT NOT NULL,
|
|
358
|
+
run_id TEXT NOT NULL,
|
|
359
|
+
generation TEXT NOT NULL,
|
|
360
|
+
progress_id TEXT NOT NULL,
|
|
361
|
+
sequence INTEGER,
|
|
362
|
+
payload TEXT NOT NULL,
|
|
363
|
+
received_at TEXT NOT NULL,
|
|
364
|
+
PRIMARY KEY (task_id, role_name, run_id, generation, progress_id)
|
|
365
|
+
) WITHOUT ROWID;
|
|
366
|
+
CREATE INDEX IF NOT EXISTS idx_telemetry_run ON telemetry(task_id, run_id);
|
|
367
|
+
`;
|
|
368
|
+
/**
|
|
369
|
+
* Migration 2: post-baseline task-scoped record families. This single
|
|
370
|
+
* migration creates the DurableJob, CapabilityGrant, and ReleaseWorkflow
|
|
371
|
+
* tables so a fresh database receives the complete merged schema atomically.
|
|
372
|
+
*/
|
|
373
|
+
const MIGRATION_2_SQL = `
|
|
374
|
+
CREATE TABLE IF NOT EXISTS durable_jobs (
|
|
375
|
+
job_id TEXT PRIMARY KEY,
|
|
376
|
+
task_id TEXT NOT NULL,
|
|
377
|
+
idempotency_key TEXT,
|
|
378
|
+
status TEXT NOT NULL,
|
|
379
|
+
payload TEXT NOT NULL,
|
|
380
|
+
created_at TEXT NOT NULL,
|
|
381
|
+
updated_at TEXT NOT NULL
|
|
382
|
+
);
|
|
383
|
+
CREATE INDEX IF NOT EXISTS idx_durable_jobs_task ON durable_jobs(task_id);
|
|
384
|
+
CREATE INDEX IF NOT EXISTS idx_durable_jobs_status ON durable_jobs(status);
|
|
385
|
+
CREATE UNIQUE INDEX IF NOT EXISTS idx_durable_jobs_idempotency
|
|
386
|
+
ON durable_jobs(task_id, idempotency_key) WHERE idempotency_key IS NOT NULL;
|
|
387
|
+
CREATE TABLE IF NOT EXISTS capability_grants (
|
|
388
|
+
task_id TEXT NOT NULL,
|
|
389
|
+
grant_id TEXT NOT NULL,
|
|
390
|
+
payload TEXT NOT NULL,
|
|
391
|
+
updated_at TEXT NOT NULL,
|
|
392
|
+
PRIMARY KEY (task_id, grant_id)
|
|
393
|
+
);
|
|
394
|
+
|
|
395
|
+
CREATE TABLE IF NOT EXISTS release_workflows (
|
|
396
|
+
task_id TEXT NOT NULL,
|
|
397
|
+
workflow_id TEXT NOT NULL,
|
|
398
|
+
payload TEXT NOT NULL,
|
|
399
|
+
updated_at TEXT NOT NULL,
|
|
400
|
+
PRIMARY KEY (task_id, workflow_id)
|
|
401
|
+
);
|
|
402
|
+
`;
|
|
403
|
+
/**
|
|
404
|
+
* Migration 3: Job caller key hashes (task-14, rr13). Durable SHA-256 hashes of
|
|
405
|
+
* the caller key bound to each launched Session, used to fail-closed verify a
|
|
406
|
+
* DurableJob's caller against durable Run state.
|
|
407
|
+
*/
|
|
408
|
+
const MIGRATION_3_SQL = `
|
|
409
|
+
CREATE TABLE IF NOT EXISTS job_caller_key_hashes (
|
|
410
|
+
task_id TEXT NOT NULL,
|
|
411
|
+
role_name TEXT NOT NULL,
|
|
412
|
+
agent_id TEXT NOT NULL,
|
|
413
|
+
hash TEXT NOT NULL,
|
|
414
|
+
updated_at TEXT NOT NULL,
|
|
415
|
+
PRIMARY KEY (task_id, role_name, agent_id)
|
|
416
|
+
);
|
|
417
|
+
`;
|
|
418
|
+
/**
|
|
419
|
+
* Migration 4: DurableJob IDs are Task-local. Migration 2 accidentally made
|
|
420
|
+
* job_id the global primary key, so two Tasks allocating their first `job-1`
|
|
421
|
+
* could overwrite one another through the upsert path. Rebuild the table with
|
|
422
|
+
* the actual record identity `(task_id, job_id)` and restore its indexes.
|
|
423
|
+
* Never edit migration 2's checksum: existing Homes reach this repair through
|
|
424
|
+
* this adjacent schema migration.
|
|
425
|
+
*/
|
|
426
|
+
const MIGRATION_4_SQL = `
|
|
427
|
+
CREATE TABLE durable_jobs_v4 (
|
|
428
|
+
job_id TEXT NOT NULL,
|
|
429
|
+
task_id TEXT NOT NULL,
|
|
430
|
+
idempotency_key TEXT,
|
|
431
|
+
status TEXT NOT NULL,
|
|
432
|
+
payload TEXT NOT NULL,
|
|
433
|
+
created_at TEXT NOT NULL,
|
|
434
|
+
updated_at TEXT NOT NULL,
|
|
435
|
+
PRIMARY KEY (task_id, job_id)
|
|
436
|
+
);
|
|
437
|
+
INSERT INTO durable_jobs_v4
|
|
438
|
+
(job_id, task_id, idempotency_key, status, payload, created_at, updated_at)
|
|
439
|
+
SELECT job_id, task_id, idempotency_key, status, payload, created_at, updated_at
|
|
440
|
+
FROM durable_jobs;
|
|
441
|
+
DROP TABLE durable_jobs;
|
|
442
|
+
ALTER TABLE durable_jobs_v4 RENAME TO durable_jobs;
|
|
443
|
+
CREATE INDEX idx_durable_jobs_task ON durable_jobs(task_id);
|
|
444
|
+
CREATE INDEX idx_durable_jobs_status ON durable_jobs(status);
|
|
445
|
+
CREATE UNIQUE INDEX idx_durable_jobs_idempotency
|
|
446
|
+
ON durable_jobs(task_id, idempotency_key) WHERE idempotency_key IS NOT NULL;
|
|
447
|
+
`;
|
|
448
|
+
/**
|
|
449
|
+
* Migration 5: telemetry aggregate (Issue 09). The `telemetry` table (migration
|
|
450
|
+
* 1, §4.4) stores the bounded latest-per-key progress window; this companion
|
|
451
|
+
* table holds the authoritative per-Run/generation summary (count, first/last,
|
|
452
|
+
* max sequence, error count) so aggregates stay accurate after the window is
|
|
453
|
+
* pruned. Triggers maintain it on telemetry INSERT/UPDATE; DELETE intentionally
|
|
454
|
+
* leaves it untouched because pruned rows were still observed.
|
|
455
|
+
*/
|
|
456
|
+
const MIGRATION_5_SQL = `
|
|
457
|
+
CREATE TABLE IF NOT EXISTS telemetry_aggregate (
|
|
458
|
+
task_id TEXT NOT NULL,
|
|
459
|
+
role_name TEXT NOT NULL,
|
|
460
|
+
run_id TEXT NOT NULL,
|
|
461
|
+
generation TEXT NOT NULL,
|
|
462
|
+
first_at TEXT NOT NULL,
|
|
463
|
+
last_at TEXT NOT NULL,
|
|
464
|
+
count INTEGER NOT NULL,
|
|
465
|
+
max_sequence INTEGER,
|
|
466
|
+
error_count INTEGER NOT NULL DEFAULT 0,
|
|
467
|
+
updated_at TEXT NOT NULL,
|
|
468
|
+
PRIMARY KEY (task_id, role_name, run_id, generation)
|
|
469
|
+
) WITHOUT ROWID;
|
|
470
|
+
|
|
471
|
+
CREATE TRIGGER IF NOT EXISTS telemetry_ai AFTER INSERT ON telemetry
|
|
472
|
+
BEGIN
|
|
473
|
+
INSERT INTO telemetry_aggregate
|
|
474
|
+
(task_id, role_name, run_id, generation, first_at, last_at, count, max_sequence, error_count, updated_at)
|
|
475
|
+
VALUES
|
|
476
|
+
(NEW.task_id, NEW.role_name, NEW.run_id, NEW.generation, NEW.received_at, NEW.received_at, 1, NEW.sequence,
|
|
477
|
+
CASE WHEN json_valid(NEW.payload)
|
|
478
|
+
AND (COALESCE(json_extract(NEW.payload, '$.error'), '') <> ''
|
|
479
|
+
OR COALESCE(json_extract(NEW.payload, '$.errorKind'), '') <> '')
|
|
480
|
+
THEN 1 ELSE 0 END,
|
|
481
|
+
NEW.received_at)
|
|
482
|
+
ON CONFLICT(task_id, role_name, run_id, generation) DO UPDATE SET
|
|
483
|
+
first_at = MIN(telemetry_aggregate.first_at, excluded.first_at),
|
|
484
|
+
last_at = MAX(telemetry_aggregate.last_at, excluded.last_at),
|
|
485
|
+
count = telemetry_aggregate.count + 1,
|
|
486
|
+
max_sequence = CASE
|
|
487
|
+
WHEN excluded.max_sequence IS NOT NULL
|
|
488
|
+
AND (telemetry_aggregate.max_sequence IS NULL OR excluded.max_sequence > telemetry_aggregate.max_sequence)
|
|
489
|
+
THEN excluded.max_sequence ELSE telemetry_aggregate.max_sequence END,
|
|
490
|
+
error_count = telemetry_aggregate.error_count + excluded.error_count,
|
|
491
|
+
updated_at = excluded.updated_at;
|
|
492
|
+
END;
|
|
493
|
+
|
|
494
|
+
CREATE TRIGGER IF NOT EXISTS telemetry_au AFTER UPDATE ON telemetry
|
|
495
|
+
BEGIN
|
|
496
|
+
UPDATE telemetry_aggregate SET
|
|
497
|
+
last_at = CASE WHEN NEW.received_at > last_at THEN NEW.received_at ELSE last_at END,
|
|
498
|
+
max_sequence = CASE
|
|
499
|
+
WHEN NEW.sequence IS NOT NULL AND (max_sequence IS NULL OR NEW.sequence > max_sequence)
|
|
500
|
+
THEN NEW.sequence ELSE max_sequence END,
|
|
501
|
+
updated_at = NEW.received_at
|
|
502
|
+
WHERE task_id = NEW.task_id AND role_name = NEW.role_name
|
|
503
|
+
AND run_id = NEW.run_id AND generation = NEW.generation;
|
|
504
|
+
END;
|
|
505
|
+
`;
|
|
506
|
+
/**
|
|
507
|
+
* Migration 6: Issue 06 ReviewFinding ledger. Cross-Round semantic review
|
|
508
|
+
* findings with stable keys and Leader dispositions. Failed execution attempts
|
|
509
|
+
* never create rows; only completed Rounds feed the ledger.
|
|
510
|
+
*/
|
|
511
|
+
const MIGRATION_6_SQL = `
|
|
512
|
+
CREATE TABLE IF NOT EXISTS review_findings (
|
|
513
|
+
task_id TEXT NOT NULL,
|
|
514
|
+
finding_id TEXT NOT NULL,
|
|
515
|
+
stable_key TEXT NOT NULL,
|
|
516
|
+
severity TEXT NOT NULL,
|
|
517
|
+
payload TEXT NOT NULL,
|
|
518
|
+
updated_at TEXT NOT NULL,
|
|
519
|
+
PRIMARY KEY (task_id, finding_id)
|
|
520
|
+
);
|
|
521
|
+
CREATE INDEX IF NOT EXISTS idx_review_findings_task ON review_findings(task_id);
|
|
522
|
+
CREATE INDEX IF NOT EXISTS idx_review_findings_stable_key ON review_findings(task_id, stable_key);
|
|
523
|
+
`;
|
|
524
|
+
/**
|
|
525
|
+
* Version 5 migration: session owner physical identity records (Issue 03).
|
|
526
|
+
*
|
|
527
|
+
* One row per runtime generation, keyed by launch id. The payload column
|
|
528
|
+
* stores the full versioned JSON record; typed columns support the
|
|
529
|
+
* reconciliation queries (task/role lookup, PID liveness).
|
|
530
|
+
*/
|
|
531
|
+
const MIGRATION_7_SQL = `
|
|
532
|
+
CREATE TABLE IF NOT EXISTS session_owners (
|
|
533
|
+
launch_id TEXT PRIMARY KEY,
|
|
534
|
+
scope TEXT NOT NULL CHECK (scope IN ('task','global')),
|
|
535
|
+
task_id TEXT,
|
|
536
|
+
role_name TEXT NOT NULL,
|
|
537
|
+
agent_id TEXT NOT NULL,
|
|
538
|
+
native_session_id TEXT,
|
|
539
|
+
provider_root_pid INTEGER,
|
|
540
|
+
payload TEXT NOT NULL,
|
|
541
|
+
recorded_at TEXT NOT NULL
|
|
542
|
+
);
|
|
543
|
+
CREATE INDEX IF NOT EXISTS idx_session_owners_task
|
|
544
|
+
ON session_owners(task_id, role_name);
|
|
545
|
+
`;
|
|
546
|
+
/**
|
|
547
|
+
* Migration 8: Resource GC registry (Issue 10).
|
|
548
|
+
*
|
|
549
|
+
* GC-owned table for resource lifecycle records. The registry is GC's own
|
|
550
|
+
* state — it is not part of the aggregate and never participates in aggregate
|
|
551
|
+
* versioning. Records are stored as full versioned JSON in `payload`, with
|
|
552
|
+
* typed columns for the fields GC queries (disposition, kind, task_id).
|
|
553
|
+
*/
|
|
554
|
+
const MIGRATION_8_SQL = `
|
|
555
|
+
CREATE TABLE IF NOT EXISTS resource_registry (
|
|
556
|
+
id TEXT PRIMARY KEY,
|
|
557
|
+
kind TEXT NOT NULL,
|
|
558
|
+
path TEXT NOT NULL,
|
|
559
|
+
disposition TEXT NOT NULL,
|
|
560
|
+
task_id TEXT,
|
|
561
|
+
payload TEXT NOT NULL,
|
|
562
|
+
created_at TEXT NOT NULL,
|
|
563
|
+
updated_at TEXT NOT NULL
|
|
564
|
+
);
|
|
565
|
+
CREATE INDEX IF NOT EXISTS idx_resource_registry_disposition
|
|
566
|
+
ON resource_registry(disposition);
|
|
567
|
+
CREATE INDEX IF NOT EXISTS idx_resource_registry_task
|
|
568
|
+
ON resource_registry(task_id);
|
|
569
|
+
`;
|
|
570
|
+
/**
|
|
571
|
+
* Migration 9: GateArtifact storage (Issue 08). Content-addressed gate
|
|
572
|
+
* evidence records with per-step logs stored as BLOBs. The artifact key is
|
|
573
|
+
* the SHA-256 of the identity tuple (Project + commit + plan digest +
|
|
574
|
+
* toolchain digest + L2 boundary), so the same tuple always maps to one row.
|
|
575
|
+
* Typed columns support the reuse lookup paths (exact-commit L2 search,
|
|
576
|
+
* Project-level prune) without scanning payloads.
|
|
577
|
+
*/
|
|
578
|
+
const MIGRATION_9_SQL = `
|
|
579
|
+
CREATE TABLE IF NOT EXISTS gate_artifacts (
|
|
580
|
+
key TEXT PRIMARY KEY,
|
|
581
|
+
project_id TEXT NOT NULL,
|
|
582
|
+
level TEXT NOT NULL CHECK (level IN ('L1','L2')),
|
|
583
|
+
commit_sha TEXT NOT NULL,
|
|
584
|
+
plan_digest TEXT NOT NULL,
|
|
585
|
+
toolchain_digest TEXT NOT NULL,
|
|
586
|
+
target_ref TEXT,
|
|
587
|
+
status TEXT NOT NULL CHECK (status IN ('incomplete','complete')),
|
|
588
|
+
outcome TEXT NOT NULL CHECK (outcome IN ('unknown','succeeded','failed')),
|
|
589
|
+
payload TEXT NOT NULL,
|
|
590
|
+
created_at TEXT NOT NULL,
|
|
591
|
+
completed_at TEXT,
|
|
592
|
+
last_used_at TEXT NOT NULL
|
|
593
|
+
);
|
|
594
|
+
|
|
595
|
+
CREATE INDEX IF NOT EXISTS idx_gate_artifacts_project_commit
|
|
596
|
+
ON gate_artifacts(project_id, commit_sha);
|
|
597
|
+
|
|
598
|
+
CREATE INDEX IF NOT EXISTS idx_gate_artifacts_project_last_used
|
|
599
|
+
ON gate_artifacts(project_id, last_used_at);
|
|
600
|
+
|
|
601
|
+
CREATE TABLE IF NOT EXISTS gate_artifact_logs (
|
|
602
|
+
artifact_key TEXT NOT NULL,
|
|
603
|
+
step_name TEXT NOT NULL,
|
|
604
|
+
log_content BLOB NOT NULL,
|
|
605
|
+
log_digest TEXT NOT NULL,
|
|
606
|
+
log_bytes INTEGER NOT NULL,
|
|
607
|
+
PRIMARY KEY (artifact_key, step_name),
|
|
608
|
+
FOREIGN KEY (artifact_key) REFERENCES gate_artifacts(key) ON DELETE CASCADE
|
|
609
|
+
);
|
|
610
|
+
`;
|
|
611
|
+
const MIGRATIONS = [
|
|
612
|
+
{ version: 1, axis: "layout", sql: MIGRATION_1_SQL },
|
|
613
|
+
{ version: 2, axis: "record", recordKind: "durableJob+capability-grant+release-workflow", sql: MIGRATION_2_SQL },
|
|
614
|
+
{ version: 3, axis: "record", recordKind: "jobCallerKeyHash", sql: MIGRATION_3_SQL },
|
|
615
|
+
{ version: 4, axis: "record", recordKind: "durableJob", sql: MIGRATION_4_SQL },
|
|
616
|
+
{ version: 5, axis: "record", recordKind: "telemetryAggregate", sql: MIGRATION_5_SQL },
|
|
617
|
+
{ version: 6, axis: "record", recordKind: "reviewFinding", sql: MIGRATION_6_SQL },
|
|
618
|
+
{ version: 7, axis: "record", recordKind: "sessionOwner", sql: MIGRATION_7_SQL },
|
|
619
|
+
{ version: 8, axis: "record", recordKind: "resource-registry", sql: MIGRATION_8_SQL },
|
|
620
|
+
{ version: 9, axis: "record", recordKind: "gateArtifact", sql: MIGRATION_9_SQL }
|
|
621
|
+
];
|
|
622
|
+
function checksum(sql) {
|
|
623
|
+
return createHash("sha256").update(sql).digest("hex");
|
|
624
|
+
}
|
|
625
|
+
/**
|
|
626
|
+
* Apply pending migrations idempotently inside transactions.
|
|
627
|
+
*
|
|
628
|
+
* Each migration runs in its own transaction: the DDL and the
|
|
629
|
+
* `schema_migrations` bookkeeping commit atomically, so a crash mid-migration
|
|
630
|
+
* rolls back and the next open re-applies cleanly. Re-running on a current
|
|
631
|
+
* database performs no work (every version is already recorded).
|
|
632
|
+
*/
|
|
633
|
+
export function migrateSqliteSchema(db) {
|
|
634
|
+
db.exec(`
|
|
635
|
+
CREATE TABLE IF NOT EXISTS schema_migrations (
|
|
636
|
+
version INTEGER PRIMARY KEY,
|
|
637
|
+
axis TEXT NOT NULL CHECK (axis IN ('layout','aggregate','record')),
|
|
638
|
+
record_kind TEXT,
|
|
639
|
+
applied_at TEXT NOT NULL,
|
|
640
|
+
checksum TEXT NOT NULL
|
|
641
|
+
)
|
|
642
|
+
`);
|
|
643
|
+
const applied = new Set(db.prepare("SELECT version FROM schema_migrations").all().map((row) => row.version));
|
|
644
|
+
const newlyApplied = [];
|
|
645
|
+
for (const migration of MIGRATIONS) {
|
|
646
|
+
if (applied.has(migration.version))
|
|
647
|
+
continue;
|
|
648
|
+
const apply = db.transaction(() => {
|
|
649
|
+
db.exec(migration.sql);
|
|
650
|
+
db.prepare(`INSERT INTO schema_migrations (version, axis, record_kind, applied_at, checksum)
|
|
651
|
+
VALUES (?, ?, ?, ?, ?)`).run(migration.version, migration.axis, migration.recordKind ?? null, new Date().toISOString(), checksum(migration.sql));
|
|
652
|
+
});
|
|
653
|
+
apply();
|
|
654
|
+
newlyApplied.push(migration.version);
|
|
655
|
+
}
|
|
656
|
+
const version = MIGRATIONS.reduce((max, m) => Math.max(max, m.version), 0);
|
|
657
|
+
return { applied: newlyApplied, version };
|
|
658
|
+
}
|
|
659
|
+
/** The names of every table the schema creates (for tests/introspection). */
|
|
660
|
+
export const SQLITE_SCHEMA_TABLES = [
|
|
661
|
+
"schema_migrations",
|
|
662
|
+
"home_meta",
|
|
663
|
+
"config",
|
|
664
|
+
"configured_agents",
|
|
665
|
+
"agent_profiles",
|
|
666
|
+
"projects",
|
|
667
|
+
"global_roles",
|
|
668
|
+
"global_role_session_sets",
|
|
669
|
+
"global_sequences",
|
|
670
|
+
"tasks_catalog",
|
|
671
|
+
"managed_workspaces",
|
|
672
|
+
"id_sequences",
|
|
673
|
+
"coordination_locks",
|
|
674
|
+
"integration_queue",
|
|
675
|
+
"durable_jobs",
|
|
676
|
+
"job_caller_key_hashes",
|
|
677
|
+
"outbox",
|
|
678
|
+
"mailboxes",
|
|
679
|
+
"mailbox_signals",
|
|
680
|
+
"task_records",
|
|
681
|
+
"task_roles",
|
|
682
|
+
"role_session_sets",
|
|
683
|
+
"work_items",
|
|
684
|
+
"work_item_candidates",
|
|
685
|
+
"agent_runs",
|
|
686
|
+
"active_runs",
|
|
687
|
+
"review_rounds",
|
|
688
|
+
"review_findings",
|
|
689
|
+
"change_sets",
|
|
690
|
+
"integration_attempts",
|
|
691
|
+
"messages",
|
|
692
|
+
"input_requests",
|
|
693
|
+
"decisions",
|
|
694
|
+
"milestones",
|
|
695
|
+
"events",
|
|
696
|
+
"task_projections",
|
|
697
|
+
"telemetry",
|
|
698
|
+
"telemetry_aggregate",
|
|
699
|
+
"capability_grants",
|
|
700
|
+
"release_workflows",
|
|
701
|
+
"session_owners",
|
|
702
|
+
"resource_registry",
|
|
703
|
+
"gate_artifacts",
|
|
704
|
+
"gate_artifact_logs"
|
|
705
|
+
];
|