patchrelay 0.8.5 → 0.8.6
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
CHANGED
|
@@ -45,6 +45,19 @@ export class ReconciliationActionApplier {
|
|
|
45
45
|
if (decision.outcome === "fail" || decision.outcome === "release") {
|
|
46
46
|
const failedAction = decision.actions.find((action) => action.type === "mark_run_failed");
|
|
47
47
|
if (decision.outcome === "release" && failedAction?.type !== "mark_run_failed") {
|
|
48
|
+
const releasedAction = decision.actions.find((action) => action.type === "release_issue_ownership");
|
|
49
|
+
if (releasedAction?.type !== "release_issue_ownership") {
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
await this.callbacks.releaseRunDuringReconciliation(snapshot.runLease.projectId, snapshot.runLease.linearIssueId, {
|
|
53
|
+
runId: snapshot.runLease.id,
|
|
54
|
+
...(threadId ? { threadId } : {}),
|
|
55
|
+
...(turnId ? { turnId } : {}),
|
|
56
|
+
...(nextLifecycleStatus ? { nextLifecycleStatus } : {}),
|
|
57
|
+
...(snapshot.input.live?.linear?.status === "known" && snapshot.input.live.linear.issue?.stateName
|
|
58
|
+
? { currentLinearState: snapshot.input.live.linear.issue.stateName }
|
|
59
|
+
: {}),
|
|
60
|
+
});
|
|
48
61
|
return;
|
|
49
62
|
}
|
|
50
63
|
await this.callbacks.failRunDuringReconciliation(snapshot.runLease.projectId, snapshot.runLease.linearIssueId, failedAction?.type === "mark_run_failed" && failedAction.threadId
|
|
@@ -68,6 +68,23 @@ export function reconcileIssue(input) {
|
|
|
68
68
|
function reconcileActiveRun(params) {
|
|
69
69
|
const { issue, liveLinear, liveCodex, obligations, policy } = params;
|
|
70
70
|
const run = issue.activeRun;
|
|
71
|
+
const authoritativeStopState = resolveAuthoritativeStopState(liveLinear);
|
|
72
|
+
if (authoritativeStopState) {
|
|
73
|
+
return {
|
|
74
|
+
outcome: "release",
|
|
75
|
+
reasons: [`live Linear state is already ${authoritativeStopState.stateName}`],
|
|
76
|
+
actions: [
|
|
77
|
+
{
|
|
78
|
+
type: "release_issue_ownership",
|
|
79
|
+
projectId: issue.projectId,
|
|
80
|
+
linearIssueId: issue.linearIssueId,
|
|
81
|
+
runId: run.id,
|
|
82
|
+
nextLifecycleStatus: authoritativeStopState.lifecycleStatus,
|
|
83
|
+
reason: `live Linear state is already ${authoritativeStopState.stateName}`,
|
|
84
|
+
},
|
|
85
|
+
],
|
|
86
|
+
};
|
|
87
|
+
}
|
|
71
88
|
if (run.status === "queued") {
|
|
72
89
|
return {
|
|
73
90
|
outcome: "launch",
|
|
@@ -295,6 +312,27 @@ function matchesActiveLinearOwnership(liveLinear, policy) {
|
|
|
295
312
|
}
|
|
296
313
|
return liveLinear.issue?.stateName === policy.activeLinearStateName;
|
|
297
314
|
}
|
|
315
|
+
function resolveAuthoritativeStopState(liveLinear) {
|
|
316
|
+
if (liveLinear.status !== "known" || !liveLinear.issue?.stateName) {
|
|
317
|
+
return undefined;
|
|
318
|
+
}
|
|
319
|
+
const stateName = liveLinear.issue.stateName.trim();
|
|
320
|
+
const normalizedName = stateName.toLowerCase();
|
|
321
|
+
const normalizedType = liveLinear.issue.stateType?.trim().toLowerCase();
|
|
322
|
+
if (normalizedType === "completed" || normalizedName === "done" || normalizedName === "completed" || normalizedName === "complete") {
|
|
323
|
+
return {
|
|
324
|
+
stateName,
|
|
325
|
+
lifecycleStatus: "completed",
|
|
326
|
+
};
|
|
327
|
+
}
|
|
328
|
+
if (normalizedName === "human needed") {
|
|
329
|
+
return {
|
|
330
|
+
stateName,
|
|
331
|
+
lifecycleStatus: "paused",
|
|
332
|
+
};
|
|
333
|
+
}
|
|
334
|
+
return undefined;
|
|
335
|
+
}
|
|
298
336
|
function relevantObligations(issue, obligations) {
|
|
299
337
|
const activeRunId = issue.activeRun?.id;
|
|
300
338
|
return obligations.filter((obligation) => {
|
|
@@ -22,6 +22,10 @@ export async function buildReconciliationSnapshot(params) {
|
|
|
22
22
|
issue: {
|
|
23
23
|
id: issue.id,
|
|
24
24
|
stateName: issue.stateName,
|
|
25
|
+
...(() => {
|
|
26
|
+
const currentState = issue.workflowStates?.find((state) => state.name === issue.stateName);
|
|
27
|
+
return currentState?.type ? { stateType: currentState.type } : {};
|
|
28
|
+
})(),
|
|
25
29
|
},
|
|
26
30
|
}
|
|
27
31
|
: ({ status: "unknown" }))
|
|
@@ -36,6 +36,7 @@ export class ServiceStageFinalizer {
|
|
|
36
36
|
deliverPendingObligations: (projectId, linearIssueId, threadId, turnId) => this.deliverPendingObligations(projectId, linearIssueId, threadId, turnId),
|
|
37
37
|
completeRun: (projectId, linearIssueId, thread, params) => this.completeReconciledRun(projectId, linearIssueId, thread, params),
|
|
38
38
|
failRunDuringReconciliation: (projectId, linearIssueId, threadId, message, options) => this.failRunLeaseDuringReconciliation(projectId, linearIssueId, threadId, message, options),
|
|
39
|
+
releaseRunDuringReconciliation: (projectId, linearIssueId, params) => this.releaseRunDuringReconciliation(projectId, linearIssueId, params),
|
|
39
40
|
});
|
|
40
41
|
}
|
|
41
42
|
async getActiveStageStatus(issueKey) {
|
|
@@ -468,13 +469,21 @@ export class ServiceStageFinalizer {
|
|
|
468
469
|
if (targetRunLease.workspaceOwnershipId !== undefined) {
|
|
469
470
|
const workspace = this.stores.workspaceOwnership.getWorkspaceOwnership(targetRunLease.workspaceOwnershipId);
|
|
470
471
|
if (workspace) {
|
|
472
|
+
const workspaceOwnedByTargetRun = workspace.currentRunLeaseId === targetRunLeaseId ||
|
|
473
|
+
(issueControl?.activeWorkspaceOwnershipId === workspace.id && issueControl.activeRunLeaseId === targetRunLeaseId);
|
|
471
474
|
this.stores.workspaceOwnership.upsertWorkspaceOwnership({
|
|
472
475
|
projectId,
|
|
473
476
|
linearIssueId,
|
|
474
477
|
branchName: workspace.branchName,
|
|
475
478
|
worktreePath: workspace.worktreePath,
|
|
476
|
-
status:
|
|
477
|
-
|
|
479
|
+
status: workspaceOwnedByTargetRun
|
|
480
|
+
? status === "released"
|
|
481
|
+
? "released"
|
|
482
|
+
: status === "completed"
|
|
483
|
+
? "active"
|
|
484
|
+
: "paused"
|
|
485
|
+
: workspace.status,
|
|
486
|
+
...(workspaceOwnedByTargetRun
|
|
478
487
|
? { currentRunLeaseId: null }
|
|
479
488
|
: workspace.currentRunLeaseId !== undefined
|
|
480
489
|
? { currentRunLeaseId: workspace.currentRunLeaseId }
|
|
@@ -489,9 +498,11 @@ export class ServiceStageFinalizer {
|
|
|
489
498
|
projectId,
|
|
490
499
|
linearIssueId,
|
|
491
500
|
activeRunLeaseId: null,
|
|
492
|
-
...(
|
|
493
|
-
? { activeWorkspaceOwnershipId:
|
|
494
|
-
:
|
|
501
|
+
...(status === "released"
|
|
502
|
+
? { activeWorkspaceOwnershipId: null }
|
|
503
|
+
: issueControl.activeWorkspaceOwnershipId !== undefined
|
|
504
|
+
? { activeWorkspaceOwnershipId: issueControl.activeWorkspaceOwnershipId }
|
|
505
|
+
: {}),
|
|
495
506
|
...(issueControl.serviceOwnedCommentId ? { serviceOwnedCommentId: issueControl.serviceOwnedCommentId } : {}),
|
|
496
507
|
...(issueControl.activeAgentSessionId ? { activeAgentSessionId: issueControl.activeAgentSessionId } : {}),
|
|
497
508
|
lifecycleStatus: params.nextLifecycleStatus,
|
|
@@ -623,6 +634,44 @@ export class ServiceStageFinalizer {
|
|
|
623
634
|
}
|
|
624
635
|
await this.failStageRunDuringReconciliation(stageRun, threadId, message, options);
|
|
625
636
|
}
|
|
637
|
+
async releaseRunDuringReconciliation(projectId, linearIssueId, params) {
|
|
638
|
+
const runId = typeof params.runId === "number" ? params.runId : Number(params.runId);
|
|
639
|
+
if (!Number.isFinite(runId)) {
|
|
640
|
+
return;
|
|
641
|
+
}
|
|
642
|
+
this.runAtomically(() => {
|
|
643
|
+
this.finishLedgerRun(projectId, linearIssueId, "released", {
|
|
644
|
+
stageRunId: runId,
|
|
645
|
+
...(params.threadId ? { threadId: params.threadId } : {}),
|
|
646
|
+
...(params.turnId ? { turnId: params.turnId } : {}),
|
|
647
|
+
nextLifecycleStatus: params.nextLifecycleStatus ?? "completed",
|
|
648
|
+
});
|
|
649
|
+
const existingIssue = this.stores.issueWorkflows.getTrackedIssue(projectId, linearIssueId);
|
|
650
|
+
this.stores.workflowCoordinator.upsertTrackedIssue({
|
|
651
|
+
projectId,
|
|
652
|
+
linearIssueId,
|
|
653
|
+
...(params.currentLinearState
|
|
654
|
+
? { currentLinearState: params.currentLinearState }
|
|
655
|
+
: existingIssue?.currentLinearState
|
|
656
|
+
? { currentLinearState: existingIssue.currentLinearState }
|
|
657
|
+
: {}),
|
|
658
|
+
lifecycleStatus: params.nextLifecycleStatus ?? "completed",
|
|
659
|
+
});
|
|
660
|
+
});
|
|
661
|
+
const issue = this.stores.issueWorkflows.getTrackedIssue(projectId, linearIssueId);
|
|
662
|
+
this.feed?.publish({
|
|
663
|
+
level: "info",
|
|
664
|
+
kind: "workflow",
|
|
665
|
+
issueKey: issue?.issueKey,
|
|
666
|
+
projectId,
|
|
667
|
+
...(issue?.selectedWorkflowId ? { workflowId: issue.selectedWorkflowId } : {}),
|
|
668
|
+
status: params.nextLifecycleStatus === "paused" ? "transition_suppressed" : "completed",
|
|
669
|
+
summary: params.nextLifecycleStatus === "paused"
|
|
670
|
+
? "Released stale run after terminal Linear pause"
|
|
671
|
+
: "Released stale run after terminal Linear completion",
|
|
672
|
+
detail: params.currentLinearState ? `Live Linear state is already ${params.currentLinearState}.` : undefined,
|
|
673
|
+
});
|
|
674
|
+
}
|
|
626
675
|
findStageRunForIssue(projectId, linearIssueId, threadId) {
|
|
627
676
|
return (threadId ? this.stores.issueWorkflows.getStageRunByThreadId(threadId) : undefined) ??
|
|
628
677
|
this.stores.issueWorkflows.getLatestStageRunForIssue(projectId, linearIssueId);
|