@straiffi/archon 1.8.21 → 1.8.22
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/dist/client/assets/{ActivityView-BcguXTZ5.js → ActivityView-CdEPjmqD.js} +1 -1
- package/dist/client/assets/GitHubActionsDialog-CA189pr2.js +1 -0
- package/dist/client/assets/VerificationDialog-BPVkhKRj.js +4 -0
- package/dist/client/assets/{architectureDiagram-3BPJPVTR-DnC7Twj0.js → architectureDiagram-3BPJPVTR-BrZ08mqq.js} +1 -1
- package/dist/client/assets/{blockDiagram-GPEHLZMM-D0VbjiKd.js → blockDiagram-GPEHLZMM-b93XwJ6Z.js} +1 -1
- package/dist/client/assets/{c4Diagram-AAUBKEIU-CzQ3l15K.js → c4Diagram-AAUBKEIU-BKOdbZ_v.js} +1 -1
- package/dist/client/assets/channel-D0zqzBT_.js +1 -0
- package/dist/client/assets/{chunk-2J33WTMH-B5UGiqLW.js → chunk-2J33WTMH-D7Vxn_1m.js} +1 -1
- package/dist/client/assets/{chunk-3OPIFGDE-SCksTqtu.js → chunk-3OPIFGDE-B1zo2UZq.js} +1 -1
- package/dist/client/assets/{chunk-5ZQYHXKU-IOkwLOtG.js → chunk-5ZQYHXKU-BajvkDdQ.js} +1 -1
- package/dist/client/assets/{chunk-727SXJPM-Do60Xs9p.js → chunk-727SXJPM-CSH7cVpV.js} +1 -1
- package/dist/client/assets/{chunk-AQP2D5EJ-CuQT3imB.js → chunk-AQP2D5EJ-DE-wgUNP.js} +1 -1
- package/dist/client/assets/{chunk-CSCIHK7Q-BkxMIESX.js → chunk-CSCIHK7Q-Ba7JgCEf.js} +1 -1
- package/dist/client/assets/{chunk-KSCS5N6A-uUU3sWgR.js → chunk-KSCS5N6A-0o3YWkHM.js} +1 -1
- package/dist/client/assets/{chunk-L5ZTLDWV-DLYccV6J.js → chunk-L5ZTLDWV-DihWczP8.js} +1 -1
- package/dist/client/assets/{chunk-LZXEDZCA-CgJFfCRZ.js → chunk-LZXEDZCA-Bxnu-hme.js} +2 -2
- package/dist/client/assets/{chunk-ND2GUHAM-D7IQSQfK.js → chunk-ND2GUHAM-CoaR5kmP.js} +1 -1
- package/dist/client/assets/{chunk-NZK2D7GU-B4O6JWAo.js → chunk-NZK2D7GU-By92Rd3d.js} +1 -1
- package/dist/client/assets/{chunk-O5CBEL6O-DidlQEqb.js → chunk-O5CBEL6O-D83Pkgfu.js} +1 -1
- package/dist/client/assets/{chunk-WU5MYG2G-2-oq_Gue.js → chunk-WU5MYG2G-v8OVf5Lz.js} +1 -1
- package/dist/client/assets/classDiagram-4FO5ZUOK-ChhyvHH2.js +1 -0
- package/dist/client/assets/classDiagram-v2-Q7XG4LA2-Cl02kurK.js +1 -0
- package/dist/client/assets/{dagre-BM42HDAG-TubuN0hR.js → dagre-BM42HDAG-Dq1ALF7Z.js} +1 -1
- package/dist/client/assets/{diagram-2AECGRRQ-DNcUk6rV.js → diagram-2AECGRRQ-1t1_TKtu.js} +1 -1
- package/dist/client/assets/{diagram-5GNKFQAL-B1sJn4w_.js → diagram-5GNKFQAL-DtvYRL6f.js} +1 -1
- package/dist/client/assets/{diagram-KO2AKTUF-CnW6ZuQe.js → diagram-KO2AKTUF-PRNrSX5F.js} +1 -1
- package/dist/client/assets/{diagram-LMA3HP47-YHUmDzyS.js → diagram-LMA3HP47-DaTX0DhT.js} +1 -1
- package/dist/client/assets/{diagram-OG6HWLK6-BkOQphxq.js → diagram-OG6HWLK6-C2p9p-92.js} +1 -1
- package/dist/client/assets/{erDiagram-TEJ5UH35-oNMxAdxN.js → erDiagram-TEJ5UH35-DKzLtnp-.js} +1 -1
- package/dist/client/assets/{flowDiagram-I6XJVG4X-ByuCV-Am.js → flowDiagram-I6XJVG4X-GdL8lm6e.js} +1 -1
- package/dist/client/assets/{ganttDiagram-6RSMTGT7-CRkNH8Ft.js → ganttDiagram-6RSMTGT7-Ok-4XYwT.js} +1 -1
- package/dist/client/assets/{gitGraphDiagram-PVQCEYII-DRXzXjd8.js → gitGraphDiagram-PVQCEYII-DSMrOCK7.js} +1 -1
- package/dist/client/assets/index-Bs8KsdV9.js +276 -0
- package/dist/client/assets/index-DdwM8Wgt.css +2 -0
- package/dist/client/assets/{infoDiagram-5YYISTIA-CJ4-hWch.js → infoDiagram-5YYISTIA-Dl4hRXis.js} +1 -1
- package/dist/client/assets/{ishikawaDiagram-YF4QCWOH-CW1xqvim.js → ishikawaDiagram-YF4QCWOH-C1Dc1fTT.js} +1 -1
- package/dist/client/assets/{journeyDiagram-JHISSGLW-BVVFg8_h.js → journeyDiagram-JHISSGLW-CUscmJze.js} +1 -1
- package/dist/client/assets/{kanban-definition-UN3LZRKU-AcLZ63tA.js → kanban-definition-UN3LZRKU-C0nwYjQV.js} +1 -1
- package/dist/client/assets/{line-CG_TG6Ci.js → line-ByiuEIg_.js} +1 -1
- package/dist/client/assets/{mermaid-parser.core-UDLSm_c3.js → mermaid-parser.core-B-XdAyDR.js} +1 -1
- package/dist/client/assets/{mermaid.core-IvBk22DF.js → mermaid.core-D_bIfuZD.js} +3 -3
- package/dist/client/assets/{mindmap-definition-RKZ34NQL-BflWNtnl.js → mindmap-definition-RKZ34NQL-BGNiPJDK.js} +1 -1
- package/dist/client/assets/{pieDiagram-4H26LBE5-Dm1iGMm7.js → pieDiagram-4H26LBE5-CP6FpAoO.js} +1 -1
- package/dist/client/assets/{quadrantDiagram-W4KKPZXB-CM4zJd_R.js → quadrantDiagram-W4KKPZXB-dsAWBAQn.js} +1 -1
- package/dist/client/assets/{requirementDiagram-4Y6WPE33-miuP86cp.js → requirementDiagram-4Y6WPE33-DJH-zHqF.js} +1 -1
- package/dist/client/assets/{sankeyDiagram-5OEKKPKP-zUcRagtr.js → sankeyDiagram-5OEKKPKP-BJMnZrqH.js} +1 -1
- package/dist/client/assets/{sequenceDiagram-3UESZ5HK-BRYqVpsT.js → sequenceDiagram-3UESZ5HK-CzNjUSlM.js} +1 -1
- package/dist/client/assets/{stateDiagram-AJRCARHV-DwcF0DJm.js → stateDiagram-AJRCARHV-jppMhOwh.js} +1 -1
- package/dist/client/assets/stateDiagram-v2-BHNVJYJU-CLR1WgdV.js +1 -0
- package/dist/client/assets/{timeline-definition-PNZ67QCA-BzH929s_.js → timeline-definition-PNZ67QCA-BX2CI5H_.js} +1 -1
- package/dist/client/assets/{vennDiagram-CIIHVFJN-BIq8rooy.js → vennDiagram-CIIHVFJN-Bd1KZaXh.js} +1 -1
- package/dist/client/assets/{wardleyDiagram-YWT4CUSO-CyV_gL5J.js → wardleyDiagram-YWT4CUSO-D-0BNz49.js} +1 -1
- package/dist/client/assets/{xychartDiagram-2RQKCTM6-Dxo-U-qe.js → xychartDiagram-2RQKCTM6-DbYa0ZWK.js} +1 -1
- package/dist/client/index.html +2 -2
- package/dist/server/db.js +353 -19
- package/dist/server/db.js.map +1 -1
- package/dist/server/index.js +121 -3
- package/dist/server/index.js.map +1 -1
- package/dist/server/lib/approvalReconciliation.js +101 -0
- package/dist/server/lib/approvalReconciliation.js.map +1 -0
- package/dist/server/lib/approvalRequests.js +587 -0
- package/dist/server/lib/approvalRequests.js.map +1 -0
- package/dist/server/lib/approvalRoutes.js +143 -0
- package/dist/server/lib/approvalRoutes.js.map +1 -0
- package/dist/server/lib/buildExecutionEnvironment.js +128 -11
- package/dist/server/lib/buildExecutionEnvironment.js.map +1 -1
- package/dist/server/lib/checkDefinitions.js +29 -1
- package/dist/server/lib/checkDefinitions.js.map +1 -1
- package/dist/server/lib/checkRunner.js +306 -27
- package/dist/server/lib/checkRunner.js.map +1 -1
- package/dist/server/lib/checkRuns.js +4 -2
- package/dist/server/lib/checkRuns.js.map +1 -1
- package/dist/server/lib/consequentialActionAuthority.js +142 -0
- package/dist/server/lib/consequentialActionAuthority.js.map +1 -0
- package/dist/server/lib/consequentialActions.js +231 -0
- package/dist/server/lib/consequentialActions.js.map +1 -0
- package/dist/server/lib/dockerSandboxEnvironmentOperations.js +119 -0
- package/dist/server/lib/dockerSandboxEnvironmentOperations.js.map +1 -1
- package/dist/server/lib/dockerSandboxExecutionEnvironment.js +174 -11
- package/dist/server/lib/dockerSandboxExecutionEnvironment.js.map +1 -1
- package/dist/server/lib/dockerSandboxExecutionProvider.js +1 -1
- package/dist/server/lib/dockerSandboxExecutionProvider.js.map +1 -1
- package/dist/server/lib/dockerSandboxNetworkPolicy.js +222 -0
- package/dist/server/lib/dockerSandboxNetworkPolicy.js.map +1 -0
- package/dist/server/lib/executionEnvironmentContexts.js +4 -2
- package/dist/server/lib/executionEnvironmentContexts.js.map +1 -1
- package/dist/server/lib/executionEnvironmentLifecycle.js +34 -0
- package/dist/server/lib/executionEnvironmentLifecycle.js.map +1 -1
- package/dist/server/lib/executionEnvironmentRoutes.js +51 -4
- package/dist/server/lib/executionEnvironmentRoutes.js.map +1 -1
- package/dist/server/lib/executionEnvironmentValidation.js +55 -3
- package/dist/server/lib/executionEnvironmentValidation.js.map +1 -1
- package/dist/server/lib/executionEnvironmentValidationOperations.js +155 -11
- package/dist/server/lib/executionEnvironmentValidationOperations.js.map +1 -1
- package/dist/server/lib/executionEnvironments.js +11 -5
- package/dist/server/lib/executionEnvironments.js.map +1 -1
- package/dist/server/lib/executionProfileStore.js +12 -3
- package/dist/server/lib/executionProfileStore.js.map +1 -1
- package/dist/server/lib/executionProfiles.js +131 -8
- package/dist/server/lib/executionProfiles.js.map +1 -1
- package/dist/server/lib/executionReconciliation.js +206 -143
- package/dist/server/lib/executionReconciliation.js.map +1 -1
- package/dist/server/lib/executionRunAuthority.js +175 -7
- package/dist/server/lib/executionRunAuthority.js.map +1 -1
- package/dist/server/lib/executionRuns.js +26 -4
- package/dist/server/lib/executionRuns.js.map +1 -1
- package/dist/server/lib/executionTimeline.js +2 -1
- package/dist/server/lib/executionTimeline.js.map +1 -1
- package/dist/server/lib/git.js +52 -10
- package/dist/server/lib/git.js.map +1 -1
- package/dist/server/lib/gitPushApprovals.js +367 -0
- package/dist/server/lib/gitPushApprovals.js.map +1 -0
- package/dist/server/lib/mobileAccess.js +115 -1
- package/dist/server/lib/mobileAccess.js.map +1 -1
- package/dist/server/lib/productionCheckApprovals.js +106 -0
- package/dist/server/lib/productionCheckApprovals.js.map +1 -0
- package/dist/server/lib/types.js +40 -0
- package/dist/server/lib/types.js.map +1 -1
- package/dist/server/lib/workspaceProcessEnvironment.js +2 -2
- package/dist/server/lib/workspaceProcessEnvironment.js.map +1 -1
- package/dist/server/workers/review.js +15 -9
- package/dist/server/workers/review.js.map +1 -1
- package/package.json +1 -1
- package/dist/client/assets/GitHubActionsDialog-BGecTzoq.js +0 -1
- package/dist/client/assets/VerificationDialog-D4QkMLZr.js +0 -4
- package/dist/client/assets/channel-BwRO4uiw.js +0 -1
- package/dist/client/assets/classDiagram-4FO5ZUOK-BIszcze-.js +0 -1
- package/dist/client/assets/classDiagram-v2-Q7XG4LA2-D5dvxldS.js +0 -1
- package/dist/client/assets/index-CYPgEn1h.css +0 -2
- package/dist/client/assets/index-Ck2OKMsl.js +0 -276
- package/dist/client/assets/stateDiagram-v2-BHNVJYJU-DmICWsmn.js +0 -1
package/dist/server/db.js
CHANGED
|
@@ -3,7 +3,7 @@ import { createHash } from 'crypto';
|
|
|
3
3
|
import { parseCheckDefinitionsJsonStrict, parseLegacyTestCommandsJsonStrict, projectCheckDefinitionsToTestCommands, } from './lib/checkDefinitions.js';
|
|
4
4
|
import { ensureParentDirectory, resolveArchonDbPath } from './lib/runtimePaths.js';
|
|
5
5
|
import { TRANSCRIPT_RUN_TEXT_LIMIT_BYTES, truncateUtf8 } from './lib/transcriptRuns.js';
|
|
6
|
-
import { createExecutionProfile, fingerprintExecutionProfile, serializeExecutionProfile, } from './lib/executionProfiles.js';
|
|
6
|
+
import { createExecutionProfile, EXECUTION_PROFILE_SCHEMA_VERSION, fingerprintExecutionProfile, normalizeExecutionProfile, serializeExecutionProfile, } from './lib/executionProfiles.js';
|
|
7
7
|
const dbPath = ensureParentDirectory(resolveArchonDbPath());
|
|
8
8
|
const db = new Database(dbPath);
|
|
9
9
|
db.pragma('foreign_keys = ON');
|
|
@@ -181,7 +181,7 @@ db.exec(`
|
|
|
181
181
|
id TEXT PRIMARY KEY,
|
|
182
182
|
project_id TEXT NOT NULL,
|
|
183
183
|
revision INTEGER NOT NULL CHECK (revision > 0),
|
|
184
|
-
schema_version INTEGER NOT NULL CHECK (schema_version
|
|
184
|
+
schema_version INTEGER NOT NULL CHECK (schema_version IN (1, 2)),
|
|
185
185
|
fingerprint TEXT NOT NULL CHECK (length(fingerprint) = 64),
|
|
186
186
|
profile_json TEXT NOT NULL CHECK (
|
|
187
187
|
json_valid(profile_json)
|
|
@@ -220,6 +220,46 @@ db.exec(`
|
|
|
220
220
|
CREATE INDEX IF NOT EXISTS idx_execution_profile_audit_project
|
|
221
221
|
ON execution_profile_audit_events(project_id, created_at DESC, id DESC);
|
|
222
222
|
`);
|
|
223
|
+
const executionProfileRevisionsTableSql = db.prepare("SELECT sql FROM sqlite_master WHERE type = 'table' AND name = 'execution_profile_revisions'").get()?.sql ?? '';
|
|
224
|
+
if (!executionProfileRevisionsTableSql.includes('schema_version IN (1, 2)')) {
|
|
225
|
+
db.pragma('foreign_keys = OFF');
|
|
226
|
+
db.transaction(() => {
|
|
227
|
+
const referencingTriggers = db.prepare(`
|
|
228
|
+
SELECT name FROM sqlite_master
|
|
229
|
+
WHERE type = 'trigger' AND instr(sql, 'execution_profile_revisions') > 0
|
|
230
|
+
`).all();
|
|
231
|
+
for (const trigger of referencingTriggers) {
|
|
232
|
+
db.exec(`DROP TRIGGER IF EXISTS "${trigger.name.replaceAll('"', '""')}"`);
|
|
233
|
+
}
|
|
234
|
+
db.exec(`
|
|
235
|
+
CREATE TABLE execution_profile_revisions_v2 (
|
|
236
|
+
id TEXT PRIMARY KEY,
|
|
237
|
+
project_id TEXT NOT NULL,
|
|
238
|
+
revision INTEGER NOT NULL CHECK (revision > 0),
|
|
239
|
+
schema_version INTEGER NOT NULL CHECK (schema_version IN (1, 2)),
|
|
240
|
+
fingerprint TEXT NOT NULL CHECK (length(fingerprint) = 64),
|
|
241
|
+
profile_json TEXT NOT NULL CHECK (
|
|
242
|
+
json_valid(profile_json)
|
|
243
|
+
AND json_type(profile_json) = 'object'
|
|
244
|
+
AND length(CAST(profile_json AS BLOB)) BETWEEN 2 AND 16384
|
|
245
|
+
),
|
|
246
|
+
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
247
|
+
UNIQUE (project_id, revision),
|
|
248
|
+
UNIQUE (project_id, fingerprint),
|
|
249
|
+
UNIQUE (project_id, id),
|
|
250
|
+
FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE
|
|
251
|
+
);
|
|
252
|
+
INSERT INTO execution_profile_revisions_v2 (
|
|
253
|
+
id, project_id, revision, schema_version, fingerprint, profile_json, created_at
|
|
254
|
+
)
|
|
255
|
+
SELECT id, project_id, revision, schema_version, fingerprint, profile_json, created_at
|
|
256
|
+
FROM execution_profile_revisions;
|
|
257
|
+
DROP TABLE execution_profile_revisions;
|
|
258
|
+
ALTER TABLE execution_profile_revisions_v2 RENAME TO execution_profile_revisions;
|
|
259
|
+
`);
|
|
260
|
+
})();
|
|
261
|
+
db.pragma('foreign_keys = ON');
|
|
262
|
+
}
|
|
223
263
|
const backfillExecutionProfiles = () => {
|
|
224
264
|
const projects = db.prepare(`
|
|
225
265
|
SELECT id, build_execution_provider, active_execution_profile_revision_id
|
|
@@ -231,7 +271,7 @@ const backfillExecutionProfiles = () => {
|
|
|
231
271
|
const insertRevision = db.prepare(`
|
|
232
272
|
INSERT OR IGNORE INTO execution_profile_revisions (
|
|
233
273
|
id, project_id, revision, schema_version, fingerprint, profile_json
|
|
234
|
-
) VALUES (?, ?, 1,
|
|
274
|
+
) VALUES (?, ?, 1, ${EXECUTION_PROFILE_SCHEMA_VERSION}, ?, ?)
|
|
235
275
|
`);
|
|
236
276
|
const activateRevision = db.prepare(`
|
|
237
277
|
UPDATE projects SET active_execution_profile_revision_id = ? WHERE id = ?
|
|
@@ -257,7 +297,70 @@ const backfillExecutionProfiles = () => {
|
|
|
257
297
|
}
|
|
258
298
|
})();
|
|
259
299
|
};
|
|
300
|
+
const backfillExecutionProfilesToSchemaV2 = () => {
|
|
301
|
+
const legacyRows = db.prepare(`
|
|
302
|
+
SELECT projects.id AS project_id,
|
|
303
|
+
revisions.id AS revision_id,
|
|
304
|
+
revisions.profile_json
|
|
305
|
+
FROM projects
|
|
306
|
+
INNER JOIN execution_profile_revisions revisions
|
|
307
|
+
ON revisions.id = projects.active_execution_profile_revision_id
|
|
308
|
+
AND revisions.project_id = projects.id
|
|
309
|
+
WHERE revisions.schema_version = 1 AND projects.deletion_started_at IS NULL
|
|
310
|
+
`).all();
|
|
311
|
+
const nextRevisionNumber = db.prepare(`
|
|
312
|
+
SELECT COALESCE(MAX(revision), 0) + 1 AS revision
|
|
313
|
+
FROM execution_profile_revisions WHERE project_id = ?
|
|
314
|
+
`);
|
|
315
|
+
const findRevisionByFingerprint = db.prepare(`
|
|
316
|
+
SELECT id FROM execution_profile_revisions WHERE project_id = ? AND fingerprint = ?
|
|
317
|
+
`);
|
|
318
|
+
const insertRevision = db.prepare(`
|
|
319
|
+
INSERT OR IGNORE INTO execution_profile_revisions (
|
|
320
|
+
id, project_id, revision, schema_version, fingerprint, profile_json
|
|
321
|
+
) VALUES (?, ?, ?, ${EXECUTION_PROFILE_SCHEMA_VERSION}, ?, ?)
|
|
322
|
+
`);
|
|
323
|
+
const activateRevision = db.prepare(`
|
|
324
|
+
UPDATE projects SET active_execution_profile_revision_id = ?
|
|
325
|
+
WHERE id = ? AND active_execution_profile_revision_id = ?
|
|
326
|
+
`);
|
|
327
|
+
const insertEvent = db.prepare(`
|
|
328
|
+
INSERT OR IGNORE INTO execution_profile_audit_events (
|
|
329
|
+
id, project_id, event_type, profile_revision_id, previous_profile_revision_id
|
|
330
|
+
) VALUES (?, ?, 'revision_activated', ?, ?)
|
|
331
|
+
`);
|
|
332
|
+
db.transaction(() => {
|
|
333
|
+
for (const row of legacyRows) {
|
|
334
|
+
let normalized;
|
|
335
|
+
try {
|
|
336
|
+
normalized = normalizeExecutionProfile(JSON.parse(row.profile_json));
|
|
337
|
+
}
|
|
338
|
+
catch {
|
|
339
|
+
normalized = null;
|
|
340
|
+
}
|
|
341
|
+
if (!normalized?.ok) {
|
|
342
|
+
throw new Error(`Stored execution profile revision ${row.revision_id} is invalid`);
|
|
343
|
+
}
|
|
344
|
+
const fingerprint = fingerprintExecutionProfile(normalized.profile);
|
|
345
|
+
const existing = findRevisionByFingerprint.get(row.project_id, fingerprint);
|
|
346
|
+
let revisionId;
|
|
347
|
+
if (existing) {
|
|
348
|
+
revisionId = existing.id;
|
|
349
|
+
}
|
|
350
|
+
else {
|
|
351
|
+
revisionId = `${row.project_id}:execution-profile:v2`;
|
|
352
|
+
const revisionNumber = nextRevisionNumber.get(row.project_id).revision;
|
|
353
|
+
insertRevision.run(revisionId, row.project_id, revisionNumber, fingerprint, serializeExecutionProfile(normalized.profile));
|
|
354
|
+
}
|
|
355
|
+
const activated = activateRevision.run(revisionId, row.project_id, row.revision_id);
|
|
356
|
+
if (activated.changes > 0) {
|
|
357
|
+
insertEvent.run(`${revisionId}:v2-backfill`, row.project_id, revisionId, row.revision_id);
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
})();
|
|
361
|
+
};
|
|
260
362
|
backfillExecutionProfiles();
|
|
363
|
+
backfillExecutionProfilesToSchemaV2();
|
|
261
364
|
const hostCompatibilityProfile = createExecutionProfile('host');
|
|
262
365
|
const hostCompatibilityProfileJson = serializeExecutionProfile(hostCompatibilityProfile).replaceAll("'", "''");
|
|
263
366
|
const hostCompatibilityFingerprint = fingerprintExecutionProfile(hostCompatibilityProfile);
|
|
@@ -272,7 +375,7 @@ db.exec(`
|
|
|
272
375
|
INSERT INTO execution_profile_revisions (
|
|
273
376
|
id, project_id, revision, schema_version, fingerprint, profile_json
|
|
274
377
|
) VALUES (
|
|
275
|
-
NEW.id || ':execution-profile:1', NEW.id, 1,
|
|
378
|
+
NEW.id || ':execution-profile:1', NEW.id, 1, ${EXECUTION_PROFILE_SCHEMA_VERSION},
|
|
276
379
|
CASE WHEN NEW.build_execution_provider = 'docker_sandbox'
|
|
277
380
|
THEN '${dockerProfileFingerprint}' ELSE '${hostCompatibilityFingerprint}' END,
|
|
278
381
|
CASE WHEN NEW.build_execution_provider = 'docker_sandbox'
|
|
@@ -2614,6 +2717,217 @@ db.exec(`
|
|
|
2614
2717
|
END;
|
|
2615
2718
|
|
|
2616
2719
|
`);
|
|
2720
|
+
db.exec(`
|
|
2721
|
+
CREATE TABLE IF NOT EXISTS approval_requests (
|
|
2722
|
+
id TEXT PRIMARY KEY,
|
|
2723
|
+
project_id TEXT NOT NULL,
|
|
2724
|
+
execution_run_id TEXT NOT NULL,
|
|
2725
|
+
action_kind TEXT NOT NULL,
|
|
2726
|
+
action_schema_version INTEGER NOT NULL,
|
|
2727
|
+
action_json TEXT NOT NULL,
|
|
2728
|
+
action_fingerprint TEXT NOT NULL,
|
|
2729
|
+
authority_schema_version INTEGER NOT NULL,
|
|
2730
|
+
authority_json TEXT NOT NULL,
|
|
2731
|
+
status TEXT NOT NULL DEFAULT 'pending',
|
|
2732
|
+
expires_at DATETIME NOT NULL,
|
|
2733
|
+
decision_at DATETIME,
|
|
2734
|
+
consumed_at DATETIME,
|
|
2735
|
+
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
2736
|
+
updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
2737
|
+
UNIQUE (id, project_id),
|
|
2738
|
+
UNIQUE (execution_run_id),
|
|
2739
|
+
FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE,
|
|
2740
|
+
FOREIGN KEY (execution_run_id, project_id) REFERENCES execution_runs(id, project_id) ON DELETE CASCADE,
|
|
2741
|
+
CHECK (length(id) BETWEEN 1 AND 128),
|
|
2742
|
+
CHECK (action_kind IN ('git_push', 'custom_check')),
|
|
2743
|
+
CHECK (action_schema_version = 1),
|
|
2744
|
+
CHECK (length(CAST(action_json AS BLOB)) BETWEEN 2 AND 65536),
|
|
2745
|
+
CHECK (length(action_fingerprint) = 64 AND action_fingerprint NOT GLOB '*[^0-9a-f]*'),
|
|
2746
|
+
CHECK (authority_schema_version = 1),
|
|
2747
|
+
CHECK (length(CAST(authority_json AS BLOB)) BETWEEN 2 AND 16384),
|
|
2748
|
+
CHECK (status IN ('pending', 'approved', 'denied', 'cancelled', 'expired', 'invalidated', 'consumed')),
|
|
2749
|
+
CHECK (status NOT IN ('approved', 'denied', 'consumed') OR decision_at IS NOT NULL),
|
|
2750
|
+
CHECK (status <> 'pending' OR decision_at IS NULL),
|
|
2751
|
+
CHECK ((status = 'consumed') = (consumed_at IS NOT NULL))
|
|
2752
|
+
);
|
|
2753
|
+
|
|
2754
|
+
CREATE TABLE IF NOT EXISTS approval_request_events (
|
|
2755
|
+
id TEXT PRIMARY KEY,
|
|
2756
|
+
project_id TEXT NOT NULL,
|
|
2757
|
+
approval_request_id TEXT NOT NULL,
|
|
2758
|
+
sequence INTEGER NOT NULL,
|
|
2759
|
+
event_type TEXT NOT NULL,
|
|
2760
|
+
from_status TEXT,
|
|
2761
|
+
to_status TEXT NOT NULL,
|
|
2762
|
+
actor_classification TEXT NOT NULL,
|
|
2763
|
+
actor_reference TEXT,
|
|
2764
|
+
reason TEXT,
|
|
2765
|
+
metadata_schema_version INTEGER NOT NULL DEFAULT 1,
|
|
2766
|
+
metadata_json TEXT,
|
|
2767
|
+
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
2768
|
+
UNIQUE (approval_request_id, sequence),
|
|
2769
|
+
FOREIGN KEY (approval_request_id, project_id) REFERENCES approval_requests(id, project_id) ON DELETE CASCADE,
|
|
2770
|
+
CHECK (sequence >= 1),
|
|
2771
|
+
CHECK (length(event_type) BETWEEN 1 AND 100),
|
|
2772
|
+
CHECK (from_status IS NULL OR from_status IN ('pending', 'approved', 'denied', 'cancelled', 'expired', 'invalidated', 'consumed')),
|
|
2773
|
+
CHECK (to_status IN ('pending', 'approved', 'denied', 'cancelled', 'expired', 'invalidated', 'consumed')),
|
|
2774
|
+
CHECK (actor_classification IN ('human', 'system', 'action_executor')),
|
|
2775
|
+
CHECK (actor_reference IS NULL OR length(CAST(actor_reference AS BLOB)) BETWEEN 1 AND 200),
|
|
2776
|
+
CHECK (reason IS NULL OR length(CAST(reason AS BLOB)) <= 2000),
|
|
2777
|
+
CHECK (metadata_schema_version = 1),
|
|
2778
|
+
CHECK (metadata_json IS NULL OR length(CAST(metadata_json AS BLOB)) <= 4096)
|
|
2779
|
+
);
|
|
2780
|
+
|
|
2781
|
+
CREATE TRIGGER IF NOT EXISTS preserve_approval_request_identity
|
|
2782
|
+
BEFORE UPDATE OF id, project_id, execution_run_id, action_kind, action_schema_version,
|
|
2783
|
+
action_json, action_fingerprint, authority_schema_version, authority_json, expires_at, created_at
|
|
2784
|
+
ON approval_requests
|
|
2785
|
+
BEGIN
|
|
2786
|
+
SELECT RAISE(ABORT, 'Approval request identity and evidence are immutable');
|
|
2787
|
+
END;
|
|
2788
|
+
|
|
2789
|
+
CREATE TRIGGER IF NOT EXISTS preserve_approval_request_decision_evidence
|
|
2790
|
+
BEFORE UPDATE OF decision_at, consumed_at ON approval_requests
|
|
2791
|
+
WHEN (OLD.decision_at IS NOT NULL AND NEW.decision_at IS NOT OLD.decision_at)
|
|
2792
|
+
OR (OLD.consumed_at IS NOT NULL AND NEW.consumed_at IS NOT OLD.consumed_at)
|
|
2793
|
+
BEGIN
|
|
2794
|
+
SELECT RAISE(ABORT, 'Approval request decision evidence is immutable');
|
|
2795
|
+
END;
|
|
2796
|
+
|
|
2797
|
+
CREATE TRIGGER IF NOT EXISTS validate_approval_request_status_transition
|
|
2798
|
+
BEFORE UPDATE OF status ON approval_requests
|
|
2799
|
+
WHEN NOT (
|
|
2800
|
+
(OLD.status = 'pending' AND NEW.status IN ('approved', 'denied', 'cancelled', 'expired', 'invalidated'))
|
|
2801
|
+
OR (OLD.status = 'approved' AND NEW.status IN ('consumed', 'expired', 'invalidated'))
|
|
2802
|
+
)
|
|
2803
|
+
BEGIN
|
|
2804
|
+
SELECT RAISE(ABORT, 'Invalid approval request status transition');
|
|
2805
|
+
END;
|
|
2806
|
+
|
|
2807
|
+
CREATE TRIGGER IF NOT EXISTS preserve_approval_request_events
|
|
2808
|
+
BEFORE UPDATE ON approval_request_events
|
|
2809
|
+
BEGIN
|
|
2810
|
+
SELECT RAISE(ABORT, 'Approval request events are append-only');
|
|
2811
|
+
END;
|
|
2812
|
+
|
|
2813
|
+
CREATE TRIGGER IF NOT EXISTS prevent_approval_request_event_deletion
|
|
2814
|
+
BEFORE DELETE ON approval_request_events
|
|
2815
|
+
WHEN EXISTS (
|
|
2816
|
+
SELECT 1 FROM projects
|
|
2817
|
+
WHERE id = OLD.project_id AND deletion_started_at IS NULL
|
|
2818
|
+
)
|
|
2819
|
+
BEGIN
|
|
2820
|
+
SELECT RAISE(ABORT, 'Approval request events are append-only');
|
|
2821
|
+
END;
|
|
2822
|
+
|
|
2823
|
+
CREATE INDEX IF NOT EXISTS idx_approval_requests_project_created
|
|
2824
|
+
ON approval_requests(project_id, created_at DESC, id DESC);
|
|
2825
|
+
CREATE INDEX IF NOT EXISTS idx_approval_requests_project_status_expiry
|
|
2826
|
+
ON approval_requests(project_id, status, expires_at, id);
|
|
2827
|
+
CREATE INDEX IF NOT EXISTS idx_approval_requests_action_kind
|
|
2828
|
+
ON approval_requests(project_id, action_kind, created_at DESC, id DESC);
|
|
2829
|
+
CREATE INDEX IF NOT EXISTS idx_approval_request_events_sequence
|
|
2830
|
+
ON approval_request_events(project_id, approval_request_id, sequence ASC);
|
|
2831
|
+
`);
|
|
2832
|
+
const migrateApprovalRequestActionLimit = () => {
|
|
2833
|
+
const tableSql = db.prepare("SELECT sql FROM sqlite_master WHERE type = 'table' AND name = 'approval_requests'").get()?.sql ?? '';
|
|
2834
|
+
if (tableSql.includes('BETWEEN 2 AND 65536')) {
|
|
2835
|
+
return;
|
|
2836
|
+
}
|
|
2837
|
+
db.exec('PRAGMA foreign_keys = OFF');
|
|
2838
|
+
try {
|
|
2839
|
+
db.exec('BEGIN');
|
|
2840
|
+
db.exec(`
|
|
2841
|
+
CREATE TABLE approval_requests_v2 (
|
|
2842
|
+
id TEXT PRIMARY KEY,
|
|
2843
|
+
project_id TEXT NOT NULL,
|
|
2844
|
+
execution_run_id TEXT NOT NULL,
|
|
2845
|
+
action_kind TEXT NOT NULL,
|
|
2846
|
+
action_schema_version INTEGER NOT NULL,
|
|
2847
|
+
action_json TEXT NOT NULL,
|
|
2848
|
+
action_fingerprint TEXT NOT NULL,
|
|
2849
|
+
authority_schema_version INTEGER NOT NULL,
|
|
2850
|
+
authority_json TEXT NOT NULL,
|
|
2851
|
+
status TEXT NOT NULL DEFAULT 'pending',
|
|
2852
|
+
expires_at DATETIME NOT NULL,
|
|
2853
|
+
decision_at DATETIME,
|
|
2854
|
+
consumed_at DATETIME,
|
|
2855
|
+
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
2856
|
+
updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
2857
|
+
UNIQUE (id, project_id),
|
|
2858
|
+
UNIQUE (execution_run_id),
|
|
2859
|
+
FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE,
|
|
2860
|
+
FOREIGN KEY (execution_run_id, project_id) REFERENCES execution_runs(id, project_id) ON DELETE CASCADE,
|
|
2861
|
+
CHECK (length(id) BETWEEN 1 AND 128),
|
|
2862
|
+
CHECK (action_kind IN ('git_push', 'custom_check')),
|
|
2863
|
+
CHECK (action_schema_version = 1),
|
|
2864
|
+
CHECK (length(CAST(action_json AS BLOB)) BETWEEN 2 AND 65536),
|
|
2865
|
+
CHECK (length(action_fingerprint) = 64 AND action_fingerprint NOT GLOB '*[^0-9a-f]*'),
|
|
2866
|
+
CHECK (authority_schema_version = 1),
|
|
2867
|
+
CHECK (length(CAST(authority_json AS BLOB)) BETWEEN 2 AND 16384),
|
|
2868
|
+
CHECK (status IN ('pending', 'approved', 'denied', 'cancelled', 'expired', 'invalidated', 'consumed')),
|
|
2869
|
+
CHECK (status NOT IN ('approved', 'denied', 'consumed') OR decision_at IS NOT NULL),
|
|
2870
|
+
CHECK (status <> 'pending' OR decision_at IS NULL),
|
|
2871
|
+
CHECK ((status = 'consumed') = (consumed_at IS NOT NULL))
|
|
2872
|
+
);
|
|
2873
|
+
INSERT INTO approval_requests_v2 (
|
|
2874
|
+
id, project_id, execution_run_id, action_kind, action_schema_version, action_json,
|
|
2875
|
+
action_fingerprint, authority_schema_version, authority_json, status, expires_at,
|
|
2876
|
+
decision_at, consumed_at, created_at, updated_at
|
|
2877
|
+
)
|
|
2878
|
+
SELECT
|
|
2879
|
+
id, project_id, execution_run_id, action_kind, action_schema_version, action_json,
|
|
2880
|
+
action_fingerprint, authority_schema_version, authority_json, status, expires_at,
|
|
2881
|
+
decision_at, consumed_at, created_at, updated_at
|
|
2882
|
+
FROM approval_requests;
|
|
2883
|
+
DROP TABLE approval_requests;
|
|
2884
|
+
ALTER TABLE approval_requests_v2 RENAME TO approval_requests;
|
|
2885
|
+
`);
|
|
2886
|
+
db.exec('COMMIT');
|
|
2887
|
+
}
|
|
2888
|
+
catch (error) {
|
|
2889
|
+
db.exec('ROLLBACK');
|
|
2890
|
+
throw error;
|
|
2891
|
+
}
|
|
2892
|
+
finally {
|
|
2893
|
+
db.exec('PRAGMA foreign_keys = ON');
|
|
2894
|
+
}
|
|
2895
|
+
db.exec(`
|
|
2896
|
+
CREATE TRIGGER preserve_approval_request_identity
|
|
2897
|
+
BEFORE UPDATE OF id, project_id, execution_run_id, action_kind, action_schema_version,
|
|
2898
|
+
action_json, action_fingerprint, authority_schema_version, authority_json, expires_at, created_at
|
|
2899
|
+
ON approval_requests
|
|
2900
|
+
BEGIN
|
|
2901
|
+
SELECT RAISE(ABORT, 'Approval request identity and evidence are immutable');
|
|
2902
|
+
END;
|
|
2903
|
+
|
|
2904
|
+
CREATE TRIGGER preserve_approval_request_decision_evidence
|
|
2905
|
+
BEFORE UPDATE OF decision_at, consumed_at ON approval_requests
|
|
2906
|
+
WHEN (OLD.decision_at IS NOT NULL AND NEW.decision_at IS NOT OLD.decision_at)
|
|
2907
|
+
OR (OLD.consumed_at IS NOT NULL AND NEW.consumed_at IS NOT OLD.consumed_at)
|
|
2908
|
+
BEGIN
|
|
2909
|
+
SELECT RAISE(ABORT, 'Approval request decision evidence is immutable');
|
|
2910
|
+
END;
|
|
2911
|
+
|
|
2912
|
+
CREATE TRIGGER validate_approval_request_status_transition
|
|
2913
|
+
BEFORE UPDATE OF status ON approval_requests
|
|
2914
|
+
WHEN NOT (
|
|
2915
|
+
(OLD.status = 'pending' AND NEW.status IN ('approved', 'denied', 'cancelled', 'expired', 'invalidated'))
|
|
2916
|
+
OR (OLD.status = 'approved' AND NEW.status IN ('consumed', 'expired', 'invalidated'))
|
|
2917
|
+
)
|
|
2918
|
+
BEGIN
|
|
2919
|
+
SELECT RAISE(ABORT, 'Invalid approval request status transition');
|
|
2920
|
+
END;
|
|
2921
|
+
|
|
2922
|
+
CREATE INDEX idx_approval_requests_project_created
|
|
2923
|
+
ON approval_requests(project_id, created_at DESC, id DESC);
|
|
2924
|
+
CREATE INDEX idx_approval_requests_project_status_expiry
|
|
2925
|
+
ON approval_requests(project_id, status, expires_at, id);
|
|
2926
|
+
CREATE INDEX idx_approval_requests_action_kind
|
|
2927
|
+
ON approval_requests(project_id, action_kind, created_at DESC, id DESC);
|
|
2928
|
+
`);
|
|
2929
|
+
};
|
|
2930
|
+
migrateApprovalRequestActionLimit();
|
|
2617
2931
|
db.exec(`
|
|
2618
2932
|
CREATE TABLE IF NOT EXISTS build_setup_evidence (
|
|
2619
2933
|
id TEXT PRIMARY KEY,
|
|
@@ -3102,7 +3416,7 @@ db.exec(`
|
|
|
3102
3416
|
CHECK (length(definition_fingerprint) = 64 AND definition_fingerprint NOT GLOB '*[^0-9a-f]*'),
|
|
3103
3417
|
CHECK (outcome IS NULL OR outcome IN ('passed', 'command_failure', 'spawn_failure',
|
|
3104
3418
|
'infrastructure_failure', 'evidence_persistence_failure', 'timeout',
|
|
3105
|
-
'workspace_changed_before_start', 'user_stop', 'restart_loss', 'shutdown_loss')),
|
|
3419
|
+
'workspace_changed_before_start', 'user_stop', 'restart_loss', 'shutdown_loss', 'ambiguous_process_loss')),
|
|
3106
3420
|
CHECK (output_revision >= 0 AND retained_start_offset >= 0 AND retained_end_offset >= retained_start_offset
|
|
3107
3421
|
AND total_persisted_bytes >= retained_end_offset),
|
|
3108
3422
|
CHECK (output_retention_state IN ('retained', 'expired'))
|
|
@@ -3113,14 +3427,23 @@ ensureColumn('check_attempt_details', 'retained_output', "TEXT NOT NULL DEFAULT
|
|
|
3113
3427
|
const checkAttemptSchema = db.prepare(`
|
|
3114
3428
|
SELECT sql FROM sqlite_master WHERE type = 'table' AND name = 'check_attempt_details'
|
|
3115
3429
|
`).get();
|
|
3116
|
-
if (checkAttemptSchema && !checkAttemptSchema.sql.includes('evidence_persistence_failure')
|
|
3117
|
-
|
|
3118
|
-
|
|
3119
|
-
|
|
3120
|
-
|
|
3121
|
-
|
|
3122
|
-
|
|
3123
|
-
|
|
3430
|
+
if (checkAttemptSchema && (!checkAttemptSchema.sql.includes('evidence_persistence_failure')
|
|
3431
|
+
|| !checkAttemptSchema.sql.includes('ambiguous_process_loss'))) {
|
|
3432
|
+
const foreignKeysEnabled = db.pragma('foreign_keys', { simple: true }) === 1;
|
|
3433
|
+
if (foreignKeysEnabled) {
|
|
3434
|
+
db.pragma('foreign_keys = OFF');
|
|
3435
|
+
}
|
|
3436
|
+
try {
|
|
3437
|
+
const referencingTriggers = db.prepare(`
|
|
3438
|
+
SELECT name FROM sqlite_master
|
|
3439
|
+
WHERE type = 'trigger' AND sql LIKE '%check_attempt_details%'
|
|
3440
|
+
`).all();
|
|
3441
|
+
for (const trigger of referencingTriggers) {
|
|
3442
|
+
db.exec(`DROP TRIGGER IF EXISTS "${trigger.name.replaceAll('"', '""')}"`);
|
|
3443
|
+
}
|
|
3444
|
+
db.transaction(() => {
|
|
3445
|
+
db.exec(`
|
|
3446
|
+
CREATE TABLE check_attempt_details_replacement (
|
|
3124
3447
|
execution_run_id TEXT PRIMARY KEY,
|
|
3125
3448
|
batch_run_id TEXT NOT NULL,
|
|
3126
3449
|
definition_id TEXT NOT NULL,
|
|
@@ -3146,13 +3469,13 @@ if (checkAttemptSchema && !checkAttemptSchema.sql.includes('evidence_persistence
|
|
|
3146
3469
|
CHECK (length(definition_fingerprint) = 64 AND definition_fingerprint NOT GLOB '*[^0-9a-f]*'),
|
|
3147
3470
|
CHECK (outcome IS NULL OR outcome IN ('passed', 'command_failure', 'spawn_failure',
|
|
3148
3471
|
'infrastructure_failure', 'evidence_persistence_failure', 'timeout',
|
|
3149
|
-
'workspace_changed_before_start', 'user_stop', 'restart_loss', 'shutdown_loss')),
|
|
3472
|
+
'workspace_changed_before_start', 'user_stop', 'restart_loss', 'shutdown_loss', 'ambiguous_process_loss')),
|
|
3150
3473
|
CHECK (output_revision >= 0 AND retained_start_offset >= 0 AND retained_end_offset >= retained_start_offset
|
|
3151
3474
|
AND total_persisted_bytes >= retained_end_offset),
|
|
3152
3475
|
CHECK (output_retention_state IN ('retained', 'expired'))
|
|
3153
3476
|
);
|
|
3154
3477
|
|
|
3155
|
-
INSERT INTO
|
|
3478
|
+
INSERT INTO check_attempt_details_replacement (
|
|
3156
3479
|
execution_run_id, batch_run_id, definition_id, command_snapshot_json, definition_fingerprint,
|
|
3157
3480
|
initial_workspace_identity_json, final_workspace_identity_json, outcome, rerun_of_attempt_run_id,
|
|
3158
3481
|
exit_code, exit_signal, exit_error, output_revision, retained_start_offset, retained_end_offset,
|
|
@@ -3163,11 +3486,22 @@ if (checkAttemptSchema && !checkAttemptSchema.sql.includes('evidence_persistence
|
|
|
3163
3486
|
initial_workspace_identity_json, final_workspace_identity_json, outcome, rerun_of_attempt_run_id,
|
|
3164
3487
|
exit_code, exit_signal, exit_error, output_revision, retained_start_offset, retained_end_offset,
|
|
3165
3488
|
total_persisted_bytes, retained_output, output_retention_state
|
|
3166
|
-
FROM
|
|
3489
|
+
FROM check_attempt_details;
|
|
3167
3490
|
|
|
3168
|
-
DROP TABLE
|
|
3169
|
-
|
|
3170
|
-
|
|
3491
|
+
DROP TABLE check_attempt_details;
|
|
3492
|
+
ALTER TABLE check_attempt_details_replacement RENAME TO check_attempt_details;
|
|
3493
|
+
`);
|
|
3494
|
+
})();
|
|
3495
|
+
}
|
|
3496
|
+
finally {
|
|
3497
|
+
if (foreignKeysEnabled) {
|
|
3498
|
+
db.pragma('foreign_keys = ON');
|
|
3499
|
+
}
|
|
3500
|
+
}
|
|
3501
|
+
const foreignKeyViolation = db.pragma('foreign_key_check')[0];
|
|
3502
|
+
if (foreignKeyViolation) {
|
|
3503
|
+
throw new Error(`Check attempt migration left an invalid foreign key: ${JSON.stringify(foreignKeyViolation)}`);
|
|
3504
|
+
}
|
|
3171
3505
|
}
|
|
3172
3506
|
db.exec(`
|
|
3173
3507
|
CREATE TABLE IF NOT EXISTS check_attempt_diagnostic_parsers (
|