@open-tomato/rafa 0.33.0 → 0.34.1

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.
@@ -13,7 +13,7 @@ import {
13
13
  trackerPathFor,
14
14
  updateTrackerLine,
15
15
  writeTrackerBlocker
16
- } from "./index-zn08ywsv.js";
16
+ } from "./index-v8d7c6sw.js";
17
17
  import {
18
18
  ACROSS_STORES_ORDER,
19
19
  CORE_ADAPTER_REGISTRY,
@@ -127,7 +127,7 @@ import {
127
127
  writeNames,
128
128
  writePositionFile,
129
129
  writeSqliteStore
130
- } from "./index-kz7e4gtt.js";
130
+ } from "./index-bebv92ha.js";
131
131
  import {
132
132
  CHANGE_LEVELS,
133
133
  CONFIG_DEFAULTS,
@@ -155,7 +155,7 @@ import {
155
155
  requiredPrerequisite,
156
156
  resolveTiers,
157
157
  stampPrompt
158
- } from "./index-dv0ycmp8.js";
158
+ } from "./index-vp0xghk2.js";
159
159
  import {
160
160
  promotable
161
161
  } from "./index-d6d0942q.js";
@@ -8183,6 +8183,11 @@ function createGhPullRequests(options) {
8183
8183
  const text = textArgument(title, "editTitle", "title", false);
8184
8184
  await succeed(["pr", "edit", target, "--title", text], `gh pr edit ${target} --title <title>`);
8185
8185
  },
