@kynver-app/runtime 0.1.102 → 0.1.105
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cleanup-completion-blocker.d.ts +10 -0
- package/dist/cleanup-guards.d.ts +1 -6
- package/dist/cleanup-worktree-salvage.d.ts +7 -0
- package/dist/cli.js +181 -64
- package/dist/cli.js.map +4 -4
- package/dist/index.js +181 -64
- package/dist/index.js.map +4 -4
- package/dist/server/cleanup.d.ts +3 -0
- package/dist/server/cleanup.js +3511 -0
- package/dist/server/cleanup.js.map +7 -0
- package/dist/server/default-repo.d.ts +1 -0
- package/dist/server/default-repo.js +228 -0
- package/dist/server/default-repo.js.map +7 -0
- package/dist/server/harness-notice.d.ts +2 -0
- package/dist/server/harness-notice.js +287 -0
- package/dist/server/harness-notice.js.map +7 -0
- package/dist/server/heavy-verification.d.ts +2 -0
- package/dist/server/heavy-verification.js +223 -0
- package/dist/server/heavy-verification.js.map +7 -0
- package/dist/server/landing.d.ts +1 -0
- package/dist/server/landing.js +44 -0
- package/dist/server/landing.js.map +7 -0
- package/dist/server/memory-cost-enforce.d.ts +1 -0
- package/dist/server/memory-cost-enforce.js +470 -0
- package/dist/server/memory-cost-enforce.js.map +7 -0
- package/dist/server/memory-cost.d.ts +1 -0
- package/dist/server/memory-cost.js +184 -0
- package/dist/server/memory-cost.js.map +7 -0
- package/dist/server/monitor.d.ts +3 -0
- package/dist/server/monitor.js +1577 -0
- package/dist/server/monitor.js.map +7 -0
- package/dist/server/orchestration.d.ts +10 -0
- package/dist/server/orchestration.js +444 -0
- package/dist/server/orchestration.js.map +7 -0
- package/dist/server/pr-evidence.d.ts +2 -0
- package/dist/server/pr-evidence.js +163 -0
- package/dist/server/pr-evidence.js.map +7 -0
- package/dist/server/repo-search.d.ts +1 -0
- package/dist/server/repo-search.js +224 -0
- package/dist/server/repo-search.js.map +7 -0
- package/dist/server/worker-policy.d.ts +2 -0
- package/dist/server/worker-policy.js +177 -0
- package/dist/server/worker-policy.js.map +7 -0
- package/dist/status.d.ts +4 -0
- package/package.json +63 -3
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { IndexedWorktree } from "./cleanup-worktree-index.js";
|
|
2
|
+
import type { RawHarnessWorkerStatus } from "./status.js";
|
|
3
|
+
/**
|
|
4
|
+
* Whether a persisted `completionBlocker` should still block whole-worktree removal.
|
|
5
|
+
*
|
|
6
|
+
* Dead workers with landed/clean work may keep replay metadata on disk after the
|
|
7
|
+
* board advanced externally — those blockers are stale for GC and must not pin
|
|
8
|
+
* worktrees indefinitely.
|
|
9
|
+
*/
|
|
10
|
+
export declare function completionBlockerBlocksWorktreeRemoval(indexed: IndexedWorktree, status?: RawHarnessWorkerStatus): boolean;
|
package/dist/cleanup-guards.d.ts
CHANGED
|
@@ -1,13 +1,8 @@
|
|
|
1
1
|
import type { CleanupSkipReason, WorktreeRemovalGuardHook } from "./cleanup-types.js";
|
|
2
2
|
import type { IndexedWorktree } from "./cleanup-worktree-index.js";
|
|
3
3
|
import type { CleanupRunLivenessContext } from "./cleanup-run-liveness.js";
|
|
4
|
-
import type { GitAncestry } from "./git.js";
|
|
5
|
-
import type { RawHarnessWorkerStatus } from "./status.js";
|
|
6
4
|
export { materialWorktreeChanges } from "./cleanup-guards-helpers.js";
|
|
7
|
-
|
|
8
|
-
export declare function isLandedGitAncestry(ancestry: GitAncestry | null | undefined): boolean;
|
|
9
|
-
/** Blocks whole-worktree removal when commits are not landed or tree is dirty. */
|
|
10
|
-
export declare function isPrOrUnmergedWork(status: RawHarnessWorkerStatus): boolean;
|
|
5
|
+
export { isLandedGitAncestry, isPrOrUnmergedWork } from "./cleanup-worktree-salvage.js";
|
|
11
6
|
export interface WorktreeGuardInput {
|
|
12
7
|
indexed: IndexedWorktree | null;
|
|
13
8
|
/** Resolved worktree directory (required for overlay guards on orphans). */
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { GitAncestry } from "./git.js";
|
|
2
|
+
import type { RawHarnessWorkerStatus } from "./status.js";
|
|
3
|
+
export declare function prUrlFromFinalResult(finalResult: unknown): string | null;
|
|
4
|
+
/** True when git ancestry shows the worker branch is fully landed on the run base. */
|
|
5
|
+
export declare function isLandedGitAncestry(ancestry: GitAncestry | null | undefined): boolean;
|
|
6
|
+
/** Blocks whole-worktree removal when commits are not landed or tree is dirty. */
|
|
7
|
+
export declare function isPrOrUnmergedWork(status: RawHarnessWorkerStatus): boolean;
|
package/dist/cli.js
CHANGED
|
@@ -1783,7 +1783,7 @@ var NO_START_MS = 18e4;
|
|
|
1783
1783
|
var STALE_MS = 6e5;
|
|
1784
1784
|
function computeAttention(input) {
|
|
1785
1785
|
const now = Date.now();
|
|
1786
|
-
if (input.completionBlocker) {
|
|
1786
|
+
if (input.completionBlocker && !isSkippedTerminalCompletionBlocker(input.completionBlocker)) {
|
|
1787
1787
|
return { state: "blocked", reason: input.completionBlocker };
|
|
1788
1788
|
}
|
|
1789
1789
|
if (input.finalResult) {
|
|
@@ -1821,6 +1821,9 @@ function computeAttention(input) {
|
|
|
1821
1821
|
return { state: "done", reason: "final result recorded" };
|
|
1822
1822
|
}
|
|
1823
1823
|
if (!input.alive) {
|
|
1824
|
+
if (isAbandonedEmptyWorker(input)) {
|
|
1825
|
+
return { state: "done", reason: "empty abandoned worker record" };
|
|
1826
|
+
}
|
|
1824
1827
|
const classified = classifyExitFailure(input.error);
|
|
1825
1828
|
if (classified) return { state: "blocked", reason: classified.reason };
|
|
1826
1829
|
const salvage = assessExitedWorkerSalvage({
|
|
@@ -1855,6 +1858,19 @@ function computeAttention(input) {
|
|
|
1855
1858
|
}
|
|
1856
1859
|
return { state: "ok", reason: "recent activity" };
|
|
1857
1860
|
}
|
|
1861
|
+
function isSkippedTerminalCompletionBlocker(reason) {
|
|
1862
|
+
const text = reason?.trim();
|
|
1863
|
+
if (!text) return false;
|
|
1864
|
+
return /completion acknowledged but board not advanced/i.test(text) && /task already terminal/i.test(text);
|
|
1865
|
+
}
|
|
1866
|
+
function isAbandonedEmptyWorker(input) {
|
|
1867
|
+
if (input.finalResult) return false;
|
|
1868
|
+
if (input.taskId || input.agentOsId) return false;
|
|
1869
|
+
if (input.stdoutBytes > 0 || (input.stderrBytes ?? 0) > 0 || input.heartbeatBytes > 0) return false;
|
|
1870
|
+
if (input.error?.trim()) return false;
|
|
1871
|
+
if ((input.changedFiles ?? []).some((line) => line.trim())) return false;
|
|
1872
|
+
return /empty worker dir|marked abandoned/i.test(input.reconcileReason ?? "");
|
|
1873
|
+
}
|
|
1858
1874
|
function hasMergedTargetPrReconciliation(value) {
|
|
1859
1875
|
let record = null;
|
|
1860
1876
|
if (typeof value === "string") record = extractEmbeddedWorkerFinalResultRecord(value);
|
|
@@ -1899,6 +1915,7 @@ function computeWorkerStatus(worker, options = {}) {
|
|
|
1899
1915
|
]);
|
|
1900
1916
|
const error = parsed.error || (!alive && !finalResult ? tailFile(worker.stderrPath, 10).trim() || void 0 : void 0);
|
|
1901
1917
|
const completionBlocker = typeof worker.completionBlocker === "string" && worker.completionBlocker.trim() ? worker.completionBlocker.trim() : null;
|
|
1918
|
+
const effectiveCompletionBlocker = isSkippedTerminalCompletionBlocker(completionBlocker) ? null : completionBlocker;
|
|
1902
1919
|
const landingContract = worker.repairTargetPrUrl ? {
|
|
1903
1920
|
landingOnly: false,
|
|
1904
1921
|
targetPrUrls: [worker.repairTargetPrUrl],
|
|
@@ -1910,6 +1927,7 @@ function computeWorkerStatus(worker, options = {}) {
|
|
|
1910
1927
|
finalResult,
|
|
1911
1928
|
firstEventAt: parsed.firstEventAt,
|
|
1912
1929
|
stdoutBytes,
|
|
1930
|
+
stderrBytes,
|
|
1913
1931
|
heartbeatBytes,
|
|
1914
1932
|
lastActivityAt,
|
|
1915
1933
|
heartbeatBlocker: heartbeat.heartbeatBlocker,
|
|
@@ -1917,12 +1935,15 @@ function computeWorkerStatus(worker, options = {}) {
|
|
|
1917
1935
|
error,
|
|
1918
1936
|
changedFiles,
|
|
1919
1937
|
gitAncestry,
|
|
1920
|
-
completionBlocker,
|
|
1938
|
+
completionBlocker: effectiveCompletionBlocker,
|
|
1921
1939
|
landingContract,
|
|
1922
1940
|
prUrl: worker.repairTargetPrUrl ?? worker.taskPrUrl ?? null,
|
|
1923
|
-
localOnly: worker.localOnly === true
|
|
1941
|
+
localOnly: worker.localOnly === true,
|
|
1942
|
+
taskId: worker.taskId ?? null,
|
|
1943
|
+
agentOsId: worker.agentOsId ?? null,
|
|
1944
|
+
reconcileReason: worker.reconcileReason ?? null
|
|
1924
1945
|
});
|
|
1925
|
-
const workerStatusLabel =
|
|
1946
|
+
const workerStatusLabel = effectiveCompletionBlocker || attention.state === "blocked" ? "blocked" : completionAcknowledged || attention.state === "done" ? "done" : finalResult ? "exited" : alive ? "running" : "exited";
|
|
1926
1947
|
return {
|
|
1927
1948
|
runId: worker.runId,
|
|
1928
1949
|
worker: worker.name,
|
|
@@ -2839,6 +2860,9 @@ function enforceCursorWorkerProvider(input) {
|
|
|
2839
2860
|
if (taskAllowsClaudeWorker(task)) {
|
|
2840
2861
|
return routing;
|
|
2841
2862
|
}
|
|
2863
|
+
if (routing.rule === "explicit:cli" && isClaudeFamilyProvider(routing.provider)) {
|
|
2864
|
+
return routing;
|
|
2865
|
+
}
|
|
2842
2866
|
if (!isClaudeFamilyProvider(routing.provider)) {
|
|
2843
2867
|
return routing;
|
|
2844
2868
|
}
|
|
@@ -3872,7 +3896,7 @@ function resolveWorkerLaunch(input) {
|
|
|
3872
3896
|
if (!input.task || Object.keys(input.task).length === 0) {
|
|
3873
3897
|
return afterCursorPolicy;
|
|
3874
3898
|
}
|
|
3875
|
-
if (afterCursorPolicy.rule === "explicit:model_provider_alias" || afterCursorPolicy.rule === "explicit:model_provider_alias_overrode_provider") {
|
|
3899
|
+
if (afterCursorPolicy.rule === "explicit:model_provider_alias" || afterCursorPolicy.rule === "explicit:model_provider_alias_overrode_provider" || afterCursorPolicy.rule === "explicit:cli") {
|
|
3876
3900
|
return afterCursorPolicy;
|
|
3877
3901
|
}
|
|
3878
3902
|
if (isClaudeFamilyProvider(afterCursorPolicy.provider) && (input.explicitProviderIsOperatorOverride || taskAllowsClaudeWorker(input.task))) {
|
|
@@ -4486,6 +4510,7 @@ function asString(value) {
|
|
|
4486
4510
|
var ADVANCED_OUTCOMES = /* @__PURE__ */ new Set([
|
|
4487
4511
|
"review_scheduled",
|
|
4488
4512
|
"review_already_scheduled",
|
|
4513
|
+
"skipped_terminal_task",
|
|
4489
4514
|
"closed",
|
|
4490
4515
|
"dispatched",
|
|
4491
4516
|
"dispatch_already_done"
|
|
@@ -4833,6 +4858,36 @@ function defaultPrBody(taskId, workerName, runId) {
|
|
|
4833
4858
|
"Opened by orchestrator completion enforcement so production review receives a reviewable artifact."
|
|
4834
4859
|
].filter(Boolean).join("\n");
|
|
4835
4860
|
}
|
|
4861
|
+
function commitDirtyToExistingPr(input) {
|
|
4862
|
+
if (input.snapshot.changedFiles.length === 0) {
|
|
4863
|
+
return {
|
|
4864
|
+
ok: true,
|
|
4865
|
+
prUrl: input.prUrl,
|
|
4866
|
+
headCommit: input.snapshot.headCommit ?? resolveHeadCommit(input.snapshot.worktreePath, input.exec) ?? void 0
|
|
4867
|
+
};
|
|
4868
|
+
}
|
|
4869
|
+
const pushResult = commitAndPushBranch({
|
|
4870
|
+
worktreePath: input.snapshot.worktreePath,
|
|
4871
|
+
branch: input.snapshot.branch,
|
|
4872
|
+
commitMessage: input.commitMessage,
|
|
4873
|
+
hasDirtyFiles: true,
|
|
4874
|
+
exec: input.exec
|
|
4875
|
+
});
|
|
4876
|
+
if (!pushResult.ok) {
|
|
4877
|
+
return {
|
|
4878
|
+
ok: false,
|
|
4879
|
+
reason: `PR-ready handoff blocked: ${pushResult.detail ?? "git commit/push failed"}`,
|
|
4880
|
+
nextAction: "Commit and push the dirty worker changes to the existing PR branch, then rerun `kynver worker complete`."
|
|
4881
|
+
};
|
|
4882
|
+
}
|
|
4883
|
+
return {
|
|
4884
|
+
ok: true,
|
|
4885
|
+
prUrl: input.prUrl,
|
|
4886
|
+
headCommit: pushResult.headCommit ?? input.snapshot.headCommit ?? void 0,
|
|
4887
|
+
committed: pushResult.committed,
|
|
4888
|
+
pushed: pushResult.pushed
|
|
4889
|
+
};
|
|
4890
|
+
}
|
|
4836
4891
|
function ensurePrReadyHandoff(input, exec = defaultPrHandoffExec) {
|
|
4837
4892
|
const prUrlHint = input.prUrlHint ?? extractPrUrlFromText(input.status.finalResult) ?? null;
|
|
4838
4893
|
const snapshot = buildPrHandoffSnapshotFromStatus(input.status, {
|
|
@@ -4856,10 +4911,23 @@ function ensurePrReadyHandoff(input, exec = defaultPrHandoffExec) {
|
|
|
4856
4911
|
snapshot
|
|
4857
4912
|
});
|
|
4858
4913
|
if (!requirement.required) {
|
|
4859
|
-
|
|
4914
|
+
if (prUrlHint) {
|
|
4915
|
+
return commitDirtyToExistingPr({
|
|
4916
|
+
snapshot,
|
|
4917
|
+
prUrl: prUrlHint,
|
|
4918
|
+
commitMessage: `chore(harness): update PR handoff for ${input.worker.name}`,
|
|
4919
|
+
exec
|
|
4920
|
+
});
|
|
4921
|
+
}
|
|
4922
|
+
return { ok: true };
|
|
4860
4923
|
}
|
|
4861
4924
|
if (prUrlHint) {
|
|
4862
|
-
return {
|
|
4925
|
+
return commitDirtyToExistingPr({
|
|
4926
|
+
snapshot,
|
|
4927
|
+
prUrl: prUrlHint,
|
|
4928
|
+
commitMessage: `chore(harness): update PR handoff for ${input.worker.name}`,
|
|
4929
|
+
exec
|
|
4930
|
+
});
|
|
4863
4931
|
}
|
|
4864
4932
|
if (!ghAvailable(exec)) {
|
|
4865
4933
|
const dirty = snapshot.changedFiles.length;
|
|
@@ -4922,11 +4990,12 @@ function ensurePrReadyHandoff(input, exec = defaultPrHandoffExec) {
|
|
|
4922
4990
|
}
|
|
4923
4991
|
const existing = findOpenPrUrl(snapshot.worktreePath, repo, snapshot.branch, exec);
|
|
4924
4992
|
if (existing) {
|
|
4925
|
-
return {
|
|
4926
|
-
|
|
4993
|
+
return commitDirtyToExistingPr({
|
|
4994
|
+
snapshot,
|
|
4927
4995
|
prUrl: existing,
|
|
4928
|
-
|
|
4929
|
-
|
|
4996
|
+
commitMessage: `chore(harness): update PR handoff for ${input.worker.name}`,
|
|
4997
|
+
exec
|
|
4998
|
+
});
|
|
4930
4999
|
}
|
|
4931
5000
|
const hasDirty = snapshot.changedFiles.length > 0;
|
|
4932
5001
|
let committed = false;
|
|
@@ -5322,6 +5391,11 @@ function deriveNextAction(input) {
|
|
|
5322
5391
|
}
|
|
5323
5392
|
return null;
|
|
5324
5393
|
}
|
|
5394
|
+
function isSkippedTerminalCompletionBlocker2(reason) {
|
|
5395
|
+
const text = reason?.trim();
|
|
5396
|
+
if (!text) return false;
|
|
5397
|
+
return /completion acknowledged but board not advanced/i.test(text) && /task already terminal/i.test(text);
|
|
5398
|
+
}
|
|
5325
5399
|
function deriveHandoffState(input) {
|
|
5326
5400
|
if (input.prUrl) return "pr_handoff";
|
|
5327
5401
|
if (input.headCommit) return "commit_handoff";
|
|
@@ -5378,6 +5452,23 @@ async function tryCompleteWorker(args) {
|
|
|
5378
5452
|
if (!forceReplay && shouldReplayHarnessCompletion(worker)) {
|
|
5379
5453
|
clearCompletionBlockerForReplay(worker);
|
|
5380
5454
|
}
|
|
5455
|
+
const skipPrHandoff = args.skipPrHandoff === true || args.skipPrHandoff === "true";
|
|
5456
|
+
if (!skipPrHandoff && worker.dispatched && taskId) {
|
|
5457
|
+
const handoff = ensurePrReadyHandoff({ worker, run, status });
|
|
5458
|
+
if (!handoff.ok) {
|
|
5459
|
+
persistCompletionBlocker(worker, handoff.reason);
|
|
5460
|
+
return {
|
|
5461
|
+
ok: false,
|
|
5462
|
+
reason: handoff.reason,
|
|
5463
|
+
nextAction: handoff.nextAction,
|
|
5464
|
+
completionBlocked: true
|
|
5465
|
+
};
|
|
5466
|
+
}
|
|
5467
|
+
if (handoff.prUrl || handoff.headCommit) {
|
|
5468
|
+
status = computeWorkerStatus(worker, workerStatusOptions(run));
|
|
5469
|
+
status = applyPrHandoffToStatus(status, handoff);
|
|
5470
|
+
}
|
|
5471
|
+
}
|
|
5381
5472
|
const headCommit = status.gitAncestry.headIsAncestorOfBase === false && status.gitAncestry.head ? status.gitAncestry.head : status.headCommit;
|
|
5382
5473
|
if (worker.dispatched) {
|
|
5383
5474
|
const handoff = assessWorktreeCompletionHandoff({
|
|
@@ -5398,22 +5489,6 @@ async function tryCompleteWorker(args) {
|
|
|
5398
5489
|
};
|
|
5399
5490
|
}
|
|
5400
5491
|
}
|
|
5401
|
-
const skipPrHandoff = args.skipPrHandoff === true || args.skipPrHandoff === "true";
|
|
5402
|
-
if (!skipPrHandoff && worker.dispatched && taskId) {
|
|
5403
|
-
const handoff = ensurePrReadyHandoff({ worker, run, status });
|
|
5404
|
-
if (!handoff.ok) {
|
|
5405
|
-
persistCompletionBlocker(worker, handoff.reason);
|
|
5406
|
-
return {
|
|
5407
|
-
ok: false,
|
|
5408
|
-
reason: handoff.reason,
|
|
5409
|
-
nextAction: handoff.nextAction,
|
|
5410
|
-
completionBlocked: true
|
|
5411
|
-
};
|
|
5412
|
-
}
|
|
5413
|
-
if (handoff.prUrl || handoff.headCommit) {
|
|
5414
|
-
status = applyPrHandoffToStatus(status, handoff);
|
|
5415
|
-
}
|
|
5416
|
-
}
|
|
5417
5492
|
const base = resolveBaseUrl(args.baseUrl ? String(args.baseUrl) : void 0);
|
|
5418
5493
|
const explicitSecret = args.secret ? String(args.secret) : void 0;
|
|
5419
5494
|
let secret = await resolveCallbackSecretWithMint(explicitSecret, agentOsId, { baseUrl: base });
|
|
@@ -5619,7 +5694,8 @@ function buildWorkerBoardEntry(input) {
|
|
|
5619
5694
|
headCommit
|
|
5620
5695
|
});
|
|
5621
5696
|
const rawBlocker = worker.completionBlocker;
|
|
5622
|
-
const
|
|
5697
|
+
const rawCompletionBlocker = typeof rawBlocker === "string" && rawBlocker ? rawBlocker : void 0;
|
|
5698
|
+
const completionBlocker = isSkippedTerminalCompletionBlocker2(rawCompletionBlocker) ? void 0 : rawCompletionBlocker;
|
|
5623
5699
|
const boardStatus = completionBlocker ? "blocked" : status.status;
|
|
5624
5700
|
const boardAttention = completionBlocker ? "blocked" : status.attention.state;
|
|
5625
5701
|
const completionResponse = asRecord3(worker.completionResponse);
|
|
@@ -5704,7 +5780,8 @@ function buildWorkerBoardEntry(input) {
|
|
|
5704
5780
|
}
|
|
5705
5781
|
function isMetadataTerminalDone(worker) {
|
|
5706
5782
|
const status = typeof worker.status === "string" ? worker.status : "";
|
|
5707
|
-
const
|
|
5783
|
+
const rawCompletionBlocker = typeof worker.completionBlocker === "string" && worker.completionBlocker.trim().length > 0 ? worker.completionBlocker : void 0;
|
|
5784
|
+
const completionBlocker = rawCompletionBlocker && !isSkippedTerminalCompletionBlocker2(rawCompletionBlocker);
|
|
5708
5785
|
if (completionBlocker) return false;
|
|
5709
5786
|
if (typeof worker.completionReportedAt === "string" && worker.completionReportedAt.trim()) return true;
|
|
5710
5787
|
if (worker.completionOutcome === "acknowledged") return true;
|
|
@@ -6966,6 +7043,11 @@ function readAdmissionExhaustion(result) {
|
|
|
6966
7043
|
if (!raw || typeof raw !== "object") return null;
|
|
6967
7044
|
return raw;
|
|
6968
7045
|
}
|
|
7046
|
+
function readDispatchSkipDrain(result) {
|
|
7047
|
+
const raw = result.dispatchSkipDrain;
|
|
7048
|
+
if (!raw || typeof raw !== "object") return null;
|
|
7049
|
+
return raw;
|
|
7050
|
+
}
|
|
6969
7051
|
function readHarnessWorkerContext(decision) {
|
|
6970
7052
|
const raw = decision.harnessWorkerContext;
|
|
6971
7053
|
if (!raw || typeof raw !== "object") return null;
|
|
@@ -7129,6 +7211,7 @@ async function dispatchRun(args) {
|
|
|
7129
7211
|
const result = first.result;
|
|
7130
7212
|
if (dryRun) {
|
|
7131
7213
|
const admissionExhaustion2 = readAdmissionExhaustion(result);
|
|
7214
|
+
const dispatchSkipDrain2 = readDispatchSkipDrain(result);
|
|
7132
7215
|
const summary2 = {
|
|
7133
7216
|
runId: run.id,
|
|
7134
7217
|
agentOsId,
|
|
@@ -7147,7 +7230,8 @@ async function dispatchRun(args) {
|
|
|
7147
7230
|
pagesScanned: result.pagesScanned ?? null,
|
|
7148
7231
|
candidatesExhausted: result.candidatesExhausted ?? null,
|
|
7149
7232
|
capacityIdle: admissionExhaustion2?.capacityIdle === true,
|
|
7150
|
-
admissionExhaustion: admissionExhaustion2
|
|
7233
|
+
admissionExhaustion: admissionExhaustion2,
|
|
7234
|
+
dispatchSkipDrain: dispatchSkipDrain2
|
|
7151
7235
|
};
|
|
7152
7236
|
if (pipeline) return { ok: true, ...summary2 };
|
|
7153
7237
|
console.log(JSON.stringify(summary2, null, 2));
|
|
@@ -7337,13 +7421,15 @@ async function dispatchRun(args) {
|
|
|
7337
7421
|
}
|
|
7338
7422
|
const startedCount = outcomes.filter((o) => o.started).length;
|
|
7339
7423
|
const admissionExhaustion = readAdmissionExhaustion(result);
|
|
7340
|
-
const
|
|
7424
|
+
const dispatchSkipDrain = readDispatchSkipDrain(result);
|
|
7425
|
+
const capacityIdle = startedCount === 0 && (admissionExhaustion?.capacityIdle === true || Number(result.resourceGate?.slotsAvailable) > 0);
|
|
7341
7426
|
if (capacityIdle && admissionExhaustion?.summary) {
|
|
7342
7427
|
const retryCeiling = admissionExhaustion.skipReasonCounts?.retry_ceiling_exceeded ?? 0;
|
|
7343
|
-
const recovery = admissionExhaustion.overAttemptIdleRecovery;
|
|
7428
|
+
const recovery = result.overAttemptIdleRecovery ?? admissionExhaustion.overAttemptIdleRecovery;
|
|
7344
7429
|
const recoveryNote = recovery?.attempted === true ? `; over_attempt_recovery minted=${recovery.minted ?? 0} started=${recovery.started ?? 0}` : retryCeiling > 0 ? "; over_attempt_recovery not attempted" : "";
|
|
7430
|
+
const drainNote = dispatchSkipDrain ? `; dispatch_skip_drain scanned=${dispatchSkipDrain.scanned ?? 0} advanced=${dispatchSkipDrain.advanced ?? 0}` : "";
|
|
7345
7431
|
console.error(
|
|
7346
|
-
`[dispatch] ${admissionExhaustion.summary}${retryCeiling > 0 ? `; retry_ceiling_exceeded=${retryCeiling}` : ""}${recoveryNote}`
|
|
7432
|
+
`[dispatch] ${admissionExhaustion.summary}${retryCeiling > 0 ? `; retry_ceiling_exceeded=${retryCeiling}` : ""}${recoveryNote}${drainNote}`
|
|
7347
7433
|
);
|
|
7348
7434
|
}
|
|
7349
7435
|
const summary = {
|
|
@@ -7354,6 +7440,7 @@ async function dispatchRun(args) {
|
|
|
7354
7440
|
startedCount,
|
|
7355
7441
|
capacityIdle,
|
|
7356
7442
|
admissionExhaustion,
|
|
7443
|
+
dispatchSkipDrain,
|
|
7357
7444
|
outcomes,
|
|
7358
7445
|
skipped: skipped.map((d) => ({
|
|
7359
7446
|
taskId: d.task.id,
|
|
@@ -9354,30 +9441,7 @@ function indexedWorktreeHasMaterialChanges(entry) {
|
|
|
9354
9441
|
return materialWorktreeChanges2(gitStatusShort(entry.worktreePath)).length > 0;
|
|
9355
9442
|
}
|
|
9356
9443
|
|
|
9357
|
-
// src/cleanup-
|
|
9358
|
-
function deriveRunTerminal(indexed, ctx) {
|
|
9359
|
-
if (ctx) return ctx.runTerminalCache.derive(indexed.run);
|
|
9360
|
-
return deriveTerminalRunStatus(indexed.run);
|
|
9361
|
-
}
|
|
9362
|
-
function isWorkerProcessLive(indexed) {
|
|
9363
|
-
if (isPidAlive(indexed.worker.pid)) return true;
|
|
9364
|
-
if (!indexed.worker.pid) return indexedWorktreeStatus(indexed).alive;
|
|
9365
|
-
return false;
|
|
9366
|
-
}
|
|
9367
|
-
function isRunStaleActive(indexed, ctx) {
|
|
9368
|
-
if (TERMINAL_RUN_STATUSES.has(indexed.run.status)) return false;
|
|
9369
|
-
return deriveRunTerminal(indexed, ctx) !== null;
|
|
9370
|
-
}
|
|
9371
|
-
function runBlocksWorktreeRemoval(indexed, ctx) {
|
|
9372
|
-
if (isWorkerProcessLive(indexed)) return true;
|
|
9373
|
-
if (indexed.worker.completionBlocker) return true;
|
|
9374
|
-
if (TERMINAL_RUN_STATUSES.has(indexed.run.status)) return false;
|
|
9375
|
-
if (isRunStaleActive(indexed, ctx)) return false;
|
|
9376
|
-
if (!isFinishedWorkerStatus(indexedWorktreeStatus(indexed))) return true;
|
|
9377
|
-
return deriveRunTerminal(indexed, ctx) === null;
|
|
9378
|
-
}
|
|
9379
|
-
|
|
9380
|
-
// src/cleanup-guards.ts
|
|
9444
|
+
// src/cleanup-worktree-salvage.ts
|
|
9381
9445
|
function prUrlFromFinalResult(finalResult) {
|
|
9382
9446
|
if (typeof finalResult === "string") {
|
|
9383
9447
|
const match = finalResult.match(/https:\/\/github\.com\/[^\s]+\/pull\/\d+/i);
|
|
@@ -9402,12 +9466,63 @@ function isPrOrUnmergedWork(status) {
|
|
|
9402
9466
|
if (status.changedFiles.length > 0 && status.finalResult) return true;
|
|
9403
9467
|
return false;
|
|
9404
9468
|
}
|
|
9469
|
+
|
|
9470
|
+
// src/cleanup-completion-blocker.ts
|
|
9471
|
+
function completionBlockerBlocksWorktreeRemoval(indexed, status) {
|
|
9472
|
+
const blocker = typeof indexed.worker.completionBlocker === "string" ? indexed.worker.completionBlocker.trim() : "";
|
|
9473
|
+
if (!blocker) return false;
|
|
9474
|
+
if (isWorkerProcessLive(indexed)) return true;
|
|
9475
|
+
const resolved = status ?? indexedWorktreeStatus(indexed);
|
|
9476
|
+
if (!isFinishedWorkerStatus(resolved)) return true;
|
|
9477
|
+
if (isPrOrUnmergedWork(resolved)) return true;
|
|
9478
|
+
if (materialWorktreeChanges2(resolved.changedFiles).length > 0) return true;
|
|
9479
|
+
const landing = assessWorkerLanding({
|
|
9480
|
+
finalResult: resolved.finalResult,
|
|
9481
|
+
changedFiles: resolved.changedFiles,
|
|
9482
|
+
gitAncestry: resolved.gitAncestry,
|
|
9483
|
+
prUrl: prUrlFromFinalResult(resolved.finalResult)
|
|
9484
|
+
});
|
|
9485
|
+
if (landing.blocked) return true;
|
|
9486
|
+
return false;
|
|
9487
|
+
}
|
|
9488
|
+
|
|
9489
|
+
// src/cleanup-run-liveness.ts
|
|
9490
|
+
function deriveRunTerminal(indexed, ctx) {
|
|
9491
|
+
if (ctx) return ctx.runTerminalCache.derive(indexed.run);
|
|
9492
|
+
return deriveTerminalRunStatus(indexed.run);
|
|
9493
|
+
}
|
|
9494
|
+
function isWorkerProcessLive(indexed) {
|
|
9495
|
+
if (isPidAlive(indexed.worker.pid)) return true;
|
|
9496
|
+
if (!indexed.worker.pid) return indexedWorktreeStatus(indexed).alive;
|
|
9497
|
+
return false;
|
|
9498
|
+
}
|
|
9499
|
+
function isRunStaleActive(indexed, ctx) {
|
|
9500
|
+
if (TERMINAL_RUN_STATUSES.has(indexed.run.status)) return false;
|
|
9501
|
+
return deriveRunTerminal(indexed, ctx) !== null;
|
|
9502
|
+
}
|
|
9503
|
+
function runBlocksWorktreeRemoval(indexed, ctx) {
|
|
9504
|
+
if (isWorkerProcessLive(indexed)) return true;
|
|
9505
|
+
const status = indexedWorktreeStatus(indexed);
|
|
9506
|
+
if (completionBlockerBlocksWorktreeRemoval(indexed, status)) return true;
|
|
9507
|
+
if (isFinishedWorkerStatus(status)) return false;
|
|
9508
|
+
if (TERMINAL_RUN_STATUSES.has(indexed.run.status)) return false;
|
|
9509
|
+
if (isRunStaleActive(indexed, ctx)) return false;
|
|
9510
|
+
return deriveRunTerminal(indexed, ctx) === null;
|
|
9511
|
+
}
|
|
9512
|
+
|
|
9513
|
+
// src/cleanup-guards.ts
|
|
9405
9514
|
function effectiveWorktreeAgeMs(input) {
|
|
9406
9515
|
const { indexed, includeOrphans, worktreesAgeMs, terminalWorktreesAgeMs } = input;
|
|
9407
9516
|
if (!indexed) return includeOrphans ? terminalWorktreesAgeMs : worktreesAgeMs;
|
|
9408
9517
|
if (TERMINAL_RUN_STATUSES.has(indexed.run.status)) {
|
|
9409
9518
|
return terminalWorktreesAgeMs;
|
|
9410
9519
|
}
|
|
9520
|
+
if (input.liveness && isRunStaleActive(indexed, input.liveness)) {
|
|
9521
|
+
return terminalWorktreesAgeMs;
|
|
9522
|
+
}
|
|
9523
|
+
if (input.liveness && isFinishedWorkerStatus(indexedWorktreeStatus(indexed)) && !isWorkerProcessLive(indexed)) {
|
|
9524
|
+
return terminalWorktreesAgeMs;
|
|
9525
|
+
}
|
|
9411
9526
|
return worktreesAgeMs;
|
|
9412
9527
|
}
|
|
9413
9528
|
function skipWorktreeRemoval(input) {
|
|
@@ -9421,7 +9536,7 @@ function skipWorktreeRemoval(input) {
|
|
|
9421
9536
|
if (ageThresholdMs > 0 && ageMs < ageThresholdMs) return "below_age_threshold";
|
|
9422
9537
|
const status = indexedWorktreeStatus(indexed);
|
|
9423
9538
|
if (isWorkerProcessLive(indexed)) return "active_worker";
|
|
9424
|
-
if (indexed
|
|
9539
|
+
if (completionBlockerBlocksWorktreeRemoval(indexed, status)) return "completion_blocked";
|
|
9425
9540
|
if (runBlocksWorktreeRemoval(indexed, input.liveness)) return "run_still_active";
|
|
9426
9541
|
if (!isFinishedWorkerStatus(status)) return "run_still_active";
|
|
9427
9542
|
if (isPrOrUnmergedWork(status)) return "pr_or_unmerged_commits";
|
|
@@ -9577,7 +9692,9 @@ function skipRunDirectoryRemoval(input) {
|
|
|
9577
9692
|
}
|
|
9578
9693
|
if (!runDirectoryIsEmpty(runPath)) return "run_still_active";
|
|
9579
9694
|
const run = loadRunStatus(harnessRoot, runId);
|
|
9580
|
-
if (run && !TERMINAL_RUN_STATUSES.has(run.status))
|
|
9695
|
+
if (run && !TERMINAL_RUN_STATUSES.has(run.status)) {
|
|
9696
|
+
if (!deriveTerminalRunStatus(run)) return "run_still_active";
|
|
9697
|
+
}
|
|
9581
9698
|
if (runDirectoriesAgeMs > 0 && ageMs < runDirectoriesAgeMs) return "below_age_threshold";
|
|
9582
9699
|
return null;
|
|
9583
9700
|
}
|
|
@@ -10667,14 +10784,14 @@ function runHarnessCleanup(options = {}) {
|
|
|
10667
10784
|
const paths = resolvePaths(options);
|
|
10668
10785
|
emitCleanupProgress("scan", `${paths.scanRoots.length} harness root(s)`);
|
|
10669
10786
|
const activeGuards = collectActiveWorktreeGuards(paths.scanRoots, paths.now);
|
|
10670
|
-
emitCleanupProgress("index", "building worktree index");
|
|
10671
|
-
const index = mergeWorktreeIndexes(paths.scanRoots);
|
|
10672
|
-
emitCleanupProgress("index", `${index.size} indexed worktree(s)`);
|
|
10673
|
-
const liveness = { runTerminalCache: new CleanupRunTerminalCache() };
|
|
10674
10787
|
const finalizedRuns = retention.finalizeStaleRuns ? finalizeStaleRuns().map((f) => ({ runId: f.runId, from: f.from, to: f.to })) : [];
|
|
10675
10788
|
if (finalizedRuns.length > 0) {
|
|
10676
10789
|
emitCleanupProgress("finalize", `${finalizedRuns.length} stale run(s) marked terminal`);
|
|
10677
10790
|
}
|
|
10791
|
+
emitCleanupProgress("index", "building worktree index");
|
|
10792
|
+
const index = mergeWorktreeIndexes(paths.scanRoots);
|
|
10793
|
+
emitCleanupProgress("index", `${index.size} indexed worktree(s)`);
|
|
10794
|
+
const liveness = { runTerminalCache: new CleanupRunTerminalCache() };
|
|
10678
10795
|
const skips = [];
|
|
10679
10796
|
const actions = [];
|
|
10680
10797
|
const processedPaths = /* @__PURE__ */ new Set();
|
|
@@ -14037,7 +14154,7 @@ async function runLandingMaintainerLaneTick(args) {
|
|
|
14037
14154
|
const agentOsId = String(required(String(args.agentOsId || ""), "--agent-os-id"));
|
|
14038
14155
|
const repoSlug = String(args.repo || LANDING_MAINTAINER_LANE_SPEC.defaultRepo).trim();
|
|
14039
14156
|
const fleet = args.fleet === true || args.fleet === "true";
|
|
14040
|
-
const execute = args.execute
|
|
14157
|
+
const execute = args.execute === true || args.execute === "true";
|
|
14041
14158
|
const runId = args.run ? String(args.run) : void 0;
|
|
14042
14159
|
const resourceGate = observeRunnerResourceGate({
|
|
14043
14160
|
runId: runId ?? "fleet-lane-tick"
|