patchrelay 0.51.2 → 0.51.4

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.51.2",
4
- "commit": "525db768a1e0",
5
- "builtAt": "2026-04-22T03:51:24.647Z"
3
+ "version": "0.51.4",
4
+ "commit": "68e71c39b593",
5
+ "builtAt": "2026-04-22T04:33:28.283Z"
6
6
  }
@@ -1,4 +1,4 @@
1
- import { shouldSyncVisibleIssueComment, syncVisibleStatusComment, } from "./linear-status-comment-sync.js";
1
+ import { collapseVisibleStatusComment, shouldSyncVisibleIssueComment, syncVisibleStatusComment, } from "./linear-status-comment-sync.js";
2
2
  import { LinearAgentSessionClient } from "./linear-agent-session-client.js";
3
3
  import { LinearProgressReporter } from "./linear-progress-reporter.js";
4
4
  import { syncActiveWorkflowState } from "./linear-workflow-state-sync.js";
@@ -49,6 +49,13 @@ export class LinearSessionSync {
49
49
  ...(options ? { options } : {}),
50
50
  });
51
51
  }
52
+ else if (syncedIssue.statusCommentId) {
53
+ await collapseVisibleStatusComment({
54
+ issue: syncedIssue,
55
+ linear,
56
+ logger: this.logger,
57
+ });
58
+ }
52
59
  }
53
60
  catch (error) {
54
61
  const msg = error instanceof Error ? error.message : String(error);
@@ -25,8 +25,37 @@ export async function syncVisibleStatusComment(params) {
25
25
  logger.warn({ issueKey: issue.issueKey, error: msg }, "Failed to sync Linear status comment");
26
26
  }
27
27
  }
28
- export function shouldSyncVisibleIssueComment(_issue, _hasAgentSession) {
29
- return true;
28
+ export function shouldSyncVisibleIssueComment(issue, hasAgentSession) {
29
+ if (!hasAgentSession) {
30
+ return true;
31
+ }
32
+ if (issue.sessionState === "waiting_input" || issue.factoryState === "awaiting_input") {
33
+ return true;
34
+ }
35
+ if (issue.sessionState === "failed" || issue.factoryState === "failed" || issue.factoryState === "escalated") {
36
+ return true;
37
+ }
38
+ if (issue.factoryState === "done") {
39
+ return issue.prState !== "merged";
40
+ }
41
+ return false;
42
+ }
43
+ export async function collapseVisibleStatusComment(params) {
44
+ const { issue, linear, logger } = params;
45
+ if (!issue.statusCommentId) {
46
+ return;
47
+ }
48
+ try {
49
+ await linear.upsertIssueComment({
50
+ issueId: issue.linearIssueId,
51
+ commentId: issue.statusCommentId,
52
+ body: renderCollapsedStatusComment(),
53
+ });
54
+ }
55
+ catch (error) {
56
+ const msg = error instanceof Error ? error.message : String(error);
57
+ logger.warn({ issueId: issue.linearIssueId, error: msg }, "Failed to collapse Linear status comment");
58
+ }
30
59
  }
31
60
  function renderStatusComment(db, issue, trackedIssue, options) {
32
61
  const activeRun = issue.activeRunId ? db.runs.getRunById(issue.activeRunId) : undefined;
@@ -97,21 +126,18 @@ function renderStatusComment(db, issue, trackedIssue, options) {
97
126
  : `PR: ${linkedLabel}`;
98
127
  lines.push("", prLine);
99
128
  }
100
- if (latestRun) {
101
- lines.push("", `Latest run: ${formatLatestRun(latestRun)}`);
102
- if (latestRun.failureReason) {
103
- lines.push("", `Failure: ${latestRun.failureReason}`);
104
- }
105
- if (completionCheck && completionCheck.outcome !== "needs_input" && completionCheck.summary !== statusNote) {
106
- lines.push("", `Completion check: ${completionCheck.summary}`);
107
- }
108
- }
109
129
  if (issue.lastGitHubFailureCheckName && (issue.factoryState === "repairing_ci" || issue.prCheckStatus === "failed" || issue.prCheckStatus === "failure")) {
110
130
  lines.push("", `Latest failing check: ${issue.lastGitHubFailureCheckName}`);
111
131
  }
112
- lines.push("", "_PatchRelay updates this comment as it works. Review and merge remain downstream._");
113
132
  return lines.join("\n");
114
133
  }
134
+ function renderCollapsedStatusComment() {
135
+ return [
136
+ "## PatchRelay status",
137
+ "",
138
+ "Live status is in the agent session and activity feed. This comment is reused only when PatchRelay needs human input or intervention.",
139
+ ].join("\n");
140
+ }
115
141
  function statusHeadline(issue, activeRunType) {
116
142
  const prContext = derivePrDisplayContext(issue);
117
143
  if (activeRunType) {
@@ -193,10 +219,6 @@ function statusHeadline(issue, activeRunType) {
193
219
  return humanize(issue.factoryState);
194
220
  }
195
221
  }
196
- function formatLatestRun(run) {
197
- const at = run.endedAt ?? run.startedAt;
198
- return `${humanize(run.runType)} ${run.status} at ${at}`;
199
- }
200
222
  function humanize(value) {
201
223
  return value.replaceAll("_", " ");
202
224
  }
@@ -34,7 +34,10 @@ export class ServiceStartupRecovery {
34
34
  continue;
35
35
  }
36
36
  const activeRun = syncedIssue.activeRunId ? this.db.runs.getRunById(syncedIssue.activeRunId) : undefined;
37
- await this.linearSync.syncSession(syncedIssue, activeRun ? { activeRunType: activeRun.runType } : undefined);
37
+ if (!activeRun) {
38
+ continue;
39
+ }
40
+ await this.linearSync.syncSession(syncedIssue, { activeRunType: activeRun.runType });
38
41
  }
39
42
  }
