@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.
package/dist/cli.js CHANGED
@@ -850,6 +850,34 @@ var init_config_schema_tests = __esm(() => {
850
850
  };
851
851
  });
852
852
 
853
+ // src/config-schema-triage.ts
854
+ function isThreshold(raw) {
855
+ return typeof raw === "number" && Number.isFinite(raw) && raw > 0 && raw <= SIMILARITY_THRESHOLD_MAX;
856
+ }
857
+ function isCandidateCount(raw) {
858
+ return typeof raw === "number" && Number.isSafeInteger(raw) && raw >= SIMILARITY_CANDIDATES_MIN && raw <= SIMILARITY_CANDIDATES_MAX;
859
+ }
860
+ var SIMILARITY_THRESHOLD_MAX = 1, SIMILARITY_CANDIDATES_MIN = 1, SIMILARITY_CANDIDATES_MAX = 10, similarityThreshold = (raw, at) => raw === false || isThreshold(raw) ? { value: raw, problems: [], extras: [] } : refused(at, raw, `false or a number above 0 and at most ${String(SIMILARITY_THRESHOLD_MAX)}`), similarityCandidates = (raw, at) => isCandidateCount(raw) ? { value: raw, problems: [], extras: [] } : refused(at, raw, `a whole number from ${String(SIMILARITY_CANDIDATES_MIN)} to ${String(SIMILARITY_CANDIDATES_MAX)}`), TRIAGE_DEFAULTS, TRIAGE_SETTINGS;
861
+ var init_config_schema_triage = __esm(() => {
862
+ init_config_sections();
863
+ TRIAGE_DEFAULTS = Object.freeze({
864
+ triageSimilarityThreshold: 0.3,
865
+ triageSimilarityCandidates: 3
866
+ });
867
+ TRIAGE_SETTINGS = {
868
+ triageSimilarityThreshold: {
869
+ key: "triage.similarity.threshold",
870
+ read: similarityThreshold,
871
+ cli: false
872
+ },
873
+ triageSimilarityCandidates: {
874
+ key: "triage.similarity.candidates",
875
+ read: similarityCandidates,
876
+ cli: false
877
+ }
878
+ };
879
+ });
880
+
853
881
  // src/config-schema-wrap-up.ts
