@seanyao/roll 4.719.2 → 4.720.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/CHANGELOG.md +11 -0
- package/dist/roll.mjs +512 -208
- 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
|
|
7289
|
-
return `${text2}${
|
|
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,
|
|
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 ===
|
|
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
|
|
31178
|
-
if (
|
|
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,
|
|
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 =
|
|
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
|
|
80315
|
-
if (
|
|
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(
|
|
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
|
|
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 (
|
|
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 (
|
|
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
|
|
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 (
|
|
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 || !
|
|
244546
|
+
if (depth > 3 || !existsSync141(dir))
|
|
244547
244547
|
return;
|
|
244548
244548
|
let entries = [];
|
|
244549
244549
|
try {
|
|
244550
|
-
entries =
|
|
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 &&
|
|
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 (
|
|
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 (
|
|
244607
|
-
|
|
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:
|
|
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 (!
|
|
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 (!
|
|
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 (!
|
|
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
|
-
|
|
244819
|
+
rmSync27(dirname77(sandbox), { recursive: true, force: true });
|
|
244820
244820
|
}
|
|
244821
244821
|
}
|
|
244822
244822
|
}
|
|
@@ -247550,6 +247550,7 @@ Loop \u5DF2\u5904\u4E8E\u6682\u505C
|
|
|
247550
247550
|
Loop \u5DF2\u6682\u505C \u2014 \u540E\u7EED\u6392\u7A0B\u5468\u671F\u5C06\u8DF3\u8FC7
|
|
247551
247551
|
`);
|
|
247552
247552
|
process.stdout.write("mode: guided \u2014 scheduler will not start long-running Story execution until `roll loop resume`\n");
|
|
247553
|
+
process.stdout.write(" supervisor one-shot: `roll loop go --cards <ids> --max-cycles 1` runs exactly those cards while staying paused (no autonomous scheduling).\n");
|
|
247553
247554
|
return 0;
|
|
247554
247555
|
}
|
|
247555
247556
|
async function loopResumeCommand(_args, deps = realDeps()) {
|
|
@@ -259883,9 +259884,9 @@ function writeFeatureStub(issue, featuresDir, epic = "backlog-lifecycle") {
|
|
|
259883
259884
|
if (existsSync47(path)) {
|
|
259884
259885
|
const existing = readFileSync47(path, "utf8");
|
|
259885
259886
|
const block = ac ? ac + "\n" : "";
|
|
259886
|
-
const
|
|
259887
|
+
const sep9 = existing.endsWith("\n") || existing === "" ? "" : "\n";
|
|
259887
259888
|
if (block)
|
|
259888
|
-
appendFileSync5(path,
|
|
259889
|
+
appendFileSync5(path, sep9 + block);
|
|
259889
259890
|
return path;
|
|
259890
259891
|
}
|
|
259891
259892
|
writeFileSync26(path, featureStubContent(issue));
|
|
@@ -262225,8 +262226,8 @@ function writeSet(runtimeDir11, set) {
|
|
|
262225
262226
|
writeFileSync30(tmp, JSON.stringify([...set].sort(), null, 2), "utf8");
|
|
262226
262227
|
writeFileSync30(path, readFileSync53(tmp, "utf8"), "utf8");
|
|
262227
262228
|
try {
|
|
262228
|
-
const { rmSync:
|
|
262229
|
-
|
|
262229
|
+
const { rmSync: rmSync31 } = __require("node:fs");
|
|
262230
|
+
rmSync31(tmp, { force: true });
|
|
262230
262231
|
} catch {
|
|
262231
262232
|
}
|
|
262232
262233
|
}
|
|
@@ -264497,6 +264498,7 @@ import { createInterface as createInterface3 } from "node:readline";
|
|
|
264497
264498
|
// packages/cli/dist/lib/goal-progress.js
|
|
264498
264499
|
init_dist();
|
|
264499
264500
|
var GOAL_ALLOWED_CARDS_ENV = "ROLL_LOOP_GO_ALLOWED_CARDS";
|
|
264501
|
+
var GOAL_GUIDED_ENV = "ROLL_LOOP_GO_GUIDED";
|
|
264500
264502
|
var DELIVERY_STATUSES = /* @__PURE__ */ new Set(["built", "published", "done", "merged"]);
|
|
264501
264503
|
var DELIVERY_OUTCOMES = /* @__PURE__ */ new Set(["published_pending_merge", "delivered"]);
|
|
264502
264504
|
function parseAllowedCardsEnv(env = process.env) {
|
|
@@ -264505,6 +264507,11 @@ function parseAllowedCardsEnv(env = process.env) {
|
|
|
264505
264507
|
const raw = env[GOAL_ALLOWED_CARDS_ENV] ?? "";
|
|
264506
264508
|
return new Set(raw.split(",").map((s) => s.trim()).filter((s) => s !== ""));
|
|
264507
264509
|
}
|
|
264510
|
+
function isGuidedRunOnce(allowed2, env = process.env) {
|
|
264511
|
+
if ((env[GOAL_GUIDED_ENV] ?? "").trim() !== "1")
|
|
264512
|
+
return false;
|
|
264513
|
+
return allowed2 !== void 0 && allowed2.size > 0;
|
|
264514
|
+
}
|
|
264508
264515
|
var OUT_OF_SCOPE_STATUS = "\u{1F6AB} Hold (outside goal scope)";
|
|
264509
264516
|
function scopeBacklogForAllowedCards(items, allowed2) {
|
|
264510
264517
|
if (allowed2 === void 0)
|
|
@@ -265428,6 +265435,7 @@ function parseOptions(args) {
|
|
|
265428
265435
|
let noTmux = false;
|
|
265429
265436
|
let attach = false;
|
|
265430
265437
|
let scopeSpecified = false;
|
|
265438
|
+
let cardsFlagSpecified = false;
|
|
265431
265439
|
let reviewMode = "auto";
|
|
265432
265440
|
let reviewModeSpecified = false;
|
|
265433
265441
|
for (let i = 0; i < args.length; i += 1) {
|
|
@@ -265497,11 +265505,13 @@ function parseOptions(args) {
|
|
|
265497
265505
|
}
|
|
265498
265506
|
if (arg === "--cards") {
|
|
265499
265507
|
cards.push(...parseCards(args[i + 1]));
|
|
265508
|
+
cardsFlagSpecified = true;
|
|
265500
265509
|
i += 1;
|
|
265501
265510
|
continue;
|
|
265502
265511
|
}
|
|
265503
265512
|
if (arg.startsWith("--cards=")) {
|
|
265504
265513
|
cards.push(...parseCards(arg.slice("--cards=".length)));
|
|
265514
|
+
cardsFlagSpecified = true;
|
|
265505
265515
|
continue;
|
|
265506
265516
|
}
|
|
265507
265517
|
if (!arg.startsWith("-"))
|
|
@@ -265517,6 +265527,7 @@ function parseOptions(args) {
|
|
|
265517
265527
|
attach,
|
|
265518
265528
|
scope,
|
|
265519
265529
|
scopeSpecified,
|
|
265530
|
+
cardsFlagSpecified,
|
|
265520
265531
|
reviewMode,
|
|
265521
265532
|
reviewModeSpecified,
|
|
265522
265533
|
...maxCycles !== void 0 ? { maxCycles } : {},
|
|
@@ -265526,6 +265537,9 @@ function parseOptions(args) {
|
|
|
265526
265537
|
function hasHelpArg2(args) {
|
|
265527
265538
|
return args.includes("--help") || args.includes("-h");
|
|
265528
265539
|
}
|
|
265540
|
+
function isGuidedRun(opts) {
|
|
265541
|
+
return opts.cardsFlagSpecified && opts.scopeSpecified && opts.scope.kind === "cards" && opts.scope.cards.length > 0;
|
|
265542
|
+
}
|
|
265529
265543
|
function loopGoHelp() {
|
|
265530
265544
|
return [
|
|
265531
265545
|
"Usage: roll loop go [--epic <name>|--cards <ids>|--all] [--for <duration>] [--max-cycles <n>] [--review <auto|hetero|self|off>] [--attach] [--no-tmux]",
|
|
@@ -265550,6 +265564,12 @@ function loopGoHelp() {
|
|
|
265550
265564
|
" The startup banner shows the EFFECTIVE scope; an inherited scope is flagged so a flagless go can never silently narrow. Pass --all to reset to the full backlog.",
|
|
265551
265565
|
" \u542F\u52A8\u6A2A\u5E45\u663E\u793A\u751F\u6548\u7684 scope\uFF1B\u6CBF\u7528\u7684 scope \u4F1A\u88AB\u6807\u6CE8\uFF0Cflagless go \u7EDD\u4E0D\u9759\u9ED8\u6536\u7A84\u3002\u7528 --all \u91CD\u7F6E\u56DE\u5168\u91CF\u3002",
|
|
265552
265566
|
"",
|
|
265567
|
+
"Guided one-shot while paused (FIX-1472):",
|
|
265568
|
+
" An explicit `roll loop go --cards X` runs the named card(s) even while `roll loop pause` is in effect \u2014 it is a supervisor action, not autonomous scheduling. It installs no scheduler and picks no other card; bound it with --max-cycles.",
|
|
265569
|
+
" \u663E\u5F0F\u7684 `roll loop go --cards X` \u5373\u4F7F\u5728 `roll loop pause` \u751F\u6548\u65F6\u4E5F\u4F1A\u6267\u884C\u6307\u5B9A\u5361\u2014\u2014\u8FD9\u662F Supervisor \u624B\u52A8\u52A8\u4F5C\uFF0C\u4E0D\u662F\u81EA\u4E3B\u6392\u7A0B\u3002\u5B83\u4E0D\u5B89\u88C5\u6392\u7A0B\u3001\u4E0D\u9009\u522B\u7684\u5361\uFF1B\u7528 --max-cycles \u9650\u5B9A\u6B21\u6570\u3002",
|
|
265570
|
+
" Pause still stops autonomous scheduling; only an explicitly-named --cards scope bypasses it (a flagless/inherited/--all/--epic go keeps honoring the pause).",
|
|
265571
|
+
" pause \u4ECD\u7136\u505C\u6B62\u81EA\u4E3B\u6392\u7A0B\uFF1B\u53EA\u6709\u672C\u6B21\u663E\u5F0F\u6307\u5B9A\u7684 --cards \u8303\u56F4\u624D\u4F1A\u7ED5\u8FC7\uFF08flagless/\u6CBF\u7528/--all/--epic \u7684 go \u4ECD\u9075\u5B88\u6682\u505C\uFF09\u3002",
|
|
265572
|
+
"",
|
|
265553
265573
|
"Progress guardrails (the loop stops on NO PROGRESS, not on cost):",
|
|
265554
265574
|
" productivity floor A cycle whose agent EXECUTED but produced 0 commits and no delivery is a `gave_up` terminal \u2014 alerted on the first occurrence (no streak).",
|
|
265555
265575
|
" dead-loop breaker A card is skipped after consecutive no-progress cycles; the whole goal STOPS after K consecutive no-progress cycles (a loud ALERT) \u2014 an unmergeable card can never spin forever.",
|
|
@@ -265673,6 +265693,20 @@ function followGoLiveFeed(projectPath3, rollBin4) {
|
|
|
265673
265693
|
watch.on("error", finish);
|
|
265674
265694
|
});
|
|
265675
265695
|
}
|
|
265696
|
+
function buildRunOnceChildEnv(input, base = process.env) {
|
|
265697
|
+
return {
|
|
265698
|
+
...base,
|
|
265699
|
+
ROLL_LOOP_GO_CHILD: "1",
|
|
265700
|
+
// FIX-1022: unattended go-driver child must never trigger the macOS
|
|
265701
|
+
// screencapture TCC prompt (isTTY is unreliable under PTY wrapping).
|
|
265702
|
+
ROLL_NO_SCREENCAP: base["ROLL_NO_SCREENCAP"] ?? "1",
|
|
265703
|
+
...input.allowedCards !== void 0 ? { [GOAL_ALLOWED_CARDS_ENV]: input.allowedCards.join(",") } : {},
|
|
265704
|
+
// FIX-1472: guided is set on EVERY call (never conditionally spread) so an
|
|
265705
|
+
// inherited ambient "1" cannot survive into an unguided child. Fail closed
|
|
265706
|
+
// to "0" — isGuidedRunOnce treats anything but "1" as not guided.
|
|
265707
|
+
[GOAL_GUIDED_ENV]: input.guided === true ? "1" : "0"
|
|
265708
|
+
};
|
|
265709
|
+
}
|
|
265676
265710
|
function realRunOnce(input) {
|
|
265677
265711
|
const bin = rollBin();
|
|
265678
265712
|
const cmd = bin.endsWith(".js") || bin.endsWith(".mjs") ? process.execPath : bin;
|
|
@@ -265681,14 +265715,7 @@ function realRunOnce(input) {
|
|
|
265681
265715
|
const child = spawn8(cmd, args, {
|
|
265682
265716
|
cwd: input.projectPath,
|
|
265683
265717
|
detached: true,
|
|
265684
|
-
env:
|
|
265685
|
-
...process.env,
|
|
265686
|
-
ROLL_LOOP_GO_CHILD: "1",
|
|
265687
|
-
// FIX-1022: unattended go-driver child must never trigger the macOS
|
|
265688
|
-
// screencapture TCC prompt (isTTY is unreliable under PTY wrapping).
|
|
265689
|
-
ROLL_NO_SCREENCAP: process.env["ROLL_NO_SCREENCAP"] ?? "1",
|
|
265690
|
-
...input.allowedCards !== void 0 ? { [GOAL_ALLOWED_CARDS_ENV]: input.allowedCards.join(",") } : {}
|
|
265691
|
-
},
|
|
265718
|
+
env: buildRunOnceChildEnv(input),
|
|
265692
265719
|
stdio: "inherit"
|
|
265693
265720
|
});
|
|
265694
265721
|
child.on("exit", (code) => resolve17(code ?? 1));
|
|
@@ -266587,11 +266614,12 @@ async function runGoWorker(id, opts, deps) {
|
|
|
266587
266614
|
progress = progressFromGoal(goal);
|
|
266588
266615
|
initialUsage = runSummaryFromGoal(goal);
|
|
266589
266616
|
const deadlineSec = timeboxDeadlineSec(goal, opts, startedSec);
|
|
266617
|
+
const guided = isGuidedRun(opts);
|
|
266590
266618
|
bus.appendEvent(evPath, { type: "goal:session_start", sessionId: sid, scope: goal.scope, ts: startedSec });
|
|
266591
266619
|
while (true) {
|
|
266592
266620
|
if (stopRequested)
|
|
266593
266621
|
break;
|
|
266594
|
-
if (existsSync64(pauseMarkerPath2(id.path, id.slug))) {
|
|
266622
|
+
if (!guided && existsSync64(pauseMarkerPath2(id.path, id.slug))) {
|
|
266595
266623
|
stopReason2 = "pause_marker";
|
|
266596
266624
|
break;
|
|
266597
266625
|
}
|
|
@@ -266647,10 +266675,10 @@ async function runGoWorker(id, opts, deps) {
|
|
|
266647
266675
|
}
|
|
266648
266676
|
if (stopRequested)
|
|
266649
266677
|
break;
|
|
266650
|
-
if (existsSync64(pauseMarkerPath2(id.path, id.slug)))
|
|
266678
|
+
if (!guided && existsSync64(pauseMarkerPath2(id.path, id.slug)))
|
|
266651
266679
|
continue;
|
|
266652
266680
|
const before = readRunSnapshot(runsPath2(id.path));
|
|
266653
|
-
await deps.runOnce({ projectPath: id.path, allowedCards });
|
|
266681
|
+
await deps.runOnce({ projectPath: id.path, allowedCards, ...guided ? { guided: true } : {} });
|
|
266654
266682
|
goal = updateUsage(id.path, goal, baseline, initialUsage, deps.nowIso());
|
|
266655
266683
|
writeGoal(gPath, goal);
|
|
266656
266684
|
const after = readRunSnapshot(runsPath2(id.path));
|
|
@@ -266707,7 +266735,7 @@ async function runGoWorker(id, opts, deps) {
|
|
|
266707
266735
|
stopReason2 = "safety_pause";
|
|
266708
266736
|
break;
|
|
266709
266737
|
}
|
|
266710
|
-
if (existsSync64(pauseMarkerPath2(id.path, id.slug))) {
|
|
266738
|
+
if (!guided && existsSync64(pauseMarkerPath2(id.path, id.slug))) {
|
|
266711
266739
|
stopReason2 = "pause_marker";
|
|
266712
266740
|
break;
|
|
266713
266741
|
}
|
|
@@ -271420,8 +271448,8 @@ function topBar(input) {
|
|
|
271420
271448
|
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
271449
|
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
271450
|
const active = input.machinePage === m7.key;
|
|
271423
|
-
const
|
|
271424
|
-
return
|
|
271451
|
+
const sep9 = i > 0 ? `<span style="color:#3a4360;flex:none;">\xB7</span>` : "";
|
|
271452
|
+
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
271453
|
}).join("") + `</nav>`;
|
|
271426
271454
|
const releaseTag = input.snapshot.release?.latestTag;
|
|
271427
271455
|
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 +277728,11 @@ function printMergeSummary(summary) {
|
|
|
277700
277728
|
function summaryAction(summary, file) {
|
|
277701
277729
|
for (let index = summary.length - 1; index >= 0; index -= 1) {
|
|
277702
277730
|
const entry = summary[index] ?? "";
|
|
277703
|
-
const
|
|
277704
|
-
if (
|
|
277731
|
+
const sep9 = entry.indexOf("|");
|
|
277732
|
+
if (sep9 < 0)
|
|
277705
277733
|
continue;
|
|
277706
|
-
if (entry.slice(
|
|
277707
|
-
return entry.slice(0,
|
|
277734
|
+
if (entry.slice(sep9 + 1) === file)
|
|
277735
|
+
return entry.slice(0, sep9);
|
|
277708
277736
|
}
|
|
277709
277737
|
return null;
|
|
277710
277738
|
}
|
|
@@ -278249,10 +278277,10 @@ function emitInitUi(projectDir, hasAgents, syncStatus, summary, shouldNudge, mod
|
|
|
278249
278277
|
lines3.push(" " + c(iconColor, icon) + " " + c(iconColor, fLabel, { bold: true }));
|
|
278250
278278
|
const nudgeMsg = shouldNudge ? renderDesignNudge(msgLang6())[0] ?? "" : "";
|
|
278251
278279
|
const nudgePair = shouldNudge && nudgeMsg !== "" ? (() => {
|
|
278252
|
-
const
|
|
278253
|
-
if (
|
|
278280
|
+
const sep9 = nudgeMsg.indexOf(" \u2014 ");
|
|
278281
|
+
if (sep9 < 0)
|
|
278254
278282
|
return [nudgeMsg, ""];
|
|
278255
|
-
return [nudgeMsg.slice(0,
|
|
278283
|
+
return [nudgeMsg.slice(0, sep9), nudgeMsg.slice(sep9 + 3)];
|
|
278256
278284
|
})() : void 0;
|
|
278257
278285
|
const nextItems = nextItemsOverride ?? (hasAgents ? [
|
|
278258
278286
|
...nudgePair ? [nudgePair] : [],
|
|
@@ -281224,7 +281252,7 @@ async function loopExhaustionSplitCommand(argv, deps = realExhaustionSplitDeps()
|
|
|
281224
281252
|
init_dist2();
|
|
281225
281253
|
init_dist();
|
|
281226
281254
|
init_dist3();
|
|
281227
|
-
import { appendFileSync as appendFileSync21, existsSync as
|
|
281255
|
+
import { appendFileSync as appendFileSync21, existsSync as existsSync135, mkdirSync as mkdirSync74, readdirSync as readdirSync49, readFileSync as readFileSync135, writeFileSync as writeFileSync72 } from "node:fs";
|
|
281228
281256
|
import { dirname as dirname74, join as join158 } from "node:path";
|
|
281229
281257
|
|
|
281230
281258
|
// packages/cli/dist/runner/executor.js
|
|
@@ -288367,7 +288395,7 @@ function applyCorrectionCircuitBreaker(projectPath3, slug3, eventsPath4, alertsP
|
|
|
288367
288395
|
init_dist2();
|
|
288368
288396
|
init_dist3();
|
|
288369
288397
|
import { execFileSync as execFileSync31 } from "node:child_process";
|
|
288370
|
-
import { readFileSync as readFileSync133 } from "node:fs";
|
|
288398
|
+
import { readFileSync as readFileSync133, readdirSync as readdirSync48, realpathSync as realpathSync20 } from "node:fs";
|
|
288371
288399
|
import { homedir as homedir33 } from "node:os";
|
|
288372
288400
|
import { basename as basename22, dirname as dirname72, join as join156, relative as relative13, resolve as resolve13 } from "node:path";
|
|
288373
288401
|
function git3(args, cwd) {
|
|
@@ -288437,6 +288465,60 @@ function extractCycleId(dirName) {
|
|
|
288437
288465
|
const m7 = /^(cycle-\d{8}-\d{6}-\d+)$/.exec(dirName);
|
|
288438
288466
|
return m7 ? m7[1] : void 0;
|
|
288439
288467
|
}
|
|
288468
|
+
var ORPHAN_DELIVERED_OUTCOMES = /* @__PURE__ */ new Set(["delivered", "merged"]);
|
|
288469
|
+
function realpathSafe2(p) {
|
|
288470
|
+
try {
|
|
288471
|
+
return realpathSync20(p);
|
|
288472
|
+
} catch {
|
|
288473
|
+
return p;
|
|
288474
|
+
}
|
|
288475
|
+
}
|
|
288476
|
+
function defaultReadDirNames(dir) {
|
|
288477
|
+
try {
|
|
288478
|
+
return readdirSync48(dir, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => e.name);
|
|
288479
|
+
} catch {
|
|
288480
|
+
return [];
|
|
288481
|
+
}
|
|
288482
|
+
}
|
|
288483
|
+
function scanOrphanLoopWorktrees(repoRoot3, registeredPaths, cycles, deps) {
|
|
288484
|
+
const worktreesDir = join156(repoRoot3, ".roll", "loop", "worktrees");
|
|
288485
|
+
const names = deps.readDir ? deps.readDir(worktreesDir) : defaultReadDirNames(worktreesDir);
|
|
288486
|
+
const out3 = [];
|
|
288487
|
+
for (const name of [...names].sort()) {
|
|
288488
|
+
const absPath = resolve13(join156(worktreesDir, name));
|
|
288489
|
+
if (registeredPaths.has(realpathSafe2(absPath)))
|
|
288490
|
+
continue;
|
|
288491
|
+
const cycleId = extractCycleId(name);
|
|
288492
|
+
const ce = cycleId ? cycles.get(cycleId) : void 0;
|
|
288493
|
+
const outcome = ce?.outcome;
|
|
288494
|
+
const active = isActiveCycle(cycleId, repoRoot3, deps);
|
|
288495
|
+
const rec = {
|
|
288496
|
+
path: absPath,
|
|
288497
|
+
owner: "loop",
|
|
288498
|
+
...cycleId ? { cycleId } : {},
|
|
288499
|
+
...ce?.storyId ? { storyId: ce.storyId } : {},
|
|
288500
|
+
...outcome ? { outcome } : {},
|
|
288501
|
+
dirtyTracked: "unknown",
|
|
288502
|
+
// no git metadata for a deregistered dir
|
|
288503
|
+
dirtyUntracked: "unknown",
|
|
288504
|
+
ahead: null,
|
|
288505
|
+
mergeEvidence: { kind: "none" },
|
|
288506
|
+
active,
|
|
288507
|
+
disposition: "preserved_orphan",
|
|
288508
|
+
reason: ""
|
|
288509
|
+
};
|
|
288510
|
+
if (active) {
|
|
288511
|
+
rec.reason = "orphan loop dir with an active cycle lock; never reclaimed";
|
|
288512
|
+
} else if (outcome && ORPHAN_DELIVERED_OUTCOMES.has(outcome)) {
|
|
288513
|
+
rec.disposition = "orphan_reclaimable";
|
|
288514
|
+
rec.reason = `orphan loop dir (deregistered from git); owning cycle outcome '${outcome}' is delivered \u2014 bounded reclaim`;
|
|
288515
|
+
} else {
|
|
288516
|
+
rec.reason = `orphan loop dir (deregistered from git); delivery not provable (cycle outcome '${outcome ?? "unknown"}') \u2014 preserved, reclaim manually after review`;
|
|
288517
|
+
}
|
|
288518
|
+
out3.push(rec);
|
|
288519
|
+
}
|
|
288520
|
+
return out3;
|
|
288521
|
+
}
|
|
288440
288522
|
function detectDirty(wtPath, deps) {
|
|
288441
288523
|
try {
|
|
288442
288524
|
const g = deps.git ?? git3;
|
|
@@ -288637,6 +288719,10 @@ function auditWorktrees(deps) {
|
|
|
288637
288719
|
baseRec.reason = disp.reason;
|
|
288638
288720
|
records.push(baseRec);
|
|
288639
288721
|
}
|
|
288722
|
+
const registeredLoopPaths = new Set(records.map((r) => realpathSafe2(resolve13(r.path))));
|
|
288723
|
+
for (const orphan of scanOrphanLoopWorktrees(repoRoot3, registeredLoopPaths, cycles, deps)) {
|
|
288724
|
+
records.push(orphan);
|
|
288725
|
+
}
|
|
288640
288726
|
let ephemeralBranches = [];
|
|
288641
288727
|
try {
|
|
288642
288728
|
const g = deps.git ?? git3;
|
|
@@ -288695,7 +288781,9 @@ function renderHuman(output) {
|
|
|
288695
288781
|
"preserved_unpublished",
|
|
288696
288782
|
"preserved_dirty_no_tcr",
|
|
288697
288783
|
"preserved_needs_review",
|
|
288784
|
+
"preserved_orphan",
|
|
288698
288785
|
"disposable_candidate",
|
|
288786
|
+
"orphan_reclaimable",
|
|
288699
288787
|
"external_unmanaged"
|
|
288700
288788
|
];
|
|
288701
288789
|
for (const disp of order) {
|
|
@@ -288767,12 +288855,24 @@ function worktreeAuditCommand(args, deps) {
|
|
|
288767
288855
|
init_dist2();
|
|
288768
288856
|
import { execFileSync as execFileSync32 } from "node:child_process";
|
|
288769
288857
|
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", "patch_equivalent"]);
|
|
288858
|
+
import { appendFileSync as appendFileSync20, existsSync as existsSync134, mkdirSync as mkdirSync73, rmSync as rmSync25 } from "node:fs";
|
|
288859
|
+
import { dirname as dirname73, join as join157, relative as relative14, resolve as resolve14, sep as sep8 } from "node:path";
|
|
288860
|
+
var MERGED_KINDS = /* @__PURE__ */ new Set(["ancestor", "patch_equivalent", "final_tree"]);
|
|
288773
288861
|
function isSafelyDisposable(rec) {
|
|
288774
288862
|
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
288863
|
}
|
|
288864
|
+
function isReclaimableOrphan(rec) {
|
|
288865
|
+
return rec.owner === "loop" && rec.active === false && rec.disposition === "orphan_reclaimable";
|
|
288866
|
+
}
|
|
288867
|
+
function isBoundedLoopWorktreeDir(repoRoot3, path) {
|
|
288868
|
+
const base = resolve14(join157(repoRoot3, ".roll", "loop", "worktrees"));
|
|
288869
|
+
const abs = resolve14(path);
|
|
288870
|
+
if (abs === base)
|
|
288871
|
+
return false;
|
|
288872
|
+
if (!abs.startsWith(base + sep8))
|
|
288873
|
+
return false;
|
|
288874
|
+
return dirname73(abs) === base;
|
|
288875
|
+
}
|
|
288776
288876
|
var PROTECTED_BRANCHES = /* @__PURE__ */ new Set(["main", "master", "HEAD"]);
|
|
288777
288877
|
function resolveStandaloneMergedBranches(audit, deps) {
|
|
288778
288878
|
const out3 = [];
|
|
@@ -288797,20 +288897,25 @@ function planWorktreeCleanup(audit, threshold, standaloneMergedBranches = []) {
|
|
|
288797
288897
|
const loopWorktrees = audit.records.filter((r) => r.owner === "loop");
|
|
288798
288898
|
const canaryTotal = audit.ephemeralBranches.length + loopWorktrees.length;
|
|
288799
288899
|
const excess = canaryTotal - threshold;
|
|
288800
|
-
const pool = audit.records.filter(isSafelyDisposable).sort((a, b) => a.path.localeCompare(b.path));
|
|
288900
|
+
const pool = audit.records.filter((r) => isSafelyDisposable(r) || isReclaimableOrphan(r)).sort((a, b) => a.path.localeCompare(b.path));
|
|
288801
288901
|
const branchPool = [...standaloneMergedBranches].sort((a, b) => a.branch.localeCompare(b.branch));
|
|
288802
288902
|
const excessN = excess > 0 ? excess : 0;
|
|
288803
288903
|
const chosen = pool.slice(0, Math.min(excessN, pool.length));
|
|
288804
288904
|
const remainingExcess = excessN - chosen.length;
|
|
288805
288905
|
const chosenBranches = branchPool.slice(0, Math.min(remainingExcess, branchPool.length));
|
|
288806
288906
|
const chosenPaths = new Set(chosen.map((r) => r.path));
|
|
288807
|
-
const candidates = chosen.map((r) =>
|
|
288808
|
-
|
|
288809
|
-
|
|
288810
|
-
|
|
288811
|
-
|
|
288812
|
-
|
|
288813
|
-
|
|
288907
|
+
const candidates = chosen.map((r) => {
|
|
288908
|
+
const orphan = isReclaimableOrphan(r);
|
|
288909
|
+
return {
|
|
288910
|
+
path: r.path,
|
|
288911
|
+
...r.cycleId ? { cycleId: r.cycleId } : {},
|
|
288912
|
+
...r.branch ? { branch: r.branch } : {},
|
|
288913
|
+
// An orphan has no registered HEAD; apply skips the head check for rm_dir.
|
|
288914
|
+
expectedHead: orphan ? "" : r.head,
|
|
288915
|
+
reason: orphan ? "orphan_reclaimable" : "disposable_candidate",
|
|
288916
|
+
reclaim: orphan ? "rm_dir" : "git_worktree"
|
|
288917
|
+
};
|
|
288918
|
+
});
|
|
288814
288919
|
const preserved = audit.records.filter((r) => !chosenPaths.has(r.path)).map((r) => ({ path: r.path, disposition: r.disposition, reason: r.reason }));
|
|
288815
288920
|
const countedWorktrees = loopWorktrees.map((r) => ({
|
|
288816
288921
|
path: r.path,
|
|
@@ -288829,9 +288934,12 @@ function planWorktreeCleanup(audit, threshold, standaloneMergedBranches = []) {
|
|
|
288829
288934
|
preserved
|
|
288830
288935
|
};
|
|
288831
288936
|
}
|
|
288832
|
-
function defaultRemoveBranch(repositoryRoot, branch) {
|
|
288937
|
+
function defaultRemoveBranch(repositoryRoot, branch, expectedSha) {
|
|
288938
|
+
if (!isFullGitOid(expectedSha)) {
|
|
288939
|
+
return { ok: false, detail: `refused: expected sha ${expectedSha} is not a full git OID` };
|
|
288940
|
+
}
|
|
288833
288941
|
try {
|
|
288834
|
-
execFileSync32("git", ["-C", repositoryRoot, "
|
|
288942
|
+
execFileSync32("git", ["-C", repositoryRoot, "update-ref", "-d", `refs/heads/${branch}`, expectedSha], {
|
|
288835
288943
|
encoding: "utf8",
|
|
288836
288944
|
stdio: ["ignore", "pipe", "pipe"]
|
|
288837
288945
|
});
|
|
@@ -288841,13 +288949,47 @@ function defaultRemoveBranch(repositoryRoot, branch) {
|
|
|
288841
288949
|
return { ok: true, detail: "" };
|
|
288842
288950
|
}
|
|
288843
288951
|
function defaultRemoveWorktree(repositoryRoot, path) {
|
|
288952
|
+
let gitErr = "";
|
|
288844
288953
|
try {
|
|
288845
288954
|
execFileSync32("git", ["-C", repositoryRoot, "worktree", "remove", "--force", path], {
|
|
288846
288955
|
encoding: "utf8",
|
|
288847
288956
|
stdio: ["ignore", "pipe", "pipe"]
|
|
288848
288957
|
});
|
|
288849
288958
|
} catch (err16) {
|
|
288850
|
-
|
|
288959
|
+
gitErr = err16 instanceof Error ? err16.message : String(err16);
|
|
288960
|
+
}
|
|
288961
|
+
if (existsSync134(path)) {
|
|
288962
|
+
if (!isBoundedLoopWorktreeDir(repositoryRoot, path)) {
|
|
288963
|
+
return { ok: false, detail: gitErr || `refused: ${path} is outside .roll/loop/worktrees` };
|
|
288964
|
+
}
|
|
288965
|
+
try {
|
|
288966
|
+
rmSync25(path, { recursive: true, force: true });
|
|
288967
|
+
} catch (e) {
|
|
288968
|
+
return { ok: false, detail: gitErr || (e instanceof Error ? e.message : String(e)) };
|
|
288969
|
+
}
|
|
288970
|
+
} else if (gitErr) {
|
|
288971
|
+
return { ok: false, detail: gitErr };
|
|
288972
|
+
}
|
|
288973
|
+
try {
|
|
288974
|
+
execFileSync32("git", ["-C", repositoryRoot, "worktree", "prune", "--expire", "now"], {
|
|
288975
|
+
encoding: "utf8",
|
|
288976
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
288977
|
+
});
|
|
288978
|
+
} catch {
|
|
288979
|
+
}
|
|
288980
|
+
return { ok: true, detail: "" };
|
|
288981
|
+
}
|
|
288982
|
+
function defaultReclaimOrphanDir(repositoryRoot, path) {
|
|
288983
|
+
if (!isBoundedLoopWorktreeDir(repositoryRoot, path)) {
|
|
288984
|
+
return { ok: false, detail: `refused: ${path} is outside .roll/loop/worktrees` };
|
|
288985
|
+
}
|
|
288986
|
+
if (!existsSync134(path)) {
|
|
288987
|
+
return { ok: false, detail: "missing: orphan dir already gone" };
|
|
288988
|
+
}
|
|
288989
|
+
try {
|
|
288990
|
+
rmSync25(path, { recursive: true, force: true });
|
|
288991
|
+
} catch (e) {
|
|
288992
|
+
return { ok: false, detail: e instanceof Error ? e.message : String(e) };
|
|
288851
288993
|
}
|
|
288852
288994
|
try {
|
|
288853
288995
|
execFileSync32("git", ["-C", repositoryRoot, "worktree", "prune", "--expire", "now"], {
|
|
@@ -288862,6 +289004,7 @@ async function applyWorktreeCleanup(plan, options) {
|
|
|
288862
289004
|
const repositoryRoot = resolve14(options.repositoryRoot);
|
|
288863
289005
|
const auditFn = options.audit ?? (() => auditWorktrees({ repoRoot: repositoryRoot, home: homedir34() }));
|
|
288864
289006
|
const removeFn = options.removeWorktree ?? defaultRemoveWorktree;
|
|
289007
|
+
const reclaimOrphanFn = options.reclaimOrphanDir ?? defaultReclaimOrphanDir;
|
|
288865
289008
|
const removeBranchFn = options.removeBranch ?? defaultRemoveBranch;
|
|
288866
289009
|
const emit4 = options.emit ?? (() => {
|
|
288867
289010
|
});
|
|
@@ -288877,17 +289020,47 @@ async function applyWorktreeCleanup(plan, options) {
|
|
|
288877
289020
|
emit4({ type: "worktree_cleanup_refused", path: candidate.path, reason, ts: nowMs2() });
|
|
288878
289021
|
};
|
|
288879
289022
|
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"}`);
|
|
289023
|
+
refuse(candidate.reclaim === "rm_dir" ? "missing: orphan dir no longer present (already reclaimed)" : "missing: worktree no longer registered (already removed or pruned)");
|
|
288885
289024
|
continue;
|
|
288886
289025
|
}
|
|
288887
289026
|
if (rec.active) {
|
|
288888
289027
|
refuse("active: worktree activated concurrently (fresh lock/heartbeat)");
|
|
288889
289028
|
continue;
|
|
288890
289029
|
}
|
|
289030
|
+
if (candidate.reclaim === "rm_dir") {
|
|
289031
|
+
if (!isReclaimableOrphan(rec)) {
|
|
289032
|
+
refuse(`disposition: fresh audit reports '${rec.disposition}' (orphan no longer provably delivered)`);
|
|
289033
|
+
continue;
|
|
289034
|
+
}
|
|
289035
|
+
const removal2 = {
|
|
289036
|
+
path: rec.path,
|
|
289037
|
+
expectedHead: "",
|
|
289038
|
+
reclaim: "rm_dir",
|
|
289039
|
+
...rec.cycleId ? { cycleId: rec.cycleId } : {}
|
|
289040
|
+
};
|
|
289041
|
+
if (options.dryRun) {
|
|
289042
|
+
removed.push(removal2);
|
|
289043
|
+
continue;
|
|
289044
|
+
}
|
|
289045
|
+
const result3 = reclaimOrphanFn(repositoryRoot, rec.path);
|
|
289046
|
+
if (!result3.ok) {
|
|
289047
|
+
refuse(`reclaim-failed: ${result3.detail}`);
|
|
289048
|
+
continue;
|
|
289049
|
+
}
|
|
289050
|
+
removed.push(removal2);
|
|
289051
|
+
emit4({
|
|
289052
|
+
type: "worktree_cleanup_applied",
|
|
289053
|
+
path: rec.path,
|
|
289054
|
+
expectedHead: "",
|
|
289055
|
+
...rec.cycleId ? { cycleId: rec.cycleId } : {},
|
|
289056
|
+
ts: nowMs2()
|
|
289057
|
+
});
|
|
289058
|
+
continue;
|
|
289059
|
+
}
|
|
289060
|
+
if (rec.head !== candidate.expectedHead) {
|
|
289061
|
+
refuse(`changed-head: expected ${candidate.expectedHead}, found ${rec.head ?? "none"}`);
|
|
289062
|
+
continue;
|
|
289063
|
+
}
|
|
288891
289064
|
if (rec.dirtyTracked === true) {
|
|
288892
289065
|
refuse("dirty: tracked changes appeared after planning");
|
|
288893
289066
|
continue;
|
|
@@ -288903,6 +289076,7 @@ async function applyWorktreeCleanup(plan, options) {
|
|
|
288903
289076
|
const removal = {
|
|
288904
289077
|
path: rec.path,
|
|
288905
289078
|
expectedHead: candidate.expectedHead,
|
|
289079
|
+
reclaim: "git_worktree",
|
|
288906
289080
|
...rec.branch ? { branch: rec.branch } : {},
|
|
288907
289081
|
...rec.cycleId ? { cycleId: rec.cycleId } : {}
|
|
288908
289082
|
};
|
|
@@ -288968,7 +289142,7 @@ async function applyWorktreeCleanup(plan, options) {
|
|
|
288968
289142
|
branchesRemoved.push(removalB);
|
|
288969
289143
|
continue;
|
|
288970
289144
|
}
|
|
288971
|
-
const r = removeBranchFn(repositoryRoot, bc.branch);
|
|
289145
|
+
const r = removeBranchFn(repositoryRoot, bc.branch, bc.expectedSha);
|
|
288972
289146
|
if (!r.ok) {
|
|
288973
289147
|
refuseB(`delete-failed: ${r.detail}`);
|
|
288974
289148
|
continue;
|
|
@@ -289058,12 +289232,22 @@ function renderPlanHuman(plan, mode) {
|
|
|
289058
289232
|
lines3.push(`minimal candidate set (${plan.canaryTotal} \u2192 ${plan.projectedTotal})`);
|
|
289059
289233
|
for (const c2 of plan.candidates) {
|
|
289060
289234
|
const tags = [c2.branch, c2.cycleId].filter(Boolean).join(" ");
|
|
289061
|
-
|
|
289235
|
+
const label5 = c2.reclaim === "rm_dir" ? "orphan_reclaimable \xB7 bounded rm" : "disposable_candidate";
|
|
289236
|
+
const kind = c2.reclaim === "rm_dir" ? "orphan " : "worktree";
|
|
289237
|
+
lines3.push(` ${kind} ${rel(c2.path)}${tags ? " " + tags : ""} [${label5}]`);
|
|
289062
289238
|
}
|
|
289063
289239
|
for (const b of plan.branchCandidates) {
|
|
289064
289240
|
lines3.push(` branch ${b.branch} ${b.expectedSha.slice(0, 9)} [merged: ${b.mergeKind}]`);
|
|
289065
289241
|
}
|
|
289066
289242
|
lines3.push("");
|
|
289243
|
+
const preservedOrphans = plan.preserved.filter((p) => p.disposition === "preserved_orphan");
|
|
289244
|
+
if (preservedOrphans.length > 0) {
|
|
289245
|
+
lines3.push(`preserved orphan dirs (${preservedOrphans.length}) \u2014 visible + counted, never auto-deleted`);
|
|
289246
|
+
for (const p of preservedOrphans)
|
|
289247
|
+
lines3.push(` ${rel(p.path)} \u2014 ${p.reason}`);
|
|
289248
|
+
lines3.push(" Reclaim one after review with: roll worktree cleanup --reclaim-orphan <path>");
|
|
289249
|
+
lines3.push("");
|
|
289250
|
+
}
|
|
289067
289251
|
if (mode === "dry-run") {
|
|
289068
289252
|
lines3.push("Dry run \u2014 no git state changed.");
|
|
289069
289253
|
lines3.push("Apply the audited set with: roll worktree cleanup --apply");
|
|
@@ -289104,7 +289288,7 @@ function renderResultHuman(result2) {
|
|
|
289104
289288
|
}
|
|
289105
289289
|
return lines3.join("\n").trimEnd() + "\n";
|
|
289106
289290
|
}
|
|
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
|
|
289291
|
+
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 delivered and attached to no worktree. Delivery\n is PROVEN by one of: every commit is an ancestor of the integration branch;\n `git cherry` shows every commit already has an equivalent patch upstream; or\n (FIX-1471, squash merges) the branch tip tree is byte-identical to the merge\n commit of the branch's OWN merged GitHub PR (matched by exact head ref) AND\n that merge commit is an ancestor of the integration branch. A merged PR alone\n is NEVER sufficient, and no arbitrary same-tree commit on the integration\n branch is ever used. Never a path/ref that is merely old or counted, and never\n a preserved (unpublished / dirty / active / external / current / protected /\n 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
289292
|
function resolveThreshold() {
|
|
289109
289293
|
const parsed = parseInt(process.env["ROLL_BRANCH_CANARY_MAX"] ?? "", 10);
|
|
289110
289294
|
return Number.isFinite(parsed) && parsed > 0 ? parsed : DEFAULT_BRANCH_CANARY_MAX;
|
|
@@ -289112,20 +289296,41 @@ function resolveThreshold() {
|
|
|
289112
289296
|
function gitCap(repoRoot3, args) {
|
|
289113
289297
|
return execFileSync32("git", ["-C", repoRoot3, ...args], { encoding: "utf8", stdio: ["ignore", "pipe", "pipe"] });
|
|
289114
289298
|
}
|
|
289115
|
-
function classifyBranchMerge(branch, integrationBranch, git5) {
|
|
289299
|
+
function classifyBranchMerge(branch, integrationBranch, git5, prMergeCommit) {
|
|
289116
289300
|
if (git5(["merge-base", "--is-ancestor", branch, integrationBranch]).ok)
|
|
289117
289301
|
return "ancestor";
|
|
289118
289302
|
const cherry = git5(["cherry", integrationBranch, branch]);
|
|
289119
|
-
if (
|
|
289303
|
+
if (cherry.ok) {
|
|
289304
|
+
const lines3 = cherry.stdout.split("\n").map((l) => l.trim()).filter((l) => l !== "");
|
|
289305
|
+
if (lines3.length > 0 && lines3.every((l) => l.startsWith("-")))
|
|
289306
|
+
return "patch_equivalent";
|
|
289307
|
+
}
|
|
289308
|
+
return classifyFinalTreeDelivery(branch, integrationBranch, git5, prMergeCommit);
|
|
289309
|
+
}
|
|
289310
|
+
function classifyFinalTreeDelivery(branch, integrationBranch, git5, prMergeCommit) {
|
|
289311
|
+
const rawMerge = prMergeCommit(branch);
|
|
289312
|
+
if (rawMerge === null)
|
|
289120
289313
|
return null;
|
|
289121
|
-
const
|
|
289122
|
-
if (
|
|
289314
|
+
const merge = rawMerge.trim();
|
|
289315
|
+
if (!isFullGitOid(merge))
|
|
289123
289316
|
return null;
|
|
289124
|
-
if (
|
|
289317
|
+
if (!git5(["merge-base", "--is-ancestor", merge, integrationBranch]).ok)
|
|
289125
289318
|
return null;
|
|
289126
|
-
|
|
289127
|
-
|
|
289128
|
-
|
|
289319
|
+
const branchTreeProbe = git5(["rev-parse", `${branch}^{tree}`]);
|
|
289320
|
+
if (!branchTreeProbe.ok)
|
|
289321
|
+
return null;
|
|
289322
|
+
const branchTree = branchTreeProbe.stdout.trim();
|
|
289323
|
+
if (branchTree === "")
|
|
289324
|
+
return null;
|
|
289325
|
+
const mergeTreeProbe = git5(["rev-parse", `${merge}^{tree}`]);
|
|
289326
|
+
if (!mergeTreeProbe.ok)
|
|
289327
|
+
return null;
|
|
289328
|
+
const mergeTree = mergeTreeProbe.stdout.trim();
|
|
289329
|
+
if (mergeTree === "")
|
|
289330
|
+
return null;
|
|
289331
|
+
if (branchTree !== mergeTree)
|
|
289332
|
+
return null;
|
|
289333
|
+
return "final_tree";
|
|
289129
289334
|
}
|
|
289130
289335
|
function buildStandaloneBranchDeps(repoRoot3, audit, integrationBranch) {
|
|
289131
289336
|
const attachedBranches = new Set(audit.records.filter((r) => r.owner === "loop" && typeof r.branch === "string" && r.branch !== "").map((r) => r.branch.replace(/^refs\/heads\//, "")));
|
|
@@ -289145,11 +289350,12 @@ function buildStandaloneBranchDeps(repoRoot3, audit, integrationBranch) {
|
|
|
289145
289350
|
return null;
|
|
289146
289351
|
}
|
|
289147
289352
|
},
|
|
289148
|
-
// FIX-1458 (#1465): delivery is proven
|
|
289149
|
-
//
|
|
289150
|
-
//
|
|
289151
|
-
//
|
|
289152
|
-
//
|
|
289353
|
+
// FIX-1458 (#1465), FIX-1471: delivery is proven by fresh git patch evidence
|
|
289354
|
+
// (ancestor OR `git cherry` patch-equivalence) OR, for the squash-merge case,
|
|
289355
|
+
// by the branch tip tree matching the merge commit of the branch's OWN merged
|
|
289356
|
+
// GitHub PR (an ancestor of integration). A merged PR label alone never
|
|
289357
|
+
// authorizes deletion, and no arbitrary same-tree integration commit is ever
|
|
289358
|
+
// consulted — that would silently discard unique commits (US-ORG-003/007/004).
|
|
289153
289359
|
branchMerge: (branch) => classifyBranchMerge(branch, integrationBranch, (args) => {
|
|
289154
289360
|
try {
|
|
289155
289361
|
return { ok: true, stdout: gitCap(repoRoot3, args) };
|
|
@@ -289157,9 +289363,39 @@ function buildStandaloneBranchDeps(repoRoot3, audit, integrationBranch) {
|
|
|
289157
289363
|
const stdout = typeof err16.stdout === "string" ? err16.stdout : "";
|
|
289158
289364
|
return { ok: false, stdout };
|
|
289159
289365
|
}
|
|
289160
|
-
})
|
|
289366
|
+
}, (b) => ghMergedPrMergeCommit(repoRoot3, b))
|
|
289161
289367
|
};
|
|
289162
289368
|
}
|
|
289369
|
+
function isFullGitOid(value) {
|
|
289370
|
+
return /^[0-9a-f]{40}$/.test(value) || /^[0-9a-f]{64}$/.test(value);
|
|
289371
|
+
}
|
|
289372
|
+
function parseMergedPrMergeCommit(raw, branch) {
|
|
289373
|
+
let j;
|
|
289374
|
+
try {
|
|
289375
|
+
j = JSON.parse(raw);
|
|
289376
|
+
} catch {
|
|
289377
|
+
return null;
|
|
289378
|
+
}
|
|
289379
|
+
if (j.state !== "MERGED")
|
|
289380
|
+
return null;
|
|
289381
|
+
if (typeof j.mergedAt !== "string" || j.mergedAt === "")
|
|
289382
|
+
return null;
|
|
289383
|
+
if (typeof j.headRefName !== "string" || j.headRefName !== branch)
|
|
289384
|
+
return null;
|
|
289385
|
+
const oid = j.mergeCommit?.oid;
|
|
289386
|
+
if (typeof oid !== "string" || !isFullGitOid(oid))
|
|
289387
|
+
return null;
|
|
289388
|
+
return oid;
|
|
289389
|
+
}
|
|
289390
|
+
function ghMergedPrMergeCommit(repoRoot3, branch) {
|
|
289391
|
+
let out3;
|
|
289392
|
+
try {
|
|
289393
|
+
out3 = execFileSync32("gh", ["pr", "view", branch, "--json", "state,mergedAt,mergeCommit,headRefName"], { cwd: repoRoot3, encoding: "utf8", stdio: ["ignore", "pipe", "pipe"] });
|
|
289394
|
+
} catch {
|
|
289395
|
+
return null;
|
|
289396
|
+
}
|
|
289397
|
+
return parseMergedPrMergeCommit(out3, branch);
|
|
289398
|
+
}
|
|
289163
289399
|
function resolveIntegrationForCleanup(repoRoot3) {
|
|
289164
289400
|
try {
|
|
289165
289401
|
const head = gitCap(repoRoot3, ["symbolic-ref", "--quiet", "--short", "refs/remotes/origin/HEAD"]).trim();
|
|
@@ -289189,10 +289425,57 @@ async function worktreeCleanupCommand(args, deps) {
|
|
|
289189
289425
|
home: deps?.home ?? homedir34(),
|
|
289190
289426
|
git: deps?.git,
|
|
289191
289427
|
readFile: deps?.readFile,
|
|
289428
|
+
readDir: deps?.readDir,
|
|
289192
289429
|
nowISO: deps?.nowISO,
|
|
289193
289430
|
nowSec: deps?.nowSec,
|
|
289194
289431
|
integrationBranch: deps?.integrationBranch
|
|
289195
289432
|
};
|
|
289433
|
+
const reclaimIdx = args.indexOf("--reclaim-orphan");
|
|
289434
|
+
if (reclaimIdx >= 0) {
|
|
289435
|
+
const named2 = args[reclaimIdx + 1];
|
|
289436
|
+
if (!named2) {
|
|
289437
|
+
process.stderr.write("roll worktree cleanup: --reclaim-orphan requires a <path>.\n");
|
|
289438
|
+
return 2;
|
|
289439
|
+
}
|
|
289440
|
+
const fresh = auditWorktrees(fullDeps);
|
|
289441
|
+
const target = resolve14(named2);
|
|
289442
|
+
const rec = fresh.records.find((r2) => resolve14(r2.path) === target);
|
|
289443
|
+
if (!rec) {
|
|
289444
|
+
process.stderr.write(`refused: ${named2} is not in the worktree audit.
|
|
289445
|
+
`);
|
|
289446
|
+
return 2;
|
|
289447
|
+
}
|
|
289448
|
+
if (rec.owner !== "loop") {
|
|
289449
|
+
process.stderr.write(`refused: ${named2} is not a loop worktree (owner=${rec.owner}).
|
|
289450
|
+
`);
|
|
289451
|
+
return 2;
|
|
289452
|
+
}
|
|
289453
|
+
if (rec.active) {
|
|
289454
|
+
process.stderr.write(`refused: ${named2} has an active cycle lock.
|
|
289455
|
+
`);
|
|
289456
|
+
return 2;
|
|
289457
|
+
}
|
|
289458
|
+
if (rec.disposition !== "orphan_reclaimable" && rec.disposition !== "preserved_orphan") {
|
|
289459
|
+
process.stderr.write(`refused: ${named2} audits as '${rec.disposition}', not an orphan dir \u2014 use --apply for registered worktrees.
|
|
289460
|
+
`);
|
|
289461
|
+
return 2;
|
|
289462
|
+
}
|
|
289463
|
+
if (!isBoundedLoopWorktreeDir(repoRoot3, rec.path)) {
|
|
289464
|
+
process.stderr.write(`refused: ${named2} is outside .roll/loop/worktrees.
|
|
289465
|
+
`);
|
|
289466
|
+
return 2;
|
|
289467
|
+
}
|
|
289468
|
+
const reclaimFn = deps?.reclaimOrphanDir ?? defaultReclaimOrphanDir;
|
|
289469
|
+
const r = reclaimFn(repoRoot3, rec.path);
|
|
289470
|
+
if (!r.ok) {
|
|
289471
|
+
process.stderr.write(`reclaim-failed: ${r.detail}
|
|
289472
|
+
`);
|
|
289473
|
+
return 1;
|
|
289474
|
+
}
|
|
289475
|
+
process.stdout.write(`reclaimed orphan dir: ${rec.path}
|
|
289476
|
+
`);
|
|
289477
|
+
return 0;
|
|
289478
|
+
}
|
|
289196
289479
|
const threshold = resolveThreshold();
|
|
289197
289480
|
const integrationBranch = fullDeps.integrationBranch ?? resolveIntegrationForCleanup(repoRoot3);
|
|
289198
289481
|
const auditNow = auditWorktrees(fullDeps);
|
|
@@ -289330,13 +289613,13 @@ function announceReport(projectPath3, slug3, storyId, opener = (p) => {
|
|
|
289330
289613
|
const latest = join158(cardArchiveDir(projectPath3, storyId), "latest");
|
|
289331
289614
|
const review = join158(latest, reviewFileName(storyId));
|
|
289332
289615
|
const legacyReport = join158(latest, reportFileName(storyId));
|
|
289333
|
-
const report =
|
|
289616
|
+
const report = existsSync135(review) ? review : existsSync135(legacyReport) ? legacyReport : null;
|
|
289334
289617
|
if (report === null)
|
|
289335
289618
|
return null;
|
|
289336
289619
|
const label5 = currentLang() === "zh" ? "\u9A8C\u6536 Review Page" : "Acceptance Review Page";
|
|
289337
289620
|
process.stdout.write(`${label5}: ${report}
|
|
289338
289621
|
`);
|
|
289339
|
-
const muted =
|
|
289622
|
+
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}`));
|
|
289340
289623
|
if (!muted)
|
|
289341
289624
|
opener(report);
|
|
289342
289625
|
return report;
|
|
@@ -289360,7 +289643,7 @@ function cycleSignalTeardown(paths, cycleId, branch, sig, deps = {}) {
|
|
|
289360
289643
|
}
|
|
289361
289644
|
let owned = false;
|
|
289362
289645
|
try {
|
|
289363
|
-
owned =
|
|
289646
|
+
owned = existsSync135(paths.lockPath) && readLockOwner(paths.lockPath)?.pid === pid;
|
|
289364
289647
|
} catch {
|
|
289365
289648
|
owned = false;
|
|
289366
289649
|
}
|
|
@@ -289505,7 +289788,7 @@ function incrementConsecutiveFails(projectPath3, slug3, alertsPath, eventsPath4,
|
|
|
289505
289788
|
const counterFile = join158(rt, "consecutive-fails");
|
|
289506
289789
|
let count = 0;
|
|
289507
289790
|
try {
|
|
289508
|
-
if (
|
|
289791
|
+
if (existsSync135(counterFile)) {
|
|
289509
289792
|
count = parseInt(readFileSync135(counterFile, "utf8").trim(), 10) || 0;
|
|
289510
289793
|
}
|
|
289511
289794
|
} catch {
|
|
@@ -289519,7 +289802,7 @@ function incrementConsecutiveFails(projectPath3, slug3, alertsPath, eventsPath4,
|
|
|
289519
289802
|
if (count < threshold)
|
|
289520
289803
|
return;
|
|
289521
289804
|
const pauseMarker = join158(projectPath3, ".roll", "loop", `PAUSE-${slug3}`);
|
|
289522
|
-
if (
|
|
289805
|
+
if (existsSync135(pauseMarker))
|
|
289523
289806
|
return;
|
|
289524
289807
|
const alertMsg = `# ALERT \u2014 loop auto-paused after ${count} consecutive failures
|
|
289525
289808
|
|
|
@@ -289555,7 +289838,7 @@ loop run-once: \u8FDE\u7EED ${count} \u6B21\u5931\u8D25\u540E\u81EA\u52A8\u6682\
|
|
|
289555
289838
|
}
|
|
289556
289839
|
function writeRootCausePause(projectPath3, slug3, alertsPath, eventsPath4, cycleId, attribution, count, snapshotPath2) {
|
|
289557
289840
|
const pauseMarker = join158(projectPath3, ".roll", "loop", `PAUSE-${slug3}`);
|
|
289558
|
-
if (
|
|
289841
|
+
if (existsSync135(pauseMarker))
|
|
289559
289842
|
return;
|
|
289560
289843
|
const playbook = playbookForFailure(attribution.failureClass, attribution.rootCauseKey);
|
|
289561
289844
|
const alertMsg = `# ALERT \u2014 loop auto-paused on ${attribution.failureClass} failure
|
|
@@ -289617,7 +289900,7 @@ function handleNonCardFailure(projectPath3, slug3, alertsPath, eventsPath4, runt
|
|
|
289617
289900
|
function readFailurePauseThreshold(projectPath3) {
|
|
289618
289901
|
try {
|
|
289619
289902
|
const policy = join158(projectPath3, ".roll", "policy.yaml");
|
|
289620
|
-
if (!
|
|
289903
|
+
if (!existsSync135(policy))
|
|
289621
289904
|
return PAUSE_THRESHOLD;
|
|
289622
289905
|
return parsePolicy(readFileSync135(policy, "utf8")).loopSafety.maxConsecutiveFailures;
|
|
289623
289906
|
} catch {
|
|
@@ -289647,7 +289930,7 @@ function incrementConsecutiveIdle(projectPath3, slug3) {
|
|
|
289647
289930
|
const file = idleCounterPath(projectPath3, slug3);
|
|
289648
289931
|
let count = 0;
|
|
289649
289932
|
try {
|
|
289650
|
-
if (
|
|
289933
|
+
if (existsSync135(file)) {
|
|
289651
289934
|
count = parseInt(readFileSync135(file, "utf8").trim(), 10) || 0;
|
|
289652
289935
|
}
|
|
289653
289936
|
} catch {
|
|
@@ -289717,7 +290000,7 @@ function readExternalBlock(eventsPath4, cycleId) {
|
|
|
289717
290000
|
const authDetails = [];
|
|
289718
290001
|
const networkDetails = [];
|
|
289719
290002
|
try {
|
|
289720
|
-
if (!
|
|
290003
|
+
if (!existsSync135(eventsPath4))
|
|
289721
290004
|
return null;
|
|
289722
290005
|
for (const line of readFileSync135(eventsPath4, "utf8").split("\n")) {
|
|
289723
290006
|
if (line.trim() === "" || !line.includes("agent:blocked"))
|
|
@@ -289782,7 +290065,7 @@ function writeReviewerBlockedAlert(projectPath3, slug3, alertsPath, eventsPath4,
|
|
|
289782
290065
|
}
|
|
289783
290066
|
if (block.cause === "auth") {
|
|
289784
290067
|
const pauseMarker = join158(projectPath3, ".roll", "loop", `PAUSE-${slug3}`);
|
|
289785
|
-
if (!
|
|
290068
|
+
if (!existsSync135(pauseMarker)) {
|
|
289786
290069
|
try {
|
|
289787
290070
|
writeFileSync72(pauseMarker, msg7, "utf8");
|
|
289788
290071
|
bus.appendEvent(eventsPath4, { type: "policy:safety_pause", loop: "ci", reason: `agent auth block: ${agents}`, ts: ts2 });
|
|
@@ -289810,7 +290093,7 @@ function buildLoopRouteDeps(projectPath3) {
|
|
|
289810
290093
|
}
|
|
289811
290094
|
return readRouteSlot(text2, slot);
|
|
289812
290095
|
} catch {
|
|
289813
|
-
if (
|
|
290096
|
+
if (existsSync135(agentsYaml)) {
|
|
289814
290097
|
throw new Error("legacy agent configuration is unsupported; run `roll agent migrate`");
|
|
289815
290098
|
}
|
|
289816
290099
|
return void 0;
|
|
@@ -289911,7 +290194,7 @@ async function loopRunOnceCommand(args) {
|
|
|
289911
290194
|
removePendingPublish(rt, storyId);
|
|
289912
290195
|
}
|
|
289913
290196
|
}
|
|
289914
|
-
if (isLoopPaused(id.path, id.slug)) {
|
|
290197
|
+
if (isLoopPaused(id.path, id.slug) && !isGuidedRunOnce(allowedCards)) {
|
|
289915
290198
|
const lang10 = resolveLang({
|
|
289916
290199
|
rollLang: process.env["ROLL_LANG"],
|
|
289917
290200
|
lcAll: process.env["LC_ALL"],
|
|
@@ -290088,7 +290371,7 @@ loop run-once: \u8BC4\u5BA1\u5224\u5B9A ${resizeStory} \u8303\u56F4\u8FC7\u5927(
|
|
|
290088
290371
|
slug: id.slug,
|
|
290089
290372
|
count: idleCount,
|
|
290090
290373
|
resolveState: () => resolveLoopRunState(id.path, id.slug),
|
|
290091
|
-
readBacklog: () =>
|
|
290374
|
+
readBacklog: () => existsSync135(backlogFile) ? readFileSync135(backlogFile, "utf8") : "",
|
|
290092
290375
|
scheduler: createScheduler(process.platform, { uid: process.getuid?.() ?? 0 }),
|
|
290093
290376
|
loopLabel: launchdLabel("loop", id.slug),
|
|
290094
290377
|
now: () => (/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -290277,7 +290560,7 @@ async function isOffline(resolve17 = (h) => lookup2(h)) {
|
|
|
290277
290560
|
}
|
|
290278
290561
|
}
|
|
290279
290562
|
function isLoopPaused(projectPath3, slug3) {
|
|
290280
|
-
return
|
|
290563
|
+
return existsSync135(join158(projectPath3, ".roll", "loop", `PAUSE-${slug3}`));
|
|
290281
290564
|
}
|
|
290282
290565
|
function branchCanaryTrips(projectPath3, slug3, rt, alertsPath) {
|
|
290283
290566
|
let ephemeralBranchCount = 0;
|
|
@@ -290288,7 +290571,7 @@ function branchCanaryTrips(projectPath3, slug3, rt, alertsPath) {
|
|
|
290288
290571
|
}
|
|
290289
290572
|
let worktreeCount = 0;
|
|
290290
290573
|
try {
|
|
290291
|
-
worktreeCount =
|
|
290574
|
+
worktreeCount = readdirSync49(join158(rt, "worktrees"), { withFileTypes: true }).filter((e) => e.isDirectory()).length;
|
|
290292
290575
|
} catch {
|
|
290293
290576
|
}
|
|
290294
290577
|
const parsed = parseInt(process.env["ROLL_BRANCH_CANARY_MAX"] ?? "", 10);
|
|
@@ -290396,7 +290679,7 @@ function checkCoreWorktreeContamination(cwd) {
|
|
|
290396
290679
|
// packages/cli/dist/commands/offboard.js
|
|
290397
290680
|
init_dist();
|
|
290398
290681
|
import { spawnSync as spawnSync16 } from "node:child_process";
|
|
290399
|
-
import { existsSync as
|
|
290682
|
+
import { existsSync as existsSync136, readFileSync as readFileSync136, realpathSync as realpathSync21, renameSync as renameSync29, rmSync as rmSync26, writeFileSync as writeFileSync73 } from "node:fs";
|
|
290400
290683
|
import { homedir as homedir35 } from "node:os";
|
|
290401
290684
|
import { join as join159, resolve as resolve15 } from "node:path";
|
|
290402
290685
|
function pal4() {
|
|
@@ -290497,7 +290780,7 @@ function writeFileAtomic2(path, text2) {
|
|
|
290497
290780
|
writeFileSync73(tmp, text2);
|
|
290498
290781
|
renameSync29(tmp, path);
|
|
290499
290782
|
} catch (error) {
|
|
290500
|
-
|
|
290783
|
+
rmSync26(tmp, { force: true });
|
|
290501
290784
|
throw error;
|
|
290502
290785
|
}
|
|
290503
290786
|
}
|
|
@@ -290523,12 +290806,12 @@ function offboardCommand(args) {
|
|
|
290523
290806
|
}
|
|
290524
290807
|
let projectDir;
|
|
290525
290808
|
try {
|
|
290526
|
-
projectDir =
|
|
290809
|
+
projectDir = realpathSync21(resolve15(process.cwd()));
|
|
290527
290810
|
} catch {
|
|
290528
290811
|
projectDir = process.cwd();
|
|
290529
290812
|
}
|
|
290530
290813
|
const changeset = changesetPath2(projectDir);
|
|
290531
|
-
if (!
|
|
290814
|
+
if (!existsSync136(changeset)) {
|
|
290532
290815
|
err10(m4("offboard.no_changeset_en"));
|
|
290533
290816
|
err10(m4("offboard.no_changeset_zh"));
|
|
290534
290817
|
process.stderr.write("\n");
|
|
@@ -290627,7 +290910,7 @@ function offboardCommand(args) {
|
|
|
290627
290910
|
process.stdout.write(m4("offboard.applying_offboard") + "\n");
|
|
290628
290911
|
for (const item of mergedFiles) {
|
|
290629
290912
|
const path = resolveChangesetItem(projectDir, item);
|
|
290630
|
-
if (!
|
|
290913
|
+
if (!existsSync136(path))
|
|
290631
290914
|
continue;
|
|
290632
290915
|
const before = readFileSync136(path, "utf8");
|
|
290633
290916
|
const after = stripRollMergeBlocks(before);
|
|
@@ -290640,8 +290923,8 @@ function offboardCommand(args) {
|
|
|
290640
290923
|
for (const item of files) {
|
|
290641
290924
|
try {
|
|
290642
290925
|
const path = resolveChangesetItem(projectDir, item);
|
|
290643
|
-
|
|
290644
|
-
if (!
|
|
290926
|
+
rmSync26(path, { force: true });
|
|
290927
|
+
if (!existsSync136(path))
|
|
290645
290928
|
process.stdout.write(` removed file ${item}
|
|
290646
290929
|
`);
|
|
290647
290930
|
} catch {
|
|
@@ -290649,7 +290932,7 @@ function offboardCommand(args) {
|
|
|
290649
290932
|
}
|
|
290650
290933
|
for (const item of dirs) {
|
|
290651
290934
|
try {
|
|
290652
|
-
|
|
290935
|
+
rmSync26(resolveChangesetItem(projectDir, item), { recursive: true, force: true });
|
|
290653
290936
|
process.stdout.write(` removed dir ${item}
|
|
290654
290937
|
`);
|
|
290655
290938
|
} catch {
|
|
@@ -290657,7 +290940,7 @@ function offboardCommand(args) {
|
|
|
290657
290940
|
}
|
|
290658
290941
|
for (const item of giEntries) {
|
|
290659
290942
|
const gi = join159(projectDir, ".gitignore");
|
|
290660
|
-
if (
|
|
290943
|
+
if (existsSync136(gi)) {
|
|
290661
290944
|
const lines3 = readFileSync136(gi, "utf8").split("\n");
|
|
290662
290945
|
if (lines3.includes(item)) {
|
|
290663
290946
|
const kept = lines3.filter((l) => l !== item);
|
|
@@ -290673,21 +290956,21 @@ function offboardCommand(args) {
|
|
|
290673
290956
|
if (r === 0)
|
|
290674
290957
|
process.stdout.write(` unloaded ${item}
|
|
290675
290958
|
`);
|
|
290676
|
-
|
|
290959
|
+
rmSync26(plistPath, { force: true });
|
|
290677
290960
|
}
|
|
290678
|
-
|
|
290961
|
+
rmSync26(changeset, { force: true });
|
|
290679
290962
|
ok11(m4("offboard.offboard_complete_offboard"));
|
|
290680
290963
|
return 0;
|
|
290681
290964
|
}
|
|
290682
290965
|
|
|
290683
290966
|
// packages/cli/dist/commands/prices.js
|
|
290684
290967
|
init_dist();
|
|
290685
|
-
import { existsSync as
|
|
290968
|
+
import { existsSync as existsSync138, readdirSync as readdirSync51, readFileSync as readFileSync138 } from "node:fs";
|
|
290686
290969
|
import { join as join161 } from "node:path";
|
|
290687
290970
|
|
|
290688
290971
|
// packages/cli/dist/commands/prices-refresh.js
|
|
290689
290972
|
init_dist();
|
|
290690
|
-
import { existsSync as
|
|
290973
|
+
import { existsSync as existsSync137, mkdirSync as mkdirSync75, readdirSync as readdirSync50, readFileSync as readFileSync137, writeFileSync as writeFileSync74 } from "node:fs";
|
|
290691
290974
|
import { join as join160 } from "node:path";
|
|
290692
290975
|
var FetchError = class extends Error {
|
|
290693
290976
|
constructor(message) {
|
|
@@ -290909,9 +291192,9 @@ function vendorFromSnapshotName(name) {
|
|
|
290909
291192
|
return match[2] ?? "anthropic";
|
|
290910
291193
|
}
|
|
290911
291194
|
function latestSnapshotPath(snapshotDir, vendor) {
|
|
290912
|
-
if (!
|
|
291195
|
+
if (!existsSync137(snapshotDir))
|
|
290913
291196
|
return null;
|
|
290914
|
-
const snaps =
|
|
291197
|
+
const snaps = readdirSync50(snapshotDir).filter((name) => SNAPSHOT_RE.test(name) && vendorFromSnapshotName(name) === vendor).sort().map((name) => join160(snapshotDir, name));
|
|
290915
291198
|
return snaps[snaps.length - 1] ?? null;
|
|
290916
291199
|
}
|
|
290917
291200
|
function readPrices(path) {
|
|
@@ -291118,10 +291401,10 @@ async function pricesRefreshCommand(args, deps = {}) {
|
|
|
291118
291401
|
// packages/cli/dist/commands/prices.js
|
|
291119
291402
|
function loadSnapshots() {
|
|
291120
291403
|
const dir = join161(repoRoot2(), "lib", "prices");
|
|
291121
|
-
if (!
|
|
291404
|
+
if (!existsSync138(dir)) {
|
|
291122
291405
|
throw new Error(`no price snapshots found in ${dir}; run \`roll prices refresh\``);
|
|
291123
291406
|
}
|
|
291124
|
-
const files =
|
|
291407
|
+
const files = readdirSync51(dir).filter((n) => n.startsWith("snapshot-") && n.endsWith(".json")).sort();
|
|
291125
291408
|
return files.map((name) => {
|
|
291126
291409
|
const data = JSON.parse(readFileSync138(join161(dir, name), "utf8"));
|
|
291127
291410
|
for (const key of ["version", "effective_at", "source_url", "prices"]) {
|
|
@@ -291302,7 +291585,7 @@ init_dist3();
|
|
|
291302
291585
|
init_dist();
|
|
291303
291586
|
init_render();
|
|
291304
291587
|
import { execFileSync as execFileSync35 } from "node:child_process";
|
|
291305
|
-
import { mkdirSync as mkdirSync79, readFileSync as readFileSync142, writeFileSync as writeFileSync78, existsSync as
|
|
291588
|
+
import { mkdirSync as mkdirSync79, readFileSync as readFileSync142, writeFileSync as writeFileSync78, existsSync as existsSync142 } from "node:fs";
|
|
291306
291589
|
import { dirname as dirname78, join as join165 } from "node:path";
|
|
291307
291590
|
|
|
291308
291591
|
// packages/cli/dist/lib/release-consistency.js
|
|
@@ -291311,7 +291594,7 @@ init_dist3();
|
|
|
291311
291594
|
init_dist();
|
|
291312
291595
|
init_render();
|
|
291313
291596
|
import { execFileSync as execFileSync34 } from "node:child_process";
|
|
291314
|
-
import { existsSync as
|
|
291597
|
+
import { existsSync as existsSync140, mkdirSync as mkdirSync77, readFileSync as readFileSync140, readdirSync as readdirSync53, statSync as statSync39, writeFileSync as writeFileSync76 } from "node:fs";
|
|
291315
291598
|
import { dirname as dirname76, join as join163 } from "node:path";
|
|
291316
291599
|
|
|
291317
291600
|
// packages/cli/dist/lib/consistency-audit.js
|
|
@@ -291319,7 +291602,7 @@ init_dist2();
|
|
|
291319
291602
|
init_dist3();
|
|
291320
291603
|
init_dist();
|
|
291321
291604
|
import { execFile as execFile19 } from "node:child_process";
|
|
291322
|
-
import { existsSync as
|
|
291605
|
+
import { existsSync as existsSync139, mkdirSync as mkdirSync76, readFileSync as readFileSync139, readdirSync as readdirSync52, writeFileSync as writeFileSync75 } from "node:fs";
|
|
291323
291606
|
import { dirname as dirname75, join as join162 } from "node:path";
|
|
291324
291607
|
import { promisify as promisify18 } from "node:util";
|
|
291325
291608
|
init_dist();
|
|
@@ -291328,7 +291611,7 @@ var PROBE_CAP = 20;
|
|
|
291328
291611
|
var COUNT_WINDOW_SEC = 72 * 3600;
|
|
291329
291612
|
var execFileAsync18 = promisify18(execFile19);
|
|
291330
291613
|
function readJsonl(path) {
|
|
291331
|
-
if (!
|
|
291614
|
+
if (!existsSync139(path))
|
|
291332
291615
|
return [];
|
|
291333
291616
|
const out3 = [];
|
|
291334
291617
|
for (const line of readFileSync139(path, "utf8").split("\n")) {
|
|
@@ -291344,7 +291627,7 @@ function readJsonl(path) {
|
|
|
291344
291627
|
return out3;
|
|
291345
291628
|
}
|
|
291346
291629
|
function readJson(path) {
|
|
291347
|
-
if (!
|
|
291630
|
+
if (!existsSync139(path))
|
|
291348
291631
|
return null;
|
|
291349
291632
|
try {
|
|
291350
291633
|
const parsed = JSON.parse(readFileSync139(path, "utf8"));
|
|
@@ -291372,10 +291655,10 @@ function hasScreenshotArtifact(reportPath) {
|
|
|
291372
291655
|
}
|
|
291373
291656
|
}
|
|
291374
291657
|
const dir = join162(runDir, "screenshots");
|
|
291375
|
-
if (!
|
|
291658
|
+
if (!existsSync139(dir))
|
|
291376
291659
|
return false;
|
|
291377
291660
|
try {
|
|
291378
|
-
return
|
|
291661
|
+
return readdirSync52(dir).some((name) => /\.png$/i.test(name));
|
|
291379
291662
|
} catch {
|
|
291380
291663
|
return false;
|
|
291381
291664
|
}
|
|
@@ -291409,7 +291692,7 @@ async function gatherAuditSnapshot(projectPath3, runtimeDir11, deps = {}) {
|
|
|
291409
291692
|
const snapshot = emptyAuditSnapshot(nowSec, TERMINAL_SCHEMA_EPOCH_SEC);
|
|
291410
291693
|
const skipped2 = [];
|
|
291411
291694
|
const backlogPath = join162(projectPath3, ".roll", "backlog.md");
|
|
291412
|
-
if (
|
|
291695
|
+
if (existsSync139(backlogPath)) {
|
|
291413
291696
|
snapshot.backlog = parseBacklog(readFileSync139(backlogPath, "utf8")).map((r) => ({ id: r.id, status: r.status }));
|
|
291414
291697
|
}
|
|
291415
291698
|
snapshot.index = readIndex(projectPath3);
|
|
@@ -291418,7 +291701,7 @@ async function gatherAuditSnapshot(projectPath3, runtimeDir11, deps = {}) {
|
|
|
291418
291701
|
let eventFailed = 0;
|
|
291419
291702
|
const terminal = [];
|
|
291420
291703
|
const eventsPath4 = join162(runtimeDir11, "events.ndjson");
|
|
291421
|
-
if (
|
|
291704
|
+
if (existsSync139(eventsPath4)) {
|
|
291422
291705
|
for (const line of readFileSync139(eventsPath4, "utf8").split("\n")) {
|
|
291423
291706
|
const e = parseEventLine(line);
|
|
291424
291707
|
if (e === null)
|
|
@@ -291441,22 +291724,22 @@ async function gatherAuditSnapshot(projectPath3, runtimeDir11, deps = {}) {
|
|
|
291441
291724
|
if (!row2.status.includes("\u2705"))
|
|
291442
291725
|
continue;
|
|
291443
291726
|
const card = cardArchiveDir(projectPath3, row2.id);
|
|
291444
|
-
if (!
|
|
291727
|
+
if (!existsSync139(card))
|
|
291445
291728
|
continue;
|
|
291446
291729
|
const reportPath = join162(card, "latest", reportFileName(row2.id));
|
|
291447
291730
|
const specPath = join162(card, "spec.md");
|
|
291448
291731
|
let noVisualSurface = false;
|
|
291449
|
-
if (
|
|
291732
|
+
if (existsSync139(specPath)) {
|
|
291450
291733
|
try {
|
|
291451
291734
|
noVisualSurface = !hasVisualEvidenceAc(readFileSync139(specPath, "utf8"));
|
|
291452
291735
|
} catch {
|
|
291453
291736
|
}
|
|
291454
291737
|
}
|
|
291455
291738
|
snapshot.attest[row2.id] = {
|
|
291456
|
-
report:
|
|
291457
|
-
acMap:
|
|
291458
|
-
visualEvidence:
|
|
291459
|
-
machineSkip:
|
|
291739
|
+
report: existsSync139(reportPath),
|
|
291740
|
+
acMap: existsSync139(join162(card, "ac-map.json")),
|
|
291741
|
+
visualEvidence: existsSync139(reportPath) ? hasScreenshotArtifact(reportPath) : false,
|
|
291742
|
+
machineSkip: existsSync139(reportPath) ? hasMachineCaptureSkip2(reportPath) : false,
|
|
291460
291743
|
noVisualSurface
|
|
291461
291744
|
};
|
|
291462
291745
|
}
|
|
@@ -291628,7 +291911,7 @@ function walkTextFiles(projectDir, relDir, extensions) {
|
|
|
291628
291911
|
const dirAbs = join163(projectDir, dirRel);
|
|
291629
291912
|
let entries;
|
|
291630
291913
|
try {
|
|
291631
|
-
entries =
|
|
291914
|
+
entries = readdirSync53(dirAbs, { withFileTypes: true });
|
|
291632
291915
|
} catch {
|
|
291633
291916
|
return;
|
|
291634
291917
|
}
|
|
@@ -291837,11 +292120,11 @@ function manualDirectMainDelivery(projectDir, id, mergeShas) {
|
|
|
291837
292120
|
function findCardSpec(projectDir, id) {
|
|
291838
292121
|
const featuresDir = join163(projectDir, ".roll", "features");
|
|
291839
292122
|
try {
|
|
291840
|
-
for (const epic of
|
|
292123
|
+
for (const epic of readdirSync53(featuresDir, { withFileTypes: true })) {
|
|
291841
292124
|
if (!epic.isDirectory())
|
|
291842
292125
|
continue;
|
|
291843
292126
|
const spec = join163(featuresDir, epic.name, id, "spec.md");
|
|
291844
|
-
if (
|
|
292127
|
+
if (existsSync140(spec))
|
|
291845
292128
|
return spec;
|
|
291846
292129
|
}
|
|
291847
292130
|
} catch {
|
|
@@ -291860,12 +292143,12 @@ function cardChangelogExempt(projectDir, id) {
|
|
|
291860
292143
|
}
|
|
291861
292144
|
function checkFeaturesCatalog(projectDir) {
|
|
291862
292145
|
const backlog = join163(projectDir, ".roll", "backlog.md");
|
|
291863
|
-
if (!
|
|
292146
|
+
if (!existsSync140(backlog))
|
|
291864
292147
|
return { status: "pass", gaps: [] };
|
|
291865
292148
|
const backlogText = readText(backlog);
|
|
291866
292149
|
const gaps = [];
|
|
291867
292150
|
const features = join163(projectDir, ".roll", "features.md");
|
|
291868
|
-
if (
|
|
292151
|
+
if (existsSync140(features)) {
|
|
291869
292152
|
const featuresText = readText(features);
|
|
291870
292153
|
for (const featName of readDoneFeatures(backlogText).keys()) {
|
|
291871
292154
|
const escaped = escapeRegExp4(featName);
|
|
@@ -291889,7 +292172,7 @@ function checkFeaturesCatalog(projectDir) {
|
|
|
291889
292172
|
}
|
|
291890
292173
|
function checkTruthLive(projectDir) {
|
|
291891
292174
|
const backlog = join163(projectDir, ".roll", "backlog.md");
|
|
291892
|
-
if (!
|
|
292175
|
+
if (!existsSync140(backlog))
|
|
291893
292176
|
return { status: "pass", gaps: [] };
|
|
291894
292177
|
const backlogText = readText(backlog);
|
|
291895
292178
|
const facts = backlogRowFacts(backlogText);
|
|
@@ -291934,17 +292217,17 @@ function checkTruthLive(projectDir) {
|
|
|
291934
292217
|
function checkCards(projectDir) {
|
|
291935
292218
|
const backlog = join163(projectDir, ".roll", "backlog.md");
|
|
291936
292219
|
const featuresDir = join163(projectDir, ".roll", "features");
|
|
291937
|
-
if (!
|
|
292220
|
+
if (!existsSync140(backlog) || !existsSync140(featuresDir))
|
|
291938
292221
|
return { status: "pass", gaps: [] };
|
|
291939
292222
|
const cardEpic = /* @__PURE__ */ new Map();
|
|
291940
292223
|
try {
|
|
291941
|
-
for (const epic of
|
|
292224
|
+
for (const epic of readdirSync53(featuresDir, { withFileTypes: true })) {
|
|
291942
292225
|
if (!epic.isDirectory())
|
|
291943
292226
|
continue;
|
|
291944
|
-
for (const card of
|
|
292227
|
+
for (const card of readdirSync53(join163(featuresDir, epic.name), { withFileTypes: true })) {
|
|
291945
292228
|
if (!card.isDirectory())
|
|
291946
292229
|
continue;
|
|
291947
|
-
if (
|
|
292230
|
+
if (existsSync140(join163(featuresDir, epic.name, card.name, "spec.md")) && !cardEpic.has(card.name)) {
|
|
291948
292231
|
cardEpic.set(card.name, epic.name);
|
|
291949
292232
|
}
|
|
291950
292233
|
}
|
|
@@ -291979,12 +292262,12 @@ function checkCards(projectDir) {
|
|
|
291979
292262
|
const ev = /\[evidence\]\(([^)]+)\)/.exec(line);
|
|
291980
292263
|
if (ev !== null) {
|
|
291981
292264
|
const target = (ev[1] ?? "").replace(/^\.roll\//, "");
|
|
291982
|
-
if (!
|
|
292265
|
+
if (!existsSync140(join163(projectDir, ".roll", target))) {
|
|
291983
292266
|
gaps.push(`Backlog row ${id} evidence link is broken: ${ev[1] ?? ""}`);
|
|
291984
292267
|
}
|
|
291985
292268
|
} else if (line.includes(STATUS_MARKER.done)) {
|
|
291986
292269
|
const epic = cardEpic.get(id) ?? "";
|
|
291987
|
-
if (!
|
|
292270
|
+
if (!existsSync140(join163(featuresDir, epic, id, "latest", `${id}-report.html`))) {
|
|
291988
292271
|
if (hasAcBlock(epic, id)) {
|
|
291989
292272
|
gaps.push(`Done backlog row ${id} has ACs but no attest report`);
|
|
291990
292273
|
} else {
|
|
@@ -292007,7 +292290,7 @@ function checkCards(projectDir) {
|
|
|
292007
292290
|
function checkDocs(projectDir) {
|
|
292008
292291
|
const gaps = checkTopLevelCommands(activeDocsFiles(projectDir), DOC_RETIRED_TOP_LEVEL_COMMANDS);
|
|
292009
292292
|
const changelogPath = join163(projectDir, "CHANGELOG.md");
|
|
292010
|
-
if (
|
|
292293
|
+
if (existsSync140(changelogPath)) {
|
|
292011
292294
|
const changelog = readText(changelogPath);
|
|
292012
292295
|
for (const id of releaseDeltaCardIds(projectDir)) {
|
|
292013
292296
|
const base = id.replace(/[a-z]$/, "");
|
|
@@ -292082,7 +292365,7 @@ function checkSite(projectDir) {
|
|
|
292082
292365
|
const gaps = checkTopLevelCommands(activeSiteFiles(projectDir), SITE_HIDDEN_TOP_LEVEL_COMMANDS);
|
|
292083
292366
|
const siteJs = join163(projectDir, "site", "roll-data.js");
|
|
292084
292367
|
const backlog = join163(projectDir, ".roll", "backlog.md");
|
|
292085
|
-
if (!
|
|
292368
|
+
if (!existsSync140(siteJs) || !existsSync140(backlog))
|
|
292086
292369
|
return { status: gaps.length === 0 ? "pass" : "fail", gaps };
|
|
292087
292370
|
const siteText = readText(siteJs);
|
|
292088
292371
|
const seenGuideRefs = /* @__PURE__ */ new Set();
|
|
@@ -292091,7 +292374,7 @@ function checkSite(projectDir) {
|
|
|
292091
292374
|
if (seenGuideRefs.has(rel2))
|
|
292092
292375
|
continue;
|
|
292093
292376
|
seenGuideRefs.add(rel2);
|
|
292094
|
-
if (!
|
|
292377
|
+
if (!existsSync140(join163(projectDir, rel2))) {
|
|
292095
292378
|
gaps.push(`site/roll-data.js links a guide that does not exist: ${rel2}`);
|
|
292096
292379
|
}
|
|
292097
292380
|
}
|
|
@@ -292130,7 +292413,7 @@ function checkSite(projectDir) {
|
|
|
292130
292413
|
}
|
|
292131
292414
|
function listFiles2(dir) {
|
|
292132
292415
|
try {
|
|
292133
|
-
return
|
|
292416
|
+
return readdirSync53(dir).filter((n) => {
|
|
292134
292417
|
try {
|
|
292135
292418
|
return statSync39(join163(dir, n)).isFile();
|
|
292136
292419
|
} catch {
|
|
@@ -292145,7 +292428,7 @@ function checkI18n(projectDir) {
|
|
|
292145
292428
|
const gaps = [];
|
|
292146
292429
|
const guideEn = join163(projectDir, "guide", "en");
|
|
292147
292430
|
const guideZh = join163(projectDir, "guide", "zh");
|
|
292148
|
-
if (
|
|
292431
|
+
if (existsSync140(guideEn) && existsSync140(guideZh)) {
|
|
292149
292432
|
const enFiles = new Set(listFiles2(guideEn));
|
|
292150
292433
|
const zhFiles = new Set(listFiles2(guideZh));
|
|
292151
292434
|
const enOnly = [...enFiles].filter((f) => !zhFiles.has(f)).sort();
|
|
@@ -292156,12 +292439,12 @@ function checkI18n(projectDir) {
|
|
|
292156
292439
|
gaps.push(`guide/zh/${f} has no corresponding guide/en/${f}`);
|
|
292157
292440
|
}
|
|
292158
292441
|
const i18nDir = join163(projectDir, "lib", "i18n");
|
|
292159
|
-
if (
|
|
292442
|
+
if (existsSync140(i18nDir)) {
|
|
292160
292443
|
const keysEn = /* @__PURE__ */ new Set();
|
|
292161
292444
|
const keysZh = /* @__PURE__ */ new Set();
|
|
292162
292445
|
let shFiles = [];
|
|
292163
292446
|
try {
|
|
292164
|
-
shFiles =
|
|
292447
|
+
shFiles = readdirSync53(i18nDir).filter((n) => n.endsWith(".sh")).sort();
|
|
292165
292448
|
} catch {
|
|
292166
292449
|
shFiles = [];
|
|
292167
292450
|
}
|
|
@@ -292201,7 +292484,7 @@ function rglobBats(dir) {
|
|
|
292201
292484
|
const walk2 = (d) => {
|
|
292202
292485
|
let entries = [];
|
|
292203
292486
|
try {
|
|
292204
|
-
entries =
|
|
292487
|
+
entries = readdirSync53(d);
|
|
292205
292488
|
} catch {
|
|
292206
292489
|
return;
|
|
292207
292490
|
}
|
|
@@ -292226,7 +292509,7 @@ function checkTests(projectDir) {
|
|
|
292226
292509
|
const gaps = [];
|
|
292227
292510
|
const backlog = join163(projectDir, ".roll", "backlog.md");
|
|
292228
292511
|
const testsDir = join163(projectDir, "tests");
|
|
292229
|
-
if (!
|
|
292512
|
+
if (!existsSync140(backlog))
|
|
292230
292513
|
return { status: "pass", gaps: [] };
|
|
292231
292514
|
const backlogText = readText(backlog);
|
|
292232
292515
|
const allFeatures = /* @__PURE__ */ new Set();
|
|
@@ -292251,7 +292534,7 @@ function checkTests(projectDir) {
|
|
|
292251
292534
|
doneFeatures.push(currentFeature);
|
|
292252
292535
|
}
|
|
292253
292536
|
}
|
|
292254
|
-
const testFiles =
|
|
292537
|
+
const testFiles = existsSync140(testsDir) ? rglobBats(testsDir) : [];
|
|
292255
292538
|
if (testFiles.length === 0)
|
|
292256
292539
|
return { status: "pass", gaps: [] };
|
|
292257
292540
|
for (const feat of doneFeatures) {
|
|
@@ -292603,7 +292886,7 @@ function writeReleaseTestProof(exec) {
|
|
|
292603
292886
|
function assertTestProofFresh(exec) {
|
|
292604
292887
|
const cwd = exec("git", ["rev-parse", "--show-toplevel"]).trim();
|
|
292605
292888
|
const proofPath = join165(cwd, ".roll", "last-test-pass");
|
|
292606
|
-
if (!
|
|
292889
|
+
if (!existsSync142(proofPath)) {
|
|
292607
292890
|
throw new Error("test-pass proof missing \u2014 run `roll test` before committing");
|
|
292608
292891
|
}
|
|
292609
292892
|
const body = readFileSync142(proofPath, "utf8");
|
|
@@ -292623,28 +292906,49 @@ function assertTestProofFresh(exec) {
|
|
|
292623
292906
|
throw new Error("code changed since last test run \u2014 run `roll test` on the current staged tree");
|
|
292624
292907
|
}
|
|
292625
292908
|
}
|
|
292909
|
+
function hasOnlyPreparedReleaseChanges(exec) {
|
|
292910
|
+
const status2 = exec("git", ["status", "--porcelain", "--untracked-files=all"]).trim();
|
|
292911
|
+
if (status2 === "")
|
|
292912
|
+
return false;
|
|
292913
|
+
const allowed2 = /* @__PURE__ */ new Set(["package.json", "CHANGELOG.md"]);
|
|
292914
|
+
const unexpected = status2.split("\n").map((line) => line.slice(2).trim()).filter((path) => !allowed2.has(path));
|
|
292915
|
+
if (unexpected.length > 0) {
|
|
292916
|
+
throw new Error(`unexpected worktree changes during release recovery: ${unexpected.join(", ")}`);
|
|
292917
|
+
}
|
|
292918
|
+
return true;
|
|
292919
|
+
}
|
|
292626
292920
|
function commitPushWithGate(opts) {
|
|
292627
292921
|
const { branch, message, rollManaged, exec } = opts;
|
|
292628
292922
|
const original = exec("git", ["rev-parse", "--abbrev-ref", "HEAD"]).trim();
|
|
292923
|
+
const preparedChanges = hasOnlyPreparedReleaseChanges(exec);
|
|
292629
292924
|
let createdLocal = false;
|
|
292925
|
+
const checkoutExistingBranch = () => {
|
|
292926
|
+
if (preparedChanges) {
|
|
292927
|
+
exec("git", ["checkout", "--merge", branch]);
|
|
292928
|
+
return;
|
|
292929
|
+
}
|
|
292930
|
+
exec("git", ["checkout", branch]);
|
|
292931
|
+
};
|
|
292630
292932
|
const checkoutBranch = () => {
|
|
292933
|
+
let localSha = "";
|
|
292631
292934
|
try {
|
|
292632
|
-
|
|
292633
|
-
if (localSha !== "") {
|
|
292634
|
-
exec("git", ["checkout", branch]);
|
|
292635
|
-
return;
|
|
292636
|
-
}
|
|
292935
|
+
localSha = exec("git", ["rev-parse", "--verify", `refs/heads/${branch}`]).trim();
|
|
292637
292936
|
} catch {
|
|
292638
292937
|
}
|
|
292938
|
+
if (localSha !== "") {
|
|
292939
|
+
checkoutExistingBranch();
|
|
292940
|
+
return;
|
|
292941
|
+
}
|
|
292942
|
+
let remote = "";
|
|
292639
292943
|
try {
|
|
292640
|
-
|
|
292641
|
-
if (remote !== "") {
|
|
292642
|
-
exec("git", ["fetch", "origin", `${branch}:${branch}`]);
|
|
292643
|
-
exec("git", ["checkout", branch]);
|
|
292644
|
-
return;
|
|
292645
|
-
}
|
|
292944
|
+
remote = exec("git", ["ls-remote", "--heads", "origin", branch]).trim();
|
|
292646
292945
|
} catch {
|
|
292647
292946
|
}
|
|
292947
|
+
if (remote !== "") {
|
|
292948
|
+
exec("git", ["fetch", "origin", `${branch}:${branch}`]);
|
|
292949
|
+
checkoutExistingBranch();
|
|
292950
|
+
return;
|
|
292951
|
+
}
|
|
292648
292952
|
createdLocal = true;
|
|
292649
292953
|
exec("git", ["checkout", "-b", branch]);
|
|
292650
292954
|
};
|
|
@@ -292747,7 +293051,7 @@ function realReleaseDeps() {
|
|
|
292747
293051
|
commitPushWithGate({
|
|
292748
293052
|
branch,
|
|
292749
293053
|
message,
|
|
292750
|
-
rollManaged:
|
|
293054
|
+
rollManaged: existsSync142(join165(cwd, ".roll")),
|
|
292751
293055
|
exec: (cmd, args) => execFileSync35(cmd, args, { cwd, encoding: "utf8", stdio: ["ignore", "pipe", "pipe"], timeout: 6e5 })
|
|
292752
293056
|
});
|
|
292753
293057
|
},
|
|
@@ -293072,13 +293376,13 @@ ${c("dim", "\u2192 Running the golden-path showcase (real models; its verdict do
|
|
|
293072
293376
|
init_dist();
|
|
293073
293377
|
init_render();
|
|
293074
293378
|
import { spawnSync as spawnSync19 } from "node:child_process";
|
|
293075
|
-
import { existsSync as
|
|
293379
|
+
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";
|
|
293076
293380
|
import { join as join167 } from "node:path";
|
|
293077
293381
|
|
|
293078
293382
|
// packages/cli/dist/lib/roll-capture-install.js
|
|
293079
293383
|
init_dist();
|
|
293080
293384
|
import { spawnSync as spawnSync18 } from "node:child_process";
|
|
293081
|
-
import { chmodSync as chmodSync2, existsSync as
|
|
293385
|
+
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";
|
|
293082
293386
|
import { homedir as homedir36 } from "node:os";
|
|
293083
293387
|
import { join as join166 } from "node:path";
|
|
293084
293388
|
var RELEASE_API = "https://api.github.com/repos/seanyao/roll-capture/releases/latest";
|
|
@@ -293105,7 +293409,7 @@ function defaultRollCaptureInstallDeps() {
|
|
|
293105
293409
|
home: homedir36(),
|
|
293106
293410
|
uid: typeof process.getuid === "function" ? process.getuid() : void 0,
|
|
293107
293411
|
...platform3 === "darwin" && !isCi3(process.env) && process.env["ROLL_SKIP_CAPTURE_INSTALL"] !== "1" ? { hasAquaGUI: macosHasAquaGUI3(execFile20) } : {},
|
|
293108
|
-
exists:
|
|
293412
|
+
exists: existsSync143,
|
|
293109
293413
|
renamePath: renameSync30,
|
|
293110
293414
|
execFile: execFile20,
|
|
293111
293415
|
fetchLatestRelease: (timeoutMs) => defaultFetchLatestRelease(timeoutMs, process.env, execFile20),
|
|
@@ -293177,7 +293481,7 @@ async function installRollCapture(deps = defaultRollCaptureInstallDeps()) {
|
|
|
293177
293481
|
} catch (error) {
|
|
293178
293482
|
return manual(`install failed: ${errorMessage2(error)}`, release.tagName);
|
|
293179
293483
|
} finally {
|
|
293180
|
-
|
|
293484
|
+
rmSync28(workDir, { recursive: true, force: true });
|
|
293181
293485
|
}
|
|
293182
293486
|
}
|
|
293183
293487
|
function renderRollCaptureInstallResult(result2, lang10) {
|
|
@@ -293313,7 +293617,7 @@ function macosHasAquaGUI3(execFile20) {
|
|
|
293313
293617
|
}
|
|
293314
293618
|
function findExtractedApp(dir) {
|
|
293315
293619
|
try {
|
|
293316
|
-
for (const name of
|
|
293620
|
+
for (const name of readdirSync55(dir)) {
|
|
293317
293621
|
const candidate = join166(dir, name);
|
|
293318
293622
|
const st = lstatSync12(candidate);
|
|
293319
293623
|
if (st.isSymbolicLink())
|
|
@@ -293334,7 +293638,7 @@ function findExtractedApp(dir) {
|
|
|
293334
293638
|
function findBundleExecutable(appPath) {
|
|
293335
293639
|
const macosDir = join166(appPath, "Contents", "MacOS");
|
|
293336
293640
|
try {
|
|
293337
|
-
for (const name of
|
|
293641
|
+
for (const name of readdirSync55(macosDir)) {
|
|
293338
293642
|
const candidate = join166(macosDir, name);
|
|
293339
293643
|
const st = lstatSync12(candidate);
|
|
293340
293644
|
if (st.isSymbolicLink())
|
|
@@ -293351,7 +293655,7 @@ function replaceAppAtomically(app, targetApp, deps) {
|
|
|
293351
293655
|
const renamePath = deps.renamePath ?? renameSync30;
|
|
293352
293656
|
const backupApp = `${targetApp}.bak`;
|
|
293353
293657
|
const hadExisting = lstatExists(targetApp);
|
|
293354
|
-
|
|
293658
|
+
rmSync28(backupApp, { recursive: true, force: true });
|
|
293355
293659
|
if (!hadExisting) {
|
|
293356
293660
|
renamePath(app, targetApp);
|
|
293357
293661
|
return;
|
|
@@ -293363,10 +293667,10 @@ function replaceAppAtomically(app, targetApp, deps) {
|
|
|
293363
293667
|
backupCreated = true;
|
|
293364
293668
|
renamePath(app, targetApp);
|
|
293365
293669
|
targetReplaced = true;
|
|
293366
|
-
|
|
293670
|
+
rmSync28(backupApp, { recursive: true, force: true });
|
|
293367
293671
|
} catch (error) {
|
|
293368
293672
|
if (targetReplaced)
|
|
293369
|
-
|
|
293673
|
+
rmSync28(targetApp, { recursive: true, force: true });
|
|
293370
293674
|
if (backupCreated) {
|
|
293371
293675
|
try {
|
|
293372
293676
|
renamePath(backupApp, targetApp);
|
|
@@ -293542,7 +293846,7 @@ function setupSnapshot(watch) {
|
|
|
293542
293846
|
function walk(dir, lines3) {
|
|
293543
293847
|
let names;
|
|
293544
293848
|
try {
|
|
293545
|
-
names =
|
|
293849
|
+
names = readdirSync56(dir);
|
|
293546
293850
|
} catch {
|
|
293547
293851
|
return;
|
|
293548
293852
|
}
|
|
@@ -293617,11 +293921,11 @@ function tmuxPresent() {
|
|
|
293617
293921
|
function submoduleGuard() {
|
|
293618
293922
|
const pkg = rollPkgDir();
|
|
293619
293923
|
const skills = join167(pkg, "skills");
|
|
293620
|
-
const hasGit =
|
|
293621
|
-
const hasMods =
|
|
293924
|
+
const hasGit = existsSync144(join167(pkg, ".git"));
|
|
293925
|
+
const hasMods = existsSync144(join167(pkg, ".gitmodules"));
|
|
293622
293926
|
let empty = true;
|
|
293623
293927
|
try {
|
|
293624
|
-
empty =
|
|
293928
|
+
empty = readdirSync56(skills).length === 0;
|
|
293625
293929
|
} catch {
|
|
293626
293930
|
empty = true;
|
|
293627
293931
|
}
|
|
@@ -293735,7 +294039,7 @@ async function setupCommand2(args) {
|
|
|
293735
294039
|
return 1;
|
|
293736
294040
|
}
|
|
293737
294041
|
}
|
|
293738
|
-
if (!
|
|
294042
|
+
if (!existsSync144(rollPkgConventions())) {
|
|
293739
294043
|
err13(m5("shared.convention_source_not_found_at_2", rollPkgConventions()));
|
|
293740
294044
|
err13(m5("shared.run_this_from_the_roll_repo"));
|
|
293741
294045
|
return 1;
|
|
@@ -293826,7 +294130,7 @@ init_showcase2();
|
|
|
293826
294130
|
// packages/cli/dist/commands/test.js
|
|
293827
294131
|
init_dist2();
|
|
293828
294132
|
import { spawnSync as spawnSync20 } from "node:child_process";
|
|
293829
|
-
import { appendFileSync as appendFileSync22, existsSync as
|
|
294133
|
+
import { appendFileSync as appendFileSync22, existsSync as existsSync145, mkdirSync as mkdirSync82, readFileSync as readFileSync145, rmSync as rmSync29, writeFileSync as writeFileSync80 } from "node:fs";
|
|
293830
294134
|
import { dirname as dirname79, join as join168, resolve as resolve16 } from "node:path";
|
|
293831
294135
|
function pal6() {
|
|
293832
294136
|
const noColor2 = (process.env["NO_COLOR"] ?? "") !== "";
|
|
@@ -293848,7 +294152,7 @@ function currentEvidenceFrame() {
|
|
|
293848
294152
|
if (raw === "")
|
|
293849
294153
|
return null;
|
|
293850
294154
|
const runDir = resolve16(raw);
|
|
293851
|
-
if (!
|
|
294155
|
+
if (!existsSync145(runDir))
|
|
293852
294156
|
return null;
|
|
293853
294157
|
const frame = {
|
|
293854
294158
|
runDir,
|
|
@@ -293905,7 +294209,7 @@ function appendRollTestEvidence(frame, cmd, argv, status2, stdout, stderr) {
|
|
|
293905
294209
|
}
|
|
293906
294210
|
function isolationGetType() {
|
|
293907
294211
|
const file = join168(process.cwd(), ".roll", "local.yaml");
|
|
293908
|
-
if (!
|
|
294212
|
+
if (!existsSync145(file))
|
|
293909
294213
|
return "none";
|
|
293910
294214
|
let text2;
|
|
293911
294215
|
try {
|
|
@@ -293957,11 +294261,11 @@ function resetLockPath() {
|
|
|
293957
294261
|
return ".roll/.iso-reset.lock";
|
|
293958
294262
|
}
|
|
293959
294263
|
function resetLockHeld() {
|
|
293960
|
-
return
|
|
294264
|
+
return existsSync145(resetLockPath());
|
|
293961
294265
|
}
|
|
293962
294266
|
function resetAcquireLock() {
|
|
293963
294267
|
const lock = resetLockPath();
|
|
293964
|
-
if (
|
|
294268
|
+
if (existsSync145(lock))
|
|
293965
294269
|
return false;
|
|
293966
294270
|
mkdirSync82(dirname79(lock), { recursive: true });
|
|
293967
294271
|
writeFileSync80(lock, `${process.pid}
|
|
@@ -293969,7 +294273,7 @@ function resetAcquireLock() {
|
|
|
293969
294273
|
return true;
|
|
293970
294274
|
}
|
|
293971
294275
|
function resetReleaseLock() {
|
|
293972
|
-
|
|
294276
|
+
rmSync29(resetLockPath(), { force: true });
|
|
293973
294277
|
}
|
|
293974
294278
|
function runForward(cmd, argv) {
|
|
293975
294279
|
const frame = currentEvidenceFrame();
|
|
@@ -293987,7 +294291,7 @@ function runForward(cmd, argv) {
|
|
|
293987
294291
|
}
|
|
293988
294292
|
function readTestScript(cwd) {
|
|
293989
294293
|
const p = join168(cwd, "package.json");
|
|
293990
|
-
if (!
|
|
294294
|
+
if (!existsSync145(p))
|
|
293991
294295
|
return void 0;
|
|
293992
294296
|
try {
|
|
293993
294297
|
const pkg = JSON.parse(readFileSync145(p, "utf8"));
|
|
@@ -294001,7 +294305,7 @@ function detectVitestVersion(cwd) {
|
|
|
294001
294305
|
let dir = cwd;
|
|
294002
294306
|
for (let i = 0; i < 6; i++) {
|
|
294003
294307
|
const vp = join168(dir, "node_modules", "vitest", "package.json");
|
|
294004
|
-
if (
|
|
294308
|
+
if (existsSync145(vp)) {
|
|
294005
294309
|
try {
|
|
294006
294310
|
const v = JSON.parse(readFileSync145(vp, "utf8")).version;
|
|
294007
294311
|
if (typeof v === "string")
|
|
@@ -294039,14 +294343,14 @@ function writeTestProof(cwd, mode, command) {
|
|
|
294039
294343
|
function ensureSkillsSubmoduleReady() {
|
|
294040
294344
|
const pkg = rollPkgDir();
|
|
294041
294345
|
const required2 = join168(pkg, "skills", "roll-onboard", "SKILL.md");
|
|
294042
|
-
if (
|
|
294346
|
+
if (existsSync145(required2))
|
|
294043
294347
|
return true;
|
|
294044
|
-
if (
|
|
294348
|
+
if (existsSync145(join168(pkg, ".git")) && existsSync145(join168(pkg, ".gitmodules"))) {
|
|
294045
294349
|
spawnSync20("git", ["submodule", "update", "--init", "--recursive", "--quiet", "skills"], {
|
|
294046
294350
|
cwd: pkg,
|
|
294047
294351
|
stdio: "ignore"
|
|
294048
294352
|
});
|
|
294049
|
-
if (
|
|
294353
|
+
if (existsSync145(required2))
|
|
294050
294354
|
return true;
|
|
294051
294355
|
}
|
|
294052
294356
|
err14("roll test: skills submodule is empty");
|
|
@@ -294055,7 +294359,7 @@ function ensureSkillsSubmoduleReady() {
|
|
|
294055
294359
|
}
|
|
294056
294360
|
function ensureNoneIsolation() {
|
|
294057
294361
|
const type = isolationGetType();
|
|
294058
|
-
if (type === "none" && !
|
|
294362
|
+
if (type === "none" && !existsSync145(join168(process.cwd(), ".roll", "local.yaml"))) {
|
|
294059
294363
|
infoErr("isolation: no test_isolation config, falling back to type=none (host)");
|
|
294060
294364
|
}
|
|
294061
294365
|
if (!SUPPORTED_TYPES.includes(type)) {
|
|
@@ -294170,7 +294474,7 @@ function testCommand(args) {
|
|
|
294170
294474
|
}
|
|
294171
294475
|
const cwd = process.cwd();
|
|
294172
294476
|
const resolution = resolveGateCommand({
|
|
294173
|
-
hasPackageJson:
|
|
294477
|
+
hasPackageJson: existsSync145(join168(cwd, "package.json")),
|
|
294174
294478
|
testScript: readTestScript(cwd),
|
|
294175
294479
|
vitestVersion: detectVitestVersion(cwd)
|
|
294176
294480
|
});
|
|
@@ -294596,7 +294900,7 @@ function emitRepair(json, outcome) {
|
|
|
294596
294900
|
// packages/cli/dist/commands/truth.js
|
|
294597
294901
|
init_dist();
|
|
294598
294902
|
init_dist2();
|
|
294599
|
-
import { statSync as statSync43, readFileSync as readFileSync147, writeFileSync as writeFileSync82, mkdirSync as mkdirSync84, existsSync as
|
|
294903
|
+
import { statSync as statSync43, readFileSync as readFileSync147, writeFileSync as writeFileSync82, mkdirSync as mkdirSync84, existsSync as existsSync146 } from "node:fs";
|
|
294600
294904
|
import { dirname as dirname81, join as join171 } from "node:path";
|
|
294601
294905
|
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";
|
|
294602
294906
|
function formatTruth(t2, lang10) {
|
|
@@ -294644,7 +294948,7 @@ function truthAuditCommand(args, lang10) {
|
|
|
294644
294948
|
const cwd = process.cwd();
|
|
294645
294949
|
const nowSec = Math.floor(Date.now() / 1e3);
|
|
294646
294950
|
const backlogPath = join171(cwd, ".roll", "backlog.md");
|
|
294647
|
-
const backlogRows =
|
|
294951
|
+
const backlogRows = existsSync146(backlogPath) ? parseBacklog(readFileSync147(backlogPath, "utf8")).map((r) => ({ id: r.id, status: r.status })) : [];
|
|
294648
294952
|
const deliveries = ensureDeliveriesFresh(cwd, nodeFreshnessPort4, nodeExecPort);
|
|
294649
294953
|
const snapshot = emptyAuditSnapshot(nowSec, TERMINAL_SCHEMA_EPOCH_SEC);
|
|
294650
294954
|
snapshot.backlog = backlogRows;
|
|
@@ -294729,7 +295033,7 @@ function truthCommand(args) {
|
|
|
294729
295033
|
|
|
294730
295034
|
// packages/cli/dist/commands/tune.js
|
|
294731
295035
|
init_dist2();
|
|
294732
|
-
import { existsSync as
|
|
295036
|
+
import { existsSync as existsSync147, readFileSync as readFileSync148 } from "node:fs";
|
|
294733
295037
|
import { join as join172 } from "node:path";
|
|
294734
295038
|
function projectPath2() {
|
|
294735
295039
|
return (process.env["ROLL_MAIN_PROJECT"] ?? "").trim() || process.cwd();
|
|
@@ -294738,7 +295042,7 @@ function str6(v, dflt = "") {
|
|
|
294738
295042
|
return typeof v === "string" ? v : dflt;
|
|
294739
295043
|
}
|
|
294740
295044
|
function parseJsonl(path) {
|
|
294741
|
-
if (!
|
|
295045
|
+
if (!existsSync147(path))
|
|
294742
295046
|
return [];
|
|
294743
295047
|
let text2;
|
|
294744
295048
|
try {
|
|
@@ -294914,7 +295218,7 @@ function tuneCommand(argv) {
|
|
|
294914
295218
|
// packages/cli/dist/commands/update.js
|
|
294915
295219
|
init_dist();
|
|
294916
295220
|
import { spawnSync as spawnSync21 } from "node:child_process";
|
|
294917
|
-
import { existsSync as
|
|
295221
|
+
import { existsSync as existsSync148, mkdtempSync as mkdtempSync10, readFileSync as readFileSync149, rmSync as rmSync30 } from "node:fs";
|
|
294918
295222
|
import { tmpdir as tmpdir14 } from "node:os";
|
|
294919
295223
|
import { join as join173 } from "node:path";
|
|
294920
295224
|
function pal7() {
|
|
@@ -295004,7 +295308,7 @@ function downloadAndInstallCurl(tag) {
|
|
|
295004
295308
|
}
|
|
295005
295309
|
return { ok: true, newVersion: treeVersion(extractDir) };
|
|
295006
295310
|
} finally {
|
|
295007
|
-
|
|
295311
|
+
rmSync30(tmpDir, { recursive: true, force: true });
|
|
295008
295312
|
}
|
|
295009
295313
|
}
|
|
295010
295314
|
function checkInstalledVersionOrRetry() {
|
|
@@ -295024,11 +295328,11 @@ function checkInstalledVersionOrRetry() {
|
|
|
295024
295328
|
}
|
|
295025
295329
|
}
|
|
295026
295330
|
function invalidateUpdateCache() {
|
|
295027
|
-
|
|
295331
|
+
rmSync30(join173(rollHome(), ".update-check"), { force: true });
|
|
295028
295332
|
}
|
|
295029
295333
|
function showChangelog() {
|
|
295030
295334
|
const changelog = join173(rollPkgDir(), "CHANGELOG.md");
|
|
295031
|
-
if (!
|
|
295335
|
+
if (!existsSync148(changelog))
|
|
295032
295336
|
return;
|
|
295033
295337
|
const { BOLD: BOLD2, CYAN, NC } = pal7();
|
|
295034
295338
|
process.stdout.write(`${BOLD2}${m6("changelog.heading")}:${NC}
|
|
@@ -295059,7 +295363,7 @@ async function updateCommand(args) {
|
|
|
295059
295363
|
info5(m6("update.current_version", rollVersion()));
|
|
295060
295364
|
let installMethod = "npm";
|
|
295061
295365
|
const methodFile = join173(rollPkgDir(), ".install-method");
|
|
295062
|
-
if (
|
|
295366
|
+
if (existsSync148(methodFile)) {
|
|
295063
295367
|
installMethod = readFileSync149(methodFile, "utf8").trim() || "npm";
|
|
295064
295368
|
}
|
|
295065
295369
|
if (installMethod === "curl") {
|