40
43
  async recoverDelegatedIssueStateFromLinear() {
@@ -36,6 +36,7 @@ export function deriveIssueStatusNote(params) {
36
36
  ? completionCheck.question ?? completionCheck.summary
37
37
  : completionCheck?.summary);
38
38
  const latestRunNote = clean(extractLatestAssistantSummary(params.latestRun));
39
+ const latestFailureReason = clean(params.latestRun?.failureReason);
39
40
  const latestEventNote = clean(eventStatusNote(params.latestEvent));
40
41
  const failureSummary = clean(params.failureSummary);
41
42
  const waitingReason = clean(params.waitingReason);
@@ -50,7 +51,7 @@ export function deriveIssueStatusNote(params) {
50
51
  break;
51
52
  case "failed":
52
53
  case "escalated":
53
- note = latestEventNote ?? completionCheckNote ?? failureSummary ?? latestRunNote ?? sessionSummary;
54
+ note = latestEventNote ?? completionCheckNote ?? failureSummary ?? latestFailureReason ?? latestRunNote ?? sessionSummary;
54
55
  break;
55
56
  case "done":
56
57
  note = completionCheckNote ?? sessionSummary ?? latestRunNote ?? failureSummary;
@@ -2,8 +2,7 @@ export function isInertPatchRelayComment(issue, commentId, body, actorType) {
2
2
  if (commentId === issue.statusCommentId) {
3
3
  return true;
4
4
  }
5
- if (body.startsWith("## PatchRelay status")
6
- && body.includes("_PatchRelay updates this comment as it works. Review and merge remain downstream._")) {
5
+ if (body.startsWith("## PatchRelay status")) {
7
6
  return true;
8
7
  }
9
8
  const normalizedActorType = actorType?.trim().toLowerCase();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "patchrelay",
3
- "version": "0.51.2",
3
+ "version": "0.51.4",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "repository": {