854
882
  function isRetryCount(raw) {
855
883
  return typeof raw === "number" && Number.isSafeInteger(raw) && raw >= WRAP_UP_RETRIES_MIN && raw <= WRAP_UP_RETRIES_MAX;
@@ -1065,6 +1093,7 @@ var init_config_schema = __esm(() => {
1065
1093
  init_config_schema_hub();
1066
1094
  init_config_schema_release();
1067
1095
  init_config_schema_tests();
1096
+ init_config_schema_triage();
1068
1097
  init_config_schema_wrap_up();
1069
1098
  init_config_sections();
1070
1099
  init_routing();
@@ -1101,6 +1130,7 @@ var init_config_schema = __esm(() => {
1101
1130
  roadmapIssue: null,
1102
1131
  claimsStaleAfter: "3d",
1103
1132
  claimsAhead: "off",
1133
+ ...TRIAGE_DEFAULTS,
1104
1134
  ...RELEASE_DEFAULTS,
1105
1135
  cleanupStaleDays: 30,
1106
1136
  cleanupWorktreeIdleDays: 7,
@@ -1188,6 +1218,7 @@ var init_config_schema = __esm(() => {
1188
1218
  roadmapIssue: { key: "roadmap.issue", read: issueNumber, cli: false },
1189
1219
  claimsStaleAfter: { key: "claims.staleAfter", read: claimsStaleAfter, cli: false },
1190
1220
  claimsAhead: { key: "claims.ahead", read: claimsAhead, cli: false },
1221
+ ...TRIAGE_SETTINGS,
1191
1222
  ...RELEASE_SETTINGS,
1192
1223
  cleanupStaleDays: { key: "cleanup.staleDays", read: dayCount, cli: false },
1193
1224
  cleanupWorktreeIdleDays: {
@@ -1297,6 +1328,8 @@ function readLayer(rawOf, atOf) {
1297
1328
  roadmapIssue: read("roadmapIssue"),
1298
1329
  claimsStaleAfter: read("claimsStaleAfter"),
1299
1330
  claimsAhead: read("claimsAhead"),
1331
+ triageSimilarityThreshold: read("triageSimilarityThreshold"),
1332
+ triageSimilarityCandidates: read("triageSimilarityCandidates"),
1300
1333
  releaseEnabled: read("releaseEnabled"),
1301
1334
  releaseVersionFile: read("releaseVersionFile"),
1302
1335
  releaseChangelog: read("releaseChangelog"),
@@ -1360,7 +1393,13 @@ function sortKeys(document, path) {
1360
1393
  }
1361
1394
  return { named, extras, problems };
1362
1395
  }
1396
+ function tabIndentProblems(text2, path) {
1397
+ return text2.split(/\r?\n/).flatMap((line, index) => TAB_INDENTED_LINE.test(line) ? [`${path}: line ${index + 1} is indented with a tab; YAML indents with spaces only`] : []);
1398
+ }
1363
1399
  function parseYaml(text2, path) {
1400
+ const tabProblems = tabIndentProblems(text2, path);
1401
+ if (tabProblems.length > 0)
1402
+ throw new ConfigError(tabProblems);
1364
1403
  try {
1365
1404
  return Bun.YAML.parse(text2);
1366
1405
  } catch (error) {
@@ -1461,7 +1500,7 @@ function resolveConfig(layers = {}) {
1461
1500
  warnings
1462
1501
  };
1463
1502
  }
1464
- var ConfigError, MAP_SETTINGS;
1503
+ var ConfigError, TAB_INDENTED_LINE, MAP_SETTINGS;
1465
1504
  var init_config = __esm(() => {
1466
1505
  init_config_schema_hub();
1467
1506
  init_config_schema();
@@ -1477,6 +1516,7 @@ var init_config = __esm(() => {
1477
1516
  this.problems = problems;
1478
1517
  }
1479
1518
  };
1519
+ TAB_INDENTED_LINE = /^[ \t]*\t[ \t]*[^\s#]/;
1480
1520
  MAP_SETTINGS = new Set([
1481
1521
  "tiersSkills",
1482
1522
  "tiersAgents",
@@ -2193,7 +2233,7 @@ function restoreRunningCommand(previous) {
2193
2233
  var running = null;
2194
2234
 
2195
2235
  // package.json
2196
- var name = "@open-tomato/rafa", version = "0.33.0";
2236
+ var name = "@open-tomato/rafa", version = "0.34.1";
2197
2237
  var init_package = () => {};
2198
2238
 
2199
2239
  // src/cli/version.ts
@@ -6522,11 +6562,18 @@ var init_store = __esm(() => {
6522
6562
  // src/effort/store/location.ts
6523
6563
  import { existsSync as existsSync6, realpathSync as realpathSync4 } from "fs";
6524
6564
  import { tmpdir } from "os";
6525
- import { isAbsolute as isAbsolute9, join as join15, relative as relative4, resolve as resolve2, sep } from "path";
6565
+ import { basename as basename4, dirname as dirname10, isAbsolute as isAbsolute9, join as join15, relative as relative4, resolve as resolve2, sep } from "path";
6526
6566
  function canonical(path) {
6527
6567
  const resolved = resolve2(path);
6528
6568
  return existsSync6(resolved) ? realpathSync4(resolved) : resolved;
6529
6569
  }
6570
+ function realPathOf(path) {
6571
+ const resolved = resolve2(path);
6572
+ if (existsSync6(resolved))
6573
+ return realpathSync4(resolved);
6574
+ const parent = dirname10(resolved);
6575
+ return parent === resolved ? resolved : join15(realPathOf(parent), basename4(resolved));
6576
+ }
6530
6577
  function isWithin(path, dir) {
6531
6578
  const between = relative4(resolve2(dir), resolve2(path));
6532
6579
  const climbsOut = between === ".." || between.startsWith(`..${sep}`);
@@ -6551,7 +6598,7 @@ function isTestProcess(main = Bun.main, env = process.env) {
6551
6598
  return main.endsWith(TEST_ENTRY_SUFFIX) || env[TEST_PROCESS_VARIABLE] === "1";
6552
6599
  }
6553
6600
  function isUnderTempDir(path, tempDir = tmpdir()) {
6554
- return isWithin(path, tempDir) || isWithin(path, canonical(tempDir));
6601
+ return isWithin(path, tempDir) || isWithin(realPathOf(path), realPathOf(tempDir));
6555
6602
  }
6556
6603
  function currentProbe() {
6557
6604
  return { main: Bun.main, env: process.env, tempDir: tmpdir() };
@@ -6618,7 +6665,7 @@ var init_ndjson = __esm(() => {
6618
6665
 
6619
6666
  // src/runtime/identity.ts
6620
6667
  import { existsSync as existsSync7, readFileSync as readFileSync13, realpathSync as realpathSync5 } from "fs";
6621
- import { dirname as dirname10, join as join17, resolve as resolve3 } from "path";
6668
+ import { dirname as dirname11, join as join17, resolve as resolve3 } from "path";
6622
6669
  function realOrResolved(path) {
6623
6670
  const resolved = resolve3(path);
6624
6671
  return existsSync7(resolved) ? realpathSync5(resolved) : resolved;
@@ -6641,7 +6688,7 @@ function nearestRafaPackage(start) {
6641
6688
  const manifest = join17(dir, MANIFEST);
6642
6689
  if (existsSync7(manifest) && manifestName(manifest) === name)
6643
6690
  return dir;
6644
- const parent = dirname10(dir);
6691
+ const parent = dirname11(dir);
6645
6692
  if (parent === dir)
6646
6693
  return null;
6647
6694
  dir = parent;
@@ -6649,7 +6696,7 @@ function nearestRafaPackage(start) {
6649
6696
  }
6650
6697
  function readRuntimeIdentity(entry = Bun.main) {
6651
6698
  const real = realOrResolved(entry);
6652
- const packageDir = nearestRafaPackage(dirname10(real));
6699
+ const packageDir = nearestRafaPackage(dirname11(real));
6653
6700
  if (packageDir !== null && existsSync7(join17(packageDir, CHECKOUT_ENTRY))) {
6654
6701
  return { kind: "development", entry: real, checkout: packageDir };
6655
6702
  }
@@ -6663,16 +6710,16 @@ var init_identity2 = __esm(() => {
6663
6710
 
6664
6711
  // src/effort/store/development-build.ts
6665
6712
  import { tmpdir as tmpdir2 } from "os";
6666
- import { basename as basename4, dirname as dirname11, isAbsolute as isAbsolute10 } from "path";
6713
+ import { basename as basename5, dirname as dirname12, isAbsolute as isAbsolute10 } from "path";
6667
6714
  function appliedByName(identity) {
6668
6715
  return identity.kind === "development" ? `${RAFA_VERSION}${DEVELOPMENT_MARK}${identity.checkout}` : RAFA_VERSION;
6669
6716
  }
6670
6717
  function storeProjectRoot(path) {
6671
- const storeDir = dirname11(path);
6672
- const scopeDir = dirname11(storeDir);
6673
- if (basename4(storeDir) !== EFFORT_DIR_NAME || basename4(scopeDir) !== SCOPE_DIR)
6718
+ const storeDir = dirname12(path);
6719
+ const scopeDir = dirname12(storeDir);
6720
+ if (basename5(storeDir) !== EFFORT_DIR_NAME || basename5(scopeDir) !== SCOPE_DIR)
6674
6721
  return null;
6675
- return dirname11(scopeDir);
6722
+ return dirname12(scopeDir);
6676
6723
  }
6677
6724
  function isOwnedStore(path, env, tempDir) {
6678
6725
  if (isUnderTempDir(path, tempDir))
@@ -7401,7 +7448,7 @@ var init_store_identity = __esm(() => {
7401
7448
 
7402
7449
  // src/effort/store/store-meta.ts
7403
7450
  import { randomUUID as randomUUID3 } from "crypto";
7404
- import { dirname as dirname12 } from "path";
7451
+ import { dirname as dirname13 } from "path";
7405
7452
  function toStoredInteger(value) {
7406
7453
  return BigInt.asIntN(STORED_INTEGER_BITS, value);
7407
7454
  }
@@ -7424,7 +7471,7 @@ function readStoreMeta(db) {
7424
7471
  };
7425
7472
  }
7426
7473
  function projectFor(storePath, recorded, seams) {
7427
- const read = (seams.readProject ?? readProjectIdentity)(dirname12(storePath));
7474
+ const read = (seams.readProject ?? readProjectIdentity)(dirname13(storePath));
7428
7475
  if (read.rootCommit !== null)
7429
7476
  return { rootCommit: read.rootCommit, remote: read.remote };
7430
7477
  if (recorded === null)
@@ -7467,7 +7514,7 @@ var init_store_meta = __esm(() => {
7467
7514
 
7468
7515
  // src/effort/store/sqlite.ts
7469
7516
  import { existsSync as existsSync8, mkdirSync as mkdirSync6 } from "fs";
7470
- import { dirname as dirname13, join as join18 } from "path";
7517
+ import { dirname as dirname14, join as join18 } from "path";
7471
7518
  import { Database } from "bun:sqlite";
7472
7519
  function sqliteStorePath(repoRoot) {
7473
7520
  return join18(effortStoreDir(repoRoot), SQLITE_STORE_FILE_NAME);
@@ -7496,7 +7543,7 @@ function batchEntries(kind, rows2) {
7496
7543
  function withSqliteStore(path, access, create, use, identity = {}) {
7497
7544
  guardTestProcess(path);
7498
7545
  if (create)
7499
- mkdirSync6(dirname13(path), { recursive: true });
7546
+ mkdirSync6(dirname14(path), { recursive: true });
7500
7547
  const db = new Database(path, { readwrite: true, create });
7501
7548
  try {
7502
7549
  db.run(`PRAGMA busy_timeout = ${String(activeStoreSettings().busyTimeoutMs)}`);
@@ -8667,7 +8714,7 @@ var init_migrate = __esm(() => {
8667
8714
  import { randomUUID as randomUUID4 } from "crypto";
8668
8715
  import { existsSync as existsSync13, mkdtempSync, rmSync as rmSync7 } from "fs";
8669
8716
  import { tmpdir as tmpdir3 } from "os";
8670
- import { dirname as dirname14, join as join20 } from "path";
8717
+ import { dirname as dirname15, join as join20 } from "path";
8671
8718
  import { Database as Database5, SQLiteError as SQLiteError2 } from "bun:sqlite";
8672
8719
  function refuseNdjson(backend) {
8673
8720
  if (backend !== "ndjson")
@@ -8714,7 +8761,7 @@ function recordedMeta(path) {
8714
8761
  }
8715
8762
  function refuseOtherProject(options) {
8716
8763
  const other = recordedMeta(options.otherPath);
8717
- const here = recordedMeta(options.path)?.projectRootCommit ?? (options.readProject ?? readProjectIdentity)(dirname14(options.path)).rootCommit;
8764
+ const here = recordedMeta(options.path)?.projectRootCommit ?? (options.readProject ?? readProjectIdentity)(dirname15(options.path)).rootCommit;
8718
8765
  const there = other?.projectRootCommit ?? null;
8719
8766
  if (here !== null && there !== null && here !== there) {
8720
8767
  throw new MergeRefusal("other-project", `REFUSED \u2014 ${options.otherPath} is the effort store of another project (root commit ${there});` + ` this store's project has root commit ${here}. ${NOTHING_MERGED}`);
@@ -10255,28 +10302,49 @@ function viewedIssue(stdout, number) {
10255
10302
  const checked = payload;
10256
10303
  return { ...checked, labels: labelNames(checked.labels) ?? [] };
10257
10304
  }
10258
- function listedIssues(stdout) {
10259
- const command = "gh issue list";
10260
- const payload = parseJson(stdout, command);
10305
+ function listedRows(stdout) {
10306
+ const payload = parseJson(stdout, LIST_COMMAND);
10261
10307
  if (!Array.isArray(payload)) {
10262
- throw new Error(`${PREFIX2}: ${command} answered ${describeValue(payload)}, expected a list`);
10308
+ throw new Error(`${PREFIX2}: ${LIST_COMMAND} answered ${describeValue(payload)}, expected a list`);
10263
10309
  }
10264
- return payload.map((row2, index) => {
10265
- const refuse2 = (problem) => new Error(`${PREFIX2}: ${command} answered row ${index} ${problem}`);
10266
- if (!isMapping(row2))
10267
- throw refuse2(`${describeValue(row2)}, expected a mapping`);
10268
- const { number, url } = row2;
10269
- const labels = labelNames(row2["labels"]);
10270
- if (typeof number !== "number" || !isIssueNumber3(String(number))) {
10271
- throw refuse2(`with number ${describeValue(number)}, expected an issue number`);
10272
- }
10273
- if (typeof url !== "string")
10274
- throw refuse2(`with url ${describeValue(url)}, expected a string`);
10275
- if (labels === null)
10276
- throw refuse2("with labels that are not a list of named labels");
10277
- return { number, url, labels };
10310
+ return payload;
10311
+ }
10312
+ function rowRefusal(index, problem) {
10313
+ return new Error(`${PREFIX2}: ${LIST_COMMAND} answered row ${index} ${problem}`);
10314
+ }
10315
+ function listedRow(row2, index) {
10316
+ if (!isMapping(row2))
10317
+ throw rowRefusal(index, `${describeValue(row2)}, expected a mapping`);
10318
+ const { number, url } = row2;
10319
+ const labels = labelNames(row2["labels"]);
10320
+ if (typeof number !== "number" || !isIssueNumber3(String(number))) {
10321
+ throw rowRefusal(index, `with number ${describeValue(number)}, expected an issue number`);
10322
+ }
10323
+ if (typeof url !== "string")
10324
+ throw rowRefusal(index, `with url ${describeValue(url)}, expected a string`);
10325
+ if (labels === null)
10326
+ throw rowRefusal(index, "with labels that are not a list of named labels");
10327
+ return { number, url, labels };
10328
+ }
10329
+ function listedIssues(stdout) {
10330
+ return listedRows(stdout).map(listedRow);
10331
+ }
10332
+ function listedOpenIssues(stdout) {
10333
+ return listedRows(stdout).map((row2, index) => {
10334
+ const listed3 = listedRow(row2, index);
10335
+ const { title: title2, body } = row2;
10336
+ if (typeof title2 !== "string")
10337
+ throw rowRefusal(index, `with title ${describeValue(title2)}, expected a string`);
10338
+ if (typeof body !== "string")
10339
+ throw rowRefusal(index, `with body ${describeValue(body)}, expected a string`);
10340
+ return { ...listed3, title: title2, body };
10278
10341
  });
10279
10342
  }
10343
+ function refOfRow(row2) {
10344
+ const module = labelValue(row2.labels, GITHUB_LABELS.modulePrefix);
10345
+ const ref = { opt: 0, kind: "github", externalId: String(row2.number), url: row2.url };
10346
+ return module === undefined ? ref : { ...ref, module };
10347
+ }
10280
10348
  function labelValue(labels, prefix) {
10281
10349
  return labels.find((label) => label.startsWith(prefix))?.slice(prefix.length);
10282
10350
  }
@@ -10290,7 +10358,7 @@ function moduleOfLabels(labels) {
10290
10358
  function stateOf(viewed) {
10291
10359
  if (viewed.state === "OPEN")
10292
10360
  return "todo";
10293
- return viewed.stateReason === NOT_PLANNED ? "cancelled" : "done";
10361
+ return viewed.stateReason !== null && CANCELLED_REASONS.has(viewed.stateReason) ? "cancelled" : "done";
10294
10362
  }
10295
10363
  function issueOf2(ref, viewed) {
10296
10364
  const priority = labelValue(viewed.labels, GITHUB_LABELS.priorityPrefix);
@@ -10401,12 +10469,26 @@ function createGithubTracker(options) {
10401
10469
  ...optionalFlag("--label", moduleLabel),
10402
10470
  ...optionalFlag("--label", typeLabel),
10403
10471
  ...optionalFlag("--search", query.text)
10404
- ], "gh issue list");
10405
- return listedIssues(stdout).map((row2) => {
10406
- const module = labelValue(row2.labels, GITHUB_LABELS.modulePrefix);
10407
- const ref = { opt: 0, kind: "github", externalId: String(row2.number), url: row2.url };
10408
- return module === undefined ? ref : { ...ref, module };
10409
- });
10472
+ ], LIST_COMMAND);
10473
+ return listedIssues(stdout).map(refOfRow);
10474
+ },
10475
+ openIssues: async (type) => {
10476
+ if (!isOneOf2(ISSUE_TYPES, type)) {
10477
+ throw new TypeError(`${PREFIX2}: openIssues refused type ${describeValue(type)}, expected one of: ${ISSUE_TYPES.join(", ")}`);
10478
+ }
10479
+ const stdout = await run([
10480
+ "issue",
10481
+ "list",
10482
+ "--state",
10483
+ "open",
10484
+ "--json",
10485
+ OPEN_LIST_FIELDS,
10486
+ "--limit",
10487
+ String(OPEN_ISSUES_LIMIT),
10488
+ "--label",
10489
+ `${GITHUB_LABELS.typePrefix}${type}`
10490
+ ], LIST_COMMAND);
10491
+ return listedOpenIssues(stdout).map((row2) => ({ ref: refOfRow(row2), title: row2.title, body: row2.body }));
10410
10492
  },
10411
10493
  comment: async (ref, body) => {
10412
10494
  const number = issueNumberOfRef(ref);
@@ -10437,7 +10519,7 @@ function createGithubTracker(options) {
10437
10519
  };
10438
10520
  return Object.freeze(tracker);
10439
10521
  }
10440
- var PREFIX2 = "github tracker", GITHUB_LABELS, VIEW_FIELDS = "number,title,body,state,stateReason,labels,url", LIST_FIELDS = "number,url,labels", DEFAULT_FIND_LIMIT = 30, NOT_PLANNED = "NOT_PLANNED", ISSUE_NUMBER, REPO_PART, CREATED_ISSUE_URL, REST_STATES, TIMED_OUT, DRAFT_CHECKS;
10522
+ var PREFIX2 = "github tracker", GITHUB_LABELS, VIEW_FIELDS = "number,title,body,state,stateReason,labels,url", LIST_COMMAND = "gh issue list", LIST_FIELDS = "number,url,labels", OPEN_LIST_FIELDS = "number,url,labels,title,body", OPEN_ISSUES_LIMIT = 1000, DEFAULT_FIND_LIMIT = 30, CANCELLED_REASONS, ISSUE_NUMBER, REPO_PART, CREATED_ISSUE_URL, REST_STATES, TIMED_OUT, DRAFT_CHECKS;
10441
10523
  var init_github = __esm(() => {
10442
10524
  init_config_sections();
10443
10525
  init_issue_values();
@@ -10448,6 +10530,7 @@ var init_github = __esm(() => {
10448
10530
  needsTriage: "needs-triage",
10449
10531
  blockedByPrefix: "blocked-by:OPT-"
10450
10532
  });
10533
+ CANCELLED_REASONS = new Set(["NOT_PLANNED", "DUPLICATE"]);
10451
10534
  ISSUE_NUMBER = /^[1-9]\d*$/;
10452
10535
  REPO_PART = /^[\w.-]+$/;
10453
10536
  CREATED_ISSUE_URL = /^https:\/\/[^\s/]+\/[\w.-]+\/[\w.-]+\/issues\/([1-9]\d*)$/;
@@ -10601,7 +10684,7 @@ function parseLocalIssue(contents, sourcePath) {
10601
10684
  function warnThroughActiveOutput2(message) {
10602
10685
  activeOutput().warn(message);
10603
10686
  }
10604
- async function listLocalIssues(issuesDir, warn = warnThroughActiveOutput2) {
10687
+ async function listLocalIssues(issuesDir, warn = warnThroughActiveOutput2, reader = "find") {
10605
10688
  const numbers = await issueNumbers(issuesDir);
10606
10689
  const settled = await Promise.allSettled(numbers.map(async (number) => {
10607
10690
  const path = issuePath(issuesDir, number);
@@ -10616,7 +10699,7 @@ async function listLocalIssues(issuesDir, warn = warnThroughActiveOutput2) {
10616
10699
  return settled.flatMap((result) => {
10617
10700
  if (result.status === "fulfilled")
10618
10701
  return [result.value];
10619
- warn(`${messageOf(result.reason)}; find skipped it`);
10702
+ warn(`${messageOf(result.reason)}; ${reader} skipped it`);
10620
10703
  return [];
10621
10704
  });
10622
10705
  }
@@ -10702,6 +10785,14 @@ function createLocalTracker(options) {
10702
10785
  const matched = (await listLocalIssues(issuesDir, warn)).filter(({ record }) => matchesQuery(record, query));
10703
10786
  return matched.slice(0, query.limit ?? matched.length).map(({ number, record }) => refFor(number, record.draft.opt));
10704
10787
  },
10788
+ openIssues: async (type) => {
10789
+ const open = (await listLocalIssues(issuesDir, warn, "openIssues")).filter(({ record }) => record.draft.type === type && !CLOSED_STATES.has(record.state));
10790
+ return open.map(({ number, record }) => ({
10791
+ ref: refFor(number, record.draft.opt),
10792
+ title: record.draft.title,
10793
+ body: record.draft.body
10794
+ }));
10795
+ },
10705
10796
  comment: async (ref, body) => {
10706
10797
  const { number, record } = await read(ref);
10707
10798
  await rewrite(number, { ...record, comments: [...record.comments, `${now()}: ${body}`] });
@@ -10714,13 +10805,14 @@ function createLocalTracker(options) {
10714
10805
  };
10715
10806
  return Object.freeze(tracker);
10716
10807
  }
10717
- var PREFIX3 = "local tracker", FRONTMATTER, ISSUE_NUMBER2, ISSUE_EXTENSION = ".md", FIELD_CHECKS;
10808
+ var PREFIX3 = "local tracker", FRONTMATTER, ISSUE_NUMBER2, CLOSED_STATES, ISSUE_EXTENSION = ".md", FIELD_CHECKS;
10718
10809
  var init_local2 = __esm(() => {
10719
10810
  init_config_sections();
10720
10811
  init_active();
10721
10812
  init_issue_values();
10722
10813
  FRONTMATTER = /^---\n([\s\S]*?)\n---\n([\s\S]*)$/;
10723
10814
  ISSUE_NUMBER2 = /^[1-9]\d*$/;
10815
+ CLOSED_STATES = new Set(["done", "released", "cancelled"]);
10724
10816
  FIELD_CHECKS = {
10725
10817
  opt: [isFiniteNumber, "a number"],
10726
10818
  type: [oneOf3(ISSUE_TYPES), `one of: ${ISSUE_TYPES.join(", ")}`],
@@ -13037,7 +13129,7 @@ var init_store2 = __esm(() => {
13037
13129
 
13038
13130
  // src/effort/session-log.ts
13039
13131
  import { createReadStream } from "fs";
13040
- import { basename as basename5 } from "path";
13132
+ import { basename as basename6 } from "path";
13041
13133
  import { createInterface } from "readline";
13042
13134
  function emptyUsageTotals() {
13043
13135
  return {
@@ -13065,7 +13157,7 @@ function bump(counts, key) {
13065
13157
  counts[key] = (counts[key] ?? 0) + 1;
13066
13158
  }
13067
13159
  function sessionIdFromPath(filePath) {
13068
- return basename5(filePath).replace(/\.jsonl$/i, "");
13160
+ return basename6(filePath).replace(/\.jsonl$/i, "");
13069
13161
  }
13070
13162
  async function* readLines(filePath) {
13071
13163
  const stream = createReadStream(filePath, { encoding: "utf8" });
@@ -13786,7 +13878,7 @@ var init_skill_invocations = __esm(() => {
13786
13878
 
13787
13879
  // src/effort/skill-use.ts
13788
13880
  import { existsSync as existsSync17, readdirSync as readdirSync6 } from "fs";
13789
- import { dirname as dirname15, join as join27 } from "path";
13881
+ import { dirname as dirname16, join as join27 } from "path";
13790
13882
  function asObject4(value) {
13791
13883
  return typeof value === "object" && value !== null && !Array.isArray(value) ? value : null;
13792
13884
  }
@@ -13798,7 +13890,7 @@ function parseRecord4(line) {
13798
13890
  }
13799
13891
  }
13800
13892
  function subagentLogPaths(sessionLogPath) {
13801
- const dir = join27(dirname15(sessionLogPath), sessionIdFromPath(sessionLogPath), "subagents");
13893
+ const dir = join27(dirname16(sessionLogPath), sessionIdFromPath(sessionLogPath), "subagents");
13802
13894
  if (!existsSync17(dir))
13803
13895
  return [];
13804
13896
  return readdirSync6(dir, { withFileTypes: true }).filter((entry) => entry.isFile() && SUBAGENT_LOG_NAME.test(entry.name)).map((entry) => join27(dir, entry.name)).sort((a, b) => a.localeCompare(b));
@@ -14425,7 +14517,7 @@ var init_dispatches = __esm(() => {
14425
14517
  });
14426
14518
 
14427
14519
  // src/preflight/prerequisites-md.ts
14428
- import { basename as basename6, dirname as dirname16, join as join29 } from "path";
14520
+ import { basename as basename7, dirname as dirname17, join as join29 } from "path";
14429
14521
  function inferSectionTag(header) {
14430
14522
  if (SECTION_AUTO_RE.test(header))
14431
14523
  return "auto";
@@ -14563,10 +14655,10 @@ function mergePlanPrerequisites(settings, content) {
14563
14655
  });
14564
14656
  }
14565
14657
  function prerequisitesPathForPlan(planPath) {
14566
- const stub = PLAN_FILE_RE.exec(basename6(planPath))?.[1];
14658
+ const stub = PLAN_FILE_RE.exec(basename7(planPath))?.[1];
14567
14659
  if (stub === undefined)
14568
14660
  return null;
14569
- return join29(dirname16(planPath), `PREREQUISITES-${stub}.md`);
14661
+ return join29(dirname17(planPath), `PREREQUISITES-${stub}.md`);
14570
14662
  }
14571
14663
  async function readIfPresent2(path) {
14572
14664
  try {
@@ -16363,6 +16455,27 @@ var init_handout = __esm(() => {
16363
16455
  EMPTY_RESOLUTION = { loadedTiers: [], items: [], collisions: [] };
16364
16456
  });
16365
16457
 
16458
+ // src/start/inherited-notice.ts
16459
+ function oneLine5(text2) {
16460
+ return text2.replace(/\s+/g, " ").trim();
16461
+ }
16462
+ function failureLine(failure2) {
16463
+ return `- \`${oneLine5(failure2.file)}\`: ${oneLine5(failure2.name)}`;
16464
+ }
16465
+ function renderInheritedSection(failures) {
16466
+ if (failures.length === 0)
16467
+ return "";
16468
+ const listed3 = failures.slice(0, INHERITED_LIST_CAP).map(failureLine);
16469
+ const rest = failures.length - listed3.length;
16470
+ const more = rest > 0 ? [`- ...and ${rest} more.`] : [];
16471
+ return [INHERITED_HEADING, INHERITED_SENTENCE, ...listed3, ...more].join(`
16472
+ `);
16473
+ }
16474
+ var INHERITED_HEADING = "## Failures this run inherited", INHERITED_SENTENCE, INHERITED_LIST_CAP = 20;
16475
+ var init_inherited_notice = __esm(() => {
16476
+ 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.";
16477
+ });
16478
+
16366
16479
  // src/effort/store/preflight.ts
16367
16480
  import { existsSync as existsSync24 } from "fs";
16368
16481
  function refusedWrite6(reason) {
@@ -17227,7 +17340,7 @@ var init_blocked = __esm(() => {
17227
17340
 
17228
17341
  // src/commands/plan/validate.ts
17229
17342
  import { readFileSync as readFileSync21 } from "fs";
17230
- import { basename as basename7, resolve as resolve11 } from "path";
17343
+ import { basename as basename8, resolve as resolve11 } from "path";
17231
17344
  function readFileOrNull(path) {
17232
17345
  return path !== null && isFile(path) ? readFileSync21(path, "utf8") : null;
17233
17346
  }
@@ -17236,7 +17349,7 @@ function checkStoreRules(file, markdown) {
17236
17349
  return findStoreRuleProblems({
17237
17350
  plan: markdown,
17238
17351
  prerequisites: readFileOrNull(prerequisitesPath),
17239
- prerequisitesName: basename7(prerequisitesPath ?? "PREREQUISITES-<stub>.md")
17352
+ prerequisitesName: basename8(prerequisitesPath ?? "PREREQUISITES-<stub>.md")
17240
17353
  });
17241
17354
  }
17242
17355
  function validatePlan(file) {
@@ -17552,7 +17665,7 @@ var init_review_stamp = __esm(() => {
17552
17665
 
17553
17666
  // src/board/gate.ts
17554
17667
  import { existsSync as existsSync26, mkdirSync as mkdirSync8, readFileSync as readFileSync22, renameSync as renameSync5, statSync as statSync10, writeFileSync as writeFileSync7 } from "fs";
17555
- import { basename as basename8, dirname as dirname17, join as join31, resolve as resolve12 } from "path";
17668
+ import { basename as basename9, dirname as dirname18, join as join31, resolve as resolve12 } from "path";
17556
17669
  function readGateFlags(args) {
17557
17670
  return {
17558
17671
  skipReview: args.includes(SKIP_REVIEW_FLAG),
@@ -17592,8 +17705,8 @@ function isWrittenFile(repoRoot, path) {
17592
17705
  return existsSync26(full) && statSync10(full).isFile();
17593
17706
  }
17594
17707
  function rejectedPath(path) {
17595
- const parent = dirname17(path);
17596
- return parent === "." ? join31(REJECTED_DIR, basename8(path)) : join31(parent, REJECTED_DIR, basename8(path));
17708
+ const parent = dirname18(path);
17709
+ return parent === "." ? join31(REJECTED_DIR, basename9(path)) : join31(parent, REJECTED_DIR, basename9(path));
17597
17710
  }
17598
17711
  function moveWritten(options, output, reason) {
17599
17712
  for (const path of [options.planPath, options.prerequisitesPath]) {
@@ -17601,7 +17714,7 @@ function moveWritten(options, output, reason) {
17601
17714
  continue;
17602
17715
  const destination = rejectedPath(path);
17603
17716
  try {
17604
- mkdirSync8(resolve12(options.repoRoot, dirname17(destination)), { recursive: true });
17717
+ mkdirSync8(resolve12(options.repoRoot, dirname18(destination)), { recursive: true });
17605
17718
  renameSync5(resolve12(options.repoRoot, path), resolve12(options.repoRoot, destination));
17606
17719
  output.info(`\uD83D\uDDC3 Moved ${path} to ${destination}: ${reason}`);
17607
17720
  } catch (error) {
@@ -18064,7 +18177,7 @@ var init_naming = __esm(() => {
18064
18177
 
18065
18178
  // src/board/previous-copy.ts
18066
18179
  import { linkSync as linkSync2, mkdirSync as mkdirSync9, readdirSync as readdirSync8, statSync as statSync11, unlinkSync } from "fs";
18067
- import { basename as basename9, extname, join as join32, resolve as resolve13 } from "path";
18180
+ import { basename as basename10, extname, join as join32, resolve as resolve13 } from "path";
18068
18181
  function previousDir(specsDir) {
18069
18182
  return join32(specsDir, PREVIOUS_DIR_NAME);
18070
18183
  }
@@ -18077,7 +18190,7 @@ function previousTimestamp(mtime) {
18077
18190
  return `${date}T${time}Z`;
18078
18191
  }
18079
18192
  function previousCopyName(savedName, timestamp, attempt) {
18080
- const stem = basename9(savedName, extname(savedName));
18193
+ const stem = basename10(savedName, extname(savedName));
18081
18194
  const suffix = attempt < FIRST_SUFFIX ? "" : `-${String(attempt)}`;
18082
18195
  return `${stem}.${timestamp}${suffix}${COPY_EXTENSION}`;
18083
18196
  }
@@ -18101,7 +18214,7 @@ function movePreviousCopy(options) {
18101
18214
  const source = resolve13(repoRoot, path2);
18102
18215
  const directory2 = previousDir(specsDir);
18103
18216
  const absoluteDirectory = resolve13(repoRoot, directory2);
18104
- const savedName = basename9(path2);
18217
+ const savedName = basename10(path2);
18105
18218
  let name2;
18106
18219
  try {
18107
18220
  const timestamp = previousTimestamp(statSync11(source).mtime);
@@ -18141,7 +18254,7 @@ var init_previous_copy = __esm(() => {
18141
18254
 
18142
18255
  // src/board/issue.ts
18143
18256
  import { existsSync as existsSync27, mkdirSync as mkdirSync10, readFileSync as readFileSync23, statSync as statSync12, writeFileSync as writeFileSync8 } from "fs";
18144
- import { dirname as dirname18, resolve as resolve14 } from "path";
18257
+ import { dirname as dirname19, resolve as resolve14 } from "path";
18145
18258
  function detailOf3(result, command) {
18146
18259
  const written = result.stderr.trim() || result.stdout.trim();
18147
18260
  return written === "" ? `${command} failed and wrote nothing` : `${command} failed: ${written}`;
@@ -18366,7 +18479,7 @@ function writeSpecSnapshot(options) {
18366
18479
  }
18367
18480
  const previous = action === "created" ? null : movePreviousCopy({ repoRoot, specsDir, path: path2 }).path;
18368
18481
  try {
18369
- mkdirSync10(dirname18(absolute), { recursive: true });
18482
+ mkdirSync10(dirname19(absolute), { recursive: true });
18370
18483
  writeFileSync8(absolute, text2);
18371
18484
  } catch (error) {
18372
18485
  throw new Error(`${PREFIX5}: the snapshot ${path2} could not be written: ${messageOf(error)}`, { cause: error });
@@ -19021,6 +19134,11 @@ function createGhPullRequests(options) {
19021
19134
  const text2 = textArgument(title2, "editTitle", "title", false);
19022
19135
  await succeed(["pr", "edit", target, "--title", text2], `gh pr edit ${target} --title <title>`);
19023
19136
  },
19137
+ editBase: async (number, base) => {
19138
+ const target = pullNumber(number, "editBase");
19139
+ const branch = textArgument(base, "editBase", "base", false);
19140
+ await succeed(["pr", "edit", target, "--base", branch], `gh pr edit ${target} --base ${branch}`);
19141
+ },
19024
19142
  editBody: async (number, body) => {
19025
19143
  const target = pullNumber(number, "editBody");
19026
19144
  const text2 = bodyText(body, "editBody");
@@ -19918,7 +20036,7 @@ var init_setup_config = __esm(() => {
19918
20036
 
19919
20037
  // src/board/setup.ts
19920
20038
  import { existsSync as existsSync28, lstatSync, mkdirSync as mkdirSync11, readFileSync as readFileSync25, statSync as statSync13, writeFileSync as writeFileSync9 } from "fs";
19921
- import { dirname as dirname19, join as join33 } from "path";
20039
+ import { dirname as dirname20, join as join33 } from "path";
19922
20040
  import { fileURLToPath } from "url";
19923
20041
  function boardChanged(report) {
19924
20042
  return report.parts.some((part) => part.outcome === "created");
@@ -20030,7 +20148,7 @@ function writeShippedFile(root, file) {
20030
20148
  }
20031
20149
  try {
20032
20150
  const text2 = file.read();
20033
- mkdirSync11(dirname19(path2), { recursive: true });
20151
+ mkdirSync11(dirname20(path2), { recursive: true });
20034
20152
  writeFileSync9(path2, text2, { encoding: "utf8", flag: "wx" });
20035
20153
  return partOf("template", name2, "created", `written from ${file.source}`);
20036
20154
  } catch (error) {
@@ -20226,7 +20344,7 @@ var init_setup = __esm(() => {
20226
20344
  init_readiness();
20227
20345
  init_roadmap();
20228
20346
  init_setup_config();
20229
- MODULE_DIR = dirname19(fileURLToPath(import.meta.url));
20347
+ MODULE_DIR = dirname20(fileURLToPath(import.meta.url));
20230
20348
  ROADMAP_LABEL = `${GITHUB_LABELS.typePrefix}roadmap`;
20231
20349
  BOARD_LABELS = Object.freeze([
20232
20350
  {
@@ -20975,7 +21093,7 @@ var init_roadmap_board = __esm(() => {
20975
21093
 
20976
21094
  // src/board/board-cache.ts
20977
21095
  import { mkdirSync as mkdirSync12, readFileSync as readFileSync26, renameSync as renameSync6, writeFileSync as writeFileSync10 } from "fs";
20978
- import { dirname as dirname20, join as join34 } from "path";
21096
+ import { dirname as dirname21, join as join34 } from "path";
20979
21097
  function changedArgs(watermark) {
20980
21098
  return Object.freeze([
20981
21099
  "api",
@@ -21028,7 +21146,7 @@ function fileOf(cache) {
21028
21146
  function writeCache(path2, cache) {
21029
21147
  const temporary = `${path2}.${String(process.pid)}.tmp`;
21030
21148
  try {
21031
- mkdirSync12(dirname20(path2), { recursive: true });
21149
+ mkdirSync12(dirname21(path2), { recursive: true });
21032
21150
  writeFileSync10(temporary, `${JSON.stringify(fileOf(cache))}
21033
21151
  `);
21034
21152
  renameSync6(temporary, path2);
@@ -21960,7 +22078,7 @@ var init_preflight_sync = __esm(() => {
21960
22078
  import { randomUUID as randomUUID10 } from "crypto";
21961
22079
  import { existsSync as existsSync30, readFileSync as readFileSync27 } from "fs";
21962
22080
  import { homedir as homedir4 } from "os";
21963
- import { basename as basename10, join as join35 } from "path";
22081
+ import { basename as basename11, join as join35 } from "path";
21964
22082
  function knownMissingNotice(lines) {
21965
22083
  return lines.length === 0 ? [] : [...lines, KNOWN_MISSING_SENTENCE];
21966
22084
  }
@@ -22007,7 +22125,7 @@ function refuseUnresolvableAgents(options) {
22007
22125
  if (missing2.length === 0 && colliding.length === 0 && unresolved.length === 0)
22008
22126
  return;
22009
22127
  throw new CommandExit(1, [
22010
- `\u274C Refusing to start: ${basename10(path2)} ${refusedNames(missing2.length, colliding.length, unresolved.length)}` + ` (loop.settingSources: ${settings.settingSources.join(", ")}; tiers.rafa: ${settings.tiersRafa}).`,
22128
+ `\u274C Refusing to start: ${basename11(path2)} ${refusedNames(missing2.length, colliding.length, unresolved.length)}` + ` (loop.settingSources: ${settings.settingSources.join(", ")}; tiers.rafa: ${settings.tiersRafa}).`,
22011
22129
  ...missing2.map((agent) => ` ${missingAgentLine(agent)}`),
22012
22130
  ...colliding.map((skill) => ` ${skillCollisionLine(skill)}`),
22013
22131
  ...unresolved.map((skill) => ` ${unresolvedSkillLine(skill)}`),
@@ -22025,11 +22143,11 @@ function refuseStoreRuleBreaks(planPath) {
22025
22143
  const problems = findStoreRuleProblems({
22026
22144
  plan: markdown,
22027
22145
  prerequisites,
22028
- prerequisitesName: basename10(prerequisitesPath ?? "PREREQUISITES-<stub>.md")
22146
+ prerequisitesName: basename11(prerequisitesPath ?? "PREREQUISITES-<stub>.md")
22029
22147
  });
22030
22148
  if (problems.length === 0)
22031
22149
  return;
22032
- const file = basename10(path2);
22150
+ const file = basename11(path2);
22033
22151
  throw new CommandExit(1, [
22034
22152
  `\u274C Refusing to start: ${file} breaks ${problems.length} effort-store rule(s) that \`rafa plan validate\` checks.`,
22035
22153
  ...problems.map((problem) => ` ${storeRuleLine(file, problem)}`),
@@ -22062,7 +22180,7 @@ async function loadItems(options) {
22062
22180
  function refuseMalformedItems(planPath, items) {
22063
22181
  if (items.malformed.length === 0)
22064
22182
  return;
22065
- const file = basename10(prerequisitesPathForPlan(planPath) ?? planPath);
22183
+ const file = basename11(prerequisitesPathForPlan(planPath) ?? planPath);
22066
22184
  const [head = "", ...rest] = malformedPrerequisiteLines(file, items.malformed);
22067
22185
  throw new CommandExit(1, [
22068
22186
  `\u274C Refusing to start: ${head}`,
@@ -22075,7 +22193,7 @@ function skippedStartLine(item, tracker) {
22075
22193
  return `\u23ED start-only item ${item.kind} ${JSON.stringify(item.name)} was not checked:` + " it is probed on a first dispatch alone," + ` and ${tracker} already holds a ticked task`;
22076
22194
  }
22077
22195
  function announceSkippedStart(planPath, skipped2) {
22078
- const tracker = basename10(trackerPathFor(planPath));
22196
+ const tracker = basename11(trackerPathFor(planPath));
22079
22197
  const output = activeOutput();
22080
22198
  for (const [index, item] of skipped2.entries()) {
22081
22199
  const lead = index === 0 ? `
@@ -22094,7 +22212,7 @@ function startTier(planPath, items) {
22094
22212
  function announceReminders(planPath, reminders) {
22095
22213
  if (reminders.length === 0)
22096
22214
  return;
22097
- const file = basename10(prerequisitesPathForPlan(planPath) ?? planPath);
22215
+ const file = basename11(prerequisitesPathForPlan(planPath) ?? planPath);
22098
22216
  const output = activeOutput();
22099
22217
  output.info(`
22100
22218
  \uD83D\uDCCC ${file} names ${reminders.length} step(s) the preflight does not check:`);
@@ -22245,16 +22363,16 @@ function baseLines(base) {
22245
22363
  return [`${BASE_PROMPT_PREFIX}${base}: run \`bun test --changed=${base}\` for the tests your changes reach.`];
22246
22364
  }
22247
22365
  function sectionLines(sections2) {
22248
- return [sections2.skills, sections2.lessons].filter((section2) => section2.trim().length > 0).flatMap((section2) => [section2, ""]);
22366
+ return [sections2.skills, sections2.lessons, sections2.inherited].filter((section2) => section2.trim().length > 0).flatMap((section2) => [section2, ""]);
22249
22367
  }
22250
- function buildTaskPrompt(taskText, promptContent, planText, knownMissing = [], blocker = null, sections2 = NO_TASK_SECTIONS, base = null) {
22368
+ function buildTaskPrompt(taskText, promptContent, planText, knownMissing = [], blocker = null, sections2 = NO_TASK_SECTIONS, base = null, inherited = []) {
22251
22369
  return [
22252
22370
  `Your scoped task is: ${taskText}`,
22253
22371
  "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.",
22254
22372
  ...blockerLines(blocker),
22255
22373
  ...baseLines(base),
22256
22374
  "",
22257
- ...sectionLines(sections2),
22375
+ ...sectionLines({ ...sections2, inherited: renderInheritedSection(inherited) }),
22258
22376
  promptContent,
22259
22377
  planText,
22260
22378
  ...knownMissingNotice(knownMissing)
@@ -22296,7 +22414,7 @@ async function dispatchTask(options) {
22296
22414
  const resolution = options.serving === null ? EMPTY_RESOLUTION : resolveSessionTiers(options.serving);
22297
22415
  const task = taskInputFor(taskInfo, taskText, declaration, options.planContent);
22298
22416
  const handed = await handOut(options.handout, task, resolution, options.repoRoot);
22299
- const prompt = withStamp(buildTaskPrompt(taskText, options.promptContent, injection.text, options.knownMissing, taskInfo.blocker ?? null, { skills: renderSkillsSection(handed.skills), lessons: renderLessonsSection(handed.lessons) }, options.base));
22417
+ 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));
22300
22418
  const served2 = serveForSession(options.serving, resolution);
22301
22419
  const sessionId = (options.newSessionId ?? randomUUID11)();
22302
22420
  const session = await run(prompt, flags, sessionId, options.settingSources, served2, options.checkout);
@@ -22415,6 +22533,7 @@ var init_dispatch = __esm(() => {
22415
22533
  init_progress();
22416
22534
  init_tracker();
22417
22535
  init_handout();
22536
+ init_inherited_notice();
22418
22537
  init_preflight2();
22419
22538
  init_serving();
22420
22539
  init_stamp2();
@@ -22636,7 +22755,7 @@ var QUOTE_WINDOW = 3;
22636
22755
  // src/inventory/search/scratch.ts
22637
22756
  import { copyFileSync as copyFileSync2, mkdirSync as mkdirSync13, mkdtempSync as mkdtempSync2, rmSync as rmSync8 } from "fs";
22638
22757
  import { tmpdir as tmpdir4 } from "os";
22639
- import { basename as basename11, join as join36 } from "path";
22758
+ import { basename as basename12, join as join36 } from "path";
22640
22759
  function safeName(name2) {
22641
22760
  return name2 !== "" && name2 !== "." && name2 !== ".." && !/[\\/]/.test(name2);
22642
22761
  }
@@ -22661,7 +22780,7 @@ function createScratchCopy(candidates, root = tmpdir4()) {
22661
22780
  for (const { name: name2, path: path2 } of candidates) {
22662
22781
  const target = join36(dir, name2);
22663
22782
  mkdirSync13(target);
22664
- const copy = join36(target, basename11(path2));
22783
+ const copy = join36(target, basename12(path2));
22665
22784
  copyFileSync2(path2, copy);
22666
22785
  paths.set(name2, copy);
22667
22786
  }
@@ -23086,7 +23205,7 @@ var init_show2 = __esm(() => {
23086
23205
 
23087
23206
  // src/commands/agent/vendor.ts
23088
23207
  import { copyFileSync as copyFileSync3, existsSync as existsSync32, mkdirSync as mkdirSync14, readFileSync as readFileSync28, writeFileSync as writeFileSync11 } from "fs";
23089
- import { basename as basename12, join as join38 } from "path";
23208
+ import { basename as basename13, join as join38 } from "path";
23090
23209
  function readForce(value) {
23091
23210
  if (value === undefined || value === false || value === "false")
23092
23211
  return false;
@@ -23152,7 +23271,7 @@ function planCopies(names, project, force, entry) {
23152
23271
  problems.push(` agent "${name2}": no definition under ${searched} carries that name`);
23153
23272
  continue;
23154
23273
  }
23155
- const to = join38(dir, basename12(holder.definition.path));
23274
+ const to = join38(dir, basename13(holder.definition.path));
23156
23275
  const replaced = existsSync32(to);
23157
23276
  if (replaced && !force) {
23158
23277
  problems.push(` agent "${name2}": ${to} is already there; pass --force to replace it`);
@@ -33135,7 +33254,7 @@ import {
33135
33254
  statSync as statSync20,
33136
33255
  symlinkSync
33137
33256
  } from "fs";
33138
- import { basename as basename13, delimiter as delimiter4, dirname as dirname21, join as join49, relative as relative11, resolve as resolve21 } from "path";
33257
+ import { basename as basename14, delimiter as delimiter4, dirname as dirname22, join as join49, relative as relative11, resolve as resolve21 } from "path";
33139
33258
  function exitCodeFor(outcome2) {
33140
33259
  switch (outcome2.kind) {
33141
33260
  case "done":
@@ -33241,7 +33360,7 @@ function swapIn2(staging, to) {
33241
33360
  rmSync9(outgoing, { recursive: true, force: true });
33242
33361
  }
33243
33362
  function relink(linkPath, target) {
33244
- mkdirSync16(dirname21(linkPath), { recursive: true });
33363
+ mkdirSync16(dirname22(linkPath), { recursive: true });
33245
33364
  const temporary = temporaryName(linkPath);
33246
33365
  rmSync9(temporary, { force: true });
33247
33366
  try {
@@ -33264,7 +33383,7 @@ function runBuild(repoRoot, lines) {
33264
33383
  cwd: repoRoot,
33265
33384
  env: {
33266
33385
  ...process.env,
33267
- PATH: [dirname21(process.execPath), process.env["PATH"] ?? ""].join(delimiter4)
33386
+ PATH: [dirname22(process.execPath), process.env["PATH"] ?? ""].join(delimiter4)
33268
33387
  },
33269
33388
  stdin: "ignore",
33270
33389
  stdout: stdio,
@@ -33356,7 +33475,7 @@ function replaceFile(source, target) {
33356
33475
  }
33357
33476
  }
33358
33477
  function temporaryName(path3) {
33359
- return join49(dirname21(path3), `.${basename13(path3)}.snapshot-${process.pid}`);
33478
+ return join49(dirname22(path3), `.${basename14(path3)}.snapshot-${process.pid}`);
33360
33479
  }
33361
33480
  var LINK_NAME = "rafa", FORCE_FLAG = "--force", EXIT_DONE = 0, EXIT_REFUSED = 1, EXIT_COULD_NOT_RUN = 2, TRACKER_NAME, NOTHING_DONE = "nothing was built, copied or linked", AFTERMATH, StageFailure;
33362
33481
  var init_install = __esm(() => {
@@ -35691,6 +35810,47 @@ function hasBranch(refs, stub) {
35691
35810
  const branch = `${BRANCH_PREFIX2}${stub}`;
35692
35811
  return refs.some((ref7) => ref7 === branch || ref7.endsWith(`/${branch}`));
35693
35812
  }
35813
+ function readBranchTips(sources, remote, stub, note) {
35814
+ const branch = `${BRANCH_PREFIX2}${stub}`;
35815
+ const tips = [];
35816
+ for (const ref7 of [`refs/heads/${branch}`, `refs/remotes/${remote}/${branch}`]) {
35817
+ const resolved = sources.git(["rev-parse", "--verify", "--quiet", `${ref7}^{commit}`]);
35818
+ const said = gitSaid(resolved);
35819
+ if (resolved.ok && resolved.stdout.trim() !== "") {
35820
+ tips.push(ref7);
35821
+ } else if (said !== "") {
35822
+ note(`\`${ref7}\` could not be read, so its plan reads as started: ${said}`);
35823
+ return null;
35824
+ }
35825
+ }
35826
+ return tips;
35827
+ }
35828
+ function readBranchMessages(sources, ref7, note) {
35829
+ const result = sources.git(["log", "-z", "--format=%B", `${sources.base}..${ref7}`]);
35830
+ if (!result.ok) {
35831
+ note(`the commits of \`${ref7}\` past \`${sources.base}\` could not be read, so its plan reads as started: ${gitSaid(result)}`);
35832
+ return null;
35833
+ }
35834
+ return result.stdout.split("\x00").map((message) => message.trim()).filter((message) => message !== "");
35835
+ }
35836
+ function claimsOnly(messages) {
35837
+ return messages.length > 0 && messages.every((message) => parseClaimMessage(message).kind === "ownership");
35838
+ }
35839
+ function hasStartedBranch(sources, remote, refs, stub, note) {
35840
+ if (!hasBranch(refs, stub))
35841
+ return false;
35842
+ const tips = readBranchTips(sources, remote, stub, note);
35843
+ if (tips === null)
35844
+ return true;
35845
+ if (tips.length === 0) {
35846
+ note(`\`${BRANCH_PREFIX2}${stub}\` is on ${remote} but not fetched here, so its commits were not read and its plan reads as started`);
35847
+ return true;
35848
+ }
35849
+ return tips.some((ref7) => {
35850
+ const messages = readBranchMessages(sources, ref7, note);
35851
+ return messages === null || !claimsOnly(messages);
35852
+ });
35853
+ }
35694
35854
  function openWorld(sources) {
35695
35855
  let problems = [];
35696
35856
  const note = (problem) => {
@@ -35715,7 +35875,7 @@ function openWorld(sources) {
35715
35875
  tracked: once2(() => readTracked(sources, note)),
35716
35876
  checkout: once2(() => readCheckout(sources, note)),
35717
35877
  branchPlan: once2(() => readBranchPlan(branch(), plans())),
35718
- unstartedPlan: once2(() => plans().find((plan) => !hasRun(runs(), plan.stub) && !hasBranch(refs(), plan.stub)) ?? null),
35878
+ unstartedPlan: once2(() => plans().find((plan) => !hasRun(runs(), plan.stub) && !hasStartedBranch(sources, remote, refs(), plan.stub, note)) ?? null),
35719
35879
  openPull: once2(() => readOpenPull(sources, branch(), note)),
35720
35880
  roadmap,
35721
35881
  picked: once2(async () => {
@@ -35743,6 +35903,7 @@ function branchLabel(world) {
35743
35903
  var DETACHED = "HEAD", PORCELAIN_PATH_AT = 3;
35744
35904
  var init_readings = __esm(() => {
35745
35905
  init_roadmap();
35906
+ init_record3();
35746
35907
  init_loop_sessions();
35747
35908
  init_list5();
35748
35909
  init_plan_files();
@@ -35752,7 +35913,7 @@ var init_readings = __esm(() => {
35752
35913
  });
35753
35914
 
35754
35915
  // src/next/hop-rows.ts
35755
- function oneLine5(text2) {
35916
+ function oneLine6(text2) {
35756
35917
  return text2.replace(/\s+/g, " ").trim();
35757
35918
  }
35758
35919
  function ref7(issue4) {
@@ -35793,7 +35954,7 @@ async function readGate(roadmap, pullRequest) {
35793
35954
  } catch (error) {
35794
35955
  return {
35795
35956
  state: "unknown",
35796
- reason: `could not read the owner gate of ${ref7(pullRequest)}: ${oneLine5(messageOf(error))}`,
35957
+ reason: `could not read the owner gate of ${ref7(pullRequest)}: ${oneLine6(messageOf(error))}`,
35797
35958
  owners: []
35798
35959
  };
35799
35960
  }
@@ -35829,7 +35990,7 @@ async function readPrOwnerReview(world) {
35829
35990
  id: "pr-owner-review",
35830
35991
  action: "none",
35831
35992
  reading: `waiting on ${ref7(issue4 ?? pullRequest)} (owner review)`,
35832
- proposal: `leave ${ref7(pullRequest)} to its owner's review, which rafa does not merge past: ${oneLine5(gate.reason)}`,
35993
+ proposal: `leave ${ref7(pullRequest)} to its owner's review, which rafa does not merge past: ${oneLine6(gate.reason)}`,
35833
35994
  pullRequest,
35834
35995
  ...issue4 === null ? {} : { issue: issue4 }
35835
35996
  };
@@ -35979,7 +36140,7 @@ function nameFiles(paths) {
35979
36140
  const hidden = paths.length - shown2.length;
35980
36141
  return hidden > 0 ? `${shown2.join(", ")} and ${hidden} more` : shown2.join(", ");
35981
36142
  }
35982
- function oneLine6(text2) {
36143
+ function oneLine7(text2) {
35983
36144
  return text2.replace(/\s+/g, " ").trim();
35984
36145
  }
35985
36146
  function treeLabel(checkout) {
@@ -36005,7 +36166,7 @@ async function readPullsUnusable(world) {
36005
36166
  return {
36006
36167
  id: "pulls-unusable",
36007
36168
  action: "none",
36008
- reading: `the \`${world.sources.pulls.kind}\` pull request provider could not be asked: ${oneLine6(messageOf(error))}`,
36169
+ reading: `the \`${world.sources.pulls.kind}\` pull request provider could not be asked: ${oneLine7(messageOf(error))}`,
36009
36170
  proposal: "run `rafa doctor` to see what the provider needs, then read the state again"
36010
36171
  };
36011
36172
  }
@@ -38083,7 +38244,7 @@ var init_doctor_epics = __esm(() => {
38083
38244
 
38084
38245
  // src/board/epic-guard.ts
38085
38246
  import { existsSync as existsSync43, readFileSync as readFileSync46 } from "fs";
38086
- import { dirname as dirname22, join as join56 } from "path";
38247
+ import { dirname as dirname23, join as join56 } from "path";
38087
38248
  import { fileURLToPath as fileURLToPath2 } from "url";
38088
38249
  function epicGuardSource(moduleDir = MODULE_DIR2) {
38089
38250
  return join56(moduleDir, TEMPLATES_DIRNAME, EPIC_GUARD_FILE);
@@ -38106,7 +38267,7 @@ function writeEpicGuard(root, moduleDir = MODULE_DIR2) {
38106
38267
  var MODULE_DIR2, EPIC_GUARD_PATH, EPIC_GUARD_FILE = "epic-guard.yml";
38107
38268
  var init_epic_guard = __esm(() => {
38108
38269
  init_setup();
38109
- MODULE_DIR2 = dirname22(fileURLToPath2(import.meta.url));
38270
+ MODULE_DIR2 = dirname23(fileURLToPath2(import.meta.url));
38110
38271
  EPIC_GUARD_PATH = join56(".github", "workflows", "epic-guard.yml");
38111
38272
  });
38112
38273
 
@@ -38862,7 +39023,7 @@ var init_doctor_relations = __esm(() => {
38862
39023
  });
38863
39024
 
38864
39025
  // src/commands/doctor-render.ts
38865
- import { basename as basename14, relative as relative13, sep as sep4 } from "path";
39026
+ import { basename as basename15, relative as relative13, sep as sep4 } from "path";
38866
39027
  function shownPath(path3, root) {
38867
39028
  return path3.startsWith(`${root}${sep4}`) ? relative13(root, path3) : path3;
38868
39029
  }
@@ -38881,7 +39042,7 @@ function headLine2(preflight) {
38881
39042
  const checked2 = checkedPhrase(count2, automatic, "from the config ");
38882
39043
  return `Preflight with no plan, none being at ${where}: ${checked2}, no run started.`;
38883
39044
  }
38884
- const merged = prerequisitesFile === null ? "" : `, with ${basename14(prerequisitesFile)} merged in`;
39045
+ const merged = prerequisitesFile === null ? "" : `, with ${basename15(prerequisitesFile)} merged in`;
38885
39046
  const checked = checkedPhrase(count2, automatic, "");
38886
39047
  return `Preflight for ${shownPath(plan, root)}${merged}: ${checked}, no run started.`;
38887
39048
  }
@@ -38903,7 +39064,7 @@ function reminderLines(preflight) {
38903
39064
  if (reminders.length === 0 || prerequisitesFile === null)
38904
39065
  return [];
38905
39066
  return [
38906
- `${basename14(prerequisitesFile)} names ${plural(reminders.length, "step")} the preflight does not check:`,
39067
+ `${basename15(prerequisitesFile)} names ${plural(reminders.length, "step")} the preflight does not check:`,
38907
39068
  ...reminders.map((reminder) => ` line ${String(reminder.line)}: ${reminder.description}`)
38908
39069
  ];
38909
39070
  }
@@ -40926,7 +41087,7 @@ var init_doctor_release = __esm(() => {
40926
41087
 
40927
41088
  // src/commands/doctor-tiers.ts
40928
41089
  import { readFileSync as readFileSync48, realpathSync as realpathSync12 } from "fs";
40929
- import { basename as basename15, dirname as dirname23 } from "path";
41090
+ import { basename as basename16, dirname as dirname24 } from "path";
40930
41091
  function readTextFile(path4) {
40931
41092
  try {
40932
41093
  return readFileSync48(path4, "utf8");
@@ -40934,7 +41095,7 @@ function readTextFile(path4) {
40934
41095
  return null;
40935
41096
  }
40936
41097
  }
40937
- function realPathOf(path4) {
41098
+ function realPathOf2(path4) {
40938
41099
  try {
40939
41100
  return realpathSync12(path4);
40940
41101
  } catch {
@@ -40984,9 +41145,9 @@ function keptHolder(item) {
40984
41145
  return [item.winner, ...item.copies].find((holder) => holder.source === "rafa") ?? item.winner;
40985
41146
  }
40986
41147
  function deletion(holder) {
40987
- return holder.kind === "skill" && basename15(holder.path) === SKILL_FILE2 ? `delete ${dirname23(holder.path)} (only its ${SKILL_FILE2} was compared)` : `delete ${holder.path}`;
41148
+ return holder.kind === "skill" && basename16(holder.path) === SKILL_FILE2 ? `delete ${dirname24(holder.path)} (only its ${SKILL_FILE2} was compared)` : `delete ${holder.path}`;
40988
41149
  }
40989
- function copyRows(items, realPath = realPathOf) {
41150
+ function copyRows(items, realPath = realPathOf2) {
40990
41151
  return items.flatMap((item) => {
40991
41152
  if (item.state !== "served" || item.copies.length === 0)
40992
41153
  return [];
@@ -41028,7 +41189,7 @@ function doctorTierRows(inventory, installed, readers = {}) {
41028
41189
  ...unreviewedRows(inventory.records, readText7),
41029
41190
  ...cliVersionRows(installed),
41030
41191
  ...skillUseVersionRows(installed),
41031
- ...copyRows(inventory.resolution.items, readers.realPath ?? realPathOf),
41192
+ ...copyRows(inventory.resolution.items, readers.realPath ?? realPathOf2),
41032
41193
  ...noProvenanceRows(inventory, readText7)
41033
41194
  ];
41034
41195
  }
@@ -41076,7 +41237,7 @@ var init_doctor_tiers = __esm(() => {
41076
41237
  });
41077
41238
 
41078
41239
  // src/commands/doctor.ts
41079
- import { basename as basename16, resolve as resolve24 } from "path";
41240
+ import { basename as basename17, resolve as resolve24 } from "path";
41080
41241
  function refusal6(lines2) {
41081
41242
  return new CommandExit(1, [...lines2, NOTHING_CHECKED].join(`
41082
41243
  `));
@@ -41140,7 +41301,7 @@ async function loadItems2(plan, config) {
41140
41301
  function refuseMalformedItems2(plan, items) {
41141
41302
  if (plan === null || items.malformed.length === 0)
41142
41303
  return;
41143
- const file = basename16(prerequisitesPathForPlan(plan) ?? plan);
41304
+ const file = basename17(prerequisitesPathForPlan(plan) ?? plan);
41144
41305
  const [head = "", ...rest] = malformedPrerequisiteLines(file, items.malformed);
41145
41306
  throw refusal6([
41146
41307
  `rafa doctor: ${head}`,
@@ -41165,7 +41326,7 @@ function readStartTier(plan, items) {
41165
41326
  return { tier: NO_START_TIER, items: NO_ITEMS3 };
41166
41327
  if (isFirstDispatch(plan))
41167
41328
  return { tier: { checked: start.length, skipped: 0, tracker: null }, items: start };
41168
- const tracker = basename16(trackerPathFor(plan));
41329
+ const tracker = basename17(trackerPathFor(plan));
41169
41330
  return { tier: { checked: 0, skipped: start.length, tracker }, items: NO_ITEMS3 };
41170
41331
  }
41171
41332
  async function checkPreflight(context, project, seams) {
@@ -41655,7 +41816,7 @@ function formatTaskReports(report) {
41655
41816
  const rows2 = tallies.map(tallyCells);
41656
41817
  return [...lines2, ...alignRows(TASK_REPORT_COLUMNS, rows2, TASK_REPORT_RIGHT_ALIGNED)];
41657
41818
  }
41658
- function oneLine7(text2) {
41819
+ function oneLine8(text2) {
41659
41820
  return text2.replace(/\s+/g, " ").trim();
41660
41821
  }
41661
41822
  function haltCells(halt2, check) {
@@ -41665,7 +41826,7 @@ function haltCells(halt2, check) {
41665
41826
  `${check.kind} ${JSON.stringify(check.item)}`,
41666
41827
  check.outcome,
41667
41828
  formatCount(check.durationMs),
41668
- oneLine7(check.failure)
41829
+ oneLine8(check.failure)
41669
41830
  ];
41670
41831
  }
41671
41832
  function formatPreflightHalts(report) {
@@ -41836,7 +41997,7 @@ function outlierLines(outliers) {
41836
41997
  `outliers: ${plural(outliers.sessions.length, "recent session")} over ${formatMinutes(outliers.limitMinutes)} min` + " (baseline median + 3 robust SD)"
41837
41998
  ];
41838
41999
  for (const session of outliers.sessions.slice(0, OUTLIERS_LISTED)) {
41839
- lines2.push(` ${formatMinutes(session.minutes)} min ${formatTokens(session.outputTokens)} out` + ` ${session.outcome ?? "no report"} ${session.loop}`, ` ${oneLine7(session.taskText ?? "").slice(0, TASK_TEXT_CHARS)}`);
42000
+ lines2.push(` ${formatMinutes(session.minutes)} min ${formatTokens(session.outputTokens)} out` + ` ${session.outcome ?? "no report"} ${session.loop}`, ` ${oneLine8(session.taskText ?? "").slice(0, TASK_TEXT_CHARS)}`);
41840
42001
  }
41841
42002
  if (outliers.sessions.length > OUTLIERS_LISTED) {
41842
42003
  lines2.push(` and ${outliers.sessions.length - OUTLIERS_LISTED} more in --output=json`);
@@ -42303,7 +42464,7 @@ function budgetedSessions(rows2, budgets) {
42303
42464
 
42304
42465
  // src/effort/report-skills-format.ts
42305
42466
  function matchedCell(matches) {
42306
- const texts = [...new Set(matches.map(({ text: text2 }) => oneLine7(text2)))];
42467
+ const texts = [...new Set(matches.map(({ text: text2 }) => oneLine8(text2)))];
42307
42468
  return texts.length === 0 ? "-" : texts.join(" / ");
42308
42469
  }
42309
42470
  function skillSignalCell(row3) {
@@ -42327,7 +42488,7 @@ function skillCells(row3) {
42327
42488
  function lessonCells(row3) {
42328
42489
  return [
42329
42490
  row3.id,
42330
- row3.artifact === null ? "unknown" : oneLine7(row3.artifact),
42491
+ row3.artifact === null ? "unknown" : oneLine8(row3.artifact),
42331
42492
  formatCount(row3.injected),
42332
42493
  formatCount(row3.recurred),
42333
42494
  matchedCell(row3.matches),
@@ -42347,7 +42508,7 @@ function formatMetric2(value) {
42347
42508
  function formatPlanCi(reading2) {
42348
42509
  if (reading2 === null)
42349
42510
  return `${PLAN_CI_LABEL}: not read`;
42350
- const failing = reading2.failing.length === 0 ? "" : `, failing: ${reading2.failing.map(oneLine7).join(", ")}`;
42511
+ const failing = reading2.failing.length === 0 ? "" : `, failing: ${reading2.failing.map(oneLine8).join(", ")}`;
42351
42512
  const sha = reading2.headSha.slice(0, SHORT_SHA_LENGTH);
42352
42513
  return `${PLAN_CI_LABEL}: ${reading2.verdict} on #${reading2.pr} at ${sha}, read ${reading2.readAt}${failing}`;
42353
42514
  }
@@ -45640,7 +45801,7 @@ var init_cancel = __esm(() => {
45640
45801
 
45641
45802
  // src/board/epic-template.ts
45642
45803
  import { existsSync as existsSync46, readFileSync as readFileSync51 } from "fs";
45643
- import { dirname as dirname24, join as join68 } from "path";
45804
+ import { dirname as dirname25, join as join68 } from "path";
45644
45805
  import { fileURLToPath as fileURLToPath3 } from "url";
45645
45806
  function epicTemplateSource(moduleDir = MODULE_DIR3) {
45646
45807
  return join68(moduleDir, TEMPLATES_DIRNAME, EPIC_TEMPLATE_FILE);
@@ -45676,7 +45837,7 @@ var MODULE_DIR3, EPIC_TEMPLATE_FILE = "epic.md", CRITERIA_PLACEHOLDERS, PLACEHOL
45676
45837
  var init_epic_template = __esm(() => {
45677
45838
  init_epic_body();
45678
45839
  init_setup();
45679
- MODULE_DIR3 = dirname24(fileURLToPath3(import.meta.url));
45840
+ MODULE_DIR3 = dirname25(fileURLToPath3(import.meta.url));
45680
45841
  CRITERIA_PLACEHOLDERS = Object.freeze([
45681
45842
  "- Placeholder: one line per thing that makes this epic finished," + " written so a check against main can tell whether it holds."
45682
45843
  ]);
@@ -45757,7 +45918,7 @@ var init_epic_cost = __esm(() => {
45757
45918
 
45758
45919
  // src/epic/verify-plan.ts
45759
45920
  import { existsSync as existsSync47, readFileSync as readFileSync52 } from "fs";
45760
- import { dirname as dirname25, join as join69 } from "path";
45921
+ import { dirname as dirname26, join as join69 } from "path";
45761
45922
  import { fileURLToPath as fileURLToPath4 } from "url";
45762
45923
  function opensCriterion(text2, state) {
45763
45924
  const item = LIST_ITEM2.exec(text2);
@@ -45802,7 +45963,7 @@ function criteriaToAsk(criteria) {
45802
45963
  return Object.freeze(criteria.filter((criterion) => !criterion.placeholder));
45803
45964
  }
45804
45965
  function verifyPromptCandidates(moduleDir = MODULE_DIR4) {
45805
- return [join69(moduleDir, VERIFY_PROMPT_FILE), join69(dirname25(moduleDir), VERIFY_PROMPT_FILE)];
45966
+ return [join69(moduleDir, VERIFY_PROMPT_FILE), join69(dirname26(moduleDir), VERIFY_PROMPT_FILE)];
45806
45967
  }
45807
45968
  function readVerifyPrompt(moduleDir = MODULE_DIR4) {
45808
45969
  const candidates = verifyPromptCandidates(moduleDir);
@@ -45948,7 +46109,7 @@ var MODULE_DIR4, VERIFY_PROMPT_FILE = "epic-verify-prompt.md", VERIFY_BLOCK_FENC
45948
46109
  var init_verify_plan = __esm(() => {
45949
46110
  init_epic_template();
45950
46111
  init_blocks();
45951
- MODULE_DIR4 = dirname25(fileURLToPath4(import.meta.url));
46112
+ MODULE_DIR4 = dirname26(fileURLToPath4(import.meta.url));
45952
46113
  VERIFY_PROMPT_SLOTS = ["EPIC_NUMBER", "EPIC_TITLE", "CRITERIA"];
45953
46114
  FENCE5 = /^\s*(?:`{3,}|~{3,})/u;
45954
46115
  LIST_ITEM2 = /^( {0,3})(?:[-*+]|\d{1,9}[.)])(?:\s|$)/u;
@@ -46153,9 +46314,371 @@ var init_verify_run = __esm(() => {
46153
46314
  VERDICT_KIND = VERDICT_BLOCK_FENCE.slice("rafa:".length);
46154
46315
  });
46155
46316
 
46317
+ // src/suite/baseline.ts
46318
+ import { existsSync as existsSync48, mkdirSync as mkdirSync17, readFileSync as readFileSync53, renameSync as renameSync9, writeFileSync as writeFileSync17 } from "fs";
46319
+ import { basename as basename18, dirname as dirname27, join as join71 } from "path";
46320
+ function baselinePathFor(trackerPath) {
46321
+ const match = PLAN_FILE_NAME2.exec(basename18(trackerPath));
46322
+ if (match === null) {
46323
+ throw new RangeError(`baselinePathFor: ${trackerPath} is not a PLAN or PLAN_TRACKER file`);
46324
+ }
46325
+ return join71(dirname27(trackerPath), `${BASELINE_FILE_PREFIX}${match[1] ?? ""}.json`);
46326
+ }
46327
+ function copyFailure(failure6) {
46328
+ return failure6.message === undefined ? { file: failure6.file, name: failure6.name } : { file: failure6.file, name: failure6.name, message: failure6.message };
46329
+ }
46330
+ function baselineOf(result2, recordedAt, commit) {
46331
+ return {
46332
+ version: BASELINE_VERSION,
46333
+ recordedAt: recordedAt.toISOString(),
46334
+ commit,
46335
+ command: [...result2.command],
46336
+ exitCode: result2.exitCode,
46337
+ summary: result2.summary,
46338
+ failures: result2.failures.map(copyFailure),
46339
+ errors: result2.errors,
46340
+ junit: result2.junit
46341
+ };
46342
+ }
46343
+ function writeBaseline(path4, baseline) {
46344
+ mkdirSync17(dirname27(path4), { recursive: true });
46345
+ const temporary = `${path4}.${process.pid}.tmp`;
46346
+ writeFileSync17(temporary, `${JSON.stringify(baseline, null, 2)}
46347
+ `, "utf8");
46348
+ renameSync9(temporary, path4);
46349
+ }
46350
+ function isStringOrNull(value) {
46351
+ return value === null || typeof value === "string";
46352
+ }
46353
+ function isFailure2(value) {
46354
+ if (typeof value !== "object" || value === null)
46355
+ return false;
46356
+ const record = value;
46357
+ const message = record["message"];
46358
+ return typeof record["file"] === "string" && typeof record["name"] === "string" && (message === undefined || typeof message === "string");
46359
+ }
46360
+ function badField(record) {
46361
+ const checks = [
46362
+ ["recordedAt", (value) => typeof value === "string"],
46363
+ ["commit", isStringOrNull],
46364
+ ["command", (value) => Array.isArray(value) && value.every((part) => typeof part === "string")],
46365
+ ["exitCode", (value) => Number.isInteger(value)],
46366
+ ["summary", isStringOrNull],
46367
+ ["failures", (value) => Array.isArray(value) && value.every(isFailure2)],
46368
+ ["errors", (value) => value === null || Number.isInteger(value)],
46369
+ ["junit", (value) => JUNIT_READINGS.includes(value)]
46370
+ ];
46371
+ const failed3 = checks.find(([key, check]) => !check(record[key]));
46372
+ return failed3?.[0] ?? null;
46373
+ }
46374
+ function asBaseline(parsed) {
46375
+ if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
46376
+ return { state: "unreadable", reason: "not a JSON object" };
46377
+ }
46378
+ const record = parsed;
46379
+ if (record["version"] !== BASELINE_VERSION) {
46380
+ return { state: "unreadable", reason: `version ${JSON.stringify(record["version"])}, expected ${BASELINE_VERSION}` };
46381
+ }
46382
+ const bad = badField(record);
46383
+ if (bad !== null)
46384
+ return { state: "unreadable", reason: `field ${bad} is missing or malformed` };
46385
+ return { state: "read", baseline: parsed };
46386
+ }
46387
+ function readBaseline(path4) {
46388
+ if (!existsSync48(path4))
46389
+ return { state: "missing" };
46390
+ let text2;
46391
+ try {
46392
+ text2 = readFileSync53(path4, "utf8");
46393
+ } catch (error) {
46394
+ const reason = error instanceof Error ? error.message : String(error);
46395
+ return { state: "unreadable", reason };
46396
+ }
46397
+ try {
46398
+ return asBaseline(JSON.parse(text2));
46399
+ } catch {
46400
+ return { state: "unreadable", reason: "not JSON" };
46401
+ }
46402
+ }
46403
+ function failureKey2(failure6) {
46404
+ return JSON.stringify([failure6.file, failure6.name]);
46405
+ }
46406
+ function splitFailures(failures, baseline) {
46407
+ const held = new Set((baseline?.failures ?? []).map(failureKey2));
46408
+ return {
46409
+ fresh: failures.filter((failure6) => !held.has(failureKey2(failure6))),
46410
+ known: failures.filter((failure6) => held.has(failureKey2(failure6)))
46411
+ };
46412
+ }
46413
+ var BASELINE_VERSION = 1, BASELINE_FILE_PREFIX = "SUITE_BASELINE", PLAN_FILE_NAME2, JUNIT_READINGS;
46414
+ var init_baseline = __esm(() => {
46415
+ PLAN_FILE_NAME2 = /^PLAN(?:_TRACKER)?(-.+)?\.md$/;
46416
+ JUNIT_READINGS = ["read", "missing", "unreadable"];
46417
+ });
46418
+
46419
+ // src/triage/test-failure.ts
46420
+ function caseLineOf(line, index) {
46421
+ const fail = FAIL_LINE.exec(line);
46422
+ if (fail !== null)
46423
+ return { index, name: fail[1], file: null };
46424
+ const fileCase = FILE_CASE_LINE.exec(line);
46425
+ return fileCase === null ? null : { index, name: fileCase[2], file: fileCase[1] };
46426
+ }
46427
+ function linesOf3(text2) {
46428
+ return text2.split(/\r?\n/);
46429
+ }
46430
+ function caseLinesOf(lines2) {
46431
+ return lines2.map((line, index) => caseLineOf(line, index)).filter((found) => found !== null);
46432
+ }
46433
+ function testFilesIn(text2) {
46434
+ return text2.match(TEST_FILE_ANYWHERE) ?? [];
46435
+ }
46436
+ function firstEvidence(lines2) {
46437
+ const found = lines2.find((line) => caseLineOf(line, 0) === null && !NOISE_LINES.some((noise) => noise.test(line)));
46438
+ return found === undefined ? null : found.trim().replace(ERROR_PREFIX, "");
46439
+ }
46440
+ function testFileOf(found, artifactLines, what) {
46441
+ if (found.file !== null)
46442
+ return found.file;
46443
+ const above = artifactLines.slice(0, found.index);
46444
+ const header = above.map((line) => FILE_HEADER.exec(line)?.[1]).filter((file) => file !== undefined).at(-1);
46445
+ const before = testFilesIn(above.join(`
46446
+ `));
46447
+ const after = testFilesIn(artifactLines.slice(found.index).join(`
46448
+ `));
46449
+ return header ?? before.at(-1) ?? after[0] ?? testFilesIn(what)[0] ?? null;
46450
+ }
46451
+ function evidenceOf(found, artifactLines) {
46452
+ if (found === null)
46453
+ return firstEvidence(artifactLines);
46454
+ const cases = caseLinesOf(artifactLines);
46455
+ const previous = cases.filter((other) => other.index < found.index).at(-1);
46456
+ const next = cases.find((other) => other.index > found.index);
46457
+ const before = artifactLines.slice(previous === undefined ? 0 : previous.index + 1, found.index);
46458
+ const after = artifactLines.slice(found.index + 1, next === undefined ? artifactLines.length : next.index);
46459
+ return firstEvidence(before) ?? firstEvidence(after);
46460
+ }
46461
+ function testFailureOf(bug) {
46462
+ const artifactLines = linesOf3(bug.artifact ?? "");
46463
+ const what = bug.what ?? "";
46464
+ const inArtifact = caseLinesOf(artifactLines)[0];
46465
+ if (inArtifact !== undefined) {
46466
+ return {
46467
+ file: testFileOf(inArtifact, artifactLines, what),
46468
+ name: inArtifact.name,
46469
+ evidence: evidenceOf(inArtifact, artifactLines)
46470
+ };
46471
+ }
46472
+ const inWhat = caseLinesOf(linesOf3(what))[0];
46473
+ if (inWhat === undefined)
46474
+ return null;
46475
+ return {
46476
+ file: inWhat.file ?? testFilesIn(bug.artifact ?? "")[0] ?? testFilesIn(what)[0] ?? null,
46477
+ name: inWhat.name,
46478
+ evidence: evidenceOf(null, artifactLines)
46479
+ };
46480
+ }
46481
+ var TEST_FILE = "(?:/?(?:[\\w.@+-]+/)*)[\\w.@+-]+\\.(?:test|spec)\\.[cm]?[jt]sx?", TEST_FILE_ANYWHERE, FAIL_LINE, FILE_CASE_LINE, FILE_HEADER, NOISE_LINES, ERROR_PREFIX;
46482
+ var init_test_failure = __esm(() => {
46483
+ TEST_FILE_ANYWHERE = new RegExp(`(?<![\\w.@+/-])${TEST_FILE}(?![\\w])`, "g");
46484
+ FAIL_LINE = /^\s*\(fail\)\s+(.+?)(?:\s+\[\d+(?:\.\d+)?m?s\])?\s*$/;
46485
+ FILE_CASE_LINE = new RegExp(`^\\s*(${TEST_FILE})\\s+>\\s+(.+?)\\s*$`);
46486
+ FILE_HEADER = new RegExp(`^\\s*(${TEST_FILE}):\\s*$`);
46487
+ NOISE_LINES = [
46488
+ /^\s*$/,
46489
+ FILE_HEADER,
46490
+ /^\s*\d+\s+\|/,
46491
+ /^\s*\^+\s*$/,
46492
+ /^\s*at\s/,
46493
+ /^\s*bun test v/
46494
+ ];
46495
+ ERROR_PREFIX = /^error:\s+/;
46496
+ });
46497
+
46498
+ // src/triage/bug-key.ts
46499
+ import { basename as basename19 } from "path";
46500
+ function hasText2(value) {
46501
+ return typeof value === "string" && value.trim().length > 0;
46502
+ }
46503
+ function artifactOf(bug) {
46504
+ return hasText2(bug.artifact) && textProblem(bug.artifact) === null ? bug.artifact : null;
46505
+ }
46506
+ function oneLine9(text2) {
46507
+ return text2.replace(/\s+/g, " ").trim();
46508
+ }
46509
+ function strippedText(text2) {
46510
+ const taken = text2.replace(COMMIT_HASH, "").replace(FOLDER_PREFIX, "").replace(NUMBER, "");
46511
+ return oneLine9(taken);
46512
+ }
46513
+ function keyText(text2) {
46514
+ const stripped = strippedText(text2);
46515
+ return stripped.length > 0 ? stripped : oneLine9(text2);
46516
+ }
46517
+ function testFailureKeyOf(failure6) {
46518
+ const test = failure6.file === null ? oneLine9(failure6.name) : `${basename19(failure6.file)}${CASE_SEPARATOR}${oneLine9(failure6.name)}`;
46519
+ return failure6.evidence === null ? test : `${test}${KEY_SEPARATOR}${keyText(failure6.evidence)}`;
46520
+ }
46521
+ function bugKeyOf(trackerPath, artifact, what = null) {
46522
+ const failure6 = testFailureOf({ what, artifact });
46523
+ return failure6 === null ? `${basename19(trackerPath)}${KEY_SEPARATOR}${keyText(artifact)}` : testFailureKeyOf(failure6);
46524
+ }
46525
+ function legacyBugKeyOf(trackerPath, artifact) {
46526
+ return `${basename19(trackerPath)}${KEY_SEPARATOR}${oneLine9(artifact)}`;
46527
+ }
46528
+ var KEY_SEPARATOR = ": ", CASE_SEPARATOR = " > ", COMMIT_HASH, FOLDER_PREFIX, NUMBER;
46529
+ var init_bug_key = __esm(() => {
46530
+ init_findings();
46531
+ init_test_failure();
46532
+ COMMIT_HASH = /\b[0-9a-f]{7,40}\b/gi;
46533
+ FOLDER_PREFIX = /[^\s'"`()[\]{}<>,;|]*\//g;
46534
+ NUMBER = /\d+/g;
46535
+ });
46536
+
46537
+ // src/triage/inherited.ts
46538
+ import { basename as basename20 } from "path";
46539
+ function isOpenIssueState(state) {
46540
+ return !CLOSED_ISSUE_STATES.includes(state);
46541
+ }
46542
+ function sameFailure(failure6, baseline) {
46543
+ const { file, evidence } = failure6;
46544
+ if (file === null)
46545
+ return false;
46546
+ const sameTest = basename20(file) === basename20(baseline.file) && oneLine9(failure6.name) === oneLine9(baseline.name);
46547
+ const message = baseline.message ?? null;
46548
+ if (!sameTest || !hasText2(message))
46549
+ return sameTest;
46550
+ return evidence !== null && keyText(evidence).includes(keyText(message));
46551
+ }
46552
+ function inheritedFailureOf(bug, failures) {
46553
+ const failure6 = testFailureOf(bug);
46554
+ if (failure6 === null)
46555
+ return null;
46556
+ return failures.find((baseline) => sameFailure(failure6, baseline)) ?? null;
46557
+ }
46558
+ var CLOSED_ISSUE_STATES;
46559
+ var init_inherited = __esm(() => {
46560
+ init_bug_key();
46561
+ init_test_failure();
46562
+ CLOSED_ISSUE_STATES = ["done", "released", "cancelled"];
46563
+ });
46564
+
46565
+ // src/triage/issue-text.ts
46566
+ function fenced(value) {
46567
+ const runs = Array.from(value.matchAll(/`+/g), (run) => run[0].length);
46568
+ const fence = "`".repeat(Math.max(MIN_FENCE_LENGTH3, ...runs.map((length) => length + 1)));
46569
+ const body = value.endsWith(`
46570
+ `) ? value : `${value}
46571
+ `;
46572
+ return `${fence}
46573
+ ${body}${fence}`;
46574
+ }
46575
+ function issueTitle2(redactedWhat) {
46576
+ const line = oneLine9(redactedWhat);
46577
+ const points = Array.from(line);
46578
+ if (points.length <= TITLE_MAX_LENGTH)
46579
+ return line;
46580
+ const kept = points.slice(0, TITLE_MAX_LENGTH - TITLE_CUT.length).join("");
46581
+ return `${kept.trimEnd()}${TITLE_CUT}`;
46582
+ }
46583
+ function scoreText(score) {
46584
+ return score.toFixed(2);
46585
+ }
46586
+ function similarCommentOpening(score, threshold) {
46587
+ 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.`;
46588
+ }
46589
+ function possibleDuplicatesSection(duplicates2, redact) {
46590
+ const intro = `No open bug scored at or above the similarity threshold of ${String(duplicates2.threshold)}.`;
46591
+ const listed4 = duplicates2.nearest.length === 0 ? "No open bug shares a word with this one." : duplicates2.nearest.map(({ issue: issue4, score }) => `- ${issueNameOf(issue4.ref)} (score ${scoreText(score)})`).join(`
46592
+ `);
46593
+ return `## Possible duplicates
46594
+
46595
+ ${redact(`${intro}
46596
+
46597
+ ${listed4}`)}`;
46598
+ }
46599
+ function issueNameOf(ref14) {
46600
+ return ref14.url ?? `${ref14.kind} issue ${ref14.externalId}`;
46601
+ }
46602
+ function supersedesSection(closed, redact) {
46603
+ const sentence = `This bug came back after ${issueNameOf(closed)} was closed as completed;` + " this issue is filed in its place.";
46604
+ return `## Supersedes
46605
+
46606
+ ${redact(sentence)}`;
46607
+ }
46608
+ function section2(heading, value, absent6, redact) {
46609
+ const shown2 = value === null ? absent6 : fenced(redact(value));
46610
+ return `## ${heading}
46611
+
46612
+ ${shown2}`;
46613
+ }
46614
+ function issueText(opening, values, redact) {
46615
+ return [
46616
+ opening,
46617
+ ...values.supersedes === undefined ? [] : [supersedesSection(values.supersedes, redact)],
46618
+ section2("What", values.what, "", redact),
46619
+ section2("Artifact", values.artifact, "The report gave no artifact, so a recurrence files again.", redact),
46620
+ ...values.refs === null ? [] : [values.refs],
46621
+ section2("Recurrence key", values.key, "The report gave no artifact, so this bug has no key.", redact),
46622
+ section2("Plan", values.planStub, "The dispatch resolved no plan stub.", redact),
46623
+ section2("Task", values.taskText, "", redact),
46624
+ section2("Feedback", values.feedback, "The report gave no feedback.", redact),
46625
+ ...values.duplicates === undefined ? [] : [possibleDuplicatesSection(values.duplicates, redact)]
46626
+ ].join(`
46627
+
46628
+ `) + `
46629
+ `;
46630
+ }
46631
+ var TITLE_MAX_LENGTH = 120, TITLE_CUT = "...", MIN_FENCE_LENGTH3 = 3, ISSUE_OPENING, COMMENT_OPENING = "Reported again by a rafa task session.";
46632
+ var init_issue_text = __esm(() => {
46633
+ init_bug_key();
46634
+ 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.";
46635
+ });
46636
+
46637
+ // src/triage/local-paths.ts
46638
+ import { realpathSync as realpathSync13 } from "fs";
46639
+ import { resolve as resolve29 } from "path";
46640
+ function literalPattern(text2) {
46641
+ return text2.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
46642
+ }
46643
+ function forms(path4) {
46644
+ const given = resolve29(path4);
46645
+ try {
46646
+ const real = realpathSync13(given);
46647
+ return real === given ? [given] : [given, real];
46648
+ } catch {
46649
+ return [given];
46650
+ }
46651
+ }
46652
+ function isRoot(path4) {
46653
+ return path4.length > 1;
46654
+ }
46655
+ function localPathRedactor(repoRoot, home) {
46656
+ const asHome = (pattern) => ({ pattern, under: `${HOME_MARKER}/`, bare: HOME_MARKER });
46657
+ const asRepo = (pattern) => ({ pattern, under: "", bare: "." });
46658
+ const literals = [
46659
+ ...forms(repoRoot).filter(isRoot).map((root) => asRepo(literalPattern(root))),
46660
+ ...forms(home).filter(isRoot).map((root) => asHome(literalPattern(root)))
46661
+ ];
46662
+ const roots = [
46663
+ ...[...literals].sort((a, b) => b.pattern.length - a.pattern.length),
46664
+ asHome(HOME_PATTERN)
46665
+ ];
46666
+ const alternatives = roots.map(({ pattern }) => `(${pattern})(?:${UNDER}|${PATH_END})`);
46667
+ const matcher = new RegExp(`${PATH_START}(?:${alternatives.join("|")})`, "g");
46668
+ return (text2) => text2.replace(matcher, (...groups) => {
46669
+ const index = roots.findIndex((_, at) => groups[1 + at * 2] !== undefined);
46670
+ const root = roots[index];
46671
+ return groups[2 + index * 2] === undefined ? root.bare : root.under;
46672
+ });
46673
+ }
46674
+ var HOME_MARKER = "[redacted: HOME]", SEGMENT = "[^/\\s'\"`()<>:,;]+", HOME_PATTERN, PATH_START = "(?:(?<![\\w.\\-])|(?<=(?:^|\\s)-[A-Za-z]))", UNDER = "(/)(?=[^\\s'\"`()<>])", PATH_END = "(?![\\w\\-]|\\.[\\w.\\-/])";
46675
+ var init_local_paths = __esm(() => {
46676
+ HOME_PATTERN = `(?:/${SEGMENT})*?/(?:Users|home)/${SEGMENT}`;
46677
+ });
46678
+
46156
46679
  // src/effort/store/tracker-refs.ts
46157
46680
  import { randomUUID as randomUUID15 } from "crypto";
46158
- import { existsSync as existsSync48 } from "fs";
46681
+ import { existsSync as existsSync49 } from "fs";
46159
46682
  function refused3(call, reason) {
46160
46683
  return new Error(`effort store: tracker ref ${call} ${reason}; ${REFUSAL_TAILS[call]}`);
46161
46684
  }
@@ -46217,7 +46740,7 @@ function heldRefOf(path4, seq, text2) {
46217
46740
  return stored;
46218
46741
  throw new Error(`effort store: ${path4}: findings row ${seq} holds a tracker_ref ${stored};` + " no writer stores one");
46219
46742
  }
46220
- function placeRef(db, path4, write, wanted, insertValues) {
46743
+ function placeRef(db, path4, write, wanted, superseded, insertValues) {
46221
46744
  const held = db.query(SELECT_HELD2).get(write.dispatch.sessionId, write.artifact);
46222
46745
  if (held === null) {
46223
46746
  db.query(INSERT_REF).run(...insertValues());
@@ -46228,14 +46751,27 @@ function placeRef(db, path4, write, wanted, insertValues) {
46228
46751
  return ["attached", wanted.ref];
46229
46752
  }
46230
46753
  const kept = heldRefOf(path4, held.seq, held.tracker_ref);
46754
+ if (kept.json === superseded?.json)
46755
+ throw new SupersedeInSessionRefusal(path4, held.seq);
46231
46756
  return kept.json === wanted.json ? ["held", kept.ref] : ["conflict", kept.ref];
46232
46757
  }
46758
+ function supersededOf(write, wanted) {
46759
+ if (write.supersedes === undefined)
46760
+ return null;
46761
+ const superseded = storedRefOf(write.supersedes);
46762
+ if (typeof superseded === "string")
46763
+ throw refused3("write", `supersedes a reference ${superseded}`);
46764
+ if (superseded.json === wanted.json)
46765
+ throw refused3("write", "supersedes the reference it writes");
46766
+ return superseded;
46767
+ }
46233
46768
  function writeTrackerRef(repoRoot, write, seams = {}) {
46234
46769
  checkDispatch("tracker ref", write.dispatch, write.outcome);
46235
46770
  checkArtifact2("write", write.artifact);
46236
46771
  const wanted = storedRefOf(write.ref);
46237
46772
  if (typeof wanted === "string")
46238
46773
  throw refused3("write", `has a reference ${wanted}`);
46774
+ const superseded = supersededOf(write, wanted);
46239
46775
  const path4 = sqliteStorePath(repoRoot);
46240
46776
  const { sessionId, planStub: planStub2, taskLine: taskLine2 } = write.dispatch;
46241
46777
  const collectedAt = (seams.now ?? (() => new Date))().toISOString();
@@ -46250,8 +46786,8 @@ function writeTrackerRef(repoRoot, write, seams = {}) {
46250
46786
  wanted.json,
46251
46787
  collectedAt
46252
46788
  ];
46253
- const [action, stored] = withSqliteStore(path4, "write", !existsSync48(path4), (db) => {
46254
- const place2 = db.transaction(() => placeRef(db, path4, write, wanted, insertValues));
46789
+ const [action, stored] = withSqliteStore(path4, "write", !existsSync49(path4), (db) => {
46790
+ const place2 = db.transaction(() => placeRef(db, path4, write, wanted, superseded, insertValues));
46255
46791
  return place2.immediate();
46256
46792
  });
46257
46793
  return { path: path4, action, stored };
@@ -46259,20 +46795,15 @@ function writeTrackerRef(repoRoot, write, seams = {}) {
46259
46795
  function readTrackerRef(repoRoot, artifact) {
46260
46796
  checkArtifact2("read", artifact);
46261
46797
  const path4 = sqliteStorePath(repoRoot);
46262
- if (!existsSync48(path4))
46798
+ if (!existsSync49(path4))
46263
46799
  return null;
46264
- const oldest = withSqliteStore(path4, "read", false, (db) => db.query(SELECT_OLDEST).get(artifact));
46265
- return oldest === null ? null : heldRefOf(path4, oldest.seq, oldest.tracker_ref).ref;
46800
+ const newest = withSqliteStore(path4, "read", false, (db) => db.query(SELECT_NEWEST).get(artifact));
46801
+ return newest === null ? null : heldRefOf(path4, newest.seq, newest.tracker_ref).ref;
46266
46802
  }
46267
46803
  var SELECT_HELD2 = `
46268
46804
  SELECT seq, tracker_ref FROM findings
46269
46805
  WHERE session_id = ? AND artifact = ?
46270
- `, INSERT_REF, ATTACH_REF = "UPDATE findings SET tracker_ref = ? WHERE seq = ? AND tracker_ref IS NULL", SELECT_OLDEST = `
46271
- SELECT seq, tracker_ref FROM findings
46272
- WHERE artifact = ? AND tracker_ref IS NOT NULL
46273
- ORDER BY seq
46274
- LIMIT 1
46275
- `, REFUSAL_TAILS, REF_FIELDS;
46806
+ `, INSERT_REF, ATTACH_REF = "UPDATE findings SET tracker_ref = ? WHERE seq = ? AND tracker_ref IS NULL", NEWEST_FIRST, SELECT_NEWEST, REFUSAL_TAILS, REF_FIELDS, SupersedeInSessionRefusal;
46276
46807
  var init_tracker_refs = __esm(() => {
46277
46808
  init_findings();
46278
46809
  init_origins();
@@ -46285,6 +46816,13 @@ var init_tracker_refs = __esm(() => {
46285
46816
  ${STAMPED_COLUMNS}
46286
46817
  )
46287
46818
  VALUES (?, ?, ?, ?, ?, ?, ?, ?, ${stampedValues("findings")})
46819
+ `;
46820
+ NEWEST_FIRST = ACROSS_STORES_ORDER.split(",").map((term) => `${term.trim()} DESC`).join(", ");
46821
+ SELECT_NEWEST = `
46822
+ SELECT seq, tracker_ref FROM findings
46823
+ WHERE artifact = ? AND tracker_ref IS NOT NULL
46824
+ ORDER BY ${NEWEST_FIRST}
46825
+ LIMIT 1
46288
46826
  `;
46289
46827
  REFUSAL_TAILS = { write: "nothing written", read: "nothing opened" };
46290
46828
  REF_FIELDS = [
@@ -46295,48 +46833,16 @@ var init_tracker_refs = __esm(() => {
46295
46833
  ["module", optionalText3],
46296
46834
  ["repo", optionalText3]
46297
46835
  ];
46298
- });
46299
-
46300
- // src/triage/local-paths.ts
46301
- import { realpathSync as realpathSync13 } from "fs";
46302
- import { resolve as resolve29 } from "path";
46303
- function literalPattern(text2) {
46304
- return text2.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
46305
- }
46306
- function forms(path4) {
46307
- const given = resolve29(path4);
46308
- try {
46309
- const real = realpathSync13(given);
46310
- return real === given ? [given] : [given, real];
46311
- } catch {
46312
- return [given];
46313
- }
46314
- }
46315
- function isRoot(path4) {
46316
- return path4.length > 1;
46317
- }
46318
- function localPathRedactor(repoRoot, home) {
46319
- const asHome = (pattern) => ({ pattern, under: `${HOME_MARKER}/`, bare: HOME_MARKER });
46320
- const asRepo = (pattern) => ({ pattern, under: "", bare: "." });
46321
- const literals = [
46322
- ...forms(repoRoot).filter(isRoot).map((root) => asRepo(literalPattern(root))),
46323
- ...forms(home).filter(isRoot).map((root) => asHome(literalPattern(root)))
46324
- ];
46325
- const roots = [
46326
- ...[...literals].sort((a, b) => b.pattern.length - a.pattern.length),
46327
- asHome(HOME_PATTERN)
46328
- ];
46329
- const alternatives = roots.map(({ pattern }) => `(${pattern})(?:${UNDER}|${PATH_END})`);
46330
- const matcher = new RegExp(`${PATH_START}(?:${alternatives.join("|")})`, "g");
46331
- return (text2) => text2.replace(matcher, (...groups) => {
46332
- const index = roots.findIndex((_, at) => groups[1 + at * 2] !== undefined);
46333
- const root = roots[index];
46334
- return groups[2 + index * 2] === undefined ? root.bare : root.under;
46335
- });
46336
- }
46337
- var HOME_MARKER = "[redacted: HOME]", SEGMENT = "[^/\\s'\"`()<>:,;]+", HOME_PATTERN, PATH_START = "(?:(?<![\\w.\\-])|(?<=(?:^|\\s)-[A-Za-z]))", UNDER = "(/)(?=[^\\s'\"`()<>])", PATH_END = "(?![\\w\\-]|\\.[\\w.\\-/])";
46338
- var init_local_paths = __esm(() => {
46339
- HOME_PATTERN = `(?:/${SEGMENT})*?/(?:Users|home)/${SEGMENT}`;
46836
+ SupersedeInSessionRefusal = class SupersedeInSessionRefusal extends Error {
46837
+ path;
46838
+ seq;
46839
+ constructor(path4, seq) {
46840
+ super(`effort store: ${path4}: 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");
46841
+ this.name = "SupersedeInSessionRefusal";
46842
+ this.path = path4;
46843
+ this.seq = seq;
46844
+ }
46845
+ };
46340
46846
  });
46341
46847
 
46342
46848
  // src/triage/refs-section.ts
@@ -46406,11 +46912,77 @@ var init_refs_section = __esm(() => {
46406
46912
  };
46407
46913
  });
46408
46914
 
46915
+ // src/triage/similarity.ts
46916
+ import { basename as basename21 } from "path";
46917
+ function wordSetOf(text2) {
46918
+ const words = strippedText(text2).toLowerCase().split(NON_WORD2).filter((word) => word.length > 0);
46919
+ return new Set(words);
46920
+ }
46921
+ function reportWordsOf(report2) {
46922
+ return wordSetOf(`${report2.what ?? ""}
46923
+ ${report2.artifact ?? ""}`);
46924
+ }
46925
+ function jaccard(a, b) {
46926
+ const shared = [...a].filter((word) => b.has(word)).length;
46927
+ const all = a.size + b.size - shared;
46928
+ return all === 0 ? 0 : shared / all;
46929
+ }
46930
+ function fencedValueAt(lines2, start) {
46931
+ const open = lines2.slice(start).findIndex((line) => line.trim().length > 0);
46932
+ if (open === -1)
46933
+ return null;
46934
+ const fence = lines2[start + open];
46935
+ if (!FENCE_LINE6.test(fence))
46936
+ return null;
46937
+ const body = lines2.slice(start + open + 1);
46938
+ const close = body.indexOf(fence);
46939
+ return close === -1 ? null : body.slice(0, close).join(`
46940
+ `);
46941
+ }
46942
+ function sectionValueOf(body, heading) {
46943
+ const lines2 = body.split(/\r?\n/);
46944
+ const at = lines2.findIndex((line) => HEADING_LINE3.exec(line)?.[1]?.trim() === heading);
46945
+ return at === -1 ? null : fencedValueAt(lines2, at + 1);
46946
+ }
46947
+ function issueTextOf(body) {
46948
+ return {
46949
+ what: sectionValueOf(body, "What"),
46950
+ artifact: sectionValueOf(body, "Artifact")
46951
+ };
46952
+ }
46953
+ function testFileOf2(text2) {
46954
+ const file = testFailureOf(text2)?.file ?? testFilesIn(text2.artifact ?? "")[0] ?? testFilesIn(text2.what ?? "")[0] ?? null;
46955
+ return file === null ? null : basename21(file);
46956
+ }
46957
+ function namesOtherTestFile(reportFile, issueFile) {
46958
+ return reportFile !== null && issueFile !== null && reportFile !== issueFile;
46959
+ }
46960
+ function nearestOpenBugs(report2, openBugs) {
46961
+ const words = reportWordsOf(report2);
46962
+ const reportFile = testFileOf2(report2);
46963
+ const scored = openBugs.flatMap((issue4) => {
46964
+ const text2 = issueTextOf(issue4.body);
46965
+ if (namesOtherTestFile(reportFile, testFileOf2(text2)))
46966
+ return [];
46967
+ const score = jaccard(words, reportWordsOf(text2));
46968
+ return score > 0 ? [{ issue: issue4, score }] : [];
46969
+ });
46970
+ return [...scored].sort((a, b) => b.score - a.score);
46971
+ }
46972
+ var NON_WORD2, FENCE_LINE6, HEADING_LINE3;
46973
+ var init_similarity = __esm(() => {
46974
+ init_bug_key();
46975
+ init_test_failure();
46976
+ NON_WORD2 = /\W+/;
46977
+ FENCE_LINE6 = /^`{3,}$/;
46978
+ HEADING_LINE3 = /^## (.+)$/;
46979
+ });
46980
+
46409
46981
  // src/triage/triage.ts
46410
46982
  import { homedir as homedir9 } from "os";
46411
- import { basename as basename17, join as join71 } from "path";
46983
+ import { join as join72 } from "path";
46412
46984
  function privateTriageDir(repoRoot) {
46413
- return join71(repoRoot, PRIVATE_TRIAGE_DIR);
46985
+ return join72(repoRoot, PRIVATE_TRIAGE_DIR);
46414
46986
  }
46415
46987
  function createPrivateTriageTracker(repoRoot, options = {}) {
46416
46988
  return createLocalTracker({
@@ -46445,9 +47017,6 @@ function redactSecrets(text2, secrets) {
46445
47017
  const pattern = new RegExp(values.map(literalPattern2).join("|"), "g");
46446
47018
  return text2.replace(pattern, (value) => `[redacted: ${nameByValue.get(value)}]`);
46447
47019
  }
46448
- function hasText2(value) {
46449
- return typeof value === "string" && value.trim().length > 0;
46450
- }
46451
47020
  function blockerTextOf(blockers) {
46452
47021
  const texts = blockers.map((blocker) => blocker.what).filter(hasText2);
46453
47022
  return texts.length === 0 ? null : texts.join(BLOCKER_SEPARATOR);
@@ -46466,57 +47035,11 @@ function triageBlockers(options) {
46466
47035
  return { text: text2, written: false, problem: `the blocker was not written: ${messageOf(error)}` };
46467
47036
  }
46468
47037
  }
46469
- function artifactOf(bug) {
46470
- return hasText2(bug.artifact) && textProblem(bug.artifact) === null ? bug.artifact : null;
46471
- }
46472
- function oneLine8(text2) {
46473
- return text2.replace(/\s+/g, " ").trim();
46474
- }
46475
- function bugKeyOf(trackerPath, artifact) {
46476
- return `${basename17(trackerPath)}${KEY_SEPARATOR}${oneLine8(artifact)}`;
46477
- }
46478
- function fenced(value) {
46479
- const runs = Array.from(value.matchAll(/`+/g), (run) => run[0].length);
46480
- const fence = "`".repeat(Math.max(MIN_FENCE_LENGTH3, ...runs.map((length) => length + 1)));
46481
- const body = value.endsWith(`
46482
- `) ? value : `${value}
46483
- `;
46484
- return `${fence}
46485
- ${body}${fence}`;
46486
- }
46487
- function issueTitle2(redactedWhat) {
46488
- const line = oneLine8(redactedWhat);
46489
- const points = Array.from(line);
46490
- if (points.length <= TITLE_MAX_LENGTH)
46491
- return line;
46492
- const kept = points.slice(0, TITLE_MAX_LENGTH - TITLE_CUT.length).join("");
46493
- return `${kept.trimEnd()}${TITLE_CUT}`;
46494
- }
46495
- function section2(heading, value, absent6, redact) {
46496
- const shown2 = value === null ? absent6 : fenced(redact(value));
46497
- return `## ${heading}
46498
-
46499
- ${shown2}`;
46500
- }
46501
- function issueText(opening, values, redact) {
46502
- return [
46503
- opening,
46504
- section2("What", values.what, "", redact),
46505
- section2("Artifact", values.artifact, "The report gave no artifact, so a recurrence files again.", redact),
46506
- ...values.refs === null ? [] : [values.refs],
46507
- section2("Recurrence key", values.key, "The report gave no artifact, so this bug has no key.", redact),
46508
- section2("Plan", values.planStub, "The dispatch resolved no plan stub.", redact),
46509
- section2("Task", values.taskText, "", redact),
46510
- section2("Feedback", values.feedback, "The report gave no feedback.", redact)
46511
- ].join(`
46512
-
46513
- `) + `
46514
- `;
46515
- }
46516
47038
  async function filingFor(what, bug, options, local, redact, verifyRefs) {
46517
47039
  const reported2 = artifactOf(bug);
46518
47040
  const artifact = reported2 === null ? null : local(reported2);
46519
- const searchText = artifact === null ? null : bugKeyOf(options.trackerPath, redact(artifact));
47041
+ const searchText = artifact === null ? null : bugKeyOf(options.trackerPath, redact(artifact), redact(what));
47042
+ const key = artifact === null ? null : bugKeyOf(options.trackerPath, artifact, local(what));
46520
47043
  const refs2 = await buildRefsSection(artifact === null ? null : redact(artifact), verifyRefs);
46521
47044
  const values = {
46522
47045
  what,
@@ -46540,10 +47063,23 @@ async function filingFor(what, bug, options, local, redact, verifyRefs) {
46540
47063
  },
46541
47064
  comment: issueText(COMMENT_OPENING, values, redact),
46542
47065
  searchText,
46543
- key: artifact === null ? null : bugKeyOf(options.trackerPath, artifact),
46544
- legacyKey: reported2 === null || reported2 === artifact ? null : bugKeyOf(options.trackerPath, reported2)
47066
+ key,
47067
+ legacyKeys: legacyKeysOf(options.trackerPath, key, reported2, artifact),
47068
+ values,
47069
+ compared: { what: local(what), artifact }
46545
47070
  };
46546
47071
  }
47072
+ function legacyKeysOf(trackerPath, key, reported2, artifact) {
47073
+ if (key === null || reported2 === null || artifact === null)
47074
+ return [];
47075
+ const keys = [legacyBugKeyOf(trackerPath, artifact), legacyBugKeyOf(trackerPath, reported2)];
47076
+ return [...new Set(keys)].filter((legacy) => legacy !== key);
47077
+ }
47078
+ function similarityStepOf(options) {
47079
+ const threshold = options.similarity?.triageSimilarityThreshold;
47080
+ const candidates = options.similarity?.triageSimilarityCandidates;
47081
+ return typeof threshold === "number" && typeof candidates === "number" ? { threshold, candidates } : null;
47082
+ }
46547
47083
  async function step(run, name2, action) {
46548
47084
  try {
46549
47085
  return { ok: true, value: await action() };
@@ -46563,47 +47099,109 @@ function resultOf5(run, action, fields = {}) {
46563
47099
  ...fields
46564
47100
  };
46565
47101
  }
46566
- async function settle2(run, action, ref14, foundBy) {
47102
+ async function settle2(run, action, ref14, foundBy, supersedes) {
46567
47103
  const { store } = run.route;
46568
47104
  const { key } = run.filing;
46569
47105
  if (store === null || key === null)
46570
47106
  return resultOf5(run, action, { ref: ref14, foundBy });
46571
- const stored = await step(run, "storing the reference", () => store(key, ref14));
47107
+ const stored = await step(run, "storing the reference", () => store(key, ref14, supersedes));
46572
47108
  return stored.ok ? resultOf5(run, action, { ref: ref14, foundBy, stored: stored.value }) : resultOf5(run, action, { ref: ref14, foundBy, problem: stored.problem });
46573
47109
  }
46574
- async function fileNew(run) {
46575
- const created = await step(run, `the ${run.route.channel} tracker create`, () => run.route.tracker.create(run.filing.draft));
46576
- return created.ok ? settle2(run, "filed", created.value, null) : resultOf5(run, "failed", { problem: created.problem });
47110
+ async function fileNew(run, extras = {}) {
47111
+ const { filing } = run;
47112
+ const draft = extras.supersedes === undefined && extras.duplicates === undefined ? filing.draft : { ...filing.draft, body: issueText(ISSUE_OPENING, { ...filing.values, ...extras }, run.redact) };
47113
+ const created = await step(run, `the ${run.route.channel} tracker create`, () => run.route.tracker.create(draft));
47114
+ return created.ok ? settle2(run, "filed", created.value, null, extras.supersedes) : resultOf5(run, "failed", { problem: created.problem });
46577
47115
  }
46578
- async function commentOn2(run, ref14, foundBy) {
46579
- const commented = await step(run, `the ${run.route.channel} tracker comment`, () => run.route.tracker.comment(ref14, run.filing.comment));
47116
+ async function commentOn2(run, ref14, foundBy, action = "commented", comment = run.filing.comment) {
47117
+ const commented = await step(run, `the ${run.route.channel} tracker comment`, () => run.route.tracker.comment(ref14, comment));
46580
47118
  if (!commented.ok)
46581
47119
  return resultOf5(run, "failed", { ref: ref14, foundBy, problem: commented.problem });
46582
- return foundBy === "store" ? resultOf5(run, "commented", { ref: ref14, foundBy }) : settle2(run, "commented", ref14, foundBy);
47120
+ return foundBy === "store" ? resultOf5(run, action, { ref: ref14, foundBy }) : settle2(run, action, ref14, foundBy);
46583
47121
  }
46584
- async function triageBug(run) {
47122
+ async function lookUp2(run, key, searchText) {
46585
47123
  const { route, filing } = run;
46586
- const { key, searchText } = filing;
46587
- if (key === null || searchText === null)
46588
- return fileNew(run);
46589
47124
  const { readStored } = route;
46590
47125
  if (readStored !== null) {
46591
- for (const storedKey of [key, filing.legacyKey]) {
46592
- if (storedKey === null)
46593
- continue;
47126
+ for (const storedKey of [key, ...filing.legacyKeys]) {
46594
47127
  const stored = await step(run, "reading the stored reference", () => readStored(storedKey));
46595
47128
  if (!stored.ok)
46596
- return resultOf5(run, "failed", { problem: stored.problem });
47129
+ return stored;
46597
47130
  if (stored.value !== null && stored.value.kind === route.tracker.kind) {
46598
- return commentOn2(run, stored.value, "store");
47131
+ return { ok: true, value: { ref: stored.value, foundBy: "store" } };
46599
47132
  }
46600
47133
  }
46601
47134
  }
46602
47135
  const found = await step(run, `the ${route.channel} tracker find`, () => route.tracker.find({ text: searchText, type: "bug" }));
46603
47136
  if (!found.ok)
46604
- return resultOf5(run, "failed", { problem: found.problem });
47137
+ return found;
46605
47138
  const [match] = found.value;
46606
- return match === undefined ? fileNew(run) : commentOn2(run, match, "find");
47139
+ return {
47140
+ ok: true,
47141
+ value: match === undefined ? null : { ref: match, foundBy: "find" }
47142
+ };
47143
+ }
47144
+ async function answerRecurrence(run, { ref: ref14, foundBy }) {
47145
+ if (!run.route.readsState)
47146
+ return commentOn2(run, ref14, foundBy);
47147
+ const issue4 = await step(run, `the ${run.route.channel} tracker get`, () => run.route.tracker.get(ref14));
47148
+ if (!issue4.ok)
47149
+ return resultOf5(run, "failed", { ref: ref14, foundBy, problem: issue4.problem });
47150
+ return COMPLETED_ISSUE_STATES.includes(issue4.value.state) ? fileNew(run, { supersedes: ref14 }) : commentOn2(run, ref14, foundBy);
47151
+ }
47152
+ async function commentOnNearest(run, nearest, threshold) {
47153
+ const opening = similarCommentOpening(nearest.score, threshold);
47154
+ const comment = issueText(opening, run.filing.values, run.redact);
47155
+ const result2 = await commentOn2(run, nearest.issue.ref, "similarity", "commented", comment);
47156
+ return { ...result2, score: nearest.score };
47157
+ }
47158
+ async function answerMiss(run) {
47159
+ const { tracker, similarity } = run.route;
47160
+ const list = tracker.openIssues;
47161
+ if (similarity === null || list === undefined)
47162
+ return fileNew(run);
47163
+ const listed4 = await step(run, `the ${run.route.channel} tracker openIssues`, () => list.call(tracker, "bug"));
47164
+ if (!listed4.ok)
47165
+ return resultOf5(run, "failed", { problem: listed4.problem });
47166
+ const nearest = nearestOpenBugs(run.filing.compared, listed4.value);
47167
+ const [first] = nearest;
47168
+ return first !== undefined && first.score >= similarity.threshold ? commentOnNearest(run, first, similarity.threshold) : fileNew(run, { duplicates: { threshold: similarity.threshold, nearest: nearest.slice(0, similarity.candidates) } });
47169
+ }
47170
+ async function triageBug(run) {
47171
+ const { key, searchText } = run.filing;
47172
+ if (key === null || searchText === null)
47173
+ return fileNew(run);
47174
+ const found = await lookUp2(run, key, searchText);
47175
+ if (!found.ok)
47176
+ return resultOf5(run, "failed", { problem: found.problem });
47177
+ return found.value === null ? answerMiss(run) : answerRecurrence(run, found.value);
47178
+ }
47179
+ async function triageInherited(run, commented) {
47180
+ const { key, searchText } = run.filing;
47181
+ if (key === null || searchText === null || commented.has(key))
47182
+ return resultOf5(run, "inherited");
47183
+ const found = await lookUp2(run, key, searchText);
47184
+ if (!found.ok)
47185
+ return resultOf5(run, "failed", { problem: found.problem });
47186
+ if (found.value === null)
47187
+ return resultOf5(run, "inherited");
47188
+ const { ref: ref14, foundBy } = found.value;
47189
+ const issue4 = await step(run, `the ${run.route.channel} tracker get`, () => run.route.tracker.get(ref14));
47190
+ if (!issue4.ok)
47191
+ return resultOf5(run, "failed", { ref: ref14, foundBy, problem: issue4.problem });
47192
+ if (!isOpenIssueState(issue4.value.state))
47193
+ return resultOf5(run, "inherited");
47194
+ const result2 = await commentOn2(run, ref14, foundBy, "inherited");
47195
+ if (result2.action === "inherited")
47196
+ commented.add(key);
47197
+ return result2;
47198
+ }
47199
+ function localBug(bug, what, local) {
47200
+ const artifact = artifactOf(bug);
47201
+ return {
47202
+ what: local(what),
47203
+ artifact: artifact === null ? null : local(artifact)
47204
+ };
46607
47205
  }
46608
47206
  function skippedRow(index, channel, problem) {
46609
47207
  return { index, channel, action: "skipped", ref: null, foundBy: null, stored: null, problem };
@@ -46632,14 +47230,24 @@ async function triageReport(options) {
46632
47230
  channel: "public",
46633
47231
  tracker: options.tracker,
46634
47232
  readStored: (key) => readTrackerRef(repoRoot, key),
46635
- store: (key, ref14) => writeTrackerRef(repoRoot, {
47233
+ store: (key, ref14, supersedes) => writeTrackerRef(repoRoot, {
46636
47234
  dispatch: options.dispatch,
46637
47235
  outcome: options.outcome,
46638
47236
  artifact: key,
46639
- ref: ref14
46640
- }, options.seams).action
47237
+ ref: ref14,
47238
+ ...supersedes === undefined ? {} : { supersedes }
47239
+ }, options.seams).action,
47240
+ readsState: true,
47241
+ similarity: similarityStepOf(options)
46641
47242
  },
46642
- private: { channel: "private", tracker: privateTracker, readStored: null, store: null }
47243
+ private: {
47244
+ channel: "private",
47245
+ tracker: privateTracker,
47246
+ readStored: null,
47247
+ store: null,
47248
+ readsState: false,
47249
+ similarity: null
47250
+ }
46643
47251
  };
46644
47252
  const bugs = [];
46645
47253
  for (const [index, bug] of options.report.outOfScopeBugs.entries()) {
@@ -46655,26 +47263,97 @@ async function triageReport(options) {
46655
47263
  continue;
46656
47264
  }
46657
47265
  const filing = await filingFor(what, bug, options, local, redact, verifyRefs);
46658
- bugs.push(await triageBug({ route, index, filing, redact }));
47266
+ const run = { route, index, filing, redact };
47267
+ const { inherited } = options;
47268
+ const isInherited = route.channel === "public" && inherited !== undefined && inheritedFailureOf(localBug(bug, what, local), inherited.failures) !== null;
47269
+ bugs.push(isInherited ? await triageInherited(run, inherited.commented) : await triageBug(run));
46659
47270
  }
46660
47271
  return { blocker, bugs };
46661
47272
  }
46662
- var PRIVATE_TRIAGE_DIR, SECRET_ENV_NAMES, TRIAGE_MODULE = "unassigned", BLOCKER_SEPARATOR = "; ", KEY_SEPARATOR = ": ", TITLE_MAX_LENGTH = 120, TITLE_CUT = "...", MIN_FENCE_LENGTH3 = 3, ISSUE_OPENING, COMMENT_OPENING = "Reported again by a rafa task session.";
47273
+ var PRIVATE_TRIAGE_DIR, SECRET_ENV_NAMES, TRIAGE_MODULE = "unassigned", BLOCKER_SEPARATOR = "; ", COMPLETED_ISSUE_STATES;
46663
47274
  var init_triage2 = __esm(() => {
46664
47275
  init_local2();
46665
47276
  init_config_sections();
46666
- init_findings();
46667
47277
  init_tracker_refs();
46668
47278
  init_tracker();
47279
+ init_bug_key();
47280
+ init_inherited();
47281
+ init_issue_text();
46669
47282
  init_local_paths();
46670
47283
  init_machine_fault();
46671
47284
  init_refs_section();
46672
- PRIVATE_TRIAGE_DIR = join71(".rafa", "triage", "private");
47285
+ init_similarity();
47286
+ init_bug_key();
47287
+ init_issue_text();
47288
+ PRIVATE_TRIAGE_DIR = join72(".rafa", "triage", "private");
46673
47289
  SECRET_ENV_NAMES = ["GITHUB_TOKEN", "ANTHROPIC_API_KEY", "LINEAR_API_KEY"];
46674
- 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.";
47290
+ COMPLETED_ISSUE_STATES = ["done", "released"];
47291
+ });
47292
+
47293
+ // src/start/loop-events.ts
47294
+ import { join as join73 } from "path";
47295
+ function quoted3(text2) {
47296
+ return `"${oneLine(text2).replaceAll('"', "\\\"")}"`;
47297
+ }
47298
+ function taskKind(position, verb) {
47299
+ return padKind(`task ${position.index}/${position.total} ${verb}`);
47300
+ }
47301
+ function minutes(durationMs) {
47302
+ return `${Math.max(1, Math.round(durationMs / MINUTE_MS))}m`;
47303
+ }
47304
+ function summaryOf3(event) {
47305
+ switch (event.kind) {
47306
+ case "task-start":
47307
+ return `${taskKind(event.position, "start")}${quoted3(event.text)}`;
47308
+ case "task-done":
47309
+ return event.tokens === null ? `${taskKind(event.position, "done")}${minutes(event.durationMs)}` : `${taskKind(event.position, "done")}${minutes(event.durationMs)} ${Math.round(event.tokens / TOKENS_PER_K)}k tokens`;
47310
+ case "task-blocked":
47311
+ return `${taskKind(event.position, "blocked")}${oneLine(event.reason)}`;
47312
+ case "wrap-up":
47313
+ return `${padKind("wrap-up")}${event.phase}`;
47314
+ case "pr":
47315
+ return `pr #${event.number} opened`;
47316
+ case "no-pr":
47317
+ return `${padKind("no pr")}${oneLine(event.reason)}`;
47318
+ case "halt":
47319
+ return `${padKind("halt")}${oneLine(event.reason)}`;
47320
+ case "inherited":
47321
+ return `${padKind("inherited")}${oneLine(`${event.file} > ${event.name}`)}`;
47322
+ }
47323
+ }
47324
+ function emitLoopEvent(event, now = () => new Date) {
47325
+ const { kind, ...data } = event;
47326
+ activeOutput().emit({ type: "event", name: kind, summary: summaryOf3(event), data, ts: now().toISOString() });
47327
+ }
47328
+ async function unlessText(read) {
47329
+ return activeOutputMode() === "text" ? null : read();
47330
+ }
47331
+ function taskPosition(trackerContent, lineNum) {
47332
+ const { tasks } = parsePlan(trackerContent);
47333
+ return { index: tasks.findIndex((task) => task.lineNum === lineNum) + 1, total: tasks.length };
47334
+ }
47335
+ async function taskTokens(checkout, sessionId, home) {
47336
+ try {
47337
+ const { usage } = await readSessionLog(join73(sessionLogDir(checkout, home), `${sessionId}.jsonl`));
47338
+ return usage.inputTokens + usage.cacheCreationInputTokens + usage.outputTokens;
47339
+ } catch {
47340
+ return null;
47341
+ }
47342
+ }
47343
+ var MINUTE_MS = 60000, TOKENS_PER_K = 1000;
47344
+ var init_loop_events = __esm(() => {
47345
+ init_active();
47346
+ init_collect();
47347
+ init_session_log();
47348
+ init_plan();
46675
47349
  });
46676
47350
 
46677
47351
  // src/start/triage.ts
47352
+ import { homedir as homedir10 } from "os";
47353
+ function foundByText(bug) {
47354
+ const found = FOUND_BY[bug.foundBy ?? "find"];
47355
+ return bug.score === undefined ? found : `${found} (score ${scoreText(bug.score)})`;
47356
+ }
46678
47357
  function unresolvedTracker(kind, reason) {
46679
47358
  const refuse13 = () => Promise.reject(new Error(reason));
46680
47359
  return Object.freeze({
@@ -46697,7 +47376,15 @@ function hasTriage(report2) {
46697
47376
  function issueName2(ref14) {
46698
47377
  return ref14.url ?? `${ref14.kind} issue ${ref14.externalId}`;
46699
47378
  }
46700
- function bugLines(bug) {
47379
+ function failureName(failure6) {
47380
+ return `${failure6.file} > ${failure6.name}`;
47381
+ }
47382
+ function inheritedNote(bug, name2, where, failure6) {
47383
+ const test = failure6 === undefined ? "a red test the run started with" : `the run-start failure ${failureName(failure6)}`;
47384
+ const comment = bug.ref === null ? "" : `, and ${issueName2(bug.ref)} on ${where}, found by ${foundByText(bug)}, was commented on`;
47385
+ return `${name2}: inherited: ${test}; nothing was filed${comment}.`;
47386
+ }
47387
+ function bugLines(bug, inherited) {
46701
47388
  const name2 = `out_of_scope_bugs[${bug.index}]`;
46702
47389
  const where = `the ${bug.channel} tracker`;
46703
47390
  if (bug.action === "skipped") {
@@ -46708,16 +47395,17 @@ function bugLines(bug) {
46708
47395
  return { notes: [], warnings: [warning] };
46709
47396
  }
46710
47397
  const issue4 = bug.ref === null ? "an issue" : issueName2(bug.ref);
46711
- const note = bug.action === "filed" ? `${name2}: filed on ${where} as ${issue4}.` : `${name2}: recurs in ${issue4} on ${where}, found by ${FOUND_BY[bug.foundBy ?? "find"]}; commented on it.`;
47398
+ const reached = bug.action === "filed" ? `${name2}: filed on ${where} as ${issue4}.` : `${name2}: recurs in ${issue4} on ${where}, found by ${foundByText(bug)}; commented on it.`;
47399
+ const note = bug.action === "inherited" ? inheritedNote(bug, name2, where, inherited) : reached;
46712
47400
  const problem = bug.problem === null ? [] : [`${name2}: ${bug.problem}`];
46713
47401
  const conflict = bug.stored === "conflict" ? [`${name2}: the store keeps another reference for its artifact, in place of ${issue4}.`] : [];
46714
47402
  return { notes: [note], warnings: [...problem, ...conflict] };
46715
47403
  }
46716
- function describeStartTriage(result2) {
47404
+ function describeStartTriage(result2, inherited = new Map) {
46717
47405
  const { blocker } = result2;
46718
47406
  const blockerNotes = blocker.written ? ["blockers: written onto the task line, for the next dispatch of this task."] : [];
46719
47407
  const blockerWarnings = blocker.problem === null ? [] : [`blockers: ${blocker.problem}`];
46720
- const bugs = result2.bugs.map(bugLines);
47408
+ const bugs = result2.bugs.map((bug) => bugLines(bug, inherited.get(bug.index)));
46721
47409
  const prefixed = (lines2) => lines2.map((line) => `${LINE_PREFIX}${line}`);
46722
47410
  return {
46723
47411
  notes: prefixed([...blockerNotes, ...bugs.flatMap((lines2) => lines2.notes)]),
@@ -46741,10 +47429,42 @@ async function resolvePublicTracker(options, redact) {
46741
47429
  return unresolvedTracker(options.config.trackerDefault, reason);
46742
47430
  }
46743
47431
  }
47432
+ function baselinePathOrNull(trackerPath) {
47433
+ try {
47434
+ return baselinePathFor(trackerPath);
47435
+ } catch {
47436
+ return null;
47437
+ }
47438
+ }
47439
+ function runStartFailures(trackerPath) {
47440
+ const path4 = baselinePathOrNull(trackerPath);
47441
+ const reading2 = path4 === null ? null : readBaseline(path4);
47442
+ return reading2?.state === "read" && reading2.baseline.junit === "read" ? reading2.baseline.failures : [];
47443
+ }
47444
+ function inheritedByIndex(result2, report2, failures, local, redact) {
47445
+ const named3 = result2.bugs.flatMap((bug) => {
47446
+ const reported2 = report2.outOfScopeBugs[bug.index];
47447
+ if (bug.action !== "inherited" || reported2 === undefined || !hasText2(reported2.what))
47448
+ return [];
47449
+ const artifact = artifactOf(reported2);
47450
+ const failure6 = inheritedFailureOf({
47451
+ what: local(reported2.what),
47452
+ artifact: artifact === null ? null : local(artifact)
47453
+ }, failures);
47454
+ return failure6 === null ? [] : [[bug.index, { file: redact(failure6.file), name: redact(failure6.name) }]];
47455
+ });
47456
+ return new Map(named3);
47457
+ }
46744
47458
  function createStartTriage(options) {
46745
47459
  const secrets = namedSecrets(options.config, options.env ?? process.env);
46746
47460
  const redact = (text2) => redactSecrets(text2, secrets);
47461
+ const local = localPathRedactor(options.repoRoot, homedir10());
46747
47462
  let publicTracker = null;
47463
+ let inherited = null;
47464
+ const inheritedFor = (trackerPath) => {
47465
+ inherited ??= { failures: runStartFailures(trackerPath), commented: new Set };
47466
+ return inherited;
47467
+ };
46748
47468
  const trackerFor = (report2) => {
46749
47469
  if (!report2.outOfScopeBugs.some(filesPublicly)) {
46750
47470
  return Promise.resolve(unresolvedTracker(options.config.trackerDefault, NOT_RESOLVED_REASON));
@@ -46757,6 +47477,7 @@ function createStartTriage(options) {
46757
47477
  if (!reading2.present || !hasTriage(reading2.report))
46758
47478
  return null;
46759
47479
  const { report: report2 } = reading2;
47480
+ const run = inheritedFor(input.trackerPath);
46760
47481
  const result2 = await triageReport({
46761
47482
  repoRoot: options.repoRoot,
46762
47483
  trackerPath: input.trackerPath,
@@ -46771,13 +47492,18 @@ function createStartTriage(options) {
46771
47492
  tracker: await trackerFor(report2),
46772
47493
  privateTracker: options.privateTracker,
46773
47494
  secrets,
46774
- seams: options.seams
47495
+ seams: options.seams,
47496
+ inherited: run,
47497
+ similarity: options.config
46775
47498
  });
46776
- const { notes, warnings } = describeStartTriage(result2);
47499
+ const named3 = inheritedByIndex(result2, report2, run.failures, local, redact);
47500
+ const { notes, warnings } = describeStartTriage(result2, named3);
46777
47501
  for (const note of notes)
46778
47502
  activeOutput().info(`${INDENT6}${note}`);
46779
47503
  for (const warning of warnings)
46780
47504
  activeOutput().warn(`${INDENT6}${warning}`);
47505
+ for (const failure6 of named3.values())
47506
+ emitLoopEvent({ kind: "inherited", file: failure6.file, name: failure6.name });
46781
47507
  return result2;
46782
47508
  };
46783
47509
  return async (input) => {
@@ -46795,24 +47521,31 @@ var init_triage3 = __esm(() => {
46795
47521
  init_resolve2();
46796
47522
  init_config_sections();
46797
47523
  init_parse();
47524
+ init_baseline();
47525
+ init_bug_key();
47526
+ init_inherited();
47527
+ init_issue_text();
47528
+ init_local_paths();
46798
47529
  init_triage2();
47530
+ init_loop_events();
46799
47531
  FOUND_BY = {
46800
47532
  store: "its stored reference",
46801
- find: "a tracker find"
47533
+ find: "a tracker find",
47534
+ similarity: "word similarity"
46802
47535
  };
46803
47536
  });
46804
47537
 
46805
47538
  // src/commands/epic/close.ts
46806
47539
  import { randomUUID as randomUUID16 } from "crypto";
46807
- import { join as join72 } from "path";
47540
+ import { join as join74 } from "path";
46808
47541
  function ref14(issue4) {
46809
47542
  return `#${String(issue4)}`;
46810
47543
  }
46811
- function oneLine9(text2) {
47544
+ function oneLine10(text2) {
46812
47545
  return text2.replace(/\s+/gu, " ").trim();
46813
47546
  }
46814
47547
  function criterionName(criterion) {
46815
- return `criterion ${String(criterion.number)}: ${oneLine9(criterion.text)}`;
47548
+ return `criterion ${String(criterion.number)}: ${oneLine10(criterion.text)}`;
46816
47549
  }
46817
47550
  function criteria(count2) {
46818
47551
  return count2 === 1 ? "1 criterion" : `${String(count2)} criteria`;
@@ -46836,7 +47569,7 @@ function refuseOpenMembers(number, members) {
46836
47569
  const open = members.filter((member) => member.state === "OPEN");
46837
47570
  if (open.length === 0)
46838
47571
  return;
46839
- const named3 = open.map((member) => `${ref14(member.number)} ${oneLine9(member.title)}`).join(", ");
47572
+ const named3 = open.map((member) => `${ref14(member.number)} ${oneLine10(member.title)}`).join(", ");
46840
47573
  throw refusal10(`Epic ${ref14(number)} has ${String(open.length)} open ${open.length === 1 ? "member" : "members"}: ${named3}. The gate checks an epic only once every member is closed: close them, or move them` + " out with rafa epic move <issue> --to=<epic>. No session was started");
46841
47574
  }
46842
47575
  function labelsEpicToClose(issues, epic) {
@@ -46905,10 +47638,10 @@ async function planVerification(input) {
46905
47638
  return Object.freeze({ status: "planned", sessionId, verdicts: reading2.verdicts, issues: reading2.issues });
46906
47639
  }
46907
47640
  function closeTriageFile(root, epic) {
46908
- return join72(root, ".rafa", `epic-${String(epic)}-close`);
47641
+ return join74(root, ".rafa", `epic-${String(epic)}-close`);
46909
47642
  }
46910
47643
  function failedCheckArtifact(epic, criterion) {
46911
- return `epic ${ref14(epic)} acceptance criterion: ${oneLine9(criterion.text)}`;
47644
+ return `epic ${ref14(epic)} acceptance criterion: ${oneLine10(criterion.text)}`;
46912
47645
  }
46913
47646
  function failedCheckReport(epic, failure6, commit) {
46914
47647
  const { criterion } = failure6.check;
@@ -46932,7 +47665,7 @@ function failedCheckReport(epic, failure6, commit) {
46932
47665
  skillsUsed: [],
46933
47666
  blockers: [],
46934
47667
  outOfScopeBugs: [Object.freeze({
46935
- what: `Epic ${ref14(epic)} acceptance criterion ${String(criterion.number)} fails against main: ${oneLine9(criterion.text)}`,
47668
+ what: `Epic ${ref14(epic)} acceptance criterion ${String(criterion.number)} fails against main: ${oneLine10(criterion.text)}`,
46936
47669
  artifact: failedCheckArtifact(epic, criterion),
46937
47670
  security: false,
46938
47671
  extras: []
@@ -46953,6 +47686,9 @@ function bugLine(criterion, bug) {
46953
47686
  case "commented": {
46954
47687
  return { text: `${which}: recurs in ${bug.ref === null ? "an issue" : issueName3(bug.ref)} on the ${bug.channel} tracker; commented on it.`, warn: false };
46955
47688
  }
47689
+ case "inherited": {
47690
+ return { text: `${which}: not filed: a test failure the run started with.`, warn: false };
47691
+ }
46956
47692
  case "skipped": {
46957
47693
  return { text: `${which}: not filed: ${bug.problem ?? "no reason given"}.`, warn: true };
46958
47694
  }
@@ -46990,7 +47726,8 @@ async function fileFailures(run, epic, failures, commit) {
46990
47726
  tracker,
46991
47727
  privateTracker: run.seams.privateTracker,
46992
47728
  secrets,
46993
- seams: run.seams.findings
47729
+ seams: run.seams.findings,
47730
+ similarity: run.config
46994
47731
  });
46995
47732
  for (const bug of result2.bugs) {
46996
47733
  const line = bugLine(criterion, bug);
@@ -47021,9 +47758,9 @@ async function weighResults(run, epic, ran) {
47021
47758
  if (result2.kind === "unanswered")
47022
47759
  warn3(run, `No answer for ${name2} \u2014 ${result2.reason}`);
47023
47760
  else if (result2.result === "pass")
47024
- say(run, `\u2713 Passed ${name2} \u2014 ${oneLine9(result2.evidence)}`);
47761
+ say(run, `\u2713 Passed ${name2} \u2014 ${oneLine10(result2.evidence)}`);
47025
47762
  else
47026
- warn3(run, `\u2717 Failed ${name2} \u2014 ${oneLine9(result2.evidence)}`);
47763
+ warn3(run, `\u2717 Failed ${name2} \u2014 ${oneLine10(result2.evidence)}`);
47027
47764
  }
47028
47765
  if (!ran.removal.ok) {
47029
47766
  const said = ran.removal.said === "" ? "git said nothing" : ran.removal.said;
@@ -47557,8 +48294,8 @@ var init_promote = __esm(() => {
47557
48294
  });
47558
48295
 
47559
48296
  // src/agents/vendorable.ts
47560
- import { readdirSync as readdirSync17, readFileSync as readFileSync53 } from "fs";
47561
- import { isAbsolute as isAbsolute17, join as join73 } from "path";
48297
+ import { readdirSync as readdirSync17, readFileSync as readFileSync54 } from "fs";
48298
+ import { isAbsolute as isAbsolute17, join as join75 } from "path";
47562
48299
  function planFiles(dir) {
47563
48300
  let entries;
47564
48301
  try {
@@ -47570,7 +48307,7 @@ function planFiles(dir) {
47570
48307
  }
47571
48308
  function readText7(path4) {
47572
48309
  try {
47573
- return readFileSync53(path4, "utf8");
48310
+ return readFileSync54(path4, "utf8");
47574
48311
  } catch {
47575
48312
  return null;
47576
48313
  }
@@ -47580,7 +48317,7 @@ function rafaHolds(roster, name2) {
47580
48317
  return item?.holders.some((holder) => holder.source === "rafa") === true;
47581
48318
  }
47582
48319
  function vendorableAgents(scan) {
47583
- const dir = isAbsolute17(scan.planDir) ? scan.planDir : join73(scan.repoRoot, scan.planDir);
48320
+ const dir = isAbsolute17(scan.planDir) ? scan.planDir : join75(scan.repoRoot, scan.planDir);
47584
48321
  const roots = {
47585
48322
  repoRoot: scan.repoRoot,
47586
48323
  home: scan.home,
@@ -47589,7 +48326,7 @@ function vendorableAgents(scan) {
47589
48326
  const roster = resolveAgentRoster(roots, scan.settings);
47590
48327
  const found = [];
47591
48328
  for (const file of planFiles(dir)) {
47592
- const path4 = join73(dir, file);
48329
+ const path4 = join75(dir, file);
47593
48330
  const markdown = readText7(path4);
47594
48331
  if (markdown === null)
47595
48332
  continue;
@@ -47619,8 +48356,8 @@ var init_vendorable = __esm(() => {
47619
48356
 
47620
48357
  // src/project/gitignore.ts
47621
48358
  import { createHash as createHash7 } from "crypto";
47622
- import { existsSync as existsSync49, mkdirSync as mkdirSync17, readFileSync as readFileSync54, writeFileSync as writeFileSync17 } from "fs";
47623
- import { dirname as dirname26, isAbsolute as isAbsolute18, join as join74 } from "path";
48359
+ import { existsSync as existsSync50, mkdirSync as mkdirSync18, readFileSync as readFileSync55, writeFileSync as writeFileSync18 } from "fs";
48360
+ import { dirname as dirname28, isAbsolute as isAbsolute18, join as join76 } from "path";
47624
48361
  function trackingEntry(flags) {
47625
48362
  if (flags.trackingAll) {
47626
48363
  return [
@@ -47696,19 +48433,19 @@ function attempt3(file, failure6, act) {
47696
48433
  }
47697
48434
  }
47698
48435
  function readIfPresent4(file) {
47699
- if (!existsSync49(file))
48436
+ if (!existsSync50(file))
47700
48437
  return null;
47701
- return attempt3(file, "cannot be read", () => readFileSync54(file, "utf8"));
48438
+ return attempt3(file, "cannot be read", () => readFileSync55(file, "utf8"));
47702
48439
  }
47703
48440
  function writeTrackingGitignore(root, flags) {
47704
48441
  requireAbsolute2(root);
47705
- const file = join74(root, GITIGNORE_FILE);
48442
+ const file = join76(root, GITIGNORE_FILE);
47706
48443
  const text2 = readIfPresent4(file);
47707
48444
  const next = withTrackingBlock(text2, flags, file);
47708
48445
  if (next === text2)
47709
48446
  return { file, change: "unchanged" };
47710
48447
  attempt3(file, "cannot be written", () => {
47711
- writeFileSync17(file, next);
48448
+ writeFileSync18(file, next);
47712
48449
  });
47713
48450
  const change = text2 === null ? "created" : "updated";
47714
48451
  return { file, change };
@@ -47724,7 +48461,7 @@ function trackingDigest(flags) {
47724
48461
  }
47725
48462
  function exposureNotice(root) {
47726
48463
  return [
47727
- `tracking.all is true, so ${join74(root, GITIGNORE_FILE)} no longer ignores ${SCOPE_DIR}/` + " and a commit can carry what it holds, including:",
48464
+ `tracking.all is true, so ${join76(root, GITIGNORE_FILE)} no longer ignores ${SCOPE_DIR}/` + " and a commit can carry what it holds, including:",
47728
48465
  " - unpatched issues described in specs and plans",
47729
48466
  " - effort rows with model names and token counts",
47730
48467
  " - instincts that may quote error output",
@@ -47737,7 +48474,7 @@ function printWarning2(message) {
47737
48474
  }
47738
48475
  function noticeTrackingChange(root, flags, print2 = printWarning2) {
47739
48476
  requireAbsolute2(root);
47740
- const digestFile = join74(root, TRACKING_DIGEST_FILE);
48477
+ const digestFile = join76(root, TRACKING_DIGEST_FILE);
47741
48478
  const recorded = `${trackingDigest(flags)}
47742
48479
  `;
47743
48480
  if (readIfPresent4(digestFile) === recorded) {
@@ -47746,8 +48483,8 @@ function noticeTrackingChange(root, flags, print2 = printWarning2) {
47746
48483
  if (flags.trackingAll)
47747
48484
  print2(exposureNotice(root));
47748
48485
  attempt3(digestFile, "cannot be written", () => {
47749
- mkdirSync17(dirname26(digestFile), { recursive: true });
47750
- writeFileSync17(digestFile, recorded);
48486
+ mkdirSync18(dirname28(digestFile), { recursive: true });
48487
+ writeFileSync18(digestFile, recorded);
47751
48488
  });
47752
48489
  return { digestFile, changed: true, printed: flags.trackingAll };
47753
48490
  }
@@ -47762,7 +48499,7 @@ var init_gitignore = __esm(() => {
47762
48499
  init_hop_record();
47763
48500
  init_position();
47764
48501
  init_scope();
47765
- TRACKING_DIGEST_FILE = join74(SCOPE_DIR, DIGEST_NAME);
48502
+ TRACKING_DIGEST_FILE = join76(SCOPE_DIR, DIGEST_NAME);
47766
48503
  SUB_PATHS = [
47767
48504
  { flag: "trackingSpecs", dir: "specs" },
47768
48505
  { flag: "trackingPlans", dir: "plans" }
@@ -47777,8 +48514,8 @@ var init_gitignore = __esm(() => {
47777
48514
 
47778
48515
  // src/project/roots.ts
47779
48516
  import { spawnSync as spawnSync5 } from "child_process";
47780
- import { accessSync, constants as constants2, readFileSync as readFileSync55, statSync as statSync25 } from "fs";
47781
- import { isAbsolute as isAbsolute19, join as join75, resolve as resolve30 } from "path";
48517
+ import { accessSync, constants as constants2, readFileSync as readFileSync56, statSync as statSync25 } from "fs";
48518
+ import { isAbsolute as isAbsolute19, join as join77, resolve as resolve30 } from "path";
47782
48519
  function gitToplevel(dir) {
47783
48520
  const result2 = spawnSync5("git", ["rev-parse", "--show-toplevel"], {
47784
48521
  cwd: dir,
@@ -47881,8 +48618,8 @@ function readManifest2(file, fs3) {
47881
48618
  }
47882
48619
  }
47883
48620
  function readDir(dir, fs3) {
47884
- const manifest = readManifest2(join75(dir, PACKAGE_JSON), fs3);
47885
- const files = MONOREPO_MARKER_FILES.filter((name2) => fs3.exists(join75(dir, name2)));
48621
+ const manifest = readManifest2(join77(dir, PACKAGE_JSON), fs3);
48622
+ const files = MONOREPO_MARKER_FILES.filter((name2) => fs3.exists(join77(dir, name2)));
47886
48623
  const markers = manifest.marks ? [PACKAGE_JSON, ...files] : files;
47887
48624
  return { ...manifest, dir, markers };
47888
48625
  }
@@ -47935,7 +48672,7 @@ var init_roots = __esm(() => {
47935
48672
  ]);
47936
48673
  DISK_ROOTS_FILE_SYSTEM = Object.freeze({
47937
48674
  ...DISK_FILE_SYSTEM,
47938
- readFile: (path4) => readFileSync55(path4, "utf8"),
48675
+ readFile: (path4) => readFileSync56(path4, "utf8"),
47939
48676
  isDirectory: (path4) => statSync25(path4).isDirectory(),
47940
48677
  assertWritable: (path4) => accessSync(path4, constants2.W_OK)
47941
48678
  });
@@ -48031,7 +48768,7 @@ var init_root_choice = __esm(() => {
48031
48768
  });
48032
48769
 
48033
48770
  // src/release/setting.ts
48034
- import { readFileSync as readFileSync56 } from "fs";
48771
+ import { readFileSync as readFileSync57 } from "fs";
48035
48772
  function releaseEnabledIn(text2, path4) {
48036
48773
  return parseConfigText(text2, path4).values.releaseEnabled ?? null;
48037
48774
  }
@@ -48039,7 +48776,7 @@ function readReleaseSetting(root) {
48039
48776
  const path4 = configFilePath(root);
48040
48777
  let text2;
48041
48778
  try {
48042
- text2 = readFileSync56(path4, "utf8");
48779
+ text2 = readFileSync57(path4, "utf8");
48043
48780
  } catch (error) {
48044
48781
  return { enabled: null, text: "", problem: `${path4} could not be read: ${messageOf(error)}` };
48045
48782
  }
@@ -48112,7 +48849,7 @@ var init_setting = __esm(() => {
48112
48849
  });
48113
48850
 
48114
48851
  // src/commands/init-release.ts
48115
- import { writeFileSync as writeFileSync18 } from "fs";
48852
+ import { writeFileSync as writeFileSync19 } from "fs";
48116
48853
  function releaseQuestion(versionFile, changelog) {
48117
48854
  return `Bump ${versionFile} and add a ${changelog} entry with every pull request? [Y/n] `;
48118
48855
  }
@@ -48164,7 +48901,7 @@ function writeSetting(root, text2, enabled) {
48164
48901
  return `the file this run would have written reads ${RELEASE_ENABLED_SETTING} as ${describeValue(read)}`;
48165
48902
  }
48166
48903
  try {
48167
- writeFileSync18(path4, written3, "utf8");
48904
+ writeFileSync19(path4, written3, "utf8");
48168
48905
  } catch (error) {
48169
48906
  return `it could not be written: ${messageOf(error)}`;
48170
48907
  }
@@ -48227,8 +48964,8 @@ var init_init_release = __esm(() => {
48227
48964
  });
48228
48965
 
48229
48966
  // src/project/scaffold.ts
48230
- import { existsSync as existsSync50, lstatSync as lstatSync2, mkdirSync as mkdirSync18, statSync as statSync26, writeFileSync as writeFileSync19 } from "fs";
48231
- import { join as join76 } from "path";
48967
+ import { existsSync as existsSync51, lstatSync as lstatSync2, mkdirSync as mkdirSync19, statSync as statSync26, writeFileSync as writeFileSync20 } from "fs";
48968
+ import { join as join78 } from "path";
48232
48969
  function globsLine(patterns) {
48233
48970
  return `[${patterns.map((pattern) => JSON.stringify(pattern)).join(", ")}]`;
48234
48971
  }
@@ -48244,7 +48981,7 @@ function userConfigText() {
48244
48981
  }
48245
48982
  function layoutOf2(base, tree) {
48246
48983
  const scope = scopeAt(base);
48247
- return { dirs: [scope.dir, ...tree.map((name2) => join76(scope.dir, name2))], configFile: configFilePath(base) };
48984
+ return { dirs: [scope.dir, ...tree.map((name2) => join78(scope.dir, name2))], configFile: configFilePath(base) };
48248
48985
  }
48249
48986
  function occupied(path4) {
48250
48987
  return lstatSync2(path4, { throwIfNoEntry: false }) !== undefined;
@@ -48255,7 +48992,7 @@ function isDirectory3(path4) {
48255
48992
  function directoryConflict(dir) {
48256
48993
  if (!occupied(dir) || isDirectory3(dir))
48257
48994
  return null;
48258
- return existsSync50(dir) ? `${dir} is not a directory` : `${dir} is a link to nothing`;
48995
+ return existsSync51(dir) ? `${dir} is not a directory` : `${dir} is a link to nothing`;
48259
48996
  }
48260
48997
  function layoutConflicts(layout) {
48261
48998
  const [scopeDir, ...tree] = layout.dirs;
@@ -48263,7 +49000,7 @@ function layoutConflicts(layout) {
48263
49000
  if (opening !== null)
48264
49001
  return [opening];
48265
49002
  const dirs = tree.flatMap((dir) => directoryConflict(dir) ?? []);
48266
- const file = occupied(layout.configFile) && !existsSync50(layout.configFile) ? [`${layout.configFile} is a link to nothing`] : [];
49003
+ const file = occupied(layout.configFile) && !existsSync51(layout.configFile) ? [`${layout.configFile} is a link to nothing`] : [];
48267
49004
  return [...dirs, ...file];
48268
49005
  }
48269
49006
  function scaffoldConflicts(root, home) {
@@ -48277,18 +49014,18 @@ function attempt4(path4, failure6, act) {
48277
49014
  }
48278
49015
  }
48279
49016
  function ensureDirectory(path4) {
48280
- if (existsSync50(path4))
49017
+ if (existsSync51(path4))
48281
49018
  return { path: path4, kind: "directory", change: "unchanged" };
48282
49019
  attempt4(path4, "cannot be created", () => {
48283
- mkdirSync18(path4, { recursive: true });
49020
+ mkdirSync19(path4, { recursive: true });
48284
49021
  });
48285
49022
  return { path: path4, kind: "directory", change: "created" };
48286
49023
  }
48287
49024
  function ensureFile(path4, text2) {
48288
- if (existsSync50(path4))
49025
+ if (existsSync51(path4))
48289
49026
  return { path: path4, kind: "file", change: "unchanged" };
48290
49027
  attempt4(path4, "cannot be written", () => {
48291
- writeFileSync19(path4, text2, { encoding: "utf8", flag: "wx" });
49028
+ writeFileSync20(path4, text2, { encoding: "utf8", flag: "wx" });
48292
49029
  });
48293
49030
  return { path: path4, kind: "file", change: "created" };
48294
49031
  }
@@ -48366,6 +49103,10 @@ var init_scaffold = __esm(() => {
48366
49103
  "# claims:",
48367
49104
  `# staleAfter: ${CONFIG_DEFAULTS.claimsStaleAfter} # 36h | 3d | disabled; how long a rafa:claimed claim stands before a takeover`,
48368
49105
  `# ahead: ${CONFIG_DEFAULTS.claimsAhead} # off | allow; whether a claim may reach one issue ahead`,
49106
+ "# triage:",
49107
+ "# similarity:",
49108
+ `# threshold: ${String(CONFIG_DEFAULTS.triageSimilarityThreshold)} # above 0 and at most 1 | false; the least score at which triage comments on the nearest open bug`,
49109
+ `# candidates: ${String(CONFIG_DEFAULTS.triageSimilarityCandidates)} # 1..10, the nearest issues a new bug lists as Possible duplicates`,
48369
49110
  "# release:",
48370
49111
  `# enabled: ${String(CONFIG_DEFAULTS.releaseEnabled)} # true | false | auto, which is on when both files below exist`,
48371
49112
  `# versionFile: ${CONFIG_DEFAULTS.releaseVersionFile}`,
@@ -48422,9 +49163,9 @@ var init_scaffold = __esm(() => {
48422
49163
  });
48423
49164
 
48424
49165
  // src/commands/init.ts
48425
- import { existsSync as existsSync51, readFileSync as readFileSync57 } from "fs";
48426
- import { homedir as homedir10 } from "os";
48427
- import { join as join77, relative as relative15, sep as sep6 } from "path";
49166
+ import { existsSync as existsSync52, readFileSync as readFileSync58 } from "fs";
49167
+ import { homedir as homedir11 } from "os";
49168
+ import { join as join79, relative as relative15, sep as sep6 } from "path";
48428
49169
  function refusal12(lines2) {
48429
49170
  return new CommandExit(1, [...lines2, NOTHING_WRITTEN2].join(`
48430
49171
  `));
@@ -48522,9 +49263,9 @@ function resolvedConfig3(root, home, warn4) {
48522
49263
  }
48523
49264
  }
48524
49265
  function checkGitignore(root, flags) {
48525
- const file = join77(root, GITIGNORE_FILE);
49266
+ const file = join79(root, GITIGNORE_FILE);
48526
49267
  try {
48527
- const text2 = existsSync51(file) ? readFileSync57(file, "utf8") : null;
49268
+ const text2 = existsSync52(file) ? readFileSync58(file, "utf8") : null;
48528
49269
  withTrackingBlock(text2, flags, file);
48529
49270
  } catch (error) {
48530
49271
  const message = error instanceof GitignoreError ? error.message : `rafa init: ${file} cannot be read (${messageOf(error)})`;
@@ -48556,8 +49297,8 @@ function initialise(root, start, home, context, entry) {
48556
49297
  const resolved = resolvedConfig3(root.path, home, warn4);
48557
49298
  const config = resolved.config;
48558
49299
  checkGitignore(root.path, config);
48559
- const configExisted = existsSync51(configFilePath(root.path));
48560
- const digestExisted = existsSync51(join77(root.path, TRACKING_DIGEST_FILE));
49300
+ const configExisted = existsSync52(configFilePath(root.path));
49301
+ const digestExisted = existsSync52(join79(root.path, TRACKING_DIGEST_FILE));
48561
49302
  const project = writeProjectScope(root.path);
48562
49303
  const tracking = applyTracking(root.path, config, warn4);
48563
49304
  const writes = [...project, ...trackingWrites(tracking, digestExisted), ...writeUserScope(home)];
@@ -48766,7 +49507,7 @@ var init_init = __esm(() => {
48766
49507
  init_init_release();
48767
49508
  DEFAULT_INIT_SEAMS = Object.freeze({
48768
49509
  cwd: () => process.cwd(),
48769
- home: () => homedir10(),
49510
+ home: () => homedir11(),
48770
49511
  entry: () => Bun.main,
48771
49512
  isTerminal: () => process.stdin.isTTY === true,
48772
49513
  openPrompter: () => createLinePrompter(process.stdin, process.stderr),
@@ -49174,7 +49915,7 @@ Usage: ${USAGE24}`);
49174
49915
  return "blessed";
49175
49916
  return conflicts ? "conflicts" : "all";
49176
49917
  }
49177
- function oneLine10(text2) {
49918
+ function oneLine11(text2) {
49178
49919
  return text2.trim().replace(/\s+/gu, " ");
49179
49920
  }
49180
49921
  function byConfidence2(left, right) {
@@ -49207,7 +49948,7 @@ function conflictLines(conflict) {
49207
49948
  const width = Math.max(...conflict.records.map((record) => record.id.length));
49208
49949
  return [
49209
49950
  ` ${conflict.scope} ${conflict.trigger}`,
49210
- ...conflict.records.map((record) => lessonLine2(" ", record, width, oneLine10(record.action)))
49951
+ ...conflict.records.map((record) => lessonLine2(" ", record, width, oneLine11(record.action)))
49211
49952
  ];
49212
49953
  }
49213
49954
  function renderConflicts(result2) {
@@ -49486,8 +50227,8 @@ var init_show4 = __esm(() => {
49486
50227
  });
49487
50228
 
49488
50229
  // src/commands/issue/check.ts
49489
- import { readdirSync as readdirSync18, readFileSync as readFileSync58, statSync as statSync27 } from "fs";
49490
- import { join as join78, resolve as resolve33 } from "path";
50230
+ import { readdirSync as readdirSync18, readFileSync as readFileSync59, statSync as statSync27 } from "fs";
50231
+ import { join as join80, resolve as resolve33 } from "path";
49491
50232
  function readCheckIssue(args) {
49492
50233
  if (args.length > 1) {
49493
50234
  throw lineRefusal(`Expected one issue number, got ${String(args.length)}: ${args.join(" ")}`, CHECK_USAGE);
@@ -49515,7 +50256,7 @@ function copyNames(dir, issue4) {
49515
50256
  }
49516
50257
  const prefix = `${boardId(issue4)}-`;
49517
50258
  const notes = notesFileName(issue4);
49518
- return names2.filter((name2) => name2.startsWith(prefix) && name2.endsWith(SPEC_EXTENSION) && name2 !== notes).filter((name2) => name2.length > prefix.length + SPEC_EXTENSION.length).filter((name2) => statSync27(join78(dir, name2)).isFile()).sort();
50259
+ return names2.filter((name2) => name2.startsWith(prefix) && name2.endsWith(SPEC_EXTENSION) && name2 !== notes).filter((name2) => name2.length > prefix.length + SPEC_EXTENSION.length).filter((name2) => statSync27(join80(dir, name2)).isFile()).sort();
49519
50260
  }
49520
50261
  function findSavedCopy(root, specsDir, issue4) {
49521
50262
  const names2 = copyNames(resolve33(root, specsDir), issue4);
@@ -49525,11 +50266,11 @@ function findSavedCopy(root, specsDir, issue4) {
49525
50266
  if (names2.length > 1) {
49526
50267
  throw new CommandExit(1, [
49527
50268
  `\u274C Issue #${String(issue4)} has ${String(names2.length)} saved copies under ${specsDir}; remove the stale one:`,
49528
- ...names2.map((name2) => ` ${join78(specsDir, name2)}`)
50269
+ ...names2.map((name2) => ` ${join80(specsDir, name2)}`)
49529
50270
  ].join(`
49530
50271
  `));
49531
50272
  }
49532
- const path4 = join78(specsDir, only);
50273
+ const path4 = join80(specsDir, only);
49533
50274
  return { path: path4, absolute: resolve33(root, path4) };
49534
50275
  }
49535
50276
  function liveText(live) {
@@ -49578,7 +50319,7 @@ async function readReferences(absolute, issue4, verify, stamp2) {
49578
50319
  return rows2.map((row3) => checkedRef(row3, row3.stamp));
49579
50320
  }
49580
50321
  const once4 = memoiseVerifier(verify);
49581
- const before = await readRefsText({ copy: readFileSync58(absolute, "utf8"), issue: issue4, verify: once4 });
50322
+ const before = await readRefsText({ copy: readFileSync59(absolute, "utf8"), issue: issue4, verify: once4 });
49582
50323
  const after = await restampCopyRefs({ path: absolute, issue: issue4, verify: once4 });
49583
50324
  return before.rows.map((row3, index) => checkedRef(row3, after.rows[index]?.stamp ?? null));
49584
50325
  }
@@ -50503,10 +51244,10 @@ var init_pr_lifecycle = __esm(() => {
50503
51244
  });
50504
51245
 
50505
51246
  // src/notices/notices.ts
50506
- import { mkdirSync as mkdirSync19, readFileSync as readFileSync59, writeFileSync as writeFileSync20 } from "fs";
50507
- import { dirname as dirname27, join as join79 } from "path";
51247
+ import { mkdirSync as mkdirSync20, readFileSync as readFileSync60, writeFileSync as writeFileSync21 } from "fs";
51248
+ import { dirname as dirname29, join as join81 } from "path";
50508
51249
  function noticesPath(home) {
50509
- return join79(home, ".rafa", "notices.json");
51250
+ return join81(home, ".rafa", "notices.json");
50510
51251
  }
50511
51252
  function isNoticeId(value) {
50512
51253
  return typeof value === "string" && NOTICE_IDS.includes(value);
@@ -50514,7 +51255,7 @@ function isNoticeId(value) {
50514
51255
  function readDismissed(home) {
50515
51256
  let raw;
50516
51257
  try {
50517
- raw = readFileSync59(noticesPath(home), "utf8");
51258
+ raw = readFileSync60(noticesPath(home), "utf8");
50518
51259
  } catch {
50519
51260
  return [];
50520
51261
  }
@@ -50533,8 +51274,8 @@ function readDismissed(home) {
50533
51274
  function writeDismissed(home, ids) {
50534
51275
  const all = NOTICE_IDS.filter((id3) => ids.includes(id3) || readDismissed(home).includes(id3));
50535
51276
  const path4 = noticesPath(home);
50536
- mkdirSync19(dirname27(path4), { recursive: true });
50537
- writeFileSync20(path4, `${JSON.stringify({ dismissed: all }, null, 2)}
51277
+ mkdirSync20(dirname29(path4), { recursive: true });
51278
+ writeFileSync21(path4, `${JSON.stringify({ dismissed: all }, null, 2)}
50538
51279
  `);
50539
51280
  }
50540
51281
  function pendingNotices(dismissed) {
@@ -50599,9 +51340,9 @@ var init_notices = __esm(() => {
50599
51340
  });
50600
51341
 
50601
51342
  // src/notices/run.ts
50602
- import { homedir as homedir11 } from "os";
51343
+ import { homedir as homedir12 } from "os";
50603
51344
  async function requireNoticesAnswered() {
50604
- const outcome2 = await offerNotices({ home: homedir11(), version: RAFA_VERSION }, {
51345
+ const outcome2 = await offerNotices({ home: homedir12(), version: RAFA_VERSION }, {
50605
51346
  isTerminal: () => process.stdin.isTTY === true,
50606
51347
  openPrompter: () => createLinePrompter(process.stdin, process.stderr),
50607
51348
  warn: (line) => {
@@ -50653,9 +51394,9 @@ var init_budget = __esm(() => {
50653
51394
  });
50654
51395
 
50655
51396
  // src/start/checkout-guard.ts
50656
- import { existsSync as existsSync52, realpathSync as realpathSync14 } from "fs";
51397
+ import { existsSync as existsSync53, realpathSync as realpathSync14 } from "fs";
50657
51398
  function realDirOf(path4) {
50658
- if (!existsSync52(path4))
51399
+ if (!existsSync53(path4))
50659
51400
  return null;
50660
51401
  try {
50661
51402
  return realpathSync14(path4);
@@ -51232,62 +51973,6 @@ var init_commit2 = __esm(() => {
51232
51973
  DROPPED_BLOCKER_FIELD = /^blockers\[\d+\]$/;
51233
51974
  });
51234
51975
 
51235
- // src/start/loop-events.ts
51236
- import { join as join80 } from "path";
51237
- function quoted3(text2) {
51238
- return `"${oneLine(text2).replaceAll('"', "\\\"")}"`;
51239
- }
51240
- function taskKind(position, verb) {
51241
- return padKind(`task ${position.index}/${position.total} ${verb}`);
51242
- }
51243
- function minutes(durationMs) {
51244
- return `${Math.max(1, Math.round(durationMs / MINUTE_MS))}m`;
51245
- }
51246
- function summaryOf3(event) {
51247
- switch (event.kind) {
51248
- case "task-start":
51249
- return `${taskKind(event.position, "start")}${quoted3(event.text)}`;
51250
- case "task-done":
51251
- return event.tokens === null ? `${taskKind(event.position, "done")}${minutes(event.durationMs)}` : `${taskKind(event.position, "done")}${minutes(event.durationMs)} ${Math.round(event.tokens / TOKENS_PER_K)}k tokens`;
51252
- case "task-blocked":
51253
- return `${taskKind(event.position, "blocked")}${oneLine(event.reason)}`;
51254
- case "wrap-up":
51255
- return `${padKind("wrap-up")}${event.phase}`;
51256
- case "pr":
51257
- return `pr #${event.number} opened`;
51258
- case "no-pr":
51259
- return `${padKind("no pr")}${oneLine(event.reason)}`;
51260
- case "halt":
51261
- return `${padKind("halt")}${oneLine(event.reason)}`;
51262
- }
51263
- }
51264
- function emitLoopEvent(event, now = () => new Date) {
51265
- const { kind, ...data } = event;
51266
- activeOutput().emit({ type: "event", name: kind, summary: summaryOf3(event), data, ts: now().toISOString() });
51267
- }
51268
- async function unlessText(read) {
51269
- return activeOutputMode() === "text" ? null : read();
51270
- }
51271
- function taskPosition(trackerContent, lineNum) {
51272
- const { tasks } = parsePlan(trackerContent);
51273
- return { index: tasks.findIndex((task) => task.lineNum === lineNum) + 1, total: tasks.length };
51274
- }
51275
- async function taskTokens(checkout, sessionId, home) {
51276
- try {
51277
- const { usage } = await readSessionLog(join80(sessionLogDir(checkout, home), `${sessionId}.jsonl`));
51278
- return usage.inputTokens + usage.cacheCreationInputTokens + usage.outputTokens;
51279
- } catch {
51280
- return null;
51281
- }
51282
- }
51283
- var MINUTE_MS = 60000, TOKENS_PER_K = 1000;
51284
- var init_loop_events = __esm(() => {
51285
- init_active();
51286
- init_collect();
51287
- init_session_log();
51288
- init_plan();
51289
- });
51290
-
51291
51976
  // src/start/pause.ts
51292
51977
  function storedState2(repoRoot, sessionId) {
51293
51978
  try {
@@ -51547,9 +52232,9 @@ var init_worktree_dir = () => {};
51547
52232
 
51548
52233
  // src/start/worktree.ts
51549
52234
  import { realpathSync as realpathSync15 } from "fs";
51550
- import { join as join81, resolve as resolve35 } from "path";
52235
+ import { join as join82, resolve as resolve35 } from "path";
51551
52236
  function worktreePathFor(projectRoot3, worktreeDir, stub) {
51552
- return join81(worktreeDirAt(projectRoot3, worktreeDir), stub);
52237
+ return join82(worktreeDirAt(projectRoot3, worktreeDir), stub);
51553
52238
  }
51554
52239
  function addStep2(route, plan, path4) {
51555
52240
  const label = `add the worktree for ${plan.branch} at ${path4}`;
@@ -51733,104 +52418,6 @@ var init_run_checkout = __esm(() => {
51733
52418
  init_worktree2();
51734
52419
  });
51735
52420
 
51736
- // src/suite/baseline.ts
51737
- import { existsSync as existsSync53, mkdirSync as mkdirSync20, readFileSync as readFileSync60, renameSync as renameSync9, writeFileSync as writeFileSync21 } from "fs";
51738
- import { basename as basename18, dirname as dirname28, join as join82 } from "path";
51739
- function baselinePathFor(trackerPath) {
51740
- const match = PLAN_FILE_NAME2.exec(basename18(trackerPath));
51741
- if (match === null) {
51742
- throw new RangeError(`baselinePathFor: ${trackerPath} is not a PLAN or PLAN_TRACKER file`);
51743
- }
51744
- return join82(dirname28(trackerPath), `${BASELINE_FILE_PREFIX}${match[1] ?? ""}.json`);
51745
- }
51746
- function baselineOf(result2, recordedAt, commit) {
51747
- return {
51748
- version: BASELINE_VERSION,
51749
- recordedAt: recordedAt.toISOString(),
51750
- commit,
51751
- command: [...result2.command],
51752
- exitCode: result2.exitCode,
51753
- summary: result2.summary,
51754
- failures: result2.failures.map((failure6) => ({ file: failure6.file, name: failure6.name })),
51755
- errors: result2.errors,
51756
- junit: result2.junit
51757
- };
51758
- }
51759
- function writeBaseline(path4, baseline) {
51760
- mkdirSync20(dirname28(path4), { recursive: true });
51761
- const temporary = `${path4}.${process.pid}.tmp`;
51762
- writeFileSync21(temporary, `${JSON.stringify(baseline, null, 2)}
51763
- `, "utf8");
51764
- renameSync9(temporary, path4);
51765
- }
51766
- function isStringOrNull(value) {
51767
- return value === null || typeof value === "string";
51768
- }
51769
- function isFailure2(value) {
51770
- if (typeof value !== "object" || value === null)
51771
- return false;
51772
- const record = value;
51773
- return typeof record["file"] === "string" && typeof record["name"] === "string";
51774
- }
51775
- function badField(record) {
51776
- const checks = [
51777
- ["recordedAt", (value) => typeof value === "string"],
51778
- ["commit", isStringOrNull],
51779
- ["command", (value) => Array.isArray(value) && value.every((part) => typeof part === "string")],
51780
- ["exitCode", (value) => Number.isInteger(value)],
51781
- ["summary", isStringOrNull],
51782
- ["failures", (value) => Array.isArray(value) && value.every(isFailure2)],
51783
- ["errors", (value) => value === null || Number.isInteger(value)],
51784
- ["junit", (value) => JUNIT_READINGS.includes(value)]
51785
- ];
51786
- const failed4 = checks.find(([key, check]) => !check(record[key]));
51787
- return failed4?.[0] ?? null;
51788
- }
51789
- function asBaseline(parsed) {
51790
- if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
51791
- return { state: "unreadable", reason: "not a JSON object" };
51792
- }
51793
- const record = parsed;
51794
- if (record["version"] !== BASELINE_VERSION) {
51795
- return { state: "unreadable", reason: `version ${JSON.stringify(record["version"])}, expected ${BASELINE_VERSION}` };
51796
- }
51797
- const bad = badField(record);
51798
- if (bad !== null)
51799
- return { state: "unreadable", reason: `field ${bad} is missing or malformed` };
51800
- return { state: "read", baseline: parsed };
51801
- }
51802
- function readBaseline(path4) {
51803
- if (!existsSync53(path4))
51804
- return { state: "missing" };
51805
- let text2;
51806
- try {
51807
- text2 = readFileSync60(path4, "utf8");
51808
- } catch (error) {
51809
- const reason = error instanceof Error ? error.message : String(error);
51810
- return { state: "unreadable", reason };
51811
- }
51812
- try {
51813
- return asBaseline(JSON.parse(text2));
51814
- } catch {
51815
- return { state: "unreadable", reason: "not JSON" };
51816
- }
51817
- }
51818
- function failureKey2(failure6) {
51819
- return JSON.stringify([failure6.file, failure6.name]);
51820
- }
51821
- function splitFailures(failures, baseline) {
51822
- const held = new Set((baseline?.failures ?? []).map(failureKey2));
51823
- return {
51824
- fresh: failures.filter((failure6) => !held.has(failureKey2(failure6))),
51825
- known: failures.filter((failure6) => held.has(failureKey2(failure6)))
51826
- };
51827
- }
51828
- var BASELINE_VERSION = 1, BASELINE_FILE_PREFIX = "SUITE_BASELINE", PLAN_FILE_NAME2, JUNIT_READINGS;
51829
- var init_baseline = __esm(() => {
51830
- PLAN_FILE_NAME2 = /^PLAN(?:_TRACKER)?(-.+)?\.md$/;
51831
- JUNIT_READINGS = ["read", "missing", "unreadable"];
51832
- });
51833
-
51834
52421
  // src/suite/owns.ts
51835
52422
  function none(reason, detail) {
51836
52423
  return Object.freeze({ owns: null, reason, detail });
@@ -51914,7 +52501,7 @@ var init_owns = __esm(() => {
51914
52501
 
51915
52502
  // src/suite/run.ts
51916
52503
  import { existsSync as existsSync54, mkdirSync as mkdirSync21, readFileSync as readFileSync61, rmSync as rmSync11 } from "fs";
51917
- import { dirname as dirname29, isAbsolute as isAbsolute21 } from "path";
52504
+ import { dirname as dirname30, isAbsolute as isAbsolute21 } from "path";
51918
52505
  function readNoTestFiles(stderr) {
51919
52506
  return NO_TEST_FILES_LINE.test(stderr);
51920
52507
  }
@@ -51978,8 +52565,18 @@ function closeCase(walk) {
51978
52565
  if (open === null || !open.failed)
51979
52566
  return;
51980
52567
  const held = walk.failures.some((failure6) => failure6.file === open.file && failure6.name === open.name);
51981
- if (!held)
51982
- walk.failures.push({ file: open.file, name: open.name });
52568
+ if (held)
52569
+ return;
52570
+ walk.failures.push(open.message === undefined ? { file: open.file, name: open.name } : { file: open.file, name: open.name, message: open.message });
52571
+ }
52572
+ function firstMessageLine(message) {
52573
+ const line = message?.split(/\r?\n/, 1)[0]?.trim() ?? "";
52574
+ return line === "" ? undefined : line;
52575
+ }
52576
+ function failCase(open, attributes) {
52577
+ open.failed = true;
52578
+ if (open.message === undefined)
52579
+ open.message = firstMessageLine(attributes["message"]);
51983
52580
  }
51984
52581
  function openCase(walk, attributes) {
51985
52582
  const [fileSuite, ...describes] = walk.suites;
@@ -52006,7 +52603,7 @@ function takeTag(walk, match) {
52006
52603
  return;
52007
52604
  }
52008
52605
  if ((element === "failure" || element === "error") && closing !== "/" && walk.open !== null)
52009
- walk.open.failed = true;
52606
+ failCase(walk.open, attributes);
52010
52607
  }
52011
52608
  function parseJunitFailures(xml) {
52012
52609
  if (!/<testsuites\b/.test(xml) || !xml.includes("</testsuites>"))
@@ -52046,7 +52643,7 @@ async function runSuite(options) {
52046
52643
  throw new RangeError("runSuite: an empty path list would run the whole project; leave paths out for that");
52047
52644
  }
52048
52645
  const command = suiteCommand(options.junitFile, options.paths, options.changedSince);
52049
- mkdirSync21(dirname29(options.junitFile), { recursive: true });
52646
+ mkdirSync21(dirname30(options.junitFile), { recursive: true });
52050
52647
  rmSync11(options.junitFile, { force: true });
52051
52648
  const spawn = options.spawn ?? spawnSuite;
52052
52649
  const env = suiteEnv(options.env ?? process.env);
@@ -52181,7 +52778,7 @@ var init_scope2 = __esm(() => {
52181
52778
  // src/start/suite-step.ts
52182
52779
  import { existsSync as existsSync56, mkdirSync as mkdirSync22, readFileSync as readFileSync63, renameSync as renameSync10, writeFileSync as writeFileSync22 } from "fs";
52183
52780
  import { constants as constants3 } from "os";
52184
- import { basename as basename19, dirname as dirname30, join as join84 } from "path";
52781
+ import { basename as basename22, dirname as dirname31, join as join84 } from "path";
52185
52782
  function seamsOf(context) {
52186
52783
  const seams = context.seams ?? {};
52187
52784
  return {
@@ -52200,8 +52797,8 @@ function junitFileFor(repoRoot, sessionId, kind) {
52200
52797
  }
52201
52798
  function stageLedgerPathFor(trackerPath) {
52202
52799
  const baselinePath = baselinePathFor(trackerPath);
52203
- const name2 = basename19(baselinePath).replace(BASELINE_FILE_PREFIX, STAGE_LEDGER_PREFIX);
52204
- return join84(dirname30(baselinePath), name2);
52800
+ const name2 = basename22(baselinePath).replace(BASELINE_FILE_PREFIX, STAGE_LEDGER_PREFIX);
52801
+ return join84(dirname31(baselinePath), name2);
52205
52802
  }
52206
52803
  function isLedgerEntry(value) {
52207
52804
  if (typeof value !== "object" || value === null)
@@ -52235,11 +52832,11 @@ function readStageLedger(path4) {
52235
52832
  }
52236
52833
  if (typeof parsed !== "string")
52237
52834
  return parsed;
52238
- activeOutput().warn(`\u26A0\uFE0F ${basename19(path4)} does not read (${parsed}); every finished stage's step is taken again.`);
52835
+ activeOutput().warn(`\u26A0\uFE0F ${basename22(path4)} does not read (${parsed}); every finished stage's step is taken again.`);
52239
52836
  return [];
52240
52837
  }
52241
52838
  function writeStageLedger(path4, entries) {
52242
- mkdirSync22(dirname30(path4), { recursive: true });
52839
+ mkdirSync22(dirname31(path4), { recursive: true });
52243
52840
  const temporary = `${path4}.${process.pid}.tmp`;
52244
52841
  writeFileSync22(temporary, `${JSON.stringify({ version: STAGE_LEDGER_VERSION, stages: entries }, null, 2)}
52245
52842
  `, "utf8");
@@ -52254,7 +52851,7 @@ function addToLedger(trackerPath, added) {
52254
52851
  try {
52255
52852
  writeStageLedger(path4, [...entries, ...fresh]);
52256
52853
  } catch (error) {
52257
- activeOutput().warn(`\u26A0\uFE0F Could not write ${basename19(path4)} (${messageOf(error)}); the next run takes these stage steps again.`);
52854
+ activeOutput().warn(`\u26A0\uFE0F Could not write ${basename22(path4)} (${messageOf(error)}); the next run takes these stage steps again.`);
52258
52855
  }
52259
52856
  }
52260
52857
  function sameStage(a, b) {
@@ -52389,11 +52986,11 @@ async function ensureBaseline(context) {
52389
52986
  const reading2 = readBaseline(path4);
52390
52987
  if (reading2.state === "read") {
52391
52988
  const known = reading2.baseline.failures.length;
52392
- activeOutput().info(`\uD83D\uDCCF Suite baseline reused from ${basename19(path4)} (${reading2.baseline.recordedAt}): ${known} known failure(s).`);
52989
+ activeOutput().info(`\uD83D\uDCCF Suite baseline reused from ${basename22(path4)} (${reading2.baseline.recordedAt}): ${known} known failure(s).`);
52393
52990
  return { baseline: reading2.baseline, step: null, interrupted: false };
52394
52991
  }
52395
52992
  if (reading2.state === "unreadable")
52396
- activeOutput().warn(`\u26A0\uFE0F ${basename19(path4)} does not read (${reading2.reason}); recording the baseline again.`);
52993
+ activeOutput().warn(`\u26A0\uFE0F ${basename22(path4)} does not read (${reading2.reason}); recording the baseline again.`);
52397
52994
  const commit = readHead(seams.git);
52398
52995
  const result2 = await runOne(context, seams, "baseline", {});
52399
52996
  const baseline = baselineOf(result2, seams.now(), commit);
@@ -52403,7 +53000,7 @@ async function ensureBaseline(context) {
52403
53000
  try {
52404
53001
  writeBaseline(path4, baseline);
52405
53002
  } catch (error) {
52406
- activeOutput().warn(`\u26A0\uFE0F Could not write ${basename19(path4)} (${messageOf(error)}); the next run records the baseline again.`);
53003
+ activeOutput().warn(`\u26A0\uFE0F Could not write ${basename22(path4)} (${messageOf(error)}); the next run records the baseline again.`);
52407
53004
  }
52408
53005
  const outcome2 = settleStep(context, seams, settling);
52409
53006
  const covered = completeStages(readFileSync63(context.trackerPath, "utf8"));
@@ -52614,9 +53211,34 @@ var init_suite_steps_run = __esm(() => {
52614
53211
  init_suite_step();
52615
53212
  });
52616
53213
 
53214
+ // src/start/pr-retarget.ts
53215
+ function retargetedLine(number, from, to) {
53216
+ return `\u21AA Retargeted pull request #${String(number)} from ${from} to ${to} (pr.base).`;
53217
+ }
53218
+ function refusedRetargetLine(number, from, to, detail) {
53219
+ 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.`;
53220
+ }
53221
+ async function retargetPullRequest(pull2, base, seams) {
53222
+ const from = pull2.baseRefName;
53223
+ if (from === base)
53224
+ return { kind: "unchanged" };
53225
+ try {
53226
+ await seams.pulls.editBase(pull2.number, base);
53227
+ } catch (error) {
53228
+ const detail = messageOf(error);
53229
+ seams.output.warn(refusedRetargetLine(pull2.number, from, base, detail));
53230
+ return { kind: "refused", from, to: base, detail };
53231
+ }
53232
+ seams.output.info(retargetedLine(pull2.number, from, base));
53233
+ return { kind: "retargeted", from, to: base };
53234
+ }
53235
+ var init_pr_retarget = __esm(() => {
53236
+ init_config_sections();
53237
+ });
53238
+
52617
53239
  // src/release/prepare.ts
52618
53240
  import { mkdirSync as mkdirSync23, readFileSync as readFileSync64, writeFileSync as writeFileSync23 } from "fs";
52619
- import { dirname as dirname31, join as join85, posix as posix5 } from "path";
53241
+ import { dirname as dirname32, join as join85, posix as posix5 } from "path";
52620
53242
  function levelFields(reading2) {
52621
53243
  return { level: reading2.level, levelSource: reading2.source, notesLevel: reading2.notesLevel };
52622
53244
  }
@@ -52682,7 +53304,7 @@ function nameOf2(path4) {
52682
53304
  }
52683
53305
  function writeFragment(path4, resolved, text2) {
52684
53306
  try {
52685
- mkdirSync23(dirname31(resolved), { recursive: true });
53307
+ mkdirSync23(dirname32(resolved), { recursive: true });
52686
53308
  writeFileSync23(resolved, text2);
52687
53309
  return null;
52688
53310
  } catch (error) {
@@ -52999,18 +53621,18 @@ function announceBody(write, count2) {
52999
53621
  }
53000
53622
  out.info(` ${noun} ${verb} now in the body of pull request #${write.number}.`);
53001
53623
  }
53002
- async function writeBody(io, repoRoot, write) {
53003
- const reading2 = io.readProvider(repoRoot);
53624
+ async function writeBody(io, input, write) {
53625
+ const reading2 = io.readProvider(input.repoRoot);
53004
53626
  if (reading2.provider !== "gh") {
53005
53627
  const problem2 = noProviderProblem(reading2);
53006
53628
  activeOutput().info(` No pull request body carries ${JSON.stringify(write.lines.join(" "))}: ${problem2}.`);
53007
53629
  return unwritten(null, problem2);
53008
53630
  }
53009
- const body = await carryIntoBody(io.pulls(repoRoot), io.currentBranch(), write);
53631
+ const body = await carryIntoBody(io.pulls(input.repoRoot), input.branch, write);
53010
53632
  announceBody(body, write.lines.length);
53011
53633
  return body;
53012
53634
  }
53013
- async function reportFailure(io, repoRoot, outcome2, sentence, facts) {
53635
+ async function reportFailure(io, input, outcome2, sentence, facts) {
53014
53636
  const out = activeOutput();
53015
53637
  if (outcome2 === "skipped") {
53016
53638
  out.info(`
@@ -53021,7 +53643,7 @@ async function reportFailure(io, repoRoot, outcome2, sentence, facts) {
53021
53643
  }
53022
53644
  const parts = { forecast: null, levelReport: facts.levelReport };
53023
53645
  const block2 = releaseBodyBlock(parts);
53024
- const body = await writeBody(io, repoRoot, { sentence, block: block2, lines: [sentence, ...releaseBodyLines(parts)] });
53646
+ const body = await writeBody(io, input, { sentence, block: block2, lines: [sentence, ...releaseBodyLines(parts)] });
53025
53647
  return { outcome: outcome2, sentence, forecast: null, body, ...facts };
53026
53648
  }
53027
53649
  async function reportForecast(io, input, forecast, facts) {
@@ -53031,12 +53653,12 @@ async function reportForecast(io, input, forecast, facts) {
53031
53653
  out.warn(` \u26A0\uFE0F ${problem2}`);
53032
53654
  const parts = { forecast, levelReport: facts.levelReport };
53033
53655
  const block2 = releaseBodyBlock(parts);
53034
- const body = await writeBody(io, input.repoRoot, { sentence: null, block: block2, lines: releaseBodyLines(parts) });
53656
+ const body = await writeBody(io, input, { sentence: null, block: block2, lines: releaseBodyLines(parts) });
53035
53657
  return { outcome: "released", sentence: null, forecast, body, ...facts };
53036
53658
  }
53037
53659
  async function finishRelease(input, seams = {}) {
53038
53660
  const io = { ...RELEASE_STAGE_SEAMS, ...seams };
53039
- const { preparation, repoRoot, settings } = input;
53661
+ const { preparation, repoRoot, branch, settings } = input;
53040
53662
  if (preparation === null)
53041
53663
  return NO_RELEASE;
53042
53664
  const levelReport = releaseLevelReport({
@@ -53046,31 +53668,30 @@ async function finishRelease(input, seams = {}) {
53046
53668
  });
53047
53669
  const none2 = { fragment: null, subject: null, sha: null, levelReport };
53048
53670
  if (preparation.kind === "skipped") {
53049
- return reportFailure(io, repoRoot, "skipped", preparation.sentence, none2);
53671
+ return reportFailure(io, input, "skipped", preparation.sentence, none2);
53050
53672
  }
53051
53673
  const git = io.git(repoRoot);
53052
53674
  const verification = io.verify(preparation, { git, settings });
53053
53675
  if (verification.kind === "refused") {
53054
- return reportFailure(io, repoRoot, "refused", verification.sentence, none2);
53676
+ return reportFailure(io, input, "refused", verification.sentence, none2);
53055
53677
  }
53056
53678
  const fragment = verification.path;
53057
53679
  const subject = releaseSubject(preparation);
53058
53680
  const commit = commitFragment(git, fragment, subject);
53059
53681
  if (!commit.committed) {
53060
- return reportFailure(io, repoRoot, "uncommitted", commit.sentence, { ...none2, fragment, subject });
53682
+ return reportFailure(io, input, "uncommitted", commit.sentence, { ...none2, fragment, subject });
53061
53683
  }
53062
53684
  const out = activeOutput();
53063
53685
  const sha = commit.sha;
53064
53686
  const facts = { fragment, subject, sha, levelReport };
53065
53687
  out.info(`
53066
53688
  \uD83D\uDCE6 Committed ${commitName(sha, subject)} over ${fragment}.`);
53067
- const branch = io.currentBranch();
53068
53689
  const pushed = io.push(repoRoot, branch);
53069
53690
  if (!pushed.ok) {
53070
53691
  const said = pushed.output.trim() === "" ? "" : `: ${pushed.output.trim().split(`
53071
53692
  `)[0] ?? ""}`;
53072
53693
  const sentence = `${commitName(sha, subject)} was made but could not be pushed to ${branch}${said}`;
53073
- return reportFailure(io, repoRoot, "unpushed", sentence, facts);
53694
+ return reportFailure(io, input, "unpushed", sentence, facts);
53074
53695
  }
53075
53696
  out.info(` Pushed it to ${branch}.`);
53076
53697
  const forecast = io.forecast({ git, settings, prepared: preparation, verified: verification, now: io.now() });
@@ -53088,7 +53709,6 @@ var init_release_stage = __esm(() => {
53088
53709
  init_prepare();
53089
53710
  init_verify2();
53090
53711
  init_version2();
53091
- init_git3();
53092
53712
  RELEASE_STAGE_SEAMS = {
53093
53713
  prepare: prepareRelease,
53094
53714
  verify: verifyRelease,
@@ -53098,7 +53718,6 @@ var init_release_stage = __esm(() => {
53098
53718
  push: pushBranch,
53099
53719
  pulls: ghPullRequestsIn,
53100
53720
  readProvider: (repoRoot) => resolvePrProvider({ configured: null, dir: repoRoot }),
53101
- currentBranch: getCurrentBranch,
53102
53721
  now: () => new Date
53103
53722
  };
53104
53723
  PLAN_HEADING = /^# +(.+)$/;
@@ -53432,7 +54051,7 @@ var init_promoted_check = __esm(() => {
53432
54051
 
53433
54052
  // src/start/wrap-up.ts
53434
54053
  import { posix as posix8 } from "path";
53435
- function buildWrapUpPrompt(branch, planContent, openPullRequest = null, release = null, lessons = []) {
54054
+ function buildWrapUpPrompt(branch, base, planContent, openPullRequest = null, release = null, lessons = []) {
53436
54055
  return [
53437
54056
  "* Read `@progress.txt` in full.",
53438
54057
  "* `@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 `@`.",
@@ -53445,7 +54064,7 @@ function buildWrapUpPrompt(branch, planContent, openPullRequest = null, release
53445
54064
  "* 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.",
53446
54065
  ...releaseBullets(release),
53447
54066
  `* 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.`,
53448
- pullRequestStep(branch, openPullRequest),
54067
+ pullRequestStep(branch, base, openPullRequest),
53449
54068
  "* Do not include Claude attribution in the commit or PR message.",
53450
54069
  ...lessonsSection(lessons),
53451
54070
  "",
@@ -53455,15 +54074,15 @@ function buildWrapUpPrompt(branch, planContent, openPullRequest = null, release
53455
54074
  ].join(`
53456
54075
  `);
53457
54076
  }
53458
- function oneLine11(text2) {
54077
+ function oneLine12(text2) {
53459
54078
  return text2.trim().replace(/\s+/g, " ");
53460
54079
  }
53461
54080
  function lessonLines(lesson) {
53462
- const artifact = lesson.artifact === undefined || oneLine11(lesson.artifact) === "" ? "none" : oneLine11(lesson.artifact);
54081
+ const artifact = lesson.artifact === undefined || oneLine12(lesson.artifact) === "" ? "none" : oneLine12(lesson.artifact);
53463
54082
  return [
53464
54083
  `- \`${lesson.id}\``,
53465
- ` - trigger: ${oneLine11(lesson.trigger)}`,
53466
- ` - action: ${oneLine11(lesson.action)}`,
54084
+ ` - trigger: ${oneLine12(lesson.trigger)}`,
54085
+ ` - action: ${oneLine12(lesson.action)}`,
53467
54086
  ` - artifact: ${artifact}`
53468
54087
  ];
53469
54088
  }
@@ -53488,8 +54107,8 @@ function lessonsSection(lessons) {
53488
54107
  "```"
53489
54108
  ];
53490
54109
  }
53491
- function pullRequestStep(branch, openPullRequest) {
53492
- 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.`;
54110
+ function pullRequestStep(branch, base, openPullRequest) {
54111
+ 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.`;
53493
54112
  }
53494
54113
  function releaseBullets(release) {
53495
54114
  if (release === null)
@@ -53551,11 +54170,11 @@ async function lessonsToPromote(learning) {
53551
54170
  return [];
53552
54171
  }
53553
54172
  }
53554
- async function preserveProgress(planContent, settingSources, release, serving, learning, checkout) {
54173
+ async function preserveProgress(planContent, settingSources, release, serving, learning, base, checkout) {
53555
54174
  const branch = getCurrentBranch(checkout);
53556
54175
  const openPullRequest = await openPullRequestNumber(checkout, branch);
53557
54176
  return runWrapUpSession({
53558
- buildPrompt: (lessons) => buildWrapUpPrompt(branch, planContent, openPullRequest, release, lessons),
54177
+ buildPrompt: (lessons) => buildWrapUpPrompt(branch, base, planContent, openPullRequest, release, lessons),
53559
54178
  settingSources,
53560
54179
  serving,
53561
54180
  learning,
@@ -53620,8 +54239,8 @@ function missingPullRequestBullet(branch, previousMessage) {
53620
54239
  return [opening, " Its final message follows, quoted:", "", ...quoted4(previousMessage), ""];
53621
54240
  }
53622
54241
  function buildWrapUpRetryPrompt(retry) {
53623
- const base = buildWrapUpPrompt(retry.branch, retry.planContent, null, retry.release, retry.lessons);
53624
- const [classifierKey, ...rest] = base.split(`
54242
+ const first = buildWrapUpPrompt(retry.branch, retry.base, retry.planContent, null, retry.release, retry.lessons);
54243
+ const [classifierKey, ...rest] = first.split(`
53625
54244
  `);
53626
54245
  return [
53627
54246
  classifierKey,
@@ -53631,9 +54250,9 @@ function buildWrapUpRetryPrompt(retry) {
53631
54250
  `);
53632
54251
  }
53633
54252
  function retryWrapUp(run) {
53634
- const { branch, planContent, previousMessage } = run;
54253
+ const { branch, base, planContent, previousMessage } = run;
53635
54254
  return runWrapUpSession({
53636
- buildPrompt: (lessons) => buildWrapUpRetryPrompt({ branch, planContent, release: null, lessons, previousMessage }),
54255
+ buildPrompt: (lessons) => buildWrapUpRetryPrompt({ branch, base, planContent, release: null, lessons, previousMessage }),
53637
54256
  settingSources: run.settingSources,
53638
54257
  serving: run.serving,
53639
54258
  learning: run.learning,
@@ -53678,8 +54297,9 @@ async function runWrapUp(input) {
53678
54297
  planStub: planStub2,
53679
54298
  planContent
53680
54299
  });
54300
+ const base = resolveBaseBranch(createGitRunner(checkout), settings.prBase);
53681
54301
  emitLoopEvent({ kind: "wrap-up", phase: "session" });
53682
- const finalMessage = await preserveProgress(planContent, settingSources, release, serving, wrapUpLearning, checkout);
54302
+ const finalMessage = await preserveProgress(planContent, settingSources, release, serving, wrapUpLearning, base, checkout);
53683
54303
  if (activeOutputMode() !== "text") {
53684
54304
  const pullRequest = await openPullRequestNumber(checkout, expected2.branch);
53685
54305
  emitLoopEvent(pullRequest === null ? { kind: "no-pr", reason: `no open pull request for ${expected2.branch}` } : { kind: "pr", number: pullRequest });
@@ -53693,13 +54313,14 @@ async function runWrapUp(input) {
53693
54313
  dir: checkout
53694
54314
  });
53695
54315
  emitLoopEvent({ kind: "wrap-up", phase: "release" });
53696
- const finish2 = await finishRelease({ repoRoot: checkout, settings, preparation: release }, { readProvider: readProvider2 });
54316
+ const finish2 = await finishRelease({ repoRoot: checkout, branch: expected2.branch, settings, preparation: release }, { readProvider: readProvider2 });
53697
54317
  if (readProvider2().provider !== "none") {
53698
- const delivery = await deliverPullRequest({ branch: expected2.branch, retries: settings.loopWrapUpRetries, previousMessage: finalMessage }, deliverySeamsIn({ ...input, fragment: finish2.fragment }));
54318
+ const delivery = await deliverPullRequest({ branch: expected2.branch, retries: settings.loopWrapUpRetries, previousMessage: finalMessage }, deliverySeamsIn({ ...input, base, fragment: finish2.fragment }));
53699
54319
  if (delivery.kind === "interrupted")
53700
54320
  return;
53701
54321
  if (delivery.kind === "blocked")
53702
54322
  throw new CommandExit(1, delivery.message);
54323
+ await retargetPullRequest(delivery.pull, base, { pulls: ghPullRequestsIn(checkout), output: activeOutput() });
53703
54324
  }
53704
54325
  if (ciWait) {
53705
54326
  emitLoopEvent({ kind: "wrap-up", phase: "ci" });
@@ -53792,6 +54413,7 @@ function deliverySeamsIn(context) {
53792
54413
  retry: (previousMessage) => retryWrapUp({
53793
54414
  previousMessage,
53794
54415
  branch: expected2.branch,
54416
+ base: context.base,
53795
54417
  planContent: context.planContent,
53796
54418
  settingSources: context.settingSources,
53797
54419
  serving: context.serving,
@@ -53801,7 +54423,7 @@ function deliverySeamsIn(context) {
53801
54423
  openRunnerPullRequest: async () => {
53802
54424
  const runnerInput = runnerPrInputFor({
53803
54425
  branch: expected2.branch,
53804
- base: resolveBaseBranch(createGitRunner(checkout), context.settings.prBase),
54426
+ base: context.base,
53805
54427
  planContent: context.planContent,
53806
54428
  planStub: context.planStub,
53807
54429
  notes: fragmentNotesIn(checkout, context.fragment)
@@ -53826,6 +54448,7 @@ var init_wrap_up_run = __esm(() => {
53826
54448
  init_checkout_watch();
53827
54449
  init_loop_events();
53828
54450
  init_pr_lifecycle();
54451
+ init_pr_retarget();
53829
54452
  init_release_stage();
53830
54453
  init_runner_pr();
53831
54454
  init_wrap_up_retry();
@@ -53868,7 +54491,7 @@ var init_schedule = __esm(() => {
53868
54491
 
53869
54492
  // src/start.ts
53870
54493
  import fs3 from "fs";
53871
- import { homedir as homedir12 } from "os";
54494
+ import { homedir as homedir13 } from "os";
53872
54495
  import path5 from "path";
53873
54496
  import { fileURLToPath as fileURLToPath5 } from "url";
53874
54497
  async function start(args, repoRoot) {
@@ -53879,7 +54502,7 @@ async function start(args, repoRoot) {
53879
54502
  return;
53880
54503
  let runConfig;
53881
54504
  try {
53882
- runConfig = loadRunConfig({ root: repoRoot, home: homedir12() }, args);
54505
+ runConfig = loadRunConfig({ root: repoRoot, home: homedir13() }, args);
53883
54506
  } catch (error) {
53884
54507
  if (!(error instanceof ConfigError))
53885
54508
  throw error;
@@ -53910,7 +54533,7 @@ async function start(args, repoRoot) {
53910
54533
  let expected2 = openCheckoutExpectation({ projectRoot: repoRoot, checkout, branch });
53911
54534
  await announceRiskTotal({
53912
54535
  repoRoot,
53913
- home: homedir12(),
54536
+ home: homedir13(),
53914
54537
  planPath,
53915
54538
  config: runConfig.config,
53916
54539
  environment: process.env
@@ -53935,16 +54558,16 @@ async function start(args, repoRoot) {
53935
54558
  tiersRafa: runConfig.config.tiersRafa,
53936
54559
  tiersSkills: runConfig.config.tiersSkills,
53937
54560
  tiersAgents: runConfig.config.tiersAgents,
53938
- home: homedir12()
54561
+ home: homedir13()
53939
54562
  },
53940
- sync: { resolved: runConfig, home: homedir12() },
54563
+ sync: { resolved: runConfig, home: homedir13() },
53941
54564
  claim: createStartPreflightClaim(repoRoot, runConfig.config),
53942
54565
  drift: createStartPreflightDrift(repoRoot, runConfig.config)
53943
54566
  });
53944
- const serving = { root: repoRoot, run: session.id, home: homedir12(), settings: runConfig.config };
54567
+ const serving = { root: repoRoot, run: session.id, home: homedir13(), settings: runConfig.config };
53945
54568
  const learning = {
53946
54569
  kind: runConfig.config.learningAdapter,
53947
- home: homedir12(),
54570
+ home: homedir13(),
53948
54571
  blessMinConfidence: runConfig.config.learningBlessMinConfidence
53949
54572
  };
53950
54573
  const handout = {
@@ -53960,7 +54583,7 @@ async function start(args, repoRoot) {
53960
54583
  };
53961
54584
  const hubContact = createHubContact({
53962
54585
  root: repoRoot,
53963
- home: homedir12(),
54586
+ home: homedir13(),
53964
54587
  resolved: runConfig,
53965
54588
  warn: (message) => activeOutput().warn(message),
53966
54589
  sessionId: session.id
@@ -54039,9 +54662,10 @@ async function start(args, repoRoot) {
54039
54662
  inject: injectMode,
54040
54663
  repoRoot,
54041
54664
  checkout,
54042
- home: homedir12(),
54665
+ home: homedir13(),
54043
54666
  settingSources,
54044
54667
  knownMissing,
54668
+ inherited: runStartFailures(trackerPath),
54045
54669
  serving,
54046
54670
  handout,
54047
54671
  base
@@ -55605,7 +56229,7 @@ var init_review_gate = __esm(() => {
55605
56229
 
55606
56230
  // src/plan.ts
55607
56231
  import fs5 from "fs";
55608
- import { homedir as homedir13 } from "os";
56232
+ import { homedir as homedir14 } from "os";
55609
56233
  import path8 from "path";
55610
56234
  import { fileURLToPath as fileURLToPath6 } from "url";
55611
56235
  function argValue2(args, flag2) {
@@ -55767,7 +56391,7 @@ function resolvePlanConfig(repoRoot, home) {
55767
56391
  }
55768
56392
  }
55769
56393
  async function plan(args, repoRoot, registry = CORE_ADAPTER_REGISTRY) {
55770
- const home = homedir13();
56394
+ const home = homedir14();
55771
56395
  const config = resolvePlanConfig(repoRoot, home);
55772
56396
  const {
55773
56397
  settingSources,
@@ -56860,8 +57484,16 @@ var init_merge_guard = __esm(() => {
56860
57484
  });
56861
57485
 
56862
57486
  // src/pr/unchecked.ts
56863
- function uncheckedCaseOf(workflowCount) {
56864
- return workflowCount === 0 ? "no-workflow" : "workflows-exist";
57487
+ function uncheckedCaseOf(workflowCount, baseWorkflows = null) {
57488
+ if (workflowCount === 0)
57489
+ return "no-workflow";
57490
+ if (workflowCount !== null && baseWorkflows !== null && !baseWorkflows.runsOnPullRequests) {
57491
+ return "no-pull-request-workflow";
57492
+ }
57493
+ return "workflows-exist";
57494
+ }
57495
+ function noPullRequestWorkflowLine(base) {
57496
+ return `no workflow runs on pull requests into ${base}`;
56865
57497
  }
56866
57498
  function workflowCountLine(workflowCount) {
56867
57499
  if (workflowCount === null)
@@ -56875,24 +57507,177 @@ function skipChecksCommand(number) {
56875
57507
  function uncheckedQuestion(number) {
56876
57508
  return `Merge #${number} with no checks? [y/N]`;
56877
57509
  }
56878
- function uncheckedComment(workflowCount) {
56879
- return `${UNCHECKED_MERGE_SENTENCE}
57510
+ function uncheckedComment(workflowCount, baseLine = null) {
57511
+ const countLine2 = workflowCountLine(workflowCount);
57512
+ const paragraphs = baseLine === null ? [UNCHECKED_MERGE_SENTENCE, countLine2] : [UNCHECKED_MERGE_SENTENCE, countLine2, baseLine];
57513
+ return paragraphs.join(`
56880
57514
 
56881
- ${workflowCountLine(workflowCount)}`;
57515
+ `);
56882
57516
  }
56883
- function readUnchecked(number, workflowCount) {
56884
- const which = uncheckedCaseOf(workflowCount);
56885
- const warning = which === "no-workflow" ? NO_WORKFLOW_WARNING : WORKFLOWS_EXIST_WARNING;
57517
+ function warningOf2(which, baseWorkflows) {
57518
+ if (which === "no-workflow")
57519
+ return NO_WORKFLOW_WARNING;
57520
+ if (which === "no-pull-request-workflow" && baseWorkflows !== null)
57521
+ return noPullRequestWorkflowLine(baseWorkflows.base);
57522
+ return WORKFLOWS_EXIST_WARNING;
57523
+ }
57524
+ function readUnchecked(number, workflowCount, baseWorkflows = null) {
57525
+ const which = uncheckedCaseOf(workflowCount, baseWorkflows);
57526
+ const warning = warningOf2(which, baseWorkflows);
57527
+ const isBaseCase = which === "no-pull-request-workflow";
56886
57528
  return Object.freeze({
56887
57529
  case: which,
56888
57530
  warning: Object.freeze([workflowCountLine(workflowCount), warning]),
56889
- yesMayAnswer: which === "no-workflow",
57531
+ yesMayAnswer: which !== "workflows-exist",
56890
57532
  question: uncheckedQuestion(number),
56891
- comment: uncheckedComment(workflowCount)
57533
+ comment: uncheckedComment(workflowCount, isBaseCase ? warning : null)
56892
57534
  });
56893
57535
  }
56894
57536
  var NO_WORKFLOW_WARNING = "nothing on GitHub has tested this branch; you are relying on the checks run locally", WORKFLOWS_EXIST_WARNING = "CI may not have started (a path filter, a draft, Actions disabled, or it has not registered yet); this is probably not what you want", UNCHECKED_MERGE_SENTENCE = "Merged with no checks reported, by rafa pr merge --skip-checks.";
56895
57537
 
57538
+ // src/pr/workflow-triggers.ts
57539
+ function workflowsRunOnPullRequestsInto(workflowTexts, base) {
57540
+ return workflowTexts.some((text2) => workflowNamesBase(text2, base));
57541
+ }
57542
+ function workflowNamesBase(workflowText, base) {
57543
+ let document;
57544
+ try {
57545
+ document = Bun.YAML.parse(workflowText);
57546
+ } catch {
57547
+ return true;
57548
+ }
57549
+ return readWorkflow(document, base) !== false;
57550
+ }
57551
+ function readWorkflow(document, base) {
57552
+ if (!isPlainObject(document) || !("on" in document))
57553
+ return UNKNOWN;
57554
+ const on = document.on;
57555
+ if (typeof on === "string")
57556
+ return on === PULL_REQUEST_EVENT;
57557
+ if (Array.isArray(on)) {
57558
+ if (!on.every((event) => typeof event === "string"))
57559
+ return UNKNOWN;
57560
+ return on.includes(PULL_REQUEST_EVENT);
57561
+ }
57562
+ if (!isPlainObject(on))
57563
+ return UNKNOWN;
57564
+ if (!(PULL_REQUEST_EVENT in on))
57565
+ return false;
57566
+ return readPullRequestTrigger(on[PULL_REQUEST_EVENT], base);
57567
+ }
57568
+ function readPullRequestTrigger(trigger, base) {
57569
+ if (trigger === null || trigger === undefined)
57570
+ return true;
57571
+ if (!isPlainObject(trigger))
57572
+ return UNKNOWN;
57573
+ const branches = trigger.branches;
57574
+ const ignored = trigger["branches-ignore"];
57575
+ if (branches !== undefined && ignored !== undefined)
57576
+ return UNKNOWN;
57577
+ if (branches !== undefined)
57578
+ return readBranches2(branches, base);
57579
+ if (ignored !== undefined)
57580
+ return readBranchesIgnore(ignored, base);
57581
+ return true;
57582
+ }
57583
+ function readBranches2(filter, base) {
57584
+ const patterns = patternListOf(filter);
57585
+ if (patterns === null)
57586
+ return UNKNOWN;
57587
+ let named4 = false;
57588
+ for (const pattern of patterns) {
57589
+ const negated = pattern.startsWith("!");
57590
+ const matches = branchPatternMatches(negated ? pattern.slice(1) : pattern, base);
57591
+ if (matches === null)
57592
+ return UNKNOWN;
57593
+ if (matches)
57594
+ named4 = !negated;
57595
+ }
57596
+ return named4;
57597
+ }
57598
+ function readBranchesIgnore(filter, base) {
57599
+ const patterns = patternListOf(filter);
57600
+ if (patterns === null || patterns.some((pattern) => pattern.startsWith("!")))
57601
+ return UNKNOWN;
57602
+ let anyMatch = false;
57603
+ for (const pattern of patterns) {
57604
+ const matches = branchPatternMatches(pattern, base);
57605
+ if (matches === null)
57606
+ return UNKNOWN;
57607
+ anyMatch = anyMatch || matches;
57608
+ }
57609
+ return !anyMatch;
57610
+ }
57611
+ function patternListOf(filter) {
57612
+ if (typeof filter === "string")
57613
+ return [filter];
57614
+ if (Array.isArray(filter) && filter.every((pattern) => typeof pattern === "string"))
57615
+ return filter;
57616
+ return null;
57617
+ }
57618
+ function branchPatternMatches(pattern, branch) {
57619
+ const source = regexSourceOf(pattern);
57620
+ if (source === null)
57621
+ return null;
57622
+ try {
57623
+ return new RegExp(`^${source}$`).test(branch);
57624
+ } catch {
57625
+ return null;
57626
+ }
57627
+ }
57628
+ function regexSourceOf(pattern) {
57629
+ let source = "";
57630
+ let quantifiable = false;
57631
+ let index = 0;
57632
+ while (index < pattern.length) {
57633
+ const char = pattern.charAt(index);
57634
+ if (char === "*") {
57635
+ const isDouble = pattern.charAt(index + 1) === "*";
57636
+ source += isDouble ? ".*" : "[^/]*";
57637
+ index += isDouble ? 2 : 1;
57638
+ quantifiable = false;
57639
+ } else if (char === "?" || char === "+") {
57640
+ if (!quantifiable)
57641
+ return null;
57642
+ source += char;
57643
+ index += 1;
57644
+ quantifiable = false;
57645
+ } else if (char === "[") {
57646
+ const close = pattern.indexOf("]", index + 1);
57647
+ if (close === -1)
57648
+ return null;
57649
+ const body = pattern.slice(index + 1, close);
57650
+ if (!BRACKET_BODY.test(body))
57651
+ return null;
57652
+ source += `[${body}]`;
57653
+ index = close + 1;
57654
+ quantifiable = true;
57655
+ } else if (char === "\\") {
57656
+ if (index + 1 >= pattern.length)
57657
+ return null;
57658
+ source += escapeRegex(pattern.charAt(index + 1));
57659
+ index += 2;
57660
+ quantifiable = true;
57661
+ } else {
57662
+ source += escapeRegex(char);
57663
+ index += 1;
57664
+ quantifiable = true;
57665
+ }
57666
+ }
57667
+ return source;
57668
+ }
57669
+ function escapeRegex(char) {
57670
+ return char.replace(REGEX_SPECIAL, "\\$&");
57671
+ }
57672
+ function isPlainObject(value) {
57673
+ return typeof value === "object" && value !== null && !Array.isArray(value);
57674
+ }
57675
+ var PULL_REQUEST_EVENT = "pull_request", UNKNOWN = "unknown", BRACKET_BODY, REGEX_SPECIAL;
57676
+ var init_workflow_triggers = __esm(() => {
57677
+ BRACKET_BODY = /^[A-Za-z0-9-]+$/;
57678
+ REGEX_SPECIAL = /[.*+?^${}()|[\]\\/]/g;
57679
+ });
57680
+
56896
57681
  // src/commands/pr/merge-unchecked.ts
56897
57682
  function refusal13(head, reading2, summary, tail) {
56898
57683
  return new CommandExit(1, [
@@ -56910,10 +57695,37 @@ async function workflowCountOf(pulls) {
56910
57695
  return null;
56911
57696
  }
56912
57697
  }
57698
+ function workflowBlobsOf(listing2) {
57699
+ return listing2.split("\x00").map((entry) => LS_TREE_ENTRY2.exec(entry)).filter((match) => match !== null).filter(([, type, , path9]) => type === "blob" && WORKFLOW_ENDINGS.some((ending) => path9?.endsWith(ending) === true)).map(([, , oid]) => oid ?? "");
57700
+ }
57701
+ function readBaseWorkflows(git, base) {
57702
+ if (base === "" || base.startsWith("-"))
57703
+ return null;
57704
+ if (!git(["fetch", "origin", base]).ok)
57705
+ return null;
57706
+ const listed4 = git(["ls-tree", "--full-tree", "-z", `origin/${base}`, WORKFLOWS_DIR]);
57707
+ if (!listed4.ok)
57708
+ return null;
57709
+ const blobs = workflowBlobsOf(listed4.stdout);
57710
+ if (blobs.length === 0)
57711
+ return null;
57712
+ const shown2 = blobs.map((oid) => git(["cat-file", "blob", oid]));
57713
+ if (shown2.some((result2) => !result2.ok))
57714
+ return null;
57715
+ const texts = shown2.map((result2) => result2.stdout);
57716
+ return Object.freeze({ base, runsOnPullRequests: workflowsRunOnPullRequestsInto(texts, base) });
57717
+ }
57718
+ function baseWorkflowsOf(options, workflowCount) {
57719
+ const { git, base } = options;
57720
+ if (workflowCount === null || workflowCount === 0 || git === undefined || base === undefined)
57721
+ return null;
57722
+ return readBaseWorkflows(git, base);
57723
+ }
56913
57724
  async function readUncheckedMerge(options) {
56914
57725
  const { pulls, number, yes, summary } = options;
56915
57726
  const workflowCount = await workflowCountOf(pulls);
56916
- const reading2 = readUnchecked(number, workflowCount);
57727
+ const baseWorkflows = baseWorkflowsOf(options, workflowCount);
57728
+ const reading2 = readUnchecked(number, workflowCount, baseWorkflows);
56917
57729
  if (yes && !reading2.yesMayAnswer) {
56918
57730
  throw refusal13(`rafa pr merge refuses --yes for #${number} with no checks: workflows exist, or their count could not be read.`, reading2, summary, "That is more likely a fault than a choice, so a person has to answer. Run it again without --yes on a terminal.");
56919
57731
  }
@@ -56922,7 +57734,7 @@ async function readUncheckedMerge(options) {
56922
57734
  const tail = reading2.yesMayAnswer ? "Merge it without the question with --yes." : "--yes is refused here, so run it on a terminal and answer it.";
56923
57735
  throw refusal13(`rafa pr merge asks before merging #${number} with no checks, and standard input is no terminal.`, reading2, summary, tail);
56924
57736
  }
56925
- return Object.freeze({ workflowCount, reading: reading2, yes });
57737
+ return Object.freeze({ workflowCount, baseWorkflows, reading: reading2, yes });
56926
57738
  }
56927
57739
  function isYes2(answer5) {
56928
57740
  return answer5 !== null && YES_ANSWERS5.includes(answer5.trim().toLowerCase());
@@ -56956,12 +57768,15 @@ async function postUncheckedComment(pulls, number, unchecked, warn4) {
56956
57768
  return null;
56957
57769
  }
56958
57770
  }
56959
- var INDENT12 = " ", YES_ANSWERS5;
57771
+ var INDENT12 = " ", YES_ANSWERS5, WORKFLOWS_DIR = ".github/workflows/", WORKFLOW_ENDINGS, LS_TREE_ENTRY2;
56960
57772
  var init_merge_unchecked = __esm(() => {
56961
57773
  init_command();
56962
57774
  init_confirm();
56963
57775
  init_config_sections();
57776
+ init_workflow_triggers();
56964
57777
  YES_ANSWERS5 = ["y", "yes"];
57778
+ WORKFLOW_ENDINGS = [".yml", ".yaml"];
57779
+ LS_TREE_ENTRY2 = /^\d+ (\w+) ([0-9a-f]+)\t(.+)$/s;
56965
57780
  });
56966
57781
 
56967
57782
  // src/commands/pr/merge.ts
@@ -57108,8 +57923,8 @@ async function reportAfterCleanUp(context, pr, seams, detail) {
57108
57923
  return { unblocked: null, freed };
57109
57924
  }
57110
57925
  async function askToMerge(context, pr, seams, ask) {
57111
- const { number, summary, yes, skipChecks } = ask;
57112
- const unchecked = skipChecks ? await readUncheckedMerge({ pulls: pr.pulls, number, yes, summary, isTerminal: seams.isTerminal }) : null;
57926
+ const { number, summary, yes, skipChecks, base, git } = ask;
57927
+ const unchecked = skipChecks ? await readUncheckedMerge({ pulls: pr.pulls, number, yes, summary, isTerminal: seams.isTerminal, base, git }) : null;
57113
57928
  if (unchecked === null && !yes)
57114
57929
  requireTerminal(seams, summary);
57115
57930
  context.output.info(summary);
@@ -57174,7 +57989,7 @@ async function runMerge2(context, seams) {
57174
57989
  openPrompter: prompterOf(seams)
57175
57990
  });
57176
57991
  const summary = summaryLine2(detail, method);
57177
- const answer5 = guard.go ? await askToMerge(context, pr, seams, { number: detail.number, summary, yes, skipChecks }) : { go: false, unchecked: null };
57992
+ const answer5 = guard.go ? await askToMerge(context, pr, seams, { number: detail.number, summary, yes, skipChecks, base: detail.baseRefName, git }) : { go: false, unchecked: null };
57178
57993
  const answered3 = {
57179
57994
  number: detail.number,
57180
57995
  source: pick.source,
@@ -57238,7 +58053,7 @@ function createPrMergeCommand(seams = DEFAULT_MERGE_SEAMS) {
57238
58053
  subject: "pr",
57239
58054
  action: "merge",
57240
58055
  summary: "merge a pull request and clean up both branches",
57241
- description: "Merges one pull request through the GitHub CLI and then, in code, switches to the base branch," + " pulls it fast-forward only, deletes the head branch locally and on the remote where it is still there," + " and prunes, reporting each step; a head branch this checkout has no local branch for is reported as a" + " skipped local delete, and the rest still run. Refuses before it asks anything on a dirty working tree, on a pull" + " request that is not green or does not merge, and on a head branch checked out in another worktree. It" + " shows the pull request, its branches and the method and asks `Merge? [y/N]`; `--yes` skips the question," + " and without a terminal and without `--yes` it refuses. A pull request that reports no checks at all is" + " refused unless `--skip-checks` is given, which is refused on any pull request that does report checks;" + " with it the command reads how many workflows the repository defines, prints a warning for that case, asks" + " `Merge #<n> with no checks? [y/N]` (which `--yes` answers only where the repository defines no workflow)," + " and after the merge posts one comment on the pull request saying so. Where the release runs, the release" + " guard reads the branch before the question: a `collision` is refused unless" + " `dangerous.acceptVersionCollision` is true, and `pr.versionCollision` sets whether a `missing` or" + " `stale` branch merges silently, with a warning, after its own question, or not at all. A step that fails never undoes the merge: it" + " prints what is left as commands to paste and exits 1. After the merge it ticks the `Closes #<n>` line of" + " every issue the pull request closes on every open board whose checklist lists it, or on the roadmap issue while no issue carries `type:roadmap`, warning rather than failing when that write" + ' does not land. It ends by reading every open issue whose "Blocked by:" line names an issue this pull' + " request closes, asking whether to remove `spec:blocked` from each one whose blockers have all closed;" + " `--yes` does not answer that question, and every failure of that reading is a warning. With" + " board.relationships set to native it ticks no epic checklist and, in place of that reading, prints the" + " open issues the merge freed, whose blockers GitHub clears by itself, asking nothing and writing nothing. With" + " `--output=json` the pull request, the method, the steps that ran, the follow-ups, the roadmap tick and" + " the unblock reading, or the freed issues in native mode, are the data of the terminal result event. Refuses with exit code 2 where" + " `pr.provider` is not `gh`.",
58056
+ description: "Merges one pull request through the GitHub CLI and then, in code, switches to the base branch," + " pulls it fast-forward only, deletes the head branch locally and on the remote where it is still there," + " and prunes, reporting each step; a head branch this checkout has no local branch for is reported as a" + " skipped local delete, and the rest still run. Refuses before it asks anything on a dirty working tree, on a pull" + " request that is not green or does not merge, and on a head branch checked out in another worktree. It" + " shows the pull request, its branches and the method and asks `Merge? [y/N]`; `--yes` skips the question," + " and without a terminal and without `--yes` it refuses. A pull request that reports no checks at all is" + " refused unless `--skip-checks` is given, which is refused on any pull request that does report checks;" + " with it the command reads how many workflows the repository defines, prints a warning for that case, asks" + " `Merge #<n> with no checks? [y/N]` (which `--yes` answers only where the repository defines no workflow, or" + " where no workflow file on the base has a `pull_request` trigger naming it)," + " and after the merge posts one comment on the pull request saying so. Where the release runs, the release" + " guard reads the branch before the question: a `collision` is refused unless" + " `dangerous.acceptVersionCollision` is true, and `pr.versionCollision` sets whether a `missing` or" + " `stale` branch merges silently, with a warning, after its own question, or not at all. A step that fails never undoes the merge: it" + " prints what is left as commands to paste and exits 1. After the merge it ticks the `Closes #<n>` line of" + " every issue the pull request closes on every open board whose checklist lists it, or on the roadmap issue while no issue carries `type:roadmap`, warning rather than failing when that write" + ' does not land. It ends by reading every open issue whose "Blocked by:" line names an issue this pull' + " request closes, asking whether to remove `spec:blocked` from each one whose blockers have all closed;" + " `--yes` does not answer that question, and every failure of that reading is a warning. With" + " board.relationships set to native it ticks no epic checklist and, in place of that reading, prints the" + " open issues the merge freed, whose blockers GitHub clears by itself, asking nothing and writing nothing. With" + " `--output=json` the pull request, the method, the steps that ran, the follow-ups, the roadmap tick and" + " the unblock reading, or the freed issues in native mode, are the data of the terminal result event. Refuses with exit code 2 where" + " `pr.provider` is not `gh`.",
57242
58057
  args: [
57243
58058
  {
57244
58059
  name: "n",
@@ -57254,7 +58069,7 @@ function createPrMergeCommand(seams = DEFAULT_MERGE_SEAMS) {
57254
58069
  },
57255
58070
  {
57256
58071
  name: "skip-checks",
57257
- description: "Merge a pull request that reports no checks at all, after a warning and its own question." + " Refused where the pull request reports any check; `--yes` beside it is refused where the repository" + " defines a workflow or its workflow count could not be read.",
58072
+ description: "Merge a pull request that reports no checks at all, after a warning and its own question." + " Refused where the pull request reports any check; `--yes` beside it is refused where the repository" + " defines a workflow that may run on pull requests into the base, or its workflow count could not be read.",
57258
58073
  type: "boolean"
57259
58074
  },
57260
58075
  {
@@ -57358,7 +58173,7 @@ function readFailedStep(lines2) {
57358
58173
  const head = groupStep(line.text);
57359
58174
  if (head !== undefined)
57360
58175
  group = head;
57361
- if (!line.text.startsWith(ERROR_PREFIX))
58176
+ if (!line.text.startsWith(ERROR_PREFIX2))
57362
58177
  continue;
57363
58178
  if (line.step !== undefined)
57364
58179
  return { name: line.step, source: "step-column" };
@@ -57398,7 +58213,7 @@ function readFailedLog(text2, options = {}) {
57398
58213
  total: lines2.length
57399
58214
  };
57400
58215
  }
57401
- var FAILED_LOG_TAIL_LINES = 40, UNKNOWN_STEP = "UNKNOWN STEP", GROUP_STEP_PREFIX = "##[group]Run ", ERROR_PREFIX = "##[error]", COLUMN = "\t", BOM = "\uFEFF", TIMESTAMP, COLOUR, PREFIX34 = "pr triage evidence";
58216
+ var FAILED_LOG_TAIL_LINES = 40, UNKNOWN_STEP = "UNKNOWN STEP", GROUP_STEP_PREFIX = "##[group]Run ", ERROR_PREFIX2 = "##[error]", COLUMN = "\t", BOM = "\uFEFF", TIMESTAMP, COLOUR, PREFIX34 = "pr triage evidence";
57402
58217
  var init_evidence = __esm(() => {
57403
58218
  init_config_sections();
57404
58219
  TIMESTAMP = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d+)?Z /;
@@ -58595,7 +59410,7 @@ var init_triage_guard = __esm(() => {
58595
59410
  function ignoredLines(ignored) {
58596
59411
  return ignored.map((comment) => `${INDENT15}${comment.reason}`);
58597
59412
  }
58598
- function evidenceOf(reading2) {
59413
+ function evidenceOf2(reading2) {
58599
59414
  return reading2.logs?.chosen?.evidence;
58600
59415
  }
58601
59416
  function workflowCountOf2(reading2) {
@@ -58632,7 +59447,7 @@ function noFiles(reading2) {
58632
59447
  return "none; the head merges cleanly";
58633
59448
  }
58634
59449
  function excerptBlock(reading2) {
58635
- const evidence = evidenceOf(reading2);
59450
+ const evidence = evidenceOf2(reading2);
58636
59451
  if (evidence === undefined)
58637
59452
  return [];
58638
59453
  const excerpt2 = excerptLines(evidence);
@@ -58775,7 +59590,7 @@ var init_budget2 = __esm(() => {
58775
59590
 
58776
59591
  // src/pr/plans/load.ts
58777
59592
  import { existsSync as existsSync57, readFileSync as readFileSync69 } from "fs";
58778
- import { dirname as dirname32, join as join87 } from "path";
59593
+ import { dirname as dirname33, join as join87 } from "path";
58779
59594
  import { fileURLToPath as fileURLToPath7 } from "url";
58780
59595
  function pinnedPlanFileName(triageClass) {
58781
59596
  return `resolve-${triageClass}.md`;
@@ -58853,7 +59668,7 @@ var MODULE_DIR5, PINNED_PLANS_DIRNAME = "plans", PINNED_PLAN_CLASSES, SLOT_PATTE
58853
59668
  var init_load2 = __esm(() => {
58854
59669
  init_classes();
58855
59670
  init_follow_up();
58856
- MODULE_DIR5 = dirname32(fileURLToPath7(import.meta.url));
59671
+ MODULE_DIR5 = dirname33(fileURLToPath7(import.meta.url));
58857
59672
  PINNED_PLAN_CLASSES = Object.freeze([
58858
59673
  ...SIMPLE_TRIAGE_CLASSES,
58859
59674
  ...DEPENDENCY_BUMP_SIMPLE_CLASSES
@@ -59003,7 +59818,7 @@ var init_resolve_loop = __esm(() => {
59003
59818
 
59004
59819
  // src/pr/triage/version-convert.ts
59005
59820
  import { mkdirSync as mkdirSync25, readFileSync as readFileSync70, writeFileSync as writeFileSync26 } from "fs";
59006
- import { dirname as dirname33, join as join89, posix as posix9 } from "path";
59821
+ import { dirname as dirname34, join as join89, posix as posix9 } from "path";
59007
59822
  function conversionLevel(from, to) {
59008
59823
  if (from === null)
59009
59824
  return null;
@@ -59139,7 +59954,7 @@ function writeConversion(input, plan2) {
59139
59954
  for (const [path9, text2] of writes) {
59140
59955
  const target = join89(input.worktree, path9);
59141
59956
  try {
59142
- mkdirSync25(dirname33(target), { recursive: true });
59957
+ mkdirSync25(dirname34(target), { recursive: true });
59143
59958
  writeFileSync26(target, text2);
59144
59959
  } catch (error) {
59145
59960
  return `${path9} could not be written in the worktree, which may hold part of the conversion: ${messageOf(error)}`;
@@ -59345,7 +60160,7 @@ function resolveCommentBody(run, detail, assessment, attempts, resolved, workflo
59345
60160
  at: run.now(),
59346
60161
  attempts,
59347
60162
  resolved,
59348
- evidence: evidenceOf(run.reading),
60163
+ evidence: evidenceOf2(run.reading),
59349
60164
  workflowCount
59350
60165
  });
59351
60166
  return isDependabotBranch(detail) ? `${body}
@@ -59423,7 +60238,7 @@ async function endOfAttempt(run, before, waited) {
59423
60238
  reading: reading2,
59424
60239
  outcome: { triageClass: assessment.triageClass, step: assessment.step },
59425
60240
  assessment,
59426
- evidence: fresh === null ? before.evidence : evidenceOf(reading2),
60241
+ evidence: fresh === null ? before.evidence : evidenceOf2(reading2),
59427
60242
  green,
59428
60243
  unchanged: reading2.rerun.decision === "already-assessed" ? ATTEMPT_OUTCOME_UNCHANGED : null
59429
60244
  };
@@ -59432,7 +60247,7 @@ function stopLines(run, reason, end, removal) {
59432
60247
  const prompt = end.reading.prompt ?? buildFollowUpPrompt({
59433
60248
  pr: end.reading.detail,
59434
60249
  assessment: end.assessment,
59435
- evidence: evidenceOf(end.reading)
60250
+ evidence: evidenceOf2(end.reading)
59436
60251
  });
59437
60252
  return [
59438
60253
  `\u26D4 rafa pr triage --resolve gave up on #${run.number}: ${reason}.`,
@@ -59505,7 +60320,7 @@ async function runAttempts(run, detail, first) {
59505
60320
  const runLoop = run.runLoop ?? runResolveLoop;
59506
60321
  const state = {
59507
60322
  assessment: first,
59508
- evidence: evidenceOf(run.reading),
60323
+ evidence: evidenceOf2(run.reading),
59509
60324
  reading: run.reading,
59510
60325
  previous: null,
59511
60326
  spent: spentAttempts(run.reading.attempts),
@@ -59696,7 +60511,7 @@ async function commentOn3(options, reading2, existing) {
59696
60511
  at: options.at,
59697
60512
  attempts: reading2.attempts,
59698
60513
  resolved: RESOLVED,
59699
- evidence: evidenceOf(reading2),
60514
+ evidence: evidenceOf2(reading2),
59700
60515
  workflowCount: workflowCountOf2(reading2)
59701
60516
  });
59702
60517
  try {
@@ -61260,7 +62075,7 @@ var init_proposal_file = __esm(() => {
61260
62075
 
61261
62076
  // src/backfill/backup.ts
61262
62077
  import { copyFileSync as copyFileSync5, existsSync as existsSync59, mkdirSync as mkdirSync26 } from "fs";
61263
- import { dirname as dirname34, isAbsolute as isAbsolute24, join as join93, relative as relative19, resolve as resolve37, sep as sep8 } from "path";
62078
+ import { dirname as dirname35, isAbsolute as isAbsolute24, join as join93, relative as relative19, resolve as resolve37, sep as sep8 } from "path";
61264
62079
  function backupDirectory(base) {
61265
62080
  return join93(base, BACKUP_PATH);
61266
62081
  }
@@ -61284,7 +62099,7 @@ function backupFile(path9, options) {
61284
62099
  return { path: path9, kind: "missing", backup: null };
61285
62100
  if (existsSync59(backup))
61286
62101
  return { path: path9, kind: "kept", backup };
61287
- mkdirSync26(dirname34(backup), { recursive: true });
62102
+ mkdirSync26(dirname35(backup), { recursive: true });
61288
62103
  copyFileSync5(path9, backup);
61289
62104
  return { path: path9, kind: "copied", backup };
61290
62105
  }
@@ -61314,7 +62129,7 @@ var init_backup = __esm(() => {
61314
62129
 
61315
62130
  // src/backfill/derive.ts
61316
62131
  import { readFileSync as readFileSync73, writeFileSync as writeFileSync27 } from "fs";
61317
- import { basename as basename20 } from "path";
62132
+ import { basename as basename23 } from "path";
61318
62133
  function renderWhenToUse(trigger, prevents) {
61319
62134
  return `${sentenceEnded(trigger)} Prevents: ${prevents}`;
61320
62135
  }
@@ -61424,8 +62239,8 @@ function fileChanges(name2, data, body, options) {
61424
62239
  return { changes, notes };
61425
62240
  }
61426
62241
  function planFile(entry, options) {
61427
- if (basename20(entry.path) !== SKILL_FILE) {
61428
- return inert(entry, "skipped", `${basename20(entry.path)} is no ${SKILL_FILE}, so it registers no skill`);
62242
+ if (basename23(entry.path) !== SKILL_FILE) {
62243
+ return inert(entry, "skipped", `${basename23(entry.path)} is no ${SKILL_FILE}, so it registers no skill`);
61429
62244
  }
61430
62245
  if (entry.name === null) {
61431
62246
  return inert(entry, "skipped", "its place implies no skill name, so no stack can be derived from it");
@@ -62138,7 +62953,7 @@ var init_propose = __esm(() => {
62138
62953
 
62139
62954
  // src/demote/select.ts
62140
62955
  import { readdirSync as readdirSync21, readFileSync as readFileSync76, realpathSync as realpathSync17, statSync as statSync28 } from "fs";
62141
- import { basename as basename21, dirname as dirname35, join as join95 } from "path";
62956
+ import { basename as basename24, dirname as dirname36, join as join95 } from "path";
62142
62957
  function realOrSelf(path9) {
62143
62958
  try {
62144
62959
  return realpathSync17(path9);
@@ -62154,12 +62969,12 @@ function samePath(left, right) {
62154
62969
  return resolved === other || resolved.toLowerCase() === other.toLowerCase();
62155
62970
  }
62156
62971
  function resolveDemotionScope(dir, home) {
62157
- if (basename21(dir) !== SKILLS_DIRECTORY)
62972
+ if (basename24(dir) !== SKILLS_DIRECTORY)
62158
62973
  return null;
62159
- const claude = dirname35(dir);
62160
- if (basename21(claude) !== CLAUDE_DIRECTORY)
62974
+ const claude = dirname36(dir);
62975
+ if (basename24(claude) !== CLAUDE_DIRECTORY)
62161
62976
  return null;
62162
- const base = dirname35(claude);
62977
+ const base = dirname36(claude);
62163
62978
  const demotedDir = join95(base, DEMOTED_PATH);
62164
62979
  return {
62165
62980
  scope: samePath(base, home) ? "user" : "project",
@@ -62604,7 +63419,7 @@ var init_check3 = __esm(() => {
62604
63419
  // src/demote/apply.ts
62605
63420
  import { copyFileSync as copyFileSync6, existsSync as existsSync60, mkdirSync as mkdirSync28, mkdtempSync as mkdtempSync4, readdirSync as readdirSync22, readFileSync as readFileSync77, renameSync as renameSync11, rmSync as rmSync13, statSync as statSync30, unlinkSync as unlinkSync2, writeFileSync as writeFileSync29 } from "fs";
62606
63421
  import { tmpdir as tmpdir6 } from "os";
62607
- import { dirname as dirname36, join as join97 } from "path";
63422
+ import { dirname as dirname37, join as join97 } from "path";
62608
63423
  function isLearnedPath(path9) {
62609
63424
  const segments = path9.split("/");
62610
63425
  return segments.length === 2 && segments[0] === LEARNED_DIRECTORY && (segments[1] ?? "").endsWith(MARKDOWN_EXTENSION);
@@ -62661,7 +63476,7 @@ function planKept(scope, row3, file) {
62661
63476
  return inert3(row3.path, "procedure", "kept", "procedure-shaped: left where it is, for the backfill");
62662
63477
  }
62663
63478
  const to = promotedPath(scope, row3.path);
62664
- if (existsSync60(to) || existsSync60(dirname36(to))) {
63479
+ if (existsSync60(to) || existsSync60(dirname37(to))) {
62665
63480
  return inert3(row3.path, "procedure", "refused", `kept as a skill, but ${to} is already there`);
62666
63481
  }
62667
63482
  return {
@@ -62753,7 +63568,7 @@ function planDemotion(report5, scope, seams = DEFAULT_APPLY_SEAMS) {
62753
63568
  }
62754
63569
  }
62755
63570
  function moveFile(move3) {
62756
- mkdirSync28(dirname36(move3.to), { recursive: true });
63571
+ mkdirSync28(dirname37(move3.to), { recursive: true });
62757
63572
  try {
62758
63573
  renameSync11(move3.from, move3.to);
62759
63574
  } catch {
@@ -62764,7 +63579,7 @@ function moveFile(move3) {
62764
63579
  function applyDemotion(plan2) {
62765
63580
  for (const action of plan2.actions) {
62766
63581
  if (action.write !== null) {
62767
- mkdirSync28(dirname36(action.write.path), { recursive: true });
63582
+ mkdirSync28(dirname37(action.write.path), { recursive: true });
62768
63583
  writeFileSync29(action.write.path, action.write.text, "utf8");
62769
63584
  }
62770
63585
  if (action.move !== null)
@@ -62830,7 +63645,7 @@ var init_draft = __esm(() => {
62830
63645
 
62831
63646
  // src/commands/skill/demote.ts
62832
63647
  import { existsSync as existsSync61, mkdirSync as mkdirSync29, readFileSync as readFileSync78, statSync as statSync31, writeFileSync as writeFileSync30 } from "fs";
62833
- import { dirname as dirname37, resolve as resolve40 } from "path";
63648
+ import { dirname as dirname38, resolve as resolve40 } from "path";
62834
63649
  function readApply(value) {
62835
63650
  if (value === undefined || value === false || value === "false")
62836
63651
  return false;
@@ -62892,7 +63707,7 @@ function previousReport(path9, context) {
62892
63707
  function runWrite(context, scope) {
62893
63708
  const files = selectSources(scope);
62894
63709
  const report5 = buildDemotionReport(files, previousReport(scope.reportPath, context));
62895
- mkdirSync29(dirname37(scope.reportPath), { recursive: true });
63710
+ mkdirSync29(dirname38(scope.reportPath), { recursive: true });
62896
63711
  writeFileSync30(scope.reportPath, writeDemotionReport(report5), "utf8");
62897
63712
  const result2 = {
62898
63713
  applied: false,
@@ -63797,6 +64612,504 @@ var init_status4 = __esm(() => {
63797
64612
  status_default3 = createStatusCommand();
63798
64613
  });
63799
64614
 
64615
+ // src/commands/update/stub.ts
64616
+ function stubMessage(action, issue5) {
64617
+ return `rafa update ${action} is a feature in development, tracked in ${ISSUES_URL}/${String(issue5)}.` + " Nothing was changed.";
64618
+ }
64619
+ function createUpdateStub(spec) {
64620
+ const command = {
64621
+ name: `update ${spec.action}`,
64622
+ subject: "update",
64623
+ action: spec.action,
64624
+ summary: `${spec.summary} (in development, #${String(spec.issue)})`,
64625
+ description: `${spec.summary}. In development: it refuses with exit code 1, naming #${String(spec.issue)},` + " and changes nothing.",
64626
+ args: [],
64627
+ flags: [],
64628
+ examples: [{ cmd: `rafa update ${spec.action}`, note: `Refuses, naming #${String(spec.issue)}; changes nothing.` }],
64629
+ outputs: ["text", "json"],
64630
+ needsProject: false,
64631
+ ...spec.hidden === true ? { hidden: true } : {},
64632
+ run: () => Promise.reject(new CommandExit(1, stubMessage(spec.action, spec.issue)))
64633
+ };
64634
+ return Object.freeze(command);
64635
+ }
64636
+ var ISSUES_URL = "https://github.com/open-tomato/rafa/issues";
64637
+ var init_stub = __esm(() => {
64638
+ init_command();
64639
+ });
64640
+
64641
+ // src/commands/update/board.ts
64642
+ var board_default;
64643
+ var init_board = __esm(() => {
64644
+ init_stub();
64645
+ board_default = createUpdateStub({ action: "board", summary: "apply the board's newest setup and its migrations", issue: 717 });
64646
+ });
64647
+
64648
+ // src/project/lock.ts
64649
+ import { existsSync as existsSync63, lstatSync as lstatSync3, readFileSync as readFileSync79, writeFileSync as writeFileSync31 } from "fs";
64650
+ import { join as join98 } from "path";
64651
+ function lockPath(root) {
64652
+ return join98(root, LOCK_FILE);
64653
+ }
64654
+ function projectLockText(version2) {
64655
+ const lock = { lockfileVersion: LOCKFILE_VERSION, rafa: version2 };
64656
+ return `${JSON.stringify(lock, null, 2)}
64657
+ `;
64658
+ }
64659
+ function isLink(path9) {
64660
+ try {
64661
+ return lstatSync3(path9).isSymbolicLink();
64662
+ } catch {
64663
+ return false;
64664
+ }
64665
+ }
64666
+ function refuse16(problem2) {
64667
+ throw new ProjectLockError(`${LOCK_FILE} ${problem2}`);
64668
+ }
64669
+ function lockOf(value) {
64670
+ if (typeof value !== "object" || value === null || Array.isArray(value))
64671
+ refuse16("holds no JSON object");
64672
+ const record = value;
64673
+ const format = record["lockfileVersion"];
64674
+ if (format !== LOCKFILE_VERSION) {
64675
+ refuse16(`is in lockfileVersion ${String(format)}, and this rafa reads ${String(LOCKFILE_VERSION)}: a newer rafa wrote it`);
64676
+ }
64677
+ const rafa = record["rafa"];
64678
+ if (typeof rafa !== "string" || parseSemanticVersion(rafa) === null) {
64679
+ refuse16(`names rafa ${JSON.stringify(rafa)}, which is no version`);
64680
+ }
64681
+ return { lockfileVersion: LOCKFILE_VERSION, rafa };
64682
+ }
64683
+ function readProjectLock(root) {
64684
+ const path9 = lockPath(root);
64685
+ if (!existsSync63(path9)) {
64686
+ if (isLink(path9))
64687
+ refuse16("is a link to nothing");
64688
+ return null;
64689
+ }
64690
+ let text2;
64691
+ try {
64692
+ text2 = readFileSync79(path9, "utf8");
64693
+ } catch (error) {
64694
+ refuse16(`could not be read: ${messageOf(error)}`);
64695
+ }
64696
+ let value;
64697
+ try {
64698
+ value = JSON.parse(text2);
64699
+ } catch {
64700
+ refuse16("is not JSON");
64701
+ }
64702
+ return lockOf(value);
64703
+ }
64704
+ function writeProjectLock(root, version2) {
64705
+ writeFileSync31(lockPath(root), projectLockText(version2), "utf8");
64706
+ }
64707
+ var LOCK_FILE = "rafa.lock", LOCKFILE_VERSION = 1, ProjectLockError;
64708
+ var init_lock = __esm(() => {
64709
+ init_config_sections();
64710
+ init_version2();
64711
+ ProjectLockError = class ProjectLockError extends Error {
64712
+ name = "ProjectLockError";
64713
+ };
64714
+ });
64715
+
64716
+ // src/project/update-current.ts
64717
+ import { existsSync as existsSync64 } from "fs";
64718
+ import { join as join99 } from "path";
64719
+ function missingFolders(root) {
64720
+ return PROJECT_TREE.map((name2) => join99(SCOPE_DIR, name2)).filter((relative21) => !existsSync64(join99(root, relative21)));
64721
+ }
64722
+ async function readBoard3(access) {
64723
+ if (access.gh === null)
64724
+ return { kind: "skipped", reason: access.reason };
64725
+ try {
64726
+ const held = await listBoardLabels(access.gh);
64727
+ return { kind: "labels", missing: missingBoardLabels(held).map((label) => label.name) };
64728
+ } catch (error) {
64729
+ return { kind: "skipped", reason: messageOf(error) };
64730
+ }
64731
+ }
64732
+ function lockChangeOf(range) {
64733
+ if (range.kind === "adopt")
64734
+ return "created";
64735
+ return range.kind === "patch" ? "updated" : "unchanged";
64736
+ }
64737
+ function fromOf(range) {
64738
+ if (range.kind === "patch")
64739
+ return range.from;
64740
+ if (range.kind === "same")
64741
+ return range.to;
64742
+ return null;
64743
+ }
64744
+ async function readUpdatePlan(roots, range, board) {
64745
+ const { root, lockRoot } = roots;
64746
+ return {
64747
+ root,
64748
+ lockRoot,
64749
+ from: fromOf(range),
64750
+ to: range.to,
64751
+ folders: missingFolders(root),
64752
+ board: await readBoard3(board),
64753
+ deprecations: DEPRECATIONS,
64754
+ lock: lockChangeOf(range)
64755
+ };
64756
+ }
64757
+ function planChanges(plan2) {
64758
+ const labels = plan2.board.kind === "labels" ? plan2.board.missing.length : 0;
64759
+ return plan2.folders.length > 0 || labels > 0 || plan2.deprecations.length > 0 || plan2.lock !== "unchanged";
64760
+ }
64761
+ function foldersNowThere(plan2) {
64762
+ return plan2.folders.filter((relative21) => existsSync64(join99(plan2.root, relative21)));
64763
+ }
64764
+ function writeFolders(plan2) {
64765
+ if (plan2.folders.length === 0)
64766
+ return [];
64767
+ try {
64768
+ return writeProjectScope(plan2.root).filter((write) => write.change === "created").map((write) => write.path);
64769
+ } catch (error) {
64770
+ throw new UpdateStepError("folders", messageOf(error), foldersNowThere(plan2));
64771
+ }
64772
+ }
64773
+ function createdLabels(parts) {
64774
+ return parts.filter((part) => part.outcome === "created").map((part) => `label ${part.name}`);
64775
+ }
64776
+ async function applyUpdatePlan(plan2, gh) {
64777
+ const folders = writeFolders(plan2);
64778
+ const labels = gh !== null && plan2.board.kind === "labels" && plan2.board.missing.length > 0 ? await setUpLabels(gh) : [];
64779
+ if (plan2.lock !== "unchanged") {
64780
+ try {
64781
+ writeProjectLock(plan2.lockRoot, plan2.to);
64782
+ } catch (error) {
64783
+ throw new UpdateStepError("lock", messageOf(error), [...folders, ...createdLabels(labels)]);
64784
+ }
64785
+ }
64786
+ return { folders, labels, lock: plan2.lock };
64787
+ }
64788
+ var DEPRECATIONS, UpdateStepError;
64789
+ var init_update_current = __esm(() => {
64790
+ init_setup();
64791
+ init_config_sections();
64792
+ init_lock();
64793
+ init_scaffold();
64794
+ init_scope();
64795
+ DEPRECATIONS = Object.freeze([]);
64796
+ UpdateStepError = class UpdateStepError extends Error {
64797
+ step;
64798
+ written;
64799
+ name = "UpdateStepError";
64800
+ constructor(step2, message, written3) {
64801
+ super(message);
64802
+ this.step = step2;
64803
+ this.written = written3;
64804
+ }
64805
+ };
64806
+ });
64807
+
64808
+ // src/project/update-range.ts
64809
+ function refused6(reason, message) {
64810
+ return { kind: "refused", reason, message };
64811
+ }
64812
+ function readCurrentRange(recorded, installed) {
64813
+ const to = parseSemanticVersion(installed);
64814
+ if (to === null)
64815
+ return refused6("unreadable", `the installed rafa answers "${installed}", which is no version`);
64816
+ if (recorded === null)
64817
+ return { kind: "adopt", to: installed };
64818
+ const from = parseSemanticVersion(recorded);
64819
+ if (from === null)
64820
+ return refused6("unreadable", `the project records rafa "${recorded}", which is no version`);
64821
+ const order = compareVersions(to, from);
64822
+ if (order < 0) {
64823
+ return refused6("older", `the installed rafa ${installed} is older than the project's ${recorded}, and no downgrade runs before 1.0.0`);
64824
+ }
64825
+ if (to.major !== from.major) {
64826
+ return refused6("newer-major", `the installed rafa ${installed} is a newer major than the project's ${recorded}; that move is rafa update latest`);
64827
+ }
64828
+ if (to.minor !== from.minor) {
64829
+ return refused6("newer-minor", `the installed rafa ${installed} is a newer minor than the project's ${recorded}; that move is rafa update next`);
64830
+ }
64831
+ return order === 0 ? { kind: "same", to: installed } : { kind: "patch", from: recorded, to: installed };
64832
+ }
64833
+ var init_update_range = __esm(() => {
64834
+ init_status();
64835
+ init_version2();
64836
+ });
64837
+
64838
+ // src/commands/update/current.ts
64839
+ function checkoutLockRoot(root, dir) {
64840
+ const run = Bun.spawnSync(["git", "rev-parse", "--show-toplevel"], { cwd: dir, stdout: "pipe", stderr: "pipe" });
64841
+ if (run.exitCode !== 0)
64842
+ return root;
64843
+ const top = run.stdout.toString().trim();
64844
+ return top !== "" && mainCheckoutOf(top) === root ? top : root;
64845
+ }
64846
+ function readSwitch4(context, name2) {
64847
+ const value = context.flags[name2];
64848
+ if (value === undefined || value === false || value === "false")
64849
+ return false;
64850
+ if (value === true || value === "true")
64851
+ return true;
64852
+ throw new CommandExit(1, `\u274C --${name2} takes no value, and read "${String(value)}" as one
64853
+ Usage: ${USAGE47}`);
64854
+ }
64855
+ function couldNotRun(problem2) {
64856
+ return new CommandExit(2, `FAIL \u2014 ${problem2}
64857
+ nothing was changed.`);
64858
+ }
64859
+ function projectOf17(context) {
64860
+ if (context.project === null)
64861
+ throw new Error("rafa update current runs inside a project, and was handed none");
64862
+ return context.project;
64863
+ }
64864
+ function boardAccess(project, seams) {
64865
+ let configured;
64866
+ try {
64867
+ configured = loadConfig({ root: project.root, home: project.home }, {}, () => {}).config.prProvider;
64868
+ } catch (error) {
64869
+ throw couldNotRun(`config: ${messageOf(error)}`);
64870
+ }
64871
+ const provider = resolvePrProvider({ configured, dir: project.root, readRemote: seams.readRemote }).provider;
64872
+ return provider === "gh" ? { gh: seams.openGh(project.root), reason: "" } : { gh: null, reason: `pr.provider is ${provider}, so there is no GitHub board` };
64873
+ }
64874
+ function foldersText(plan2) {
64875
+ if (plan2.folders.length === 0)
64876
+ return "nothing missing";
64877
+ return `create ${plan2.folders.join(", ")}`;
64878
+ }
64879
+ function boardText2(plan2) {
64880
+ if (plan2.board.kind === "skipped")
64881
+ return `skipped: ${plan2.board.reason}`;
64882
+ if (plan2.board.missing.length === 0)
64883
+ return "every label present";
64884
+ return `create labels ${plan2.board.missing.join(", ")}`;
64885
+ }
64886
+ function deprecationsText(plan2) {
64887
+ if (plan2.deprecations.length === 0)
64888
+ return "none registered yet (#718)";
64889
+ return `${String(plan2.deprecations.length)} to run`;
64890
+ }
64891
+ function lockText(plan2) {
64892
+ const where = plan2.lockRoot === plan2.root ? "" : ` in ${plan2.lockRoot}`;
64893
+ if (plan2.lock === "unchanged")
64894
+ return `unchanged at ${plan2.to}${where}`;
64895
+ if (plan2.lock === "created")
64896
+ return `create at ${plan2.to}${where}`;
64897
+ return `update ${String(plan2.from)} \u2192 ${plan2.to}${where}`;
64898
+ }
64899
+ function planLines2(plan2) {
64900
+ const from = plan2.from ?? `no ${LOCK_FILE}`;
64901
+ return [
64902
+ `rafa update current: ${plan2.root}, from ${from} to ${plan2.to}`,
64903
+ ` folders ${foldersText(plan2)}`,
64904
+ ` board ${boardText2(plan2)}`,
64905
+ ` deprecations ${deprecationsText(plan2)}`,
64906
+ ` ${LOCK_FILE} ${lockText(plan2)}`
64907
+ ];
64908
+ }
64909
+ function appliedLines(applied, plan2, context) {
64910
+ for (const path9 of applied.folders)
64911
+ context.output.info(`created ${path9}`);
64912
+ for (const part of applied.labels) {
64913
+ if (part.outcome === "created")
64914
+ context.output.info(`created label ${part.name}`);
64915
+ if (part.outcome === "refused")
64916
+ context.output.warn(`label ${part.name} was not created: ${part.detail}`);
64917
+ }
64918
+ if (applied.lock !== "unchanged")
64919
+ context.output.info(`wrote ${LOCK_FILE} at ${plan2.to}`);
64920
+ }
64921
+ async function confirmed3(yes, seams) {
64922
+ if (yes)
64923
+ return true;
64924
+ if (!seams.isTerminal()) {
64925
+ throw new CommandExit(1, "REFUSED \u2014 rafa update current asks before it changes anything, and there is no" + " terminal to ask on. Run it with --dry-run to see the changes, then with --yes to apply them." + `
64926
+ nothing was changed.`);
64927
+ }
64928
+ const prompter = seams.openPrompter();
64929
+ try {
64930
+ const answer5 = await prompter.ask(APPLY_QUESTION);
64931
+ return answer5 !== null && YES_ANSWERS7.includes(answer5.trim().toLowerCase());
64932
+ } finally {
64933
+ prompter.close();
64934
+ }
64935
+ }
64936
+ function readRange2(root, installed) {
64937
+ let recorded;
64938
+ try {
64939
+ recorded = readProjectLock(root)?.rafa ?? null;
64940
+ } catch (error) {
64941
+ throw couldNotRun(messageOf(error));
64942
+ }
64943
+ const range = readCurrentRange(recorded, installed);
64944
+ if (range.kind === "refused")
64945
+ throw new CommandExit(1, `REFUSED \u2014 ${range.message}
64946
+ nothing was changed.`);
64947
+ return range;
64948
+ }
64949
+ function refuseConflicts(project) {
64950
+ const conflicts2 = scaffoldConflicts(project.root, project.home);
64951
+ if (conflicts2.length === 0)
64952
+ return;
64953
+ throw couldNotRun(`the project scope cannot be written:
64954
+ ${conflicts2.map((conflict) => ` ${conflict}`).join(`
64955
+ `)}`);
64956
+ }
64957
+ function stoppedPartWay(error) {
64958
+ const kept = error.written.length === 0 ? "Nothing was written before it." : `Already written, and kept: ${error.written.join(", ")}.`;
64959
+ return new CommandExit(2, `FAIL \u2014 ${error.step}: ${error.message}
64960
+ stopped part-way. ${kept}` + `
64961
+ Fix the cause and run rafa update current again: each step writes only what is missing.`);
64962
+ }
64963
+ async function applyAndReport(plan2, gh, context) {
64964
+ let applied;
64965
+ try {
64966
+ applied = await applyUpdatePlan(plan2, gh);
64967
+ } catch (error) {
64968
+ if (error instanceof UpdateStepError)
64969
+ throw stoppedPartWay(error);
64970
+ throw error;
64971
+ }
64972
+ appliedLines(applied, plan2, context);
64973
+ return applied;
64974
+ }
64975
+ function refusedLabels(applied) {
64976
+ const refused7 = applied.labels.filter((part) => part.outcome === "refused");
64977
+ if (refused7.length === 0)
64978
+ return null;
64979
+ return new CommandExit(1, `${String(refused7.length)} board label(s) were not created; everything else was applied.` + " Run rafa update current again once gh can create them.");
64980
+ }
64981
+ function stopLine2(plan2, dryRun) {
64982
+ if (!planChanges(plan2))
64983
+ return "Nothing to change.";
64984
+ if (dryRun)
64985
+ return "Dry run: nothing was written. Run rafa update current to apply it.";
64986
+ return null;
64987
+ }
64988
+ async function runUpdateCurrent(context, seams) {
64989
+ expectNoArgument(context.args, USAGE47);
64990
+ const dryRun = readSwitch4(context, "dry-run");
64991
+ const yes = readSwitch4(context, "yes");
64992
+ const project = projectOf17(context);
64993
+ const lockRoot = seams.lockRootOf(project.root);
64994
+ const range = readRange2(lockRoot, seams.installed);
64995
+ refuseConflicts(project);
64996
+ const access = boardAccess(project, seams);
64997
+ const plan2 = await readUpdatePlan({ root: project.root, lockRoot }, range, access);
64998
+ for (const line of planLines2(plan2))
64999
+ context.output.info(line);
65000
+ const finish2 = (applied2) => {
65001
+ if (context.outputMode === "json")
65002
+ context.output.result({ plan: plan2, applied: applied2 });
65003
+ };
65004
+ const stop = stopLine2(plan2, dryRun);
65005
+ if (stop !== null) {
65006
+ context.output.info(stop);
65007
+ finish2(null);
65008
+ return;
65009
+ }
65010
+ context.output.warn("rafa cannot roll these changes back. Run it with --dry-run first to only see them.");
65011
+ if (!await confirmed3(yes, seams)) {
65012
+ context.output.info("Nothing was changed.");
65013
+ finish2(null);
65014
+ return;
65015
+ }
65016
+ const applied = await applyAndReport(plan2, access.gh, context);
65017
+ finish2(applied);
65018
+ const refusal16 = refusedLabels(applied);
65019
+ if (refusal16 !== null)
65020
+ throw refusal16;
65021
+ }
65022
+ function createUpdateCurrentCommand(seams = DEFAULT_UPDATE_CURRENT_SEAMS) {
65023
+ const command = {
65024
+ name: "update current",
65025
+ subject: "update",
65026
+ action: "current",
65027
+ summary: "bring this project to the installed rafa, within its patch range",
65028
+ description: "Brings the project to the installed rafa when the installed version shares the major and" + ` minor of the version \`${LOCK_FILE}\` records at the project root, and is the same patch or newer.` + " A project with no lock is adopted at the installed version. It creates the missing `.rafa/` folders" + " and the missing board labels on a GitHub board, runs the deprecations step (none registered yet)," + ` and writes \`${LOCK_FILE}\`. It prints every change first; \`--dry-run\` stops there. Otherwise it` + " warns that the changes cannot be rolled back and asks once, `--yes` answering; with no terminal and" + " no `--yes` it refuses with exit code 1. A newer minor or major, or an installed rafa older than the" + " lock, refuses with exit code 1, and so does a board label `gh` would not create, once everything" + " else is applied. A lock or config that cannot be read, or a `.rafa/` path holding something else," + " exits 2 before anything is asked; a folder or the lock that cannot be written exits 2 naming the" + " step and what was already written. In a linked worktree the lock is the worktree's, on its branch." + " With `--output=json` the plan and what was applied are the data of the terminal result event.",
65029
+ args: [],
65030
+ flags: [
65031
+ { name: "dry-run", description: "Print every change and write nothing.", type: "boolean" },
65032
+ { name: "yes", description: "Apply the changes without asking.", type: "boolean" }
65033
+ ],
65034
+ examples: [
65035
+ { cmd: "rafa update current --dry-run", note: "Lists the folders, labels and lock it would write, and writes nothing." },
65036
+ { cmd: "rafa update current --yes", note: "Applies them without asking, as a script or a loop host runs it." }
65037
+ ],
65038
+ outputs: ["text", "json"],
65039
+ run: async (context) => runUpdateCurrent(context, seams)
65040
+ };
65041
+ return Object.freeze(command);
65042
+ }
65043
+ var USAGE47 = "rafa update current [--dry-run] [--yes]", APPLY_QUESTION = "Apply these changes? [y/N] ", YES_ANSWERS7, DEFAULT_UPDATE_CURRENT_SEAMS, current_default2;
65044
+ var init_current2 = __esm(() => {
65045
+ init_github();
65046
+ init_command();
65047
+ init_confirm();
65048
+ init_version();
65049
+ init_config_load();
65050
+ init_config_sections();
65051
+ init_provider();
65052
+ init_lock();
65053
+ init_scaffold();
65054
+ init_update_current();
65055
+ init_update_range();
65056
+ init_worktree_root();
65057
+ init_project_id();
65058
+ init_plan_files();
65059
+ YES_ANSWERS7 = ["y", "yes"];
65060
+ DEFAULT_UPDATE_CURRENT_SEAMS = Object.freeze({
65061
+ installed: RAFA_VERSION,
65062
+ openGh: (root) => createGhRunner({ cwd: root }),
65063
+ readRemote: gitRemoteUrl,
65064
+ isTerminal: () => process.stdin.isTTY === true,
65065
+ openPrompter: () => createLinePrompter(process.stdin, process.stderr),
65066
+ lockRootOf: (root) => checkoutLockRoot(root, process.cwd())
65067
+ });
65068
+ current_default2 = createUpdateCurrentCommand();
65069
+ });
65070
+
65071
+ // src/commands/update/latest.ts
65072
+ var latest_default;
65073
+ var init_latest = __esm(() => {
65074
+ init_stub();
65075
+ latest_default = createUpdateStub({ action: "latest", summary: "move this project to the newest version", issue: 716 });
65076
+ });
65077
+
65078
+ // src/commands/update/next.ts
65079
+ var next_default2;
65080
+ var init_next2 = __esm(() => {
65081
+ init_stub();
65082
+ next_default2 = createUpdateStub({ action: "next", summary: "move this project to the newest minor of its major", issue: 716 });
65083
+ });
65084
+
65085
+ // src/commands/update/port.ts
65086
+ var port_default;
65087
+ var init_port = __esm(() => {
65088
+ init_stub();
65089
+ port_default = createUpdateStub({ action: "port", summary: "move this project to a version, as update project does", issue: 716, hidden: true });
65090
+ });
65091
+
65092
+ // src/commands/update/project.ts
65093
+ var project_default;
65094
+ var init_project = __esm(() => {
65095
+ init_stub();
65096
+ project_default = createUpdateStub({ action: "project", summary: "move this project to a version, with its migrations", issue: 716 });
65097
+ });
65098
+
65099
+ // src/commands/update/rafa.ts
65100
+ var rafa_default;
65101
+ var init_rafa = __esm(() => {
65102
+ init_stub();
65103
+ rafa_default = createUpdateStub({ action: "rafa", summary: "move this project to a version, as update project does", issue: 716, hidden: true });
65104
+ });
65105
+
65106
+ // src/commands/update/self.ts
65107
+ var self_default;
65108
+ var init_self = __esm(() => {
65109
+ init_stub();
65110
+ self_default = createUpdateStub({ action: "self", summary: "update rafa itself, from npm, a pinned version or another source", issue: 715 });
65111
+ });
65112
+
63800
65113
  // src/usage.ts
63801
65114
  async function usage(_args) {
63802
65115
  const pct = await getClaudeUsagePercent();
@@ -63928,6 +65241,14 @@ var init_commands2 = __esm(() => {
63928
65241
  init_show8();
63929
65242
  init_status4();
63930
65243
  init_switch();
65244
+ init_board();
65245
+ init_current2();
65246
+ init_latest();
65247
+ init_next2();
65248
+ init_port();
65249
+ init_project();
65250
+ init_rafa();
65251
+ init_self();
63931
65252
  init_usage2();
63932
65253
  CORE_SUBJECTS = Object.freeze([
63933
65254
  { name: "plan", summary: "create plans from specs; list, show and validate them; read their risk and needs" },
@@ -63942,7 +65263,8 @@ var init_commands2 = __esm(() => {
63942
65263
  { name: "release", summary: "read the release state of the project; settle the waiting fragments into a version; tag the commit that set it" },
63943
65264
  { name: "board", summary: "list the boards with their owner, epic count, and which is current and home" },
63944
65265
  { name: "epic", summary: "show one epic's issues as the Roadmap table; create an epic; defer or promote it; move an issue to it; close it through the gate or cancel it" },
63945
- { name: "claim", summary: "give up this device's claim on an issue; hand it to another store or withdraw the offer; accept a handover; take over a stale claim" }
65266
+ { name: "claim", summary: "give up this device's claim on an issue; hand it to another store or withdraw the offer; accept a handover; take over a stale claim" },
65267
+ { name: "update", summary: "bring this project to the installed rafa; the other updates are in development" }
63946
65268
  ]);
63947
65269
  CORE_COMMANDS = Object.freeze([
63948
65270
  create_default2,
@@ -64014,6 +65336,14 @@ var init_commands2 = __esm(() => {
64014
65336
  hand_default,
64015
65337
  accept_default,
64016
65338
  take_default,
65339
+ current_default2,
65340
+ self_default,
65341
+ project_default,
65342
+ rafa_default,
65343
+ port_default,
65344
+ board_default,
65345
+ next_default2,
65346
+ latest_default,
64017
65347
  status_default3,
64018
65348
  next_default,
64019
65349
  roadmap_default,
@@ -64029,7 +65359,7 @@ var init_commands2 = __esm(() => {
64029
65359
  });
64030
65360
 
64031
65361
  // src/rafa.ts
64032
- import { homedir as homedir14 } from "os";
65362
+ import { homedir as homedir15 } from "os";
64033
65363
 
64034
65364
  // src/cli/dispatch.ts
64035
65365
  init_active();
@@ -64644,12 +65974,12 @@ init_config_sections();
64644
65974
  init_sessions();
64645
65975
  init_scope();
64646
65976
  init_sections2();
64647
- import { mkdirSync as mkdirSync30, readFileSync as readFileSync79, renameSync as renameSync12, writeFileSync as writeFileSync31 } from "fs";
64648
- import { dirname as dirname38, join as join98 } from "path";
65977
+ import { mkdirSync as mkdirSync30, readFileSync as readFileSync80, renameSync as renameSync12, writeFileSync as writeFileSync32 } from "fs";
65978
+ import { dirname as dirname39, join as join100 } from "path";
64649
65979
  var SEEN_FILE = "status-seen.json";
64650
65980
  var SEEN_VERSION = 1;
64651
65981
  function seenFilePath(root) {
64652
- return join98(scopeAt(root).dir, SEEN_FILE);
65982
+ return join100(scopeAt(root).dir, SEEN_FILE);
64653
65983
  }
64654
65984
  function readSeenSessions(root, isAlive) {
64655
65985
  const sessions = {};
@@ -64704,7 +66034,7 @@ function asSnapshot(value) {
64704
66034
  function readSeenFile(root) {
64705
66035
  let text2;
64706
66036
  try {
64707
- text2 = readFileSync79(seenFilePath(root), "utf8");
66037
+ text2 = readFileSync80(seenFilePath(root), "utf8");
64708
66038
  } catch {
64709
66039
  return null;
64710
66040
  }
@@ -64716,9 +66046,9 @@ function readSeenFile(root) {
64716
66046
  }
64717
66047
  function writeSeenFile(root, snapshot) {
64718
66048
  const file = seenFilePath(root);
64719
- mkdirSync30(dirname38(file), { recursive: true });
66049
+ mkdirSync30(dirname39(file), { recursive: true });
64720
66050
  const temporary = `${file}.${String(process.pid)}.tmp`;
64721
- writeFileSync31(temporary, `${JSON.stringify(snapshot, null, 2)}
66051
+ writeFileSync32(temporary, `${JSON.stringify(snapshot, null, 2)}
64722
66052
  `);
64723
66053
  renameSync12(temporary, file);
64724
66054
  }
@@ -64761,7 +66091,7 @@ function createStatusHook(seams = {}) {
64761
66091
  }
64762
66092
 
64763
66093
  // src/rafa.ts
64764
- var modules = await loadInvocationModules({ cwd: process.cwd(), home: homedir14() });
66094
+ var modules = await loadInvocationModules({ cwd: process.cwd(), home: homedir15() });
64765
66095
  var { exitCode } = await dispatch(process.argv.slice(2), {
64766
66096
  registry: CORE_REGISTRY,
64767
66097
  renderHelp,