patchrelay 0.30.0 → 0.30.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/build-info.json +3 -3
- package/dist/db/migrations.js +4 -0
- package/dist/db.js +31 -12
- package/dist/linear-client.js +2 -0
- package/dist/service.js +8 -2
- package/dist/webhook-handler.js +20 -14
- package/dist/webhooks.js +1 -0
- package/package.json +1 -1
package/dist/build-info.json
CHANGED
package/dist/db/migrations.js
CHANGED
|
@@ -7,6 +7,7 @@ CREATE TABLE IF NOT EXISTS issues (
|
|
|
7
7
|
title TEXT,
|
|
8
8
|
url TEXT,
|
|
9
9
|
current_linear_state TEXT,
|
|
10
|
+
current_linear_state_type TEXT,
|
|
10
11
|
factory_state TEXT NOT NULL DEFAULT 'delegated',
|
|
11
12
|
pending_run_type TEXT,
|
|
12
13
|
pending_run_context_json TEXT,
|
|
@@ -152,6 +153,7 @@ CREATE TABLE IF NOT EXISTS issue_dependencies (
|
|
|
152
153
|
blocker_issue_key TEXT,
|
|
153
154
|
blocker_title TEXT,
|
|
154
155
|
blocker_current_linear_state TEXT,
|
|
156
|
+
blocker_current_linear_state_type TEXT,
|
|
155
157
|
updated_at TEXT NOT NULL,
|
|
156
158
|
PRIMARY KEY (project_id, linear_issue_id, blocker_linear_issue_id)
|
|
157
159
|
);
|
|
@@ -188,6 +190,8 @@ export function runPatchRelayMigrations(connection) {
|
|
|
188
190
|
addColumnIfMissing(connection, "issues", "description", "TEXT");
|
|
189
191
|
addColumnIfMissing(connection, "issues", "priority", "INTEGER");
|
|
190
192
|
addColumnIfMissing(connection, "issues", "estimate", "REAL");
|
|
193
|
+
addColumnIfMissing(connection, "issues", "current_linear_state_type", "TEXT");
|
|
194
|
+
addColumnIfMissing(connection, "issue_dependencies", "blocker_current_linear_state_type", "TEXT");
|
|
191
195
|
// Zombie/stale recovery backoff
|
|
192
196
|
addColumnIfMissing(connection, "issues", "zombie_recovery_attempts", "INTEGER NOT NULL DEFAULT 0");
|
|
193
197
|
addColumnIfMissing(connection, "issues", "last_zombie_recovery_at", "TEXT");
|
package/dist/db.js
CHANGED
|
@@ -104,6 +104,10 @@ export class PatchRelayDatabase {
|
|
|
104
104
|
sets.push("current_linear_state = COALESCE(@currentLinearState, current_linear_state)");
|
|
105
105
|
values.currentLinearState = params.currentLinearState;
|
|
106
106
|
}
|
|
107
|
+
if (params.currentLinearStateType !== undefined) {
|
|
108
|
+
sets.push("current_linear_state_type = COALESCE(@currentLinearStateType, current_linear_state_type)");
|
|
109
|
+
values.currentLinearStateType = params.currentLinearStateType;
|
|
110
|
+
}
|
|
107
111
|
if (params.factoryState !== undefined) {
|
|
108
112
|
sets.push("factory_state = @factoryState");
|
|
109
113
|
values.factoryState = params.factoryState;
|
|
@@ -207,7 +211,7 @@ export class PatchRelayDatabase {
|
|
|
207
211
|
INSERT INTO issues (
|
|
208
212
|
project_id, linear_issue_id, issue_key, title, description, url,
|
|
209
213
|
priority, estimate,
|
|
210
|
-
current_linear_state, factory_state, pending_run_type, pending_run_context_json,
|
|
214
|
+
current_linear_state, current_linear_state_type, factory_state, pending_run_type, pending_run_context_json,
|
|
211
215
|
branch_name, worktree_path, thread_id, active_run_id,
|
|
212
216
|
agent_session_id,
|
|
213
217
|
pr_number, pr_url, pr_state, pr_review_state, pr_check_status,
|
|
@@ -216,7 +220,7 @@ export class PatchRelayDatabase {
|
|
|
216
220
|
) VALUES (
|
|
217
221
|
@projectId, @linearIssueId, @issueKey, @title, @description, @url,
|
|
218
222
|
@priority, @estimate,
|
|
219
|
-
@currentLinearState, @factoryState, @pendingRunType, @pendingRunContextJson,
|
|
223
|
+
@currentLinearState, @currentLinearStateType, @factoryState, @pendingRunType, @pendingRunContextJson,
|
|
220
224
|
@branchName, @worktreePath, @threadId, @activeRunId,
|
|
221
225
|
@agentSessionId,
|
|
222
226
|
@prNumber, @prUrl, @prState, @prReviewState, @prCheckStatus,
|
|
@@ -233,6 +237,7 @@ export class PatchRelayDatabase {
|
|
|
233
237
|
priority: params.priority ?? null,
|
|
234
238
|
estimate: params.estimate ?? null,
|
|
235
239
|
currentLinearState: params.currentLinearState ?? null,
|
|
240
|
+
currentLinearStateType: params.currentLinearStateType ?? null,
|
|
236
241
|
factoryState: params.factoryState ?? "delegated",
|
|
237
242
|
pendingRunType: params.pendingRunType ?? null,
|
|
238
243
|
pendingRunContextJson: params.pendingRunContextJson ?? null,
|
|
@@ -295,11 +300,12 @@ export class PatchRelayDatabase {
|
|
|
295
300
|
blocker_issue_key,
|
|
296
301
|
blocker_title,
|
|
297
302
|
blocker_current_linear_state,
|
|
303
|
+
blocker_current_linear_state_type,
|
|
298
304
|
updated_at
|
|
299
|
-
) VALUES (?, ?, ?, ?, ?, ?, ?)
|
|
305
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
|
300
306
|
`);
|
|
301
307
|
for (const blocker of params.blockers) {
|
|
302
|
-
insert.run(params.projectId, params.linearIssueId, blocker.blockerLinearIssueId, blocker.blockerIssueKey ?? null, blocker.blockerTitle ?? null, blocker.blockerCurrentLinearState ?? null, now);
|
|
308
|
+
insert.run(params.projectId, params.linearIssueId, blocker.blockerLinearIssueId, blocker.blockerIssueKey ?? null, blocker.blockerTitle ?? null, blocker.blockerCurrentLinearState ?? null, blocker.blockerCurrentLinearStateType ?? null, now);
|
|
303
309
|
}
|
|
304
310
|
}
|
|
305
311
|
listIssueDependencies(projectId, linearIssueId) {
|
|
@@ -311,6 +317,7 @@ export class PatchRelayDatabase {
|
|
|
311
317
|
COALESCE(blockers.issue_key, d.blocker_issue_key) AS blocker_issue_key,
|
|
312
318
|
COALESCE(blockers.title, d.blocker_title) AS blocker_title,
|
|
313
319
|
COALESCE(blockers.current_linear_state, d.blocker_current_linear_state) AS blocker_current_linear_state,
|
|
320
|
+
COALESCE(blockers.current_linear_state_type, d.blocker_current_linear_state_type) AS blocker_current_linear_state_type,
|
|
314
321
|
d.updated_at
|
|
315
322
|
FROM issue_dependencies d
|
|
316
323
|
LEFT JOIN issues blockers
|
|
@@ -328,6 +335,9 @@ export class PatchRelayDatabase {
|
|
|
328
335
|
...(row.blocker_current_linear_state !== null && row.blocker_current_linear_state !== undefined
|
|
329
336
|
? { blockerCurrentLinearState: String(row.blocker_current_linear_state) }
|
|
330
337
|
: {}),
|
|
338
|
+
...(row.blocker_current_linear_state_type !== null && row.blocker_current_linear_state_type !== undefined
|
|
339
|
+
? { blockerCurrentLinearStateType: String(row.blocker_current_linear_state_type) }
|
|
340
|
+
: {}),
|
|
331
341
|
updatedAt: String(row.updated_at),
|
|
332
342
|
}));
|
|
333
343
|
}
|
|
@@ -351,7 +361,10 @@ export class PatchRelayDatabase {
|
|
|
351
361
|
ON blockers.project_id = d.project_id
|
|
352
362
|
AND blockers.linear_issue_id = d.blocker_linear_issue_id
|
|
353
363
|
WHERE d.project_id = ? AND d.linear_issue_id = ?
|
|
354
|
-
AND
|
|
364
|
+
AND (
|
|
365
|
+
COALESCE(blockers.current_linear_state_type, d.blocker_current_linear_state_type, '') != 'completed'
|
|
366
|
+
AND LOWER(TRIM(COALESCE(blockers.current_linear_state, d.blocker_current_linear_state, ''))) != 'done'
|
|
367
|
+
)
|
|
355
368
|
`).get(projectId, linearIssueId);
|
|
356
369
|
return Number(row?.count ?? 0);
|
|
357
370
|
}
|
|
@@ -370,7 +383,10 @@ export class PatchRelayDatabase {
|
|
|
370
383
|
AND blockers.linear_issue_id = d.blocker_linear_issue_id
|
|
371
384
|
WHERE d.project_id = i.project_id
|
|
372
385
|
AND d.linear_issue_id = i.linear_issue_id
|
|
373
|
-
AND
|
|
386
|
+
AND (
|
|
387
|
+
COALESCE(blockers.current_linear_state_type, d.blocker_current_linear_state_type, '') != 'completed'
|
|
388
|
+
AND LOWER(TRIM(COALESCE(blockers.current_linear_state, d.blocker_current_linear_state, ''))) != 'done'
|
|
389
|
+
)
|
|
374
390
|
)
|
|
375
391
|
`)
|
|
376
392
|
.all();
|
|
@@ -491,6 +507,7 @@ export class PatchRelayDatabase {
|
|
|
491
507
|
// ─── View builders ──────────────────────────────────────────────
|
|
492
508
|
issueToTrackedIssue(issue) {
|
|
493
509
|
const blockedBy = this.listIssueDependencies(issue.projectId, issue.linearIssueId);
|
|
510
|
+
const unresolvedBlockedBy = blockedBy.filter((entry) => !isResolvedLinearState(entry.blockerCurrentLinearStateType, entry.blockerCurrentLinearState));
|
|
494
511
|
return {
|
|
495
512
|
id: issue.id,
|
|
496
513
|
projectId: issue.projectId,
|
|
@@ -500,11 +517,10 @@ export class PatchRelayDatabase {
|
|
|
500
517
|
...(issue.url ? { issueUrl: issue.url } : {}),
|
|
501
518
|
...(issue.currentLinearState ? { currentLinearState: issue.currentLinearState } : {}),
|
|
502
519
|
factoryState: issue.factoryState,
|
|
503
|
-
blockedByCount:
|
|
504
|
-
blockedByKeys:
|
|
505
|
-
.filter((entry) => !isDoneState(entry.blockerCurrentLinearState))
|
|
520
|
+
blockedByCount: unresolvedBlockedBy.length,
|
|
521
|
+
blockedByKeys: unresolvedBlockedBy
|
|
506
522
|
.map((entry) => entry.blockerIssueKey ?? entry.blockerLinearIssueId),
|
|
507
|
-
readyForExecution: issue.pendingRunType !== undefined && issue.activeRunId === undefined,
|
|
523
|
+
readyForExecution: issue.pendingRunType !== undefined && issue.activeRunId === undefined && unresolvedBlockedBy.length === 0,
|
|
508
524
|
...(issue.activeRunId !== undefined ? { activeRunId: issue.activeRunId } : {}),
|
|
509
525
|
...(issue.agentSessionId ? { activeAgentSessionId: issue.agentSessionId } : {}),
|
|
510
526
|
updatedAt: issue.updatedAt,
|
|
@@ -544,6 +560,9 @@ function mapIssueRow(row) {
|
|
|
544
560
|
...(row.priority !== null && row.priority !== undefined ? { priority: Number(row.priority) } : {}),
|
|
545
561
|
...(row.estimate !== null && row.estimate !== undefined ? { estimate: Number(row.estimate) } : {}),
|
|
546
562
|
...(row.current_linear_state !== null ? { currentLinearState: String(row.current_linear_state) } : {}),
|
|
563
|
+
...(row.current_linear_state_type !== null && row.current_linear_state_type !== undefined
|
|
564
|
+
? { currentLinearStateType: String(row.current_linear_state_type) }
|
|
565
|
+
: {}),
|
|
547
566
|
factoryState: String(row.factory_state ?? "delegated"),
|
|
548
567
|
...(row.pending_run_type !== null && row.pending_run_type !== undefined ? { pendingRunType: String(row.pending_run_type) } : {}),
|
|
549
568
|
...(row.pending_run_context_json !== null && row.pending_run_context_json !== undefined ? { pendingRunContextJson: String(row.pending_run_context_json) } : {}),
|
|
@@ -602,6 +621,6 @@ function mapRunRow(row) {
|
|
|
602
621
|
...(row.ended_at !== null ? { endedAt: String(row.ended_at) } : {}),
|
|
603
622
|
};
|
|
604
623
|
}
|
|
605
|
-
function
|
|
606
|
-
return stateName?.trim().toLowerCase() === "done";
|
|
624
|
+
function isResolvedLinearState(stateType, stateName) {
|
|
625
|
+
return stateType === "completed" || stateName?.trim().toLowerCase() === "done";
|
|
607
626
|
}
|
package/dist/linear-client.js
CHANGED
|
@@ -15,6 +15,7 @@ const LINEAR_ISSUE_SELECTION = `
|
|
|
15
15
|
state {
|
|
16
16
|
id
|
|
17
17
|
name
|
|
18
|
+
type
|
|
18
19
|
}
|
|
19
20
|
labels {
|
|
20
21
|
nodes {
|
|
@@ -317,6 +318,7 @@ export class LinearGraphqlClient {
|
|
|
317
318
|
...(issue.estimate != null ? { estimate: issue.estimate } : {}),
|
|
318
319
|
...(issue.state?.id ? { stateId: issue.state.id } : {}),
|
|
319
320
|
...(issue.state?.name ? { stateName: issue.state.name } : {}),
|
|
321
|
+
...(issue.state?.type ? { stateType: issue.state.type } : {}),
|
|
320
322
|
...(issue.team?.id ? { teamId: issue.team.id } : {}),
|
|
321
323
|
...(issue.team?.key ? { teamKey: issue.team.key } : {}),
|
|
322
324
|
...(issue.delegate?.id ? { delegateId: issue.delegate.id } : {}),
|
package/dist/service.js
CHANGED
|
@@ -229,7 +229,10 @@ export class PatchRelayService {
|
|
|
229
229
|
AND blockers.linear_issue_id = d.blocker_linear_issue_id
|
|
230
230
|
WHERE d.project_id = i.project_id
|
|
231
231
|
AND d.linear_issue_id = i.linear_issue_id
|
|
232
|
-
AND
|
|
232
|
+
AND (
|
|
233
|
+
COALESCE(blockers.current_linear_state_type, d.blocker_current_linear_state_type, '') != 'completed'
|
|
234
|
+
AND LOWER(TRIM(COALESCE(blockers.current_linear_state, d.blocker_current_linear_state, ''))) != 'done'
|
|
235
|
+
)
|
|
233
236
|
) AS blocked_by_count,
|
|
234
237
|
(
|
|
235
238
|
SELECT json_group_array(COALESCE(blockers.issue_key, d.blocker_issue_key, d.blocker_linear_issue_id))
|
|
@@ -239,7 +242,10 @@ export class PatchRelayService {
|
|
|
239
242
|
AND blockers.linear_issue_id = d.blocker_linear_issue_id
|
|
240
243
|
WHERE d.project_id = i.project_id
|
|
241
244
|
AND d.linear_issue_id = i.linear_issue_id
|
|
242
|
-
AND
|
|
245
|
+
AND (
|
|
246
|
+
COALESCE(blockers.current_linear_state_type, d.blocker_current_linear_state_type, '') != 'completed'
|
|
247
|
+
AND LOWER(TRIM(COALESCE(blockers.current_linear_state, d.blocker_current_linear_state, ''))) != 'done'
|
|
248
|
+
)
|
|
243
249
|
) AS blocked_by_keys_json
|
|
244
250
|
FROM issues i
|
|
245
251
|
LEFT JOIN runs active_run ON active_run.id = i.active_run_id
|
package/dist/webhook-handler.js
CHANGED
|
@@ -180,6 +180,7 @@ export class WebhookHandler {
|
|
|
180
180
|
...(hydratedIssue.priority != null ? { priority: hydratedIssue.priority } : {}),
|
|
181
181
|
...(hydratedIssue.estimate != null ? { estimate: hydratedIssue.estimate } : {}),
|
|
182
182
|
...(hydratedIssue.stateName ? { currentLinearState: hydratedIssue.stateName } : {}),
|
|
183
|
+
...(hydratedIssue.stateType ? { currentLinearStateType: hydratedIssue.stateType } : {}),
|
|
183
184
|
...(pendingRunType ? { pendingRunType, factoryState: "delegated" } : {}),
|
|
184
185
|
...(clearPendingImplementation ? { pendingRunType: null } : {}),
|
|
185
186
|
...((pendingRunType || existingIssue?.pendingRunType === "implementation") && pendingRunContextJson
|
|
@@ -203,27 +204,30 @@ export class WebhookHandler {
|
|
|
203
204
|
}
|
|
204
205
|
async syncIssueDependencies(projectId, issue) {
|
|
205
206
|
let source = issue;
|
|
206
|
-
if (source.
|
|
207
|
+
if (!source.relationsKnown) {
|
|
207
208
|
const linear = await this.linearProvider.forProject(projectId);
|
|
208
209
|
if (linear) {
|
|
209
210
|
try {
|
|
210
211
|
source = mergeIssueMetadata(source, await linear.getIssue(issue.id));
|
|
211
212
|
}
|
|
212
213
|
catch {
|
|
213
|
-
//
|
|
214
|
+
// Preserve existing dependency rows when webhook relation data is incomplete.
|
|
214
215
|
}
|
|
215
216
|
}
|
|
216
217
|
}
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
218
|
+
if (source.relationsKnown) {
|
|
219
|
+
this.db.replaceIssueDependencies({
|
|
220
|
+
projectId,
|
|
221
|
+
linearIssueId: source.id,
|
|
222
|
+
blockers: source.blockedBy.map((blocker) => ({
|
|
223
|
+
blockerLinearIssueId: blocker.id,
|
|
224
|
+
...(blocker.identifier ? { blockerIssueKey: blocker.identifier } : {}),
|
|
225
|
+
...(blocker.title ? { blockerTitle: blocker.title } : {}),
|
|
226
|
+
...(blocker.stateName ? { blockerCurrentLinearState: blocker.stateName } : {}),
|
|
227
|
+
...(blocker.stateType ? { blockerCurrentLinearStateType: blocker.stateType } : {}),
|
|
228
|
+
})),
|
|
229
|
+
});
|
|
230
|
+
}
|
|
227
231
|
return source;
|
|
228
232
|
}
|
|
229
233
|
reconcileDependentReadiness(projectId, blockerLinearIssueId) {
|
|
@@ -559,10 +563,12 @@ function mergeIssueMetadata(issue, liveIssue) {
|
|
|
559
563
|
...(issue.teamKey ? {} : liveIssue.teamKey ? { teamKey: liveIssue.teamKey } : {}),
|
|
560
564
|
...(issue.stateId ? {} : liveIssue.stateId ? { stateId: liveIssue.stateId } : {}),
|
|
561
565
|
...(issue.stateName ? {} : liveIssue.stateName ? { stateName: liveIssue.stateName } : {}),
|
|
566
|
+
...(issue.stateType ? {} : liveIssue.stateType ? { stateType: liveIssue.stateType } : {}),
|
|
562
567
|
...(issue.delegateId ? {} : liveIssue.delegateId ? { delegateId: liveIssue.delegateId } : {}),
|
|
563
568
|
...(issue.delegateName ? {} : liveIssue.delegateName ? { delegateName: liveIssue.delegateName } : {}),
|
|
569
|
+
relationsKnown: issue.relationsKnown || liveIssue.blockedBy !== undefined || liveIssue.blocks !== undefined,
|
|
564
570
|
labelNames: issue.labelNames.length > 0 ? issue.labelNames : (liveIssue.labels ?? []).map((l) => l.name),
|
|
565
|
-
blockedBy: issue.
|
|
566
|
-
blocks: issue.
|
|
571
|
+
blockedBy: issue.relationsKnown ? issue.blockedBy : (liveIssue.blockedBy ?? issue.blockedBy),
|
|
572
|
+
blocks: issue.relationsKnown ? issue.blocks : (liveIssue.blocks ?? issue.blocks),
|
|
567
573
|
};
|
|
568
574
|
}
|
package/dist/webhooks.js
CHANGED
|
@@ -227,6 +227,7 @@ function extractIssueMetadata(payload) {
|
|
|
227
227
|
...(delegateName ? { delegateName } : {}),
|
|
228
228
|
...(priority != null ? { priority } : {}),
|
|
229
229
|
...(estimate != null ? { estimate } : {}),
|
|
230
|
+
relationsKnown: false,
|
|
230
231
|
labelNames: extractLabelNames(issueRecord),
|
|
231
232
|
blockedBy: [],
|
|
232
233
|
blocks: [],
|