patchrelay 0.36.15 → 0.36.16

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.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "service": "patchrelay",
3
- "version": "0.36.15",
4
- "commit": "4e1089aeeb7b",
5
- "builtAt": "2026-04-10T04:21:45.013Z"
3
+ "version": "0.36.16",
4
+ "commit": "f8ad93bd7a05",
5
+ "builtAt": "2026-04-10T04:39:43.765Z"
6
6
  }
@@ -103,6 +103,10 @@ export class InterruptedRunRecovery {
103
103
  await this.handleInterruptedRequestedChangesRun(run, issue);
104
104
  return;
105
105
  }
106
+ if (run.runType === "implementation" && !issue.prNumber) {
107
+ await this.handleInterruptedImplementationRun(run, issue);
108
+ return;
109
+ }
106
110
  const recoveredState = resolveRecoverablePostRunState(this.db.issues.getIssue(run.projectId, run.linearIssueId) ?? issue);
107
111
  this.failRunAndClear(run, "Codex turn was interrupted", recoveredState);
108
112
  await this.restoreIdleWorktree(issue);
@@ -124,6 +128,47 @@ export class InterruptedRunRecovery {
124
128
  void this.linearSync.syncSession(failedIssue, { activeRunType: run.runType });
125
129
  this.releaseLease(run.projectId, run.linearIssueId);
126
130
  }
131
+ async handleInterruptedImplementationRun(run, issue) {
132
+ const interruptedMessage = "Implementation run was interrupted before PatchRelay could publish a PR";
133
+ this.failRunAndClear(run, "Codex turn was interrupted", "delegated");
134
+ await this.restoreIdleWorktree(issue);
135
+ const refreshedIssue = this.db.issues.getIssue(run.projectId, run.linearIssueId) ?? issue;
136
+ this.db.issueSessions.appendIssueSessionEventRespectingActiveLease(run.projectId, run.linearIssueId, {
137
+ projectId: run.projectId,
138
+ linearIssueId: run.linearIssueId,
139
+ eventType: "delegated",
140
+ dedupeKey: `interrupted_implementation:implementation:${run.linearIssueId}`,
141
+ });
142
+ if (!this.db.issueSessions.peekIssueSessionWake(run.projectId, run.linearIssueId)) {
143
+ const failedIssue = this.db.issues.getIssue(run.projectId, run.linearIssueId) ?? refreshedIssue;
144
+ this.feed?.publish({
145
+ level: "error",
146
+ kind: "workflow",
147
+ issueKey: issue.issueKey,
148
+ projectId: run.projectId,
149
+ stage: run.runType,
150
+ status: "escalated",
151
+ summary: interruptedMessage,
152
+ });
153
+ void this.linearSync.emitActivity(failedIssue, buildRunFailureActivity(run.runType, interruptedMessage));
154
+ void this.linearSync.syncSession(failedIssue, { activeRunType: run.runType });
155
+ this.releaseLease(run.projectId, run.linearIssueId);
156
+ return;
157
+ }
158
+ this.feed?.publish({
159
+ level: "warn",
160
+ kind: "workflow",
161
+ issueKey: issue.issueKey,
162
+ projectId: run.projectId,
163
+ stage: run.runType,
164
+ status: "retry_queued",
165
+ summary: "Implementation run was interrupted; PatchRelay will retry automatically",
166
+ });
167
+ const recoveredIssue = this.db.issues.getIssue(run.projectId, run.linearIssueId) ?? refreshedIssue;
168
+ void this.linearSync.syncSession(recoveredIssue, { activeRunType: run.runType });
169
+ this.enqueueIssue(run.projectId, run.linearIssueId);
170
+ this.releaseLease(run.projectId, run.linearIssueId);
171
+ }
127
172
  async handleInterruptedRequestedChangesRun(run, issue) {
128
173
  const freshIssue = this.db.issues.getIssue(run.projectId, run.linearIssueId) ?? issue;
129
174
  const refreshedIssue = await this.completionPolicy.refreshIssueAfterReactivePublish(run, freshIssue);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "patchrelay",
3
- "version": "0.36.15",
3
+ "version": "0.36.16",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "repository": {