8186
+ editBase: async (number, base) => {
8187
+ const target = pullNumber(number, "editBase");
8188
+ const branch = textArgument(base, "editBase", "base", false);
8189
+ await succeed(["pr", "edit", target, "--base", branch], `gh pr edit ${target} --base ${branch}`);
8190
+ },
8186
8191
  editBody: async (number, body) => {
8187
8192
  const target = pullNumber(number, "editBody");
8188
8193
  const text = bodyText(body, "editBody");
@@ -14329,7 +14334,7 @@ function withOutlineCache(outline, root) {
14329
14334
  import path2 from "path";
14330
14335
  async function coreRoster() {
14331
14336
  const [{ CORE_REGISTRY }, { describeRegistry }] = await Promise.all([
14332
- import("./index-13caphhp.js"),
14337
+ import("./index-nr4hdb35.js"),
14333
14338
  import("./describe-5hqfwbjc.js")
14334
14339
  ]);
14335
14340
  return describeRegistry(CORE_REGISTRY, version);
@@ -20569,6 +20574,47 @@ function hasBranch(refs, stub) {
20569
20574
  const branch = `${BRANCH_PREFIX2}${stub}`;
20570
20575
  return refs.some((ref7) => ref7 === branch || ref7.endsWith(`/${branch}`));
20571
20576
  }
20577
+ function readBranchTips(sources, remote, stub, note) {
20578
+ const branch = `${BRANCH_PREFIX2}${stub}`;
20579
+ const tips = [];
20580
+ for (const ref7 of [`refs/heads/${branch}`, `refs/remotes/${remote}/${branch}`]) {
20581
+ const resolved = sources.git(["rev-parse", "--verify", "--quiet", `${ref7}^{commit}`]);
20582
+ const said = gitSaid(resolved);
20583
+ if (resolved.ok && resolved.stdout.trim() !== "") {
20584
+ tips.push(ref7);
20585
+ } else if (said !== "") {
20586
+ note(`\`${ref7}\` could not be read, so its plan reads as started: ${said}`);
20587
+ return null;
20588
+ }
20589
+ }
20590
+ return tips;
20591
+ }
20592
+ function readBranchMessages(sources, ref7, note) {
20593
+ const result = sources.git(["log", "-z", "--format=%B", `${sources.base}..${ref7}`]);
20594
+ if (!result.ok) {
20595
+ note(`the commits of \`${ref7}\` past \`${sources.base}\` could not be read, so its plan reads as started: ${gitSaid(result)}`);
20596
+ return null;
20597
+ }
20598
+ return result.stdout.split("\x00").map((message) => message.trim()).filter((message) => message !== "");
20599
+ }
20600
+ function claimsOnly(messages) {
20601
+ return messages.length > 0 && messages.every((message) => parseClaimMessage(message).kind === "ownership");
20602
+ }
20603
+ function hasStartedBranch(sources, remote, refs, stub, note) {
20604
+ if (!hasBranch(refs, stub))
20605
+ return false;
20606
+ const tips = readBranchTips(sources, remote, stub, note);
20607
+ if (tips === null)
20608
+ return true;
20609
+ if (tips.length === 0) {
20610
+ note(`\`${BRANCH_PREFIX2}${stub}\` is on ${remote} but not fetched here, so its commits were not read and its plan reads as started`);
20611
+ return true;
20612
+ }
20613
+ return tips.some((ref7) => {
20614
+ const messages = readBranchMessages(sources, ref7, note);
20615
+ return messages === null || !claimsOnly(messages);
20616
+ });
20617
+ }
20572
20618
  function openWorld(sources) {
20573
20619
  let problems = [];
20574
20620
  const note = (problem) => {
@@ -20593,7 +20639,7 @@ function openWorld(sources) {
20593
20639
  tracked: once2(() => readTracked(sources, note)),
20594
20640
  checkout: once2(() => readCheckout(sources, note)),
20595
20641
  branchPlan: once2(() => readBranchPlan(branch(), plans())),
20596
- unstartedPlan: once2(() => plans().find((plan) => !hasRun(runs(), plan.stub) && !hasBranch(refs(), plan.stub)) ?? null),
20642
+ unstartedPlan: once2(() => plans().find((plan) => !hasRun(runs(), plan.stub) && !hasStartedBranch(sources, remote, refs(), plan.stub, note)) ?? null),
20597
20643
  openPull: once2(() => readOpenPull(sources, branch(), note)),
20598
20644
  roadmap,
20599
20645
  picked: once2(async () => {
@@ -21674,7 +21720,7 @@ var ready_default = createIssueReadyCommand();
21674
21720
 
21675
21721
  // src/start.ts
21676
21722
  import fs from "fs";
21677
- import { homedir as homedir8 } from "os";
21723
+ import { homedir as homedir9 } from "os";
21678
21724
  import path4 from "path";
21679
21725
  import { fileURLToPath as fileURLToPath2 } from "url";
21680
21726
 
@@ -23761,6 +23807,26 @@ async function handOut(handout, task, resolution, repoRoot) {
23761
23807
  return { resolver: handout.resolver, skills, lessons };
23762
23808
  }
23763
23809
 
23810
+ // src/start/inherited-notice.ts
23811
+ var INHERITED_HEADING = "## Failures this run inherited";
23812
+ var INHERITED_SENTENCE = "These tests were already failing when this run started. " + "They need no proof that they predate your task: do not run `git stash` or check out a clean worktree of `main` to show it. " + "A bug you report for one of them, by its file and case, is recognised as inherited and files nothing.";
23813
+ var INHERITED_LIST_CAP = 20;
23814
+ function oneLine7(text) {
23815
+ return text.replace(/\s+/g, " ").trim();
23816
+ }
23817
+ function failureLine(failure3) {
23818
+ return `- \`${oneLine7(failure3.file)}\`: ${oneLine7(failure3.name)}`;
23819
+ }
23820
+ function renderInheritedSection(failures) {
23821
+ if (failures.length === 0)
23822
+ return "";
23823
+ const listed = failures.slice(0, INHERITED_LIST_CAP).map(failureLine);
23824
+ const rest = failures.length - listed.length;
23825
+ const more = rest > 0 ? [`- ...and ${rest} more.`] : [];
23826
+ return [INHERITED_HEADING, INHERITED_SENTENCE, ...listed, ...more].join(`
23827
+ `);
23828
+ }
23829
+
23764
23830
  // src/start/preflight.ts
23765
23831
  import { randomUUID as randomUUID7 } from "crypto";
23766
23832
  import { existsSync as existsSync27, readFileSync as readFileSync33 } from "fs";
@@ -24584,16 +24650,16 @@ function baseLines(base) {
24584
24650
  }
24585
24651
  var NO_TASK_SECTIONS = { skills: "", lessons: "" };
24586
24652
  function sectionLines(sections2) {
24587
- return [sections2.skills, sections2.lessons].filter((section) => section.trim().length > 0).flatMap((section) => [section, ""]);
24653
+ return [sections2.skills, sections2.lessons, sections2.inherited].filter((section) => section.trim().length > 0).flatMap((section) => [section, ""]);
24588
24654
  }
24589
- function buildTaskPrompt(taskText, promptContent, planText, knownMissing = [], blocker = null, sections2 = NO_TASK_SECTIONS, base = null) {
24655
+ function buildTaskPrompt(taskText, promptContent, planText, knownMissing = [], blocker = null, sections2 = NO_TASK_SECTIONS, base = null, inherited = []) {
24590
24656
  return [
24591
24657
  `Your scoped task is: ${taskText}`,
24592
24658
  "Consider tasks listed above this one in the plan checklist as completed. Do not re-evaluate or re-do them. Focus only on the scoped task.",
24593
24659
  ...blockerLines(blocker),
24594
24660
  ...baseLines(base),
24595
24661
  "",
24596
- ...sectionLines(sections2),
24662
+ ...sectionLines({ ...sections2, inherited: renderInheritedSection(inherited) }),
24597
24663
  promptContent,
24598
24664
  planText,
24599
24665
  ...knownMissingNotice(knownMissing)
@@ -24635,7 +24701,7 @@ async function dispatchTask(options) {
24635
24701
  const resolution = options.serving === null ? EMPTY_RESOLUTION : resolveSessionTiers(options.serving);
24636
24702
  const task = taskInputFor(taskInfo, taskText, declaration, options.planContent);
24637
24703
  const handed = await handOut(options.handout, task, resolution, options.repoRoot);
24638
- const prompt = withStamp(buildTaskPrompt(taskText, options.promptContent, injection.text, options.knownMissing, taskInfo.blocker ?? null, { skills: renderSkillsSection(handed.skills), lessons: renderLessonsSection(handed.lessons) }, options.base));
24704
+ const prompt = withStamp(buildTaskPrompt(taskText, options.promptContent, injection.text, options.knownMissing, taskInfo.blocker ?? null, { skills: renderSkillsSection(handed.skills), lessons: renderLessonsSection(handed.lessons) }, options.base, options.inherited));
24639
24705
  const served = serveForSession(options.serving, resolution);
24640
24706
  const sessionId = (options.newSessionId ?? randomUUID8)();
24641
24707
  const session = await run(prompt, flags, sessionId, options.settingSources, served, options.checkout);
@@ -24770,6 +24836,8 @@ function summaryOf3(event) {
24770
24836
  return `${padKind("no pr")}${oneLine(event.reason)}`;
24771
24837
  case "halt":
24772
24838
  return `${padKind("halt")}${oneLine(event.reason)}`;
24839
+ case "inherited":
24840
+ return `${padKind("inherited")}${oneLine(`${event.file} > ${event.name}`)}`;
24773
24841
  }
24774
24842
  }
24775
24843
  function emitLoopEvent(event, now = () => new Date) {
@@ -26276,6 +26344,9 @@ function baselinePathFor(trackerPath) {
26276
26344
  }
26277
26345
  return join32(dirname11(trackerPath), `${BASELINE_FILE_PREFIX}${match[1] ?? ""}.json`);
26278
26346
  }
26347
+ function copyFailure(failure3) {
26348
+ return failure3.message === undefined ? { file: failure3.file, name: failure3.name } : { file: failure3.file, name: failure3.name, message: failure3.message };
26349
+ }
26279
26350
  function baselineOf(result, recordedAt, commit) {
26280
26351
  return {
26281
26352
  version: BASELINE_VERSION,
@@ -26284,7 +26355,7 @@ function baselineOf(result, recordedAt, commit) {
26284
26355
  command: [...result.command],
26285
26356
  exitCode: result.exitCode,
26286
26357
  summary: result.summary,
26287
- failures: result.failures.map((failure3) => ({ file: failure3.file, name: failure3.name })),
26358
+ failures: result.failures.map(copyFailure),
26288
26359
  errors: result.errors,
26289
26360
  junit: result.junit
26290
26361
  };
@@ -26303,7 +26374,8 @@ function isFailure(value) {
26303
26374
  if (typeof value !== "object" || value === null)
26304
26375
  return false;
26305
26376
  const record = value;
26306
- return typeof record["file"] === "string" && typeof record["name"] === "string";
26377
+ const message = record["message"];
26378
+ return typeof record["file"] === "string" && typeof record["name"] === "string" && (message === undefined || typeof message === "string");
26307
26379
  }
26308
26380
  function badField(record) {
26309
26381
  const checks = [
@@ -26503,8 +26575,18 @@ function closeCase(walk) {
26503
26575
  if (open === null || !open.failed)
26504
26576
  return;
26505
26577
  const held = walk.failures.some((failure3) => failure3.file === open.file && failure3.name === open.name);
26506
- if (!held)
26507
- walk.failures.push({ file: open.file, name: open.name });
26578
+ if (held)
26579
+ return;
26580
+ walk.failures.push(open.message === undefined ? { file: open.file, name: open.name } : { file: open.file, name: open.name, message: open.message });
26581
+ }
26582
+ function firstMessageLine(message) {
26583
+ const line = message?.split(/\r?\n/, 1)[0]?.trim() ?? "";
26584
+ return line === "" ? undefined : line;
26585
+ }
26586
+ function failCase(open, attributes) {
26587
+ open.failed = true;
26588
+ if (open.message === undefined)
26589
+ open.message = firstMessageLine(attributes["message"]);
26508
26590
  }
26509
26591
  function openCase(walk, attributes) {
26510
26592
  const [fileSuite, ...describes] = walk.suites;
@@ -26531,7 +26613,7 @@ function takeTag(walk, match) {
26531
26613
  return;
26532
26614
  }
26533
26615
  if ((element === "failure" || element === "error") && closing !== "/" && walk.open !== null)
26534
- walk.open.failed = true;
26616
+ failCase(walk.open, attributes);
26535
26617
  }
26536
26618
  function parseJunitFailures(xml) {
26537
26619
  if (!/<testsuites\b/.test(xml) || !xml.includes("</testsuites>"))
@@ -27108,9 +27190,266 @@ function createRunSuiteSteps(options) {
27108
27190
  return { beforeSession, afterTask };
27109
27191
  }
27110
27192
 
27193
+ // src/start/triage.ts
27194
+ import { homedir as homedir8 } from "os";
27195
+
27196
+ // src/triage/bug-key.ts
27197
+ import { basename as basename11 } from "path";
27198
+
27199
+ // src/triage/test-failure.ts
27200
+ var TEST_FILE = "(?:/?(?:[\\w.@+-]+/)*)[\\w.@+-]+\\.(?:test|spec)\\.[cm]?[jt]sx?";
27201
+ var TEST_FILE_ANYWHERE = new RegExp(`(?<![\\w.@+/-])${TEST_FILE}(?![\\w])`, "g");
27202
+ var FAIL_LINE = /^\s*\(fail\)\s+(.+?)(?:\s+\[\d+(?:\.\d+)?m?s\])?\s*$/;
27203
+ var FILE_CASE_LINE = new RegExp(`^\\s*(${TEST_FILE})\\s+>\\s+(.+?)\\s*$`);
27204
+ var FILE_HEADER = new RegExp(`^\\s*(${TEST_FILE}):\\s*$`);
27205
+ var NOISE_LINES = [
27206
+ /^\s*$/,
27207
+ FILE_HEADER,
27208
+ /^\s*\d+\s+\|/,
27209
+ /^\s*\^+\s*$/,
27210
+ /^\s*at\s/,
27211
+ /^\s*bun test v/
27212
+ ];
27213
+ var ERROR_PREFIX = /^error:\s+/;
27214
+ function caseLineOf(line, index) {
27215
+ const fail = FAIL_LINE.exec(line);
27216
+ if (fail !== null)
27217
+ return { index, name: fail[1], file: null };
27218
+ const fileCase = FILE_CASE_LINE.exec(line);
27219
+ return fileCase === null ? null : { index, name: fileCase[2], file: fileCase[1] };
27220
+ }
27221
+ function linesOf2(text) {
27222
+ return text.split(/\r?\n/);
27223
+ }
27224
+ function caseLinesOf(lines) {
27225
+ return lines.map((line, index) => caseLineOf(line, index)).filter((found) => found !== null);
27226
+ }
27227
+ function testFilesIn(text) {
27228
+ return text.match(TEST_FILE_ANYWHERE) ?? [];
27229
+ }
27230
+ function firstEvidence(lines) {
27231
+ const found = lines.find((line) => caseLineOf(line, 0) === null && !NOISE_LINES.some((noise) => noise.test(line)));
27232
+ return found === undefined ? null : found.trim().replace(ERROR_PREFIX, "");
27233
+ }
27234
+ function testFileOf(found, artifactLines, what) {
27235
+ if (found.file !== null)
27236
+ return found.file;
27237
+ const above = artifactLines.slice(0, found.index);
27238
+ const header = above.map((line) => FILE_HEADER.exec(line)?.[1]).filter((file) => file !== undefined).at(-1);
27239
+ const before = testFilesIn(above.join(`
27240
+ `));
27241
+ const after = testFilesIn(artifactLines.slice(found.index).join(`
27242
+ `));
27243
+ return header ?? before.at(-1) ?? after[0] ?? testFilesIn(what)[0] ?? null;
27244
+ }
27245
+ function evidenceOf(found, artifactLines) {
27246
+ if (found === null)
27247
+ return firstEvidence(artifactLines);
27248
+ const cases = caseLinesOf(artifactLines);
27249
+ const previous = cases.filter((other) => other.index < found.index).at(-1);
27250
+ const next = cases.find((other) => other.index > found.index);
27251
+ const before = artifactLines.slice(previous === undefined ? 0 : previous.index + 1, found.index);
27252
+ const after = artifactLines.slice(found.index + 1, next === undefined ? artifactLines.length : next.index);
27253
+ return firstEvidence(before) ?? firstEvidence(after);
27254
+ }
27255
+ function testFailureOf(bug) {
27256
+ const artifactLines = linesOf2(bug.artifact ?? "");
27257
+ const what = bug.what ?? "";
27258
+ const inArtifact = caseLinesOf(artifactLines)[0];
27259
+ if (inArtifact !== undefined) {
27260
+ return {
27261
+ file: testFileOf(inArtifact, artifactLines, what),
27262
+ name: inArtifact.name,
27263
+ evidence: evidenceOf(inArtifact, artifactLines)
27264
+ };
27265
+ }
27266
+ const inWhat = caseLinesOf(linesOf2(what))[0];
27267
+ if (inWhat === undefined)
27268
+ return null;
27269
+ return {
27270
+ file: inWhat.file ?? testFilesIn(bug.artifact ?? "")[0] ?? testFilesIn(what)[0] ?? null,
27271
+ name: inWhat.name,
27272
+ evidence: evidenceOf(null, artifactLines)
27273
+ };
27274
+ }
27275
+
27276
+ // src/triage/bug-key.ts
27277
+ var KEY_SEPARATOR = ": ";
27278
+ var CASE_SEPARATOR = " > ";
27279
+ function hasText2(value) {
27280
+ return typeof value === "string" && value.trim().length > 0;
27281
+ }
27282
+ function artifactOf(bug) {
27283
+ return hasText2(bug.artifact) && textProblem(bug.artifact) === null ? bug.artifact : null;
27284
+ }
27285
+ function oneLine8(text) {
27286
+ return text.replace(/\s+/g, " ").trim();
27287
+ }
27288
+ var COMMIT_HASH = /\b[0-9a-f]{7,40}\b/gi;
27289
+ var FOLDER_PREFIX = /[^\s'"`()[\]{}<>,;|]*\//g;
27290
+ var NUMBER = /\d+/g;
27291
+ function strippedText(text) {
27292
+ const taken = text.replace(COMMIT_HASH, "").replace(FOLDER_PREFIX, "").replace(NUMBER, "");
27293
+ return oneLine8(taken);
27294
+ }
27295
+ function keyText(text) {
27296
+ const stripped = strippedText(text);
27297
+ return stripped.length > 0 ? stripped : oneLine8(text);
27298
+ }
27299
+ function testFailureKeyOf(failure3) {
27300
+ const test = failure3.file === null ? oneLine8(failure3.name) : `${basename11(failure3.file)}${CASE_SEPARATOR}${oneLine8(failure3.name)}`;
27301
+ return failure3.evidence === null ? test : `${test}${KEY_SEPARATOR}${keyText(failure3.evidence)}`;
27302
+ }
27303
+ function bugKeyOf(trackerPath, artifact, what = null) {
27304
+ const failure3 = testFailureOf({ what, artifact });
27305
+ return failure3 === null ? `${basename11(trackerPath)}${KEY_SEPARATOR}${keyText(artifact)}` : testFailureKeyOf(failure3);
27306
+ }
27307
+ function legacyBugKeyOf(trackerPath, artifact) {
27308
+ return `${basename11(trackerPath)}${KEY_SEPARATOR}${oneLine8(artifact)}`;
27309
+ }
27310
+
27311
+ // src/triage/inherited.ts
27312
+ import { basename as basename12 } from "path";
27313
+ var CLOSED_ISSUE_STATES = ["done", "released", "cancelled"];
27314
+ function isOpenIssueState(state) {
27315
+ return !CLOSED_ISSUE_STATES.includes(state);
27316
+ }
27317
+ function sameFailure(failure3, baseline) {
27318
+ const { file, evidence } = failure3;
27319
+ if (file === null)
27320
+ return false;
27321
+ const sameTest = basename12(file) === basename12(baseline.file) && oneLine8(failure3.name) === oneLine8(baseline.name);
27322
+ const message = baseline.message ?? null;
27323
+ if (!sameTest || !hasText2(message))
27324
+ return sameTest;
27325
+ return evidence !== null && keyText(evidence).includes(keyText(message));
27326
+ }
27327
+ function inheritedFailureOf(bug, failures) {
27328
+ const failure3 = testFailureOf(bug);
27329
+ if (failure3 === null)
27330
+ return null;
27331
+ return failures.find((baseline) => sameFailure(failure3, baseline)) ?? null;
27332
+ }
27333
+
27334
+ // src/triage/issue-text.ts
27335
+ var TITLE_MAX_LENGTH = 120;
27336
+ var TITLE_CUT = "...";
27337
+ var MIN_FENCE_LENGTH = 3;
27338
+ var ISSUE_OPENING = "An out-of-scope bug a rafa task session reported. The loop filed it and" + " dispatches no task for it: a plan that wants it fixed declares a task.";
27339
+ var COMMENT_OPENING = "Reported again by a rafa task session.";
27340
+ function fenced(value) {
27341
+ const runs = Array.from(value.matchAll(/`+/g), (run) => run[0].length);
27342
+ const fence = "`".repeat(Math.max(MIN_FENCE_LENGTH, ...runs.map((length) => length + 1)));
27343
+ const body = value.endsWith(`
27344
+ `) ? value : `${value}
27345
+ `;
27346
+ return `${fence}
27347
+ ${body}${fence}`;
27348
+ }
27349
+ function issueTitle2(redactedWhat) {
27350
+ const line = oneLine8(redactedWhat);
27351
+ const points = Array.from(line);
27352
+ if (points.length <= TITLE_MAX_LENGTH)
27353
+ return line;
27354
+ const kept = points.slice(0, TITLE_MAX_LENGTH - TITLE_CUT.length).join("");
27355
+ return `${kept.trimEnd()}${TITLE_CUT}`;
27356
+ }
27357
+ function scoreText(score) {
27358
+ return score.toFixed(2);
27359
+ }
27360
+ function similarCommentOpening(score, threshold) {
27361
+ return `${COMMENT_OPENING} No key found this issue: the report's words match its first report` + ` at a Jaccard similarity of ${scoreText(score)}, at or above the threshold of` + ` ${String(threshold)}, so it is taken for the same bug.`;
27362
+ }
27363
+ function possibleDuplicatesSection(duplicates2, redact) {
27364
+ const intro = `No open bug scored at or above the similarity threshold of ${String(duplicates2.threshold)}.`;
27365
+ const listed = duplicates2.nearest.length === 0 ? "No open bug shares a word with this one." : duplicates2.nearest.map(({ issue: issue3, score }) => `- ${issueNameOf(issue3.ref)} (score ${scoreText(score)})`).join(`
27366
+ `);
27367
+ return `## Possible duplicates
27368
+
27369
+ ${redact(`${intro}
27370
+
27371
+ ${listed}`)}`;
27372
+ }
27373
+ function issueNameOf(ref8) {
27374
+ return ref8.url ?? `${ref8.kind} issue ${ref8.externalId}`;
27375
+ }
27376
+ function supersedesSection(closed, redact) {
27377
+ const sentence = `This bug came back after ${issueNameOf(closed)} was closed as completed;` + " this issue is filed in its place.";
27378
+ return `## Supersedes
27379
+
27380
+ ${redact(sentence)}`;
27381
+ }
27382
+ function section(heading, value, absent2, redact) {
27383
+ const shown = value === null ? absent2 : fenced(redact(value));
27384
+ return `## ${heading}
27385
+
27386
+ ${shown}`;
27387
+ }
27388
+ function issueText(opening, values, redact) {
27389
+ return [
27390
+ opening,
27391
+ ...values.supersedes === undefined ? [] : [supersedesSection(values.supersedes, redact)],
27392
+ section("What", values.what, "", redact),
27393
+ section("Artifact", values.artifact, "The report gave no artifact, so a recurrence files again.", redact),
27394
+ ...values.refs === null ? [] : [values.refs],
27395
+ section("Recurrence key", values.key, "The report gave no artifact, so this bug has no key.", redact),
27396
+ section("Plan", values.planStub, "The dispatch resolved no plan stub.", redact),
27397
+ section("Task", values.taskText, "", redact),
27398
+ section("Feedback", values.feedback, "The report gave no feedback.", redact),
27399
+ ...values.duplicates === undefined ? [] : [possibleDuplicatesSection(values.duplicates, redact)]
27400
+ ].join(`
27401
+
27402
+ `) + `
27403
+ `;
27404
+ }
27405
+
27406
+ // src/triage/local-paths.ts
27407
+ import { realpathSync as realpathSync5 } from "fs";
27408
+ import { resolve as resolve20 } from "path";
27409
+ var HOME_MARKER = "[redacted: HOME]";
27410
+ var SEGMENT = "[^/\\s'\"`()<>:,;]+";
27411
+ var HOME_PATTERN = `(?:/${SEGMENT})*?/(?:Users|home)/${SEGMENT}`;
27412
+ var PATH_START = "(?:(?<![\\w.\\-])|(?<=(?:^|\\s)-[A-Za-z]))";
27413
+ var UNDER = "(/)(?=[^\\s'\"`()<>])";
27414
+ var PATH_END = "(?![\\w\\-]|\\.[\\w.\\-/])";
27415
+ function literalPattern(text) {
27416
+ return text.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
27417
+ }
27418
+ function forms(path3) {
27419
+ const given = resolve20(path3);
27420
+ try {
27421
+ const real = realpathSync5(given);
27422
+ return real === given ? [given] : [given, real];
27423
+ } catch {
27424
+ return [given];
27425
+ }
27426
+ }
27427
+ function isRoot(path3) {
27428
+ return path3.length > 1;
27429
+ }
27430
+ function localPathRedactor(repoRoot, home) {
27431
+ const asHome = (pattern) => ({ pattern, under: `${HOME_MARKER}/`, bare: HOME_MARKER });
27432
+ const asRepo = (pattern) => ({ pattern, under: "", bare: "." });
27433
+ const literals = [
27434
+ ...forms(repoRoot).filter(isRoot).map((root) => asRepo(literalPattern(root))),
27435
+ ...forms(home).filter(isRoot).map((root) => asHome(literalPattern(root)))
27436
+ ];
27437
+ const roots = [
27438
+ ...[...literals].sort((a, b) => b.pattern.length - a.pattern.length),
27439
+ asHome(HOME_PATTERN)
27440
+ ];
27441
+ const alternatives = roots.map(({ pattern }) => `(${pattern})(?:${UNDER}|${PATH_END})`);
27442
+ const matcher = new RegExp(`${PATH_START}(?:${alternatives.join("|")})`, "g");
27443
+ return (text) => text.replace(matcher, (...groups) => {
27444
+ const index = roots.findIndex((_, at) => groups[1 + at * 2] !== undefined);
27445
+ const root = roots[index];
27446
+ return groups[2 + index * 2] === undefined ? root.bare : root.under;
27447
+ });
27448
+ }
27449
+
27111
27450
  // src/triage/triage.ts
27112
27451
  import { homedir as homedir7 } from "os";
27113
- import { basename as basename11, join as join35 } from "path";
27452
+ import { join as join35 } from "path";
27114
27453
 
27115
27454
  // src/effort/store/tracker-refs.ts
27116
27455
  import { randomUUID as randomUUID9 } from "crypto";
@@ -27129,10 +27468,11 @@ var INSERT_REF = `
27129
27468
  VALUES (?, ?, ?, ?, ?, ?, ?, ?, ${stampedValues("findings")})
27130
27469
  `;
27131
27470
  var ATTACH_REF = "UPDATE findings SET tracker_ref = ? WHERE seq = ? AND tracker_ref IS NULL";
27132
- var SELECT_OLDEST = `
27471
+ var NEWEST_FIRST = ACROSS_STORES_ORDER.split(",").map((term) => `${term.trim()} DESC`).join(", ");
27472
+ var SELECT_NEWEST = `
27133
27473
  SELECT seq, tracker_ref FROM findings
27134
27474
  WHERE artifact = ? AND tracker_ref IS NOT NULL
27135
- ORDER BY seq
27475
+ ORDER BY ${NEWEST_FIRST}
27136
27476
  LIMIT 1
27137
27477
  `;
27138
27478
  var REFUSAL_TAILS = { write: "nothing written", read: "nothing opened" };
@@ -27205,7 +27545,18 @@ function heldRefOf(path3, seq, text) {
27205
27545
  return stored;
27206
27546
  throw new Error(`effort store: ${path3}: findings row ${seq} holds a tracker_ref ${stored};` + " no writer stores one");
27207
27547
  }
27208
- function placeRef(db, path3, write, wanted, insertValues) {
27548
+
27549
+ class SupersedeInSessionRefusal extends Error {
27550
+ path;
27551
+ seq;
27552
+ constructor(path3, seq) {
27553
+ super(`effort store: ${path3}: tracker ref write supersedes the reference findings row ${seq} holds` + " for this session and artifact, and findings_by_artifact holds one row per session and" + " artifact; nothing written");
27554
+ this.name = "SupersedeInSessionRefusal";
27555
+ this.path = path3;
27556
+ this.seq = seq;
27557
+ }
27558
+ }
27559
+ function placeRef(db, path3, write, wanted, superseded, insertValues) {
27209
27560
  const held = db.query(SELECT_HELD2).get(write.dispatch.sessionId, write.artifact);
27210
27561
  if (held === null) {
27211
27562
  db.query(INSERT_REF).run(...insertValues());
@@ -27216,14 +27567,27 @@ function placeRef(db, path3, write, wanted, insertValues) {
27216
27567
  return ["attached", wanted.ref];
27217
27568
  }
27218
27569
  const kept = heldRefOf(path3, held.seq, held.tracker_ref);
27570
+ if (kept.json === superseded?.json)
27571
+ throw new SupersedeInSessionRefusal(path3, held.seq);
27219
27572
  return kept.json === wanted.json ? ["held", kept.ref] : ["conflict", kept.ref];
27220
27573
  }
27574
+ function supersededOf(write, wanted) {
27575
+ if (write.supersedes === undefined)
27576
+ return null;
27577
+ const superseded = storedRefOf(write.supersedes);
27578
+ if (typeof superseded === "string")
27579
+ throw refused("write", `supersedes a reference ${superseded}`);
27580
+ if (superseded.json === wanted.json)
27581
+ throw refused("write", "supersedes the reference it writes");
27582
+ return superseded;
27583
+ }
27221
27584
  function writeTrackerRef(repoRoot, write, seams = {}) {
27222
27585
  checkDispatch("tracker ref", write.dispatch, write.outcome);
27223
27586
  checkArtifact("write", write.artifact);
27224
27587
  const wanted = storedRefOf(write.ref);
27225
27588
  if (typeof wanted === "string")
27226
27589
  throw refused("write", `has a reference ${wanted}`);
27590
+ const superseded = supersededOf(write, wanted);
27227
27591
  const path3 = sqliteStorePath(repoRoot);
27228
27592
  const { sessionId, planStub: planStub2, taskLine } = write.dispatch;
27229
27593
  const collectedAt = (seams.now ?? (() => new Date))().toISOString();
@@ -27239,7 +27603,7 @@ function writeTrackerRef(repoRoot, write, seams = {}) {
27239
27603
  collectedAt
27240
27604
  ];
27241
27605
  const [action, stored] = withSqliteStore(path3, "write", !existsSync33(path3), (db) => {
27242
- const place = db.transaction(() => placeRef(db, path3, write, wanted, insertValues));
27606
+ const place = db.transaction(() => placeRef(db, path3, write, wanted, superseded, insertValues));
27243
27607
  return place.immediate();
27244
27608
  });
27245
27609
  return { path: path3, action, stored };
@@ -27249,52 +27613,8 @@ function readTrackerRef(repoRoot, artifact) {
27249
27613
  const path3 = sqliteStorePath(repoRoot);
27250
27614
  if (!existsSync33(path3))
27251
27615
  return null;
27252
- const oldest = withSqliteStore(path3, "read", false, (db) => db.query(SELECT_OLDEST).get(artifact));
27253
- return oldest === null ? null : heldRefOf(path3, oldest.seq, oldest.tracker_ref).ref;
27254
- }
27255
-
27256
- // src/triage/local-paths.ts
27257
- import { realpathSync as realpathSync5 } from "fs";
27258
- import { resolve as resolve20 } from "path";
27259
- var HOME_MARKER = "[redacted: HOME]";
27260
- var SEGMENT = "[^/\\s'\"`()<>:,;]+";
27261
- var HOME_PATTERN = `(?:/${SEGMENT})*?/(?:Users|home)/${SEGMENT}`;
27262
- var PATH_START = "(?:(?<![\\w.\\-])|(?<=(?:^|\\s)-[A-Za-z]))";
27263
- var UNDER = "(/)(?=[^\\s'\"`()<>])";
27264
- var PATH_END = "(?![\\w\\-]|\\.[\\w.\\-/])";
27265
- function literalPattern(text) {
27266
- return text.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
27267
- }
27268
- function forms(path3) {
27269
- const given = resolve20(path3);
27270
- try {
27271
- const real = realpathSync5(given);
27272
- return real === given ? [given] : [given, real];
27273
- } catch {
27274
- return [given];
27275
- }
27276
- }
27277
- function isRoot(path3) {
27278
- return path3.length > 1;
27279
- }
27280
- function localPathRedactor(repoRoot, home) {
27281
- const asHome = (pattern) => ({ pattern, under: `${HOME_MARKER}/`, bare: HOME_MARKER });
27282
- const asRepo = (pattern) => ({ pattern, under: "", bare: "." });
27283
- const literals = [
27284
- ...forms(repoRoot).filter(isRoot).map((root) => asRepo(literalPattern(root))),
27285
- ...forms(home).filter(isRoot).map((root) => asHome(literalPattern(root)))
27286
- ];
27287
- const roots = [
27288
- ...[...literals].sort((a, b) => b.pattern.length - a.pattern.length),
27289
- asHome(HOME_PATTERN)
27290
- ];
27291
- const alternatives = roots.map(({ pattern }) => `(${pattern})(?:${UNDER}|${PATH_END})`);
27292
- const matcher = new RegExp(`${PATH_START}(?:${alternatives.join("|")})`, "g");
27293
- return (text) => text.replace(matcher, (...groups) => {
27294
- const index = roots.findIndex((_, at) => groups[1 + at * 2] !== undefined);
27295
- const root = roots[index];
27296
- return groups[2 + index * 2] === undefined ? root.bare : root.under;
27297
- });
27616
+ const newest = withSqliteStore(path3, "read", false, (db) => db.query(SELECT_NEWEST).get(artifact));
27617
+ return newest === null ? null : heldRefOf(path3, newest.seq, newest.tracker_ref).ref;
27298
27618
  }
27299
27619
 
27300
27620
  // src/triage/refs-section.ts
@@ -27358,17 +27678,72 @@ function createArtifactRefsVerifier(repoRoot, env) {
27358
27678
  };
27359
27679
  }
27360
27680
 
27681
+ // src/triage/similarity.ts
27682
+ import { basename as basename13 } from "path";
27683
+ var NON_WORD2 = /\W+/;
27684
+ var FENCE_LINE6 = /^`{3,}$/;
27685
+ var HEADING_LINE3 = /^## (.+)$/;
27686
+ function wordSetOf(text) {
27687
+ const words = strippedText(text).toLowerCase().split(NON_WORD2).filter((word) => word.length > 0);
27688
+ return new Set(words);
27689
+ }
27690
+ function reportWordsOf(report2) {
27691
+ return wordSetOf(`${report2.what ?? ""}
27692
+ ${report2.artifact ?? ""}`);
27693
+ }
27694
+ function jaccard(a, b) {
27695
+ const shared = [...a].filter((word) => b.has(word)).length;
27696
+ const all = a.size + b.size - shared;
27697
+ return all === 0 ? 0 : shared / all;
27698
+ }
27699
+ function fencedValueAt(lines, start) {
27700
+ const open = lines.slice(start).findIndex((line) => line.trim().length > 0);
27701
+ if (open === -1)
27702
+ return null;
27703
+ const fence = lines[start + open];
27704
+ if (!FENCE_LINE6.test(fence))
27705
+ return null;
27706
+ const body = lines.slice(start + open + 1);
27707
+ const close = body.indexOf(fence);
27708
+ return close === -1 ? null : body.slice(0, close).join(`
27709
+ `);
27710
+ }
27711
+ function sectionValueOf(body, heading) {
27712
+ const lines = body.split(/\r?\n/);
27713
+ const at = lines.findIndex((line) => HEADING_LINE3.exec(line)?.[1]?.trim() === heading);
27714
+ return at === -1 ? null : fencedValueAt(lines, at + 1);
27715
+ }
27716
+ function issueTextOf(body) {
27717
+ return {
27718
+ what: sectionValueOf(body, "What"),
27719
+ artifact: sectionValueOf(body, "Artifact")
27720
+ };
27721
+ }
27722
+ function testFileOf2(text) {
27723
+ const file = testFailureOf(text)?.file ?? testFilesIn(text.artifact ?? "")[0] ?? testFilesIn(text.what ?? "")[0] ?? null;
27724
+ return file === null ? null : basename13(file);
27725
+ }
27726
+ function namesOtherTestFile(reportFile, issueFile) {
27727
+ return reportFile !== null && issueFile !== null && reportFile !== issueFile;
27728
+ }
27729
+ function nearestOpenBugs(report2, openBugs) {
27730
+ const words = reportWordsOf(report2);
27731
+ const reportFile = testFileOf2(report2);
27732
+ const scored = openBugs.flatMap((issue3) => {
27733
+ const text = issueTextOf(issue3.body);
27734
+ if (namesOtherTestFile(reportFile, testFileOf2(text)))
27735
+ return [];
27736
+ const score = jaccard(words, reportWordsOf(text));
27737
+ return score > 0 ? [{ issue: issue3, score }] : [];
27738
+ });
27739
+ return [...scored].sort((a, b) => b.score - a.score);
27740
+ }
27741
+
27361
27742
  // src/triage/triage.ts
27362
27743
  var PRIVATE_TRIAGE_DIR = join35(".rafa", "triage", "private");
27363
27744
  var SECRET_ENV_NAMES = ["GITHUB_TOKEN", "ANTHROPIC_API_KEY", "LINEAR_API_KEY"];
27364
27745
  var TRIAGE_MODULE = "unassigned";
27365
27746
  var BLOCKER_SEPARATOR = "; ";
27366
- var KEY_SEPARATOR = ": ";
27367
- var TITLE_MAX_LENGTH = 120;
27368
- var TITLE_CUT = "...";
27369
- var MIN_FENCE_LENGTH = 3;
27370
- var ISSUE_OPENING = "An out-of-scope bug a rafa task session reported. The loop filed it and" + " dispatches no task for it: a plan that wants it fixed declares a task.";
27371
- var COMMENT_OPENING = "Reported again by a rafa task session.";
27372
27747
  function privateTriageDir(repoRoot) {
27373
27748
  return join35(repoRoot, PRIVATE_TRIAGE_DIR);
27374
27749
  }
@@ -27405,9 +27780,6 @@ function redactSecrets(text, secrets) {
27405
27780
  const pattern = new RegExp(values.map(literalPattern2).join("|"), "g");
27406
27781
  return text.replace(pattern, (value) => `[redacted: ${nameByValue.get(value)}]`);
27407
27782
  }
27408
- function hasText2(value) {
27409
- return typeof value === "string" && value.trim().length > 0;
27410
- }
27411
27783
  function blockerTextOf(blockers) {
27412
27784
  const texts = blockers.map((blocker) => blocker.what).filter(hasText2);
27413
27785
  return texts.length === 0 ? null : texts.join(BLOCKER_SEPARATOR);
@@ -27426,57 +27798,11 @@ function triageBlockers(options) {
27426
27798
  return { text, written: false, problem: `the blocker was not written: ${messageOf(error)}` };
27427
27799
  }
27428
27800
  }
27429
- function artifactOf(bug) {
27430
- return hasText2(bug.artifact) && textProblem(bug.artifact) === null ? bug.artifact : null;
27431
- }
27432
- function oneLine7(text) {
27433
- return text.replace(/\s+/g, " ").trim();
27434
- }
27435
- function bugKeyOf(trackerPath, artifact) {
27436
- return `${basename11(trackerPath)}${KEY_SEPARATOR}${oneLine7(artifact)}`;
27437
- }
27438
- function fenced(value) {
27439
- const runs = Array.from(value.matchAll(/`+/g), (run) => run[0].length);
27440
- const fence = "`".repeat(Math.max(MIN_FENCE_LENGTH, ...runs.map((length) => length + 1)));
27441
- const body = value.endsWith(`
27442
- `) ? value : `${value}
27443
- `;
27444
- return `${fence}
27445
- ${body}${fence}`;
27446
- }
27447
- function issueTitle2(redactedWhat) {
27448
- const line = oneLine7(redactedWhat);
27449
- const points = Array.from(line);
27450
- if (points.length <= TITLE_MAX_LENGTH)
27451
- return line;
27452
- const kept = points.slice(0, TITLE_MAX_LENGTH - TITLE_CUT.length).join("");
27453
- return `${kept.trimEnd()}${TITLE_CUT}`;
27454
- }
27455
- function section(heading, value, absent2, redact) {
27456
- const shown = value === null ? absent2 : fenced(redact(value));
27457
- return `## ${heading}
27458
-
27459
- ${shown}`;
27460
- }
27461
- function issueText(opening, values, redact) {
27462
- return [
27463
- opening,
27464
- section("What", values.what, "", redact),
27465
- section("Artifact", values.artifact, "The report gave no artifact, so a recurrence files again.", redact),
27466
- ...values.refs === null ? [] : [values.refs],
27467
- section("Recurrence key", values.key, "The report gave no artifact, so this bug has no key.", redact),
27468
- section("Plan", values.planStub, "The dispatch resolved no plan stub.", redact),
27469
- section("Task", values.taskText, "", redact),
27470
- section("Feedback", values.feedback, "The report gave no feedback.", redact)
27471
- ].join(`
27472
-
27473
- `) + `
27474
- `;
27475
- }
27476
27801
  async function filingFor(what, bug, options, local, redact, verifyRefs) {
27477
27802
  const reported = artifactOf(bug);
27478
27803
  const artifact = reported === null ? null : local(reported);
27479
- const searchText = artifact === null ? null : bugKeyOf(options.trackerPath, redact(artifact));
27804
+ const searchText = artifact === null ? null : bugKeyOf(options.trackerPath, redact(artifact), redact(what));
27805
+ const key = artifact === null ? null : bugKeyOf(options.trackerPath, artifact, local(what));
27480
27806
  const refs = await buildRefsSection(artifact === null ? null : redact(artifact), verifyRefs);
27481
27807
  const values = {
27482
27808
  what,
@@ -27500,10 +27826,23 @@ async function filingFor(what, bug, options, local, redact, verifyRefs) {
27500
27826
  },
27501
27827
  comment: issueText(COMMENT_OPENING, values, redact),
27502
27828
  searchText,
27503
- key: artifact === null ? null : bugKeyOf(options.trackerPath, artifact),
27504
- legacyKey: reported === null || reported === artifact ? null : bugKeyOf(options.trackerPath, reported)
27829
+ key,
27830
+ legacyKeys: legacyKeysOf(options.trackerPath, key, reported, artifact),
27831
+ values,
27832
+ compared: { what: local(what), artifact }
27505
27833
  };
27506
27834
  }
27835
+ function legacyKeysOf(trackerPath, key, reported, artifact) {
27836
+ if (key === null || reported === null || artifact === null)
27837
+ return [];
27838
+ const keys = [legacyBugKeyOf(trackerPath, artifact), legacyBugKeyOf(trackerPath, reported)];
27839
+ return [...new Set(keys)].filter((legacy) => legacy !== key);
27840
+ }
27841
+ function similarityStepOf(options) {
27842
+ const threshold = options.similarity?.triageSimilarityThreshold;
27843
+ const candidates = options.similarity?.triageSimilarityCandidates;
27844
+ return typeof threshold === "number" && typeof candidates === "number" ? { threshold, candidates } : null;
27845
+ }
27507
27846
  async function step(run, name2, action) {
27508
27847
  try {
27509
27848
  return { ok: true, value: await action() };
@@ -27523,47 +27862,110 @@ function resultOf(run, action, fields = {}) {
27523
27862
  ...fields
27524
27863
  };
27525
27864
  }
27526
- async function settle(run, action, ref8, foundBy) {
27865
+ async function settle(run, action, ref8, foundBy, supersedes) {
27527
27866
  const { store } = run.route;
27528
27867
  const { key } = run.filing;
27529
27868
  if (store === null || key === null)
27530
27869
  return resultOf(run, action, { ref: ref8, foundBy });
27531
- const stored = await step(run, "storing the reference", () => store(key, ref8));
27870
+ const stored = await step(run, "storing the reference", () => store(key, ref8, supersedes));
27532
27871
  return stored.ok ? resultOf(run, action, { ref: ref8, foundBy, stored: stored.value }) : resultOf(run, action, { ref: ref8, foundBy, problem: stored.problem });
27533
27872
  }
27534
- async function fileNew(run) {
27535
- const created = await step(run, `the ${run.route.channel} tracker create`, () => run.route.tracker.create(run.filing.draft));
27536
- return created.ok ? settle(run, "filed", created.value, null) : resultOf(run, "failed", { problem: created.problem });
27873
+ async function fileNew(run, extras = {}) {
27874
+ const { filing } = run;
27875
+ const draft = extras.supersedes === undefined && extras.duplicates === undefined ? filing.draft : { ...filing.draft, body: issueText(ISSUE_OPENING, { ...filing.values, ...extras }, run.redact) };
27876
+ const created = await step(run, `the ${run.route.channel} tracker create`, () => run.route.tracker.create(draft));
27877
+ return created.ok ? settle(run, "filed", created.value, null, extras.supersedes) : resultOf(run, "failed", { problem: created.problem });
27537
27878
  }
27538
- async function commentOn(run, ref8, foundBy) {
27539
- const commented = await step(run, `the ${run.route.channel} tracker comment`, () => run.route.tracker.comment(ref8, run.filing.comment));
27879
+ async function commentOn(run, ref8, foundBy, action = "commented", comment = run.filing.comment) {
27880
+ const commented = await step(run, `the ${run.route.channel} tracker comment`, () => run.route.tracker.comment(ref8, comment));
27540
27881
  if (!commented.ok)
27541
27882
  return resultOf(run, "failed", { ref: ref8, foundBy, problem: commented.problem });
27542
- return foundBy === "store" ? resultOf(run, "commented", { ref: ref8, foundBy }) : settle(run, "commented", ref8, foundBy);
27883
+ return foundBy === "store" ? resultOf(run, action, { ref: ref8, foundBy }) : settle(run, action, ref8, foundBy);
27543
27884
  }
27544
- async function triageBug(run) {
27885
+ async function lookUp2(run, key, searchText) {
27545
27886
  const { route, filing } = run;
27546
- const { key, searchText } = filing;
27547
- if (key === null || searchText === null)
27548
- return fileNew(run);
27549
27887
  const { readStored } = route;
27550
27888
  if (readStored !== null) {
27551
- for (const storedKey of [key, filing.legacyKey]) {
27552
- if (storedKey === null)
27553
- continue;
27889
+ for (const storedKey of [key, ...filing.legacyKeys]) {
27554
27890
  const stored = await step(run, "reading the stored reference", () => readStored(storedKey));
27555
27891
  if (!stored.ok)
27556
- return resultOf(run, "failed", { problem: stored.problem });
27892
+ return stored;
27557
27893
  if (stored.value !== null && stored.value.kind === route.tracker.kind) {
27558
- return commentOn(run, stored.value, "store");
27894
+ return { ok: true, value: { ref: stored.value, foundBy: "store" } };
27559
27895
  }
27560
27896
  }
27561
27897
  }
27562
27898
  const found = await step(run, `the ${route.channel} tracker find`, () => route.tracker.find({ text: searchText, type: "bug" }));
27563
27899
  if (!found.ok)
27564
- return resultOf(run, "failed", { problem: found.problem });
27900
+ return found;
27565
27901
  const [match] = found.value;
27566
- return match === undefined ? fileNew(run) : commentOn(run, match, "find");
27902
+ return {
27903
+ ok: true,
27904
+ value: match === undefined ? null : { ref: match, foundBy: "find" }
27905
+ };
27906
+ }
27907
+ var COMPLETED_ISSUE_STATES = ["done", "released"];
27908
+ async function answerRecurrence(run, { ref: ref8, foundBy }) {
27909
+ if (!run.route.readsState)
27910
+ return commentOn(run, ref8, foundBy);
27911
+ const issue3 = await step(run, `the ${run.route.channel} tracker get`, () => run.route.tracker.get(ref8));
27912
+ if (!issue3.ok)
27913
+ return resultOf(run, "failed", { ref: ref8, foundBy, problem: issue3.problem });
27914
+ return COMPLETED_ISSUE_STATES.includes(issue3.value.state) ? fileNew(run, { supersedes: ref8 }) : commentOn(run, ref8, foundBy);
27915
+ }
27916
+ async function commentOnNearest(run, nearest, threshold) {
27917
+ const opening = similarCommentOpening(nearest.score, threshold);
27918
+ const comment = issueText(opening, run.filing.values, run.redact);
27919
+ const result = await commentOn(run, nearest.issue.ref, "similarity", "commented", comment);
27920
+ return { ...result, score: nearest.score };
27921
+ }
27922
+ async function answerMiss(run) {
27923
+ const { tracker, similarity } = run.route;
27924
+ const list = tracker.openIssues;
27925
+ if (similarity === null || list === undefined)
27926
+ return fileNew(run);
27927
+ const listed = await step(run, `the ${run.route.channel} tracker openIssues`, () => list.call(tracker, "bug"));
27928
+ if (!listed.ok)
27929
+ return resultOf(run, "failed", { problem: listed.problem });
27930
+ const nearest = nearestOpenBugs(run.filing.compared, listed.value);
27931
+ const [first] = nearest;
27932
+ return first !== undefined && first.score >= similarity.threshold ? commentOnNearest(run, first, similarity.threshold) : fileNew(run, { duplicates: { threshold: similarity.threshold, nearest: nearest.slice(0, similarity.candidates) } });
27933
+ }
27934
+ async function triageBug(run) {
27935
+ const { key, searchText } = run.filing;
27936
+ if (key === null || searchText === null)
27937
+ return fileNew(run);
27938
+ const found = await lookUp2(run, key, searchText);
27939
+ if (!found.ok)
27940
+ return resultOf(run, "failed", { problem: found.problem });
27941
+ return found.value === null ? answerMiss(run) : answerRecurrence(run, found.value);
27942
+ }
27943
+ async function triageInherited(run, commented) {
27944
+ const { key, searchText } = run.filing;
27945
+ if (key === null || searchText === null || commented.has(key))
27946
+ return resultOf(run, "inherited");
27947
+ const found = await lookUp2(run, key, searchText);
27948
+ if (!found.ok)
27949
+ return resultOf(run, "failed", { problem: found.problem });
27950
+ if (found.value === null)
27951
+ return resultOf(run, "inherited");
27952
+ const { ref: ref8, foundBy } = found.value;
27953
+ const issue3 = await step(run, `the ${run.route.channel} tracker get`, () => run.route.tracker.get(ref8));
27954
+ if (!issue3.ok)
27955
+ return resultOf(run, "failed", { ref: ref8, foundBy, problem: issue3.problem });
27956
+ if (!isOpenIssueState(issue3.value.state))
27957
+ return resultOf(run, "inherited");
27958
+ const result = await commentOn(run, ref8, foundBy, "inherited");
27959
+ if (result.action === "inherited")
27960
+ commented.add(key);
27961
+ return result;
27962
+ }
27963
+ function localBug(bug, what, local) {
27964
+ const artifact = artifactOf(bug);
27965
+ return {
27966
+ what: local(what),
27967
+ artifact: artifact === null ? null : local(artifact)
27968
+ };
27567
27969
  }
27568
27970
  function skippedRow(index, channel, problem) {
27569
27971
  return { index, channel, action: "skipped", ref: null, foundBy: null, stored: null, problem };
@@ -27592,14 +27994,24 @@ async function triageReport(options) {
27592
27994
  channel: "public",
27593
27995
  tracker: options.tracker,
27594
27996
  readStored: (key) => readTrackerRef(repoRoot, key),
27595
- store: (key, ref8) => writeTrackerRef(repoRoot, {
27997
+ store: (key, ref8, supersedes) => writeTrackerRef(repoRoot, {
27596
27998
  dispatch: options.dispatch,
27597
27999
  outcome: options.outcome,
27598
28000
  artifact: key,
27599
- ref: ref8
27600
- }, options.seams).action
28001
+ ref: ref8,
28002
+ ...supersedes === undefined ? {} : { supersedes }
28003
+ }, options.seams).action,
28004
+ readsState: true,
28005
+ similarity: similarityStepOf(options)
27601
28006
  },
27602
- private: { channel: "private", tracker: privateTracker, readStored: null, store: null }
28007
+ private: {
28008
+ channel: "private",
28009
+ tracker: privateTracker,
28010
+ readStored: null,
28011
+ store: null,
28012
+ readsState: false,
28013
+ similarity: null
28014
+ }
27603
28015
  };
27604
28016
  const bugs = [];
27605
28017
  for (const [index, bug] of options.report.outOfScopeBugs.entries()) {
@@ -27615,7 +28027,10 @@ async function triageReport(options) {
27615
28027
  continue;
27616
28028
  }
27617
28029
  const filing = await filingFor(what, bug, options, local, redact, verifyRefs);
27618
- bugs.push(await triageBug({ route, index, filing, redact }));
28030
+ const run = { route, index, filing, redact };
28031
+ const { inherited } = options;
28032
+ const isInherited = route.channel === "public" && inherited !== undefined && inheritedFailureOf(localBug(bug, what, local), inherited.failures) !== null;
28033
+ bugs.push(isInherited ? await triageInherited(run, inherited.commented) : await triageBug(run));
27619
28034
  }
27620
28035
  return { blocker, bugs };
27621
28036
  }
@@ -27626,8 +28041,13 @@ var LINE_PREFIX = "Triage: ";
27626
28041
  var INDENT8 = " ";
27627
28042
  var FOUND_BY = {
27628
28043
  store: "its stored reference",
27629
- find: "a tracker find"
28044
+ find: "a tracker find",
28045
+ similarity: "word similarity"
27630
28046
  };
28047
+ function foundByText(bug) {
28048
+ const found = FOUND_BY[bug.foundBy ?? "find"];
28049
+ return bug.score === undefined ? found : `${found} (score ${scoreText(bug.score)})`;
28050
+ }
27631
28051
  function unresolvedTracker(kind, reason) {
27632
28052
  const refuse7 = () => Promise.reject(new Error(reason));
27633
28053
  return Object.freeze({
@@ -27650,7 +28070,15 @@ function hasTriage(report2) {
27650
28070
  function issueName2(ref8) {
27651
28071
  return ref8.url ?? `${ref8.kind} issue ${ref8.externalId}`;
27652
28072
  }
27653
- function bugLines(bug) {
28073
+ function failureName(failure3) {
28074
+ return `${failure3.file} > ${failure3.name}`;
28075
+ }
28076
+ function inheritedNote(bug, name2, where, failure3) {
28077
+ const test = failure3 === undefined ? "a red test the run started with" : `the run-start failure ${failureName(failure3)}`;
28078
+ const comment = bug.ref === null ? "" : `, and ${issueName2(bug.ref)} on ${where}, found by ${foundByText(bug)}, was commented on`;
28079
+ return `${name2}: inherited: ${test}; nothing was filed${comment}.`;
28080
+ }
28081
+ function bugLines(bug, inherited) {
27654
28082
  const name2 = `out_of_scope_bugs[${bug.index}]`;
27655
28083
  const where = `the ${bug.channel} tracker`;
27656
28084
  if (bug.action === "skipped") {
@@ -27661,16 +28089,17 @@ function bugLines(bug) {
27661
28089
  return { notes: [], warnings: [warning] };
27662
28090
  }
27663
28091
  const issue3 = bug.ref === null ? "an issue" : issueName2(bug.ref);
27664
- const note = bug.action === "filed" ? `${name2}: filed on ${where} as ${issue3}.` : `${name2}: recurs in ${issue3} on ${where}, found by ${FOUND_BY[bug.foundBy ?? "find"]}; commented on it.`;
28092
+ const reached = bug.action === "filed" ? `${name2}: filed on ${where} as ${issue3}.` : `${name2}: recurs in ${issue3} on ${where}, found by ${foundByText(bug)}; commented on it.`;
28093
+ const note = bug.action === "inherited" ? inheritedNote(bug, name2, where, inherited) : reached;
27665
28094
  const problem = bug.problem === null ? [] : [`${name2}: ${bug.problem}`];
27666
28095
  const conflict = bug.stored === "conflict" ? [`${name2}: the store keeps another reference for its artifact, in place of ${issue3}.`] : [];
27667
28096
  return { notes: [note], warnings: [...problem, ...conflict] };
27668
28097
  }
27669
- function describeStartTriage(result) {
28098
+ function describeStartTriage(result, inherited = new Map) {
27670
28099
  const { blocker } = result;
27671
28100
  const blockerNotes = blocker.written ? ["blockers: written onto the task line, for the next dispatch of this task."] : [];
27672
28101
  const blockerWarnings = blocker.problem === null ? [] : [`blockers: ${blocker.problem}`];
27673
- const bugs = result.bugs.map(bugLines);
28102
+ const bugs = result.bugs.map((bug) => bugLines(bug, inherited.get(bug.index)));
27674
28103
  const prefixed = (lines) => lines.map((line) => `${LINE_PREFIX}${line}`);
27675
28104
  return {
27676
28105
  notes: prefixed([...blockerNotes, ...bugs.flatMap((lines) => lines.notes)]),
@@ -27694,10 +28123,42 @@ async function resolvePublicTracker(options, redact) {
27694
28123
  return unresolvedTracker(options.config.trackerDefault, reason);
27695
28124
  }
27696
28125
  }
28126
+ function baselinePathOrNull(trackerPath) {
28127
+ try {
28128
+ return baselinePathFor(trackerPath);
28129
+ } catch {
28130
+ return null;
28131
+ }
28132
+ }
28133
+ function runStartFailures(trackerPath) {
28134
+ const path3 = baselinePathOrNull(trackerPath);
28135
+ const reading2 = path3 === null ? null : readBaseline(path3);
28136
+ return reading2?.state === "read" && reading2.baseline.junit === "read" ? reading2.baseline.failures : [];
28137
+ }
28138
+ function inheritedByIndex(result, report2, failures, local, redact) {
28139
+ const named3 = result.bugs.flatMap((bug) => {
28140
+ const reported = report2.outOfScopeBugs[bug.index];
28141
+ if (bug.action !== "inherited" || reported === undefined || !hasText2(reported.what))
28142
+ return [];
28143
+ const artifact = artifactOf(reported);
28144
+ const failure3 = inheritedFailureOf({
28145
+ what: local(reported.what),
28146
+ artifact: artifact === null ? null : local(artifact)
28147
+ }, failures);
28148
+ return failure3 === null ? [] : [[bug.index, { file: redact(failure3.file), name: redact(failure3.name) }]];
28149
+ });
28150
+ return new Map(named3);
28151
+ }
27697
28152
  function createStartTriage(options) {
27698
28153
  const secrets = namedSecrets(options.config, options.env ?? process.env);
27699
28154
  const redact = (text) => redactSecrets(text, secrets);
28155
+ const local = localPathRedactor(options.repoRoot, homedir8());
27700
28156
  let publicTracker = null;
28157
+ let inherited = null;
28158
+ const inheritedFor = (trackerPath) => {
28159
+ inherited ??= { failures: runStartFailures(trackerPath), commented: new Set };
28160
+ return inherited;
28161
+ };
27701
28162
  const trackerFor = (report2) => {
27702
28163
  if (!report2.outOfScopeBugs.some(filesPublicly)) {
27703
28164
  return Promise.resolve(unresolvedTracker(options.config.trackerDefault, NOT_RESOLVED_REASON));
@@ -27710,6 +28171,7 @@ function createStartTriage(options) {
27710
28171
  if (!reading2.present || !hasTriage(reading2.report))
27711
28172
  return null;
27712
28173
  const { report: report2 } = reading2;
28174
+ const run = inheritedFor(input.trackerPath);
27713
28175
  const result = await triageReport({
27714
28176
  repoRoot: options.repoRoot,
27715
28177
  trackerPath: input.trackerPath,
@@ -27724,13 +28186,18 @@ function createStartTriage(options) {
27724
28186
  tracker: await trackerFor(report2),
27725
28187
  privateTracker: options.privateTracker,
27726
28188
  secrets,
27727
- seams: options.seams
28189
+ seams: options.seams,
28190
+ inherited: run,
28191
+ similarity: options.config
27728
28192
  });
27729
- const { notes, warnings } = describeStartTriage(result);
28193
+ const named3 = inheritedByIndex(result, report2, run.failures, local, redact);
28194
+ const { notes, warnings } = describeStartTriage(result, named3);
27730
28195
  for (const note of notes)
27731
28196
  activeOutput().info(`${INDENT8}${note}`);
27732
28197
  for (const warning of warnings)
27733
28198
  activeOutput().warn(`${INDENT8}${warning}`);
28199
+ for (const failure3 of named3.values())
28200
+ emitLoopEvent({ kind: "inherited", file: failure3.file, name: failure3.name });
27734
28201
  return result;
27735
28202
  };
27736
28203
  return async (input) => {
@@ -28447,6 +28914,28 @@ function firstLine6(text) {
28447
28914
  `).map((line) => line.trim()).find((line) => line !== "") ?? "";
28448
28915
  }
28449
28916
 
28917
+ // src/start/pr-retarget.ts
28918
+ function retargetedLine(number, from, to) {
28919
+ return `\u21AA Retargeted pull request #${String(number)} from ${from} to ${to} (pr.base).`;
28920
+ }
28921
+ function refusedRetargetLine(number, from, to, detail) {
28922
+ return `\u26A0\uFE0F Could not retarget pull request #${String(number)} from ${from} to ${to} (pr.base): ${detail}. Retarget it by hand; the run carries on to the CI wait.`;
28923
+ }
28924
+ async function retargetPullRequest(pull, base, seams) {
28925
+ const from = pull.baseRefName;
28926
+ if (from === base)
28927
+ return { kind: "unchanged" };
28928
+ try {
28929
+ await seams.pulls.editBase(pull.number, base);
28930
+ } catch (error) {
28931
+ const detail = messageOf(error);
28932
+ seams.output.warn(refusedRetargetLine(pull.number, from, base, detail));
28933
+ return { kind: "refused", from, to: base, detail };
28934
+ }
28935
+ seams.output.info(retargetedLine(pull.number, from, base));
28936
+ return { kind: "retargeted", from, to: base };
28937
+ }
28938
+
28450
28939
  // src/release/prepare.ts
28451
28940
  import { mkdirSync as mkdirSync13, readFileSync as readFileSync40, writeFileSync as writeFileSync15 } from "fs";
28452
28941
  import { dirname as dirname14, join as join38, posix as posix4 } from "path";
@@ -28688,7 +29177,6 @@ var RELEASE_STAGE_SEAMS = {
28688
29177
  push: pushBranch,
28689
29178
  pulls: ghPullRequestsIn,
28690
29179
  readProvider: (repoRoot) => resolvePrProvider({ configured: null, dir: repoRoot }),
28691
- currentBranch: getCurrentBranch,
28692
29180
  now: () => new Date
28693
29181
  };
28694
29182
  var PLAN_HEADING = /^# +(.+)$/;
@@ -28831,18 +29319,18 @@ function announceBody(write, count) {
28831
29319
  }
28832
29320
  out.info(` ${noun} ${verb} now in the body of pull request #${write.number}.`);
28833
29321
  }
28834
- async function writeBody(io, repoRoot, write) {
28835
- const reading2 = io.readProvider(repoRoot);
29322
+ async function writeBody(io, input, write) {
29323
+ const reading2 = io.readProvider(input.repoRoot);
28836
29324
  if (reading2.provider !== "gh") {
28837
29325
  const problem = noProviderProblem(reading2);
28838
29326
  activeOutput().info(` No pull request body carries ${JSON.stringify(write.lines.join(" "))}: ${problem}.`);
28839
29327
  return unwritten(null, problem);
28840
29328
  }
28841
- const body = await carryIntoBody(io.pulls(repoRoot), io.currentBranch(), write);
29329
+ const body = await carryIntoBody(io.pulls(input.repoRoot), input.branch, write);
28842
29330
  announceBody(body, write.lines.length);
28843
29331
  return body;
28844
29332
  }
28845
- async function reportFailure(io, repoRoot, outcome2, sentence, facts) {
29333
+ async function reportFailure(io, input, outcome2, sentence, facts) {
28846
29334
  const out = activeOutput();
28847
29335
  if (outcome2 === "skipped") {
28848
29336
  out.info(`
@@ -28853,7 +29341,7 @@ async function reportFailure(io, repoRoot, outcome2, sentence, facts) {
28853
29341
  }
28854
29342
  const parts = { forecast: null, levelReport: facts.levelReport };
28855
29343
  const block = releaseBodyBlock(parts);
28856
- const body = await writeBody(io, repoRoot, { sentence, block, lines: [sentence, ...releaseBodyLines(parts)] });
29344
+ const body = await writeBody(io, input, { sentence, block, lines: [sentence, ...releaseBodyLines(parts)] });
28857
29345
  return { outcome: outcome2, sentence, forecast: null, body, ...facts };
28858
29346
  }
28859
29347
  var NO_RELEASE = {
@@ -28873,12 +29361,12 @@ async function reportForecast(io, input, forecast, facts) {
28873
29361
  out.warn(` \u26A0\uFE0F ${problem}`);
28874
29362
  const parts = { forecast, levelReport: facts.levelReport };
28875
29363
  const block = releaseBodyBlock(parts);
28876
- const body = await writeBody(io, input.repoRoot, { sentence: null, block, lines: releaseBodyLines(parts) });
29364
+ const body = await writeBody(io, input, { sentence: null, block, lines: releaseBodyLines(parts) });
28877
29365
  return { outcome: "released", sentence: null, forecast, body, ...facts };
28878
29366
  }
28879
29367
  async function finishRelease(input, seams = {}) {
28880
29368
  const io = { ...RELEASE_STAGE_SEAMS, ...seams };
28881
- const { preparation, repoRoot, settings } = input;
29369
+ const { preparation, repoRoot, branch, settings } = input;
28882
29370
  if (preparation === null)
28883
29371
  return NO_RELEASE;
28884
29372
  const levelReport = releaseLevelReport({
@@ -28888,31 +29376,30 @@ async function finishRelease(input, seams = {}) {
28888
29376
  });
28889
29377
  const none2 = { fragment: null, subject: null, sha: null, levelReport };
28890
29378
  if (preparation.kind === "skipped") {
28891
- return reportFailure(io, repoRoot, "skipped", preparation.sentence, none2);
29379
+ return reportFailure(io, input, "skipped", preparation.sentence, none2);
28892
29380
  }
28893
29381
  const git = io.git(repoRoot);
28894
29382
  const verification = io.verify(preparation, { git, settings });
28895
29383
  if (verification.kind === "refused") {
28896
- return reportFailure(io, repoRoot, "refused", verification.sentence, none2);
29384
+ return reportFailure(io, input, "refused", verification.sentence, none2);
28897
29385
  }
28898
29386
  const fragment = verification.path;
28899
29387
  const subject = releaseSubject(preparation);
28900
29388
  const commit = commitFragment(git, fragment, subject);
28901
29389
  if (!commit.committed) {
28902
- return reportFailure(io, repoRoot, "uncommitted", commit.sentence, { ...none2, fragment, subject });
29390
+ return reportFailure(io, input, "uncommitted", commit.sentence, { ...none2, fragment, subject });
28903
29391
  }
28904
29392
  const out = activeOutput();
28905
29393
  const sha = commit.sha;
28906
29394
  const facts = { fragment, subject, sha, levelReport };
28907
29395
  out.info(`
28908
29396
  \uD83D\uDCE6 Committed ${commitName(sha, subject)} over ${fragment}.`);
28909
- const branch = io.currentBranch();
28910
29397
  const pushed = io.push(repoRoot, branch);
28911
29398
  if (!pushed.ok) {
28912
29399
  const said = pushed.output.trim() === "" ? "" : `: ${pushed.output.trim().split(`
28913
29400
  `)[0] ?? ""}`;
28914
29401
  const sentence = `${commitName(sha, subject)} was made but could not be pushed to ${branch}${said}`;
28915
- return reportFailure(io, repoRoot, "unpushed", sentence, facts);
29402
+ return reportFailure(io, input, "unpushed", sentence, facts);
28916
29403
  }
28917
29404
  out.info(` Pushed it to ${branch}.`);
28918
29405
  const forecast = io.forecast({ git, settings, prepared: preparation, verified: verification, now: io.now() });
@@ -29225,7 +29712,7 @@ async function checkWrapUpAnswer(input) {
29225
29712
  }
29226
29713
 
29227
29714
  // src/start/wrap-up.ts
29228
- function buildWrapUpPrompt(branch, planContent, openPullRequest = null, release = null, lessons = []) {
29715
+ function buildWrapUpPrompt(branch, base, planContent, openPullRequest = null, release = null, lessons = []) {
29229
29716
  return [
29230
29717
  "* Read `@progress.txt` in full.",
29231
29718
  "* `@progress.txt` above names the file `progress.txt` at the repo root; the `@` is a reference marker, never part of a path. Do not create or write a file whose name starts with `@`.",
@@ -29238,7 +29725,7 @@ function buildWrapUpPrompt(branch, planContent, openPullRequest = null, release
29238
29725
  "* If the merge touched `bun.lock` or any `package.json`, run `bun install --frozen-lockfile` and require it to pass BEFORE pushing. It is the one-second local reproduction of the CI install step, and it catches a lockfile that no longer matches the merged manifests \u2014 the failure mode where every CI job dies at its first step and nothing downstream runs. When it fails, do NOT hand-edit the lockfile: restore the base's copy (`git checkout origin/main -- bun.lock`), run a plain `bun install` so this branch's own dependencies are re-added, and confirm the frozen run then passes.",
29239
29726
  ...releaseBullets(release),
29240
29727
  `* Commit these changes and push them to the CURRENT branch (${branch}). Never create a branch here: the work under review is this branch's, and a second branch splits one plan across two reviews.`,
29241
- pullRequestStep(branch, openPullRequest),
29728
+ pullRequestStep(branch, base, openPullRequest),
29242
29729
  "* Do not include Claude attribution in the commit or PR message.",
29243
29730
  ...lessonsSection(lessons),
29244
29731
  "",
@@ -29248,15 +29735,15 @@ function buildWrapUpPrompt(branch, planContent, openPullRequest = null, release
29248
29735
  ].join(`
29249
29736
  `);
29250
29737
  }
29251
- function oneLine8(text) {
29738
+ function oneLine9(text) {
29252
29739
  return text.trim().replace(/\s+/g, " ");
29253
29740
  }
29254
29741
  function lessonLines(lesson) {
29255
- const artifact = lesson.artifact === undefined || oneLine8(lesson.artifact) === "" ? "none" : oneLine8(lesson.artifact);
29742
+ const artifact = lesson.artifact === undefined || oneLine9(lesson.artifact) === "" ? "none" : oneLine9(lesson.artifact);
29256
29743
  return [
29257
29744
  `- \`${lesson.id}\``,
29258
- ` - trigger: ${oneLine8(lesson.trigger)}`,
29259
- ` - action: ${oneLine8(lesson.action)}`,
29745
+ ` - trigger: ${oneLine9(lesson.trigger)}`,
29746
+ ` - action: ${oneLine9(lesson.action)}`,
29260
29747
  ` - artifact: ${artifact}`
29261
29748
  ];
29262
29749
  }
@@ -29281,8 +29768,8 @@ function lessonsSection(lessons) {
29281
29768
  "```"
29282
29769
  ];
29283
29770
  }
29284
- function pullRequestStep(branch, openPullRequest) {
29285
- return openPullRequest === null ? `* No open PR was found for ${branch}: open one with \`gh pr create\`. If it answers that a PR already exists, push to that PR and update its body with \`gh pr edit\`; never open a second PR from a new branch.` : `* PR #${openPullRequest} is already open for ${branch}, likely opened by the plan's close-out: push to it and update its body with \`gh pr edit ${openPullRequest}\` so the description covers the promotions this session committed.`;
29771
+ function pullRequestStep(branch, base, openPullRequest) {
29772
+ return openPullRequest === null ? `* No open PR was found for ${branch}: open one with \`gh pr create --base ${base}\`. If it answers that a PR already exists, push to that PR and update its body with \`gh pr edit\`; never open a second PR from a new branch.` : `* PR #${openPullRequest} is already open for ${branch}, likely opened by the plan's close-out: push to it and update its body with \`gh pr edit ${openPullRequest}\` so the description covers the promotions this session committed.`;
29286
29773
  }
29287
29774
  function releaseBullets(release) {
29288
29775
  if (release === null)
@@ -29344,11 +29831,11 @@ async function lessonsToPromote(learning) {
29344
29831
  return [];
29345
29832
  }
29346
29833
  }
29347
- async function preserveProgress(planContent, settingSources, release, serving, learning, checkout) {
29834
+ async function preserveProgress(planContent, settingSources, release, serving, learning, base, checkout) {
29348
29835
  const branch = getCurrentBranch(checkout);
29349
29836
  const openPullRequest = await openPullRequestNumber(checkout, branch);
29350
29837
  return runWrapUpSession({
29351
- buildPrompt: (lessons) => buildWrapUpPrompt(branch, planContent, openPullRequest, release, lessons),
29838
+ buildPrompt: (lessons) => buildWrapUpPrompt(branch, base, planContent, openPullRequest, release, lessons),
29352
29839
  settingSources,
29353
29840
  serving,
29354
29841
  learning,
@@ -29402,8 +29889,8 @@ function missingPullRequestBullet(branch, previousMessage) {
29402
29889
  return [opening, " Its final message follows, quoted:", "", ...quoted3(previousMessage), ""];
29403
29890
  }
29404
29891
  function buildWrapUpRetryPrompt(retry) {
29405
- const base = buildWrapUpPrompt(retry.branch, retry.planContent, null, retry.release, retry.lessons);
29406
- const [classifierKey, ...rest] = base.split(`
29892
+ const first = buildWrapUpPrompt(retry.branch, retry.base, retry.planContent, null, retry.release, retry.lessons);
29893
+ const [classifierKey, ...rest] = first.split(`
29407
29894
  `);
29408
29895
  return [
29409
29896
  classifierKey,
@@ -29413,9 +29900,9 @@ function buildWrapUpRetryPrompt(retry) {
29413
29900
  `);
29414
29901
  }
29415
29902
  function retryWrapUp(run) {
29416
- const { branch, planContent, previousMessage } = run;
29903
+ const { branch, base, planContent, previousMessage } = run;
29417
29904
  return runWrapUpSession({
29418
- buildPrompt: (lessons) => buildWrapUpRetryPrompt({ branch, planContent, release: null, lessons, previousMessage }),
29905
+ buildPrompt: (lessons) => buildWrapUpRetryPrompt({ branch, base, planContent, release: null, lessons, previousMessage }),
29419
29906
  settingSources: run.settingSources,
29420
29907
  serving: run.serving,
29421
29908
  learning: run.learning,
@@ -29457,8 +29944,9 @@ async function runWrapUp(input) {
29457
29944
  planStub: planStub2,
29458
29945
  planContent
29459
29946
  });
29947
+ const base = resolveBaseBranch(createGitRunner(checkout), settings.prBase);
29460
29948
  emitLoopEvent({ kind: "wrap-up", phase: "session" });
29461
- const finalMessage = await preserveProgress(planContent, settingSources, release, serving, wrapUpLearning, checkout);
29949
+ const finalMessage = await preserveProgress(planContent, settingSources, release, serving, wrapUpLearning, base, checkout);
29462
29950
  if (activeOutputMode() !== "text") {
29463
29951
  const pullRequest = await openPullRequestNumber(checkout, expected2.branch);
29464
29952
  emitLoopEvent(pullRequest === null ? { kind: "no-pr", reason: `no open pull request for ${expected2.branch}` } : { kind: "pr", number: pullRequest });
@@ -29472,13 +29960,14 @@ async function runWrapUp(input) {
29472
29960
  dir: checkout
29473
29961
  });
29474
29962
  emitLoopEvent({ kind: "wrap-up", phase: "release" });
29475
- const finish = await finishRelease({ repoRoot: checkout, settings, preparation: release }, { readProvider });
29963
+ const finish = await finishRelease({ repoRoot: checkout, branch: expected2.branch, settings, preparation: release }, { readProvider });
29476
29964
  if (readProvider().provider !== "none") {
29477
- const delivery = await deliverPullRequest({ branch: expected2.branch, retries: settings.loopWrapUpRetries, previousMessage: finalMessage }, deliverySeamsIn({ ...input, fragment: finish.fragment }));
29965
+ const delivery = await deliverPullRequest({ branch: expected2.branch, retries: settings.loopWrapUpRetries, previousMessage: finalMessage }, deliverySeamsIn({ ...input, base, fragment: finish.fragment }));
29478
29966
  if (delivery.kind === "interrupted")
29479
29967
  return;
29480
29968
  if (delivery.kind === "blocked")
29481
29969
  throw new CommandExit(1, delivery.message);
29970
+ await retargetPullRequest(delivery.pull, base, { pulls: ghPullRequestsIn(checkout), output: activeOutput() });
29482
29971
  }
29483
29972
  if (ciWait) {
29484
29973
  emitLoopEvent({ kind: "wrap-up", phase: "ci" });
@@ -29576,6 +30065,7 @@ function deliverySeamsIn(context) {
29576
30065
  retry: (previousMessage) => retryWrapUp({
29577
30066
  previousMessage,
29578
30067
  branch: expected2.branch,
30068
+ base: context.base,
29579
30069
  planContent: context.planContent,
29580
30070
  settingSources: context.settingSources,
29581
30071
  serving: context.serving,
@@ -29585,7 +30075,7 @@ function deliverySeamsIn(context) {
29585
30075
  openRunnerPullRequest: async () => {
29586
30076
  const runnerInput = runnerPrInputFor({
29587
30077
  branch: expected2.branch,
29588
- base: resolveBaseBranch(createGitRunner(checkout), context.settings.prBase),
30078
+ base: context.base,
29589
30079
  planContent: context.planContent,
29590
30080
  planStub: context.planStub,
29591
30081
  notes: fragmentNotesIn(checkout, context.fragment)
@@ -29638,7 +30128,7 @@ async function start(args, repoRoot) {
29638
30128
  return;
29639
30129
  let runConfig;
29640
30130
  try {
29641
- runConfig = loadRunConfig({ root: repoRoot, home: homedir8() }, args);
30131
+ runConfig = loadRunConfig({ root: repoRoot, home: homedir9() }, args);
29642
30132
  } catch (error) {
29643
30133
  if (!(error instanceof ConfigError))
29644
30134
  throw error;
@@ -29669,7 +30159,7 @@ async function start(args, repoRoot) {
29669
30159
  let expected2 = openCheckoutExpectation({ projectRoot: repoRoot, checkout, branch });
29670
30160
  await announceRiskTotal({
29671
30161
  repoRoot,
29672
- home: homedir8(),
30162
+ home: homedir9(),
29673
30163
  planPath,
29674
30164
  config: runConfig.config,
29675
30165
  environment: process.env
@@ -29694,16 +30184,16 @@ async function start(args, repoRoot) {
29694
30184
  tiersRafa: runConfig.config.tiersRafa,
29695
30185
  tiersSkills: runConfig.config.tiersSkills,
29696
30186
  tiersAgents: runConfig.config.tiersAgents,
29697
- home: homedir8()
30187
+ home: homedir9()
29698
30188
  },
29699
- sync: { resolved: runConfig, home: homedir8() },
30189
+ sync: { resolved: runConfig, home: homedir9() },
29700
30190
  claim: createStartPreflightClaim(repoRoot, runConfig.config),
29701
30191
  drift: createStartPreflightDrift(repoRoot, runConfig.config)
29702
30192
  });
29703
- const serving = { root: repoRoot, run: session.id, home: homedir8(), settings: runConfig.config };
30193
+ const serving = { root: repoRoot, run: session.id, home: homedir9(), settings: runConfig.config };
29704
30194
  const learning = {
29705
30195
  kind: runConfig.config.learningAdapter,
29706
- home: homedir8(),
30196
+ home: homedir9(),
29707
30197
  blessMinConfidence: runConfig.config.learningBlessMinConfidence
29708
30198
  };
29709
30199
  const handout = {
@@ -29719,7 +30209,7 @@ async function start(args, repoRoot) {
29719
30209
  };
29720
30210
  const hubContact = createHubContact({
29721
30211
  root: repoRoot,
29722
- home: homedir8(),
30212
+ home: homedir9(),
29723
30213
  resolved: runConfig,
29724
30214
  warn: (message) => activeOutput().warn(message),
29725
30215
  sessionId: session.id
@@ -29798,9 +30288,10 @@ async function start(args, repoRoot) {
29798
30288
  inject: injectMode,
29799
30289
  repoRoot,
29800
30290
  checkout,
29801
- home: homedir8(),
30291
+ home: homedir9(),
29802
30292
  settingSources,
29803
30293
  knownMissing,
30294
+ inherited: runStartFailures(trackerPath),
29804
30295
  serving,
29805
30296
  handout,
29806
30297
  base
@@ -29895,7 +30386,7 @@ function buildBar(pct, width = 20) {
29895
30386
 
29896
30387
  // src/plan.ts
29897
30388
  import fs4 from "fs";
29898
- import { homedir as homedir9 } from "os";
30389
+ import { homedir as homedir10 } from "os";
29899
30390
  import path8 from "path";
29900
30391
  import { fileURLToPath as fileURLToPath3 } from "url";
29901
30392
 
@@ -30705,7 +31196,7 @@ function resolvePlanConfig(repoRoot, home) {
30705
31196
  }
30706
31197
  }
30707
31198
  async function plan(args, repoRoot, registry = CORE_ADAPTER_REGISTRY) {
30708
- const home = homedir9();
31199
+ const home = homedir10();
30709
31200
  const config = resolvePlanConfig(repoRoot, home);
30710
31201
  const {
30711
31202
  settingSources,
@@ -30791,4 +31282,4 @@ async function plan(args, repoRoot, registry = CORE_ADAPTER_REGISTRY) {
30791
31282
  }
30792
31283
  }
30793
31284
 
30794
- export { SERVE_CLI_VERSION, PROVENANCE_FIELD, SKILL_FILE2 as SKILL_FILE, provenanceBlock, serveVerdict, SKILL_USE_CLI_VERSION, MANIFEST_VERSION, FEATURE_TYPES, OUTPUT_CHANNELS, RUNNING_MANIFEST_SEAMS, validateManifest, loadModules, moduleSettings, pullBeforeRead, sessionLogDir, collectSessionRow, collect, AGENT_DEFINITION_DIR, VENDOR_COMMAND, readAgentDirectory, resolveAgentRoster, missingPlanAgents, isStackList, stackPaths, stacksFromEccSkillName, DESCRIPTION_LIMIT, LISTING_LIMIT, SKILL_SIGNALS, countCharacters, toolOf, pathDirectories, checkReferences, checkFile, checkDirectory, bundledAgentsDirectory, sourceItem, byName, skillItem, readTrees, parseReportArgs, formatCount, alignRows, verdictOf, failingRows, formatRows, waitForChecks, recordPlanCi, formatTokens, formatTrendSegment, formatLoopsSegment, readTrendInputs, trendReportOf, buildReport, readSkillsReport, report, SPEC_EXTENSION, boardId, notesFileName, shellQuote, ghMissingMessage, ghUnauthenticatedMessage, ghHostOf, ghPreflightItems, parseWorkingTree, parseWorktrees, readMergeRefusal, resolvePrProvider, requireGhProvider, createGhPullRequests, ghPullRequestsIn, REMOTE, BRANCH_PREFIX2 as BRANCH_PREFIX, parseBaseStanding, hasDiverged, parseClaimMessage, claimBranchIssue, makeOwnershipCommit, fetchClaimBranches, readClaimBranch, pushOwnershipCommit, readClaimState, idleText, ROADMAP_TITLE, ROADMAP_SETTING, parseRoadmapBody, createGhRoadmapSearch, severalRoadmapsMessage, createGhOpenPullRequests, closedIssuesIn, branchClaims, scanClaimBranches, readEpicBody, SPEC_BLOCKED_LABEL, hasSpecBlockedLabel, readBlockedBy, EPIC_LABEL_PREFIX, epicSlugsOf, isNotPlanned, groupByEpicLabel, localDay, readEpics, BOARD_LISTING_LIMIT, createGhBoardListing, validate_default, RefsBlockError, fingerprintText, PREVIOUS_COPY_WARN_ABOVE, previousDir, countPreviousCopies, createGhSpecIssueReader, readRoadmapSetting, ROADMAP_LABEL, BOARD_LABELS, LABEL_LIST_LIMIT, SPEC_TEMPLATE_PATH, TEMPLATES_DIRNAME, boardChanged, listBoardLabels, missingBoardLabels, anythingAt, writeShippedFile, setUpBoard, resolveDefaultBoard, unlabelledRoadmapMessage, createGhRoadmapBody, tickSentence, appendLine, tickLine, editChecklist, noBoardListsLine, epicTickSentence, tickRoadmapAfterMerge, createGhIssueBoard, createLinePrompter, readBlockedIssues, renderBlockedIssues, resolveTracker, DEFAULT_ISSUE_SEAMS, lineRefusal, expectTwoArguments, readTextFlag, readNonBlankFlag, readRequiredFlag, readChoice, readChoiceFlag, issueProject, issueSubjectConfig, resolveIssueTracker, issueRef, issueName, urlLines, onTracker, unblock_default, unblockAfterMerge, HORIZON_LABEL_PREFIX, readEpicProblems, epicProblemMessage, isWaiting, blockerToken, addBlockerToBody, labelsLineBlockersOf, LABELS_READS, createLabelsRelations, EPIC_TYPE_LABEL, STAGE_LABELS, unlabelReleased, readRefsText, readCopyRefs, restampCopyRefs, RefVerifyError, blockerArg, createNativeRelations, selectBoardRelations, NATIVE_MODE, NATIVE_RETRY_HINT, nativeEpicLeft, nativeAlreadyMessage, nativeParentLine, readBoardRepository, configuredMoveRelations, readConfiguredRelations, blockedLineSentence, readDoctorRefs, renderDoctorRefs, readEpicDependents, REASON_FLAG, renderHorizonComment, renderParkedPullRequestComment, renderMoveComment, renderCloseComment, renderCancelComment, cancelMoveReason, renderDependentComment, renderUnblockNote, readReason, unaskedReasonMessage, blankReasonMessage, HORIZONS, horizonOf, readListedEpics, invalidateRows, runIssueList, ISSUE_LIST_FLAGS, list_default, firstNowEpic, show_default, samePlace, resolvePlace, createPlanDirNames, hasPlanFor, memoiseVerifier, LEARNED_DIRECTORY, ORIGIN_FIELD, DEMOTION_VERDICTS, WHEN_TO_USE_HEADING, bodySections, classifySkill, firstSentence, convertToInstinct, rankCandidates, readRankCandidate, NO_NEXT_STEP, readSchemaReport, gateMeaning, unknownAdditive, unknownAdditiveWarning, developmentApplied, isFirstDispatch, createStartPreflightClaim, checkDrift, driftLines, readDoctorEffortSync, renderDoctorEffortSync, effortSyncRefusal, SESSION_ID_FLAG2 as SESSION_ID_FLAG, readBoardBody, openBoards, boardOfEpic, createOwnerResolver, resolveLoopSeams, sessionIdFlag, refusal3 as refusal, isLive, planLabel, sessionLine, projectRoot2 as projectRoot, readRecords, pickSession, writeSession, readSessionChecklist, phaseNote, phasedCounts, checkboxAt, estimateEta, readSessionFinishes, formatDuration, etaLine, RELEASE_REMOTE, RELEASE_BASE_BRANCH, parseSemanticVersion, readManifestVersion, replaceManifestVersion, gitPathOf, receiptProblem, readReceiptVerdict, FRAGMENT_PLAN_ID_PATTERN, serializeFragment, allocateFragmentName, directoryPrefix, releaseCommitSubject, readSettle, buildSettle, waitingSettingsOf, readWaiting, forecastPhrase, DEFAULT_RELEASE_SEAMS, compareVersions, changelogVersions, readTags, status_default, epicEndLines, watchDryEpic, nextOwnerGate, resolveReleaseEnabled, refuseMisplacedRuntime, readBinPath, self_update_default, versionTag, INDENT4 as INDENT, cleanUpAfterMerge, reportFollowUps, RELEASE_BLOCK_OPEN, RELEASE_BLOCK_CLOSE, localPlanNotes, readGuard, guardLines, mergeGuardSettings, readMergeGuard, guardReaction, followsMerge, readSettleAfterMerge, DEFAULT_BASE_BRANCH, ghNextBoard, openNextSources, WORKTREES_SUBDIR, resolveWorktreePath, crossRepositoryRefusal, addResolveWorktree, removeResolveWorktree, pushResolved, worktreeAt, cleanupSteps, dryRunLines, runCleanupSteps, defaultCleanupSeams, readCleanup, cleanupCounts, openNextRelations, list_default2 as list_default1, readBranch2 as readBranch, readPlans, readOpenPull, readBranchPlan, readNextState, readHomeAfterLoop, commandWords, nextQuestion, NEXT_COMMAND_ACTIONS, LABELS_ONLY_ACTIONS, actionInvocation, runAction, HINT_FLAG_SPEC, actionOutput, endWithNextStep, endingWith, lazyPrompter2 as lazyPrompter, ready_default, DEFAULT_PLAN_FILE, resolvePlanPath, assessPlanRisk, renderRiskText, announceRiskTotal, resolveCreateSpec, TRIAGE_MODULE, namedSecrets, triageReport, unresolvedTracker, DISK_ROOTS_FILE_SYSTEM, gitToplevel, rootRefusal, rootCandidates, DEFAULT_CI_TIMEOUT_MIN, CI_POLL_INTERVAL_MS, DEFAULT_CI_ATTEMPTS, MECHANICAL_CONFLICT_SENTENCE, start, usage, createPlanRefsVerifier, plan };
31285
+ export { SERVE_CLI_VERSION, PROVENANCE_FIELD, SKILL_FILE2 as SKILL_FILE, provenanceBlock, serveVerdict, SKILL_USE_CLI_VERSION, MANIFEST_VERSION, FEATURE_TYPES, OUTPUT_CHANNELS, RUNNING_MANIFEST_SEAMS, validateManifest, loadModules, moduleSettings, pullBeforeRead, sessionLogDir, collectSessionRow, collect, AGENT_DEFINITION_DIR, VENDOR_COMMAND, readAgentDirectory, resolveAgentRoster, missingPlanAgents, isStackList, stackPaths, stacksFromEccSkillName, DESCRIPTION_LIMIT, LISTING_LIMIT, SKILL_SIGNALS, countCharacters, toolOf, pathDirectories, checkReferences, checkFile, checkDirectory, bundledAgentsDirectory, sourceItem, byName, skillItem, readTrees, parseReportArgs, formatCount, alignRows, verdictOf, failingRows, formatRows, waitForChecks, recordPlanCi, formatTokens, formatTrendSegment, formatLoopsSegment, readTrendInputs, trendReportOf, buildReport, readSkillsReport, report, SPEC_EXTENSION, boardId, notesFileName, shellQuote, ghMissingMessage, ghUnauthenticatedMessage, ghHostOf, ghPreflightItems, parseWorkingTree, parseWorktrees, readMergeRefusal, resolvePrProvider, requireGhProvider, createGhPullRequests, ghPullRequestsIn, REMOTE, BRANCH_PREFIX2 as BRANCH_PREFIX, parseBaseStanding, hasDiverged, parseClaimMessage, claimBranchIssue, makeOwnershipCommit, fetchClaimBranches, readClaimBranch, pushOwnershipCommit, readClaimState, idleText, ROADMAP_TITLE, ROADMAP_SETTING, parseRoadmapBody, createGhRoadmapSearch, severalRoadmapsMessage, createGhOpenPullRequests, closedIssuesIn, branchClaims, scanClaimBranches, readEpicBody, SPEC_BLOCKED_LABEL, hasSpecBlockedLabel, readBlockedBy, EPIC_LABEL_PREFIX, epicSlugsOf, isNotPlanned, groupByEpicLabel, localDay, readEpics, BOARD_LISTING_LIMIT, createGhBoardListing, validate_default, RefsBlockError, fingerprintText, PREVIOUS_COPY_WARN_ABOVE, previousDir, countPreviousCopies, createGhSpecIssueReader, readRoadmapSetting, ROADMAP_LABEL, BOARD_LABELS, LABEL_LIST_LIMIT, SPEC_TEMPLATE_PATH, TEMPLATES_DIRNAME, boardChanged, listBoardLabels, missingBoardLabels, setUpLabels, anythingAt, writeShippedFile, setUpBoard, resolveDefaultBoard, unlabelledRoadmapMessage, createGhRoadmapBody, tickSentence, appendLine, tickLine, editChecklist, noBoardListsLine, epicTickSentence, tickRoadmapAfterMerge, createGhIssueBoard, createLinePrompter, readBlockedIssues, renderBlockedIssues, resolveTracker, DEFAULT_ISSUE_SEAMS, lineRefusal, expectTwoArguments, readTextFlag, readNonBlankFlag, readRequiredFlag, readChoice, readChoiceFlag, issueProject, issueSubjectConfig, resolveIssueTracker, issueRef, issueName, urlLines, onTracker, unblock_default, unblockAfterMerge, HORIZON_LABEL_PREFIX, readEpicProblems, epicProblemMessage, isWaiting, blockerToken, addBlockerToBody, labelsLineBlockersOf, LABELS_READS, createLabelsRelations, EPIC_TYPE_LABEL, STAGE_LABELS, unlabelReleased, readRefsText, readCopyRefs, restampCopyRefs, RefVerifyError, blockerArg, createNativeRelations, selectBoardRelations, NATIVE_MODE, NATIVE_RETRY_HINT, nativeEpicLeft, nativeAlreadyMessage, nativeParentLine, readBoardRepository, configuredMoveRelations, readConfiguredRelations, blockedLineSentence, readDoctorRefs, renderDoctorRefs, readEpicDependents, REASON_FLAG, renderHorizonComment, renderParkedPullRequestComment, renderMoveComment, renderCloseComment, renderCancelComment, cancelMoveReason, renderDependentComment, renderUnblockNote, readReason, unaskedReasonMessage, blankReasonMessage, HORIZONS, horizonOf, readListedEpics, invalidateRows, runIssueList, ISSUE_LIST_FLAGS, list_default, firstNowEpic, show_default, samePlace, resolvePlace, createPlanDirNames, hasPlanFor, memoiseVerifier, LEARNED_DIRECTORY, ORIGIN_FIELD, DEMOTION_VERDICTS, WHEN_TO_USE_HEADING, bodySections, classifySkill, firstSentence, convertToInstinct, rankCandidates, readRankCandidate, NO_NEXT_STEP, readSchemaReport, gateMeaning, unknownAdditive, unknownAdditiveWarning, developmentApplied, isFirstDispatch, createStartPreflightClaim, checkDrift, driftLines, readDoctorEffortSync, renderDoctorEffortSync, effortSyncRefusal, SESSION_ID_FLAG2 as SESSION_ID_FLAG, readBoardBody, openBoards, boardOfEpic, createOwnerResolver, resolveLoopSeams, sessionIdFlag, refusal3 as refusal, isLive, planLabel, sessionLine, projectRoot2 as projectRoot, readRecords, pickSession, writeSession, readSessionChecklist, phaseNote, phasedCounts, checkboxAt, estimateEta, readSessionFinishes, formatDuration, etaLine, RELEASE_REMOTE, RELEASE_BASE_BRANCH, parseSemanticVersion, readManifestVersion, replaceManifestVersion, gitPathOf, receiptProblem, readReceiptVerdict, FRAGMENT_PLAN_ID_PATTERN, serializeFragment, allocateFragmentName, directoryPrefix, releaseCommitSubject, readSettle, buildSettle, waitingSettingsOf, readWaiting, forecastPhrase, DEFAULT_RELEASE_SEAMS, compareVersions, changelogVersions, readTags, status_default, epicEndLines, watchDryEpic, nextOwnerGate, resolveReleaseEnabled, refuseMisplacedRuntime, readBinPath, self_update_default, versionTag, INDENT4 as INDENT, cleanUpAfterMerge, reportFollowUps, RELEASE_BLOCK_OPEN, RELEASE_BLOCK_CLOSE, localPlanNotes, readGuard, guardLines, mergeGuardSettings, readMergeGuard, guardReaction, followsMerge, readSettleAfterMerge, DEFAULT_BASE_BRANCH, ghNextBoard, openNextSources, WORKTREES_SUBDIR, resolveWorktreePath, crossRepositoryRefusal, addResolveWorktree, removeResolveWorktree, pushResolved, worktreeAt, cleanupSteps, dryRunLines, runCleanupSteps, defaultCleanupSeams, readCleanup, cleanupCounts, openNextRelations, list_default2 as list_default1, readBranch2 as readBranch, readPlans, readOpenPull, readBranchPlan, readNextState, readHomeAfterLoop, commandWords, nextQuestion, NEXT_COMMAND_ACTIONS, LABELS_ONLY_ACTIONS, actionInvocation, runAction, HINT_FLAG_SPEC, actionOutput, endWithNextStep, endingWith, lazyPrompter2 as lazyPrompter, ready_default, DEFAULT_PLAN_FILE, resolvePlanPath, assessPlanRisk, renderRiskText, announceRiskTotal, resolveCreateSpec, TRIAGE_MODULE, namedSecrets, triageReport, unresolvedTracker, DISK_ROOTS_FILE_SYSTEM, gitToplevel, rootRefusal, rootCandidates, DEFAULT_CI_TIMEOUT_MIN, CI_POLL_INTERVAL_MS, DEFAULT_CI_ATTEMPTS, MECHANICAL_CONFLICT_SENTENCE, start, usage, createPlanRefsVerifier, plan };