@on-par/factory-cli 0.9.11-next.6 → 0.9.11-next.60
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 +16 -2
- package/dist/cli/base-health-ci.d.ts.map +1 -1
- package/dist/cli/base-health-ci.js +30 -5
- package/dist/cli/base-health-ci.js.map +1 -1
- package/dist/cli/base-hold.d.ts +7 -13
- package/dist/cli/base-hold.d.ts.map +1 -1
- package/dist/cli/base-hold.js +44 -30
- 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 +13 -2
- 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.d.ts.map +1 -1
- package/dist/cli/doctor.js +3 -1
- package/dist/cli/doctor.js.map +1 -1
- package/dist/cli/help-groups.d.ts.map +1 -1
- package/dist/cli/help-groups.js +4 -1
- package/dist/cli/help-groups.js.map +1 -1
- package/dist/cli/index.d.ts +14 -32
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +60 -173
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/json-contract.d.ts +306 -0
- package/dist/cli/json-contract.d.ts.map +1 -0
- package/dist/cli/json-contract.js +208 -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/land.d.ts.map +1 -1
- package/dist/cli/land.js +19 -1
- package/dist/cli/land.js.map +1 -1
- package/dist/cli/lane-environment-pause-error.d.ts +3 -0
- package/dist/cli/lane-environment-pause-error.d.ts.map +1 -1
- package/dist/cli/lane-environment-pause-error.js +4 -0
- package/dist/cli/lane-environment-pause-error.js.map +1 -1
- 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 +11 -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 +50 -0
- package/dist/cli/queue-preflight.d.ts.map +1 -0
- package/dist/cli/queue-preflight.js +327 -0
- package/dist/cli/queue-preflight.js.map +1 -0
- package/dist/cli/risk-policy.d.ts +8 -0
- package/dist/cli/risk-policy.d.ts.map +1 -0
- package/dist/cli/risk-policy.js +34 -0
- package/dist/cli/risk-policy.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 +9 -1
- package/dist/cli/run-lane.js.map +1 -1
- package/dist/cli/run.d.ts.map +1 -1
- package/dist/cli/run.js +17 -11
- 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 +2 -14
- package/dist/cli/ship-steps.d.ts.map +1 -1
- package/dist/cli/ship-steps.js +19 -32
- package/dist/cli/ship-steps.js.map +1 -1
- package/dist/cli/ship.d.ts.map +1 -1
- package/dist/cli/ship.js +26 -24
- package/dist/cli/ship.js.map +1 -1
- package/dist/cli/slice-index.d.ts +16 -0
- package/dist/cli/slice-index.d.ts.map +1 -0
- package/dist/cli/slice-index.js +27 -0
- package/dist/cli/slice-index.js.map +1 -0
- 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/steerability-report.d.ts +9 -0
- package/dist/cli/steerability-report.d.ts.map +1 -0
- package/dist/cli/steerability-report.js +28 -0
- package/dist/cli/steerability-report.js.map +1 -0
- 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/dist/cli/worktree-reap.d.ts.map +1 -1
- package/dist/cli/worktree-reap.js +7 -1
- package/dist/cli/worktree-reap.js.map +1 -1
- package/package.json +8 -5
- package/schemas/cost.schema.json +114 -0
- package/schemas/daemon-status.schema.json +44 -0
- package/schemas/logs-line.schema.json +118 -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
|
@@ -10,15 +10,19 @@ import { LaneEnvironmentPauseError } from './lane-environment-pause-error.js';
|
|
|
10
10
|
import { INVALID_BRANCH_PREFIX_MESSAGE, resolveBranchPrefixOption } from './branch-prefix.js';
|
|
11
11
|
import { sweepLaneClaims, warnQueueDiagnostics } from './queue-startup.js';
|
|
12
12
|
import { proposeTriageQueue } from './triage-open-prs.js';
|
|
13
|
+
import { makeQueuePreflightOps, preflightQueuedIssue } from './queue-preflight.js';
|
|
13
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';
|
|
14
|
-
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';
|
|
16
|
+
import { formatSteerabilityReport } from '@on-par/factory-core/internal';
|
|
15
17
|
import { runTui } from '@on-par/factory-tui';
|
|
18
|
+
import { clearSelfUpdateStop } from './self-update-stop.js';
|
|
16
19
|
import { admissionStateReaderFor, runQueueReconcile } from './admission.js';
|
|
17
20
|
import { cmdClassifierBacktest, ClassifierBacktestInputError, } from './classifier-backtest.js';
|
|
21
|
+
import { buildSteerabilityReport } from './steerability-report.js';
|
|
18
22
|
import { runHarnessCheckCommand } from './harness-check.js';
|
|
19
|
-
import {
|
|
23
|
+
import { landIssue } from './land.js';
|
|
20
24
|
import { STOP_SET_MESSAGE } from './stop-sentinel.js';
|
|
21
|
-
import { AwaitingReviewError, CiFailedError, CiUnverifiedError,
|
|
25
|
+
import { AwaitingReviewError, CiFailedError, CiUnverifiedError, LandConflictError, LandFailureError, MergeGatedError, } from './land-pr.js';
|
|
22
26
|
import { setLandRuntime } from './land-runtime.js';
|
|
23
27
|
import { cmdRun } from './run.js';
|
|
24
28
|
import { shipIssue } from './ship.js';
|
|
@@ -875,7 +879,7 @@ async function cmdModels(opts = {}) {
|
|
|
875
879
|
const byModel = new Map(diagnoses.map((d) => [d.model, d]));
|
|
876
880
|
for (const m of registry.list()) {
|
|
877
881
|
const tiers = registry.getTiers(m).join('/');
|
|
878
|
-
const est = registry.estimateCost(m, 1_000_000, 1_000_000);
|
|
882
|
+
const est = registry.estimateCost(m, { inputTokens: 1_000_000, outputTokens: 1_000_000 }).cost;
|
|
879
883
|
const cost = est === null ? 'unpriced' : `$${est.toFixed(2)}/M`;
|
|
880
884
|
const d = byModel.get(m);
|
|
881
885
|
const avail = d ? modelStatusIcon(d) : styleText('red', '❌');
|
|
@@ -888,6 +892,7 @@ async function cmdModels(opts = {}) {
|
|
|
888
892
|
console.log(` ${tier}: ${models.join(' ')}`);
|
|
889
893
|
}
|
|
890
894
|
}
|
|
895
|
+
const CACHED_RATE_MARKER = ' (cached at full input rate)';
|
|
891
896
|
async function cmdCost(opts = {}) {
|
|
892
897
|
const repoRoot = await getRepoRoot();
|
|
893
898
|
const paths = getFactoryPaths(repoRoot);
|
|
@@ -911,7 +916,7 @@ async function cmdCost(opts = {}) {
|
|
|
911
916
|
}
|
|
912
917
|
console.log(styleText('bold', `== Costs for issue ${opts.issue} ==`));
|
|
913
918
|
for (const c of filtered) {
|
|
914
|
-
console.log(` ${c.task} ${c.model} ${c.cost === null || c.cost === undefined ? 'unknown' : `$${c.cost.toFixed(4)}`}${c.failoverReason ? ` [failover: ${c.failoverReason}]` : ''}`);
|
|
919
|
+
console.log(` ${c.task} ${c.model} ${c.cost === null || c.cost === undefined ? 'unknown' : `$${c.cost.toFixed(4)}`}${c.failoverReason ? ` [failover: ${c.failoverReason}]` : ''}${c.cachedRateMissing === true ? CACHED_RATE_MARKER : ''}`);
|
|
915
920
|
}
|
|
916
921
|
const t = aggregateCosts(filtered).total;
|
|
917
922
|
console.log(' ---');
|
|
@@ -924,7 +929,14 @@ async function cmdCost(opts = {}) {
|
|
|
924
929
|
}
|
|
925
930
|
const byModel = new Map();
|
|
926
931
|
for (const c of costs) {
|
|
927
|
-
const e = byModel.get(c.model) ?? {
|
|
932
|
+
const e = byModel.get(c.model) ?? {
|
|
933
|
+
tasks: 0,
|
|
934
|
+
priced: 0,
|
|
935
|
+
pricedCount: 0,
|
|
936
|
+
unpriced: 0,
|
|
937
|
+
failovers: 0,
|
|
938
|
+
cachedRateMissing: 0,
|
|
939
|
+
};
|
|
928
940
|
e.tasks++;
|
|
929
941
|
if (c.unpriced === true || c.cost === null || c.cost === undefined)
|
|
930
942
|
e.unpriced++;
|
|
@@ -934,13 +946,16 @@ async function cmdCost(opts = {}) {
|
|
|
934
946
|
}
|
|
935
947
|
if (c.failoverReason)
|
|
936
948
|
e.failovers++;
|
|
949
|
+
if (c.cachedRateMissing === true)
|
|
950
|
+
e.cachedRateMissing++;
|
|
937
951
|
byModel.set(c.model, e);
|
|
938
952
|
}
|
|
939
953
|
console.log(styleText('bold', '== Cost Summary =='));
|
|
940
|
-
for (const [model, { tasks, priced, pricedCount, unpriced, failovers }] of byModel) {
|
|
954
|
+
for (const [model, { tasks, priced, pricedCount, unpriced, failovers, cachedRateMissing }] of byModel) {
|
|
941
955
|
const failoverSuffix = failovers > 0 ? ` (${failovers} failover${failovers === 1 ? '' : 's'})` : '';
|
|
942
956
|
const modelCost = formatCostTotal(pricedCount === 0 && unpriced > 0 ? null : priced, unpriced);
|
|
943
|
-
|
|
957
|
+
const cachedSuffix = cachedRateMissing > 0 ? CACHED_RATE_MARKER : '';
|
|
958
|
+
console.log(` ${model}: ${tasks} tasks, ${modelCost}${failoverSuffix}${cachedSuffix}`);
|
|
944
959
|
}
|
|
945
960
|
const grand = aggregateCosts(costs).total;
|
|
946
961
|
console.log(' ---');
|
|
@@ -956,6 +971,7 @@ async function cmdCheck(issueRaw, opts) {
|
|
|
956
971
|
const repoConfig = configLoaders.loadRepoConfig(repoRoot);
|
|
957
972
|
const effective = resolveEffectiveConfig(repoConfig);
|
|
958
973
|
const router = new coreOps.ModelRouter(applyRepoConfig(configLoaders.loadModelsConfig(), repoConfig), configLoaders.loadRoutesConfig(), false, undefined, effective.allowExperimental, effective.localOnly);
|
|
974
|
+
router.setCodexFullAccessTasks?.(configLoaders.loadFactoryConfigForRepo(getFactoryPaths(repoRoot).config).sandbox.codexFullAccessTasks);
|
|
959
975
|
router.setLocalAuthSink?.(recordLocalAuthFailure);
|
|
960
976
|
runModel = createDeepModelRunner(router, repoRoot);
|
|
961
977
|
}
|
|
@@ -1049,12 +1065,27 @@ async function cmdClassifierReport(opts = {}) {
|
|
|
1049
1065
|
const repoRoot = await getRepoRoot();
|
|
1050
1066
|
const paths = getFactoryPaths(repoRoot);
|
|
1051
1067
|
const report = summarizeClassifierOutcomes(parseClassifierOutcomes(readTextFileOrEmpty(paths.classifierOutcomes)));
|
|
1068
|
+
const steerability = await buildSteerabilityReport(paths);
|
|
1052
1069
|
if (opts.json) {
|
|
1053
|
-
console.log(JSON.stringify(report));
|
|
1070
|
+
console.log(JSON.stringify({ ...report, steerability }));
|
|
1054
1071
|
return;
|
|
1055
1072
|
}
|
|
1056
1073
|
for (const line of formatClassifierReport(report))
|
|
1057
1074
|
console.log(line);
|
|
1075
|
+
console.log('');
|
|
1076
|
+
for (const line of formatSteerabilityReport(steerability))
|
|
1077
|
+
console.log(line);
|
|
1078
|
+
}
|
|
1079
|
+
async function cmdReport(opts = {}) {
|
|
1080
|
+
if (!opts.steerability)
|
|
1081
|
+
throw new CliExitError('factory: report requires --steerability', 2);
|
|
1082
|
+
const report = await buildSteerabilityReport(getFactoryPaths(await getRepoRoot()));
|
|
1083
|
+
if (opts.json) {
|
|
1084
|
+
console.log(JSON.stringify(report));
|
|
1085
|
+
return;
|
|
1086
|
+
}
|
|
1087
|
+
for (const line of formatSteerabilityReport(report))
|
|
1088
|
+
console.log(line);
|
|
1058
1089
|
}
|
|
1059
1090
|
async function cmdClassifierBacktestEntry(opts) {
|
|
1060
1091
|
const branchPrefix = branchPrefixOrExit(opts.branchPrefix);
|
|
@@ -1064,6 +1095,7 @@ async function cmdClassifierBacktestEntry(opts) {
|
|
|
1064
1095
|
const repoConfig = configLoaders.loadRepoConfig(repoRoot);
|
|
1065
1096
|
const effective = resolveEffectiveConfig(repoConfig);
|
|
1066
1097
|
const router = new coreOps.ModelRouter(applyRepoConfig(configLoaders.loadModelsConfig(), repoConfig), configLoaders.loadRoutesConfig(), false, undefined, effective.allowExperimental, effective.localOnly);
|
|
1098
|
+
router.setCodexFullAccessTasks?.(configLoaders.loadFactoryConfigForRepo(paths.config).sandbox.codexFullAccessTasks);
|
|
1067
1099
|
router.setLocalAuthSink?.(recordLocalAuthFailure);
|
|
1068
1100
|
try {
|
|
1069
1101
|
await cmdClassifierBacktest({ ...opts, branchPrefix }, {
|
|
@@ -1315,6 +1347,7 @@ async function cmdTui(opts = {}, runTuiImpl = runTui) {
|
|
|
1315
1347
|
const routesConfig = configLoaders.loadRoutesConfig();
|
|
1316
1348
|
const effective = resolveEffectiveConfig(repoConfig);
|
|
1317
1349
|
const router = new coreOps.ModelRouter(modelsConfig, routesConfig, false, undefined, effective.allowExperimental, effective.localOnly);
|
|
1350
|
+
router.setCodexFullAccessTasks?.(configLoaders.loadFactoryConfigForRepo(paths.config).sandbox.codexFullAccessTasks);
|
|
1318
1351
|
router.setLocalAuthSink?.(recordLocalAuthFailure);
|
|
1319
1352
|
const effectiveConfigLines = describeEffectiveConfig({
|
|
1320
1353
|
router,
|
|
@@ -1995,6 +2028,7 @@ async function cmdTriage(opts) {
|
|
|
1995
2028
|
const routesConfig = configLoaders.loadRoutesConfig();
|
|
1996
2029
|
const effective = resolveEffectiveConfig(repoConfig);
|
|
1997
2030
|
const router = new coreOps.ModelRouter(modelsConfig, routesConfig, false, undefined, effective.allowExperimental, effective.localOnly);
|
|
2031
|
+
router.setCodexFullAccessTasks?.(configLoaders.loadFactoryConfigForRepo(paths.config).sandbox.codexFullAccessTasks);
|
|
1998
2032
|
router.setLocalAuthSink?.(recordLocalAuthFailure);
|
|
1999
2033
|
router.skipMissingHarnessClis(coreOps.isCommandAvailable);
|
|
2000
2034
|
router.setCostSink((entry) => logCost(paths.costs, { ...entry, issue: '-' }));
|
|
@@ -2415,8 +2449,7 @@ async function cmdFactoryd(opts) {
|
|
|
2415
2449
|
const previousUpdate = readUpdateState(updateFile);
|
|
2416
2450
|
for (const stop of recoverStaleDrain(previousUpdate, (file) => {
|
|
2417
2451
|
try {
|
|
2418
|
-
|
|
2419
|
-
return true;
|
|
2452
|
+
return clearSelfUpdateStop(file);
|
|
2420
2453
|
}
|
|
2421
2454
|
catch {
|
|
2422
2455
|
return false;
|
|
@@ -2542,167 +2575,12 @@ export function laneQueueDeps(queue, lane) {
|
|
|
2542
2575
|
countRemaining: async () => (await queue.list(lane)).length,
|
|
2543
2576
|
};
|
|
2544
2577
|
}
|
|
2578
|
+
export { preflightQueuedIssue };
|
|
2545
2579
|
export function createQueuePreflightOps(octokit, owner, repo, opts = {}) {
|
|
2546
|
-
return {
|
|
2547
|
-
autoMergeStack: opts.autoMergeStack ?? false,
|
|
2548
|
-
getPullRefs: async (prNumber) => {
|
|
2549
|
-
const { data } = await octokit.rest.pulls.get({ owner, repo, pull_number: prNumber });
|
|
2550
|
-
return { headRef: data.head.ref, baseRef: data.base.ref, headSha: data.head.sha };
|
|
2551
|
-
},
|
|
2552
|
-
expectedHeadRepoFullName: `${owner}/${repo}`,
|
|
2553
|
-
findOpenPR: (issue) => findOpenPRForIssue(octokit, owner, repo, issue),
|
|
2554
|
-
getLandState: (prNumber) => getPullRequestLandState(octokit, owner, repo, prNumber),
|
|
2580
|
+
return makeQueuePreflightOps(octokit, owner, repo, {
|
|
2555
2581
|
watch: (ref) => internalOps.watchChecks({ octokit, owner, repo, ref }),
|
|
2556
|
-
|
|
2557
|
-
|
|
2558
|
-
const { data } = await octokit.rest.pulls.get({ owner, repo, pull_number: prNumber });
|
|
2559
|
-
if (data.merged || data.merged_at)
|
|
2560
|
-
return 'merged';
|
|
2561
|
-
return data.state === 'open' ? 'open' : 'closed';
|
|
2562
|
-
},
|
|
2563
|
-
saveSlicePlan: async (issue, plan) => {
|
|
2564
|
-
await upsertSlicePlanComment({ octokit, repo: `${owner}/${repo}`, issue, plan });
|
|
2565
|
-
},
|
|
2566
|
-
};
|
|
2567
|
-
}
|
|
2568
|
-
/** ADR-0147/ADR-0161: walk every pr-open slice PR bottom-up. Merged layers are recorded, a closed layer parks
|
|
2569
|
-
* (naming the layers above it), and the lowest open layer defers, or is adopted when stack auto-merge is on and
|
|
2570
|
-
* its base is main with green CI on the rebased head. Returns undefined to fall through to the Closes-PR
|
|
2571
|
-
* preflight only when no slice PR is still open. */
|
|
2572
|
-
async function preflightSlice(issue, deps) {
|
|
2573
|
-
let lookup;
|
|
2574
|
-
try {
|
|
2575
|
-
lookup = await deps.findSlicePlan(issue);
|
|
2576
|
-
}
|
|
2577
|
-
catch (err) {
|
|
2578
|
-
return { kind: 'park', reason: `slice plan lookup failed: ${errorDetail(err)}` };
|
|
2579
|
-
}
|
|
2580
|
-
if (lookup.status === 'invalid') {
|
|
2581
|
-
return {
|
|
2582
|
-
kind: 'park',
|
|
2583
|
-
reason: `slice plan comment ${lookup.commentId} on #${issue} is unreadable (${lookup.error})`,
|
|
2584
|
-
};
|
|
2585
|
-
}
|
|
2586
|
-
if (lookup.status === 'not-found')
|
|
2587
|
-
return undefined;
|
|
2588
|
-
const { plan } = lookup;
|
|
2589
|
-
const n = plan.slices.length;
|
|
2590
|
-
const open = plan.slices.filter((s) => s.state === 'pr-open');
|
|
2591
|
-
if (open.length === 0)
|
|
2592
|
-
return undefined;
|
|
2593
|
-
let next = plan;
|
|
2594
|
-
let lastMerged;
|
|
2595
|
-
const save = async () => {
|
|
2596
|
-
if (lastMerged === undefined)
|
|
2597
|
-
return undefined;
|
|
2598
|
-
try {
|
|
2599
|
-
await deps.saveSlicePlan(issue, next);
|
|
2600
|
-
}
|
|
2601
|
-
catch (err) {
|
|
2602
|
-
return {
|
|
2603
|
-
kind: 'park',
|
|
2604
|
-
reason: `${lastMerged.label} PR #${lastMerged.pr} merged but updating the slice plan failed: ${errorDetail(err)}`,
|
|
2605
|
-
};
|
|
2606
|
-
}
|
|
2607
|
-
return undefined;
|
|
2608
|
-
};
|
|
2609
|
-
for (const [at, slice] of open.entries()) {
|
|
2610
|
-
const label = `slice ${slice.index}/${n}`;
|
|
2611
|
-
if (slice.prNumber === undefined) {
|
|
2612
|
-
return { kind: 'park', reason: `${label} of #${issue} is pr-open but the slice plan records no PR number` };
|
|
2613
|
-
}
|
|
2614
|
-
const pr = slice.prNumber;
|
|
2615
|
-
let state;
|
|
2616
|
-
try {
|
|
2617
|
-
state = await deps.getPullState(pr);
|
|
2618
|
-
}
|
|
2619
|
-
catch (err) {
|
|
2620
|
-
return { kind: 'park', reason: `${label} PR #${pr} state lookup failed: ${errorDetail(err)}` };
|
|
2621
|
-
}
|
|
2622
|
-
if (state === 'merged') {
|
|
2623
|
-
next = withSliceState(next, slice.index, 'merged', pr);
|
|
2624
|
-
lastMerged = { label, pr };
|
|
2625
|
-
continue;
|
|
2626
|
-
}
|
|
2627
|
-
if (state === 'closed') {
|
|
2628
|
-
const above = open.slice(at + 1);
|
|
2629
|
-
const suffix = above.length > 0
|
|
2630
|
-
? `; layers above it: ${above.map((a) => `slice ${a.index}/${n} (#${a.prNumber})`).join(', ')}`
|
|
2631
|
-
: '';
|
|
2632
|
-
return { kind: 'park', reason: `${label} PR #${pr} was closed without merging${suffix}` };
|
|
2633
|
-
}
|
|
2634
|
-
const saved = await save();
|
|
2635
|
-
if (saved)
|
|
2636
|
-
return saved;
|
|
2637
|
-
if (!deps.autoMergeStack)
|
|
2638
|
-
return { kind: 'defer' };
|
|
2639
|
-
let refs;
|
|
2640
|
-
try {
|
|
2641
|
-
refs = await deps.getPullRefs(pr);
|
|
2642
|
-
}
|
|
2643
|
-
catch (err) {
|
|
2644
|
-
return { kind: 'park', reason: `${label} PR #${pr} ref lookup failed: ${errorDetail(err)}` };
|
|
2645
|
-
}
|
|
2646
|
-
// GitHub has not retargeted the layer to main yet: the lower layer is still unmerged.
|
|
2647
|
-
if (refs.baseRef !== 'main')
|
|
2648
|
-
return { kind: 'defer' };
|
|
2649
|
-
let outcome;
|
|
2650
|
-
try {
|
|
2651
|
-
outcome = await deps.watch(refs.headSha);
|
|
2652
|
-
}
|
|
2653
|
-
catch (err) {
|
|
2654
|
-
return { kind: 'park', reason: `CI watch for ${label} PR #${pr} failed: ${errorDetail(err)}` };
|
|
2655
|
-
}
|
|
2656
|
-
if (outcome === 'failure')
|
|
2657
|
-
return { kind: 'park', reason: `CI for ${label} PR #${pr} ended failure` };
|
|
2658
|
-
if (outcome !== 'success')
|
|
2659
|
-
return { kind: 'defer' };
|
|
2660
|
-
return { kind: 'adopt', branch: refs.headRef, stackLayer: { index: slice.index, count: n, prNumber: pr } };
|
|
2661
|
-
}
|
|
2662
|
-
return (await save()) ?? undefined;
|
|
2663
|
-
}
|
|
2664
|
-
/** Resolve queued work from GitHub evidence without consuming model or worktree resources. */
|
|
2665
|
-
export async function preflightQueuedIssue(issue, deps) {
|
|
2666
|
-
if (issue.labels.includes('factory:in-progress'))
|
|
2667
|
-
return { kind: 'defer' };
|
|
2668
|
-
const slice = await preflightSlice(issue.number, deps);
|
|
2669
|
-
if (slice)
|
|
2670
|
-
return slice;
|
|
2671
|
-
let pr;
|
|
2672
|
-
try {
|
|
2673
|
-
pr = await deps.findOpenPR(issue.number);
|
|
2674
|
-
}
|
|
2675
|
-
catch (err) {
|
|
2676
|
-
return { kind: 'park', reason: `PR lookup failed: ${errorDetail(err)}` };
|
|
2677
|
-
}
|
|
2678
|
-
if (!pr)
|
|
2679
|
-
return { kind: 'build' };
|
|
2680
|
-
if (pr.headRepoFullName !== deps.expectedHeadRepoFullName) {
|
|
2681
|
-
return { kind: 'park', reason: `PR #${pr.number} head is not in ${deps.expectedHeadRepoFullName}` };
|
|
2682
|
-
}
|
|
2683
|
-
let state;
|
|
2684
|
-
try {
|
|
2685
|
-
state = await deps.getLandState(pr.number);
|
|
2686
|
-
}
|
|
2687
|
-
catch (err) {
|
|
2688
|
-
return { kind: 'park', reason: `PR #${pr.number} merge-state lookup failed: ${errorDetail(err)}` };
|
|
2689
|
-
}
|
|
2690
|
-
if (state.isDraft !== false) {
|
|
2691
|
-
return { kind: 'park', reason: `PR #${pr.number} is draft or its draft state is unavailable` };
|
|
2692
|
-
}
|
|
2693
|
-
if (!state.mergeStateStatus || state.mergeStateStatus === 'UNKNOWN') {
|
|
2694
|
-
return { kind: 'park', reason: `PR #${pr.number} merge state is unavailable` };
|
|
2695
|
-
}
|
|
2696
|
-
let outcome;
|
|
2697
|
-
try {
|
|
2698
|
-
outcome = await deps.watch(pr.headSha);
|
|
2699
|
-
}
|
|
2700
|
-
catch (err) {
|
|
2701
|
-
return { kind: 'park', reason: `CI watch for PR #${pr.number} failed: ${errorDetail(err)}` };
|
|
2702
|
-
}
|
|
2703
|
-
if (outcome !== 'success')
|
|
2704
|
-
return { kind: 'park', reason: `CI for PR #${pr.number} ended ${outcome}` };
|
|
2705
|
-
return { kind: 'adopt', branch: pr.branch };
|
|
2582
|
+
...opts,
|
|
2583
|
+
});
|
|
2706
2584
|
}
|
|
2707
2585
|
export async function planRunLanes(input) {
|
|
2708
2586
|
if (input.localQueue) {
|
|
@@ -2831,7 +2709,7 @@ export async function cmdResumeApproved(opts = {}) {
|
|
|
2831
2709
|
}
|
|
2832
2710
|
const USAGE_UNAVAILABLE_LINE = '[factory] supervise: usage signal unavailable — proceeding without resume gate (set FACTORY_USAGE_ESTIMATOR=1 to gate on the heuristic)';
|
|
2833
2711
|
export async function superviseLoop(deps) {
|
|
2834
|
-
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;
|
|
2712
|
+
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;
|
|
2835
2713
|
let cycle = 0;
|
|
2836
2714
|
if (resumeAtSource === 'flag') {
|
|
2837
2715
|
const detail = 'usage watchdog threshold set by flag: --usage-threshold';
|
|
@@ -2879,7 +2757,15 @@ export async function superviseLoop(deps) {
|
|
|
2879
2757
|
}
|
|
2880
2758
|
}
|
|
2881
2759
|
emitEvent(eventsFile, 'resumed', 'usage', `supervise cycle ${cycle}: trailing usage (${source}) at ${Math.round(pct * 100)}% of cap — starting run`);
|
|
2882
|
-
|
|
2760
|
+
const marker = readMarker(stopFile);
|
|
2761
|
+
if (marker?.source === 'usage-watchdog') {
|
|
2762
|
+
clearStop(stopFile);
|
|
2763
|
+
}
|
|
2764
|
+
else if (marker) {
|
|
2765
|
+
writeLine(`[factory] supervise: STOP set by ${marker.source}${marker.at ? ` at ${marker.at}` : ''}; waiting for factory resume`);
|
|
2766
|
+
while (pathExists(stopFile))
|
|
2767
|
+
await sleep(pollMs);
|
|
2768
|
+
}
|
|
2883
2769
|
if (ingest) {
|
|
2884
2770
|
const appended = await ingest();
|
|
2885
2771
|
if (appended > 0)
|
|
@@ -2958,7 +2844,7 @@ function logEventQuietly(eventsFile, type, msg) {
|
|
|
2958
2844
|
/** Creates the STOP file in one exclusive open (no check-then-write race). Returns false when it already existed. */
|
|
2959
2845
|
function createStopFile(stopFile) {
|
|
2960
2846
|
try {
|
|
2961
|
-
writeFileSync(stopFile, '', { flag: 'wx' });
|
|
2847
|
+
writeFileSync(stopFile, formatStopMarker('manual'), { flag: 'wx' });
|
|
2962
2848
|
return true;
|
|
2963
2849
|
}
|
|
2964
2850
|
catch (err) {
|
|
@@ -3158,6 +3044,7 @@ export const PROGRAM_HANDLERS = {
|
|
|
3158
3044
|
cmdRuns,
|
|
3159
3045
|
cmdKpis,
|
|
3160
3046
|
cmdClassifierReport,
|
|
3047
|
+
cmdReport,
|
|
3161
3048
|
cmdClassifierBacktestEntry,
|
|
3162
3049
|
cmdGarden,
|
|
3163
3050
|
cmdTui,
|