patchrelay 0.50.0 → 0.50.2
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/README.md +2 -2
- package/config/patchrelay.example.json +2 -1
- package/dist/build-info.json +3 -3
- package/dist/cli/cluster-health.js +66 -4
- package/dist/completion-check.js +6 -0
- package/dist/config.js +32 -12
- package/dist/issue-class.js +5 -25
- package/dist/prompting/patchrelay.js +102 -156
- package/dist/run-budgets.js +3 -3
- package/dist/webhooks/desired-stage-recorder.js +9 -7
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -61,12 +61,12 @@ patchrelay service status
|
|
|
61
61
|
patchrelay dashboard
|
|
62
62
|
```
|
|
63
63
|
|
|
64
|
-
Each repo needs two workflow files
|
|
64
|
+
Each repo needs two workflow files for repo-specific run behavior:
|
|
65
65
|
|
|
66
66
|
- `IMPLEMENTATION_WORKFLOW.md` — implementation, CI repair, queue repair runs
|
|
67
67
|
- `REVIEW_WORKFLOW.md` — review fix runs
|
|
68
68
|
|
|
69
|
-
Keep them short, action-oriented, human-authored. See [prompting.md](./docs/prompting.md) for how the built-in scaffold composes them
|
|
69
|
+
Keep them short, action-oriented, human-authored. Durable machine-level policy belongs in Codex `developer_instructions`; workflow files are for repo-local behavior and validation. See [prompting.md](./docs/prompting.md) for how the built-in scaffold composes them.
|
|
70
70
|
|
|
71
71
|
Full install, ingress, and GitHub/Linear app setup: [self-hosting.md](./docs/self-hosting.md). Daily ops and CLI cheatsheet: [operator-guide.md](./docs/operator-guide.md).
|
|
72
72
|
|
|
@@ -6,7 +6,8 @@
|
|
|
6
6
|
"git_bin": "git",
|
|
7
7
|
"codex": {
|
|
8
8
|
"model": "gpt-5.3-codex-spark",
|
|
9
|
-
"reasoning_effort": "high"
|
|
9
|
+
"reasoning_effort": "high",
|
|
10
|
+
"developer_instructions": "Keep the public API stable for this installation unless the task explicitly requires a breaking change."
|
|
10
11
|
}
|
|
11
12
|
}
|
|
12
13
|
}
|
package/dist/build-info.json
CHANGED
|
@@ -490,7 +490,13 @@ function deriveCiOwner(params) {
|
|
|
490
490
|
return "paused";
|
|
491
491
|
if (params.factoryState === "changes_requested")
|
|
492
492
|
return "patchrelay";
|
|
493
|
-
if (params.reviewQuillAttempt
|
|
493
|
+
if (params.reviewQuillAttempt?.backlog
|
|
494
|
+
&& params.currentHeadSha
|
|
495
|
+
&& params.reviewQuillAttempt.headSha
|
|
496
|
+
&& params.currentHeadSha !== params.reviewQuillAttempt.headSha) {
|
|
497
|
+
return "review-quill";
|
|
498
|
+
}
|
|
499
|
+
if (params.reviewQuillAttempt && !params.reviewQuillAttempt.backlog)
|
|
494
500
|
return "review-quill";
|
|
495
501
|
if (headAdvancedPastBlockingReview)
|
|
496
502
|
return "reviewer";
|
|
@@ -524,7 +530,10 @@ function describeCiOwnership(params) {
|
|
|
524
530
|
: "PatchRelay owns the next requested-changes move";
|
|
525
531
|
}
|
|
526
532
|
if (params.owner === "review-quill") {
|
|
527
|
-
|
|
533
|
+
if (params.reviewQuillAttempt?.backlog) {
|
|
534
|
+
return "review-quill is actively reconciling this repo; this PR is waiting in the current review backlog";
|
|
535
|
+
}
|
|
536
|
+
return params.reviewQuillAttempt?.id && params.reviewQuillAttempt.status
|
|
528
537
|
? `review-quill attempt #${params.reviewQuillAttempt.id} is ${params.reviewQuillAttempt.status} on the current head`
|
|
529
538
|
: "review-quill owns the current review attempt";
|
|
530
539
|
}
|
|
@@ -576,7 +585,14 @@ function describeCiOwnership(params) {
|
|
|
576
585
|
return "No visible next owner for this PR state";
|
|
577
586
|
}
|
|
578
587
|
function isResolvedDependency(dep) {
|
|
579
|
-
|
|
588
|
+
const stateType = dep.blockerCurrentLinearStateType?.trim().toLowerCase();
|
|
589
|
+
const state = dep.blockerCurrentLinearState?.trim().toLowerCase();
|
|
590
|
+
return stateType === "completed"
|
|
591
|
+
|| stateType === "canceled"
|
|
592
|
+
|| stateType === "cancelled"
|
|
593
|
+
|| state === "done"
|
|
594
|
+
|| state === "canceled"
|
|
595
|
+
|| state === "cancelled";
|
|
580
596
|
}
|
|
581
597
|
function needsReviewAutomation(issue) {
|
|
582
598
|
if (issue.factoryState === "awaiting_queue" || issue.factoryState === "done") {
|
|
@@ -586,6 +602,7 @@ function needsReviewAutomation(issue) {
|
|
|
586
602
|
}
|
|
587
603
|
async function collectReviewQuillAttemptOwners(snapshots, config, runCommand) {
|
|
588
604
|
const owners = new Map();
|
|
605
|
+
const repoBacklog = await probeReviewQuillRepoBacklog(runCommand);
|
|
589
606
|
for (const snapshot of snapshots) {
|
|
590
607
|
const issueKey = snapshot.issue.issueKey;
|
|
591
608
|
const prNumber = snapshot.issue.prNumber;
|
|
@@ -601,8 +618,12 @@ async function collectReviewQuillAttemptOwners(snapshots, config, runCommand) {
|
|
|
601
618
|
const activeAttempt = probe.attempts.find((attempt) => (attempt.status === "queued" || attempt.status === "running")
|
|
602
619
|
&& !attempt.stale
|
|
603
620
|
&& attempt.headSha === probe.currentHeadSha);
|
|
604
|
-
if (!activeAttempt)
|
|
621
|
+
if (!activeAttempt) {
|
|
622
|
+
if (repoBacklog.has(repoFullName)) {
|
|
623
|
+
owners.set(issueKey, { backlog: true, headSha: probe.latestAttemptHeadSha });
|
|
624
|
+
}
|
|
605
625
|
continue;
|
|
626
|
+
}
|
|
606
627
|
owners.set(issueKey, {
|
|
607
628
|
id: activeAttempt.id,
|
|
608
629
|
status: activeAttempt.status,
|
|
@@ -611,6 +632,42 @@ async function collectReviewQuillAttemptOwners(snapshots, config, runCommand) {
|
|
|
611
632
|
}
|
|
612
633
|
return owners;
|
|
613
634
|
}
|
|
635
|
+
async function probeReviewQuillRepoBacklog(runCommand) {
|
|
636
|
+
let result;
|
|
637
|
+
try {
|
|
638
|
+
result = await runCommand("review-quill", ["status", "--json"]);
|
|
639
|
+
}
|
|
640
|
+
catch {
|
|
641
|
+
return new Set();
|
|
642
|
+
}
|
|
643
|
+
if (result.exitCode !== 0) {
|
|
644
|
+
return new Set();
|
|
645
|
+
}
|
|
646
|
+
const parsed = safeJsonParse(result.stdout);
|
|
647
|
+
if (!parsed || parsed.runtime?.reconcileInProgress !== true || !Array.isArray(parsed.repos)) {
|
|
648
|
+
return new Set();
|
|
649
|
+
}
|
|
650
|
+
const activeRepos = new Set();
|
|
651
|
+
for (const repo of parsed.repos) {
|
|
652
|
+
if (!repo || typeof repo !== "object")
|
|
653
|
+
continue;
|
|
654
|
+
const repoFullName = typeof repo.repoFullName === "string"
|
|
655
|
+
? String(repo.repoFullName).trim()
|
|
656
|
+
: undefined;
|
|
657
|
+
const runningAttempts = typeof repo.runningAttempts === "number"
|
|
658
|
+
? Number(repo.runningAttempts)
|
|
659
|
+
: 0;
|
|
660
|
+
const queuedAttempts = typeof repo.queuedAttempts === "number"
|
|
661
|
+
? Number(repo.queuedAttempts)
|
|
662
|
+
: 0;
|
|
663
|
+
if (!repoFullName)
|
|
664
|
+
continue;
|
|
665
|
+
if (runningAttempts > 0 || queuedAttempts > 0) {
|
|
666
|
+
activeRepos.add(repoFullName);
|
|
667
|
+
}
|
|
668
|
+
}
|
|
669
|
+
return activeRepos;
|
|
670
|
+
}
|
|
614
671
|
async function collectActiveOverlapFindings(snapshots, runCommand) {
|
|
615
672
|
const findings = [];
|
|
616
673
|
const diffsByProject = new Map();
|
|
@@ -723,6 +780,7 @@ async function probeReviewQuillAttempts(runCommand, repoFullName, prNumber) {
|
|
|
723
780
|
if (!prProbe.ok) {
|
|
724
781
|
return { ok: false, error: prProbe.error };
|
|
725
782
|
}
|
|
783
|
+
let latestAttemptHeadSha;
|
|
726
784
|
const attempts = parsedAttempts.attempts.flatMap((entry) => {
|
|
727
785
|
if (!entry || typeof entry !== "object")
|
|
728
786
|
return [];
|
|
@@ -730,6 +788,9 @@ async function probeReviewQuillAttempts(runCommand, repoFullName, prNumber) {
|
|
|
730
788
|
const headSha = entry.headSha;
|
|
731
789
|
const status = entry.status;
|
|
732
790
|
const stale = entry.stale;
|
|
791
|
+
if (!latestAttemptHeadSha && typeof headSha === "string" && headSha.trim().length > 0) {
|
|
792
|
+
latestAttemptHeadSha = headSha.trim();
|
|
793
|
+
}
|
|
733
794
|
if (typeof id !== "number"
|
|
734
795
|
|| typeof headSha !== "string"
|
|
735
796
|
|| (status !== "queued" && status !== "running")) {
|
|
@@ -745,6 +806,7 @@ async function probeReviewQuillAttempts(runCommand, repoFullName, prNumber) {
|
|
|
745
806
|
return {
|
|
746
807
|
ok: true,
|
|
747
808
|
currentHeadSha: prProbe.pr.headRefOid,
|
|
809
|
+
latestAttemptHeadSha,
|
|
748
810
|
attempts,
|
|
749
811
|
};
|
|
750
812
|
}
|
package/dist/completion-check.js
CHANGED
|
@@ -101,6 +101,12 @@ function buildCompletionCheckPrompt(params) {
|
|
|
101
101
|
'- "done" if the task was successfully completed without a PR.',
|
|
102
102
|
'- "failed" if the run stopped incorrectly and PatchRelay should not auto-continue.',
|
|
103
103
|
"",
|
|
104
|
+
"Bias rules:",
|
|
105
|
+
'- Prefer "continue" when the task looks like normal code-delivery work and the run simply ended before publishing a branch or PR.',
|
|
106
|
+
'- Use "done" only when the issue explicitly permits a no-PR outcome or the deliverable is clearly non-repo work such as planning, orchestration, or follow-up issue creation.',
|
|
107
|
+
'- Use "needs_input" only when one specific missing human decision blocks the very next concrete action. Do not use it just because the branch might need more work.',
|
|
108
|
+
'- If the run appears unfinished but still actionable by PatchRelay, return "continue", not "done".',
|
|
109
|
+
"",
|
|
104
110
|
"Facts:",
|
|
105
111
|
`- Issue: ${params.issue.issueKey ?? params.issue.linearIssueId}`,
|
|
106
112
|
...(params.issue.title ? [`- Title: ${params.issue.title}`] : []),
|
package/dist/config.js
CHANGED
|
@@ -9,6 +9,21 @@ import { ensureAbsolutePath } from "./utils.js";
|
|
|
9
9
|
const LINEAR_OAUTH_CALLBACK_PATH = "/oauth/linear/callback";
|
|
10
10
|
const REPO_SETTINGS_DIRNAME = ".patchrelay";
|
|
11
11
|
const REPO_SETTINGS_FILENAME = "project.json";
|
|
12
|
+
const DEFAULT_PATCHRELAY_DEVELOPER_INSTRUCTIONS = [
|
|
13
|
+
"You are PatchRelay's coding agent.",
|
|
14
|
+
"",
|
|
15
|
+
"Core rules:",
|
|
16
|
+
"- Complete the delegated task in the current repository and worktree.",
|
|
17
|
+
"- Stay inside the issue's scope. Do not widen into unrelated cleanup or polish.",
|
|
18
|
+
"- Use repository docs and workflow files as the source of truth for local conventions.",
|
|
19
|
+
"- Prefer concrete fixes over speculative defenses.",
|
|
20
|
+
"- For code-delivery work, publish before stopping.",
|
|
21
|
+
"- If you change files for an implementation run, commit, push the issue branch, and open or update the PR.",
|
|
22
|
+
"- For repair runs, work on the existing PR branch and do not open a new PR.",
|
|
23
|
+
"- A requested-changes repair is only complete after a newer PR head is pushed, unless a genuine external blocker prevents correct publication.",
|
|
24
|
+
"- If a broader inconsistency is not required to make this task correct, mention it briefly instead of expanding scope.",
|
|
25
|
+
"- Before publishing, do one brief reviewer-minded pass on the current head and fix likely in-scope blockers.",
|
|
26
|
+
].join("\n");
|
|
12
27
|
const trustedActorsSchema = z
|
|
13
28
|
.object({
|
|
14
29
|
ids: z.array(z.string().min(1)).default([]),
|
|
@@ -22,9 +37,9 @@ const repoSettingsSchema = z.object({
|
|
|
22
37
|
branch_prefix: z.string().min(1).optional(),
|
|
23
38
|
});
|
|
24
39
|
const repairBudgetsSchema = z.object({
|
|
25
|
-
ci_repair: z.number().int().positive().default(
|
|
26
|
-
queue_repair: z.number().int().positive().default(
|
|
27
|
-
review_fix: z.number().int().positive().default(
|
|
40
|
+
ci_repair: z.number().int().positive().default(10),
|
|
41
|
+
queue_repair: z.number().int().positive().default(10),
|
|
42
|
+
review_fix: z.number().int().positive().default(10),
|
|
28
43
|
});
|
|
29
44
|
const projectSchema = z.object({
|
|
30
45
|
id: z.string().min(1),
|
|
@@ -37,9 +52,9 @@ const projectSchema = z.object({
|
|
|
37
52
|
trigger_events: z.array(z.string().min(1)).min(1).optional(),
|
|
38
53
|
branch_prefix: z.string().min(1).optional(),
|
|
39
54
|
repair_budgets: repairBudgetsSchema.default({
|
|
40
|
-
ci_repair:
|
|
41
|
-
queue_repair:
|
|
42
|
-
review_fix:
|
|
55
|
+
ci_repair: 10,
|
|
56
|
+
queue_repair: 10,
|
|
57
|
+
review_fix: 10,
|
|
43
58
|
}),
|
|
44
59
|
/** Check names that are review gates (AI Review, quality analysis). Default: code class. */
|
|
45
60
|
review_checks: z.array(z.string().min(1)).default([]),
|
|
@@ -63,9 +78,9 @@ const repositorySchema = z.object({
|
|
|
63
78
|
trigger_events: z.array(z.string().min(1)).min(1).optional(),
|
|
64
79
|
branch_prefix: z.string().min(1).optional(),
|
|
65
80
|
repair_budgets: repairBudgetsSchema.default({
|
|
66
|
-
ci_repair:
|
|
67
|
-
queue_repair:
|
|
68
|
-
review_fix:
|
|
81
|
+
ci_repair: 10,
|
|
82
|
+
queue_repair: 10,
|
|
83
|
+
review_fix: 10,
|
|
69
84
|
}),
|
|
70
85
|
github: z.object({
|
|
71
86
|
webhook_secret: z.string().min(1).optional(),
|
|
@@ -238,6 +253,13 @@ function loadPromptLayer(configDir, layer) {
|
|
|
238
253
|
replaceSections: Object.fromEntries(Object.entries(layer.replace_sections).map(([sectionId, fragmentPath]) => [sectionId, readPromptFile(configDir, fragmentPath)])),
|
|
239
254
|
};
|
|
240
255
|
}
|
|
256
|
+
function mergeDeveloperInstructions(custom) {
|
|
257
|
+
const normalized = custom?.trim();
|
|
258
|
+
if (!normalized) {
|
|
259
|
+
return DEFAULT_PATCHRELAY_DEVELOPER_INSTRUCTIONS;
|
|
260
|
+
}
|
|
261
|
+
return `${DEFAULT_PATCHRELAY_DEVELOPER_INSTRUCTIONS}\n\n## Local Developer Instructions\n\n${normalized}`;
|
|
262
|
+
}
|
|
241
263
|
function expandEnv(value, env) {
|
|
242
264
|
if (typeof value === "string") {
|
|
243
265
|
return value.replace(/\$\{([A-Z0-9_]+)(?::-(.*?))?\}/g, (_match, name, fallback) => {
|
|
@@ -548,9 +570,7 @@ export function loadConfig(configPath = process.env.PATCHRELAY_CONFIG ?? getDefa
|
|
|
548
570
|
...(parsed.runner.codex.reasoning_effort ? { reasoningEffort: parsed.runner.codex.reasoning_effort } : {}),
|
|
549
571
|
...(parsed.runner.codex.service_name ? { serviceName: parsed.runner.codex.service_name } : {}),
|
|
550
572
|
...(parsed.runner.codex.base_instructions ? { baseInstructions: parsed.runner.codex.base_instructions } : {}),
|
|
551
|
-
|
|
552
|
-
? { developerInstructions: parsed.runner.codex.developer_instructions }
|
|
553
|
-
: {}),
|
|
573
|
+
developerInstructions: mergeDeveloperInstructions(parsed.runner.codex.developer_instructions),
|
|
554
574
|
approvalPolicy: parsed.runner.codex.approval_policy,
|
|
555
575
|
sandboxMode: parsed.runner.codex.sandbox_mode,
|
|
556
576
|
persistExtendedHistory: parsed.runner.codex.persist_extended_history,
|
package/dist/issue-class.js
CHANGED
|
@@ -1,35 +1,15 @@
|
|
|
1
|
-
function normalizeText(value) {
|
|
2
|
-
return value?.trim().toLowerCase() ?? "";
|
|
3
|
-
}
|
|
4
|
-
function looksLikeUmbrellaText(issue) {
|
|
5
|
-
const haystack = `${normalizeText(issue.title)}\n${normalizeText(issue.description)}`;
|
|
6
|
-
if (!haystack.trim())
|
|
7
|
-
return false;
|
|
8
|
-
return [
|
|
9
|
-
"umbrella",
|
|
10
|
-
"tracker",
|
|
11
|
-
"tracking",
|
|
12
|
-
"rollout",
|
|
13
|
-
"migration",
|
|
14
|
-
"convergence",
|
|
15
|
-
"audit",
|
|
16
|
-
"follow-up issues",
|
|
17
|
-
"planning/specification issue only",
|
|
18
|
-
].some((token) => haystack.includes(token));
|
|
19
|
-
}
|
|
20
1
|
export function classifyIssue(params) {
|
|
21
|
-
if (params.issue.issueClassSource === "explicit"
|
|
22
|
-
&& (params.issue.issueClass === "implementation" || params.issue.issueClass === "orchestration")) {
|
|
23
|
-
return { issueClass: params.issue.issueClass, issueClassSource: "explicit" };
|
|
24
|
-
}
|
|
25
2
|
if (params.issue.parentLinearIssueId) {
|
|
26
3
|
return { issueClass: "implementation", issueClassSource: "hierarchy" };
|
|
27
4
|
}
|
|
28
5
|
if (params.childIssueCount > 0) {
|
|
6
|
+
if (params.issue.issueClassSource === "explicit" && params.issue.issueClass === "orchestration") {
|
|
7
|
+
return { issueClass: "orchestration", issueClassSource: "explicit" };
|
|
8
|
+
}
|
|
29
9
|
return { issueClass: "orchestration", issueClassSource: "hierarchy" };
|
|
30
10
|
}
|
|
31
|
-
if (
|
|
32
|
-
return { issueClass: "
|
|
11
|
+
if (params.issue.issueClassSource === "explicit" && params.issue.issueClass === "implementation") {
|
|
12
|
+
return { issueClass: "implementation", issueClassSource: "explicit" };
|
|
33
13
|
}
|
|
34
14
|
return { issueClass: "implementation", issueClassSource: "heuristic" };
|
|
35
15
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { existsSync
|
|
1
|
+
import { existsSync } from "node:fs";
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
const WORKFLOW_FILES = {
|
|
4
4
|
implementation: "IMPLEMENTATION_WORKFLOW.md",
|
|
@@ -22,12 +22,10 @@ export const PATCHRELAY_REPLACEABLE_SECTION_IDS = [
|
|
|
22
22
|
"workflow-guidance",
|
|
23
23
|
"publication-contract",
|
|
24
24
|
];
|
|
25
|
-
function
|
|
25
|
+
function hasWorkflowFile(repoPath, runType) {
|
|
26
26
|
const filename = WORKFLOW_FILES[runType];
|
|
27
27
|
const filePath = path.join(repoPath, filename);
|
|
28
|
-
|
|
29
|
-
return undefined;
|
|
30
|
-
return readFileSync(filePath, "utf8").trim();
|
|
28
|
+
return existsSync(filePath);
|
|
31
29
|
}
|
|
32
30
|
function buildPromptHeader(issue) {
|
|
33
31
|
return [
|
|
@@ -102,7 +100,7 @@ function summarizeRelationEntries(entries, options) {
|
|
|
102
100
|
}
|
|
103
101
|
return lines;
|
|
104
102
|
}
|
|
105
|
-
function
|
|
103
|
+
function buildIssueTopology(context) {
|
|
106
104
|
const unresolvedBlockers = Array.isArray(context?.unresolvedBlockers)
|
|
107
105
|
? context.unresolvedBlockers.filter((entry) => Boolean(entry) && typeof entry === "object")
|
|
108
106
|
: [];
|
|
@@ -111,18 +109,10 @@ function buildCoordinationGuidance(context) {
|
|
|
111
109
|
: Array.isArray(context?.trackedDependents)
|
|
112
110
|
? context.trackedDependents.filter((entry) => Boolean(entry) && typeof entry === "object")
|
|
113
111
|
: [];
|
|
114
|
-
const lines = [
|
|
115
|
-
"### Coordination / Issue Topology",
|
|
116
|
-
"",
|
|
117
|
-
"First decide whether this issue should publish code itself or mainly coordinate other issues.",
|
|
118
|
-
"If this issue is a parent tracker, umbrella, migration program, or convergence container and the concrete implementation belongs in child issues, do not create a duplicate umbrella PR.",
|
|
119
|
-
"When child issues already own the concrete code slices, use this issue to coordinate, create or refine follow-up issues, or verify convergence. Only ship code here if this issue still has unique implementation scope that is not already owned elsewhere.",
|
|
120
|
-
"Prefer one PR per concrete implementation issue over a broad parent branch that restates overlapping child work.",
|
|
121
|
-
];
|
|
122
112
|
if (unresolvedBlockers.length === 0 && childIssues.length === 0) {
|
|
123
|
-
return
|
|
113
|
+
return [];
|
|
124
114
|
}
|
|
125
|
-
lines
|
|
115
|
+
const lines = ["### Issue Topology", ""];
|
|
126
116
|
if (unresolvedBlockers.length > 0) {
|
|
127
117
|
lines.push("Unresolved blockers:");
|
|
128
118
|
lines.push(...summarizeRelationEntries(unresolvedBlockers));
|
|
@@ -136,35 +126,25 @@ function buildCoordinationGuidance(context) {
|
|
|
136
126
|
}
|
|
137
127
|
return lines;
|
|
138
128
|
}
|
|
139
|
-
function
|
|
129
|
+
function buildConstraints(issue, context) {
|
|
140
130
|
const description = issue.description?.trim();
|
|
141
131
|
const scope = extractIssueSection(description, "Scope");
|
|
142
132
|
const acceptance = extractIssueSection(description, "Acceptance criteria")
|
|
143
133
|
?? extractIssueSection(description, "Success criteria");
|
|
144
134
|
const relevantCode = extractIssueSection(description, "Relevant code");
|
|
135
|
+
const topology = buildIssueTopology(context);
|
|
145
136
|
return [
|
|
146
|
-
"##
|
|
137
|
+
"## Constraints",
|
|
147
138
|
"",
|
|
148
|
-
"Stay inside the delegated task.",
|
|
149
|
-
"Finish the issue completely enough to satisfy its stated scope and acceptance criteria, but do not widen it into unrelated product polish or follow-up cleanup.",
|
|
150
|
-
"Only broaden to adjacent routes, copy, or supporting surfaces when the issue text or repository guidance explicitly says they are the same user flow.",
|
|
151
|
-
"Your implementation goal is to leave the branch review-ready, not merely locally working: look for likely regressions or invariant breaks in the touched flow before you stop.",
|
|
152
|
-
"If a narrow patch fixes the immediate symptom but leaves the same underlying risk elsewhere in the changed flow, keep going until the branch is likely to survive strict review or clearly explain the blocker.",
|
|
153
|
-
"If you notice a worthwhile broader inconsistency that is not required to make this task correct, mention it in your summary as follow-up context instead of expanding the implementation.",
|
|
139
|
+
"Stay inside the delegated task. Do not widen scope into unrelated cleanup or optional polish.",
|
|
154
140
|
"",
|
|
155
141
|
...(scope ? ["### In Scope", "", scope, ""] : []),
|
|
156
142
|
...(acceptance ? ["### Acceptance / Done", "", acceptance, ""] : []),
|
|
157
143
|
...(relevantCode ? ["### Relevant Code", "", relevantCode, ""] : []),
|
|
158
|
-
...
|
|
159
|
-
"",
|
|
160
|
-
"### Likely Review Invariants",
|
|
161
|
-
"",
|
|
162
|
-
"- Check the surfaces explicitly named in the task before stopping.",
|
|
163
|
-
"- If repository guidance says certain changed surfaces are one flow, verify that shared flow, but do not treat unrelated surrounding cleanup as part of this task.",
|
|
164
|
-
"- A review repair should fix the concrete concern on the current head, not silently expand the Linear issue into a broader rewrite.",
|
|
144
|
+
...topology,
|
|
165
145
|
].join("\n");
|
|
166
146
|
}
|
|
167
|
-
function
|
|
147
|
+
function buildOrchestrationConstraints(context) {
|
|
168
148
|
const unresolvedBlockers = Array.isArray(context?.unresolvedBlockers)
|
|
169
149
|
? context.unresolvedBlockers.filter((entry) => Boolean(entry) && typeof entry === "object")
|
|
170
150
|
: [];
|
|
@@ -174,18 +154,11 @@ function buildOrchestrationScopeDiscipline(context) {
|
|
|
174
154
|
? context.trackedDependents.filter((entry) => Boolean(entry) && typeof entry === "object")
|
|
175
155
|
: [];
|
|
176
156
|
return [
|
|
177
|
-
"##
|
|
157
|
+
"## Constraints",
|
|
178
158
|
"",
|
|
179
|
-
"This issue is orchestration work.",
|
|
180
|
-
"
|
|
181
|
-
"
|
|
182
|
-
"Adopt already-existing canonical child issues when they cover the intended split.",
|
|
183
|
-
"Do not recreate child issues that already exist under this parent unless a genuinely missing required slice remains.",
|
|
184
|
-
"Do not create an overlapping umbrella PR unless this parent clearly owns unique direct cleanup work that child issues do not already cover.",
|
|
185
|
-
"If child work is still in motion, babysit the plan, record useful observations, and return to waiting.",
|
|
186
|
-
"If child work looks delivered, audit whether the original parent goal is actually satisfied.",
|
|
187
|
-
"Create blocking follow-up work only when it is necessary to satisfy the original parent goal.",
|
|
188
|
-
"Prefer non-blocking follow-up issues over keeping the umbrella open for optional polish or adjacent expansion.",
|
|
159
|
+
"This issue is orchestration work. Coordinate convergence instead of duplicating child implementation.",
|
|
160
|
+
"Do not open an overlapping umbrella PR unless this parent owns unique direct work.",
|
|
161
|
+
"Leave later-wave child issues queued unless they are immediately actionable.",
|
|
189
162
|
"",
|
|
190
163
|
"### Child Issue Summaries",
|
|
191
164
|
"",
|
|
@@ -199,29 +172,28 @@ function buildOrchestrationScopeDiscipline(context) {
|
|
|
199
172
|
"### Convergence Rule",
|
|
200
173
|
"",
|
|
201
174
|
"- Close the umbrella when the original parent goal is satisfied.",
|
|
202
|
-
"-
|
|
203
|
-
"- Do not invent optional expansion without explicit human approval.",
|
|
175
|
+
"- Create blocking follow-up work only when it is required to satisfy that goal.",
|
|
204
176
|
].join("\n");
|
|
205
177
|
}
|
|
206
|
-
function
|
|
178
|
+
function buildHumanContextLines(context) {
|
|
207
179
|
const promptContext = typeof context?.promptContext === "string" ? context.promptContext.trim() : "";
|
|
208
180
|
const latestPrompt = typeof context?.promptBody === "string" ? context.promptBody.trim() : "";
|
|
209
181
|
const operatorPrompt = typeof context?.operatorPrompt === "string" ? context.operatorPrompt.trim() : "";
|
|
210
182
|
const userComment = typeof context?.userComment === "string" ? context.userComment.trim() : "";
|
|
211
183
|
const lines = [];
|
|
212
184
|
if (promptContext) {
|
|
213
|
-
lines.push("
|
|
185
|
+
lines.push("Linear session context:", promptContext, "");
|
|
214
186
|
}
|
|
215
187
|
if (latestPrompt) {
|
|
216
|
-
lines.push("
|
|
188
|
+
lines.push("Latest human instruction:", latestPrompt, "");
|
|
217
189
|
}
|
|
218
190
|
if (operatorPrompt) {
|
|
219
|
-
lines.push("
|
|
191
|
+
lines.push("Operator prompt:", operatorPrompt, "");
|
|
220
192
|
}
|
|
221
193
|
if (userComment) {
|
|
222
|
-
lines.push("
|
|
194
|
+
lines.push("Human follow-up comment:", userComment, "");
|
|
223
195
|
}
|
|
224
|
-
return lines
|
|
196
|
+
return lines;
|
|
225
197
|
}
|
|
226
198
|
function resolveRequestedChangesMode(runType, context) {
|
|
227
199
|
if (runType === "branch_upkeep") {
|
|
@@ -298,12 +270,12 @@ function buildRequestedChangesContext(runType, context) {
|
|
|
298
270
|
const mode = resolveRequestedChangesMode(runType, context);
|
|
299
271
|
const lines = [];
|
|
300
272
|
if (mode === "branch_upkeep") {
|
|
301
|
-
lines.push("
|
|
273
|
+
lines.push("Branch upkeep is required on the existing PR branch.", "Goal: restore merge readiness on the current branch and push a newer head without regressing review or CI readiness.");
|
|
302
274
|
}
|
|
303
275
|
else {
|
|
304
276
|
const reviewer = typeof context?.reviewerName === "string" ? context.reviewerName : undefined;
|
|
305
277
|
const reviewBody = typeof context?.reviewBody === "string" ? context.reviewBody.trim() : "";
|
|
306
|
-
lines.push("
|
|
278
|
+
lines.push("Requested changes on the existing PR branch.", "Goal: restore review readiness and push a newer head on the current PR branch.", "Address the real concern behind the feedback and verify nearby invariants in the touched flow before you publish.", reviewer ? `Reviewer: ${reviewer}` : "", reviewBody ? `Review summary:\n${reviewBody}` : "");
|
|
307
279
|
appendStructuredReviewContext(lines, context);
|
|
308
280
|
}
|
|
309
281
|
return lines.join("\n").trim();
|
|
@@ -313,10 +285,8 @@ function buildCiRepairContext(context) {
|
|
|
313
285
|
? context.ciSnapshot
|
|
314
286
|
: undefined;
|
|
315
287
|
return [
|
|
316
|
-
"
|
|
317
|
-
"",
|
|
318
|
-
"Goal: restore CI readiness on the existing PR branch so the next full CI run is likely to pass.",
|
|
319
|
-
"A full CI iteration has settled failed on your PR. Start from the specific failing check/job/step below on the latest remote PR branch tip, but do not stop at a narrow patch if the same root cause is likely to fail other checks in the suite.",
|
|
288
|
+
"Settled CI failure on the existing PR branch.",
|
|
289
|
+
"Goal: restore CI readiness and push a branch that is likely to pass the next full CI run.",
|
|
320
290
|
snapshot?.gateCheckName ? `Gate check: ${String(snapshot.gateCheckName)}` : "",
|
|
321
291
|
snapshot?.gateCheckStatus ? `Gate status: ${String(snapshot.gateCheckStatus)}` : "",
|
|
322
292
|
snapshot?.settledAt ? `Settled at: ${String(snapshot.settledAt)}` : "",
|
|
@@ -332,14 +302,6 @@ function buildCiRepairContext(context) {
|
|
|
332
302
|
Array.isArray(context?.annotations) && context.annotations.length > 0
|
|
333
303
|
? `Annotations:\n${context.annotations.map((entry) => `- ${String(entry)}`).join("\n")}`
|
|
334
304
|
: "",
|
|
335
|
-
"",
|
|
336
|
-
"Fetch the latest remote branch state first. If the branch moved since this failure, restart from the new tip instead of pushing older work.",
|
|
337
|
-
"Read the latest logs for the named failing check, identify the root cause, and check whether that same cause is likely to affect other jobs or checks.",
|
|
338
|
-
"Fix the root cause, not just the first visible symptom.",
|
|
339
|
-
"Do not change workflows, dependency installation, or unrelated tests unless the failing logs clearly point there.",
|
|
340
|
-
"Run the narrowest local verification that gives real confidence for the suite, then commit and push.",
|
|
341
|
-
"Do not open a new PR. Keep working on the existing branch until the branch is likely to pass CI again or the situation is clearly stuck.",
|
|
342
|
-
"Do not change test expectations unless the test is genuinely wrong.",
|
|
343
305
|
].filter(Boolean).join("\n");
|
|
344
306
|
}
|
|
345
307
|
function appendQueueRepairContext(lines, context) {
|
|
@@ -383,67 +345,57 @@ function appendQueueRepairContext(lines, context) {
|
|
|
383
345
|
function buildQueueRepairContext(context) {
|
|
384
346
|
const lines = [];
|
|
385
347
|
appendQueueRepairContext(lines, context);
|
|
386
|
-
lines.push("
|
|
348
|
+
lines.push("Merge queue rejection on the existing PR branch.", "Goal: restore a mergeable branch, verify the queue-blocking fix, and push the existing PR branch.", context?.failureReason ? `Failure reason: ${String(context.failureReason)}` : "");
|
|
387
349
|
return lines.filter(Boolean).join("\n");
|
|
388
350
|
}
|
|
389
|
-
function
|
|
351
|
+
function buildFollowUpContextLines(issue, runType, context) {
|
|
390
352
|
const wakeReason = typeof context?.wakeReason === "string" ? context.wakeReason : undefined;
|
|
391
353
|
const followUps = Array.isArray(context?.followUps) ? context.followUps : [];
|
|
392
354
|
const followUpLines = followUps
|
|
393
355
|
.filter((entry) => Boolean(entry) && typeof entry === "object")
|
|
394
356
|
.map((entry) => `${String(entry.type ?? "follow_up")} from ${String(entry.author ?? "unknown")}: ${String(entry.text ?? "").trim()}`.trim())
|
|
395
357
|
.filter((line) => !line.endsWith(":"));
|
|
396
|
-
const lines = [
|
|
397
|
-
|
|
398
|
-
""
|
|
399
|
-
wakeReason === "
|
|
400
|
-
? "
|
|
401
|
-
: wakeReason === "
|
|
402
|
-
? "
|
|
403
|
-
: wakeReason === "
|
|
404
|
-
? "
|
|
405
|
-
: wakeReason === "
|
|
406
|
-
? "
|
|
407
|
-
: wakeReason === "
|
|
408
|
-
? "
|
|
409
|
-
: wakeReason === "
|
|
410
|
-
? "
|
|
411
|
-
: wakeReason === "
|
|
412
|
-
? "
|
|
413
|
-
: wakeReason === "
|
|
414
|
-
? "
|
|
415
|
-
:
|
|
416
|
-
|
|
417
|
-
: `Why this turn exists: Continue the existing ${runType} run from the latest issue state.`,
|
|
418
|
-
wakeReason === "direct_reply"
|
|
419
|
-
? "Required action now: Apply the latest human answer, continue from the current branch/session context, and publish the next concrete result."
|
|
420
|
-
: wakeReason === "initial_delegate"
|
|
421
|
-
? "Required action now: Inspect the umbrella goal, review the child set, and record the next orchestration step."
|
|
422
|
-
: wakeReason === "completion_check_continue"
|
|
423
|
-
? "Required action now: Continue from the current branch and thread context, finish the task, and publish the next concrete result."
|
|
424
|
-
: "Required action now: Continue from the latest branch state, refresh any stale assumptions, and publish the next concrete result.",
|
|
425
|
-
"",
|
|
426
|
-
];
|
|
358
|
+
const lines = [];
|
|
359
|
+
const turnReason = wakeReason === "direct_reply"
|
|
360
|
+
? "Human reply to the previous question."
|
|
361
|
+
: wakeReason === "initial_delegate"
|
|
362
|
+
? "Initial orchestration turn after delegation."
|
|
363
|
+
: wakeReason === "child_delivered"
|
|
364
|
+
? "A child issue was delivered."
|
|
365
|
+
: wakeReason === "child_changed"
|
|
366
|
+
? "A child issue changed state."
|
|
367
|
+
: wakeReason === "child_regressed"
|
|
368
|
+
? "A child issue regressed."
|
|
369
|
+
: wakeReason === "human_instruction"
|
|
370
|
+
? "A human added new orchestration guidance."
|
|
371
|
+
: wakeReason === "completion_check_continue"
|
|
372
|
+
? "The previous turn ended without a PR and PatchRelay chose to continue automatically."
|
|
373
|
+
: wakeReason === "branch_upkeep"
|
|
374
|
+
? "GitHub still shows the PR branch as needing upkeep."
|
|
375
|
+
: wakeReason === "followup_comment"
|
|
376
|
+
? "A human follow-up comment arrived after the previous turn."
|
|
377
|
+
: `Continue the existing ${runType} run from the latest issue state.`;
|
|
378
|
+
lines.push(`Turn reason: ${turnReason}`);
|
|
427
379
|
if (wakeReason === "completion_check_continue" && typeof context?.completionCheckSummary === "string" && context.completionCheckSummary.trim()) {
|
|
428
|
-
lines.push(`Completion check summary: ${context.completionCheckSummary.trim()}
|
|
380
|
+
lines.push(`Completion check summary: ${context.completionCheckSummary.trim()}`);
|
|
429
381
|
}
|
|
430
382
|
if (followUpLines.length > 0) {
|
|
431
|
-
lines.push("Recent updates:");
|
|
383
|
+
lines.push("", "Recent updates:");
|
|
432
384
|
followUpLines.forEach((line) => lines.push(`- ${line}`));
|
|
433
|
-
lines.push("");
|
|
434
385
|
}
|
|
435
386
|
if (issue.prNumber || issue.prHeadSha || issue.prReviewState || context?.mergeStateStatus) {
|
|
436
|
-
lines.push("
|
|
387
|
+
lines.push("", "Current PR facts:", `Fact freshness: ${context?.githubFactsFresh === true
|
|
437
388
|
? "refreshed immediately before this turn was created."
|
|
438
389
|
: "may now be stale; refresh before making irreversible decisions."}`, issue.prNumber ? `Current PR: #${issue.prNumber}` : "", issue.prHeadSha ? `Current relevant head SHA: ${issue.prHeadSha}` : "", issue.prReviewState ? `Current review state: ${issue.prReviewState}` : "", typeof context?.mergeStateStatus === "string" ? `Merge state against ${String(context?.baseBranch ?? "main")}: ${String(context.mergeStateStatus)}` : "");
|
|
439
390
|
}
|
|
440
|
-
return lines.filter(Boolean)
|
|
391
|
+
return lines.filter(Boolean);
|
|
441
392
|
}
|
|
442
|
-
function
|
|
393
|
+
function buildCurrentContext(runType, issue, context, followUp = false) {
|
|
443
394
|
const lines = [];
|
|
444
395
|
if (followUp) {
|
|
445
|
-
lines.push(
|
|
396
|
+
lines.push(...buildFollowUpContextLines(issue, runType, context), "");
|
|
446
397
|
}
|
|
398
|
+
lines.push(...buildHumanContextLines(context));
|
|
447
399
|
switch (runType) {
|
|
448
400
|
case "ci_repair":
|
|
449
401
|
lines.push(buildCiRepairContext(context));
|
|
@@ -459,71 +411,72 @@ function buildReactiveContext(runType, issue, context, followUp = false) {
|
|
|
459
411
|
break;
|
|
460
412
|
}
|
|
461
413
|
const content = lines.map((line) => line.trimEnd()).join("\n").trim();
|
|
462
|
-
|
|
414
|
+
if (!content.length)
|
|
415
|
+
return undefined;
|
|
416
|
+
return ["## Current Context", "", content].join("\n");
|
|
463
417
|
}
|
|
464
418
|
function buildWorkflowGuidance(repoPath, runType) {
|
|
465
|
-
const
|
|
466
|
-
if (
|
|
467
|
-
return
|
|
468
|
-
|
|
469
|
-
|
|
419
|
+
const filename = WORKFLOW_FILES[runType];
|
|
420
|
+
if (hasWorkflowFile(repoPath, runType)) {
|
|
421
|
+
return [
|
|
422
|
+
"## Workflow",
|
|
423
|
+
"",
|
|
424
|
+
`Read and follow \`${filename}\` in the repository for task-specific behavior before making irreversible changes.`,
|
|
425
|
+
].join("\n");
|
|
470
426
|
}
|
|
471
|
-
return
|
|
427
|
+
return [
|
|
428
|
+
"## Workflow",
|
|
429
|
+
"",
|
|
430
|
+
"Use repository docs and local guidance as the source of truth for task-specific behavior.",
|
|
431
|
+
].join("\n");
|
|
472
432
|
}
|
|
473
433
|
function buildOrchestrationWorkflowGuidance() {
|
|
474
434
|
return [
|
|
475
|
-
"## Workflow
|
|
435
|
+
"## Workflow",
|
|
476
436
|
"",
|
|
477
|
-
"Use the wake reason and
|
|
478
|
-
"Typical orchestration phases are: initial setup, waiting on child progress, reviewing delivered child work, final audit, creating a justified follow-up, or closing the umbrella.",
|
|
437
|
+
"Use the wake reason and child issue summaries to decide the next orchestration step.",
|
|
479
438
|
"Keep outputs concise and observable in Linear.",
|
|
480
439
|
].join("\n");
|
|
481
440
|
}
|
|
441
|
+
function buildPrePushSelfReviewSection(target) {
|
|
442
|
+
const publishTarget = target === "new_pr"
|
|
443
|
+
? "open or update the PR"
|
|
444
|
+
: "push the existing PR branch";
|
|
445
|
+
return [
|
|
446
|
+
"## Final Self-Review Before Push",
|
|
447
|
+
"",
|
|
448
|
+
`Before you ${publishTarget}, do one brief reviewer-minded pass on the current head.`,
|
|
449
|
+
"Fix any likely in-scope blocker you can see now: missing edge-case handling, broken adjacent invariant in the touched flow, mismatch between the PR explanation and the code, or an obviously unreviewable half-finished branch.",
|
|
450
|
+
"Do not widen scope for optional cleanup. If the issue explicitly allows a non-PR outcome, complete that outcome clearly; otherwise publish before stopping.",
|
|
451
|
+
];
|
|
452
|
+
}
|
|
482
453
|
function buildPublicationContract(runType, issueClass) {
|
|
483
454
|
if (issueClass === "orchestration") {
|
|
484
455
|
return [
|
|
485
|
-
"##
|
|
456
|
+
"## Publish",
|
|
486
457
|
"",
|
|
487
|
-
"
|
|
488
|
-
"
|
|
489
|
-
"Valid orchestration outcomes include: recording an observation, updating the rollout plan, creating follow-up issues, opening a small cleanup PR that the parent clearly owns, or closing the umbrella.",
|
|
490
|
-
"If you create new blocking follow-up work, justify it against the original parent goal rather than optional polish.",
|
|
458
|
+
"Publish the orchestration outcome clearly: observation, follow-up issues, rollout update, closeout, or a small parent-owned cleanup PR.",
|
|
459
|
+
"Do not open an overlapping umbrella PR unless this parent owns unique direct work.",
|
|
491
460
|
].join("\n");
|
|
492
461
|
}
|
|
493
462
|
if (runType === "implementation") {
|
|
494
463
|
return [
|
|
495
|
-
"##
|
|
464
|
+
"## Publish",
|
|
496
465
|
"",
|
|
497
|
-
"
|
|
498
|
-
"If the
|
|
499
|
-
"If the worktree already contains relevant changes for this issue, verify them and publish them.",
|
|
500
|
-
"If you changed files for this issue, commit them, push the issue branch, and open or update the PR before stopping.",
|
|
501
|
-
"Do not stop with only local commits or uncommitted changes.",
|
|
466
|
+
"If this is code-delivery work, publish before stopping: commit, push the issue branch, and open or update the PR.",
|
|
467
|
+
"If the issue explicitly allows a non-PR outcome, complete that outcome clearly instead of inventing a PR.",
|
|
502
468
|
"",
|
|
503
|
-
"
|
|
504
|
-
"",
|
|
505
|
-
"When you open or update a PR, shape the body so a strict reviewer can decide in one pass.",
|
|
506
|
-
"",
|
|
507
|
-
"Title: imperative, ≤72 chars. Do not prefix with the issue key — the branch carries it.",
|
|
508
|
-
"",
|
|
509
|
-
"Body sections, in this order. Omit any that do not apply but keep the order:",
|
|
510
|
-
"",
|
|
511
|
-
" ## Why — 1-3 sentences on the problem and motivation.",
|
|
512
|
-
" ## What — ≤5 bullets naming the files or surfaces that change.",
|
|
513
|
-
" ## Tradeoffs — one explicit tradeoff taken, or the single word \"None\".",
|
|
514
|
-
" ## Risks — 1-3 things a strict reviewer would ask about. For each, either fix it before committing or explain why it is acceptable. This section is load-bearing; a strict reviewer reads it first.",
|
|
515
|
-
"",
|
|
516
|
-
"Do not restate the diff in prose. Quote the ambiguous fragment directly if the reader needs to see it.",
|
|
517
|
-
"Do not add a \"Verification\" or \"I ran these commands\" section; CI owns pass/fail and posts check runs the reviewer already sees.",
|
|
469
|
+
...buildPrePushSelfReviewSection("new_pr"),
|
|
518
470
|
].join("\n");
|
|
519
471
|
}
|
|
520
472
|
return [
|
|
521
|
-
"##
|
|
473
|
+
"## Publish",
|
|
522
474
|
"",
|
|
523
|
-
"
|
|
524
|
-
"If you changed files for this repair, commit them and push the same branch before stopping.",
|
|
475
|
+
"Restore and publish on the existing PR branch: commit and push the same branch.",
|
|
525
476
|
"Do not open a new PR.",
|
|
526
|
-
"
|
|
477
|
+
"A PR-less stop is not a successful outcome for a repair run unless a genuine external blocker prevents any correct push.",
|
|
478
|
+
"",
|
|
479
|
+
...buildPrePushSelfReviewSection("existing_pr"),
|
|
527
480
|
].join("\n");
|
|
528
481
|
}
|
|
529
482
|
function buildSections(issue, runType, repoPath, context, followUp = false) {
|
|
@@ -531,20 +484,13 @@ function buildSections(issue, runType, repoPath, context, followUp = false) {
|
|
|
531
484
|
const sections = [
|
|
532
485
|
{ id: "header", content: buildPromptHeader(issue) },
|
|
533
486
|
];
|
|
534
|
-
const
|
|
535
|
-
if (followUp && reactiveContext) {
|
|
536
|
-
sections.push({ id: "follow-up-turn", content: reactiveContext });
|
|
537
|
-
}
|
|
487
|
+
const currentContext = buildCurrentContext(runType, issue, context, followUp);
|
|
538
488
|
sections.push({ id: "task-objective", content: buildTaskObjective(issue) }, {
|
|
539
489
|
id: "scope-discipline",
|
|
540
|
-
content: issueClass === "orchestration" ?
|
|
490
|
+
content: issueClass === "orchestration" ? buildOrchestrationConstraints(context) : buildConstraints(issue, context),
|
|
541
491
|
});
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
sections.push({ id: "human-context", content: humanContext });
|
|
545
|
-
}
|
|
546
|
-
if (!followUp && reactiveContext) {
|
|
547
|
-
sections.push({ id: "reactive-context", content: reactiveContext });
|
|
492
|
+
if (currentContext) {
|
|
493
|
+
sections.push({ id: "reactive-context", content: currentContext });
|
|
548
494
|
}
|
|
549
495
|
const workflow = issueClass === "orchestration"
|
|
550
496
|
? buildOrchestrationWorkflowGuidance()
|
package/dist/run-budgets.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
export const DEFAULT_CI_REPAIR_BUDGET =
|
|
2
|
-
export const DEFAULT_QUEUE_REPAIR_BUDGET =
|
|
3
|
-
export const DEFAULT_REVIEW_FIX_BUDGET =
|
|
1
|
+
export const DEFAULT_CI_REPAIR_BUDGET = 10;
|
|
2
|
+
export const DEFAULT_QUEUE_REPAIR_BUDGET = 10;
|
|
3
|
+
export const DEFAULT_REVIEW_FIX_BUDGET = 10;
|
|
4
4
|
export function getCiRepairBudget(project) {
|
|
5
5
|
return project?.repairBudgets?.ciRepair ?? DEFAULT_CI_REPAIR_BUDGET;
|
|
6
6
|
}
|
|
@@ -304,13 +304,15 @@ export class DesiredStageRecorder {
|
|
|
304
304
|
? "child_delivered"
|
|
305
305
|
: wasResolved && !isResolved
|
|
306
306
|
? "child_regressed"
|
|
307
|
-
:
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
307
|
+
: undefined;
|
|
308
|
+
if (eventType) {
|
|
309
|
+
wakeOrchestrationParentsForChildEvent({
|
|
310
|
+
db: this.db,
|
|
311
|
+
child: issue,
|
|
312
|
+
eventType,
|
|
313
|
+
changeKind,
|
|
314
|
+
});
|
|
315
|
+
}
|
|
314
316
|
}
|
|
315
317
|
return {
|
|
316
318
|
issue: this.db.issueToTrackedIssue(issue),
|