patchrelay 0.52.3 → 0.52.5

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.52.3",
4
- "commit": "76e8fb03067d",
5
- "builtAt": "2026-04-22T19:20:36.664Z"
3
+ "version": "0.52.5",
4
+ "commit": "eea06016664a",
5
+ "builtAt": "2026-04-22T21:13:17.024Z"
6
6
  }
package/dist/config.js CHANGED
@@ -23,6 +23,8 @@ const DEFAULT_PATCHRELAY_DEVELOPER_INSTRUCTIONS = [
23
23
  "- A requested-changes repair is only complete after a newer PR head is pushed, unless a genuine external blocker prevents correct publication.",
24
24
  "- If you change schema, enums, shared vocabulary, normalization helpers, or compatibility mappings, inspect the main read/write paths that can bypass the new abstraction and fix or cover any mismatch before publishing.",
25
25
  "- For CI repair, do not change code or config until you either reproduce the failure on the exact failing head or can point to a concrete log signature that justifies the fix. If you cannot reproduce it, prefer a rerun-only repair over speculative changes.",
26
+ "- For main repair, first verify that the incident still persists on the exact failing main SHA. If a rerun clears transient runner issues such as disk pressure or network lag, treat that as the repair instead of inventing branch changes.",
27
+ "- Do not propose or implement moving CI, deploy, or tests to different nodes or runner pools unless a human explicitly asked for that infrastructure migration.",
26
28
  "- If a broader inconsistency is not required to make this task correct, mention it briefly instead of expanding scope.",
27
29
  "- Before publishing, do one brief reviewer-minded pass on the current head and fix likely in-scope blockers.",
28
30
  ].join("\n");
