@on-par/factory-cli 0.9.11-next.4 → 0.9.11-next.41
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/README.md +1 -1
- package/dist/cli/base-health-ci.d.ts +4 -0
- package/dist/cli/base-health-ci.d.ts.map +1 -0
- package/dist/cli/base-health-ci.js +9 -0
- package/dist/cli/base-health-ci.js.map +1 -0
- package/dist/cli/base-hold.d.ts +14 -11
- package/dist/cli/base-hold.d.ts.map +1 -1
- package/dist/cli/base-hold.js +43 -20
- package/dist/cli/base-hold.js.map +1 -1
- package/dist/cli/cost-json.d.ts +5 -31
- package/dist/cli/cost-json.d.ts.map +1 -1
- package/dist/cli/cost-json.js.map +1 -1
- package/dist/cli/daemon.d.ts +2 -15
- package/dist/cli/daemon.d.ts.map +1 -1
- package/dist/cli/daemon.js +5 -11
- package/dist/cli/daemon.js.map +1 -1
- package/dist/cli/doctor.js +1 -1
- package/dist/cli/doctor.js.map +1 -1
- package/dist/cli/index.d.ts +10 -48
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +27 -186
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/json-contract.d.ts +299 -0
- package/dist/cli/json-contract.d.ts.map +1 -0
- package/dist/cli/json-contract.js +206 -0
- package/dist/cli/json-contract.js.map +1 -0
- package/dist/cli/land-pr.d.ts.map +1 -1
- package/dist/cli/land-pr.js +5 -0
- package/dist/cli/land-pr.js.map +1 -1
- package/dist/cli/lane-environment-pause-error.d.ts +19 -0
- package/dist/cli/lane-environment-pause-error.d.ts.map +1 -0
- package/dist/cli/lane-environment-pause-error.js +24 -0
- package/dist/cli/lane-environment-pause-error.js.map +1 -0
- package/dist/cli/logs.d.ts +14 -0
- package/dist/cli/logs.d.ts.map +1 -1
- package/dist/cli/logs.js +46 -4
- package/dist/cli/logs.js.map +1 -1
- package/dist/cli/program-observe.d.ts.map +1 -1
- package/dist/cli/program-observe.js +5 -2
- package/dist/cli/program-observe.js.map +1 -1
- package/dist/cli/queue-add-json.d.ts +3 -25
- package/dist/cli/queue-add-json.d.ts.map +1 -1
- package/dist/cli/queue-add-json.js.map +1 -1
- package/dist/cli/queue-list.d.ts +6 -24
- package/dist/cli/queue-list.d.ts.map +1 -1
- package/dist/cli/queue-list.js +6 -1
- package/dist/cli/queue-list.js.map +1 -1
- package/dist/cli/queue-preflight.d.ts +45 -0
- package/dist/cli/queue-preflight.d.ts.map +1 -0
- package/dist/cli/queue-preflight.js +277 -0
- package/dist/cli/queue-preflight.js.map +1 -0
- package/dist/cli/run-branch.d.ts +24 -0
- package/dist/cli/run-branch.d.ts.map +1 -0
- package/dist/cli/run-branch.js +38 -0
- package/dist/cli/run-branch.js.map +1 -0
- package/dist/cli/run-lane.d.ts.map +1 -1
- package/dist/cli/run-lane.js +5 -2
- package/dist/cli/run-lane.js.map +1 -1
- package/dist/cli/run.d.ts.map +1 -1
- package/dist/cli/run.js +10 -3
- package/dist/cli/run.js.map +1 -1
- package/dist/cli/runs-diffstat.d.ts +6 -25
- package/dist/cli/runs-diffstat.d.ts.map +1 -1
- package/dist/cli/runs-diffstat.js.map +1 -1
- package/dist/cli/runs.d.ts +4 -19
- package/dist/cli/runs.d.ts.map +1 -1
- package/dist/cli/runs.js +1 -1
- package/dist/cli/runs.js.map +1 -1
- package/dist/cli/self-update-stop.d.ts +7 -0
- package/dist/cli/self-update-stop.d.ts.map +1 -0
- package/dist/cli/self-update-stop.js +27 -0
- package/dist/cli/self-update-stop.js.map +1 -0
- package/dist/cli/ship-run-state.d.ts +5 -0
- package/dist/cli/ship-run-state.d.ts.map +1 -0
- package/dist/cli/ship-run-state.js +12 -0
- package/dist/cli/ship-run-state.js.map +1 -0
- package/dist/cli/ship-steps.d.ts +1 -1
- package/dist/cli/ship-steps.d.ts.map +1 -1
- package/dist/cli/ship-steps.js +5 -2
- package/dist/cli/ship-steps.js.map +1 -1
- package/dist/cli/ship.d.ts.map +1 -1
- package/dist/cli/ship.js +25 -21
- package/dist/cli/ship.js.map +1 -1
- package/dist/cli/status-json.d.ts +7 -28
- package/dist/cli/status-json.d.ts.map +1 -1
- package/dist/cli/status-json.js +3 -1
- package/dist/cli/status-json.js.map +1 -1
- package/dist/cli/status.d.ts.map +1 -1
- package/dist/cli/status.js +4 -1
- package/dist/cli/status.js.map +1 -1
- package/dist/cli/stop-resume-json.d.ts +2 -10
- package/dist/cli/stop-resume-json.d.ts.map +1 -1
- package/dist/cli/stop-resume-json.js +1 -0
- package/dist/cli/stop-resume-json.js.map +1 -1
- package/dist/cli/usage-json.d.ts +4 -13
- package/dist/cli/usage-json.d.ts.map +1 -1
- package/dist/cli/usage-json.js.map +1 -1
- package/package.json +8 -5
- package/schemas/cost.schema.json +100 -0
- package/schemas/daemon-status.schema.json +44 -0
- package/schemas/logs-line.schema.json +114 -0
- package/schemas/queue-add.schema.json +73 -0
- package/schemas/queue-list.schema.json +77 -0
- package/schemas/resume.schema.json +36 -0
- package/schemas/runs-diffstat.schema.json +81 -0
- package/schemas/runs.schema.json +75 -0
- package/schemas/status.schema.json +200 -0
- package/schemas/stop.schema.json +36 -0
- package/schemas/unpark.schema.json +57 -0
- package/schemas/usage.schema.json +28 -0
package/dist/cli/index.js
CHANGED
|
@@ -6,18 +6,21 @@ import { createRequire } from 'node:module';
|
|
|
6
6
|
import { arch, hostname as osHostname, release, platform, userInfo } from 'node:os';
|
|
7
7
|
import { basename, dirname, isAbsolute, join, relative, resolve } from 'node:path';
|
|
8
8
|
import { promisify, styleText } from 'node:util';
|
|
9
|
+
import { LaneEnvironmentPauseError } from './lane-environment-pause-error.js';
|
|
9
10
|
import { INVALID_BRANCH_PREFIX_MESSAGE, resolveBranchPrefixOption } from './branch-prefix.js';
|
|
10
11
|
import { sweepLaneClaims, warnQueueDiagnostics } from './queue-startup.js';
|
|
11
12
|
import { proposeTriageQueue } from './triage-open-prs.js';
|
|
13
|
+
import { makeQueuePreflightOps, preflightQueuedIssue } from './queue-preflight.js';
|
|
12
14
|
import { acquirePortLease, aggregateCosts, appendKpiHistoryLine, applyRepoConfig, buildPhase, checkPhase, clearProxyState, computeHealthKpis, ConstitutionLoader, createDefaultWorkSourceRegistry, createLogger, createLaneProxy, defaultFindPortListeners, describeEffectiveConfig, detectPostMergeDefects, diagnoseModels, probeDeclaredHarnesses, estimateTrailingSpend, fetchDefectSources, fetchSubscriptionUsage, formatUsageReport, getConstitutionsDir, getFactoryPaths, GITHUB_ISSUE_SOURCE, hasUnresolvedPark, InvalidArtifactsDirError, InvalidWorkRequestInputError, InvalidWorkspaceError, isCommandAvailable, isProxyRunning, kpiBranchPrefixes, kpisToHistoryRecord, laneBaseUrl, laneEnv, laneHostLabel, loadFactoryConfigForRepo, loadModelsConfig, loadRepoConfig, loadRoutesConfig, LOCAL_BRIEF_SOURCE, joinClassifierOutcomes, mergeClassifierOutcomes, parseClassifierOutcomes, summarizeClassifierOutcomes, formatClassifierReport, mergedPrRefs, ModelRegistry, ModelRouter, parkReasonFor, parseKpiHistory, parseQueue, planPhase, readEvents, readPortLeases, readQueue, readUsage, reapOrphanProcesses, reconstructKpiEvents, recordLeasePgid, releasePortLease, renderKpiReport, renderKpiTrend, resolveDefectWindowDays, parseSizeGateMode, parseMaxSlices, SIZE_GATE_MODES, resolveEffectiveModelPins, resolveEnvironmentProxy, resolveArtifactsDir, resolveLocalOnlyPolicy, resolveMergePolicy, resolveSkipCI, resolveTimeouts, resolveUsageCap, resolveWatchdogPolicy, runAutoIngest, shipPhase, formatCostTotal, validateQueue, watchUsage, writeLocalRunReport, writeProxyState, } from '@on-par/factory-core';
|
|
13
|
-
import { acquirePidFile, createCheckResumeStore,
|
|
15
|
+
import { acquirePidFile, createCheckResumeStore, DEFAULT_GARDEN_MAX_CLUSTERS, DEFAULT_GARDEN_SINCE, capGardenClusters, clusterGarden, dedupGardenClusters, filterGardenOnlyNew, filterGardenWindow, markGardenTrackingUnknown, parseGardenDuration, readHarvestEvents, renderGardenJson, renderGardenReport, laneWorktreePath, migrateRepoConfigToYaml, REPO_CONFIG_YAML_HEADER, claudeConfigDirOverride, claudeKeychainService, cleanupWorktree, createDaemonLogSink, factoryBranchIssue, factoryBranchPrefixes, createFactorydServer, createRunRuntime, createShipExecutor, createGithubQueue, createLocalSmallDryRun, createOctokitFilingClient, createOctokitGreenPrClient, createOctokitQueueClient, daemonRuntimePaths, defaultRegistryPath, ensureDir, fetchHumanEventSourcesCached, FACTORY_RUNTIME_CONFIG_KEYS, findUnmergedGreenPrs, formatGcReport, gitFetch, listMicroVms, listOrphanContainers, logCost, logEvent, planQueueMigration, pollBlockedParks, readCosts, readGithubQueueSnapshot, reapCleanShipWorktree, reapLaneWorktree, reapOrphanContainers, reapOrphanMicroVm, releaseRuntimeFiles, releaseStaleClaims, resolveBranchPrefix, resolveEffectiveConfig, resolveExperimental, resolveLocalOnly, resolvePrClassifierPolicy, resolveReviewFloorRules, RunLockHeldError, runOvernightQueue, parseRemoteSlug, setupWorktree, shellEscape, listBuiltInCheckers, sweepWorktrees, watchChecks, withFileLock, withGitLock, withRunLock, writePortFile, buildTriagePrompt, parseTriageCandidates, recordLocalAuthFailure, laneSlug, formatStopMarker, readStopMarker, } from '@on-par/factory-core/internal';
|
|
14
16
|
import { runTui } from '@on-par/factory-tui';
|
|
17
|
+
import { clearSelfUpdateStop } from './self-update-stop.js';
|
|
15
18
|
import { admissionStateReaderFor, runQueueReconcile } from './admission.js';
|
|
16
19
|
import { cmdClassifierBacktest, ClassifierBacktestInputError, } from './classifier-backtest.js';
|
|
17
20
|
import { runHarnessCheckCommand } from './harness-check.js';
|
|
18
|
-
import {
|
|
21
|
+
import { landIssue } from './land.js';
|
|
19
22
|
import { STOP_SET_MESSAGE } from './stop-sentinel.js';
|
|
20
|
-
import { AwaitingReviewError, CiFailedError, CiUnverifiedError,
|
|
23
|
+
import { AwaitingReviewError, CiFailedError, CiUnverifiedError, LandConflictError, LandFailureError, MergeGatedError, } from './land-pr.js';
|
|
21
24
|
import { setLandRuntime } from './land-runtime.js';
|
|
22
25
|
import { cmdRun } from './run.js';
|
|
23
26
|
import { shipIssue } from './ship.js';
|
|
@@ -338,7 +341,7 @@ setShipRuntime(() => ({
|
|
|
338
341
|
IssueSkippedError,
|
|
339
342
|
IssueDecomposedError,
|
|
340
343
|
}));
|
|
341
|
-
export { shipIssue };
|
|
344
|
+
export { LaneEnvironmentPauseError, shipIssue };
|
|
342
345
|
export function hasGitHubToken(env = process.env, tryToken) {
|
|
343
346
|
if (env.GITHUB_TOKEN || env.GH_TOKEN)
|
|
344
347
|
return true;
|
|
@@ -955,6 +958,7 @@ async function cmdCheck(issueRaw, opts) {
|
|
|
955
958
|
const repoConfig = configLoaders.loadRepoConfig(repoRoot);
|
|
956
959
|
const effective = resolveEffectiveConfig(repoConfig);
|
|
957
960
|
const router = new coreOps.ModelRouter(applyRepoConfig(configLoaders.loadModelsConfig(), repoConfig), configLoaders.loadRoutesConfig(), false, undefined, effective.allowExperimental, effective.localOnly);
|
|
961
|
+
router.setCodexFullAccessTasks?.(configLoaders.loadFactoryConfigForRepo(getFactoryPaths(repoRoot).config).sandbox.codexFullAccessTasks);
|
|
958
962
|
router.setLocalAuthSink?.(recordLocalAuthFailure);
|
|
959
963
|
runModel = createDeepModelRunner(router, repoRoot);
|
|
960
964
|
}
|
|
@@ -1063,6 +1067,7 @@ async function cmdClassifierBacktestEntry(opts) {
|
|
|
1063
1067
|
const repoConfig = configLoaders.loadRepoConfig(repoRoot);
|
|
1064
1068
|
const effective = resolveEffectiveConfig(repoConfig);
|
|
1065
1069
|
const router = new coreOps.ModelRouter(applyRepoConfig(configLoaders.loadModelsConfig(), repoConfig), configLoaders.loadRoutesConfig(), false, undefined, effective.allowExperimental, effective.localOnly);
|
|
1070
|
+
router.setCodexFullAccessTasks?.(configLoaders.loadFactoryConfigForRepo(paths.config).sandbox.codexFullAccessTasks);
|
|
1066
1071
|
router.setLocalAuthSink?.(recordLocalAuthFailure);
|
|
1067
1072
|
try {
|
|
1068
1073
|
await cmdClassifierBacktest({ ...opts, branchPrefix }, {
|
|
@@ -1314,6 +1319,7 @@ async function cmdTui(opts = {}, runTuiImpl = runTui) {
|
|
|
1314
1319
|
const routesConfig = configLoaders.loadRoutesConfig();
|
|
1315
1320
|
const effective = resolveEffectiveConfig(repoConfig);
|
|
1316
1321
|
const router = new coreOps.ModelRouter(modelsConfig, routesConfig, false, undefined, effective.allowExperimental, effective.localOnly);
|
|
1322
|
+
router.setCodexFullAccessTasks?.(configLoaders.loadFactoryConfigForRepo(paths.config).sandbox.codexFullAccessTasks);
|
|
1317
1323
|
router.setLocalAuthSink?.(recordLocalAuthFailure);
|
|
1318
1324
|
const effectiveConfigLines = describeEffectiveConfig({
|
|
1319
1325
|
router,
|
|
@@ -1366,24 +1372,6 @@ export class LaneParkError extends Error {
|
|
|
1366
1372
|
return this.outcome.failingChecks;
|
|
1367
1373
|
}
|
|
1368
1374
|
}
|
|
1369
|
-
/** CHECK found every failing checker also failing on the base SHA (#1928): the issue is
|
|
1370
|
-
* released to queued (not parked) and the lane pauses. */
|
|
1371
|
-
export class LaneEnvironmentPauseError extends Error {
|
|
1372
|
-
baseSha;
|
|
1373
|
-
failingChecks;
|
|
1374
|
-
failureSignature;
|
|
1375
|
-
/** BUILD output was kept for `factory resume --from check` (#2564); runLane must not reap it. */
|
|
1376
|
-
buildPreserved;
|
|
1377
|
-
constructor(message, failure) {
|
|
1378
|
-
super(message);
|
|
1379
|
-
if (failure.buildPreserved !== undefined)
|
|
1380
|
-
this.buildPreserved = failure.buildPreserved;
|
|
1381
|
-
this.baseSha = failure.baseSha;
|
|
1382
|
-
this.failingChecks = failure.failingChecks;
|
|
1383
|
-
if (failure.failureSignature !== undefined)
|
|
1384
|
-
this.failureSignature = failure.failureSignature;
|
|
1385
|
-
}
|
|
1386
|
-
}
|
|
1387
1375
|
/** Resolves the stable lane URL a build/check agent should use, by probing whether
|
|
1388
1376
|
* a factory proxy (started by `factory run`/`supervise` or `factory proxy`) is
|
|
1389
1377
|
* currently alive for the configured domain. Never throws — an absent/dead proxy
|
|
@@ -2012,6 +2000,7 @@ async function cmdTriage(opts) {
|
|
|
2012
2000
|
const routesConfig = configLoaders.loadRoutesConfig();
|
|
2013
2001
|
const effective = resolveEffectiveConfig(repoConfig);
|
|
2014
2002
|
const router = new coreOps.ModelRouter(modelsConfig, routesConfig, false, undefined, effective.allowExperimental, effective.localOnly);
|
|
2003
|
+
router.setCodexFullAccessTasks?.(configLoaders.loadFactoryConfigForRepo(paths.config).sandbox.codexFullAccessTasks);
|
|
2015
2004
|
router.setLocalAuthSink?.(recordLocalAuthFailure);
|
|
2016
2005
|
router.skipMissingHarnessClis(coreOps.isCommandAvailable);
|
|
2017
2006
|
router.setCostSink((entry) => logCost(paths.costs, { ...entry, issue: '-' }));
|
|
@@ -2432,8 +2421,7 @@ async function cmdFactoryd(opts) {
|
|
|
2432
2421
|
const previousUpdate = readUpdateState(updateFile);
|
|
2433
2422
|
for (const stop of recoverStaleDrain(previousUpdate, (file) => {
|
|
2434
2423
|
try {
|
|
2435
|
-
|
|
2436
|
-
return true;
|
|
2424
|
+
return clearSelfUpdateStop(file);
|
|
2437
2425
|
}
|
|
2438
2426
|
catch {
|
|
2439
2427
|
return false;
|
|
@@ -2559,167 +2547,12 @@ export function laneQueueDeps(queue, lane) {
|
|
|
2559
2547
|
countRemaining: async () => (await queue.list(lane)).length,
|
|
2560
2548
|
};
|
|
2561
2549
|
}
|
|
2550
|
+
export { preflightQueuedIssue };
|
|
2562
2551
|
export function createQueuePreflightOps(octokit, owner, repo, opts = {}) {
|
|
2563
|
-
return {
|
|
2564
|
-
autoMergeStack: opts.autoMergeStack ?? false,
|
|
2565
|
-
getPullRefs: async (prNumber) => {
|
|
2566
|
-
const { data } = await octokit.rest.pulls.get({ owner, repo, pull_number: prNumber });
|
|
2567
|
-
return { headRef: data.head.ref, baseRef: data.base.ref, headSha: data.head.sha };
|
|
2568
|
-
},
|
|
2569
|
-
expectedHeadRepoFullName: `${owner}/${repo}`,
|
|
2570
|
-
findOpenPR: (issue) => findOpenPRForIssue(octokit, owner, repo, issue),
|
|
2571
|
-
getLandState: (prNumber) => getPullRequestLandState(octokit, owner, repo, prNumber),
|
|
2552
|
+
return makeQueuePreflightOps(octokit, owner, repo, {
|
|
2572
2553
|
watch: (ref) => internalOps.watchChecks({ octokit, owner, repo, ref }),
|
|
2573
|
-
|
|
2574
|
-
|
|
2575
|
-
const { data } = await octokit.rest.pulls.get({ owner, repo, pull_number: prNumber });
|
|
2576
|
-
if (data.merged || data.merged_at)
|
|
2577
|
-
return 'merged';
|
|
2578
|
-
return data.state === 'open' ? 'open' : 'closed';
|
|
2579
|
-
},
|
|
2580
|
-
saveSlicePlan: async (issue, plan) => {
|
|
2581
|
-
await upsertSlicePlanComment({ octokit, repo: `${owner}/${repo}`, issue, plan });
|
|
2582
|
-
},
|
|
2583
|
-
};
|
|
2584
|
-
}
|
|
2585
|
-
/** ADR-0147/ADR-0161: walk every pr-open slice PR bottom-up. Merged layers are recorded, a closed layer parks
|
|
2586
|
-
* (naming the layers above it), and the lowest open layer defers, or is adopted when stack auto-merge is on and
|
|
2587
|
-
* its base is main with green CI on the rebased head. Returns undefined to fall through to the Closes-PR
|
|
2588
|
-
* preflight only when no slice PR is still open. */
|
|
2589
|
-
async function preflightSlice(issue, deps) {
|
|
2590
|
-
let lookup;
|
|
2591
|
-
try {
|
|
2592
|
-
lookup = await deps.findSlicePlan(issue);
|
|
2593
|
-
}
|
|
2594
|
-
catch (err) {
|
|
2595
|
-
return { kind: 'park', reason: `slice plan lookup failed: ${errorDetail(err)}` };
|
|
2596
|
-
}
|
|
2597
|
-
if (lookup.status === 'invalid') {
|
|
2598
|
-
return {
|
|
2599
|
-
kind: 'park',
|
|
2600
|
-
reason: `slice plan comment ${lookup.commentId} on #${issue} is unreadable (${lookup.error})`,
|
|
2601
|
-
};
|
|
2602
|
-
}
|
|
2603
|
-
if (lookup.status === 'not-found')
|
|
2604
|
-
return undefined;
|
|
2605
|
-
const { plan } = lookup;
|
|
2606
|
-
const n = plan.slices.length;
|
|
2607
|
-
const open = plan.slices.filter((s) => s.state === 'pr-open');
|
|
2608
|
-
if (open.length === 0)
|
|
2609
|
-
return undefined;
|
|
2610
|
-
let next = plan;
|
|
2611
|
-
let lastMerged;
|
|
2612
|
-
const save = async () => {
|
|
2613
|
-
if (lastMerged === undefined)
|
|
2614
|
-
return undefined;
|
|
2615
|
-
try {
|
|
2616
|
-
await deps.saveSlicePlan(issue, next);
|
|
2617
|
-
}
|
|
2618
|
-
catch (err) {
|
|
2619
|
-
return {
|
|
2620
|
-
kind: 'park',
|
|
2621
|
-
reason: `${lastMerged.label} PR #${lastMerged.pr} merged but updating the slice plan failed: ${errorDetail(err)}`,
|
|
2622
|
-
};
|
|
2623
|
-
}
|
|
2624
|
-
return undefined;
|
|
2625
|
-
};
|
|
2626
|
-
for (const [at, slice] of open.entries()) {
|
|
2627
|
-
const label = `slice ${slice.index}/${n}`;
|
|
2628
|
-
if (slice.prNumber === undefined) {
|
|
2629
|
-
return { kind: 'park', reason: `${label} of #${issue} is pr-open but the slice plan records no PR number` };
|
|
2630
|
-
}
|
|
2631
|
-
const pr = slice.prNumber;
|
|
2632
|
-
let state;
|
|
2633
|
-
try {
|
|
2634
|
-
state = await deps.getPullState(pr);
|
|
2635
|
-
}
|
|
2636
|
-
catch (err) {
|
|
2637
|
-
return { kind: 'park', reason: `${label} PR #${pr} state lookup failed: ${errorDetail(err)}` };
|
|
2638
|
-
}
|
|
2639
|
-
if (state === 'merged') {
|
|
2640
|
-
next = withSliceState(next, slice.index, 'merged', pr);
|
|
2641
|
-
lastMerged = { label, pr };
|
|
2642
|
-
continue;
|
|
2643
|
-
}
|
|
2644
|
-
if (state === 'closed') {
|
|
2645
|
-
const above = open.slice(at + 1);
|
|
2646
|
-
const suffix = above.length > 0
|
|
2647
|
-
? `; layers above it: ${above.map((a) => `slice ${a.index}/${n} (#${a.prNumber})`).join(', ')}`
|
|
2648
|
-
: '';
|
|
2649
|
-
return { kind: 'park', reason: `${label} PR #${pr} was closed without merging${suffix}` };
|
|
2650
|
-
}
|
|
2651
|
-
const saved = await save();
|
|
2652
|
-
if (saved)
|
|
2653
|
-
return saved;
|
|
2654
|
-
if (!deps.autoMergeStack)
|
|
2655
|
-
return { kind: 'defer' };
|
|
2656
|
-
let refs;
|
|
2657
|
-
try {
|
|
2658
|
-
refs = await deps.getPullRefs(pr);
|
|
2659
|
-
}
|
|
2660
|
-
catch (err) {
|
|
2661
|
-
return { kind: 'park', reason: `${label} PR #${pr} ref lookup failed: ${errorDetail(err)}` };
|
|
2662
|
-
}
|
|
2663
|
-
// GitHub has not retargeted the layer to main yet: the lower layer is still unmerged.
|
|
2664
|
-
if (refs.baseRef !== 'main')
|
|
2665
|
-
return { kind: 'defer' };
|
|
2666
|
-
let outcome;
|
|
2667
|
-
try {
|
|
2668
|
-
outcome = await deps.watch(refs.headSha);
|
|
2669
|
-
}
|
|
2670
|
-
catch (err) {
|
|
2671
|
-
return { kind: 'park', reason: `CI watch for ${label} PR #${pr} failed: ${errorDetail(err)}` };
|
|
2672
|
-
}
|
|
2673
|
-
if (outcome === 'failure')
|
|
2674
|
-
return { kind: 'park', reason: `CI for ${label} PR #${pr} ended failure` };
|
|
2675
|
-
if (outcome !== 'success')
|
|
2676
|
-
return { kind: 'defer' };
|
|
2677
|
-
return { kind: 'adopt', branch: refs.headRef, stackLayer: { index: slice.index, count: n, prNumber: pr } };
|
|
2678
|
-
}
|
|
2679
|
-
return (await save()) ?? undefined;
|
|
2680
|
-
}
|
|
2681
|
-
/** Resolve queued work from GitHub evidence without consuming model or worktree resources. */
|
|
2682
|
-
export async function preflightQueuedIssue(issue, deps) {
|
|
2683
|
-
if (issue.labels.includes('factory:in-progress'))
|
|
2684
|
-
return { kind: 'defer' };
|
|
2685
|
-
const slice = await preflightSlice(issue.number, deps);
|
|
2686
|
-
if (slice)
|
|
2687
|
-
return slice;
|
|
2688
|
-
let pr;
|
|
2689
|
-
try {
|
|
2690
|
-
pr = await deps.findOpenPR(issue.number);
|
|
2691
|
-
}
|
|
2692
|
-
catch (err) {
|
|
2693
|
-
return { kind: 'park', reason: `PR lookup failed: ${errorDetail(err)}` };
|
|
2694
|
-
}
|
|
2695
|
-
if (!pr)
|
|
2696
|
-
return { kind: 'build' };
|
|
2697
|
-
if (pr.headRepoFullName !== deps.expectedHeadRepoFullName) {
|
|
2698
|
-
return { kind: 'park', reason: `PR #${pr.number} head is not in ${deps.expectedHeadRepoFullName}` };
|
|
2699
|
-
}
|
|
2700
|
-
let state;
|
|
2701
|
-
try {
|
|
2702
|
-
state = await deps.getLandState(pr.number);
|
|
2703
|
-
}
|
|
2704
|
-
catch (err) {
|
|
2705
|
-
return { kind: 'park', reason: `PR #${pr.number} merge-state lookup failed: ${errorDetail(err)}` };
|
|
2706
|
-
}
|
|
2707
|
-
if (state.isDraft !== false) {
|
|
2708
|
-
return { kind: 'park', reason: `PR #${pr.number} is draft or its draft state is unavailable` };
|
|
2709
|
-
}
|
|
2710
|
-
if (!state.mergeStateStatus || state.mergeStateStatus === 'UNKNOWN') {
|
|
2711
|
-
return { kind: 'park', reason: `PR #${pr.number} merge state is unavailable` };
|
|
2712
|
-
}
|
|
2713
|
-
let outcome;
|
|
2714
|
-
try {
|
|
2715
|
-
outcome = await deps.watch(pr.headSha);
|
|
2716
|
-
}
|
|
2717
|
-
catch (err) {
|
|
2718
|
-
return { kind: 'park', reason: `CI watch for PR #${pr.number} failed: ${errorDetail(err)}` };
|
|
2719
|
-
}
|
|
2720
|
-
if (outcome !== 'success')
|
|
2721
|
-
return { kind: 'park', reason: `CI for PR #${pr.number} ended ${outcome}` };
|
|
2722
|
-
return { kind: 'adopt', branch: pr.branch };
|
|
2554
|
+
...opts,
|
|
2555
|
+
});
|
|
2723
2556
|
}
|
|
2724
2557
|
export async function planRunLanes(input) {
|
|
2725
2558
|
if (input.localQueue) {
|
|
@@ -2848,7 +2681,7 @@ export async function cmdResumeApproved(opts = {}) {
|
|
|
2848
2681
|
}
|
|
2849
2682
|
const USAGE_UNAVAILABLE_LINE = '[factory] supervise: usage signal unavailable — proceeding without resume gate (set FACTORY_USAGE_ESTIMATOR=1 to gate on the heuristic)';
|
|
2850
2683
|
export async function superviseLoop(deps) {
|
|
2851
|
-
const { cap, resumeAt, pollMs, watch = true, watchSource = 'default', repoConfigLabel, resumeAtSource = 'default', pollSource = 'default', estimator = false, stopFile, eventsFile, now, runQueue, readUsageFn = () => readUsage({ cap, estimator }), pathExists = existsSync, clearStop = (path) => rmSync(path, { force: true }), sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms)), emitEvent = logEvent, writeLine = (line) => console.log(line), ingest, } = deps;
|
|
2684
|
+
const { cap, resumeAt, pollMs, watch = true, watchSource = 'default', repoConfigLabel, resumeAtSource = 'default', pollSource = 'default', estimator = false, stopFile, eventsFile, now, runQueue, readUsageFn = () => readUsage({ cap, estimator }), pathExists = existsSync, clearStop = (path) => rmSync(path, { force: true }), readStopMarker: readMarker = readStopMarker, sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms)), emitEvent = logEvent, writeLine = (line) => console.log(line), ingest, } = deps;
|
|
2852
2685
|
let cycle = 0;
|
|
2853
2686
|
if (resumeAtSource === 'flag') {
|
|
2854
2687
|
const detail = 'usage watchdog threshold set by flag: --usage-threshold';
|
|
@@ -2896,7 +2729,15 @@ export async function superviseLoop(deps) {
|
|
|
2896
2729
|
}
|
|
2897
2730
|
}
|
|
2898
2731
|
emitEvent(eventsFile, 'resumed', 'usage', `supervise cycle ${cycle}: trailing usage (${source}) at ${Math.round(pct * 100)}% of cap — starting run`);
|
|
2899
|
-
|
|
2732
|
+
const marker = readMarker(stopFile);
|
|
2733
|
+
if (marker?.source === 'usage-watchdog') {
|
|
2734
|
+
clearStop(stopFile);
|
|
2735
|
+
}
|
|
2736
|
+
else if (marker) {
|
|
2737
|
+
writeLine(`[factory] supervise: STOP set by ${marker.source}${marker.at ? ` at ${marker.at}` : ''}; waiting for factory resume`);
|
|
2738
|
+
while (pathExists(stopFile))
|
|
2739
|
+
await sleep(pollMs);
|
|
2740
|
+
}
|
|
2900
2741
|
if (ingest) {
|
|
2901
2742
|
const appended = await ingest();
|
|
2902
2743
|
if (appended > 0)
|
|
@@ -2975,7 +2816,7 @@ function logEventQuietly(eventsFile, type, msg) {
|
|
|
2975
2816
|
/** Creates the STOP file in one exclusive open (no check-then-write race). Returns false when it already existed. */
|
|
2976
2817
|
function createStopFile(stopFile) {
|
|
2977
2818
|
try {
|
|
2978
|
-
writeFileSync(stopFile, '', { flag: 'wx' });
|
|
2819
|
+
writeFileSync(stopFile, formatStopMarker('manual'), { flag: 'wx' });
|
|
2979
2820
|
return true;
|
|
2980
2821
|
}
|
|
2981
2822
|
catch (err) {
|