patchrelay 0.49.0 → 0.49.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/cli/data.js +40 -15
- package/dist/effective-active-run.js +15 -0
- package/dist/run-reconciler.js +14 -0
- package/dist/tracked-issue-list-query.js +37 -12
- package/dist/tracked-issue-projector.js +16 -8
- package/dist/tracked-issue-query.js +5 -1
- package/package.json +1 -1
package/dist/build-info.json
CHANGED
package/dist/cli/data.js
CHANGED
|
@@ -9,6 +9,8 @@ import { buildManualRetryAttemptReset, resolveRetryTarget } from "../manual-issu
|
|
|
9
9
|
import { WorktreeManager } from "../worktree-manager.js";
|
|
10
10
|
import { parseDelegationObservedPayload, parseRunReleasedAuthorityPayload } from "../delegation-audit.js";
|
|
11
11
|
import { CliOperatorApiClient } from "./operator-client.js";
|
|
12
|
+
import { resolveEffectiveActiveRun } from "../effective-active-run.js";
|
|
13
|
+
import { derivePatchRelayWaitingReason } from "../waiting-reason.js";
|
|
12
14
|
function safeJsonParse(value) {
|
|
13
15
|
if (!value)
|
|
14
16
|
return undefined;
|
|
@@ -105,8 +107,11 @@ export class CliDataAccess extends CliOperatorApiClient {
|
|
|
105
107
|
if (!issue)
|
|
106
108
|
return undefined;
|
|
107
109
|
const dbIssue = this.db.issues.getIssueByKey(issueKey);
|
|
108
|
-
const activeRun = dbIssue.activeRunId ? this.db.runs.getRunById(dbIssue.activeRunId) : undefined;
|
|
109
110
|
const latestRun = this.db.runs.getLatestRunForIssue(issue.projectId, issue.linearIssueId);
|
|
111
|
+
const activeRun = resolveEffectiveActiveRun({
|
|
112
|
+
activeRun: dbIssue.activeRunId ? this.db.runs.getRunById(dbIssue.activeRunId) : undefined,
|
|
113
|
+
latestRun,
|
|
114
|
+
});
|
|
110
115
|
const latestReport = normalizeStageReport(latestRun?.reportJson, latestRun?.status);
|
|
111
116
|
const latestSummary = safeJsonParse(latestRun?.summaryJson);
|
|
112
117
|
const completionCheck = latestRun ? extractCompletionCheck(latestRun) : undefined;
|
|
@@ -139,7 +144,10 @@ export class CliDataAccess extends CliOperatorApiClient {
|
|
|
139
144
|
if (!issue)
|
|
140
145
|
return undefined;
|
|
141
146
|
const dbIssue = this.db.issues.getIssueByKey(issueKey);
|
|
142
|
-
const run =
|
|
147
|
+
const run = resolveEffectiveActiveRun({
|
|
148
|
+
activeRun: dbIssue.activeRunId ? this.db.runs.getRunById(dbIssue.activeRunId) : undefined,
|
|
149
|
+
latestRun: this.db.runs.getLatestRunForIssue(issue.projectId, issue.linearIssueId),
|
|
150
|
+
});
|
|
143
151
|
if (!run)
|
|
144
152
|
return undefined;
|
|
145
153
|
const live = run.threadId &&
|
|
@@ -469,19 +477,36 @@ export class CliDataAccess extends CliOperatorApiClient {
|
|
|
469
477
|
ORDER BY i.updated_at DESC, i.issue_key ASC
|
|
470
478
|
`)
|
|
471
479
|
.all(...values);
|
|
472
|
-
const items = rows.map((row) =>
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
480
|
+
const items = rows.map((row) => {
|
|
481
|
+
const detachedActiveRun = row.active_run_type === null
|
|
482
|
+
&& (row.latest_run_status === "queued" || row.latest_run_status === "running");
|
|
483
|
+
const activeRunType = row.active_run_type !== null
|
|
484
|
+
? String(row.active_run_type)
|
|
485
|
+
: detachedActiveRun && row.latest_run_type !== null
|
|
486
|
+
? String(row.latest_run_type)
|
|
487
|
+
: undefined;
|
|
488
|
+
const waitingReason = detachedActiveRun
|
|
489
|
+
? derivePatchRelayWaitingReason({
|
|
490
|
+
activeRunId: 1,
|
|
491
|
+
factoryState: String(row.factory_state ?? "delegated"),
|
|
492
|
+
})
|
|
493
|
+
: row.waiting_reason !== null
|
|
494
|
+
? String(row.waiting_reason)
|
|
495
|
+
: undefined;
|
|
496
|
+
return {
|
|
497
|
+
...(row.issue_key !== null ? { issueKey: String(row.issue_key) } : {}),
|
|
498
|
+
...(row.title !== null ? { title: String(row.title) } : {}),
|
|
499
|
+
projectId: String(row.project_id),
|
|
500
|
+
...(row.current_linear_state !== null ? { currentLinearState: String(row.current_linear_state) } : {}),
|
|
501
|
+
...(row.session_state !== null ? { sessionState: detachedActiveRun ? "running" : String(row.session_state) } : {}),
|
|
502
|
+
factoryState: String(row.factory_state ?? "delegated"),
|
|
503
|
+
...(waitingReason ? { waitingReason } : {}),
|
|
504
|
+
...(activeRunType ? { activeRunType } : {}),
|
|
505
|
+
...(row.latest_run_type !== null ? { latestRunType: String(row.latest_run_type) } : {}),
|
|
506
|
+
...(row.latest_run_status !== null ? { latestRunStatus: String(row.latest_run_status) } : {}),
|
|
507
|
+
updatedAt: String(row.updated_at),
|
|
508
|
+
};
|
|
509
|
+
});
|
|
485
510
|
return items.filter((item) => {
|
|
486
511
|
if (options?.active && !item.activeRunType)
|
|
487
512
|
return false;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
function isActiveRunStatus(status) {
|
|
2
|
+
return status === "queued" || status === "running";
|
|
3
|
+
}
|
|
4
|
+
export function hasDetachedActiveLatestRun(params) {
|
|
5
|
+
return params.activeRunId === undefined
|
|
6
|
+
&& params.latestRun !== undefined
|
|
7
|
+
&& isActiveRunStatus(params.latestRun.status);
|
|
8
|
+
}
|
|
9
|
+
export function resolveEffectiveActiveRun(params) {
|
|
10
|
+
if (params.activeRun)
|
|
11
|
+
return params.activeRun;
|
|
12
|
+
if (params.latestRun && isActiveRunStatus(params.latestRun.status))
|
|
13
|
+
return params.latestRun;
|
|
14
|
+
return undefined;
|
|
15
|
+
}
|
package/dist/run-reconciler.js
CHANGED
|
@@ -4,6 +4,7 @@ import { resolveAuthoritativeLinearStopState } from "./linear-workflow.js";
|
|
|
4
4
|
import { buildRunFailureActivity } from "./linear-session-reporting.js";
|
|
5
5
|
import { getThreadTurns } from "./codex-thread-utils.js";
|
|
6
6
|
import { resolveRecoverablePostRunState } from "./interrupted-run-recovery.js";
|
|
7
|
+
import { resolveEffectiveActiveRun } from "./effective-active-run.js";
|
|
7
8
|
export class RunReconciler {
|
|
8
9
|
db;
|
|
9
10
|
logger;
|
|
@@ -33,6 +34,19 @@ export class RunReconciler {
|
|
|
33
34
|
const { run, issue, recoveryLease } = params;
|
|
34
35
|
const acquiredRecoveryLease = recoveryLease === true;
|
|
35
36
|
let effectiveIssue = issue;
|
|
37
|
+
const effectiveActiveRun = resolveEffectiveActiveRun({
|
|
38
|
+
activeRun: issue.activeRunId === run.id ? run : undefined,
|
|
39
|
+
latestRun: run,
|
|
40
|
+
});
|
|
41
|
+
if (effectiveActiveRun?.id === run.id && issue.activeRunId !== run.id) {
|
|
42
|
+
effectiveIssue = this.withHeldLease(run.projectId, run.linearIssueId, () => this.db.issues.upsertIssue({
|
|
43
|
+
projectId: run.projectId,
|
|
44
|
+
linearIssueId: run.linearIssueId,
|
|
45
|
+
activeRunId: run.id,
|
|
46
|
+
...(run.threadId ? { threadId: run.threadId } : {}),
|
|
47
|
+
})) ?? effectiveIssue;
|
|
48
|
+
this.logger.info({ issueKey: effectiveIssue.issueKey, runId: run.id, runType: run.runType }, "Reattached detached active run during reconciliation");
|
|
49
|
+
}
|
|
36
50
|
if (!effectiveIssue.delegatedToPatchRelay) {
|
|
37
51
|
const authority = await this.confirmDelegationAuthorityBeforeRelease(run, effectiveIssue);
|
|
38
52
|
effectiveIssue = authority.issue;
|
|
@@ -2,6 +2,7 @@ import { parseGitHubFailureContext, summarizeGitHubFailureContext } from "./gith
|
|
|
2
2
|
import { derivePatchRelayWaitingReason } from "./waiting-reason.js";
|
|
3
3
|
import { deriveIssueStatusNote } from "./status-note.js";
|
|
4
4
|
import { isIssueSessionReadyForExecution } from "./issue-session.js";
|
|
5
|
+
import { hasDetachedActiveLatestRun } from "./effective-active-run.js";
|
|
5
6
|
function shouldSuppressStatusNote(params) {
|
|
6
7
|
if (!params.activeRunType && params.sessionState !== "running")
|
|
7
8
|
return false;
|
|
@@ -155,11 +156,24 @@ export class TrackedIssueListQuery {
|
|
|
155
156
|
const hasPendingSessionEvents = Number(row.pending_session_event_count ?? 0) > 0;
|
|
156
157
|
const hasPendingWake = hasPendingSessionEvents
|
|
157
158
|
|| this.db.issueSessions.peekIssueSessionWake(String(row.project_id), String(row.linear_issue_id)) !== undefined;
|
|
159
|
+
const detachedActiveRun = hasDetachedActiveLatestRun({
|
|
160
|
+
activeRunId: row.active_run_type !== null ? 1 : undefined,
|
|
161
|
+
latestRun: row.latest_run_status !== null
|
|
162
|
+
? { id: 0, status: String(row.latest_run_status) }
|
|
163
|
+
: undefined,
|
|
164
|
+
});
|
|
165
|
+
const effectiveActiveRunType = row.active_run_type !== null
|
|
166
|
+
? String(row.active_run_type)
|
|
167
|
+
: detachedActiveRun && row.latest_run_type !== null
|
|
168
|
+
? String(row.latest_run_type)
|
|
169
|
+
: undefined;
|
|
158
170
|
const readyForExecution = isIssueSessionReadyForExecution({
|
|
159
|
-
...(typeof row.session_state === "string"
|
|
171
|
+
...(typeof row.session_state === "string"
|
|
172
|
+
? { sessionState: detachedActiveRun ? "running" : String(row.session_state) }
|
|
173
|
+
: {}),
|
|
160
174
|
factoryState: String(row.factory_state ?? "delegated"),
|
|
161
175
|
...(row.delegated_to_patchrelay !== null ? { delegatedToPatchRelay: Number(row.delegated_to_patchrelay) !== 0 } : {}),
|
|
162
|
-
...(row.active_run_type !== null ? { activeRunId: 1 } : {}),
|
|
176
|
+
...((row.active_run_type !== null || detachedActiveRun) ? { activeRunId: 1 } : {}),
|
|
163
177
|
blockedByCount,
|
|
164
178
|
hasPendingWake,
|
|
165
179
|
hasLegacyPendingRun: row.pending_run_type !== null && row.pending_run_type !== undefined,
|
|
@@ -177,9 +191,9 @@ export class TrackedIssueListQuery {
|
|
|
177
191
|
const sessionSummary = typeof row.summary_text === "string" && row.summary_text.trim().length > 0
|
|
178
192
|
? row.summary_text
|
|
179
193
|
: undefined;
|
|
180
|
-
const
|
|
194
|
+
const derivedWaitingReason = derivePatchRelayWaitingReason({
|
|
181
195
|
...(row.delegated_to_patchrelay !== null ? { delegatedToPatchRelay: Number(row.delegated_to_patchrelay) !== 0 } : {}),
|
|
182
|
-
...(row.active_run_type !== null ? {
|
|
196
|
+
...((row.active_run_type !== null || detachedActiveRun) ? { activeRunId: 1 } : {}),
|
|
183
197
|
blockedByKeys,
|
|
184
198
|
factoryState: String(row.factory_state ?? "delegated"),
|
|
185
199
|
...(row.pending_run_type !== null ? { pendingRunType: String(row.pending_run_type) } : {}),
|
|
@@ -192,6 +206,7 @@ export class TrackedIssueListQuery {
|
|
|
192
206
|
...(row.last_blocking_review_head_sha !== null ? { lastBlockingReviewHeadSha: String(row.last_blocking_review_head_sha) } : {}),
|
|
193
207
|
...(row.last_github_failure_check_name !== null ? { latestFailureCheckName: String(row.last_github_failure_check_name) } : {}),
|
|
194
208
|
});
|
|
209
|
+
const waitingReason = detachedActiveRun ? derivedWaitingReason : sessionWaitingReason ?? derivedWaitingReason;
|
|
195
210
|
const latestRun = row.latest_run_type !== null && row.latest_run_status !== null
|
|
196
211
|
? {
|
|
197
212
|
id: 0,
|
|
@@ -222,29 +237,39 @@ export class TrackedIssueListQuery {
|
|
|
222
237
|
waitingReason,
|
|
223
238
|
}) ?? waitingReason;
|
|
224
239
|
const statusNoteForReturn = shouldSuppressStatusNote({
|
|
225
|
-
activeRunType:
|
|
226
|
-
sessionState: row.session_state,
|
|
240
|
+
activeRunType: effectiveActiveRunType,
|
|
241
|
+
sessionState: detachedActiveRun ? "running" : row.session_state,
|
|
227
242
|
statusNote: statusNoteCandidate,
|
|
228
243
|
})
|
|
229
244
|
? undefined
|
|
230
245
|
: statusNoteCandidate;
|
|
231
|
-
const
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
246
|
+
const activeCompletionCheckThreadId = row.active_run_type !== null
|
|
247
|
+
? row.active_completion_check_thread_id
|
|
248
|
+
: detachedActiveRun
|
|
249
|
+
? row.latest_run_completion_check_thread_id
|
|
250
|
+
: null;
|
|
251
|
+
const activeCompletionCheckOutcome = row.active_run_type !== null
|
|
252
|
+
? row.active_completion_check_outcome
|
|
253
|
+
: detachedActiveRun
|
|
254
|
+
? row.latest_run_completion_check_outcome
|
|
255
|
+
: null;
|
|
256
|
+
const completionCheckActive = typeof activeCompletionCheckThreadId === "string"
|
|
257
|
+
&& activeCompletionCheckThreadId.length > 0
|
|
258
|
+
&& activeCompletionCheckOutcome === null
|
|
259
|
+
&& effectiveActiveRunType !== undefined;
|
|
235
260
|
return {
|
|
236
261
|
...(row.issue_key !== null ? { issueKey: String(row.issue_key) } : {}),
|
|
237
262
|
...(row.title !== null ? { title: String(row.title) } : {}),
|
|
238
263
|
...(statusNoteForReturn ? { statusNote: statusNoteForReturn } : {}),
|
|
239
264
|
projectId: String(row.project_id),
|
|
240
265
|
delegatedToPatchRelay: row.delegated_to_patchrelay === null ? true : Number(row.delegated_to_patchrelay) !== 0,
|
|
241
|
-
...(row.session_state !== null ? { sessionState: String(row.session_state) } : {}),
|
|
266
|
+
...(row.session_state !== null ? { sessionState: detachedActiveRun ? "running" : String(row.session_state) } : {}),
|
|
242
267
|
factoryState: String(row.factory_state ?? "delegated"),
|
|
243
268
|
blockedByCount,
|
|
244
269
|
blockedByKeys,
|
|
245
270
|
readyForExecution,
|
|
246
271
|
...(row.current_linear_state !== null ? { currentLinearState: String(row.current_linear_state) } : {}),
|
|
247
|
-
...(
|
|
272
|
+
...(effectiveActiveRunType ? { activeRunType: effectiveActiveRunType } : {}),
|
|
248
273
|
...(row.pending_run_type !== null ? { pendingRunType: String(row.pending_run_type) } : {}),
|
|
249
274
|
...(row.latest_run_type !== null ? { latestRunType: String(row.latest_run_type) } : {}),
|
|
250
275
|
...(row.latest_run_status !== null ? { latestRunStatus: String(row.latest_run_status) } : {}),
|
|
@@ -2,6 +2,7 @@ import { parseGitHubFailureContext } from "./github-failure-context.js";
|
|
|
2
2
|
import { isIssueSessionReadyForExecution } from "./issue-session.js";
|
|
3
3
|
import { deriveIssueStatusNote } from "./status-note.js";
|
|
4
4
|
import { derivePatchRelayWaitingReason } from "./waiting-reason.js";
|
|
5
|
+
import { hasDetachedActiveLatestRun, resolveEffectiveActiveRun } from "./effective-active-run.js";
|
|
5
6
|
export function isResolvedLinearState(stateType, stateName) {
|
|
6
7
|
return stateType === "completed" || stateName?.trim().toLowerCase() === "done";
|
|
7
8
|
}
|
|
@@ -9,9 +10,17 @@ export function buildTrackedIssueRecord(params) {
|
|
|
9
10
|
const unresolvedBlockedBy = params.blockedBy.filter((entry) => !isResolvedLinearState(entry.blockerCurrentLinearStateType, entry.blockerCurrentLinearState));
|
|
10
11
|
const failureContext = parseGitHubFailureContext(params.issue.lastGitHubFailureContextJson);
|
|
11
12
|
const blockedByKeys = unresolvedBlockedBy.map((entry) => entry.blockerIssueKey ?? entry.blockerLinearIssueId);
|
|
13
|
+
const effectiveActiveRun = resolveEffectiveActiveRun({
|
|
14
|
+
activeRun: params.issue.activeRunId !== undefined && params.latestRun?.id === params.issue.activeRunId ? params.latestRun : undefined,
|
|
15
|
+
latestRun: params.latestRun,
|
|
16
|
+
});
|
|
17
|
+
const detachedActiveRun = hasDetachedActiveLatestRun({
|
|
18
|
+
activeRunId: params.issue.activeRunId,
|
|
19
|
+
latestRun: params.latestRun,
|
|
20
|
+
});
|
|
12
21
|
const waitingReason = derivePatchRelayWaitingReason({
|
|
13
22
|
delegatedToPatchRelay: params.issue.delegatedToPatchRelay,
|
|
14
|
-
...(
|
|
23
|
+
...(effectiveActiveRun ? { activeRunId: effectiveActiveRun.id } : {}),
|
|
15
24
|
blockedByKeys,
|
|
16
25
|
factoryState: params.issue.factoryState,
|
|
17
26
|
pendingRunType: params.issue.pendingRunType,
|
|
@@ -33,11 +42,9 @@ export function buildTrackedIssueRecord(params) {
|
|
|
33
42
|
blockedByKeys,
|
|
34
43
|
waitingReason,
|
|
35
44
|
});
|
|
36
|
-
const completionCheckActive = Boolean(
|
|
37
|
-
&&
|
|
38
|
-
&&
|
|
39
|
-
&& params.latestRun.completionCheckThreadId
|
|
40
|
-
&& !params.latestRun.completionCheckOutcome);
|
|
45
|
+
const completionCheckActive = Boolean(effectiveActiveRun?.status === "running"
|
|
46
|
+
&& effectiveActiveRun.completionCheckThreadId
|
|
47
|
+
&& !effectiveActiveRun.completionCheckOutcome);
|
|
41
48
|
return {
|
|
42
49
|
id: params.issue.id,
|
|
43
50
|
projectId: params.issue.projectId,
|
|
@@ -61,7 +68,7 @@ export function buildTrackedIssueRecord(params) {
|
|
|
61
68
|
sessionState: params.session?.sessionState,
|
|
62
69
|
factoryState: params.issue.factoryState,
|
|
63
70
|
delegatedToPatchRelay: params.issue.delegatedToPatchRelay,
|
|
64
|
-
activeRunId:
|
|
71
|
+
...(effectiveActiveRun ? { activeRunId: effectiveActiveRun.id } : {}),
|
|
65
72
|
blockedByCount: unresolvedBlockedBy.length,
|
|
66
73
|
hasPendingWake: params.hasPendingWake,
|
|
67
74
|
hasLegacyPendingRun: params.issue.pendingRunType !== undefined,
|
|
@@ -79,8 +86,9 @@ export function buildTrackedIssueRecord(params) {
|
|
|
79
86
|
...(failureContext?.summary ? { latestFailureSummary: failureContext.summary } : {}),
|
|
80
87
|
...(waitingReason ? { waitingReason } : {}),
|
|
81
88
|
...(completionCheckActive ? { completionCheckActive } : {}),
|
|
82
|
-
...(
|
|
89
|
+
...(effectiveActiveRun ? { activeRunId: effectiveActiveRun.id } : {}),
|
|
83
90
|
...(params.issue.agentSessionId ? { activeAgentSessionId: params.issue.agentSessionId } : {}),
|
|
91
|
+
...(detachedActiveRun && params.session?.sessionState ? { sessionState: "running" } : {}),
|
|
84
92
|
updatedAt: params.issue.updatedAt,
|
|
85
93
|
};
|
|
86
94
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { buildTrackedIssueRecord } from "./tracked-issue-projector.js";
|
|
2
2
|
import { deriveIssueSessionState, isIssueSessionReadyForExecution } from "./issue-session.js";
|
|
3
|
+
import { resolveEffectiveActiveRun } from "./effective-active-run.js";
|
|
3
4
|
export class TrackedIssueQuery {
|
|
4
5
|
issues;
|
|
5
6
|
issueSessions;
|
|
@@ -55,7 +56,10 @@ export class TrackedIssueQuery {
|
|
|
55
56
|
if (!issue)
|
|
56
57
|
return undefined;
|
|
57
58
|
const tracked = this.issueToTrackedIssue(issue);
|
|
58
|
-
const activeRun =
|
|
59
|
+
const activeRun = resolveEffectiveActiveRun({
|
|
60
|
+
activeRun: issue.activeRunId ? this.runs.getRunById(issue.activeRunId) : undefined,
|
|
61
|
+
latestRun: this.runs.getLatestRunForIssue(issue.projectId, issue.linearIssueId),
|
|
62
|
+
});
|
|
59
63
|
return {
|
|
60
64
|
issue: tracked,
|
|
61
65
|
...(activeRun ? { activeRun } : {}),
|