@seanyao/roll 4.720.1 → 4.721.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +11 -0
- package/dist/roll.mjs +199 -42
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,17 @@
|
|
|
2
2
|
|
|
3
3
|
## Unreleased
|
|
4
4
|
|
|
5
|
+
## v4.721.2 — 2026-07-21
|
|
6
|
+
|
|
7
|
+
### 稳定性
|
|
8
|
+
- loop 的构建子进程失联或被系统杀掉后,现在会在限定时间内察觉、收尾并写入终态(标记为 aborted),不再让整个受监督周期永久挂住;新增子进程存活/超时检测(FIX-1474)[loop]
|
|
9
|
+
|
|
10
|
+
## v4.721.1 — 2026-07-21
|
|
11
|
+
|
|
12
|
+
### 稳定性
|
|
13
|
+
- loop 看门狗的"进展"判定从 stdout 改为 git 状态(新 commit / 工作区文件变动):输出缓冲型 agent 写文件时不再被 15 分钟无进展误杀;新增 `no-state-change` 杀因——默认 25 分钟无 commit 且无文件变动即终止(即使 stdout 仍在输出),原地打转的 cycle 不再烧满 45 分钟墙钟;阈值可用 `ROLL_CYCLE_NO_STATE_CHANGE_SEC` 覆盖(FIX-1477)[loop]
|
|
14
|
+
- attest 闸的"无 AC 块豁免"提前到渲染之前评估:无 AC 块的卡不再因渲染失败或空壳报告被硬拦,直接合法放行(FIX-1476)[loop]
|
|
15
|
+
|
|
5
16
|
## v4.720.1 — 2026-07-20
|
|
6
17
|
|
|
7
18
|
### 稳定性
|
package/dist/roll.mjs
CHANGED
|
@@ -14639,18 +14639,36 @@ function timeoutTeardownCommands(ctx) {
|
|
|
14639
14639
|
{ kind: "release_lock" }
|
|
14640
14640
|
];
|
|
14641
14641
|
}
|
|
14642
|
+
function abortedTeardownCommands(ctx) {
|
|
14643
|
+
return [
|
|
14644
|
+
{ kind: "kill_agent", graceSec: WATCHDOG_KILL_GRACE_SEC },
|
|
14645
|
+
{ kind: "measure_worktree" },
|
|
14646
|
+
{ kind: "emit_event", event: cycleEndEvent(ctx, "aborted") },
|
|
14647
|
+
{ kind: "append_run", status: "aborted", outcome: mapV2Status("aborted"), cycleId: ctx.cycleId },
|
|
14648
|
+
{
|
|
14649
|
+
kind: "append_alert",
|
|
14650
|
+
message: `cycle ${ctx.cycleId}: builder child process lost (killed out-of-band) \u2014 cycle aborted; worktree preserved`
|
|
14651
|
+
},
|
|
14652
|
+
{ kind: "release_lock" }
|
|
14653
|
+
];
|
|
14654
|
+
}
|
|
14642
14655
|
function cycleTimeoutVerdict(input) {
|
|
14643
14656
|
const wall = input.wallLimitSec ?? CYCLE_WALL_TIMEOUT_SEC;
|
|
14644
14657
|
const idle = input.noProgressLimitSec ?? CYCLE_NO_PROGRESS_SEC;
|
|
14658
|
+
const stateIdle = input.noStateChangeLimitSec ?? CYCLE_NO_STATE_CHANGE_SEC;
|
|
14645
14659
|
if (wall > 0 && input.elapsedSec >= wall) {
|
|
14646
14660
|
return { timedOut: true, reason: "wall", elapsedSec: input.elapsedSec, idleSec: input.idleSec };
|
|
14647
14661
|
}
|
|
14648
14662
|
if (idle > 0 && input.idleSec >= idle) {
|
|
14649
14663
|
return { timedOut: true, reason: "no-progress", elapsedSec: input.elapsedSec, idleSec: input.idleSec };
|
|
14650
14664
|
}
|
|
14665
|
+
if (input.stateIdleSec !== void 0 && stateIdle > 0 && input.stateIdleSec >= stateIdle) {
|
|
14666
|
+
return { timedOut: true, reason: "no-state-change", elapsedSec: input.elapsedSec, idleSec: input.idleSec };
|
|
14667
|
+
}
|
|
14651
14668
|
const wallRemain = wall > 0 ? wall - input.elapsedSec : Number.POSITIVE_INFINITY;
|
|
14652
14669
|
const idleRemain = idle > 0 ? idle - input.idleSec : Number.POSITIVE_INFINITY;
|
|
14653
|
-
|
|
14670
|
+
const stateRemain = input.stateIdleSec !== void 0 && stateIdle > 0 ? stateIdle - input.stateIdleSec : Number.POSITIVE_INFINITY;
|
|
14671
|
+
return { timedOut: false, remainingSec: Math.min(wallRemain, idleRemain, stateRemain) };
|
|
14654
14672
|
}
|
|
14655
14673
|
function stallVerdict(input) {
|
|
14656
14674
|
const threshold = input.stallThresholdSec ?? CYCLE_STALL_THRESHOLD_SEC;
|
|
@@ -14828,6 +14846,12 @@ function cycleStep(state, event) {
|
|
|
14828
14846
|
{ kind: "cleanup_worktree", branch: state.ctx.branch }
|
|
14829
14847
|
]);
|
|
14830
14848
|
case "agent_exited": {
|
|
14849
|
+
if (event.lost === true) {
|
|
14850
|
+
return {
|
|
14851
|
+
state: { ...state, phase: "execute", terminal: "aborted", done: true, ctx: { ...state.ctx, agentExitCode: event.exit, agentTimedOut: event.timedOut } },
|
|
14852
|
+
commands: abortedTeardownCommands(terminalCtx(state))
|
|
14853
|
+
};
|
|
14854
|
+
}
|
|
14831
14855
|
const plan = retryPlan({ attempt: state.attempt, exit: event.exit, timedOut: event.timedOut });
|
|
14832
14856
|
if (plan.action === "abort_timeout") {
|
|
14833
14857
|
return {
|
|
@@ -15141,7 +15165,7 @@ function adversarialDegradedCmd(cycleId, storyId, cause, from = "adversarial") {
|
|
|
15141
15165
|
function initialCycleState(ctx) {
|
|
15142
15166
|
return { phase: "pick", ctx, attempt: 0, done: false };
|
|
15143
15167
|
}
|
|
15144
|
-
var CYCLE_TIMEOUT_SEC, WATCHDOG_KILL_GRACE_SEC, CYCLE_WALL_TIMEOUT_SEC, CYCLE_NO_PROGRESS_SEC, CYCLE_STALL_THRESHOLD_SEC, STALL_STARTUP_GRACE_SEC, MAX_AGENT_ATTEMPTS, RETRY_BASE_BACKOFF_SEC, EVENT_VALID_PHASES;
|
|
15168
|
+
var CYCLE_TIMEOUT_SEC, WATCHDOG_KILL_GRACE_SEC, CYCLE_WALL_TIMEOUT_SEC, CYCLE_NO_PROGRESS_SEC, CYCLE_NO_STATE_CHANGE_SEC, CYCLE_STALL_THRESHOLD_SEC, STALL_STARTUP_GRACE_SEC, MAX_AGENT_ATTEMPTS, RETRY_BASE_BACKOFF_SEC, EVENT_VALID_PHASES;
|
|
15145
15169
|
var init_orchestrator = __esm({
|
|
15146
15170
|
"packages/core/dist/loop/orchestrator.js"() {
|
|
15147
15171
|
"use strict";
|
|
@@ -15153,6 +15177,7 @@ var init_orchestrator = __esm({
|
|
|
15153
15177
|
WATCHDOG_KILL_GRACE_SEC = 5;
|
|
15154
15178
|
CYCLE_WALL_TIMEOUT_SEC = 2700;
|
|
15155
15179
|
CYCLE_NO_PROGRESS_SEC = 900;
|
|
15180
|
+
CYCLE_NO_STATE_CHANGE_SEC = 1500;
|
|
15156
15181
|
CYCLE_STALL_THRESHOLD_SEC = 600;
|
|
15157
15182
|
STALL_STARTUP_GRACE_SEC = 120;
|
|
15158
15183
|
MAX_AGENT_ATTEMPTS = 3;
|
|
@@ -246181,6 +246206,10 @@ function spawnAndWait(bin, args, opts, pty = false, profile) {
|
|
|
246181
246206
|
stderr += d.toString("utf8");
|
|
246182
246207
|
});
|
|
246183
246208
|
liveAgents.add(child);
|
|
246209
|
+
try {
|
|
246210
|
+
opts.onSpawn?.(child);
|
|
246211
|
+
} catch {
|
|
246212
|
+
}
|
|
246184
246213
|
let settled = false;
|
|
246185
246214
|
let exitDrainFallback;
|
|
246186
246215
|
const settle = (result2) => {
|
|
@@ -251254,6 +251283,11 @@ function runAttestGate(worktreeCwd, storyId, cycleId, mode, sinceSec, sinks, sco
|
|
|
251254
251283
|
sinks.event({ cycleId, verdict: "skipped", reasons: reasons2 });
|
|
251255
251284
|
return { verdict: "skipped", mode, reasons: reasons2, blocked: blocked4 };
|
|
251256
251285
|
}
|
|
251286
|
+
if (storyHasAcBlock(specRepoCwd, storyId) === false) {
|
|
251287
|
+
const reasons2 = ["story has no AC block; acceptance report not required"];
|
|
251288
|
+
sinks.event({ cycleId, verdict: "produced", reasons: reasons2 });
|
|
251289
|
+
return { verdict: "produced", mode, reasons: reasons2, blocked: false };
|
|
251290
|
+
}
|
|
251257
251291
|
if (renderExitCode !== 0) {
|
|
251258
251292
|
const reasons2 = [`attest render failed for ${storyId} (exit ${renderExitCode})`];
|
|
251259
251293
|
const blocked4 = mode === "hard";
|
|
@@ -251261,11 +251295,6 @@ function runAttestGate(worktreeCwd, storyId, cycleId, mode, sinceSec, sinks, sco
|
|
|
251261
251295
|
sinks.event({ cycleId, verdict: "skipped", reasons: reasons2 });
|
|
251262
251296
|
return { verdict: "skipped", mode, reasons: reasons2, blocked: blocked4 };
|
|
251263
251297
|
}
|
|
251264
|
-
if (storyHasAcBlock(specRepoCwd, storyId) === false) {
|
|
251265
|
-
const reasons2 = ["story has no AC block; acceptance report not required"];
|
|
251266
|
-
sinks.event({ cycleId, verdict: "produced", reasons: reasons2 });
|
|
251267
|
-
return { verdict: "produced", mode, reasons: reasons2, blocked: false };
|
|
251268
|
-
}
|
|
251269
251298
|
const diagnostics = violatesMustDeclareSurface(specRepoCwd, storyId) ? [MUST_DECLARE_FAIL_REASON] : [];
|
|
251270
251299
|
const redAcs = redAcFailures(worktreeCwd, storyId, scoreRepoCwd);
|
|
251271
251300
|
if (redAcs.length > 0) {
|
|
@@ -284515,7 +284544,8 @@ function readCycleTimeoutThresholds(repoCwd) {
|
|
|
284515
284544
|
}
|
|
284516
284545
|
return {
|
|
284517
284546
|
wallSec: envNum("ROLL_CYCLE_WALL_TIMEOUT_SEC") ?? wallSec,
|
|
284518
|
-
noProgressSec: envNum("ROLL_CYCLE_NO_PROGRESS_SEC") ?? noProgressSec
|
|
284547
|
+
noProgressSec: envNum("ROLL_CYCLE_NO_PROGRESS_SEC") ?? noProgressSec,
|
|
284548
|
+
noStateChangeSec: envNum("ROLL_CYCLE_NO_STATE_CHANGE_SEC") ?? CYCLE_NO_STATE_CHANGE_SEC
|
|
284519
284549
|
};
|
|
284520
284550
|
}
|
|
284521
284551
|
function readStallThreshold(repoCwd) {
|
|
@@ -284531,15 +284561,18 @@ function readStallThreshold(repoCwd) {
|
|
|
284531
284561
|
}
|
|
284532
284562
|
function startSpawnTimeoutWatchdog(opts) {
|
|
284533
284563
|
const { cycleId, thresholds, clock: clock2, commitCount, appendEvent: appendEvent3 } = opts;
|
|
284564
|
+
const stateSignature = opts.stateSignature;
|
|
284534
284565
|
const kill = opts.kill ?? (() => killLiveAgents("SIGKILL"));
|
|
284535
284566
|
const pollMs = opts.pollMs ?? (Number((process.env["ROLL_TIMEOUT_POLL_MS"] ?? "").trim()) || TIMEOUT_POLL_MS);
|
|
284536
|
-
if (thresholds.wallSec <= 0 && thresholds.noProgressSec <= 0) {
|
|
284567
|
+
if (thresholds.wallSec <= 0 && thresholds.noProgressSec <= 0 && thresholds.noStateChangeSec <= 0) {
|
|
284537
284568
|
return { markProgress: () => {
|
|
284538
284569
|
}, stop: () => ({ firedReason: null }) };
|
|
284539
284570
|
}
|
|
284540
284571
|
const startSec = clock2();
|
|
284541
284572
|
let lastProgressSec = startSec;
|
|
284573
|
+
let lastStateSec = startSec;
|
|
284542
284574
|
let lastCommitCount = -1;
|
|
284575
|
+
let lastSignature;
|
|
284543
284576
|
let firedReason = null;
|
|
284544
284577
|
let running = false;
|
|
284545
284578
|
const markProgress = () => {
|
|
@@ -284554,16 +284587,32 @@ function startSpawnTimeoutWatchdog(opts) {
|
|
|
284554
284587
|
const n = await commitCount();
|
|
284555
284588
|
if (n > lastCommitCount) {
|
|
284556
284589
|
lastCommitCount = n;
|
|
284557
|
-
|
|
284590
|
+
const now3 = clock2();
|
|
284591
|
+
lastProgressSec = now3;
|
|
284592
|
+
lastStateSec = now3;
|
|
284558
284593
|
}
|
|
284559
284594
|
} catch {
|
|
284560
284595
|
}
|
|
284596
|
+
if (stateSignature !== void 0) {
|
|
284597
|
+
try {
|
|
284598
|
+
const sig = await stateSignature();
|
|
284599
|
+
if (lastSignature !== void 0 && sig !== lastSignature) {
|
|
284600
|
+
const now3 = clock2();
|
|
284601
|
+
lastProgressSec = now3;
|
|
284602
|
+
lastStateSec = now3;
|
|
284603
|
+
}
|
|
284604
|
+
lastSignature = sig;
|
|
284605
|
+
} catch {
|
|
284606
|
+
}
|
|
284607
|
+
}
|
|
284561
284608
|
const now2 = clock2();
|
|
284562
284609
|
const verdict = cycleTimeoutVerdict({
|
|
284563
284610
|
elapsedSec: now2 - startSec,
|
|
284564
284611
|
idleSec: now2 - lastProgressSec,
|
|
284612
|
+
stateIdleSec: now2 - lastStateSec,
|
|
284565
284613
|
wallLimitSec: thresholds.wallSec,
|
|
284566
|
-
noProgressLimitSec: thresholds.noProgressSec
|
|
284614
|
+
noProgressLimitSec: thresholds.noProgressSec,
|
|
284615
|
+
noStateChangeLimitSec: thresholds.noStateChangeSec
|
|
284567
284616
|
});
|
|
284568
284617
|
if (verdict.timedOut) {
|
|
284569
284618
|
firedReason = verdict.reason;
|
|
@@ -284593,6 +284642,12 @@ function startSpawnTimeoutWatchdog(opts) {
|
|
|
284593
284642
|
lastCommitCount = await commitCount();
|
|
284594
284643
|
} catch {
|
|
284595
284644
|
}
|
|
284645
|
+
if (stateSignature !== void 0) {
|
|
284646
|
+
try {
|
|
284647
|
+
lastSignature = await stateSignature();
|
|
284648
|
+
} catch {
|
|
284649
|
+
}
|
|
284650
|
+
}
|
|
284596
284651
|
})();
|
|
284597
284652
|
const timer = setInterval(() => void tick(), pollMs);
|
|
284598
284653
|
timer.unref?.();
|
|
@@ -284662,6 +284717,68 @@ function startStallDetector(opts) {
|
|
|
284662
284717
|
}
|
|
284663
284718
|
};
|
|
284664
284719
|
}
|
|
284720
|
+
var LIVENESS_POLL_MS = 5e3;
|
|
284721
|
+
var LIVENESS_CONFIRM_TICKS = 2;
|
|
284722
|
+
function defaultIsAlive(pid) {
|
|
284723
|
+
try {
|
|
284724
|
+
process.kill(pid, 0);
|
|
284725
|
+
return true;
|
|
284726
|
+
} catch (e) {
|
|
284727
|
+
return e.code === "EPERM";
|
|
284728
|
+
}
|
|
284729
|
+
}
|
|
284730
|
+
function startBuilderLivenessProbe(opts) {
|
|
284731
|
+
const { cycleId, agent, appendEvent: appendEvent3 } = opts;
|
|
284732
|
+
const isAlive = opts.isAlive ?? defaultIsAlive;
|
|
284733
|
+
const kill = opts.kill ?? (() => killLiveAgents("SIGKILL"));
|
|
284734
|
+
const pollMs = opts.pollMs ?? (Number((process.env["ROLL_LIVENESS_POLL_MS"] ?? "").trim()) || LIVENESS_POLL_MS);
|
|
284735
|
+
const confirmTicks = opts.confirmTicks ?? LIVENESS_CONFIRM_TICKS;
|
|
284736
|
+
let lost = false;
|
|
284737
|
+
let deadStreak = 0;
|
|
284738
|
+
const tick = () => {
|
|
284739
|
+
if (lost || !opts.spawnPending())
|
|
284740
|
+
return;
|
|
284741
|
+
const pid = opts.pid();
|
|
284742
|
+
if (pid === void 0) {
|
|
284743
|
+
deadStreak = 0;
|
|
284744
|
+
return;
|
|
284745
|
+
}
|
|
284746
|
+
let alive = true;
|
|
284747
|
+
try {
|
|
284748
|
+
alive = isAlive(pid);
|
|
284749
|
+
} catch {
|
|
284750
|
+
}
|
|
284751
|
+
if (alive) {
|
|
284752
|
+
deadStreak = 0;
|
|
284753
|
+
return;
|
|
284754
|
+
}
|
|
284755
|
+
deadStreak += 1;
|
|
284756
|
+
if (deadStreak < confirmTicks)
|
|
284757
|
+
return;
|
|
284758
|
+
lost = true;
|
|
284759
|
+
clearInterval(timer);
|
|
284760
|
+
try {
|
|
284761
|
+
appendEvent3({ type: "cycle:agent_lost", cycleId, agent, pid, ts: Date.now() });
|
|
284762
|
+
} catch {
|
|
284763
|
+
}
|
|
284764
|
+
try {
|
|
284765
|
+
kill();
|
|
284766
|
+
} catch {
|
|
284767
|
+
}
|
|
284768
|
+
try {
|
|
284769
|
+
opts.onLost?.({ pid });
|
|
284770
|
+
} catch {
|
|
284771
|
+
}
|
|
284772
|
+
};
|
|
284773
|
+
const timer = setInterval(tick, pollMs);
|
|
284774
|
+
timer.unref?.();
|
|
284775
|
+
return {
|
|
284776
|
+
stop: () => {
|
|
284777
|
+
clearInterval(timer);
|
|
284778
|
+
return { lost };
|
|
284779
|
+
}
|
|
284780
|
+
};
|
|
284781
|
+
}
|
|
284665
284782
|
function createCaptureMarkerSink(runDir, capture) {
|
|
284666
284783
|
let buf = "";
|
|
284667
284784
|
const pending = [];
|
|
@@ -284760,11 +284877,13 @@ async function executeSpawnAgentCommand(cmd, ports, ctx) {
|
|
|
284760
284877
|
appendEvent: (ev) => ports.events.appendEvent(ports.paths.eventsPath, ev),
|
|
284761
284878
|
thresholdSec: readStallThreshold(ports.repoCwd).thresholdSec
|
|
284762
284879
|
});
|
|
284880
|
+
const statusSignature = ports.git.worktreeStatusSignature;
|
|
284763
284881
|
const timeoutWatchdog = startSpawnTimeoutWatchdog({
|
|
284764
284882
|
cycleId: ctx.cycleId ?? "",
|
|
284765
284883
|
thresholds: readCycleTimeoutThresholds(ports.repoCwd),
|
|
284766
284884
|
clock: ports.clock,
|
|
284767
284885
|
commitCount: () => ports.git.commitsAhead(execCwd, observeBase),
|
|
284886
|
+
...statusSignature !== void 0 ? { stateSignature: () => statusSignature(execCwd) } : {},
|
|
284768
284887
|
appendEvent: (ev) => ports.events.appendEvent(ports.paths.eventsPath, ev)
|
|
284769
284888
|
});
|
|
284770
284889
|
const skillBodyForSpawn = maybeInjectProjectMap(
|
|
@@ -284783,6 +284902,24 @@ ${skillBodyForSpawn}` : skillBodyForSpawn;
|
|
|
284783
284902
|
let timeoutFired = null;
|
|
284784
284903
|
let activeMainLeak = { detected: false, files: [] };
|
|
284785
284904
|
let mainLeakWatchdog;
|
|
284905
|
+
let spawnedPid;
|
|
284906
|
+
let spawnSettled = false;
|
|
284907
|
+
let agentLost = false;
|
|
284908
|
+
let resolveLostRace;
|
|
284909
|
+
const lostRace = new Promise((resolve17) => {
|
|
284910
|
+
resolveLostRace = () => resolve17({ stdout: "", stderr: "", exitCode: 137, timedOut: false });
|
|
284911
|
+
});
|
|
284912
|
+
const livenessProbe = startBuilderLivenessProbe({
|
|
284913
|
+
cycleId: ctx.cycleId ?? "",
|
|
284914
|
+
agent: cmd.agent,
|
|
284915
|
+
pid: () => spawnedPid,
|
|
284916
|
+
spawnPending: () => !spawnSettled,
|
|
284917
|
+
appendEvent: (ev) => ports.events.appendEvent(ports.paths.eventsPath, ev),
|
|
284918
|
+
onLost: () => {
|
|
284919
|
+
agentLost = true;
|
|
284920
|
+
resolveLostRace?.();
|
|
284921
|
+
}
|
|
284922
|
+
});
|
|
284786
284923
|
try {
|
|
284787
284924
|
appendWriteProtectionEvent(ports, applyMainCheckoutWriteProtection({
|
|
284788
284925
|
repoCwd: ports.repoCwd,
|
|
@@ -284791,37 +284928,47 @@ ${skillBodyForSpawn}` : skillBodyForSpawn;
|
|
|
284791
284928
|
nowMs: () => eventTs2(ports)
|
|
284792
284929
|
}));
|
|
284793
284930
|
mainLeakWatchdog = startMainCheckoutLeakWatchdog(ports, ctx);
|
|
284794
|
-
res = await
|
|
284795
|
-
|
|
284796
|
-
|
|
284797
|
-
|
|
284798
|
-
|
|
284799
|
-
|
|
284800
|
-
|
|
284801
|
-
|
|
284802
|
-
|
|
284803
|
-
|
|
284804
|
-
|
|
284805
|
-
|
|
284806
|
-
|
|
284807
|
-
|
|
284808
|
-
|
|
284809
|
-
|
|
284810
|
-
|
|
284811
|
-
|
|
284812
|
-
|
|
284813
|
-
|
|
284814
|
-
|
|
284815
|
-
|
|
284816
|
-
|
|
284817
|
-
|
|
284818
|
-
|
|
284819
|
-
|
|
284931
|
+
res = await Promise.race([
|
|
284932
|
+
ports.agentSpawn(cmd.agent, {
|
|
284933
|
+
purpose: "builder",
|
|
284934
|
+
// E4: the builder runs in the submodule cycle worktree for a submodule
|
|
284935
|
+
// story (execCwd); its git env + writable roots target the submodule's
|
|
284936
|
+
// own repo/object store so its commits actually land there.
|
|
284937
|
+
cwd: execCwd,
|
|
284938
|
+
skillBody: finalSkillBody,
|
|
284939
|
+
...ctx.evidenceRunDir !== void 0 ? { runDir: ctx.evidenceRunDir } : {},
|
|
284940
|
+
writableRoots: submoduleAgentWritableRoots(ports.repoCwd, execRepoCwd, ports.paths.alertsPath),
|
|
284941
|
+
...ctx.model !== void 0 && ctx.model !== "" ? { model: ctx.model } : {},
|
|
284942
|
+
env: {
|
|
284943
|
+
...process.env,
|
|
284944
|
+
ROLL_LOOP_ALERT: ports.paths.alertsPath,
|
|
284945
|
+
...worktreeGitEnv(execCwd, ports.repoCwd),
|
|
284946
|
+
...agentSpawnEnvironment(cmd.agent)
|
|
284947
|
+
},
|
|
284948
|
+
// FIX-204B: pin the executor-picked story into the agent prompt — the
|
|
284949
|
+
// claim (pick_story → 🔨) and the work must be the same story.
|
|
284950
|
+
...ctx.storyId !== void 0 && ctx.storyId !== "" ? { storyId: ctx.storyId } : {},
|
|
284951
|
+
onChunk: (d) => {
|
|
284952
|
+
timeoutWatchdog.markProgress();
|
|
284953
|
+
stallDetector.markProgress();
|
|
284954
|
+
captureSink?.onChunk(d);
|
|
284955
|
+
try {
|
|
284956
|
+
appendFileSync16(livePath, d);
|
|
284957
|
+
} catch {
|
|
284958
|
+
}
|
|
284959
|
+
signalRecorder.accept(d);
|
|
284960
|
+
},
|
|
284961
|
+
// FIX-1474: hand the live child to the liveness probe so an
|
|
284962
|
+
// out-of-band death is detected within a bounded window.
|
|
284963
|
+
onSpawn: (child) => {
|
|
284964
|
+
spawnedPid = child.pid;
|
|
284820
284965
|
}
|
|
284821
|
-
|
|
284822
|
-
|
|
284823
|
-
|
|
284966
|
+
}),
|
|
284967
|
+
lostRace
|
|
284968
|
+
]);
|
|
284824
284969
|
} finally {
|
|
284970
|
+
spawnSettled = true;
|
|
284971
|
+
livenessProbe.stop();
|
|
284825
284972
|
if (mainLeakWatchdog !== void 0) {
|
|
284826
284973
|
activeMainLeak = await mainLeakWatchdog.stop();
|
|
284827
284974
|
}
|
|
@@ -284929,7 +285076,7 @@ ${res.stderr}`.split("\n").find((l) => l.trim() !== "") ?? "").slice(0, 200);
|
|
|
284929
285076
|
// FIX-343 (step ①): the builder session id is part of the auditable
|
|
284930
285077
|
// header — the attest gate's scorer≠builder-session invariant is then
|
|
284931
285078
|
// traceable to a recorded build-session id, not asserted.
|
|
284932
|
-
`# exit=${res.exitCode} timedOut=${res.timedOut} build-session=${builderSessionId}
|
|
285079
|
+
`# exit=${res.exitCode} timedOut=${res.timedOut} lost=${agentLost} build-session=${builderSessionId}
|
|
284933
285080
|
--- stdout ---
|
|
284934
285081
|
${res.stdout}
|
|
284935
285082
|
--- stderr ---
|
|
@@ -284977,7 +285124,10 @@ ${res.stderr}
|
|
|
284977
285124
|
cycleStartSec: ctx.startSec
|
|
284978
285125
|
}) ?? void 0 : void 0;
|
|
284979
285126
|
return {
|
|
284980
|
-
|
|
285127
|
+
// FIX-1474: a lost child converges the cycle to the explicit `aborted`
|
|
285128
|
+
// terminal in the orchestrator (no retry, no blocked) — the death was
|
|
285129
|
+
// environmental, recorded fail-loud via cycle:agent_lost.
|
|
285130
|
+
event: { type: "agent_exited", exit: res.exitCode, timedOut: res.timedOut, ...agentLost ? { lost: true } : {} },
|
|
284981
285131
|
// FIX-343 (step ①): persist the builder session id on the cycle context so
|
|
284982
285132
|
// it survives to the attest gate (the scorer≠builder-session invariant is then
|
|
284983
285133
|
// traceable to a recorded build-session id, not asserted).
|
|
@@ -287752,6 +287902,13 @@ function nodePorts(opts) {
|
|
|
287752
287902
|
const n = Number((r.stdout ?? "0").trim());
|
|
287753
287903
|
return Number.isFinite(n) ? n : 0;
|
|
287754
287904
|
},
|
|
287905
|
+
async worktreeStatusSignature(worktreeCwd) {
|
|
287906
|
+
const r = await execFileAsync17("git", ["status", "--porcelain"], {
|
|
287907
|
+
cwd: worktreeCwd,
|
|
287908
|
+
encoding: "utf8"
|
|
287909
|
+
});
|
|
287910
|
+
return r.stdout ?? "";
|
|
287911
|
+
},
|
|
287755
287912
|
async mainAhead(repoCwd) {
|
|
287756
287913
|
const r = await execFileAsync17("git", ["rev-list", "--count", "origin/main..main"], {
|
|
287757
287914
|
cwd: repoCwd,
|