@seanyao/roll 4.719.1 → 4.719.3

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.
Files changed (3) hide show
  1. package/CHANGELOG.md +10 -0
  2. package/dist/roll.mjs +393 -186
  3. package/package.json +1 -1
package/dist/roll.mjs CHANGED
@@ -7285,8 +7285,8 @@ function writeSyncBlock(original, block) {
7285
7285
  }
7286
7286
  }
7287
7287
  if (start === -1) {
7288
- const sep8 = text2.endsWith("\n\n") || text2 === "" ? "" : text2.endsWith("\n") ? "\n" : "\n\n";
7289
- return `${text2}${sep8}${block}
7288
+ const sep9 = text2.endsWith("\n\n") || text2 === "" ? "" : text2.endsWith("\n") ? "\n" : "\n\n";
7289
+ return `${text2}${sep9}${block}
7290
7290
  `;
7291
7291
  }
7292
7292
  let end = start + 1;
@@ -16195,7 +16195,7 @@ function parseFlowMap2(body) {
16195
16195
  }
16196
16196
  return out3;
16197
16197
  }
16198
- function splitTopLevel(s, sep8) {
16198
+ function splitTopLevel(s, sep9) {
16199
16199
  const parts = [];
16200
16200
  let depth = 0;
16201
16201
  let cur = "";
@@ -16204,7 +16204,7 @@ function splitTopLevel(s, sep8) {
16204
16204
  depth++;
16205
16205
  else if (ch === "}")
16206
16206
  depth = Math.max(0, depth - 1);
16207
- if (ch === sep8 && depth === 0) {
16207
+ if (ch === sep9 && depth === 0) {
16208
16208
  parts.push(cur);
16209
16209
  cur = "";
16210
16210
  } else {
@@ -31174,15 +31174,15 @@ var init_mcp = __esm({
31174
31174
  }
31175
31175
  }
31176
31176
  tryParseMessage() {
31177
- const sep8 = this.buffer.indexOf("\r\n\r\n");
31178
- if (sep8 < 0)
31177
+ const sep9 = this.buffer.indexOf("\r\n\r\n");
31178
+ if (sep9 < 0)
31179
31179
  return void 0;
31180
- const header = this.buffer.slice(0, sep8).toString("utf8");
31180
+ const header = this.buffer.slice(0, sep9).toString("utf8");
31181
31181
  const length = /^Content-Length:\s*(\d+)$/im.exec(header)?.[1];
31182
31182
  if (length === void 0)
31183
31183
  return void 0;
31184
31184
  const bodyLength = Number.parseInt(length, 10);
31185
- const start = sep8 + 4;
31185
+ const start = sep9 + 4;
31186
31186
  if (this.buffer.length < start + bodyLength)
31187
31187
  return void 0;
31188
31188
  const body = this.buffer.slice(start, start + bodyLength);
@@ -80311,11 +80311,11 @@ ${lanes.join("\n")}
80311
80311
  if (i < rootLength) {
80312
80312
  return void 0;
80313
80313
  }
80314
- const sep8 = directory.lastIndexOf(directorySeparator, i - 1);
80315
- if (sep8 === -1) {
80314
+ const sep9 = directory.lastIndexOf(directorySeparator, i - 1);
80315
+ if (sep9 === -1) {
80316
80316
  return void 0;
80317
80317
  }
80318
- return directory.substr(0, Math.max(sep8, rootLength));
80318
+ return directory.substr(0, Math.max(sep9, rootLength));
80319
80319
  }
80320
80320
  }
80321
80321
  }
@@ -244439,7 +244439,7 @@ __export(showcase_exports, {
244439
244439
  showcaseCommand: () => showcaseCommand
244440
244440
  });
244441
244441
  import { spawnSync as spawnSync17 } from "node:child_process";
244442
- import { cpSync as cpSync2, existsSync as existsSync140, mkdirSync as mkdirSync78, mkdtempSync as mkdtempSync8, readdirSync as readdirSync53, readFileSync as readFileSync141, rmSync as rmSync26, statSync as statSync40, writeFileSync as writeFileSync77 } from "node:fs";
244442
+ import { cpSync as cpSync2, existsSync as existsSync141, mkdirSync as mkdirSync78, mkdtempSync as mkdtempSync8, readdirSync as readdirSync54, readFileSync as readFileSync141, rmSync as rmSync27, statSync as statSync40, writeFileSync as writeFileSync77 } from "node:fs";
244443
244443
  import { tmpdir as tmpdir13 } from "node:os";
244444
244444
  import { dirname as dirname77, join as join164 } from "node:path";
244445
244445
  import { fileURLToPath as fileURLToPath3 } from "node:url";
@@ -244482,7 +244482,7 @@ function parseArgs3(args) {
244482
244482
  function packageRoot() {
244483
244483
  let dir = dirname77(fileURLToPath3(import.meta.url));
244484
244484
  for (let i = 0; i < 12; i++) {
244485
- if (existsSync140(join164(dir, "conventions")))
244485
+ if (existsSync141(join164(dir, "conventions")))
244486
244486
  return dir;
244487
244487
  const parent = dirname77(dir);
244488
244488
  if (parent === dir)
@@ -244493,7 +244493,7 @@ function packageRoot() {
244493
244493
  }
244494
244494
  function rollBin3() {
244495
244495
  const devPath = join164(packageRoot(), "packages", "cli", "bin", "roll.js");
244496
- if (existsSync140(devPath))
244496
+ if (existsSync141(devPath))
244497
244497
  return devPath;
244498
244498
  return "roll";
244499
244499
  }
@@ -244520,7 +244520,7 @@ function runRoll(sandbox, rollHome4, args, opts = {}) {
244520
244520
  }
244521
244521
  function fileNonEmpty3(p) {
244522
244522
  try {
244523
- return existsSync140(p) && statSync40(p).size > 0;
244523
+ return existsSync141(p) && statSync40(p).size > 0;
244524
244524
  } catch {
244525
244525
  return false;
244526
244526
  }
@@ -244532,7 +244532,7 @@ function makeSandbox(sourceProject) {
244532
244532
  mkdirSync78(sandbox, { recursive: true });
244533
244533
  mkdirSync78(rollHome4, { recursive: true });
244534
244534
  const srcRoll = join164(sourceProject, ".roll");
244535
- if (existsSync140(srcRoll)) {
244535
+ if (existsSync141(srcRoll)) {
244536
244536
  cpSync2(srcRoll, join164(sandbox, ".roll"), { recursive: true });
244537
244537
  } else {
244538
244538
  mkdirSync78(join164(sandbox, ".roll", "features"), { recursive: true });
@@ -244543,11 +244543,11 @@ function findCardSpec2(sandbox, card) {
244543
244543
  const featuresDir = join164(sandbox, ".roll", "features");
244544
244544
  const candidates = [];
244545
244545
  const walk2 = (dir, depth) => {
244546
- if (depth > 3 || !existsSync140(dir))
244546
+ if (depth > 3 || !existsSync141(dir))
244547
244547
  return;
244548
244548
  let entries = [];
244549
244549
  try {
244550
- entries = readdirSync53(dir);
244550
+ entries = readdirSync54(dir);
244551
244551
  } catch {
244552
244552
  return;
244553
244553
  }
@@ -244560,7 +244560,7 @@ function findCardSpec2(sandbox, card) {
244560
244560
  continue;
244561
244561
  }
244562
244562
  if (isDir4) {
244563
- if (name === card && existsSync140(join164(full, "spec.md")))
244563
+ if (name === card && existsSync141(join164(full, "spec.md")))
244564
244564
  candidates.push(join164(full, "spec.md"));
244565
244565
  walk2(full, depth + 1);
244566
244566
  }
@@ -244574,7 +244574,7 @@ function resetSandbox(sandbox, card) {
244574
244574
  let reset2 = false;
244575
244575
  let ok12 = false;
244576
244576
  const backlogPath = join164(sandbox, ".roll", "backlog.md");
244577
- if (existsSync140(backlogPath)) {
244577
+ if (existsSync141(backlogPath)) {
244578
244578
  const before = readFileSync141(backlogPath, "utf8");
244579
244579
  const cardRow = before.split("\n").find((line) => line.startsWith("|") && line.includes(card));
244580
244580
  const hadStatusToken = cardRow !== void 0 && statusMarkerRe(false).test(cardRow);
@@ -244603,8 +244603,8 @@ function resetSandbox(sandbox, card) {
244603
244603
  notes.push("backlog: .roll/backlog.md absent");
244604
244604
  }
244605
244605
  const pulseCmd = join164(sandbox, "packages", "cli", "src", "commands", "pulse.ts");
244606
- if (existsSync140(pulseCmd)) {
244607
- rmSync26(pulseCmd, { force: true });
244606
+ if (existsSync141(pulseCmd)) {
244607
+ rmSync27(pulseCmd, { force: true });
244608
244608
  notes.push("removed prior pulse.ts surface");
244609
244609
  }
244610
244610
  return { ok: ok12, reset: reset2, notes };
@@ -244636,7 +244636,7 @@ async function captureScreenshots(sandbox, card) {
244636
244636
  const web = await infra.captureScreenshot({
244637
244637
  kind: "web",
244638
244638
  out: webOut,
244639
- url: existsSync140(nowPage) ? `file://${nowPage}#now` : "about:blank"
244639
+ url: existsSync141(nowPage) ? `file://${nowPage}#now` : "about:blank"
244640
244640
  });
