patchrelay 0.8.8 → 0.9.0
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 +64 -62
- package/dist/agent-session-plan.js +17 -17
- package/dist/build-info.json +3 -3
- package/dist/cli/commands/issues.js +12 -12
- package/dist/cli/data.js +109 -298
- package/dist/cli/formatters/text.js +22 -28
- package/dist/config.js +13 -166
- package/dist/db/migrations.js +46 -154
- package/dist/db.js +369 -45
- package/dist/factory-state.js +55 -0
- package/dist/github-webhook-handler.js +199 -0
- package/dist/github-webhooks.js +166 -0
- package/dist/hook-runner.js +28 -0
- package/dist/http.js +48 -22
- package/dist/issue-query-service.js +33 -38
- package/dist/linear-workflow.js +5 -118
- package/dist/preflight.js +1 -6
- package/dist/project-resolution.js +12 -1
- package/dist/run-orchestrator.js +446 -0
- package/dist/{stage-reporting.js → run-reporting.js} +11 -13
- package/dist/service-runtime.js +21 -54
- package/dist/service-webhooks.js +7 -52
- package/dist/service.js +39 -61
- package/dist/webhook-handler.js +387 -0
- package/dist/webhook-installation-handler.js +3 -8
- package/package.json +2 -1
- package/dist/db/authoritative-ledger-store.js +0 -536
- package/dist/db/issue-projection-store.js +0 -54
- package/dist/db/issue-workflow-coordinator.js +0 -320
- package/dist/db/issue-workflow-store.js +0 -194
- package/dist/db/run-report-store.js +0 -33
- package/dist/db/stage-event-store.js +0 -33
- package/dist/db/webhook-event-store.js +0 -59
- package/dist/db-ports.js +0 -5
- package/dist/ledger-ports.js +0 -1
- package/dist/reconciliation-action-applier.js +0 -68
- package/dist/reconciliation-actions.js +0 -1
- package/dist/reconciliation-engine.js +0 -350
- package/dist/reconciliation-snapshot-builder.js +0 -135
- package/dist/reconciliation-types.js +0 -1
- package/dist/service-stage-finalizer.js +0 -753
- package/dist/service-stage-runner.js +0 -336
- package/dist/service-webhook-processor.js +0 -411
- package/dist/stage-agent-activity-publisher.js +0 -59
- package/dist/stage-event-ports.js +0 -1
- package/dist/stage-failure.js +0 -92
- package/dist/stage-handoff.js +0 -107
- package/dist/stage-launch.js +0 -84
- package/dist/stage-lifecycle-publisher.js +0 -284
- package/dist/stage-turn-input-dispatcher.js +0 -104
- package/dist/webhook-agent-session-handler.js +0 -228
- package/dist/webhook-comment-handler.js +0 -141
- package/dist/webhook-desired-stage-recorder.js +0 -122
- package/dist/webhook-event-ports.js +0 -1
- package/dist/workflow-policy.js +0 -149
- package/dist/workflow-ports.js +0 -1
- /package/dist/{installation-ports.js → github-types.js} +0 -0
package/dist/config.js
CHANGED
|
@@ -7,32 +7,6 @@ import { ensureAbsolutePath } from "./utils.js";
|
|
|
7
7
|
const LINEAR_OAUTH_CALLBACK_PATH = "/oauth/linear/callback";
|
|
8
8
|
const REPO_SETTINGS_DIRNAME = ".patchrelay";
|
|
9
9
|
const REPO_SETTINGS_FILENAME = "project.json";
|
|
10
|
-
const workflowSchema = z.object({
|
|
11
|
-
id: z.string().min(1),
|
|
12
|
-
when_state: z.string().min(1),
|
|
13
|
-
active_state: z.string().min(1),
|
|
14
|
-
workflow_file: z.string().min(1),
|
|
15
|
-
fallback_state: z.string().min(1).nullable().optional(),
|
|
16
|
-
});
|
|
17
|
-
const workflowDefinitionSchema = z.object({
|
|
18
|
-
id: z.string().min(1),
|
|
19
|
-
stages: z.array(workflowSchema).min(1),
|
|
20
|
-
});
|
|
21
|
-
const workflowSelectionSchema = z.object({
|
|
22
|
-
default_workflow: z.string().min(1).optional(),
|
|
23
|
-
by_label: z
|
|
24
|
-
.array(z.object({
|
|
25
|
-
label: z.string().min(1),
|
|
26
|
-
workflow: z.string().min(1),
|
|
27
|
-
}))
|
|
28
|
-
.default([]),
|
|
29
|
-
});
|
|
30
|
-
const workflowLabelsSchema = z
|
|
31
|
-
.object({
|
|
32
|
-
working: z.string().min(1).optional(),
|
|
33
|
-
awaiting_handoff: z.string().min(1).optional(),
|
|
34
|
-
})
|
|
35
|
-
.optional();
|
|
36
10
|
const trustedActorsSchema = z
|
|
37
11
|
.object({
|
|
38
12
|
ids: z.array(z.string().min(1)).default([]),
|
|
@@ -42,11 +16,6 @@ const trustedActorsSchema = z
|
|
|
42
16
|
})
|
|
43
17
|
.optional();
|
|
44
18
|
const repoSettingsSchema = z.object({
|
|
45
|
-
workflows: z.array(workflowSchema).min(1).optional(),
|
|
46
|
-
workflow_definitions: z.array(workflowDefinitionSchema).min(1).optional(),
|
|
47
|
-
workflow_selection: workflowSelectionSchema.optional(),
|
|
48
|
-
workflow_labels: workflowLabelsSchema,
|
|
49
|
-
trusted_actors: trustedActorsSchema,
|
|
50
19
|
trigger_events: z.array(z.string().min(1)).min(1).optional(),
|
|
51
20
|
branch_prefix: z.string().min(1).optional(),
|
|
52
21
|
});
|
|
@@ -54,16 +23,16 @@ const projectSchema = z.object({
|
|
|
54
23
|
id: z.string().min(1),
|
|
55
24
|
repo_path: z.string().min(1),
|
|
56
25
|
worktree_root: z.string().min(1).optional(),
|
|
57
|
-
workflows: z.array(workflowSchema).min(1).optional(),
|
|
58
|
-
workflow_definitions: z.array(workflowDefinitionSchema).min(1).optional(),
|
|
59
|
-
workflow_selection: workflowSelectionSchema.optional(),
|
|
60
|
-
workflow_labels: workflowLabelsSchema,
|
|
61
26
|
trusted_actors: trustedActorsSchema,
|
|
62
27
|
issue_key_prefixes: z.array(z.string().min(1)).default([]),
|
|
63
28
|
linear_team_ids: z.array(z.string().min(1)).default([]),
|
|
64
29
|
allow_labels: z.array(z.string().min(1)).default([]),
|
|
65
30
|
trigger_events: z.array(z.string().min(1)).min(1).optional(),
|
|
66
31
|
branch_prefix: z.string().min(1).optional(),
|
|
32
|
+
github: z.object({
|
|
33
|
+
webhook_secret: z.string().min(1).optional(),
|
|
34
|
+
repo_full_name: z.string().min(1).optional(),
|
|
35
|
+
}).optional(),
|
|
67
36
|
});
|
|
68
37
|
const configSchema = z.object({
|
|
69
38
|
server: z.object({
|
|
@@ -75,6 +44,7 @@ const configSchema = z.object({
|
|
|
75
44
|
}),
|
|
76
45
|
ingress: z.object({
|
|
77
46
|
linear_webhook_path: z.string().default("/webhooks/linear"),
|
|
47
|
+
github_webhook_path: z.string().default("/webhooks/github"),
|
|
78
48
|
max_body_bytes: z.number().int().positive().default(262144),
|
|
79
49
|
max_timestamp_skew_seconds: z.number().int().positive().default(60),
|
|
80
50
|
}),
|
|
@@ -151,42 +121,6 @@ function normalizeTriggerEvents(actor, configured) {
|
|
|
151
121
|
});
|
|
152
122
|
return [...required, ...extras];
|
|
153
123
|
}
|
|
154
|
-
const builtinWorkflows = [
|
|
155
|
-
{
|
|
156
|
-
id: "development",
|
|
157
|
-
when_state: "Start",
|
|
158
|
-
active_state: "Implementing",
|
|
159
|
-
workflow_file: "IMPLEMENTATION_WORKFLOW.md",
|
|
160
|
-
fallback_state: "Human Needed",
|
|
161
|
-
},
|
|
162
|
-
{
|
|
163
|
-
id: "review",
|
|
164
|
-
when_state: "Review",
|
|
165
|
-
active_state: "Reviewing",
|
|
166
|
-
workflow_file: "REVIEW_WORKFLOW.md",
|
|
167
|
-
fallback_state: "Human Needed",
|
|
168
|
-
},
|
|
169
|
-
{
|
|
170
|
-
id: "deploy",
|
|
171
|
-
when_state: "Deploy",
|
|
172
|
-
active_state: "Deploying",
|
|
173
|
-
workflow_file: "DEPLOY_WORKFLOW.md",
|
|
174
|
-
fallback_state: "Human Needed",
|
|
175
|
-
},
|
|
176
|
-
{
|
|
177
|
-
id: "cleanup",
|
|
178
|
-
when_state: "Cleanup",
|
|
179
|
-
active_state: "Cleaning Up",
|
|
180
|
-
workflow_file: "CLEANUP_WORKFLOW.md",
|
|
181
|
-
fallback_state: "Human Needed",
|
|
182
|
-
},
|
|
183
|
-
];
|
|
184
|
-
const builtinWorkflowDefinitions = [
|
|
185
|
-
{
|
|
186
|
-
id: "default",
|
|
187
|
-
stages: builtinWorkflows,
|
|
188
|
-
},
|
|
189
|
-
];
|
|
190
124
|
function withSectionDefaults(input) {
|
|
191
125
|
const source = input && typeof input === "object" ? input : {};
|
|
192
126
|
const { linear: _linear, runner: _runner, ...rest } = source;
|
|
@@ -283,9 +217,6 @@ function readEnvFilesForProfile(configPath, profile) {
|
|
|
283
217
|
const paths = getEnvFilesForProfile(configPath, profile);
|
|
284
218
|
return Object.assign({}, ...paths.map((envPath) => readEnvFile(envPath)));
|
|
285
219
|
}
|
|
286
|
-
function resolveWorkflowFilePath(repoPath, workflowFile) {
|
|
287
|
-
return path.isAbsolute(workflowFile) ? ensureAbsolutePath(workflowFile) : path.resolve(repoPath, workflowFile);
|
|
288
|
-
}
|
|
289
220
|
function parseJsonFile(filePath, label) {
|
|
290
221
|
const raw = readFileSync(filePath, "utf8");
|
|
291
222
|
try {
|
|
@@ -309,49 +240,6 @@ function readRepoSettings(repoPath, env) {
|
|
|
309
240
|
configPath,
|
|
310
241
|
};
|
|
311
242
|
}
|
|
312
|
-
function mergeWorkflowStages(repoPath, workflows) {
|
|
313
|
-
return workflows.map((workflow) => ({
|
|
314
|
-
id: workflow.id,
|
|
315
|
-
whenState: workflow.when_state,
|
|
316
|
-
activeState: workflow.active_state,
|
|
317
|
-
workflowFile: resolveWorkflowFilePath(repoPath, workflow.workflow_file),
|
|
318
|
-
...(workflow.fallback_state ? { fallbackState: workflow.fallback_state } : {}),
|
|
319
|
-
}));
|
|
320
|
-
}
|
|
321
|
-
function mergeWorkflowDefinitions(repoPath, definitions) {
|
|
322
|
-
return definitions.map((definition) => ({
|
|
323
|
-
id: definition.id,
|
|
324
|
-
stages: mergeWorkflowStages(repoPath, definition.stages),
|
|
325
|
-
}));
|
|
326
|
-
}
|
|
327
|
-
function resolveProjectWorkflowConfig(repoPath, project, repoSettings) {
|
|
328
|
-
const selectedDefinitions = repoSettings?.workflow_definitions ??
|
|
329
|
-
project.workflow_definitions ??
|
|
330
|
-
(repoSettings?.workflows
|
|
331
|
-
? [{ id: "default", stages: repoSettings.workflows }]
|
|
332
|
-
: project.workflows
|
|
333
|
-
? [{ id: "default", stages: project.workflows }]
|
|
334
|
-
: builtinWorkflowDefinitions.map((definition) => ({ id: definition.id, stages: [...definition.stages] })));
|
|
335
|
-
const workflowDefinitions = mergeWorkflowDefinitions(repoPath, selectedDefinitions);
|
|
336
|
-
const selectionSource = repoSettings?.workflow_selection ?? project.workflow_selection;
|
|
337
|
-
const defaultWorkflowId = selectionSource?.default_workflow ?? workflowDefinitions[0]?.id;
|
|
338
|
-
const workflows = workflowDefinitions.find((definition) => definition.id === defaultWorkflowId)?.stages ?? workflowDefinitions[0]?.stages ?? [];
|
|
339
|
-
return {
|
|
340
|
-
workflows,
|
|
341
|
-
...(workflowDefinitions.length > 0 ? { workflowDefinitions } : {}),
|
|
342
|
-
...(defaultWorkflowId || (selectionSource?.by_label?.length ?? 0) > 0
|
|
343
|
-
? {
|
|
344
|
-
workflowSelection: {
|
|
345
|
-
...(defaultWorkflowId ? { defaultWorkflowId } : {}),
|
|
346
|
-
byLabel: (selectionSource?.by_label ?? []).map((entry) => ({
|
|
347
|
-
label: entry.label,
|
|
348
|
-
workflowId: entry.workflow,
|
|
349
|
-
})),
|
|
350
|
-
},
|
|
351
|
-
}
|
|
352
|
-
: {}),
|
|
353
|
-
};
|
|
354
|
-
}
|
|
355
243
|
function defaultWorktreeRoot(projectId) {
|
|
356
244
|
return path.join(getPatchRelayDataDir(), "worktrees", projectId);
|
|
357
245
|
}
|
|
@@ -427,6 +315,7 @@ export function loadConfig(configPath = process.env.PATCHRELAY_CONFIG ?? getDefa
|
|
|
427
315
|
},
|
|
428
316
|
ingress: {
|
|
429
317
|
linearWebhookPath: parsed.ingress.linear_webhook_path,
|
|
318
|
+
githubWebhookPath: parsed.ingress.github_webhook_path,
|
|
430
319
|
maxBodyBytes: parsed.ingress.max_body_bytes,
|
|
431
320
|
maxTimestampSkewSeconds: parsed.ingress.max_timestamp_skew_seconds,
|
|
432
321
|
},
|
|
@@ -478,24 +367,11 @@ export function loadConfig(configPath = process.env.PATCHRELAY_CONFIG ?? getDefa
|
|
|
478
367
|
projects: parsed.projects.map((project) => {
|
|
479
368
|
const repoPath = ensureAbsolutePath(project.repo_path);
|
|
480
369
|
const repoSettings = readRepoSettings(repoPath, env);
|
|
481
|
-
const
|
|
482
|
-
const workflowLabels = repoSettings?.workflow_labels ?? project.workflow_labels;
|
|
483
|
-
const trustedActors = repoSettings?.trusted_actors ?? project.trusted_actors;
|
|
370
|
+
const trustedActors = project.trusted_actors;
|
|
484
371
|
return {
|
|
485
372
|
id: project.id,
|
|
486
373
|
repoPath,
|
|
487
374
|
worktreeRoot: ensureAbsolutePath(project.worktree_root ?? defaultWorktreeRoot(project.id)),
|
|
488
|
-
workflows: workflowConfig.workflows,
|
|
489
|
-
...(workflowConfig.workflowDefinitions ? { workflowDefinitions: workflowConfig.workflowDefinitions } : {}),
|
|
490
|
-
...(workflowConfig.workflowSelection ? { workflowSelection: workflowConfig.workflowSelection } : {}),
|
|
491
|
-
...(workflowLabels
|
|
492
|
-
? {
|
|
493
|
-
workflowLabels: {
|
|
494
|
-
...(workflowLabels.working ? { working: workflowLabels.working } : {}),
|
|
495
|
-
...(workflowLabels.awaiting_handoff ? { awaitingHandoff: workflowLabels.awaiting_handoff } : {}),
|
|
496
|
-
},
|
|
497
|
-
}
|
|
498
|
-
: {}),
|
|
499
375
|
...(trustedActors
|
|
500
376
|
? {
|
|
501
377
|
trustedActors: {
|
|
@@ -513,6 +389,12 @@ export function loadConfig(configPath = process.env.PATCHRELAY_CONFIG ?? getDefa
|
|
|
513
389
|
project.trigger_events),
|
|
514
390
|
branchPrefix: repoSettings?.branch_prefix ?? project.branch_prefix ?? defaultBranchPrefix(project.id),
|
|
515
391
|
...(repoSettings?.configPath ? { repoSettingsPath: repoSettings.configPath } : {}),
|
|
392
|
+
...(project.github ? {
|
|
393
|
+
github: {
|
|
394
|
+
...(project.github.webhook_secret ? { webhookSecret: project.github.webhook_secret } : {}),
|
|
395
|
+
...(project.github.repo_full_name ? { repoFullName: project.github.repo_full_name } : {}),
|
|
396
|
+
},
|
|
397
|
+
} : {}),
|
|
516
398
|
};
|
|
517
399
|
}),
|
|
518
400
|
};
|
|
@@ -578,41 +460,6 @@ function validateConfigSemantics(config, options) {
|
|
|
578
460
|
}
|
|
579
461
|
linearTeamIds.set(teamId, project.id);
|
|
580
462
|
}
|
|
581
|
-
const workflowDefinitions = project.workflowDefinitions ?? [{ id: "default", stages: project.workflows }];
|
|
582
|
-
const definitionIds = new Set();
|
|
583
|
-
for (const definition of workflowDefinitions) {
|
|
584
|
-
const normalizedDefinitionId = definition.id.trim().toLowerCase();
|
|
585
|
-
if (definitionIds.has(normalizedDefinitionId)) {
|
|
586
|
-
throw new Error(`Workflow definition "${definition.id}" is configured more than once in project ${project.id}`);
|
|
587
|
-
}
|
|
588
|
-
definitionIds.add(normalizedDefinitionId);
|
|
589
|
-
const workflowIds = new Set();
|
|
590
|
-
const workflowStates = new Set();
|
|
591
|
-
for (const workflow of definition.stages) {
|
|
592
|
-
const normalizedWorkflowId = workflow.id.trim().toLowerCase();
|
|
593
|
-
if (workflowIds.has(normalizedWorkflowId)) {
|
|
594
|
-
throw new Error(`Workflow id "${workflow.id}" is configured more than once in project ${project.id}`);
|
|
595
|
-
}
|
|
596
|
-
workflowIds.add(normalizedWorkflowId);
|
|
597
|
-
const normalizedState = workflow.whenState.trim().toLowerCase();
|
|
598
|
-
if (workflowStates.has(normalizedState)) {
|
|
599
|
-
throw new Error(`Linear state "${workflow.whenState}" is configured for more than one workflow in project ${project.id}`);
|
|
600
|
-
}
|
|
601
|
-
workflowStates.add(normalizedState);
|
|
602
|
-
}
|
|
603
|
-
}
|
|
604
|
-
if (project.workflowSelection?.defaultWorkflowId) {
|
|
605
|
-
const normalizedDefaultWorkflowId = project.workflowSelection.defaultWorkflowId.trim().toLowerCase();
|
|
606
|
-
if (!workflowDefinitions.some((definition) => definition.id.trim().toLowerCase() === normalizedDefaultWorkflowId)) {
|
|
607
|
-
throw new Error(`Default workflow "${project.workflowSelection.defaultWorkflowId}" does not exist in project ${project.id}`);
|
|
608
|
-
}
|
|
609
|
-
}
|
|
610
|
-
for (const rule of project.workflowSelection?.byLabel ?? []) {
|
|
611
|
-
const normalizedWorkflowId = rule.workflowId.trim().toLowerCase();
|
|
612
|
-
if (!workflowDefinitions.some((definition) => definition.id.trim().toLowerCase() === normalizedWorkflowId)) {
|
|
613
|
-
throw new Error(`Workflow selection for label "${rule.label}" points to unknown workflow "${rule.workflowId}" in project ${project.id}`);
|
|
614
|
-
}
|
|
615
|
-
}
|
|
616
463
|
}
|
|
617
464
|
if (config.operatorApi.enabled &&
|
|
618
465
|
config.server.bind !== "127.0.0.1" &&
|
package/dist/db/migrations.js
CHANGED
|
@@ -1,151 +1,67 @@
|
|
|
1
|
-
const
|
|
2
|
-
CREATE TABLE IF NOT EXISTS
|
|
3
|
-
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
4
|
-
webhook_id TEXT NOT NULL UNIQUE,
|
|
5
|
-
received_at TEXT NOT NULL,
|
|
6
|
-
event_type TEXT NOT NULL,
|
|
7
|
-
issue_id TEXT,
|
|
8
|
-
project_id TEXT,
|
|
9
|
-
headers_json TEXT NOT NULL,
|
|
10
|
-
payload_json TEXT NOT NULL,
|
|
11
|
-
signature_valid INTEGER NOT NULL,
|
|
12
|
-
dedupe_status TEXT NOT NULL,
|
|
13
|
-
processing_status TEXT NOT NULL DEFAULT 'pending'
|
|
14
|
-
);
|
|
15
|
-
|
|
16
|
-
CREATE TABLE IF NOT EXISTS event_receipts (
|
|
17
|
-
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
18
|
-
source TEXT NOT NULL,
|
|
19
|
-
external_id TEXT NOT NULL,
|
|
20
|
-
event_type TEXT NOT NULL,
|
|
21
|
-
received_at TEXT NOT NULL,
|
|
22
|
-
acceptance_status TEXT NOT NULL,
|
|
23
|
-
processing_status TEXT NOT NULL DEFAULT 'pending',
|
|
24
|
-
project_id TEXT,
|
|
25
|
-
linear_issue_id TEXT,
|
|
26
|
-
headers_json TEXT,
|
|
27
|
-
payload_json TEXT,
|
|
28
|
-
UNIQUE(source, external_id)
|
|
29
|
-
);
|
|
30
|
-
|
|
31
|
-
CREATE TABLE IF NOT EXISTS issue_control (
|
|
32
|
-
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
33
|
-
project_id TEXT NOT NULL,
|
|
34
|
-
linear_issue_id TEXT NOT NULL,
|
|
35
|
-
selected_workflow_id TEXT,
|
|
36
|
-
desired_stage TEXT,
|
|
37
|
-
desired_receipt_id INTEGER,
|
|
38
|
-
active_run_lease_id INTEGER,
|
|
39
|
-
active_workspace_ownership_id INTEGER,
|
|
40
|
-
service_owned_comment_id TEXT,
|
|
41
|
-
active_agent_session_id TEXT,
|
|
42
|
-
lifecycle_status TEXT NOT NULL,
|
|
43
|
-
updated_at TEXT NOT NULL,
|
|
44
|
-
UNIQUE(project_id, linear_issue_id)
|
|
45
|
-
);
|
|
46
|
-
|
|
47
|
-
CREATE TABLE IF NOT EXISTS issue_projection (
|
|
1
|
+
const schema = `
|
|
2
|
+
CREATE TABLE IF NOT EXISTS issues (
|
|
48
3
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
49
4
|
project_id TEXT NOT NULL,
|
|
50
5
|
linear_issue_id TEXT NOT NULL,
|
|
51
6
|
issue_key TEXT,
|
|
52
7
|
title TEXT,
|
|
53
|
-
|
|
8
|
+
url TEXT,
|
|
54
9
|
current_linear_state TEXT,
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
10
|
+
factory_state TEXT NOT NULL DEFAULT 'delegated',
|
|
11
|
+
pending_run_type TEXT,
|
|
12
|
+
pending_run_context_json TEXT,
|
|
13
|
+
branch_name TEXT,
|
|
14
|
+
worktree_path TEXT,
|
|
15
|
+
thread_id TEXT,
|
|
16
|
+
active_run_id INTEGER,
|
|
17
|
+
status_comment_id TEXT,
|
|
18
|
+
agent_session_id TEXT,
|
|
19
|
+
pr_number INTEGER,
|
|
20
|
+
pr_url TEXT,
|
|
21
|
+
pr_state TEXT,
|
|
22
|
+
pr_review_state TEXT,
|
|
23
|
+
pr_check_status TEXT,
|
|
24
|
+
ci_repair_attempts INTEGER NOT NULL DEFAULT 0,
|
|
25
|
+
queue_repair_attempts INTEGER NOT NULL DEFAULT 0,
|
|
69
26
|
updated_at TEXT NOT NULL,
|
|
70
27
|
UNIQUE(project_id, linear_issue_id)
|
|
71
28
|
);
|
|
72
29
|
|
|
73
|
-
CREATE TABLE IF NOT EXISTS
|
|
30
|
+
CREATE TABLE IF NOT EXISTS runs (
|
|
74
31
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
75
|
-
|
|
32
|
+
issue_id INTEGER NOT NULL REFERENCES issues(id),
|
|
76
33
|
project_id TEXT NOT NULL,
|
|
77
34
|
linear_issue_id TEXT NOT NULL,
|
|
78
|
-
|
|
79
|
-
stage TEXT NOT NULL,
|
|
35
|
+
run_type TEXT NOT NULL DEFAULT 'implementation',
|
|
80
36
|
status TEXT NOT NULL,
|
|
81
|
-
|
|
82
|
-
workflow_file TEXT NOT NULL DEFAULT '',
|
|
83
|
-
prompt_text TEXT NOT NULL DEFAULT '',
|
|
37
|
+
prompt_text TEXT,
|
|
84
38
|
thread_id TEXT,
|
|
85
|
-
parent_thread_id TEXT,
|
|
86
39
|
turn_id TEXT,
|
|
87
|
-
|
|
88
|
-
|
|
40
|
+
parent_thread_id TEXT,
|
|
41
|
+
summary_json TEXT,
|
|
42
|
+
report_json TEXT,
|
|
89
43
|
failure_reason TEXT,
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
FOREIGN KEY(trigger_receipt_id) REFERENCES event_receipts(id) ON DELETE SET NULL
|
|
44
|
+
started_at TEXT NOT NULL,
|
|
45
|
+
ended_at TEXT
|
|
93
46
|
);
|
|
94
47
|
|
|
95
|
-
CREATE TABLE IF NOT EXISTS
|
|
48
|
+
CREATE TABLE IF NOT EXISTS webhook_events (
|
|
96
49
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
parent_thread_id TEXT,
|
|
103
|
-
source TEXT NOT NULL,
|
|
104
|
-
linked_agent_session_id TEXT,
|
|
105
|
-
created_at TEXT NOT NULL,
|
|
106
|
-
updated_at TEXT NOT NULL,
|
|
107
|
-
last_opened_at TEXT,
|
|
108
|
-
FOREIGN KEY(workspace_ownership_id) REFERENCES workspace_ownership(id) ON DELETE CASCADE,
|
|
109
|
-
FOREIGN KEY(run_lease_id) REFERENCES run_leases(id) ON DELETE SET NULL
|
|
110
|
-
);
|
|
111
|
-
|
|
112
|
-
CREATE TABLE IF NOT EXISTS run_reports (
|
|
113
|
-
run_lease_id INTEGER PRIMARY KEY,
|
|
114
|
-
summary_json TEXT,
|
|
115
|
-
report_json TEXT,
|
|
116
|
-
created_at TEXT NOT NULL,
|
|
117
|
-
updated_at TEXT NOT NULL,
|
|
118
|
-
FOREIGN KEY(run_lease_id) REFERENCES run_leases(id) ON DELETE CASCADE
|
|
50
|
+
webhook_id TEXT NOT NULL UNIQUE,
|
|
51
|
+
received_at TEXT NOT NULL,
|
|
52
|
+
project_id TEXT,
|
|
53
|
+
payload_json TEXT,
|
|
54
|
+
processing_status TEXT NOT NULL DEFAULT 'pending'
|
|
119
55
|
);
|
|
120
56
|
|
|
121
57
|
CREATE TABLE IF NOT EXISTS run_thread_events (
|
|
122
58
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
123
|
-
|
|
59
|
+
run_id INTEGER NOT NULL,
|
|
124
60
|
thread_id TEXT NOT NULL,
|
|
125
61
|
turn_id TEXT,
|
|
126
62
|
method TEXT NOT NULL,
|
|
127
63
|
event_json TEXT NOT NULL,
|
|
128
|
-
created_at TEXT NOT NULL
|
|
129
|
-
FOREIGN KEY(run_lease_id) REFERENCES run_leases(id) ON DELETE CASCADE
|
|
130
|
-
);
|
|
131
|
-
|
|
132
|
-
CREATE TABLE IF NOT EXISTS obligations (
|
|
133
|
-
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
134
|
-
project_id TEXT NOT NULL,
|
|
135
|
-
linear_issue_id TEXT NOT NULL,
|
|
136
|
-
kind TEXT NOT NULL,
|
|
137
|
-
status TEXT NOT NULL,
|
|
138
|
-
source TEXT NOT NULL,
|
|
139
|
-
payload_json TEXT NOT NULL,
|
|
140
|
-
run_lease_id INTEGER,
|
|
141
|
-
thread_id TEXT,
|
|
142
|
-
turn_id TEXT,
|
|
143
|
-
dedupe_key TEXT,
|
|
144
|
-
last_error TEXT,
|
|
145
|
-
created_at TEXT NOT NULL,
|
|
146
|
-
updated_at TEXT NOT NULL,
|
|
147
|
-
completed_at TEXT,
|
|
148
|
-
FOREIGN KEY(run_lease_id) REFERENCES run_leases(id) ON DELETE SET NULL
|
|
64
|
+
created_at TEXT NOT NULL
|
|
149
65
|
);
|
|
150
66
|
|
|
151
67
|
CREATE TABLE IF NOT EXISTS linear_installations (
|
|
@@ -195,44 +111,20 @@ CREATE TABLE IF NOT EXISTS operator_feed_events (
|
|
|
195
111
|
issue_key TEXT,
|
|
196
112
|
project_id TEXT,
|
|
197
113
|
stage TEXT,
|
|
198
|
-
status TEXT
|
|
199
|
-
workflow_id TEXT,
|
|
200
|
-
next_stage TEXT
|
|
114
|
+
status TEXT
|
|
201
115
|
);
|
|
202
116
|
|
|
203
|
-
CREATE INDEX IF NOT EXISTS
|
|
204
|
-
CREATE INDEX IF NOT EXISTS
|
|
205
|
-
CREATE INDEX IF NOT EXISTS
|
|
206
|
-
CREATE INDEX IF NOT EXISTS
|
|
207
|
-
CREATE INDEX IF NOT EXISTS
|
|
208
|
-
CREATE INDEX IF NOT EXISTS
|
|
209
|
-
CREATE INDEX IF NOT EXISTS
|
|
210
|
-
CREATE INDEX IF NOT EXISTS idx_run_thread_events_run ON run_thread_events(
|
|
117
|
+
CREATE INDEX IF NOT EXISTS idx_issues_project ON issues(project_id, linear_issue_id);
|
|
118
|
+
CREATE INDEX IF NOT EXISTS idx_issues_key ON issues(issue_key);
|
|
119
|
+
CREATE INDEX IF NOT EXISTS idx_issues_ready ON issues(pending_run_type, active_run_id);
|
|
120
|
+
CREATE INDEX IF NOT EXISTS idx_issues_branch ON issues(branch_name);
|
|
121
|
+
CREATE INDEX IF NOT EXISTS idx_runs_issue ON runs(issue_id);
|
|
122
|
+
CREATE INDEX IF NOT EXISTS idx_runs_active ON runs(status, project_id, linear_issue_id);
|
|
123
|
+
CREATE INDEX IF NOT EXISTS idx_runs_thread ON runs(thread_id);
|
|
124
|
+
CREATE INDEX IF NOT EXISTS idx_run_thread_events_run ON run_thread_events(run_id, id);
|
|
211
125
|
CREATE INDEX IF NOT EXISTS idx_operator_feed_events_issue ON operator_feed_events(issue_key, id);
|
|
212
126
|
CREATE INDEX IF NOT EXISTS idx_operator_feed_events_project ON operator_feed_events(project_id, id);
|
|
213
|
-
CREATE INDEX IF NOT EXISTS idx_obligations_pending ON obligations(status, run_lease_id, kind);
|
|
214
|
-
CREATE UNIQUE INDEX IF NOT EXISTS idx_obligations_dedupe
|
|
215
|
-
ON obligations(run_lease_id, kind, dedupe_key)
|
|
216
|
-
WHERE dedupe_key IS NOT NULL;
|
|
217
127
|
`;
|
|
218
128
|
export function runPatchRelayMigrations(connection) {
|
|
219
|
-
connection.exec(
|
|
220
|
-
try {
|
|
221
|
-
connection.exec("ALTER TABLE issue_control ADD COLUMN selected_workflow_id TEXT");
|
|
222
|
-
}
|
|
223
|
-
catch {
|
|
224
|
-
// Column already exists on upgraded installs.
|
|
225
|
-
}
|
|
226
|
-
try {
|
|
227
|
-
connection.exec("ALTER TABLE operator_feed_events ADD COLUMN workflow_id TEXT");
|
|
228
|
-
}
|
|
229
|
-
catch {
|
|
230
|
-
// Column already exists on upgraded installs.
|
|
231
|
-
}
|
|
232
|
-
try {
|
|
233
|
-
connection.exec("ALTER TABLE operator_feed_events ADD COLUMN next_stage TEXT");
|
|
234
|
-
}
|
|
235
|
-
catch {
|
|
236
|
-
// Column already exists on upgraded installs.
|
|
237
|
-
}
|
|
129
|
+
connection.exec(schema);
|
|
238
130
|
}
|