patchrelay 0.36.8 → 0.36.10
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/build-info.json +3 -3
- package/dist/cli/cluster-health.js +1 -1
- package/dist/cli/data.js +12 -10
- package/dist/cli/formatters/text.js +3 -1
- package/dist/db/issue-session-store.js +15 -23
- package/dist/db/issue-store.js +559 -0
- package/dist/db/run-store.js +10 -12
- package/dist/db.js +37 -625
- package/dist/github-webhook-handler.js +36 -20
- package/dist/idle-reconciliation.js +26 -15
- package/dist/interrupted-run-recovery.js +176 -0
- package/dist/issue-query-service.js +4 -4
- package/dist/issue-session-projector.js +114 -0
- package/dist/linear-session-sync.js +6 -6
- package/dist/queue-health-monitor.js +3 -3
- package/dist/run-completion-policy.js +412 -0
- package/dist/run-finalizer.js +34 -23
- package/dist/run-launcher.js +5 -5
- package/dist/run-orchestrator.js +46 -684
- package/dist/run-recovery-service.js +26 -18
- package/dist/run-wake-planner.js +1 -1
- package/dist/service.js +9 -9
- package/dist/webhook-handler.js +5 -5
- package/dist/webhooks/agent-session-handler.js +7 -7
- package/dist/webhooks/comment-wake-handler.js +1 -1
- package/dist/webhooks/desired-stage-recorder.js +5 -5
- package/dist/webhooks/issue-removal-handler.js +3 -3
- package/dist/worktree-manager.js +69 -0
- package/dist/zombie-recovery.js +13 -0
- package/package.json +1 -1
|
@@ -0,0 +1,559 @@
|
|
|
1
|
+
import { isoNow } from "./shared.js";
|
|
2
|
+
export class IssueStore {
|
|
3
|
+
connection;
|
|
4
|
+
syncIssueSessionFromIssue;
|
|
5
|
+
constructor(connection, syncIssueSessionFromIssue) {
|
|
6
|
+
this.connection = connection;
|
|
7
|
+
this.syncIssueSessionFromIssue = syncIssueSessionFromIssue;
|
|
8
|
+
}
|
|
9
|
+
upsertIssue(params) {
|
|
10
|
+
const now = isoNow();
|
|
11
|
+
const existing = this.getIssue(params.projectId, params.linearIssueId);
|
|
12
|
+
if (existing) {
|
|
13
|
+
const sets = ["updated_at = @now"];
|
|
14
|
+
const values = {
|
|
15
|
+
now,
|
|
16
|
+
projectId: params.projectId,
|
|
17
|
+
linearIssueId: params.linearIssueId,
|
|
18
|
+
};
|
|
19
|
+
if (params.issueKey !== undefined) {
|
|
20
|
+
sets.push("issue_key = COALESCE(@issueKey, issue_key)");
|
|
21
|
+
values.issueKey = params.issueKey;
|
|
22
|
+
}
|
|
23
|
+
if (params.title !== undefined) {
|
|
24
|
+
sets.push("title = COALESCE(@title, title)");
|
|
25
|
+
values.title = params.title;
|
|
26
|
+
}
|
|
27
|
+
if (params.description !== undefined) {
|
|
28
|
+
sets.push("description = COALESCE(@description, description)");
|
|
29
|
+
values.description = params.description;
|
|
30
|
+
}
|
|
31
|
+
if (params.url !== undefined) {
|
|
32
|
+
sets.push("url = COALESCE(@url, url)");
|
|
33
|
+
values.url = params.url;
|
|
34
|
+
}
|
|
35
|
+
if (params.priority !== undefined) {
|
|
36
|
+
sets.push("priority = @priority");
|
|
37
|
+
values.priority = params.priority;
|
|
38
|
+
}
|
|
39
|
+
if (params.estimate !== undefined) {
|
|
40
|
+
sets.push("estimate = @estimate");
|
|
41
|
+
values.estimate = params.estimate;
|
|
42
|
+
}
|
|
43
|
+
if (params.currentLinearState !== undefined) {
|
|
44
|
+
sets.push("current_linear_state = COALESCE(@currentLinearState, current_linear_state)");
|
|
45
|
+
values.currentLinearState = params.currentLinearState;
|
|
46
|
+
}
|
|
47
|
+
if (params.currentLinearStateType !== undefined) {
|
|
48
|
+
sets.push("current_linear_state_type = COALESCE(@currentLinearStateType, current_linear_state_type)");
|
|
49
|
+
values.currentLinearStateType = params.currentLinearStateType;
|
|
50
|
+
}
|
|
51
|
+
if (params.factoryState !== undefined) {
|
|
52
|
+
sets.push("factory_state = @factoryState");
|
|
53
|
+
values.factoryState = params.factoryState;
|
|
54
|
+
}
|
|
55
|
+
if (params.pendingRunType !== undefined) {
|
|
56
|
+
sets.push("pending_run_type = @pendingRunType");
|
|
57
|
+
values.pendingRunType = params.pendingRunType;
|
|
58
|
+
}
|
|
59
|
+
if (params.pendingRunContextJson !== undefined) {
|
|
60
|
+
sets.push("pending_run_context_json = @pendingRunContextJson");
|
|
61
|
+
values.pendingRunContextJson = params.pendingRunContextJson;
|
|
62
|
+
}
|
|
63
|
+
if (params.branchName !== undefined) {
|
|
64
|
+
sets.push("branch_name = COALESCE(@branchName, branch_name)");
|
|
65
|
+
values.branchName = params.branchName;
|
|
66
|
+
}
|
|
67
|
+
if (params.worktreePath !== undefined) {
|
|
68
|
+
sets.push("worktree_path = COALESCE(@worktreePath, worktree_path)");
|
|
69
|
+
values.worktreePath = params.worktreePath;
|
|
70
|
+
}
|
|
71
|
+
if (params.threadId !== undefined) {
|
|
72
|
+
sets.push("thread_id = @threadId");
|
|
73
|
+
values.threadId = params.threadId;
|
|
74
|
+
}
|
|
75
|
+
if (params.activeRunId !== undefined) {
|
|
76
|
+
sets.push("active_run_id = @activeRunId");
|
|
77
|
+
values.activeRunId = params.activeRunId;
|
|
78
|
+
}
|
|
79
|
+
if (params.statusCommentId !== undefined) {
|
|
80
|
+
sets.push("status_comment_id = @statusCommentId");
|
|
81
|
+
values.statusCommentId = params.statusCommentId;
|
|
82
|
+
}
|
|
83
|
+
if (params.agentSessionId !== undefined) {
|
|
84
|
+
sets.push("agent_session_id = @agentSessionId");
|
|
85
|
+
values.agentSessionId = params.agentSessionId;
|
|
86
|
+
}
|
|
87
|
+
if (params.prNumber !== undefined) {
|
|
88
|
+
sets.push("pr_number = @prNumber");
|
|
89
|
+
values.prNumber = params.prNumber;
|
|
90
|
+
}
|
|
91
|
+
if (params.prUrl !== undefined) {
|
|
92
|
+
sets.push("pr_url = @prUrl");
|
|
93
|
+
values.prUrl = params.prUrl;
|
|
94
|
+
}
|
|
95
|
+
if (params.prState !== undefined) {
|
|
96
|
+
sets.push("pr_state = @prState");
|
|
97
|
+
values.prState = params.prState;
|
|
98
|
+
}
|
|
99
|
+
if (params.prHeadSha !== undefined) {
|
|
100
|
+
sets.push("pr_head_sha = @prHeadSha");
|
|
101
|
+
values.prHeadSha = params.prHeadSha;
|
|
102
|
+
}
|
|
103
|
+
if (params.prAuthorLogin !== undefined) {
|
|
104
|
+
sets.push("pr_author_login = @prAuthorLogin");
|
|
105
|
+
values.prAuthorLogin = params.prAuthorLogin;
|
|
106
|
+
}
|
|
107
|
+
if (params.prReviewState !== undefined) {
|
|
108
|
+
sets.push("pr_review_state = @prReviewState");
|
|
109
|
+
values.prReviewState = params.prReviewState;
|
|
110
|
+
}
|
|
111
|
+
if (params.prCheckStatus !== undefined) {
|
|
112
|
+
sets.push("pr_check_status = @prCheckStatus");
|
|
113
|
+
values.prCheckStatus = params.prCheckStatus;
|
|
114
|
+
}
|
|
115
|
+
if (params.lastBlockingReviewHeadSha !== undefined) {
|
|
116
|
+
sets.push("last_blocking_review_head_sha = @lastBlockingReviewHeadSha");
|
|
117
|
+
values.lastBlockingReviewHeadSha = params.lastBlockingReviewHeadSha;
|
|
118
|
+
}
|
|
119
|
+
if (params.lastGitHubFailureSource !== undefined) {
|
|
120
|
+
sets.push("last_github_failure_source = @lastGitHubFailureSource");
|
|
121
|
+
values.lastGitHubFailureSource = params.lastGitHubFailureSource;
|
|
122
|
+
}
|
|
123
|
+
if (params.lastGitHubFailureHeadSha !== undefined) {
|
|
124
|
+
sets.push("last_github_failure_head_sha = @lastGitHubFailureHeadSha");
|
|
125
|
+
values.lastGitHubFailureHeadSha = params.lastGitHubFailureHeadSha;
|
|
126
|
+
}
|
|
127
|
+
if (params.lastGitHubFailureSignature !== undefined) {
|
|
128
|
+
sets.push("last_github_failure_signature = @lastGitHubFailureSignature");
|
|
129
|
+
values.lastGitHubFailureSignature = params.lastGitHubFailureSignature;
|
|
130
|
+
}
|
|
131
|
+
if (params.lastGitHubFailureCheckName !== undefined) {
|
|
132
|
+
sets.push("last_github_failure_check_name = @lastGitHubFailureCheckName");
|
|
133
|
+
values.lastGitHubFailureCheckName = params.lastGitHubFailureCheckName;
|
|
134
|
+
}
|
|
135
|
+
if (params.lastGitHubFailureCheckUrl !== undefined) {
|
|
136
|
+
sets.push("last_github_failure_check_url = @lastGitHubFailureCheckUrl");
|
|
137
|
+
values.lastGitHubFailureCheckUrl = params.lastGitHubFailureCheckUrl;
|
|
138
|
+
}
|
|
139
|
+
if (params.lastGitHubFailureContextJson !== undefined) {
|
|
140
|
+
sets.push("last_github_failure_context_json = @lastGitHubFailureContextJson");
|
|
141
|
+
values.lastGitHubFailureContextJson = params.lastGitHubFailureContextJson;
|
|
142
|
+
}
|
|
143
|
+
if (params.lastGitHubFailureAt !== undefined) {
|
|
144
|
+
sets.push("last_github_failure_at = @lastGitHubFailureAt");
|
|
145
|
+
values.lastGitHubFailureAt = params.lastGitHubFailureAt;
|
|
146
|
+
}
|
|
147
|
+
if (params.lastGitHubCiSnapshotHeadSha !== undefined) {
|
|
148
|
+
sets.push("last_github_ci_snapshot_head_sha = @lastGitHubCiSnapshotHeadSha");
|
|
149
|
+
values.lastGitHubCiSnapshotHeadSha = params.lastGitHubCiSnapshotHeadSha;
|
|
150
|
+
}
|
|
151
|
+
if (params.lastGitHubCiSnapshotGateCheckName !== undefined) {
|
|
152
|
+
sets.push("last_github_ci_snapshot_gate_check_name = @lastGitHubCiSnapshotGateCheckName");
|
|
153
|
+
values.lastGitHubCiSnapshotGateCheckName = params.lastGitHubCiSnapshotGateCheckName;
|
|
154
|
+
}
|
|
155
|
+
if (params.lastGitHubCiSnapshotGateCheckStatus !== undefined) {
|
|
156
|
+
sets.push("last_github_ci_snapshot_gate_check_status = @lastGitHubCiSnapshotGateCheckStatus");
|
|
157
|
+
values.lastGitHubCiSnapshotGateCheckStatus = params.lastGitHubCiSnapshotGateCheckStatus;
|
|
158
|
+
}
|
|
159
|
+
if (params.lastGitHubCiSnapshotJson !== undefined) {
|
|
160
|
+
sets.push("last_github_ci_snapshot_json = @lastGitHubCiSnapshotJson");
|
|
161
|
+
values.lastGitHubCiSnapshotJson = params.lastGitHubCiSnapshotJson;
|
|
162
|
+
}
|
|
163
|
+
if (params.lastGitHubCiSnapshotSettledAt !== undefined) {
|
|
164
|
+
sets.push("last_github_ci_snapshot_settled_at = @lastGitHubCiSnapshotSettledAt");
|
|
165
|
+
values.lastGitHubCiSnapshotSettledAt = params.lastGitHubCiSnapshotSettledAt;
|
|
166
|
+
}
|
|
167
|
+
if (params.lastQueueSignalAt !== undefined) {
|
|
168
|
+
sets.push("last_queue_signal_at = @lastQueueSignalAt");
|
|
169
|
+
values.lastQueueSignalAt = params.lastQueueSignalAt;
|
|
170
|
+
}
|
|
171
|
+
if (params.lastQueueIncidentJson !== undefined) {
|
|
172
|
+
sets.push("last_queue_incident_json = @lastQueueIncidentJson");
|
|
173
|
+
values.lastQueueIncidentJson = params.lastQueueIncidentJson;
|
|
174
|
+
}
|
|
175
|
+
if (params.lastAttemptedFailureHeadSha !== undefined) {
|
|
176
|
+
sets.push("last_attempted_failure_head_sha = @lastAttemptedFailureHeadSha");
|
|
177
|
+
values.lastAttemptedFailureHeadSha = params.lastAttemptedFailureHeadSha;
|
|
178
|
+
}
|
|
179
|
+
if (params.lastAttemptedFailureSignature !== undefined) {
|
|
180
|
+
sets.push("last_attempted_failure_signature = @lastAttemptedFailureSignature");
|
|
181
|
+
values.lastAttemptedFailureSignature = params.lastAttemptedFailureSignature;
|
|
182
|
+
}
|
|
183
|
+
if (params.ciRepairAttempts !== undefined) {
|
|
184
|
+
sets.push("ci_repair_attempts = @ciRepairAttempts");
|
|
185
|
+
values.ciRepairAttempts = params.ciRepairAttempts;
|
|
186
|
+
}
|
|
187
|
+
if (params.queueRepairAttempts !== undefined) {
|
|
188
|
+
sets.push("queue_repair_attempts = @queueRepairAttempts");
|
|
189
|
+
values.queueRepairAttempts = params.queueRepairAttempts;
|
|
190
|
+
}
|
|
191
|
+
if (params.reviewFixAttempts !== undefined) {
|
|
192
|
+
sets.push("review_fix_attempts = @reviewFixAttempts");
|
|
193
|
+
values.reviewFixAttempts = params.reviewFixAttempts;
|
|
194
|
+
}
|
|
195
|
+
if (params.zombieRecoveryAttempts !== undefined) {
|
|
196
|
+
sets.push("zombie_recovery_attempts = @zombieRecoveryAttempts");
|
|
197
|
+
values.zombieRecoveryAttempts = params.zombieRecoveryAttempts;
|
|
198
|
+
}
|
|
199
|
+
if (params.lastZombieRecoveryAt !== undefined) {
|
|
200
|
+
sets.push("last_zombie_recovery_at = @lastZombieRecoveryAt");
|
|
201
|
+
values.lastZombieRecoveryAt = params.lastZombieRecoveryAt;
|
|
202
|
+
}
|
|
203
|
+
this.connection.prepare(`UPDATE issues SET ${sets.join(", ")} WHERE project_id = @projectId AND linear_issue_id = @linearIssueId`).run(values);
|
|
204
|
+
}
|
|
205
|
+
else {
|
|
206
|
+
this.connection.prepare(`
|
|
207
|
+
INSERT INTO issues (
|
|
208
|
+
project_id, linear_issue_id, issue_key, title, description, url,
|
|
209
|
+
priority, estimate,
|
|
210
|
+
current_linear_state, current_linear_state_type, factory_state, pending_run_type, pending_run_context_json,
|
|
211
|
+
branch_name, worktree_path, thread_id, active_run_id, status_comment_id,
|
|
212
|
+
agent_session_id,
|
|
213
|
+
pr_number, pr_url, pr_state, pr_head_sha, pr_author_login, pr_review_state, pr_check_status, last_blocking_review_head_sha,
|
|
214
|
+
last_github_failure_source, last_github_failure_head_sha, last_github_failure_signature, last_github_failure_check_name, last_github_failure_check_url, last_github_failure_context_json, last_github_failure_at,
|
|
215
|
+
last_github_ci_snapshot_head_sha, last_github_ci_snapshot_gate_check_name, last_github_ci_snapshot_gate_check_status, last_github_ci_snapshot_json, last_github_ci_snapshot_settled_at,
|
|
216
|
+
last_queue_signal_at, last_queue_incident_json,
|
|
217
|
+
last_attempted_failure_head_sha, last_attempted_failure_signature,
|
|
218
|
+
ci_repair_attempts, queue_repair_attempts, review_fix_attempts, zombie_recovery_attempts, last_zombie_recovery_at,
|
|
219
|
+
updated_at
|
|
220
|
+
) VALUES (
|
|
221
|
+
@projectId, @linearIssueId, @issueKey, @title, @description, @url,
|
|
222
|
+
@priority, @estimate,
|
|
223
|
+
@currentLinearState, @currentLinearStateType, @factoryState, @pendingRunType, @pendingRunContextJson,
|
|
224
|
+
@branchName, @worktreePath, @threadId, @activeRunId, @statusCommentId,
|
|
225
|
+
@agentSessionId,
|
|
226
|
+
@prNumber, @prUrl, @prState, @prHeadSha, @prAuthorLogin, @prReviewState, @prCheckStatus, @lastBlockingReviewHeadSha,
|
|
227
|
+
@lastGitHubFailureSource, @lastGitHubFailureHeadSha, @lastGitHubFailureSignature, @lastGitHubFailureCheckName, @lastGitHubFailureCheckUrl, @lastGitHubFailureContextJson, @lastGitHubFailureAt,
|
|
228
|
+
@lastGitHubCiSnapshotHeadSha, @lastGitHubCiSnapshotGateCheckName, @lastGitHubCiSnapshotGateCheckStatus, @lastGitHubCiSnapshotJson, @lastGitHubCiSnapshotSettledAt,
|
|
229
|
+
@lastQueueSignalAt, @lastQueueIncidentJson,
|
|
230
|
+
@lastAttemptedFailureHeadSha, @lastAttemptedFailureSignature,
|
|
231
|
+
@ciRepairAttempts, @queueRepairAttempts, @reviewFixAttempts, @zombieRecoveryAttempts, @lastZombieRecoveryAt,
|
|
232
|
+
@now
|
|
233
|
+
)
|
|
234
|
+
`).run({
|
|
235
|
+
projectId: params.projectId,
|
|
236
|
+
linearIssueId: params.linearIssueId,
|
|
237
|
+
issueKey: params.issueKey ?? null,
|
|
238
|
+
title: params.title ?? null,
|
|
239
|
+
description: params.description ?? null,
|
|
240
|
+
url: params.url ?? null,
|
|
241
|
+
priority: params.priority ?? null,
|
|
242
|
+
estimate: params.estimate ?? null,
|
|
243
|
+
currentLinearState: params.currentLinearState ?? null,
|
|
244
|
+
currentLinearStateType: params.currentLinearStateType ?? null,
|
|
245
|
+
factoryState: params.factoryState ?? "delegated",
|
|
246
|
+
pendingRunType: params.pendingRunType ?? null,
|
|
247
|
+
pendingRunContextJson: params.pendingRunContextJson ?? null,
|
|
248
|
+
branchName: params.branchName ?? null,
|
|
249
|
+
worktreePath: params.worktreePath ?? null,
|
|
250
|
+
threadId: params.threadId ?? null,
|
|
251
|
+
activeRunId: params.activeRunId ?? null,
|
|
252
|
+
statusCommentId: params.statusCommentId ?? null,
|
|
253
|
+
agentSessionId: params.agentSessionId ?? null,
|
|
254
|
+
prNumber: params.prNumber ?? null,
|
|
255
|
+
prUrl: params.prUrl ?? null,
|
|
256
|
+
prState: params.prState ?? null,
|
|
257
|
+
prHeadSha: params.prHeadSha ?? null,
|
|
258
|
+
prAuthorLogin: params.prAuthorLogin ?? null,
|
|
259
|
+
prReviewState: params.prReviewState ?? null,
|
|
260
|
+
prCheckStatus: params.prCheckStatus ?? null,
|
|
261
|
+
lastBlockingReviewHeadSha: params.lastBlockingReviewHeadSha ?? null,
|
|
262
|
+
lastGitHubFailureSource: params.lastGitHubFailureSource ?? null,
|
|
263
|
+
lastGitHubFailureHeadSha: params.lastGitHubFailureHeadSha ?? null,
|
|
264
|
+
lastGitHubFailureSignature: params.lastGitHubFailureSignature ?? null,
|
|
265
|
+
lastGitHubFailureCheckName: params.lastGitHubFailureCheckName ?? null,
|
|
266
|
+
lastGitHubFailureCheckUrl: params.lastGitHubFailureCheckUrl ?? null,
|
|
267
|
+
lastGitHubFailureContextJson: params.lastGitHubFailureContextJson ?? null,
|
|
268
|
+
lastGitHubFailureAt: params.lastGitHubFailureAt ?? null,
|
|
269
|
+
lastGitHubCiSnapshotHeadSha: params.lastGitHubCiSnapshotHeadSha ?? null,
|
|
270
|
+
lastGitHubCiSnapshotGateCheckName: params.lastGitHubCiSnapshotGateCheckName ?? null,
|
|
271
|
+
lastGitHubCiSnapshotGateCheckStatus: params.lastGitHubCiSnapshotGateCheckStatus ?? null,
|
|
272
|
+
lastGitHubCiSnapshotJson: params.lastGitHubCiSnapshotJson ?? null,
|
|
273
|
+
lastGitHubCiSnapshotSettledAt: params.lastGitHubCiSnapshotSettledAt ?? null,
|
|
274
|
+
lastQueueSignalAt: params.lastQueueSignalAt ?? null,
|
|
275
|
+
lastQueueIncidentJson: params.lastQueueIncidentJson ?? null,
|
|
276
|
+
lastAttemptedFailureHeadSha: params.lastAttemptedFailureHeadSha ?? null,
|
|
277
|
+
lastAttemptedFailureSignature: params.lastAttemptedFailureSignature ?? null,
|
|
278
|
+
ciRepairAttempts: params.ciRepairAttempts ?? 0,
|
|
279
|
+
queueRepairAttempts: params.queueRepairAttempts ?? 0,
|
|
280
|
+
reviewFixAttempts: params.reviewFixAttempts ?? 0,
|
|
281
|
+
zombieRecoveryAttempts: params.zombieRecoveryAttempts ?? 0,
|
|
282
|
+
lastZombieRecoveryAt: params.lastZombieRecoveryAt ?? null,
|
|
283
|
+
now,
|
|
284
|
+
});
|
|
285
|
+
}
|
|
286
|
+
const updated = this.getIssue(params.projectId, params.linearIssueId);
|
|
287
|
+
this.syncIssueSessionFromIssue(updated);
|
|
288
|
+
return updated;
|
|
289
|
+
}
|
|
290
|
+
getIssue(projectId, linearIssueId) {
|
|
291
|
+
const row = this.connection
|
|
292
|
+
.prepare("SELECT * FROM issues WHERE project_id = ? AND linear_issue_id = ?")
|
|
293
|
+
.get(projectId, linearIssueId);
|
|
294
|
+
return row ? mapIssueRow(row) : undefined;
|
|
295
|
+
}
|
|
296
|
+
getIssueById(id) {
|
|
297
|
+
const row = this.connection.prepare("SELECT * FROM issues WHERE id = ?").get(id);
|
|
298
|
+
return row ? mapIssueRow(row) : undefined;
|
|
299
|
+
}
|
|
300
|
+
getIssueByKey(issueKey) {
|
|
301
|
+
const row = this.connection.prepare("SELECT * FROM issues WHERE issue_key = ?").get(issueKey);
|
|
302
|
+
return row ? mapIssueRow(row) : undefined;
|
|
303
|
+
}
|
|
304
|
+
getIssueByBranch(branchName) {
|
|
305
|
+
const row = this.connection.prepare("SELECT * FROM issues WHERE branch_name = ?").get(branchName);
|
|
306
|
+
return row ? mapIssueRow(row) : undefined;
|
|
307
|
+
}
|
|
308
|
+
getIssueByPrNumber(prNumber) {
|
|
309
|
+
const row = this.connection.prepare("SELECT * FROM issues WHERE pr_number = ?").get(prNumber);
|
|
310
|
+
return row ? mapIssueRow(row) : undefined;
|
|
311
|
+
}
|
|
312
|
+
listIssues() {
|
|
313
|
+
const rows = this.connection
|
|
314
|
+
.prepare("SELECT * FROM issues ORDER BY updated_at DESC")
|
|
315
|
+
.all();
|
|
316
|
+
return rows.map(mapIssueRow);
|
|
317
|
+
}
|
|
318
|
+
listIssuesWithAgentSessions() {
|
|
319
|
+
const rows = this.connection
|
|
320
|
+
.prepare("SELECT * FROM issues WHERE agent_session_id IS NOT NULL ORDER BY updated_at DESC")
|
|
321
|
+
.all();
|
|
322
|
+
return rows.map(mapIssueRow);
|
|
323
|
+
}
|
|
324
|
+
listIssuesByState(projectId, state) {
|
|
325
|
+
const rows = this.connection
|
|
326
|
+
.prepare("SELECT * FROM issues WHERE project_id = ? AND factory_state = ? ORDER BY pr_number ASC")
|
|
327
|
+
.all(projectId, state);
|
|
328
|
+
return rows.map(mapIssueRow);
|
|
329
|
+
}
|
|
330
|
+
listIdleNonTerminalIssues() {
|
|
331
|
+
const rows = this.connection
|
|
332
|
+
.prepare(`SELECT * FROM issues
|
|
333
|
+
WHERE factory_state NOT IN ('done', 'escalated', 'failed', 'awaiting_input')
|
|
334
|
+
AND active_run_id IS NULL
|
|
335
|
+
AND pending_run_type IS NULL
|
|
336
|
+
AND pr_number IS NOT NULL`)
|
|
337
|
+
.all();
|
|
338
|
+
return rows.map(mapIssueRow);
|
|
339
|
+
}
|
|
340
|
+
listBlockedDelegatedIssues() {
|
|
341
|
+
const rows = this.connection
|
|
342
|
+
.prepare(`SELECT DISTINCT i.* FROM issues i
|
|
343
|
+
JOIN issue_dependencies d ON d.project_id = i.project_id AND d.linear_issue_id = i.linear_issue_id
|
|
344
|
+
WHERE i.factory_state = 'delegated'
|
|
345
|
+
AND i.active_run_id IS NULL
|
|
346
|
+
AND i.pending_run_type IS NULL`)
|
|
347
|
+
.all();
|
|
348
|
+
return rows.map(mapIssueRow);
|
|
349
|
+
}
|
|
350
|
+
listAwaitingQueueIssues() {
|
|
351
|
+
const rows = this.connection
|
|
352
|
+
.prepare(`SELECT * FROM issues
|
|
353
|
+
WHERE factory_state = 'awaiting_queue'
|
|
354
|
+
AND active_run_id IS NULL
|
|
355
|
+
AND pending_run_type IS NULL
|
|
356
|
+
AND pr_number IS NOT NULL`)
|
|
357
|
+
.all();
|
|
358
|
+
return rows.map(mapIssueRow);
|
|
359
|
+
}
|
|
360
|
+
setBranchOwner(projectId, linearIssueId, owner) {
|
|
361
|
+
const now = isoNow();
|
|
362
|
+
this.connection.prepare(`
|
|
363
|
+
UPDATE issues
|
|
364
|
+
SET branch_owner = ?, branch_ownership_changed_at = ?, updated_at = ?
|
|
365
|
+
WHERE project_id = ? AND linear_issue_id = ?
|
|
366
|
+
`).run(owner, now, now, projectId, linearIssueId);
|
|
367
|
+
}
|
|
368
|
+
replaceIssueDependencies(params) {
|
|
369
|
+
const now = isoNow();
|
|
370
|
+
this.connection
|
|
371
|
+
.prepare("DELETE FROM issue_dependencies WHERE project_id = ? AND linear_issue_id = ?")
|
|
372
|
+
.run(params.projectId, params.linearIssueId);
|
|
373
|
+
if (params.blockers.length === 0) {
|
|
374
|
+
return;
|
|
375
|
+
}
|
|
376
|
+
const insert = this.connection.prepare(`
|
|
377
|
+
INSERT INTO issue_dependencies (
|
|
378
|
+
project_id,
|
|
379
|
+
linear_issue_id,
|
|
380
|
+
blocker_linear_issue_id,
|
|
381
|
+
blocker_issue_key,
|
|
382
|
+
blocker_title,
|
|
383
|
+
blocker_current_linear_state,
|
|
384
|
+
blocker_current_linear_state_type,
|
|
385
|
+
updated_at
|
|
386
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
|
387
|
+
`);
|
|
388
|
+
for (const blocker of params.blockers) {
|
|
389
|
+
insert.run(params.projectId, params.linearIssueId, blocker.blockerLinearIssueId, blocker.blockerIssueKey ?? null, blocker.blockerTitle ?? null, blocker.blockerCurrentLinearState ?? null, blocker.blockerCurrentLinearStateType ?? null, now);
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
listIssueDependencies(projectId, linearIssueId) {
|
|
393
|
+
const rows = this.connection.prepare(`
|
|
394
|
+
SELECT
|
|
395
|
+
d.project_id,
|
|
396
|
+
d.linear_issue_id,
|
|
397
|
+
d.blocker_linear_issue_id,
|
|
398
|
+
COALESCE(blockers.issue_key, d.blocker_issue_key) AS blocker_issue_key,
|
|
399
|
+
COALESCE(blockers.title, d.blocker_title) AS blocker_title,
|
|
400
|
+
COALESCE(blockers.current_linear_state, d.blocker_current_linear_state) AS blocker_current_linear_state,
|
|
401
|
+
COALESCE(blockers.current_linear_state_type, d.blocker_current_linear_state_type) AS blocker_current_linear_state_type,
|
|
402
|
+
d.updated_at
|
|
403
|
+
FROM issue_dependencies d
|
|
404
|
+
LEFT JOIN issues blockers
|
|
405
|
+
ON blockers.project_id = d.project_id
|
|
406
|
+
AND blockers.linear_issue_id = d.blocker_linear_issue_id
|
|
407
|
+
WHERE d.project_id = ? AND d.linear_issue_id = ?
|
|
408
|
+
ORDER BY COALESCE(blockers.issue_key, d.blocker_issue_key, d.blocker_linear_issue_id) ASC
|
|
409
|
+
`).all(projectId, linearIssueId);
|
|
410
|
+
return rows.map((row) => ({
|
|
411
|
+
projectId: String(row.project_id),
|
|
412
|
+
linearIssueId: String(row.linear_issue_id),
|
|
413
|
+
blockerLinearIssueId: String(row.blocker_linear_issue_id),
|
|
414
|
+
...(row.blocker_issue_key !== null && row.blocker_issue_key !== undefined ? { blockerIssueKey: String(row.blocker_issue_key) } : {}),
|
|
415
|
+
...(row.blocker_title !== null && row.blocker_title !== undefined ? { blockerTitle: String(row.blocker_title) } : {}),
|
|
416
|
+
...(row.blocker_current_linear_state !== null && row.blocker_current_linear_state !== undefined
|
|
417
|
+
? { blockerCurrentLinearState: String(row.blocker_current_linear_state) }
|
|
418
|
+
: {}),
|
|
419
|
+
...(row.blocker_current_linear_state_type !== null && row.blocker_current_linear_state_type !== undefined
|
|
420
|
+
? { blockerCurrentLinearStateType: String(row.blocker_current_linear_state_type) }
|
|
421
|
+
: {}),
|
|
422
|
+
updatedAt: String(row.updated_at),
|
|
423
|
+
}));
|
|
424
|
+
}
|
|
425
|
+
listDependents(projectId, blockerLinearIssueId) {
|
|
426
|
+
const rows = this.connection.prepare(`
|
|
427
|
+
SELECT project_id, linear_issue_id
|
|
428
|
+
FROM issue_dependencies
|
|
429
|
+
WHERE project_id = ? AND blocker_linear_issue_id = ?
|
|
430
|
+
ORDER BY linear_issue_id ASC
|
|
431
|
+
`).all(projectId, blockerLinearIssueId);
|
|
432
|
+
return rows.map((row) => ({
|
|
433
|
+
projectId: String(row.project_id),
|
|
434
|
+
linearIssueId: String(row.linear_issue_id),
|
|
435
|
+
}));
|
|
436
|
+
}
|
|
437
|
+
countUnresolvedBlockers(projectId, linearIssueId) {
|
|
438
|
+
const row = this.connection.prepare(`
|
|
439
|
+
SELECT COUNT(*) AS count
|
|
440
|
+
FROM issue_dependencies d
|
|
441
|
+
LEFT JOIN issues blockers
|
|
442
|
+
ON blockers.project_id = d.project_id
|
|
443
|
+
AND blockers.linear_issue_id = d.blocker_linear_issue_id
|
|
444
|
+
WHERE d.project_id = ? AND d.linear_issue_id = ?
|
|
445
|
+
AND (
|
|
446
|
+
COALESCE(blockers.current_linear_state_type, d.blocker_current_linear_state_type, '') != 'completed'
|
|
447
|
+
AND LOWER(TRIM(COALESCE(blockers.current_linear_state, d.blocker_current_linear_state, ''))) != 'done'
|
|
448
|
+
)
|
|
449
|
+
`).get(projectId, linearIssueId);
|
|
450
|
+
return Number(row?.count ?? 0);
|
|
451
|
+
}
|
|
452
|
+
getLatestGitHubCiSnapshot(projectId, linearIssueId) {
|
|
453
|
+
const issue = this.getIssue(projectId, linearIssueId);
|
|
454
|
+
if (!issue?.lastGitHubCiSnapshotJson)
|
|
455
|
+
return undefined;
|
|
456
|
+
try {
|
|
457
|
+
return JSON.parse(issue.lastGitHubCiSnapshotJson);
|
|
458
|
+
}
|
|
459
|
+
catch {
|
|
460
|
+
return undefined;
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
export function mapIssueRow(row) {
|
|
465
|
+
return {
|
|
466
|
+
id: Number(row.id),
|
|
467
|
+
projectId: String(row.project_id),
|
|
468
|
+
linearIssueId: String(row.linear_issue_id),
|
|
469
|
+
...(row.issue_key !== null ? { issueKey: String(row.issue_key) } : {}),
|
|
470
|
+
...(row.title !== null ? { title: String(row.title) } : {}),
|
|
471
|
+
...(row.description !== null && row.description !== undefined ? { description: String(row.description) } : {}),
|
|
472
|
+
...(row.url !== null ? { url: String(row.url) } : {}),
|
|
473
|
+
...(row.priority !== null && row.priority !== undefined ? { priority: Number(row.priority) } : {}),
|
|
474
|
+
...(row.estimate !== null && row.estimate !== undefined ? { estimate: Number(row.estimate) } : {}),
|
|
475
|
+
...(row.current_linear_state !== null ? { currentLinearState: String(row.current_linear_state) } : {}),
|
|
476
|
+
...(row.current_linear_state_type !== null && row.current_linear_state_type !== undefined
|
|
477
|
+
? { currentLinearStateType: String(row.current_linear_state_type) }
|
|
478
|
+
: {}),
|
|
479
|
+
factoryState: String(row.factory_state ?? "delegated"),
|
|
480
|
+
...(row.pending_run_type !== null && row.pending_run_type !== undefined ? { pendingRunType: String(row.pending_run_type) } : {}),
|
|
481
|
+
...(row.pending_run_context_json !== null && row.pending_run_context_json !== undefined ? { pendingRunContextJson: String(row.pending_run_context_json) } : {}),
|
|
482
|
+
...(row.branch_name !== null ? { branchName: String(row.branch_name) } : {}),
|
|
483
|
+
...(row.branch_owner !== null && row.branch_owner !== undefined && String(row.branch_owner) === "patchrelay"
|
|
484
|
+
? { branchOwner: "patchrelay" }
|
|
485
|
+
: { branchOwner: "patchrelay" }),
|
|
486
|
+
...(row.branch_ownership_changed_at !== null && row.branch_ownership_changed_at !== undefined
|
|
487
|
+
? { branchOwnershipChangedAt: String(row.branch_ownership_changed_at) }
|
|
488
|
+
: {}),
|
|
489
|
+
...(row.worktree_path !== null ? { worktreePath: String(row.worktree_path) } : {}),
|
|
490
|
+
...(row.thread_id !== null ? { threadId: String(row.thread_id) } : {}),
|
|
491
|
+
...(row.active_run_id !== null ? { activeRunId: Number(row.active_run_id) } : {}),
|
|
492
|
+
...(row.status_comment_id !== null && row.status_comment_id !== undefined ? { statusCommentId: String(row.status_comment_id) } : {}),
|
|
493
|
+
...(row.agent_session_id !== null ? { agentSessionId: String(row.agent_session_id) } : {}),
|
|
494
|
+
updatedAt: String(row.updated_at),
|
|
495
|
+
...(row.pr_number !== null && row.pr_number !== undefined ? { prNumber: Number(row.pr_number) } : {}),
|
|
496
|
+
...(row.pr_url !== null && row.pr_url !== undefined ? { prUrl: String(row.pr_url) } : {}),
|
|
497
|
+
...(row.pr_state !== null && row.pr_state !== undefined ? { prState: String(row.pr_state) } : {}),
|
|
498
|
+
...(row.pr_head_sha !== null && row.pr_head_sha !== undefined ? { prHeadSha: String(row.pr_head_sha) } : {}),
|
|
499
|
+
...(row.pr_author_login !== null && row.pr_author_login !== undefined ? { prAuthorLogin: String(row.pr_author_login) } : {}),
|
|
500
|
+
...(row.pr_review_state !== null && row.pr_review_state !== undefined ? { prReviewState: String(row.pr_review_state) } : {}),
|
|
501
|
+
...(row.pr_check_status !== null && row.pr_check_status !== undefined ? { prCheckStatus: String(row.pr_check_status) } : {}),
|
|
502
|
+
...(row.last_blocking_review_head_sha !== null && row.last_blocking_review_head_sha !== undefined
|
|
503
|
+
? { lastBlockingReviewHeadSha: String(row.last_blocking_review_head_sha) }
|
|
504
|
+
: {}),
|
|
505
|
+
...(row.last_github_failure_source !== null && row.last_github_failure_source !== undefined
|
|
506
|
+
? { lastGitHubFailureSource: String(row.last_github_failure_source) }
|
|
507
|
+
: {}),
|
|
508
|
+
...(row.last_github_failure_head_sha !== null && row.last_github_failure_head_sha !== undefined
|
|
509
|
+
? { lastGitHubFailureHeadSha: String(row.last_github_failure_head_sha) }
|
|
510
|
+
: {}),
|
|
511
|
+
...(row.last_github_failure_signature !== null && row.last_github_failure_signature !== undefined
|
|
512
|
+
? { lastGitHubFailureSignature: String(row.last_github_failure_signature) }
|
|
513
|
+
: {}),
|
|
514
|
+
...(row.last_github_failure_check_name !== null && row.last_github_failure_check_name !== undefined
|
|
515
|
+
? { lastGitHubFailureCheckName: String(row.last_github_failure_check_name) }
|
|
516
|
+
: {}),
|
|
517
|
+
...(row.last_github_failure_check_url !== null && row.last_github_failure_check_url !== undefined
|
|
518
|
+
? { lastGitHubFailureCheckUrl: String(row.last_github_failure_check_url) }
|
|
519
|
+
: {}),
|
|
520
|
+
...(row.last_github_failure_context_json !== null && row.last_github_failure_context_json !== undefined
|
|
521
|
+
? { lastGitHubFailureContextJson: String(row.last_github_failure_context_json) }
|
|
522
|
+
: {}),
|
|
523
|
+
...(row.last_github_failure_at !== null && row.last_github_failure_at !== undefined
|
|
524
|
+
? { lastGitHubFailureAt: String(row.last_github_failure_at) }
|
|
525
|
+
: {}),
|
|
526
|
+
...(row.last_github_ci_snapshot_head_sha !== null && row.last_github_ci_snapshot_head_sha !== undefined
|
|
527
|
+
? { lastGitHubCiSnapshotHeadSha: String(row.last_github_ci_snapshot_head_sha) }
|
|
528
|
+
: {}),
|
|
529
|
+
...(row.last_github_ci_snapshot_gate_check_name !== null && row.last_github_ci_snapshot_gate_check_name !== undefined
|
|
530
|
+
? { lastGitHubCiSnapshotGateCheckName: String(row.last_github_ci_snapshot_gate_check_name) }
|
|
531
|
+
: {}),
|
|
532
|
+
...(row.last_github_ci_snapshot_gate_check_status !== null && row.last_github_ci_snapshot_gate_check_status !== undefined
|
|
533
|
+
? { lastGitHubCiSnapshotGateCheckStatus: String(row.last_github_ci_snapshot_gate_check_status) }
|
|
534
|
+
: {}),
|
|
535
|
+
...(row.last_github_ci_snapshot_json !== null && row.last_github_ci_snapshot_json !== undefined
|
|
536
|
+
? { lastGitHubCiSnapshotJson: String(row.last_github_ci_snapshot_json) }
|
|
537
|
+
: {}),
|
|
538
|
+
...(row.last_github_ci_snapshot_settled_at !== null && row.last_github_ci_snapshot_settled_at !== undefined
|
|
539
|
+
? { lastGitHubCiSnapshotSettledAt: String(row.last_github_ci_snapshot_settled_at) }
|
|
540
|
+
: {}),
|
|
541
|
+
...(row.last_queue_signal_at !== null && row.last_queue_signal_at !== undefined
|
|
542
|
+
? { lastQueueSignalAt: String(row.last_queue_signal_at) }
|
|
543
|
+
: {}),
|
|
544
|
+
...(row.last_queue_incident_json !== null && row.last_queue_incident_json !== undefined
|
|
545
|
+
? { lastQueueIncidentJson: String(row.last_queue_incident_json) }
|
|
546
|
+
: {}),
|
|
547
|
+
...(row.last_attempted_failure_head_sha !== null && row.last_attempted_failure_head_sha !== undefined
|
|
548
|
+
? { lastAttemptedFailureHeadSha: String(row.last_attempted_failure_head_sha) }
|
|
549
|
+
: {}),
|
|
550
|
+
...(row.last_attempted_failure_signature !== null && row.last_attempted_failure_signature !== undefined
|
|
551
|
+
? { lastAttemptedFailureSignature: String(row.last_attempted_failure_signature) }
|
|
552
|
+
: {}),
|
|
553
|
+
ciRepairAttempts: Number(row.ci_repair_attempts ?? 0),
|
|
554
|
+
queueRepairAttempts: Number(row.queue_repair_attempts ?? 0),
|
|
555
|
+
reviewFixAttempts: Number(row.review_fix_attempts ?? 0),
|
|
556
|
+
zombieRecoveryAttempts: Number(row.zombie_recovery_attempts ?? 0),
|
|
557
|
+
...(row.last_zombie_recovery_at !== null && row.last_zombie_recovery_at !== undefined ? { lastZombieRecoveryAt: String(row.last_zombie_recovery_at) } : {}),
|
|
558
|
+
};
|
|
559
|
+
}
|
package/dist/db/run-store.js
CHANGED
|
@@ -3,14 +3,12 @@ import { isoNow } from "./shared.js";
|
|
|
3
3
|
export class RunStore {
|
|
4
4
|
connection;
|
|
5
5
|
mapRunRow;
|
|
6
|
-
|
|
7
|
-
getIssue;
|
|
6
|
+
issues;
|
|
8
7
|
syncIssueSessionFromIssue;
|
|
9
|
-
constructor(connection, mapRunRow,
|
|
8
|
+
constructor(connection, mapRunRow, issues, syncIssueSessionFromIssue) {
|
|
10
9
|
this.connection = connection;
|
|
11
10
|
this.mapRunRow = mapRunRow;
|
|
12
|
-
this.
|
|
13
|
-
this.getIssue = getIssue;
|
|
11
|
+
this.issues = issues;
|
|
14
12
|
this.syncIssueSessionFromIssue = syncIssueSessionFromIssue;
|
|
15
13
|
}
|
|
16
14
|
createRun(params) {
|
|
@@ -19,8 +17,8 @@ export class RunStore {
|
|
|
19
17
|
INSERT INTO runs (issue_id, project_id, linear_issue_id, run_type, status, source_head_sha, prompt_text, started_at)
|
|
20
18
|
VALUES (?, ?, ?, ?, 'queued', ?, ?, ?)
|
|
21
19
|
`).run(params.issueId, params.projectId, params.linearIssueId, params.runType, params.sourceHeadSha ?? null, params.promptText ?? null, now);
|
|
22
|
-
const run = this.
|
|
23
|
-
const issue = this.getIssue(params.projectId, params.linearIssueId);
|
|
20
|
+
const run = this.getRunById(Number(result.lastInsertRowid));
|
|
21
|
+
const issue = this.issues.getIssue(params.projectId, params.linearIssueId);
|
|
24
22
|
if (issue) {
|
|
25
23
|
this.syncIssueSessionFromIssue(issue, { lastRunType: run.runType });
|
|
26
24
|
}
|
|
@@ -69,10 +67,10 @@ export class RunStore {
|
|
|
69
67
|
AND ended_at IS NULL
|
|
70
68
|
AND status IN ('queued', 'running')
|
|
71
69
|
`).run(params.threadId, params.parentThreadId ?? null, params.turnId ?? null, runId);
|
|
72
|
-
const run = this.
|
|
70
|
+
const run = this.getRunById(runId);
|
|
73
71
|
if (!run)
|
|
74
72
|
return;
|
|
75
|
-
const issue = this.getIssue(run.projectId, run.linearIssueId);
|
|
73
|
+
const issue = this.issues.getIssue(run.projectId, run.linearIssueId);
|
|
76
74
|
if (issue) {
|
|
77
75
|
this.syncIssueSessionFromIssue(issue);
|
|
78
76
|
}
|
|
@@ -93,13 +91,13 @@ export class RunStore {
|
|
|
93
91
|
ended_at = ?
|
|
94
92
|
WHERE id = ?
|
|
95
93
|
`).run(params.status, params.threadId ?? null, params.turnId ?? null, params.failureReason ?? null, params.summaryJson ?? null, params.reportJson ?? null, now, runId);
|
|
96
|
-
const run = this.
|
|
94
|
+
const run = this.getRunById(runId);
|
|
97
95
|
if (!run)
|
|
98
96
|
return;
|
|
99
|
-
const issue = this.getIssue(run.projectId, run.linearIssueId);
|
|
97
|
+
const issue = this.issues.getIssue(run.projectId, run.linearIssueId);
|
|
100
98
|
if (issue) {
|
|
101
99
|
this.syncIssueSessionFromIssue(issue, {
|
|
102
|
-
summaryText: extractLatestAssistantSummary(this.
|
|
100
|
+
summaryText: extractLatestAssistantSummary(this.getRunById(runId) ?? run),
|
|
103
101
|
lastRunType: run.runType,
|
|
104
102
|
});
|
|
105
103
|
}
|