244641
244641
  shots.push({
244642
244642
  surface: "web",
@@ -244648,7 +244648,7 @@ async function captureScreenshots(sandbox, card) {
244648
244648
  }
244649
244649
  function readBacklogStatus(sandbox, card) {
244650
244650
  const backlogPath = join164(sandbox, ".roll", "backlog.md");
244651
- if (!existsSync140(backlogPath))
244651
+ if (!existsSync141(backlogPath))
244652
244652
  return void 0;
244653
244653
  for (const line of readFileSync141(backlogPath, "utf8").split("\n")) {
244654
244654
  if (line.startsWith("|") && line.includes(card)) {
@@ -244661,7 +244661,7 @@ function readBacklogStatus(sandbox, card) {
244661
244661
  }
244662
244662
  function readTruthLadder(sandbox, card) {
244663
244663
  const truthPath = join164(sandbox, ".roll", "features", "truth.json");
244664
- if (!existsSync140(truthPath))
244664
+ if (!existsSync141(truthPath))
244665
244665
  return void 0;
244666
244666
  try {
244667
244667
  const snap = JSON.parse(readFileSync141(truthPath, "utf8"));
@@ -244673,7 +244673,7 @@ function readTruthLadder(sandbox, card) {
244673
244673
  }
244674
244674
  function readTcrCommits(sandbox) {
244675
244675
  const runsPath3 = join164(sandbox, ".roll", "loop", "runs.jsonl");
244676
- if (!existsSync140(runsPath3))
244676
+ if (!existsSync141(runsPath3))
244677
244677
  return [];
244678
244678
  const out3 = [];
244679
244679
  try {
@@ -244816,7 +244816,7 @@ ROLL_HOME: ${rollHome4}
244816
244816
  return 1;
244817
244817
  } finally {
244818
244818
  if (!keepSandbox) {
244819
- rmSync26(dirname77(sandbox), { recursive: true, force: true });
244819
+ rmSync27(dirname77(sandbox), { recursive: true, force: true });
244820
244820
  }
244821
244821
  }
244822
244822
  }
@@ -259883,9 +259883,9 @@ function writeFeatureStub(issue, featuresDir, epic = "backlog-lifecycle") {
259883
259883
  if (existsSync47(path)) {
259884
259884
  const existing = readFileSync47(path, "utf8");
259885
259885
  const block = ac ? ac + "\n" : "";
259886
- const sep8 = existing.endsWith("\n") || existing === "" ? "" : "\n";
259886
+ const sep9 = existing.endsWith("\n") || existing === "" ? "" : "\n";
259887
259887
  if (block)
259888
- appendFileSync5(path, sep8 + block);
259888
+ appendFileSync5(path, sep9 + block);
259889
259889
  return path;
259890
259890
  }
259891
259891
  writeFileSync26(path, featureStubContent(issue));
@@ -262225,8 +262225,8 @@ function writeSet(runtimeDir11, set) {
262225
262225
  writeFileSync30(tmp, JSON.stringify([...set].sort(), null, 2), "utf8");
262226
262226
  writeFileSync30(path, readFileSync53(tmp, "utf8"), "utf8");
262227
262227
  try {
262228
- const { rmSync: rmSync30 } = __require("node:fs");
262229
- rmSync30(tmp, { force: true });
262228
+ const { rmSync: rmSync31 } = __require("node:fs");
262229
+ rmSync31(tmp, { force: true });
262230
262230
  } catch {
262231
262231
  }
262232
262232
  }
@@ -271420,8 +271420,8 @@ function topBar(input) {
271420
271420
  const switcher = multi ? `<div class="proj-switch" style="position:relative;flex:none;"><button type="button" id="proj-switch-btn" class="proj-switch-btn" aria-haspopup="menu" aria-expanded="false" style="display:flex;align-items:center;gap:9px;cursor:pointer;background:transparent;border:0;padding:5px 7px;border-radius:8px;">` + dot + projName + `<span class="proj-caret" style="${MONO}font-size:9px;color:#8f98ad;flex:none;transition:transform .18s;">\u25BE</span></button><div id="proj-menu" class="proj-menu" role="menu" aria-label="switch project \xB7 \u5207\u6362\u9879\u76EE" hidden style="position:absolute;top:42px;left:0;min-width:240px;background:rgba(27,34,56,.99);border:1px solid #313a55;border-radius:10px;padding:6px;box-shadow:0 10px 30px rgba(7,10,20,.5);z-index:40;"><div style="${MONO}font-size:9.5px;letter-spacing:.14em;text-transform:uppercase;color:#6f7892;padding:6px 13px 8px;">${esc4(input.brand.name)} \xB7 ${bi("this machine", "\u8FD9\u53F0\u673A\u5668")}</div>` + menuItems + `</div></div>` : `<a href="${homeHref}" class="proj-switch-btn" style="display:flex;align-items:center;gap:9px;cursor:pointer;flex:none;text-decoration:none;padding:5px 7px;">` + dot + projName + `</a>`;
271421
271421
  const crumbsNav = `<nav style="flex:1 1 auto;min-width:0;display:flex;align-items:center;gap:9px;${MONO}font-size:12px;color:#8f98ad;overflow:hidden;" aria-label="machine layer \xB7 \u673A\u5668\u5C42"><span style="${MONO}font-size:9.5px;letter-spacing:.14em;text-transform:uppercase;color:#5b6480;flex:none;">${bi("Machine", "\u673A\u5668")}</span>` + MACHINE_NAV.map((m7, i) => {
271422
271422
  const active = input.machinePage === m7.key;
271423
- const sep8 = i > 0 ? `<span style="color:#3a4360;flex:none;">\xB7</span>` : "";
271424
- return sep8 + `<a class="mach-link${active ? " on" : ""}" data-machine="${m7.key}" href="${esc4(m7.href)}"${active ? ' aria-current="page"' : ""} style="text-decoration:none;color:${active ? "#fff" : "#8f98ad"};font-weight:${active ? "600" : "400"};white-space:nowrap;flex:none;">${bi(m7.en, m7.zh)}</a>`;
271423
+ const sep9 = i > 0 ? `<span style="color:#3a4360;flex:none;">\xB7</span>` : "";
271424
+ return sep9 + `<a class="mach-link${active ? " on" : ""}" data-machine="${m7.key}" href="${esc4(m7.href)}"${active ? ' aria-current="page"' : ""} style="text-decoration:none;color:${active ? "#fff" : "#8f98ad"};font-weight:${active ? "600" : "400"};white-space:nowrap;flex:none;">${bi(m7.en, m7.zh)}</a>`;
271425
271425
  }).join("") + `</nav>`;
271426
271426
  const releaseTag = input.snapshot.release?.latestTag;
271427
271427
  const releaseBadge = releaseTag !== void 0 && releaseTag !== "" ? `<span style="${MONO}font-size:11px;color:#6f7892;letter-spacing:.02em;white-space:nowrap;">${bi("release", "\u53D1\u7248")} <b style="color:#cfd5e3;font-weight:600;">${esc4(releaseTag)}</b></span>` : "";
@@ -277700,11 +277700,11 @@ function printMergeSummary(summary) {
277700
277700
  function summaryAction(summary, file) {
277701
277701
  for (let index = summary.length - 1; index >= 0; index -= 1) {
277702
277702
  const entry = summary[index] ?? "";
277703
- const sep8 = entry.indexOf("|");
277704
- if (sep8 < 0)
277703
+ const sep9 = entry.indexOf("|");
277704
+ if (sep9 < 0)
277705
277705
  continue;
277706
- if (entry.slice(sep8 + 1) === file)
277707
- return entry.slice(0, sep8);
277706
+ if (entry.slice(sep9 + 1) === file)
277707
+ return entry.slice(0, sep9);
277708
277708
  }
277709
277709
  return null;
277710
277710
  }
@@ -278249,10 +278249,10 @@ function emitInitUi(projectDir, hasAgents, syncStatus, summary, shouldNudge, mod
278249
278249
  lines3.push(" " + c(iconColor, icon) + " " + c(iconColor, fLabel, { bold: true }));
278250
278250
  const nudgeMsg = shouldNudge ? renderDesignNudge(msgLang6())[0] ?? "" : "";
278251
278251
  const nudgePair = shouldNudge && nudgeMsg !== "" ? (() => {
278252
- const sep8 = nudgeMsg.indexOf(" \u2014 ");
278253
- if (sep8 < 0)
278252
+ const sep9 = nudgeMsg.indexOf(" \u2014 ");
278253
+ if (sep9 < 0)
278254
278254
  return [nudgeMsg, ""];
278255
- return [nudgeMsg.slice(0, sep8), nudgeMsg.slice(sep8 + 3)];
278255
+ return [nudgeMsg.slice(0, sep9), nudgeMsg.slice(sep9 + 3)];
278256
278256
  })() : void 0;
278257
278257
  const nextItems = nextItemsOverride ?? (hasAgents ? [
278258
278258
  ...nudgePair ? [nudgePair] : [],
@@ -281224,7 +281224,7 @@ async function loopExhaustionSplitCommand(argv, deps = realExhaustionSplitDeps()
281224
281224
  init_dist2();
281225
281225
  init_dist();
281226
281226
  init_dist3();
281227
- import { appendFileSync as appendFileSync21, existsSync as existsSync134, mkdirSync as mkdirSync74, readdirSync as readdirSync48, readFileSync as readFileSync135, writeFileSync as writeFileSync72 } from "node:fs";
281227
+ import { appendFileSync as appendFileSync21, existsSync as existsSync135, mkdirSync as mkdirSync74, readdirSync as readdirSync49, readFileSync as readFileSync135, writeFileSync as writeFileSync72 } from "node:fs";
281228
281228
  import { dirname as dirname74, join as join158 } from "node:path";
281229
281229
 
281230
281230
  // packages/cli/dist/runner/executor.js
@@ -288367,7 +288367,7 @@ function applyCorrectionCircuitBreaker(projectPath3, slug3, eventsPath4, alertsP
288367
288367
  init_dist2();
288368
288368
  init_dist3();
288369
288369
  import { execFileSync as execFileSync31 } from "node:child_process";
288370
- import { readFileSync as readFileSync133 } from "node:fs";
288370
+ import { readFileSync as readFileSync133, readdirSync as readdirSync48, realpathSync as realpathSync20 } from "node:fs";
288371
288371
  import { homedir as homedir33 } from "node:os";
288372
288372
  import { basename as basename22, dirname as dirname72, join as join156, relative as relative13, resolve as resolve13 } from "node:path";
288373
288373
  function git3(args, cwd) {
@@ -288437,6 +288437,60 @@ function extractCycleId(dirName) {
288437
288437
  const m7 = /^(cycle-\d{8}-\d{6}-\d+)$/.exec(dirName);
288438
288438
  return m7 ? m7[1] : void 0;
288439
288439
  }
288440
+ var ORPHAN_DELIVERED_OUTCOMES = /* @__PURE__ */ new Set(["delivered", "merged"]);
288441
+ function realpathSafe2(p) {
288442
+ try {
288443
+ return realpathSync20(p);
288444
+ } catch {
288445
+ return p;
288446
+ }
288447
+ }
288448
+ function defaultReadDirNames(dir) {
288449
+ try {
288450
+ return readdirSync48(dir, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => e.name);
288451
+ } catch {
288452
+ return [];
288453
+ }
288454
+ }
288455
+ function scanOrphanLoopWorktrees(repoRoot3, registeredPaths, cycles, deps) {
288456
+ const worktreesDir = join156(repoRoot3, ".roll", "loop", "worktrees");
288457
+ const names = deps.readDir ? deps.readDir(worktreesDir) : defaultReadDirNames(worktreesDir);
288458
+ const out3 = [];
288459
+ for (const name of [...names].sort()) {
288460
+ const absPath = resolve13(join156(worktreesDir, name));
288461
+ if (registeredPaths.has(realpathSafe2(absPath)))
288462
+ continue;
288463
+ const cycleId = extractCycleId(name);
288464
+ const ce = cycleId ? cycles.get(cycleId) : void 0;
288465
+ const outcome = ce?.outcome;
288466
+ const active = isActiveCycle(cycleId, repoRoot3, deps);
288467
+ const rec = {
288468
+ path: absPath,
288469
+ owner: "loop",
288470
+ ...cycleId ? { cycleId } : {},
288471
+ ...ce?.storyId ? { storyId: ce.storyId } : {},
288472
+ ...outcome ? { outcome } : {},
288473
+ dirtyTracked: "unknown",
288474
+ // no git metadata for a deregistered dir
288475
+ dirtyUntracked: "unknown",
288476
+ ahead: null,
288477
+ mergeEvidence: { kind: "none" },
288478
+ active,
288479
+ disposition: "preserved_orphan",
288480
+ reason: ""
288481
+ };
288482
+ if (active) {
288483
+ rec.reason = "orphan loop dir with an active cycle lock; never reclaimed";
288484
+ } else if (outcome && ORPHAN_DELIVERED_OUTCOMES.has(outcome)) {
288485
+ rec.disposition = "orphan_reclaimable";
288486
+ rec.reason = `orphan loop dir (deregistered from git); owning cycle outcome '${outcome}' is delivered \u2014 bounded reclaim`;
288487
+ } else {
288488
+ rec.reason = `orphan loop dir (deregistered from git); delivery not provable (cycle outcome '${outcome ?? "unknown"}') \u2014 preserved, reclaim manually after review`;
288489
+ }
288490
+ out3.push(rec);
288491
+ }
288492
+ return out3;
288493
+ }
288440
288494
  function detectDirty(wtPath, deps) {
288441
288495
  try {
288442
288496
  const g = deps.git ?? git3;
@@ -288637,6 +288691,10 @@ function auditWorktrees(deps) {
288637
288691
  baseRec.reason = disp.reason;
288638
288692
  records.push(baseRec);
288639
288693
  }
288694
+ const registeredLoopPaths = new Set(records.map((r) => realpathSafe2(resolve13(r.path))));
288695
+ for (const orphan of scanOrphanLoopWorktrees(repoRoot3, registeredLoopPaths, cycles, deps)) {
288696
+ records.push(orphan);
288697
+ }
288640
288698
  let ephemeralBranches = [];
288641
288699
  try {
288642
288700
  const g = deps.git ?? git3;
@@ -288695,7 +288753,9 @@ function renderHuman(output) {
288695
288753
  "preserved_unpublished",
288696
288754
  "preserved_dirty_no_tcr",
288697
288755
  "preserved_needs_review",
288756
+ "preserved_orphan",
288698
288757
  "disposable_candidate",
288758
+ "orphan_reclaimable",
288699
288759
  "external_unmanaged"
288700
288760
  ];
288701
288761
  for (const disp of order) {
@@ -288767,12 +288827,24 @@ function worktreeAuditCommand(args, deps) {
288767
288827
  init_dist2();
288768
288828
  import { execFileSync as execFileSync32 } from "node:child_process";
288769
288829
  import { homedir as homedir34 } from "node:os";
288770
- import { appendFileSync as appendFileSync20, mkdirSync as mkdirSync73 } from "node:fs";
288771
- import { dirname as dirname73, join as join157, relative as relative14, resolve as resolve14 } from "node:path";
288772
- var MERGED_KINDS = /* @__PURE__ */ new Set(["ancestor", "pr_merged", "patch_equivalent"]);
288830
+ import { appendFileSync as appendFileSync20, existsSync as existsSync134, mkdirSync as mkdirSync73, rmSync as rmSync25 } from "node:fs";
288831
+ import { dirname as dirname73, join as join157, relative as relative14, resolve as resolve14, sep as sep8 } from "node:path";
288832
+ var MERGED_KINDS = /* @__PURE__ */ new Set(["ancestor", "patch_equivalent"]);
288773
288833
  function isSafelyDisposable(rec) {
288774
288834
  return rec.owner === "loop" && rec.active === false && rec.dirtyTracked === false && rec.disposition === "disposable_candidate" && MERGED_KINDS.has(rec.mergeEvidence.kind) && typeof rec.head === "string" && rec.head.length > 0;
288775
288835
  }
288836
+ function isReclaimableOrphan(rec) {
288837
+ return rec.owner === "loop" && rec.active === false && rec.disposition === "orphan_reclaimable";
288838
+ }
288839
+ function isBoundedLoopWorktreeDir(repoRoot3, path) {
288840
+ const base = resolve14(join157(repoRoot3, ".roll", "loop", "worktrees"));
288841
+ const abs = resolve14(path);
288842
+ if (abs === base)
288843
+ return false;
288844
+ if (!abs.startsWith(base + sep8))
288845
+ return false;
288846
+ return dirname73(abs) === base;
288847
+ }
288776
288848
  var PROTECTED_BRANCHES = /* @__PURE__ */ new Set(["main", "master", "HEAD"]);
288777
288849
  function resolveStandaloneMergedBranches(audit, deps) {
288778
288850
  const out3 = [];
@@ -288797,20 +288869,25 @@ function planWorktreeCleanup(audit, threshold, standaloneMergedBranches = []) {
288797
288869
  const loopWorktrees = audit.records.filter((r) => r.owner === "loop");
288798
288870
  const canaryTotal = audit.ephemeralBranches.length + loopWorktrees.length;
288799
288871
  const excess = canaryTotal - threshold;
288800
- const pool = audit.records.filter(isSafelyDisposable).sort((a, b) => a.path.localeCompare(b.path));
288872
+ const pool = audit.records.filter((r) => isSafelyDisposable(r) || isReclaimableOrphan(r)).sort((a, b) => a.path.localeCompare(b.path));
288801
288873
  const branchPool = [...standaloneMergedBranches].sort((a, b) => a.branch.localeCompare(b.branch));
288802
288874
  const excessN = excess > 0 ? excess : 0;
288803
288875
  const chosen = pool.slice(0, Math.min(excessN, pool.length));
288804
288876
  const remainingExcess = excessN - chosen.length;
288805
288877
  const chosenBranches = branchPool.slice(0, Math.min(remainingExcess, branchPool.length));
288806
288878
  const chosenPaths = new Set(chosen.map((r) => r.path));
288807
- const candidates = chosen.map((r) => ({
288808
- path: r.path,
288809
- ...r.cycleId ? { cycleId: r.cycleId } : {},
288810
- ...r.branch ? { branch: r.branch } : {},
288811
- expectedHead: r.head,
288812
- reason: "disposable_candidate"
288813
- }));
288879
+ const candidates = chosen.map((r) => {
288880
+ const orphan = isReclaimableOrphan(r);
288881
+ return {
288882
+ path: r.path,
288883
+ ...r.cycleId ? { cycleId: r.cycleId } : {},
288884
+ ...r.branch ? { branch: r.branch } : {},
288885
+ // An orphan has no registered HEAD; apply skips the head check for rm_dir.
288886
+ expectedHead: orphan ? "" : r.head,
288887
+ reason: orphan ? "orphan_reclaimable" : "disposable_candidate",
288888
+ reclaim: orphan ? "rm_dir" : "git_worktree"
288889
+ };
288890
+ });
288814
288891
  const preserved = audit.records.filter((r) => !chosenPaths.has(r.path)).map((r) => ({ path: r.path, disposition: r.disposition, reason: r.reason }));
288815
288892
  const countedWorktrees = loopWorktrees.map((r) => ({
288816
288893
  path: r.path,
@@ -288841,13 +288918,47 @@ function defaultRemoveBranch(repositoryRoot, branch) {
288841
288918
  return { ok: true, detail: "" };
288842
288919
  }
288843
288920
  function defaultRemoveWorktree(repositoryRoot, path) {
288921
+ let gitErr = "";
288844
288922
  try {
288845
288923
  execFileSync32("git", ["-C", repositoryRoot, "worktree", "remove", "--force", path], {
288846
288924
  encoding: "utf8",
288847
288925
  stdio: ["ignore", "pipe", "pipe"]
288848
288926
  });
288849
288927
  } catch (err16) {
288850
- return { ok: false, detail: err16 instanceof Error ? err16.message : String(err16) };
288928
+ gitErr = err16 instanceof Error ? err16.message : String(err16);
288929
+ }
288930
+ if (existsSync134(path)) {
288931
+ if (!isBoundedLoopWorktreeDir(repositoryRoot, path)) {
288932
+ return { ok: false, detail: gitErr || `refused: ${path} is outside .roll/loop/worktrees` };
288933
+ }
288934
+ try {
288935
+ rmSync25(path, { recursive: true, force: true });
288936
+ } catch (e) {
288937
+ return { ok: false, detail: gitErr || (e instanceof Error ? e.message : String(e)) };
288938
+ }
288939
+ } else if (gitErr) {
288940
+ return { ok: false, detail: gitErr };
288941
+ }
288942
+ try {
288943
+ execFileSync32("git", ["-C", repositoryRoot, "worktree", "prune", "--expire", "now"], {
288944
+ encoding: "utf8",
288945
+ stdio: ["ignore", "pipe", "pipe"]
288946
+ });
288947
+ } catch {
288948
+ }
288949
+ return { ok: true, detail: "" };
288950
+ }
288951
+ function defaultReclaimOrphanDir(repositoryRoot, path) {
288952
+ if (!isBoundedLoopWorktreeDir(repositoryRoot, path)) {
288953
+ return { ok: false, detail: `refused: ${path} is outside .roll/loop/worktrees` };
288954
+ }
288955
+ if (!existsSync134(path)) {
288956
+ return { ok: false, detail: "missing: orphan dir already gone" };
288957
+ }
288958
+ try {
288959
+ rmSync25(path, { recursive: true, force: true });
288960
+ } catch (e) {
288961
+ return { ok: false, detail: e instanceof Error ? e.message : String(e) };
288851
288962
  }
288852
288963
  try {
288853
288964
  execFileSync32("git", ["-C", repositoryRoot, "worktree", "prune", "--expire", "now"], {
@@ -288862,6 +288973,7 @@ async function applyWorktreeCleanup(plan, options) {
288862
288973
  const repositoryRoot = resolve14(options.repositoryRoot);
288863
288974
  const auditFn = options.audit ?? (() => auditWorktrees({ repoRoot: repositoryRoot, home: homedir34() }));
288864
288975
  const removeFn = options.removeWorktree ?? defaultRemoveWorktree;
288976
+ const reclaimOrphanFn = options.reclaimOrphanDir ?? defaultReclaimOrphanDir;
288865
288977
  const removeBranchFn = options.removeBranch ?? defaultRemoveBranch;
288866
288978
  const emit4 = options.emit ?? (() => {
288867
288979
  });
@@ -288877,17 +288989,47 @@ async function applyWorktreeCleanup(plan, options) {
288877
288989
  emit4({ type: "worktree_cleanup_refused", path: candidate.path, reason, ts: nowMs2() });
288878
288990
  };
288879
288991
  if (!rec) {
288880
- refuse("missing: worktree no longer registered (already removed or pruned)");
288881
- continue;
288882
- }
288883
- if (rec.head !== candidate.expectedHead) {
288884
- refuse(`changed-head: expected ${candidate.expectedHead}, found ${rec.head ?? "none"}`);
288992
+ refuse(candidate.reclaim === "rm_dir" ? "missing: orphan dir no longer present (already reclaimed)" : "missing: worktree no longer registered (already removed or pruned)");
288885
288993
  continue;
288886
288994
  }
288887
288995
  if (rec.active) {
288888
288996
  refuse("active: worktree activated concurrently (fresh lock/heartbeat)");
288889
288997
  continue;
288890
288998
  }
288999
+ if (candidate.reclaim === "rm_dir") {
289000
+ if (!isReclaimableOrphan(rec)) {
289001
+ refuse(`disposition: fresh audit reports '${rec.disposition}' (orphan no longer provably delivered)`);
289002
+ continue;
289003
+ }
289004
+ const removal2 = {
289005
+ path: rec.path,
289006
+ expectedHead: "",
289007
+ reclaim: "rm_dir",
289008
+ ...rec.cycleId ? { cycleId: rec.cycleId } : {}
289009
+ };
289010
+ if (options.dryRun) {
289011
+ removed.push(removal2);
289012
+ continue;
289013
+ }
289014
+ const result3 = reclaimOrphanFn(repositoryRoot, rec.path);
289015
+ if (!result3.ok) {
289016
+ refuse(`reclaim-failed: ${result3.detail}`);
289017
+ continue;
289018
+ }
289019
+ removed.push(removal2);
289020
+ emit4({
289021
+ type: "worktree_cleanup_applied",
289022
+ path: rec.path,
289023
+ expectedHead: "",
289024
+ ...rec.cycleId ? { cycleId: rec.cycleId } : {},
289025
+ ts: nowMs2()
289026
+ });
289027
+ continue;
289028
+ }
289029
+ if (rec.head !== candidate.expectedHead) {
289030
+ refuse(`changed-head: expected ${candidate.expectedHead}, found ${rec.head ?? "none"}`);
289031
+ continue;
289032
+ }
288891
289033
  if (rec.dirtyTracked === true) {
288892
289034
  refuse("dirty: tracked changes appeared after planning");
288893
289035
  continue;
@@ -288903,6 +289045,7 @@ async function applyWorktreeCleanup(plan, options) {
288903
289045
  const removal = {
288904
289046
  path: rec.path,
288905
289047
  expectedHead: candidate.expectedHead,
289048
+ reclaim: "git_worktree",
288906
289049
  ...rec.branch ? { branch: rec.branch } : {},
288907
289050
  ...rec.cycleId ? { cycleId: rec.cycleId } : {}
288908
289051
  };
@@ -289058,12 +289201,22 @@ function renderPlanHuman(plan, mode) {
289058
289201
  lines3.push(`minimal candidate set (${plan.canaryTotal} \u2192 ${plan.projectedTotal})`);
289059
289202
  for (const c2 of plan.candidates) {
289060
289203
  const tags = [c2.branch, c2.cycleId].filter(Boolean).join(" ");
289061
- lines3.push(` worktree ${rel(c2.path)}${tags ? " " + tags : ""} [disposable_candidate]`);
289204
+ const label5 = c2.reclaim === "rm_dir" ? "orphan_reclaimable \xB7 bounded rm" : "disposable_candidate";
289205
+ const kind = c2.reclaim === "rm_dir" ? "orphan " : "worktree";
289206
+ lines3.push(` ${kind} ${rel(c2.path)}${tags ? " " + tags : ""} [${label5}]`);
289062
289207
  }
289063
289208
  for (const b of plan.branchCandidates) {
289064
289209
  lines3.push(` branch ${b.branch} ${b.expectedSha.slice(0, 9)} [merged: ${b.mergeKind}]`);
289065
289210
  }
289066
289211
  lines3.push("");
289212
+ const preservedOrphans = plan.preserved.filter((p) => p.disposition === "preserved_orphan");
289213
+ if (preservedOrphans.length > 0) {
289214
+ lines3.push(`preserved orphan dirs (${preservedOrphans.length}) \u2014 visible + counted, never auto-deleted`);
289215
+ for (const p of preservedOrphans)
289216
+ lines3.push(` ${rel(p.path)} \u2014 ${p.reason}`);
289217
+ lines3.push(" Reclaim one after review with: roll worktree cleanup --reclaim-orphan <path>");
289218
+ lines3.push("");
289219
+ }
289067
289220
  if (mode === "dry-run") {
289068
289221
  lines3.push("Dry run \u2014 no git state changed.");
289069
289222
  lines3.push("Apply the audited set with: roll worktree cleanup --apply");
@@ -289104,7 +289257,7 @@ function renderResultHuman(result2) {
289104
289257
  }
289105
289258
  return lines3.join("\n").trimEnd() + "\n";
289106
289259
  }
289107
- var CLEANUP_USAGE = "Usage: roll worktree cleanup [--dry-run | --apply] [--json] [--repo <path>]\n Safely recover from branch/worktree canary pressure using the worktree\n audit as the SOLE authority. Removes ONLY inactive, merged, clean\n `disposable_candidate` loop worktrees, plus (FIX-1454) standalone ephemeral\n branches that are verifiably merged (ancestor of the integration branch or a\n merged GitHub PR) and attached to no worktree \u2014 never a path/ref that is\n merely old or counted, and never a preserved (unpublished / dirty / active /\n external / current / protected / unmerged) one.\n\n Always dry-run first. Default (no flag) is --dry-run.\n --dry-run print counted refs/dirs, audit dispositions, and the minimal\n candidate set to clear pressure. Never mutates git state.\n --apply re-run the audit before EVERY removal; remove only revalidated\n candidates via git, prune registration, emit events. A changed\n head / new dirt / missing path / concurrent activation fails\n closed (no substitution). Then resume explicitly: roll loop resume\n --json emit the schema-1 plan (dry-run) or result (apply) as JSON\n --repo override the project root (default: current directory)\n\n \u5B89\u5168\u6E05\u7406:\u4EC5\u79FB\u9664\u5BA1\u8BA1\u5224\u5B9A\u4E3A\u5DF2\u5408\u5E76\u3001\u5E72\u51C0\u3001\u975E\u6D3B\u8DC3\u7684 disposable_candidate;\n \u5148\u8DD1 --dry-run,\u518D --apply,\u6700\u540E\u624B\u52A8 roll loop resume\u3002";
289260
+ var CLEANUP_USAGE = "Usage: roll worktree cleanup [--dry-run | --apply] [--json] [--repo <path>]\n Safely recover from branch/worktree canary pressure using the worktree\n audit as the SOLE authority. Removes ONLY inactive, merged, clean\n `disposable_candidate` loop worktrees, plus (FIX-1454) standalone ephemeral\n branches that are verifiably merged (ancestor of the integration branch or a\n merged GitHub PR) and attached to no worktree \u2014 never a path/ref that is\n merely old or counted, and never a preserved (unpublished / dirty / active /\n external / current / protected / unmerged) one.\n\n Always dry-run first. Default (no flag) is --dry-run.\n --dry-run print counted refs/dirs, audit dispositions, and the minimal\n candidate set to clear pressure. Never mutates git state.\n --apply re-run the audit before EVERY removal; remove only revalidated\n candidates via git, prune registration, emit events. A changed\n head / new dirt / missing path / concurrent activation fails\n closed (no substitution). Then resume explicitly: roll loop resume\n --json emit the schema-1 plan (dry-run) or result (apply) as JSON\n --repo override the project root (default: current directory)\n --reclaim-orphan <path> (FIX-1460) bounded-rm ONE named orphan loop dir\n (deregistered from git; delivery not auto-provable) after you\n review it. Fails closed unless it is an inactive loop orphan\n inside .roll/loop/worktrees. Auto-reclaim of provably-delivered\n orphans happens under --apply.\n\n \u5B89\u5168\u6E05\u7406:\u4EC5\u79FB\u9664\u5BA1\u8BA1\u5224\u5B9A\u4E3A\u5DF2\u5408\u5E76\u3001\u5E72\u51C0\u3001\u975E\u6D3B\u8DC3\u7684 disposable_candidate;\n \u5148\u8DD1 --dry-run,\u518D --apply,\u6700\u540E\u624B\u52A8 roll loop resume\u3002";
289108
289261
  function resolveThreshold() {
289109
289262
  const parsed = parseInt(process.env["ROLL_BRANCH_CANARY_MAX"] ?? "", 10);
289110
289263
  return Number.isFinite(parsed) && parsed > 0 ? parsed : DEFAULT_BRANCH_CANARY_MAX;
@@ -289112,6 +289265,21 @@ function resolveThreshold() {
289112
289265
  function gitCap(repoRoot3, args) {
289113
289266
  return execFileSync32("git", ["-C", repoRoot3, ...args], { encoding: "utf8", stdio: ["ignore", "pipe", "pipe"] });
289114
289267
  }
289268
+ function classifyBranchMerge(branch, integrationBranch, git5) {
289269
+ if (git5(["merge-base", "--is-ancestor", branch, integrationBranch]).ok)
289270
+ return "ancestor";
289271
+ const cherry = git5(["cherry", integrationBranch, branch]);
289272
+ if (!cherry.ok)
289273
+ return null;
289274
+ const lines3 = cherry.stdout.split("\n").map((l) => l.trim()).filter((l) => l !== "");
289275
+ if (lines3.length === 0)
289276
+ return null;
289277
+ if (lines3.some((l) => l.startsWith("+")))
289278
+ return null;
289279
+ if (lines3.every((l) => l.startsWith("-")))
289280
+ return "patch_equivalent";
289281
+ return null;
289282
+ }
289115
289283
  function buildStandaloneBranchDeps(repoRoot3, audit, integrationBranch) {
289116
289284
  const attachedBranches = new Set(audit.records.filter((r) => r.owner === "loop" && typeof r.branch === "string" && r.branch !== "").map((r) => r.branch.replace(/^refs\/heads\//, "")));
289117
289285
  let currentBranch2 = null;
@@ -289130,27 +289298,19 @@ function buildStandaloneBranchDeps(repoRoot3, audit, integrationBranch) {
289130
289298
  return null;
289131
289299
  }
289132
289300
  },
289133
- branchMerge: (branch) => {
289134
- try {
289135
- execFileSync32("git", ["-C", repoRoot3, "merge-base", "--is-ancestor", branch, integrationBranch], { stdio: "ignore" });
289136
- return "ancestor";
289137
- } catch {
289138
- }
289301
+ // FIX-1458 (#1465): delivery is proven ONLY by fresh git patch evidence via
289302
+ // classifyBranchMerge (ancestor OR `git cherry` patch-equivalence). A merged
289303
+ // GitHub PR is deliberately NOT consulted: a squash merge leaves the exact
289304
+ // branch tips undelivered, so authorizing deletion on a merged PR alone
289305
+ // silently discards unique commits (US-ORG-003/007/004 in the report).
289306
+ branchMerge: (branch) => classifyBranchMerge(branch, integrationBranch, (args) => {
289139
289307
  try {
289140
- for (const line of gitCap(repoRoot3, ["branch", "--merged", integrationBranch]).split("\n")) {
289141
- if (line.replace(/^[*+]?\s+/, "").trim() === branch)
289142
- return "pr_merged";
289143
- }
289144
- } catch {
289145
- }
289146
- try {
289147
- const n = execFileSync32("gh", ["pr", "list", "--head", branch, "--state", "merged", "--json", "number", "--jq", "length"], { cwd: repoRoot3, encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] }).trim();
289148
- if (parseInt(n, 10) > 0)
289149
- return "pr_merged";
289150
- } catch {
289308
+ return { ok: true, stdout: gitCap(repoRoot3, args) };
289309
+ } catch (err16) {
289310
+ const stdout = typeof err16.stdout === "string" ? err16.stdout : "";
289311
+ return { ok: false, stdout };
289151
289312
  }
289152
- return null;
289153
- }
289313
+ })
289154
289314
  };
289155
289315
  }
289156
289316
  function resolveIntegrationForCleanup(repoRoot3) {
@@ -289182,10 +289342,57 @@ async function worktreeCleanupCommand(args, deps) {
289182
289342
  home: deps?.home ?? homedir34(),
289183
289343
  git: deps?.git,
289184
289344
  readFile: deps?.readFile,
289345
+ readDir: deps?.readDir,
289185
289346
  nowISO: deps?.nowISO,
289186
289347
  nowSec: deps?.nowSec,
289187
289348
  integrationBranch: deps?.integrationBranch
289188
289349
  };
289350
+ const reclaimIdx = args.indexOf("--reclaim-orphan");
289351
+ if (reclaimIdx >= 0) {
289352
+ const named2 = args[reclaimIdx + 1];
289353
+ if (!named2) {
289354
+ process.stderr.write("roll worktree cleanup: --reclaim-orphan requires a <path>.\n");
289355
+ return 2;
289356
+ }
289357
+ const fresh = auditWorktrees(fullDeps);
289358
+ const target = resolve14(named2);
289359
+ const rec = fresh.records.find((r2) => resolve14(r2.path) === target);
289360
+ if (!rec) {
289361
+ process.stderr.write(`refused: ${named2} is not in the worktree audit.
289362
+ `);
289363
+ return 2;
289364
+ }
289365
+ if (rec.owner !== "loop") {
289366
+ process.stderr.write(`refused: ${named2} is not a loop worktree (owner=${rec.owner}).
289367
+ `);
289368
+ return 2;
289369
+ }
289370
+ if (rec.active) {
289371
+ process.stderr.write(`refused: ${named2} has an active cycle lock.
289372
+ `);
289373
+ return 2;
289374
+ }
289375
+ if (rec.disposition !== "orphan_reclaimable" && rec.disposition !== "preserved_orphan") {
289376
+ process.stderr.write(`refused: ${named2} audits as '${rec.disposition}', not an orphan dir \u2014 use --apply for registered worktrees.
289377
+ `);
289378
+ return 2;
289379
+ }
289380
+ if (!isBoundedLoopWorktreeDir(repoRoot3, rec.path)) {
289381
+ process.stderr.write(`refused: ${named2} is outside .roll/loop/worktrees.
289382
+ `);
289383
+ return 2;
289384
+ }
289385
+ const reclaimFn = deps?.reclaimOrphanDir ?? defaultReclaimOrphanDir;
289386
+ const r = reclaimFn(repoRoot3, rec.path);
289387
+ if (!r.ok) {
289388
+ process.stderr.write(`reclaim-failed: ${r.detail}
289389
+ `);
289390
+ return 1;
289391
+ }
289392
+ process.stdout.write(`reclaimed orphan dir: ${rec.path}
289393
+ `);
289394
+ return 0;
289395
+ }
289189
289396
  const threshold = resolveThreshold();
289190
289397
  const integrationBranch = fullDeps.integrationBranch ?? resolveIntegrationForCleanup(repoRoot3);
289191
289398
  const auditNow = auditWorktrees(fullDeps);
@@ -289323,13 +289530,13 @@ function announceReport(projectPath3, slug3, storyId, opener = (p) => {
289323
289530
  const latest = join158(cardArchiveDir(projectPath3, storyId), "latest");
289324
289531
  const review = join158(latest, reviewFileName(storyId));
289325
289532
  const legacyReport = join158(latest, reportFileName(storyId));
289326
- const report = existsSync134(review) ? review : existsSync134(legacyReport) ? legacyReport : null;
289533
+ const report = existsSync135(review) ? review : existsSync135(legacyReport) ? legacyReport : null;
289327
289534
  if (report === null)
289328
289535
  return null;
289329
289536
  const label5 = currentLang() === "zh" ? "\u9A8C\u6536 Review Page" : "Acceptance Review Page";
289330
289537
  process.stdout.write(`${label5}: ${report}
289331
289538
  `);
289332
- const muted = existsSync134(join158(projectPath3, ".roll", "loop", `mute-${slug3}`)) || existsSync134(join158(process.env["ROLL_SHARED_ROOT"] || join158(process.env["HOME"] ?? "", ".shared", "roll"), "loop", `mute-${slug3}`));
289539
+ const muted = existsSync135(join158(projectPath3, ".roll", "loop", `mute-${slug3}`)) || existsSync135(join158(process.env["ROLL_SHARED_ROOT"] || join158(process.env["HOME"] ?? "", ".shared", "roll"), "loop", `mute-${slug3}`));
289333
289540
  if (!muted)
289334
289541
  opener(report);
289335
289542
  return report;
@@ -289353,7 +289560,7 @@ function cycleSignalTeardown(paths, cycleId, branch, sig, deps = {}) {
289353
289560
  }
289354
289561
  let owned = false;
289355
289562
  try {
289356
- owned = existsSync134(paths.lockPath) && readLockOwner(paths.lockPath)?.pid === pid;
289563
+ owned = existsSync135(paths.lockPath) && readLockOwner(paths.lockPath)?.pid === pid;
289357
289564
  } catch {
289358
289565
  owned = false;
289359
289566
  }
@@ -289498,7 +289705,7 @@ function incrementConsecutiveFails(projectPath3, slug3, alertsPath, eventsPath4,
289498
289705
  const counterFile = join158(rt, "consecutive-fails");
289499
289706
  let count = 0;
289500
289707
  try {
289501
- if (existsSync134(counterFile)) {
289708
+ if (existsSync135(counterFile)) {
289502
289709
  count = parseInt(readFileSync135(counterFile, "utf8").trim(), 10) || 0;
289503
289710
  }
289504
289711
  } catch {
@@ -289512,7 +289719,7 @@ function incrementConsecutiveFails(projectPath3, slug3, alertsPath, eventsPath4,
289512
289719
  if (count < threshold)
289513
289720
  return;
289514
289721
  const pauseMarker = join158(projectPath3, ".roll", "loop", `PAUSE-${slug3}`);
289515
- if (existsSync134(pauseMarker))
289722
+ if (existsSync135(pauseMarker))
289516
289723
  return;
289517
289724
  const alertMsg = `# ALERT \u2014 loop auto-paused after ${count} consecutive failures
289518
289725
 
@@ -289548,7 +289755,7 @@ loop run-once: \u8FDE\u7EED ${count} \u6B21\u5931\u8D25\u540E\u81EA\u52A8\u6682\
289548
289755
  }
289549
289756
  function writeRootCausePause(projectPath3, slug3, alertsPath, eventsPath4, cycleId, attribution, count, snapshotPath2) {
289550
289757
  const pauseMarker = join158(projectPath3, ".roll", "loop", `PAUSE-${slug3}`);
289551
- if (existsSync134(pauseMarker))
289758
+ if (existsSync135(pauseMarker))
289552
289759
  return;
289553
289760
  const playbook = playbookForFailure(attribution.failureClass, attribution.rootCauseKey);
289554
289761
  const alertMsg = `# ALERT \u2014 loop auto-paused on ${attribution.failureClass} failure
@@ -289610,7 +289817,7 @@ function handleNonCardFailure(projectPath3, slug3, alertsPath, eventsPath4, runt
289610
289817
  function readFailurePauseThreshold(projectPath3) {
289611
289818
  try {
289612
289819
  const policy = join158(projectPath3, ".roll", "policy.yaml");
289613
- if (!existsSync134(policy))
289820
+ if (!existsSync135(policy))
289614
289821
  return PAUSE_THRESHOLD;
289615
289822
  return parsePolicy(readFileSync135(policy, "utf8")).loopSafety.maxConsecutiveFailures;
289616
289823
  } catch {
@@ -289640,7 +289847,7 @@ function incrementConsecutiveIdle(projectPath3, slug3) {
289640
289847
  const file = idleCounterPath(projectPath3, slug3);
289641
289848
  let count = 0;
289642
289849
  try {
289643
- if (existsSync134(file)) {
289850
+ if (existsSync135(file)) {
289644
289851
  count = parseInt(readFileSync135(file, "utf8").trim(), 10) || 0;
289645
289852
  }
289646
289853
  } catch {
@@ -289710,7 +289917,7 @@ function readExternalBlock(eventsPath4, cycleId) {
289710
289917
  const authDetails = [];
289711
289918
  const networkDetails = [];
289712
289919
  try {
289713
- if (!existsSync134(eventsPath4))
289920
+ if (!existsSync135(eventsPath4))
289714
289921
  return null;
289715
289922
  for (const line of readFileSync135(eventsPath4, "utf8").split("\n")) {
289716
289923
  if (line.trim() === "" || !line.includes("agent:blocked"))
@@ -289775,7 +289982,7 @@ function writeReviewerBlockedAlert(projectPath3, slug3, alertsPath, eventsPath4,
289775
289982
  }
289776
289983
  if (block.cause === "auth") {
289777
289984
  const pauseMarker = join158(projectPath3, ".roll", "loop", `PAUSE-${slug3}`);
289778
- if (!existsSync134(pauseMarker)) {
289985
+ if (!existsSync135(pauseMarker)) {
289779
289986
  try {
289780
289987
  writeFileSync72(pauseMarker, msg7, "utf8");
289781
289988
  bus.appendEvent(eventsPath4, { type: "policy:safety_pause", loop: "ci", reason: `agent auth block: ${agents}`, ts: ts2 });
@@ -289803,7 +290010,7 @@ function buildLoopRouteDeps(projectPath3) {
289803
290010
  }
289804
290011
  return readRouteSlot(text2, slot);
289805
290012
  } catch {
289806
- if (existsSync134(agentsYaml)) {
290013
+ if (existsSync135(agentsYaml)) {
289807
290014
  throw new Error("legacy agent configuration is unsupported; run `roll agent migrate`");
289808
290015
  }
289809
290016
  return void 0;
@@ -290081,7 +290288,7 @@ loop run-once: \u8BC4\u5BA1\u5224\u5B9A ${resizeStory} \u8303\u56F4\u8FC7\u5927(
290081
290288
  slug: id.slug,
290082
290289
  count: idleCount,
290083
290290
  resolveState: () => resolveLoopRunState(id.path, id.slug),
290084
- readBacklog: () => existsSync134(backlogFile) ? readFileSync135(backlogFile, "utf8") : "",
290291
+ readBacklog: () => existsSync135(backlogFile) ? readFileSync135(backlogFile, "utf8") : "",
290085
290292
  scheduler: createScheduler(process.platform, { uid: process.getuid?.() ?? 0 }),
290086
290293
  loopLabel: launchdLabel("loop", id.slug),
290087
290294
  now: () => (/* @__PURE__ */ new Date()).toISOString(),
@@ -290270,7 +290477,7 @@ async function isOffline(resolve17 = (h) => lookup2(h)) {
290270
290477
  }
290271
290478
  }
290272
290479
  function isLoopPaused(projectPath3, slug3) {
290273
- return existsSync134(join158(projectPath3, ".roll", "loop", `PAUSE-${slug3}`));
290480
+ return existsSync135(join158(projectPath3, ".roll", "loop", `PAUSE-${slug3}`));
290274
290481
  }
290275
290482
  function branchCanaryTrips(projectPath3, slug3, rt, alertsPath) {
290276
290483
  let ephemeralBranchCount = 0;
@@ -290281,7 +290488,7 @@ function branchCanaryTrips(projectPath3, slug3, rt, alertsPath) {
290281
290488
  }
290282
290489
  let worktreeCount = 0;
290283
290490
  try {
290284
- worktreeCount = readdirSync48(join158(rt, "worktrees"), { withFileTypes: true }).filter((e) => e.isDirectory()).length;
290491
+ worktreeCount = readdirSync49(join158(rt, "worktrees"), { withFileTypes: true }).filter((e) => e.isDirectory()).length;
290285
290492
  } catch {
290286
290493
  }
290287
290494
  const parsed = parseInt(process.env["ROLL_BRANCH_CANARY_MAX"] ?? "", 10);
@@ -290389,7 +290596,7 @@ function checkCoreWorktreeContamination(cwd) {
290389
290596
  // packages/cli/dist/commands/offboard.js
290390
290597
  init_dist();
290391
290598
  import { spawnSync as spawnSync16 } from "node:child_process";
290392
- import { existsSync as existsSync135, readFileSync as readFileSync136, realpathSync as realpathSync20, renameSync as renameSync29, rmSync as rmSync25, writeFileSync as writeFileSync73 } from "node:fs";
290599
+ import { existsSync as existsSync136, readFileSync as readFileSync136, realpathSync as realpathSync21, renameSync as renameSync29, rmSync as rmSync26, writeFileSync as writeFileSync73 } from "node:fs";
290393
290600
  import { homedir as homedir35 } from "node:os";
290394
290601
  import { join as join159, resolve as resolve15 } from "node:path";
290395
290602
  function pal4() {
@@ -290490,7 +290697,7 @@ function writeFileAtomic2(path, text2) {
290490
290697
  writeFileSync73(tmp, text2);
290491
290698
  renameSync29(tmp, path);
290492
290699
  } catch (error) {
290493
- rmSync25(tmp, { force: true });
290700
+ rmSync26(tmp, { force: true });
290494
290701
  throw error;
290495
290702
  }
290496
290703
  }
@@ -290516,12 +290723,12 @@ function offboardCommand(args) {
290516
290723
  }
290517
290724
  let projectDir;
290518
290725
  try {
290519
- projectDir = realpathSync20(resolve15(process.cwd()));
290726
+ projectDir = realpathSync21(resolve15(process.cwd()));
290520
290727
  } catch {
290521
290728
  projectDir = process.cwd();
290522
290729
  }
290523
290730
  const changeset = changesetPath2(projectDir);
290524
- if (!existsSync135(changeset)) {
290731
+ if (!existsSync136(changeset)) {
290525
290732
  err10(m4("offboard.no_changeset_en"));
290526
290733
  err10(m4("offboard.no_changeset_zh"));
290527
290734
  process.stderr.write("\n");
@@ -290620,7 +290827,7 @@ function offboardCommand(args) {
290620
290827
  process.stdout.write(m4("offboard.applying_offboard") + "\n");
290621
290828
  for (const item of mergedFiles) {
290622
290829
  const path = resolveChangesetItem(projectDir, item);
290623
- if (!existsSync135(path))
290830
+ if (!existsSync136(path))
290624
290831
  continue;
290625
290832
  const before = readFileSync136(path, "utf8");
290626
290833
  const after = stripRollMergeBlocks(before);
@@ -290633,8 +290840,8 @@ function offboardCommand(args) {
290633
290840
  for (const item of files) {
290634
290841
  try {
290635
290842
  const path = resolveChangesetItem(projectDir, item);
290636
- rmSync25(path, { force: true });
290637
- if (!existsSync135(path))
290843
+ rmSync26(path, { force: true });
290844
+ if (!existsSync136(path))
290638
290845
  process.stdout.write(` removed file ${item}
290639
290846
  `);
290640
290847
  } catch {
@@ -290642,7 +290849,7 @@ function offboardCommand(args) {
290642
290849
  }
290643
290850
  for (const item of dirs) {
290644
290851
  try {
290645
- rmSync25(resolveChangesetItem(projectDir, item), { recursive: true, force: true });
290852
+ rmSync26(resolveChangesetItem(projectDir, item), { recursive: true, force: true });
290646
290853
  process.stdout.write(` removed dir ${item}
290647
290854
  `);
290648
290855
  } catch {
@@ -290650,7 +290857,7 @@ function offboardCommand(args) {
290650
290857
  }
290651
290858
  for (const item of giEntries) {
290652
290859
  const gi = join159(projectDir, ".gitignore");
290653
- if (existsSync135(gi)) {
290860
+ if (existsSync136(gi)) {
290654
290861
  const lines3 = readFileSync136(gi, "utf8").split("\n");
290655
290862
  if (lines3.includes(item)) {
290656
290863
  const kept = lines3.filter((l) => l !== item);
@@ -290666,21 +290873,21 @@ function offboardCommand(args) {
290666
290873
  if (r === 0)
290667
290874
  process.stdout.write(` unloaded ${item}
290668
290875
  `);
290669
- rmSync25(plistPath, { force: true });
290876
+ rmSync26(plistPath, { force: true });
290670
290877
  }
290671
- rmSync25(changeset, { force: true });
290878
+ rmSync26(changeset, { force: true });
290672
290879
  ok11(m4("offboard.offboard_complete_offboard"));
290673
290880
  return 0;
290674
290881
  }
290675
290882
 
290676
290883
  // packages/cli/dist/commands/prices.js
290677
290884
  init_dist();
290678
- import { existsSync as existsSync137, readdirSync as readdirSync50, readFileSync as readFileSync138 } from "node:fs";
290885
+ import { existsSync as existsSync138, readdirSync as readdirSync51, readFileSync as readFileSync138 } from "node:fs";
290679
290886
  import { join as join161 } from "node:path";
290680
290887
 
290681
290888
  // packages/cli/dist/commands/prices-refresh.js
290682
290889
  init_dist();
290683
- import { existsSync as existsSync136, mkdirSync as mkdirSync75, readdirSync as readdirSync49, readFileSync as readFileSync137, writeFileSync as writeFileSync74 } from "node:fs";
290890
+ import { existsSync as existsSync137, mkdirSync as mkdirSync75, readdirSync as readdirSync50, readFileSync as readFileSync137, writeFileSync as writeFileSync74 } from "node:fs";
290684
290891
  import { join as join160 } from "node:path";
290685
290892
  var FetchError = class extends Error {
290686
290893
  constructor(message) {
@@ -290902,9 +291109,9 @@ function vendorFromSnapshotName(name) {
290902
291109
  return match[2] ?? "anthropic";
290903
291110
  }
290904
291111
  function latestSnapshotPath(snapshotDir, vendor) {
290905
- if (!existsSync136(snapshotDir))
291112
+ if (!existsSync137(snapshotDir))
290906
291113
  return null;
290907
- const snaps = readdirSync49(snapshotDir).filter((name) => SNAPSHOT_RE.test(name) && vendorFromSnapshotName(name) === vendor).sort().map((name) => join160(snapshotDir, name));
291114
+ const snaps = readdirSync50(snapshotDir).filter((name) => SNAPSHOT_RE.test(name) && vendorFromSnapshotName(name) === vendor).sort().map((name) => join160(snapshotDir, name));
290908
291115
  return snaps[snaps.length - 1] ?? null;
290909
291116
  }
290910
291117
  function readPrices(path) {
@@ -291111,10 +291318,10 @@ async function pricesRefreshCommand(args, deps = {}) {
291111
291318
  // packages/cli/dist/commands/prices.js
291112
291319
  function loadSnapshots() {
291113
291320
  const dir = join161(repoRoot2(), "lib", "prices");
291114
- if (!existsSync137(dir)) {
291321
+ if (!existsSync138(dir)) {
291115
291322
  throw new Error(`no price snapshots found in ${dir}; run \`roll prices refresh\``);
291116
291323
  }
291117
- const files = readdirSync50(dir).filter((n) => n.startsWith("snapshot-") && n.endsWith(".json")).sort();
291324
+ const files = readdirSync51(dir).filter((n) => n.startsWith("snapshot-") && n.endsWith(".json")).sort();
291118
291325
  return files.map((name) => {
291119
291326
  const data = JSON.parse(readFileSync138(join161(dir, name), "utf8"));
291120
291327
  for (const key of ["version", "effective_at", "source_url", "prices"]) {
@@ -291295,7 +291502,7 @@ init_dist3();
291295
291502
  init_dist();
291296
291503
  init_render();
291297
291504
  import { execFileSync as execFileSync35 } from "node:child_process";
291298
- import { mkdirSync as mkdirSync79, readFileSync as readFileSync142, writeFileSync as writeFileSync78, existsSync as existsSync141 } from "node:fs";
291505
+ import { mkdirSync as mkdirSync79, readFileSync as readFileSync142, writeFileSync as writeFileSync78, existsSync as existsSync142 } from "node:fs";
291299
291506
  import { dirname as dirname78, join as join165 } from "node:path";
291300
291507
 
291301
291508
  // packages/cli/dist/lib/release-consistency.js
@@ -291304,7 +291511,7 @@ init_dist3();
291304
291511
  init_dist();
291305
291512
  init_render();
291306
291513
  import { execFileSync as execFileSync34 } from "node:child_process";
291307
- import { existsSync as existsSync139, mkdirSync as mkdirSync77, readFileSync as readFileSync140, readdirSync as readdirSync52, statSync as statSync39, writeFileSync as writeFileSync76 } from "node:fs";
291514
+ import { existsSync as existsSync140, mkdirSync as mkdirSync77, readFileSync as readFileSync140, readdirSync as readdirSync53, statSync as statSync39, writeFileSync as writeFileSync76 } from "node:fs";
291308
291515
  import { dirname as dirname76, join as join163 } from "node:path";
291309
291516
 
291310
291517
  // packages/cli/dist/lib/consistency-audit.js
@@ -291312,7 +291519,7 @@ init_dist2();
291312
291519
  init_dist3();
291313
291520
  init_dist();
291314
291521
  import { execFile as execFile19 } from "node:child_process";
291315
- import { existsSync as existsSync138, mkdirSync as mkdirSync76, readFileSync as readFileSync139, readdirSync as readdirSync51, writeFileSync as writeFileSync75 } from "node:fs";
291522
+ import { existsSync as existsSync139, mkdirSync as mkdirSync76, readFileSync as readFileSync139, readdirSync as readdirSync52, writeFileSync as writeFileSync75 } from "node:fs";
291316
291523
  import { dirname as dirname75, join as join162 } from "node:path";
291317
291524
  import { promisify as promisify18 } from "node:util";
291318
291525
  init_dist();
@@ -291321,7 +291528,7 @@ var PROBE_CAP = 20;
291321
291528
  var COUNT_WINDOW_SEC = 72 * 3600;
291322
291529
  var execFileAsync18 = promisify18(execFile19);
291323
291530
  function readJsonl(path) {
291324
- if (!existsSync138(path))
291531
+ if (!existsSync139(path))
291325
291532
  return [];
291326
291533
  const out3 = [];
291327
291534
  for (const line of readFileSync139(path, "utf8").split("\n")) {
@@ -291337,7 +291544,7 @@ function readJsonl(path) {
291337
291544
  return out3;
291338
291545
  }
291339
291546
  function readJson(path) {
291340
- if (!existsSync138(path))
291547
+ if (!existsSync139(path))
291341
291548
  return null;
291342
291549
  try {
291343
291550
  const parsed = JSON.parse(readFileSync139(path, "utf8"));
@@ -291365,10 +291572,10 @@ function hasScreenshotArtifact(reportPath) {
291365
291572
  }
291366
291573
  }
291367
291574
  const dir = join162(runDir, "screenshots");
291368
- if (!existsSync138(dir))
291575
+ if (!existsSync139(dir))
291369
291576
  return false;
291370
291577
  try {
291371
- return readdirSync51(dir).some((name) => /\.png$/i.test(name));
291578
+ return readdirSync52(dir).some((name) => /\.png$/i.test(name));
291372
291579
  } catch {
291373
291580
  return false;
291374
291581
  }
@@ -291402,7 +291609,7 @@ async function gatherAuditSnapshot(projectPath3, runtimeDir11, deps = {}) {
291402
291609
  const snapshot = emptyAuditSnapshot(nowSec, TERMINAL_SCHEMA_EPOCH_SEC);
291403
291610
  const skipped2 = [];
291404
291611
  const backlogPath = join162(projectPath3, ".roll", "backlog.md");
291405
- if (existsSync138(backlogPath)) {
291612
+ if (existsSync139(backlogPath)) {
291406
291613
  snapshot.backlog = parseBacklog(readFileSync139(backlogPath, "utf8")).map((r) => ({ id: r.id, status: r.status }));
291407
291614
  }
291408
291615
  snapshot.index = readIndex(projectPath3);
@@ -291411,7 +291618,7 @@ async function gatherAuditSnapshot(projectPath3, runtimeDir11, deps = {}) {
291411
291618
  let eventFailed = 0;
291412
291619
  const terminal = [];
291413
291620
  const eventsPath4 = join162(runtimeDir11, "events.ndjson");
291414
- if (existsSync138(eventsPath4)) {
291621
+ if (existsSync139(eventsPath4)) {
291415
291622
  for (const line of readFileSync139(eventsPath4, "utf8").split("\n")) {
291416
291623
  const e = parseEventLine(line);
291417
291624
  if (e === null)
@@ -291434,22 +291641,22 @@ async function gatherAuditSnapshot(projectPath3, runtimeDir11, deps = {}) {
291434
291641
  if (!row2.status.includes("\u2705"))
291435
291642
  continue;
291436
291643
  const card = cardArchiveDir(projectPath3, row2.id);
291437
- if (!existsSync138(card))
291644
+ if (!existsSync139(card))
291438
291645
  continue;
291439
291646
  const reportPath = join162(card, "latest", reportFileName(row2.id));
291440
291647
  const specPath = join162(card, "spec.md");
291441
291648
  let noVisualSurface = false;
291442
- if (existsSync138(specPath)) {
291649
+ if (existsSync139(specPath)) {
291443
291650
  try {
291444
291651
  noVisualSurface = !hasVisualEvidenceAc(readFileSync139(specPath, "utf8"));
291445
291652
  } catch {
291446
291653
  }
291447
291654
  }
291448
291655
  snapshot.attest[row2.id] = {
291449
- report: existsSync138(reportPath),
291450
- acMap: existsSync138(join162(card, "ac-map.json")),
291451
- visualEvidence: existsSync138(reportPath) ? hasScreenshotArtifact(reportPath) : false,
291452
- machineSkip: existsSync138(reportPath) ? hasMachineCaptureSkip2(reportPath) : false,
291656
+ report: existsSync139(reportPath),
291657
+ acMap: existsSync139(join162(card, "ac-map.json")),
291658
+ visualEvidence: existsSync139(reportPath) ? hasScreenshotArtifact(reportPath) : false,
291659
+ machineSkip: existsSync139(reportPath) ? hasMachineCaptureSkip2(reportPath) : false,
291453
291660
  noVisualSurface
291454
291661
  };
291455
291662
  }
@@ -291621,7 +291828,7 @@ function walkTextFiles(projectDir, relDir, extensions) {
291621
291828
  const dirAbs = join163(projectDir, dirRel);
291622
291829
  let entries;
291623
291830
  try {
291624
- entries = readdirSync52(dirAbs, { withFileTypes: true });
291831
+ entries = readdirSync53(dirAbs, { withFileTypes: true });
291625
291832
  } catch {
291626
291833
  return;
291627
291834
  }
@@ -291830,11 +292037,11 @@ function manualDirectMainDelivery(projectDir, id, mergeShas) {
291830
292037
  function findCardSpec(projectDir, id) {
291831
292038
  const featuresDir = join163(projectDir, ".roll", "features");
291832
292039
  try {
291833
- for (const epic of readdirSync52(featuresDir, { withFileTypes: true })) {
292040
+ for (const epic of readdirSync53(featuresDir, { withFileTypes: true })) {
291834
292041
  if (!epic.isDirectory())
291835
292042
  continue;
291836
292043
  const spec = join163(featuresDir, epic.name, id, "spec.md");
291837
- if (existsSync139(spec))
292044
+ if (existsSync140(spec))
291838
292045
  return spec;
291839
292046
  }
291840
292047
  } catch {
@@ -291853,12 +292060,12 @@ function cardChangelogExempt(projectDir, id) {
291853
292060
  }
291854
292061
  function checkFeaturesCatalog(projectDir) {
291855
292062
  const backlog = join163(projectDir, ".roll", "backlog.md");
291856
- if (!existsSync139(backlog))
292063
+ if (!existsSync140(backlog))
291857
292064
  return { status: "pass", gaps: [] };
291858
292065
  const backlogText = readText(backlog);
291859
292066
  const gaps = [];
291860
292067
  const features = join163(projectDir, ".roll", "features.md");
291861
- if (existsSync139(features)) {
292068
+ if (existsSync140(features)) {
291862
292069
  const featuresText = readText(features);
291863
292070
  for (const featName of readDoneFeatures(backlogText).keys()) {
291864
292071
  const escaped = escapeRegExp4(featName);
@@ -291882,7 +292089,7 @@ function checkFeaturesCatalog(projectDir) {
291882
292089
  }
291883
292090
  function checkTruthLive(projectDir) {
291884
292091
  const backlog = join163(projectDir, ".roll", "backlog.md");
291885
- if (!existsSync139(backlog))
292092
+ if (!existsSync140(backlog))
291886
292093
  return { status: "pass", gaps: [] };
291887
292094
  const backlogText = readText(backlog);
291888
292095
  const facts = backlogRowFacts(backlogText);
@@ -291927,17 +292134,17 @@ function checkTruthLive(projectDir) {
291927
292134
  function checkCards(projectDir) {
291928
292135
  const backlog = join163(projectDir, ".roll", "backlog.md");
291929
292136
  const featuresDir = join163(projectDir, ".roll", "features");
291930
- if (!existsSync139(backlog) || !existsSync139(featuresDir))
292137
+ if (!existsSync140(backlog) || !existsSync140(featuresDir))
291931
292138
  return { status: "pass", gaps: [] };
291932
292139
  const cardEpic = /* @__PURE__ */ new Map();
291933
292140
  try {
291934
- for (const epic of readdirSync52(featuresDir, { withFileTypes: true })) {
292141
+ for (const epic of readdirSync53(featuresDir, { withFileTypes: true })) {
291935
292142
  if (!epic.isDirectory())
291936
292143
  continue;
291937
- for (const card of readdirSync52(join163(featuresDir, epic.name), { withFileTypes: true })) {
292144
+ for (const card of readdirSync53(join163(featuresDir, epic.name), { withFileTypes: true })) {
291938
292145
  if (!card.isDirectory())
291939
292146
  continue;
291940
- if (existsSync139(join163(featuresDir, epic.name, card.name, "spec.md")) && !cardEpic.has(card.name)) {
292147
+ if (existsSync140(join163(featuresDir, epic.name, card.name, "spec.md")) && !cardEpic.has(card.name)) {
291941
292148
  cardEpic.set(card.name, epic.name);
291942
292149
  }
291943
292150
  }
@@ -291972,12 +292179,12 @@ function checkCards(projectDir) {
291972
292179
  const ev = /\[evidence\]\(([^)]+)\)/.exec(line);
291973
292180
  if (ev !== null) {
291974
292181
  const target = (ev[1] ?? "").replace(/^\.roll\//, "");
291975
- if (!existsSync139(join163(projectDir, ".roll", target))) {
292182
+ if (!existsSync140(join163(projectDir, ".roll", target))) {
291976
292183
  gaps.push(`Backlog row ${id} evidence link is broken: ${ev[1] ?? ""}`);
291977
292184
  }
291978
292185
  } else if (line.includes(STATUS_MARKER.done)) {
291979
292186
  const epic = cardEpic.get(id) ?? "";
291980
- if (!existsSync139(join163(featuresDir, epic, id, "latest", `${id}-report.html`))) {
292187
+ if (!existsSync140(join163(featuresDir, epic, id, "latest", `${id}-report.html`))) {
291981
292188
  if (hasAcBlock(epic, id)) {
291982
292189
  gaps.push(`Done backlog row ${id} has ACs but no attest report`);
291983
292190
  } else {
@@ -292000,7 +292207,7 @@ function checkCards(projectDir) {
292000
292207
  function checkDocs(projectDir) {
292001
292208
  const gaps = checkTopLevelCommands(activeDocsFiles(projectDir), DOC_RETIRED_TOP_LEVEL_COMMANDS);
292002
292209
  const changelogPath = join163(projectDir, "CHANGELOG.md");
292003
- if (existsSync139(changelogPath)) {
292210
+ if (existsSync140(changelogPath)) {
292004
292211
  const changelog = readText(changelogPath);
292005
292212
  for (const id of releaseDeltaCardIds(projectDir)) {
292006
292213
  const base = id.replace(/[a-z]$/, "");
@@ -292075,7 +292282,7 @@ function checkSite(projectDir) {
292075
292282
  const gaps = checkTopLevelCommands(activeSiteFiles(projectDir), SITE_HIDDEN_TOP_LEVEL_COMMANDS);
292076
292283
  const siteJs = join163(projectDir, "site", "roll-data.js");
292077
292284
  const backlog = join163(projectDir, ".roll", "backlog.md");
292078
- if (!existsSync139(siteJs) || !existsSync139(backlog))
292285
+ if (!existsSync140(siteJs) || !existsSync140(backlog))
292079
292286
  return { status: gaps.length === 0 ? "pass" : "fail", gaps };
292080
292287
  const siteText = readText(siteJs);
292081
292288
  const seenGuideRefs = /* @__PURE__ */ new Set();
@@ -292084,7 +292291,7 @@ function checkSite(projectDir) {
292084
292291
  if (seenGuideRefs.has(rel2))
292085
292292
  continue;
292086
292293
  seenGuideRefs.add(rel2);
292087
- if (!existsSync139(join163(projectDir, rel2))) {
292294
+ if (!existsSync140(join163(projectDir, rel2))) {
292088
292295
  gaps.push(`site/roll-data.js links a guide that does not exist: ${rel2}`);
292089
292296
  }
292090
292297
  }
@@ -292123,7 +292330,7 @@ function checkSite(projectDir) {
292123
292330
  }
292124
292331
  function listFiles2(dir) {
292125
292332
  try {
292126
- return readdirSync52(dir).filter((n) => {
292333
+ return readdirSync53(dir).filter((n) => {
292127
292334
  try {
292128
292335
  return statSync39(join163(dir, n)).isFile();
292129
292336
  } catch {
@@ -292138,7 +292345,7 @@ function checkI18n(projectDir) {
292138
292345
  const gaps = [];
292139
292346
  const guideEn = join163(projectDir, "guide", "en");
292140
292347
  const guideZh = join163(projectDir, "guide", "zh");
292141
- if (existsSync139(guideEn) && existsSync139(guideZh)) {
292348
+ if (existsSync140(guideEn) && existsSync140(guideZh)) {
292142
292349
  const enFiles = new Set(listFiles2(guideEn));
292143
292350
  const zhFiles = new Set(listFiles2(guideZh));
292144
292351
  const enOnly = [...enFiles].filter((f) => !zhFiles.has(f)).sort();
@@ -292149,12 +292356,12 @@ function checkI18n(projectDir) {
292149
292356
  gaps.push(`guide/zh/${f} has no corresponding guide/en/${f}`);
292150
292357
  }
292151
292358
  const i18nDir = join163(projectDir, "lib", "i18n");
292152
- if (existsSync139(i18nDir)) {
292359
+ if (existsSync140(i18nDir)) {
292153
292360
  const keysEn = /* @__PURE__ */ new Set();
292154
292361
  const keysZh = /* @__PURE__ */ new Set();
292155
292362
  let shFiles = [];
292156
292363
  try {
292157
- shFiles = readdirSync52(i18nDir).filter((n) => n.endsWith(".sh")).sort();
292364
+ shFiles = readdirSync53(i18nDir).filter((n) => n.endsWith(".sh")).sort();
292158
292365
  } catch {
292159
292366
  shFiles = [];
292160
292367
  }
@@ -292194,7 +292401,7 @@ function rglobBats(dir) {
292194
292401
  const walk2 = (d) => {
292195
292402
  let entries = [];
292196
292403
  try {
292197
- entries = readdirSync52(d);
292404
+ entries = readdirSync53(d);
292198
292405
  } catch {
292199
292406
  return;
292200
292407
  }
@@ -292219,7 +292426,7 @@ function checkTests(projectDir) {
292219
292426
  const gaps = [];
292220
292427
  const backlog = join163(projectDir, ".roll", "backlog.md");
292221
292428
  const testsDir = join163(projectDir, "tests");
292222
- if (!existsSync139(backlog))
292429
+ if (!existsSync140(backlog))
292223
292430
  return { status: "pass", gaps: [] };
292224
292431
  const backlogText = readText(backlog);
292225
292432
  const allFeatures = /* @__PURE__ */ new Set();
@@ -292244,7 +292451,7 @@ function checkTests(projectDir) {
292244
292451
  doneFeatures.push(currentFeature);
292245
292452
  }
292246
292453
  }
292247
- const testFiles = existsSync139(testsDir) ? rglobBats(testsDir) : [];
292454
+ const testFiles = existsSync140(testsDir) ? rglobBats(testsDir) : [];
292248
292455
  if (testFiles.length === 0)
292249
292456
  return { status: "pass", gaps: [] };
292250
292457
  for (const feat of doneFeatures) {
@@ -292596,7 +292803,7 @@ function writeReleaseTestProof(exec) {
292596
292803
  function assertTestProofFresh(exec) {
292597
292804
  const cwd = exec("git", ["rev-parse", "--show-toplevel"]).trim();
292598
292805
  const proofPath = join165(cwd, ".roll", "last-test-pass");
292599
- if (!existsSync141(proofPath)) {
292806
+ if (!existsSync142(proofPath)) {
292600
292807
  throw new Error("test-pass proof missing \u2014 run `roll test` before committing");
292601
292808
  }
292602
292809
  const body = readFileSync142(proofPath, "utf8");
@@ -292740,7 +292947,7 @@ function realReleaseDeps() {
292740
292947
  commitPushWithGate({
292741
292948
  branch,
292742
292949
  message,
292743
- rollManaged: existsSync141(join165(cwd, ".roll")),
292950
+ rollManaged: existsSync142(join165(cwd, ".roll")),
292744
292951
  exec: (cmd, args) => execFileSync35(cmd, args, { cwd, encoding: "utf8", stdio: ["ignore", "pipe", "pipe"], timeout: 6e5 })
292745
292952
  });
292746
292953
  },
@@ -293065,13 +293272,13 @@ ${c("dim", "\u2192 Running the golden-path showcase (real models; its verdict do
293065
293272
  init_dist();
293066
293273
  init_render();
293067
293274
  import { spawnSync as spawnSync19 } from "node:child_process";
293068
- import { existsSync as existsSync143, lstatSync as lstatSync13, mkdirSync as mkdirSync81, readFileSync as readFileSync144, readdirSync as readdirSync55, readlinkSync as readlinkSync3, statSync as statSync42 } from "node:fs";
293275
+ import { existsSync as existsSync144, lstatSync as lstatSync13, mkdirSync as mkdirSync81, readFileSync as readFileSync144, readdirSync as readdirSync56, readlinkSync as readlinkSync3, statSync as statSync42 } from "node:fs";
293069
293276
  import { join as join167 } from "node:path";
293070
293277
 
293071
293278
  // packages/cli/dist/lib/roll-capture-install.js
293072
293279
  init_dist();
293073
293280
  import { spawnSync as spawnSync18 } from "node:child_process";
293074
- import { chmodSync as chmodSync2, existsSync as existsSync142, lstatSync as lstatSync12, mkdirSync as mkdirSync80, mkdtempSync as mkdtempSync9, readdirSync as readdirSync54, readFileSync as readFileSync143, renameSync as renameSync30, rmSync as rmSync27, statSync as statSync41, writeFileSync as writeFileSync79 } from "node:fs";
293281
+ import { chmodSync as chmodSync2, existsSync as existsSync143, lstatSync as lstatSync12, mkdirSync as mkdirSync80, mkdtempSync as mkdtempSync9, readdirSync as readdirSync55, readFileSync as readFileSync143, renameSync as renameSync30, rmSync as rmSync28, statSync as statSync41, writeFileSync as writeFileSync79 } from "node:fs";
293075
293282
  import { homedir as homedir36 } from "node:os";
293076
293283
  import { join as join166 } from "node:path";
293077
293284
  var RELEASE_API = "https://api.github.com/repos/seanyao/roll-capture/releases/latest";
@@ -293098,7 +293305,7 @@ function defaultRollCaptureInstallDeps() {
293098
293305
  home: homedir36(),
293099
293306
  uid: typeof process.getuid === "function" ? process.getuid() : void 0,
293100
293307
  ...platform3 === "darwin" && !isCi3(process.env) && process.env["ROLL_SKIP_CAPTURE_INSTALL"] !== "1" ? { hasAquaGUI: macosHasAquaGUI3(execFile20) } : {},
293101
- exists: existsSync142,
293308
+ exists: existsSync143,
293102
293309
  renamePath: renameSync30,
293103
293310
  execFile: execFile20,
293104
293311
  fetchLatestRelease: (timeoutMs) => defaultFetchLatestRelease(timeoutMs, process.env, execFile20),
@@ -293170,7 +293377,7 @@ async function installRollCapture(deps = defaultRollCaptureInstallDeps()) {
293170
293377
  } catch (error) {
293171
293378
  return manual(`install failed: ${errorMessage2(error)}`, release.tagName);
293172
293379
  } finally {
293173
- rmSync27(workDir, { recursive: true, force: true });
293380
+ rmSync28(workDir, { recursive: true, force: true });
293174
293381
  }
293175
293382
  }
293176
293383
  function renderRollCaptureInstallResult(result2, lang10) {
@@ -293306,7 +293513,7 @@ function macosHasAquaGUI3(execFile20) {
293306
293513
  }
293307
293514
  function findExtractedApp(dir) {
293308
293515
  try {
293309
- for (const name of readdirSync54(dir)) {
293516
+ for (const name of readdirSync55(dir)) {
293310
293517
  const candidate = join166(dir, name);
293311
293518
  const st = lstatSync12(candidate);
293312
293519
  if (st.isSymbolicLink())
@@ -293327,7 +293534,7 @@ function findExtractedApp(dir) {
293327
293534
  function findBundleExecutable(appPath) {
293328
293535
  const macosDir = join166(appPath, "Contents", "MacOS");
293329
293536
  try {
293330
- for (const name of readdirSync54(macosDir)) {
293537
+ for (const name of readdirSync55(macosDir)) {
293331
293538
  const candidate = join166(macosDir, name);
293332
293539
  const st = lstatSync12(candidate);
293333
293540
  if (st.isSymbolicLink())
@@ -293344,7 +293551,7 @@ function replaceAppAtomically(app, targetApp, deps) {
293344
293551
  const renamePath = deps.renamePath ?? renameSync30;
293345
293552
  const backupApp = `${targetApp}.bak`;
293346
293553
  const hadExisting = lstatExists(targetApp);
293347
- rmSync27(backupApp, { recursive: true, force: true });
293554
+ rmSync28(backupApp, { recursive: true, force: true });
293348
293555
  if (!hadExisting) {
293349
293556
  renamePath(app, targetApp);
293350
293557
  return;
@@ -293356,10 +293563,10 @@ function replaceAppAtomically(app, targetApp, deps) {
293356
293563
  backupCreated = true;
293357
293564
  renamePath(app, targetApp);
293358
293565
  targetReplaced = true;
293359
- rmSync27(backupApp, { recursive: true, force: true });
293566
+ rmSync28(backupApp, { recursive: true, force: true });
293360
293567
  } catch (error) {
293361
293568
  if (targetReplaced)
293362
- rmSync27(targetApp, { recursive: true, force: true });
293569
+ rmSync28(targetApp, { recursive: true, force: true });
293363
293570
  if (backupCreated) {
293364
293571
  try {
293365
293572
  renamePath(backupApp, targetApp);
@@ -293535,7 +293742,7 @@ function setupSnapshot(watch) {
293535
293742
  function walk(dir, lines3) {
293536
293743
  let names;
293537
293744
  try {
293538
- names = readdirSync55(dir);
293745
+ names = readdirSync56(dir);
293539
293746
  } catch {
293540
293747
  return;
293541
293748
  }
@@ -293610,11 +293817,11 @@ function tmuxPresent() {
293610
293817
  function submoduleGuard() {
293611
293818
  const pkg = rollPkgDir();
293612
293819
  const skills = join167(pkg, "skills");
293613
- const hasGit = existsSync143(join167(pkg, ".git"));
293614
- const hasMods = existsSync143(join167(pkg, ".gitmodules"));
293820
+ const hasGit = existsSync144(join167(pkg, ".git"));
293821
+ const hasMods = existsSync144(join167(pkg, ".gitmodules"));
293615
293822
  let empty = true;
293616
293823
  try {
293617
- empty = readdirSync55(skills).length === 0;
293824
+ empty = readdirSync56(skills).length === 0;
293618
293825
  } catch {
293619
293826
  empty = true;
293620
293827
  }
@@ -293728,7 +293935,7 @@ async function setupCommand2(args) {
293728
293935
  return 1;
293729
293936
  }
293730
293937
  }
293731
- if (!existsSync143(rollPkgConventions())) {
293938
+ if (!existsSync144(rollPkgConventions())) {
293732
293939
  err13(m5("shared.convention_source_not_found_at_2", rollPkgConventions()));
293733
293940
  err13(m5("shared.run_this_from_the_roll_repo"));
293734
293941
  return 1;
@@ -293819,7 +294026,7 @@ init_showcase2();
293819
294026
  // packages/cli/dist/commands/test.js
293820
294027
  init_dist2();
293821
294028
  import { spawnSync as spawnSync20 } from "node:child_process";
293822
- import { appendFileSync as appendFileSync22, existsSync as existsSync144, mkdirSync as mkdirSync82, readFileSync as readFileSync145, rmSync as rmSync28, writeFileSync as writeFileSync80 } from "node:fs";
294029
+ import { appendFileSync as appendFileSync22, existsSync as existsSync145, mkdirSync as mkdirSync82, readFileSync as readFileSync145, rmSync as rmSync29, writeFileSync as writeFileSync80 } from "node:fs";
293823
294030
  import { dirname as dirname79, join as join168, resolve as resolve16 } from "node:path";
293824
294031
  function pal6() {
293825
294032
  const noColor2 = (process.env["NO_COLOR"] ?? "") !== "";
@@ -293841,7 +294048,7 @@ function currentEvidenceFrame() {
293841
294048
  if (raw === "")
293842
294049
  return null;
293843
294050
  const runDir = resolve16(raw);
293844
- if (!existsSync144(runDir))
294051
+ if (!existsSync145(runDir))
293845
294052
  return null;
293846
294053
  const frame = {
293847
294054
  runDir,
@@ -293898,7 +294105,7 @@ function appendRollTestEvidence(frame, cmd, argv, status2, stdout, stderr) {
293898
294105
  }
293899
294106
  function isolationGetType() {
293900
294107
  const file = join168(process.cwd(), ".roll", "local.yaml");
293901
- if (!existsSync144(file))
294108
+ if (!existsSync145(file))
293902
294109
  return "none";
293903
294110
  let text2;
293904
294111
  try {
@@ -293950,11 +294157,11 @@ function resetLockPath() {
293950
294157
  return ".roll/.iso-reset.lock";
293951
294158
  }
293952
294159
  function resetLockHeld() {
293953
- return existsSync144(resetLockPath());
294160
+ return existsSync145(resetLockPath());
293954
294161
  }
293955
294162
  function resetAcquireLock() {
293956
294163
  const lock = resetLockPath();
293957
- if (existsSync144(lock))
294164
+ if (existsSync145(lock))
293958
294165
  return false;
293959
294166
  mkdirSync82(dirname79(lock), { recursive: true });
293960
294167
  writeFileSync80(lock, `${process.pid}
@@ -293962,7 +294169,7 @@ function resetAcquireLock() {
293962
294169
  return true;
293963
294170
  }
293964
294171
  function resetReleaseLock() {
293965
- rmSync28(resetLockPath(), { force: true });
294172
+ rmSync29(resetLockPath(), { force: true });
293966
294173
  }
293967
294174
  function runForward(cmd, argv) {
293968
294175
  const frame = currentEvidenceFrame();
@@ -293980,7 +294187,7 @@ function runForward(cmd, argv) {
293980
294187
  }
293981
294188
  function readTestScript(cwd) {
293982
294189
  const p = join168(cwd, "package.json");
293983
- if (!existsSync144(p))
294190
+ if (!existsSync145(p))
293984
294191
  return void 0;
293985
294192
  try {
293986
294193
  const pkg = JSON.parse(readFileSync145(p, "utf8"));
@@ -293994,7 +294201,7 @@ function detectVitestVersion(cwd) {
293994
294201
  let dir = cwd;
293995
294202
  for (let i = 0; i < 6; i++) {
293996
294203
  const vp = join168(dir, "node_modules", "vitest", "package.json");
293997
- if (existsSync144(vp)) {
294204
+ if (existsSync145(vp)) {
293998
294205
  try {
293999
294206
  const v = JSON.parse(readFileSync145(vp, "utf8")).version;
294000
294207
  if (typeof v === "string")
@@ -294032,14 +294239,14 @@ function writeTestProof(cwd, mode, command) {
294032
294239
  function ensureSkillsSubmoduleReady() {
294033
294240
  const pkg = rollPkgDir();
294034
294241
  const required2 = join168(pkg, "skills", "roll-onboard", "SKILL.md");
294035
- if (existsSync144(required2))
294242
+ if (existsSync145(required2))
294036
294243
  return true;
294037
- if (existsSync144(join168(pkg, ".git")) && existsSync144(join168(pkg, ".gitmodules"))) {
294244
+ if (existsSync145(join168(pkg, ".git")) && existsSync145(join168(pkg, ".gitmodules"))) {
294038
294245
  spawnSync20("git", ["submodule", "update", "--init", "--recursive", "--quiet", "skills"], {
294039
294246
  cwd: pkg,
294040
294247
  stdio: "ignore"
294041
294248
  });
294042
- if (existsSync144(required2))
294249
+ if (existsSync145(required2))
294043
294250
  return true;
294044
294251
  }
294045
294252
  err14("roll test: skills submodule is empty");
@@ -294048,7 +294255,7 @@ function ensureSkillsSubmoduleReady() {
294048
294255
  }
294049
294256
  function ensureNoneIsolation() {
294050
294257
  const type = isolationGetType();
294051
- if (type === "none" && !existsSync144(join168(process.cwd(), ".roll", "local.yaml"))) {
294258
+ if (type === "none" && !existsSync145(join168(process.cwd(), ".roll", "local.yaml"))) {
294052
294259
  infoErr("isolation: no test_isolation config, falling back to type=none (host)");
294053
294260
  }
294054
294261
  if (!SUPPORTED_TYPES.includes(type)) {
@@ -294163,7 +294370,7 @@ function testCommand(args) {
294163
294370
  }
294164
294371
  const cwd = process.cwd();
294165
294372
  const resolution = resolveGateCommand({
294166
- hasPackageJson: existsSync144(join168(cwd, "package.json")),
294373
+ hasPackageJson: existsSync145(join168(cwd, "package.json")),
294167
294374
  testScript: readTestScript(cwd),
294168
294375
  vitestVersion: detectVitestVersion(cwd)
294169
294376
  });
@@ -294589,7 +294796,7 @@ function emitRepair(json, outcome) {
294589
294796
  // packages/cli/dist/commands/truth.js
294590
294797
  init_dist();
294591
294798
  init_dist2();
294592
- import { statSync as statSync43, readFileSync as readFileSync147, writeFileSync as writeFileSync82, mkdirSync as mkdirSync84, existsSync as existsSync145 } from "node:fs";
294799
+ import { statSync as statSync43, readFileSync as readFileSync147, writeFileSync as writeFileSync82, mkdirSync as mkdirSync84, existsSync as existsSync146 } from "node:fs";
294593
294800
  import { dirname as dirname81, join as join171 } from "node:path";
294594
294801
  var TRUTH_USAGE = "Usage: roll truth <command>\n query <storyId> Deterministic delivery-truth query (structured, zero markdown parse).\n audit Bidirectional drift audit: backlog Done \u2194 projection truth.\n\u547D\u4EE4:\n query <storyId> \u7ED3\u6784\u5316\u4EA4\u4ED8\u771F\u76F8\u67E5\u8BE2\uFF08\u786E\u5B9A\u6027\uFF0C\u4E0D\u89E3\u6790 markdown\uFF09\n audit \u53CC\u5411\u6F02\u79FB\u5BA1\u8BA1\uFF1Abacklog Done \u2194 \u6295\u5F71\u771F\u76F8";
294595
294802
  function formatTruth(t2, lang10) {
@@ -294637,7 +294844,7 @@ function truthAuditCommand(args, lang10) {
294637
294844
  const cwd = process.cwd();
294638
294845
  const nowSec = Math.floor(Date.now() / 1e3);
294639
294846
  const backlogPath = join171(cwd, ".roll", "backlog.md");
294640
- const backlogRows = existsSync145(backlogPath) ? parseBacklog(readFileSync147(backlogPath, "utf8")).map((r) => ({ id: r.id, status: r.status })) : [];
294847
+ const backlogRows = existsSync146(backlogPath) ? parseBacklog(readFileSync147(backlogPath, "utf8")).map((r) => ({ id: r.id, status: r.status })) : [];
294641
294848
  const deliveries = ensureDeliveriesFresh(cwd, nodeFreshnessPort4, nodeExecPort);
294642
294849
  const snapshot = emptyAuditSnapshot(nowSec, TERMINAL_SCHEMA_EPOCH_SEC);
294643
294850
  snapshot.backlog = backlogRows;
@@ -294722,7 +294929,7 @@ function truthCommand(args) {
294722
294929
 
294723
294930
  // packages/cli/dist/commands/tune.js
294724
294931
  init_dist2();
294725
- import { existsSync as existsSync146, readFileSync as readFileSync148 } from "node:fs";
294932
+ import { existsSync as existsSync147, readFileSync as readFileSync148 } from "node:fs";
294726
294933
  import { join as join172 } from "node:path";
294727
294934
  function projectPath2() {
294728
294935
  return (process.env["ROLL_MAIN_PROJECT"] ?? "").trim() || process.cwd();
@@ -294731,7 +294938,7 @@ function str6(v, dflt = "") {
294731
294938
  return typeof v === "string" ? v : dflt;
294732
294939
  }
294733
294940
  function parseJsonl(path) {
294734
- if (!existsSync146(path))
294941
+ if (!existsSync147(path))
294735
294942
  return [];
294736
294943
  let text2;
294737
294944
  try {
@@ -294907,7 +295114,7 @@ function tuneCommand(argv) {
294907
295114
  // packages/cli/dist/commands/update.js
294908
295115
  init_dist();
294909
295116
  import { spawnSync as spawnSync21 } from "node:child_process";
294910
- import { existsSync as existsSync147, mkdtempSync as mkdtempSync10, readFileSync as readFileSync149, rmSync as rmSync29 } from "node:fs";
295117
+ import { existsSync as existsSync148, mkdtempSync as mkdtempSync10, readFileSync as readFileSync149, rmSync as rmSync30 } from "node:fs";
294911
295118
  import { tmpdir as tmpdir14 } from "node:os";
294912
295119
  import { join as join173 } from "node:path";
294913
295120
  function pal7() {
@@ -294997,7 +295204,7 @@ function downloadAndInstallCurl(tag) {
294997
295204
  }
294998
295205
  return { ok: true, newVersion: treeVersion(extractDir) };
294999
295206
  } finally {
295000
- rmSync29(tmpDir, { recursive: true, force: true });
295207
+ rmSync30(tmpDir, { recursive: true, force: true });
295001
295208
  }
295002
295209
  }
295003
295210
  function checkInstalledVersionOrRetry() {
@@ -295017,11 +295224,11 @@ function checkInstalledVersionOrRetry() {
295017
295224
  }
295018
295225
  }
295019
295226
  function invalidateUpdateCache() {
295020
- rmSync29(join173(rollHome(), ".update-check"), { force: true });
295227
+ rmSync30(join173(rollHome(), ".update-check"), { force: true });
295021
295228
  }
295022
295229
  function showChangelog() {
295023
295230
  const changelog = join173(rollPkgDir(), "CHANGELOG.md");
295024
- if (!existsSync147(changelog))
295231
+ if (!existsSync148(changelog))
295025
295232
  return;
295026
295233
  const { BOLD: BOLD2, CYAN, NC } = pal7();
295027
295234
  process.stdout.write(`${BOLD2}${m6("changelog.heading")}:${NC}
@@ -295052,7 +295259,7 @@ async function updateCommand(args) {
295052
295259
  info5(m6("update.current_version", rollVersion()));
295053
295260
  let installMethod = "npm";
295054
295261
  const methodFile = join173(rollPkgDir(), ".install-method");
295055
- if (existsSync147(methodFile)) {
295262
+ if (existsSync148(methodFile)) {
295056
295263
  installMethod = readFileSync149(methodFile, "utf8").trim() || "npm";
295057
295264
  }
295058
295265
  if (installMethod === "curl") {