@@ -37,12 +37,7 @@ export class MainBranchHealthMonitor {
37
37
  return;
38
38
  const baseBranch = project.github.baseBranch ?? "main";
39
39
  const branchName = buildMainRepairBranchName(baseBranch);
40
- const existing = this.db.listIssues().find((issue) => (issue.projectId === projectId
41
- && issue.branchName === branchName
42
- && isMainRepairIssue(issue)
43
- && issue.factoryState !== "done"
44
- && issue.factoryState !== "failed"
45
- && issue.factoryState !== "escalated"));
40
+ const existing = this.findExistingMainRepair(projectId, branchName);
46
41
  const summary = await this.readMainBranchFailure(project.github.repoFullName, baseBranch);
47
42
  if (!summary) {
48
43
  if (existing) {
@@ -114,6 +109,33 @@ export class MainBranchHealthMonitor {
114
109
  detail: summary.failingChecks.map((check) => check.name).join(", "),
115
110
  });
116
111
  }
112
+ findExistingMainRepair(projectId, branchName) {
113
+ const candidates = this.db.listIssues()
114
+ .filter((issue) => (issue.projectId === projectId
115
+ && issue.branchName === branchName
116
+ && isMainRepairIssue(issue)
117
+ && issue.factoryState !== "done"))
118
+ .sort((left, right) => this.compareMainRepairCandidates(left, right));
119
+ return candidates[0];
120
+ }
121
+ compareMainRepairCandidates(left, right) {
122
+ const leftPriority = this.rankMainRepairCandidate(left);
123
+ const rightPriority = this.rankMainRepairCandidate(right);
124
+ if (leftPriority !== rightPriority)
125
+ return leftPriority - rightPriority;
126
+ return Date.parse(right.updatedAt) - Date.parse(left.updatedAt);
127
+ }
128
+ rankMainRepairCandidate(issue) {
129
+ if (issue.activeRunId !== undefined)
130
+ return 0;
131
+ if (issue.prState === "open" || issue.factoryState === "awaiting_queue" || issue.factoryState === "pr_open")
132
+ return 1;
133
+ if (issue.factoryState === "delegated" || issue.factoryState === "implementing")
134
+ return 2;
135
+ if (issue.factoryState === "failed" || issue.factoryState === "escalated")
136
+ return 3;
137
+ return 4;
138
+ }
117
139
  queueExistingMainRepair(issue, summary, priorityLabel) {
118
140
  if (issue.activeRunId !== undefined)
119
141
  return;
@@ -121,6 +143,15 @@ export class MainBranchHealthMonitor {
121
143
  return;
122
144
  if (issue.prState === "open" || issue.factoryState === "awaiting_queue" || issue.factoryState === "pr_open")
123
145
  return;
146
+ this.db.upsertIssue({
147
+ projectId: issue.projectId,
148
+ linearIssueId: issue.linearIssueId,
149
+ delegatedToPatchRelay: true,
150
+ factoryState: "delegated",
151
+ pendingRunType: null,
152
+ pendingRunContextJson: null,
153
+ activeRunId: null,
154
+ });
124
155
  this.db.issueSessions.appendIssueSessionEventRespectingActiveLease(issue.projectId, issue.linearIssueId, {
125
156
  projectId: issue.projectId,
126
157
  linearIssueId: issue.linearIssueId,
@@ -131,6 +162,7 @@ export class MainBranchHealthMonitor {
131
162
  failingChecks: summary.failingChecks,
132
163
  pendingChecks: summary.pendingChecks,
133
164
  priorityLabel,
165
+ promptContext: buildMainRepairPromptContext(this.config.projects.find((project) => project.id === issue.projectId) ?? { id: issue.projectId }, summary, priorityLabel),
134
166
  }),
135
167
  dedupeKey: `main_repair:${issue.projectId}:${summary.baseSha}:${summary.failingChecks.map((check) => check.name).join("|")}`,
136
168
  });
@@ -323,6 +323,23 @@ function buildCiRepairContext(context) {
323
323
  : "",
324
324
  ].filter(Boolean).join("\n");
325
325
  }
326
+ function buildMainRepairContext(context) {
327
+ const failingCheckNames = Array.isArray(context?.failingChecks)
328
+ ? context.failingChecks
329
+ .filter((entry) => Boolean(entry) && typeof entry === "object")
330
+ .map((entry) => String(entry.name ?? "").trim())
331
+ .filter((name) => name.length > 0)
332
+ : [];
333
+ return [
334
+ "Base-branch repair on the red mainline.",
335
+ "Goal: restore main by fixing the real persistent failure, not by papering over a transient runner incident.",
336
+ "Before changing code or workflow config, verify that the original incident still persists on the exact failing main SHA or identify a concrete log signature that justifies the fix.",
337
+ "For transient infrastructure symptoms such as disk pressure, runner exhaustion, or network flakiness, prefer a rerun-only repair if the rerun clears the branch.",
338
+ "Do not propose or implement moving CI, deploy, or tests onto different nodes or runner pools unless a human explicitly asked for that infrastructure migration.",
339
+ context?.baseSha ? `Failing main SHA: ${String(context.baseSha)}` : "",
340
+ failingCheckNames.length > 0 ? `Failing checks: ${failingCheckNames.join(", ")}` : "",
341
+ ].filter(Boolean).join("\n");
342
+ }
326
343
  function appendQueueRepairContext(lines, context) {
327
344
  const queueContext = context?.mergeQueueContext;
328
345
  if (!queueContext || typeof queueContext !== "object") {
@@ -433,6 +450,9 @@ function buildCurrentContext(runType, issue, context, followUp = false) {
433
450
  }
434
451
  lines.push(...buildHumanContextLines(context));
435
452
  switch (runType) {
453
+ case "main_repair":
454
+ lines.push(buildMainRepairContext(context));
455
+ break;
436
456
  case "ci_repair":
437
457
  lines.push(buildCiRepairContext(context));
438
458
  break;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "patchrelay",
3
- "version": "0.52.3",
3
+ "version": "0.52.5",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "repository": {