@on-par/factory-cli 0.9.11-next.9 → 0.9.12-next.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/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 +2 -0
- package/dist/cli/base-hold.d.ts.map +1 -1
- package/dist/cli/base-hold.js +24 -3
- 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/harness-check.d.ts.map +1 -1
- package/dist/cli/harness-check.js +8 -3
- package/dist/cli/harness-check.js.map +1 -1
- package/dist/cli/help-groups.d.ts.map +1 -1
- package/dist/cli/help-groups.js +16 -2
- package/dist/cli/help-groups.js.map +1 -1
- package/dist/cli/index.d.ts +29 -32
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +88 -174
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/json-contract.d.ts +309 -0
- package/dist/cli/json-contract.d.ts.map +1 -0
- package/dist/cli/json-contract.js +211 -0
- package/dist/cli/json-contract.js.map +1 -0
- package/dist/cli/land-pr.d.ts +4 -4
- package/dist/cli/land-pr.d.ts.map +1 -1
- package/dist/cli/land-pr.js +18 -17
- package/dist/cli/land-pr.js.map +1 -1
- package/dist/cli/land-risk.d.ts +29 -0
- package/dist/cli/land-risk.d.ts.map +1 -0
- package/dist/cli/land-risk.js +92 -0
- package/dist/cli/land-risk.js.map +1 -0
- package/dist/cli/land-runtime.d.ts +2 -1
- package/dist/cli/land-runtime.d.ts.map +1 -1
- package/dist/cli/land-runtime.js +3 -0
- package/dist/cli/land-runtime.js.map +1 -1
- package/dist/cli/land.d.ts +2 -0
- package/dist/cli/land.d.ts.map +1 -1
- package/dist/cli/land.js +46 -4
- 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 +4 -0
- package/dist/cli/logs.d.ts.map +1 -1
- package/dist/cli/logs.js +6 -2
- package/dist/cli/logs.js.map +1 -1
- package/dist/cli/model-override-messages.d.ts +4 -0
- package/dist/cli/model-override-messages.d.ts.map +1 -0
- package/dist/cli/model-override-messages.js +15 -0
- package/dist/cli/model-override-messages.js.map +1 -0
- package/dist/cli/octokit.d.ts +3 -0
- package/dist/cli/octokit.d.ts.map +1 -1
- package/dist/cli/octokit.js +1 -1
- package/dist/cli/octokit.js.map +1 -1
- package/dist/cli/program-observe.d.ts.map +1 -1
- package/dist/cli/program-observe.js +11 -0
- package/dist/cli/program-observe.js.map +1 -1
- package/dist/cli/program-run-work.d.ts.map +1 -1
- package/dist/cli/program-run-work.js +12 -0
- package/dist/cli/program-run-work.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 +52 -0
- package/dist/cli/queue-preflight.d.ts.map +1 -0
- package/dist/cli/queue-preflight.js +358 -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 +39 -0
- package/dist/cli/risk-policy.js.map +1 -0
- package/dist/cli/risk.d.ts +42 -0
- package/dist/cli/risk.d.ts.map +1 -0
- package/dist/cli/risk.js +103 -0
- package/dist/cli/risk.js.map +1 -0
- package/dist/cli/run-branch.d.ts +26 -0
- package/dist/cli/run-branch.d.ts.map +1 -0
- package/dist/cli/run-branch.js +42 -0
- package/dist/cli/run-branch.js.map +1 -0
- package/dist/cli/run-lane.d.ts +6 -1
- package/dist/cli/run-lane.d.ts.map +1 -1
- package/dist/cli/run-lane.js +52 -5
- package/dist/cli/run-lane.js.map +1 -1
- package/dist/cli/run-restack.d.ts +14 -0
- package/dist/cli/run-restack.d.ts.map +1 -0
- package/dist/cli/run-restack.js +21 -0
- package/dist/cli/run-restack.js.map +1 -0
- package/dist/cli/run.d.ts.map +1 -1
- package/dist/cli/run.js +19 -2
- 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-reports.d.ts +34 -0
- package/dist/cli/ship-reports.d.ts.map +1 -0
- package/dist/cli/ship-reports.js +54 -0
- package/dist/cli/ship-reports.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 +17 -0
- package/dist/cli/ship-run-state.js.map +1 -0
- package/dist/cli/ship-steps.d.ts +4 -47
- package/dist/cli/ship-steps.d.ts.map +1 -1
- package/dist/cli/ship-steps.js +22 -82
- package/dist/cli/ship-steps.js.map +1 -1
- package/dist/cli/ship.d.ts +2 -0
- package/dist/cli/ship.d.ts.map +1 -1
- package/dist/cli/ship.js +64 -67
- 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/steer.d.ts +50 -0
- package/dist/cli/steer.d.ts.map +1 -0
- package/dist/cli/steer.js +145 -0
- package/dist/cli/steer.js.map +1 -0
- 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 +9 -5
- package/schemas/cost.schema.json +114 -0
- package/schemas/daemon-status.schema.json +44 -0
- package/schemas/logs-line.schema.json +133 -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';
|
|
@@ -31,6 +35,7 @@ import { buildCostJson } from './cost-json.js';
|
|
|
31
35
|
import { buildQueueAddJson } from './queue-add-json.js';
|
|
32
36
|
import { renderQueueAddLines, renderUnparkLines } from './queue-output.js';
|
|
33
37
|
import { buildQueueListJson } from './queue-list.js';
|
|
38
|
+
import { cmdSteer } from './steer.js';
|
|
34
39
|
import { appendUnparkEvent, runUnpark, UnparkError } from './unpark.js';
|
|
35
40
|
import { buildRunsJson, parseRunsLimit } from './runs.js';
|
|
36
41
|
import { buildRunDiffstatJson, NoRunError, parseDiffstatIssue } from './runs-diffstat.js';
|
|
@@ -46,6 +51,7 @@ import { cmdHostedSmoke } from './hosted.js';
|
|
|
46
51
|
import { cmdHostedQueue } from './hosted-queue.js';
|
|
47
52
|
import { cmdHostedRunner } from './hosted-runner.js';
|
|
48
53
|
import { resolveFactoryCheckoutCommit, runFilingPreview } from './filing.js';
|
|
54
|
+
import { cmdRisk } from './risk.js';
|
|
49
55
|
import { buildFeedbackDeps, resolveFeedbackRepo, runFeedback } from './feedback.js';
|
|
50
56
|
import { ensureFactoryExcluded, writeSampleQueue } from './init-files.js';
|
|
51
57
|
import { cmdLogs } from './logs.js';
|
|
@@ -875,7 +881,7 @@ async function cmdModels(opts = {}) {
|
|
|
875
881
|
const byModel = new Map(diagnoses.map((d) => [d.model, d]));
|
|
876
882
|
for (const m of registry.list()) {
|
|
877
883
|
const tiers = registry.getTiers(m).join('/');
|
|
878
|
-
const est = registry.estimateCost(m, 1_000_000, 1_000_000);
|
|
884
|
+
const est = registry.estimateCost(m, { inputTokens: 1_000_000, outputTokens: 1_000_000 }).cost;
|
|
879
885
|
const cost = est === null ? 'unpriced' : `$${est.toFixed(2)}/M`;
|
|
880
886
|
const d = byModel.get(m);
|
|
881
887
|
const avail = d ? modelStatusIcon(d) : styleText('red', '❌');
|
|
@@ -888,6 +894,7 @@ async function cmdModels(opts = {}) {
|
|
|
888
894
|
console.log(` ${tier}: ${models.join(' ')}`);
|
|
889
895
|
}
|
|
890
896
|
}
|
|
897
|
+
const CACHED_RATE_MARKER = ' (cached at full input rate)';
|
|
891
898
|
async function cmdCost(opts = {}) {
|
|
892
899
|
const repoRoot = await getRepoRoot();
|
|
893
900
|
const paths = getFactoryPaths(repoRoot);
|
|
@@ -911,7 +918,7 @@ async function cmdCost(opts = {}) {
|
|
|
911
918
|
}
|
|
912
919
|
console.log(styleText('bold', `== Costs for issue ${opts.issue} ==`));
|
|
913
920
|
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}]` : ''}`);
|
|
921
|
+
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
922
|
}
|
|
916
923
|
const t = aggregateCosts(filtered).total;
|
|
917
924
|
console.log(' ---');
|
|
@@ -924,7 +931,14 @@ async function cmdCost(opts = {}) {
|
|
|
924
931
|
}
|
|
925
932
|
const byModel = new Map();
|
|
926
933
|
for (const c of costs) {
|
|
927
|
-
const e = byModel.get(c.model) ?? {
|
|
934
|
+
const e = byModel.get(c.model) ?? {
|
|
935
|
+
tasks: 0,
|
|
936
|
+
priced: 0,
|
|
937
|
+
pricedCount: 0,
|
|
938
|
+
unpriced: 0,
|
|
939
|
+
failovers: 0,
|
|
940
|
+
cachedRateMissing: 0,
|
|
941
|
+
};
|
|
928
942
|
e.tasks++;
|
|
929
943
|
if (c.unpriced === true || c.cost === null || c.cost === undefined)
|
|
930
944
|
e.unpriced++;
|
|
@@ -934,13 +948,16 @@ async function cmdCost(opts = {}) {
|
|
|
934
948
|
}
|
|
935
949
|
if (c.failoverReason)
|
|
936
950
|
e.failovers++;
|
|
951
|
+
if (c.cachedRateMissing === true)
|
|
952
|
+
e.cachedRateMissing++;
|
|
937
953
|
byModel.set(c.model, e);
|
|
938
954
|
}
|
|
939
955
|
console.log(styleText('bold', '== Cost Summary =='));
|
|
940
|
-
for (const [model, { tasks, priced, pricedCount, unpriced, failovers }] of byModel) {
|
|
956
|
+
for (const [model, { tasks, priced, pricedCount, unpriced, failovers, cachedRateMissing }] of byModel) {
|
|
941
957
|
const failoverSuffix = failovers > 0 ? ` (${failovers} failover${failovers === 1 ? '' : 's'})` : '';
|
|
942
958
|
const modelCost = formatCostTotal(pricedCount === 0 && unpriced > 0 ? null : priced, unpriced);
|
|
943
|
-
|
|
959
|
+
const cachedSuffix = cachedRateMissing > 0 ? CACHED_RATE_MARKER : '';
|
|
960
|
+
console.log(` ${model}: ${tasks} tasks, ${modelCost}${failoverSuffix}${cachedSuffix}`);
|
|
944
961
|
}
|
|
945
962
|
const grand = aggregateCosts(costs).total;
|
|
946
963
|
console.log(' ---');
|
|
@@ -956,6 +973,7 @@ async function cmdCheck(issueRaw, opts) {
|
|
|
956
973
|
const repoConfig = configLoaders.loadRepoConfig(repoRoot);
|
|
957
974
|
const effective = resolveEffectiveConfig(repoConfig);
|
|
958
975
|
const router = new coreOps.ModelRouter(applyRepoConfig(configLoaders.loadModelsConfig(), repoConfig), configLoaders.loadRoutesConfig(), false, undefined, effective.allowExperimental, effective.localOnly);
|
|
976
|
+
router.setCodexFullAccessTasks?.(configLoaders.loadFactoryConfigForRepo(getFactoryPaths(repoRoot).config).sandbox.codexFullAccessTasks);
|
|
959
977
|
router.setLocalAuthSink?.(recordLocalAuthFailure);
|
|
960
978
|
runModel = createDeepModelRunner(router, repoRoot);
|
|
961
979
|
}
|
|
@@ -1045,16 +1063,35 @@ function persistClassifierOutcomes(paths, events, sources, opts) {
|
|
|
1045
1063
|
ensureDir(paths.state);
|
|
1046
1064
|
writeFileSync(paths.classifierOutcomes, updated);
|
|
1047
1065
|
}
|
|
1066
|
+
/** `factory risk <pr>` (#3385): the handler lives in ./risk.ts; this binds its runtime. */
|
|
1067
|
+
function runRisk(pr, opts = {}) {
|
|
1068
|
+
return cmdRisk(pr, opts, { getRepoRoot, getGitHubRepo, getOctokit, CliExitError, log: console.log });
|
|
1069
|
+
}
|
|
1048
1070
|
async function cmdClassifierReport(opts = {}) {
|
|
1049
1071
|
const repoRoot = await getRepoRoot();
|
|
1050
1072
|
const paths = getFactoryPaths(repoRoot);
|
|
1051
1073
|
const report = summarizeClassifierOutcomes(parseClassifierOutcomes(readTextFileOrEmpty(paths.classifierOutcomes)));
|
|
1074
|
+
const steerability = await buildSteerabilityReport(paths);
|
|
1052
1075
|
if (opts.json) {
|
|
1053
|
-
console.log(JSON.stringify(report));
|
|
1076
|
+
console.log(JSON.stringify({ ...report, steerability }));
|
|
1054
1077
|
return;
|
|
1055
1078
|
}
|
|
1056
1079
|
for (const line of formatClassifierReport(report))
|
|
1057
1080
|
console.log(line);
|
|
1081
|
+
console.log('');
|
|
1082
|
+
for (const line of formatSteerabilityReport(steerability))
|
|
1083
|
+
console.log(line);
|
|
1084
|
+
}
|
|
1085
|
+
async function cmdReport(opts = {}) {
|
|
1086
|
+
if (!opts.steerability)
|
|
1087
|
+
throw new CliExitError('factory: report requires --steerability', 2);
|
|
1088
|
+
const report = await buildSteerabilityReport(getFactoryPaths(await getRepoRoot()));
|
|
1089
|
+
if (opts.json) {
|
|
1090
|
+
console.log(JSON.stringify(report));
|
|
1091
|
+
return;
|
|
1092
|
+
}
|
|
1093
|
+
for (const line of formatSteerabilityReport(report))
|
|
1094
|
+
console.log(line);
|
|
1058
1095
|
}
|
|
1059
1096
|
async function cmdClassifierBacktestEntry(opts) {
|
|
1060
1097
|
const branchPrefix = branchPrefixOrExit(opts.branchPrefix);
|
|
@@ -1064,6 +1101,7 @@ async function cmdClassifierBacktestEntry(opts) {
|
|
|
1064
1101
|
const repoConfig = configLoaders.loadRepoConfig(repoRoot);
|
|
1065
1102
|
const effective = resolveEffectiveConfig(repoConfig);
|
|
1066
1103
|
const router = new coreOps.ModelRouter(applyRepoConfig(configLoaders.loadModelsConfig(), repoConfig), configLoaders.loadRoutesConfig(), false, undefined, effective.allowExperimental, effective.localOnly);
|
|
1104
|
+
router.setCodexFullAccessTasks?.(configLoaders.loadFactoryConfigForRepo(paths.config).sandbox.codexFullAccessTasks);
|
|
1067
1105
|
router.setLocalAuthSink?.(recordLocalAuthFailure);
|
|
1068
1106
|
try {
|
|
1069
1107
|
await cmdClassifierBacktest({ ...opts, branchPrefix }, {
|
|
@@ -1315,6 +1353,7 @@ async function cmdTui(opts = {}, runTuiImpl = runTui) {
|
|
|
1315
1353
|
const routesConfig = configLoaders.loadRoutesConfig();
|
|
1316
1354
|
const effective = resolveEffectiveConfig(repoConfig);
|
|
1317
1355
|
const router = new coreOps.ModelRouter(modelsConfig, routesConfig, false, undefined, effective.allowExperimental, effective.localOnly);
|
|
1356
|
+
router.setCodexFullAccessTasks?.(configLoaders.loadFactoryConfigForRepo(paths.config).sandbox.codexFullAccessTasks);
|
|
1318
1357
|
router.setLocalAuthSink?.(recordLocalAuthFailure);
|
|
1319
1358
|
const effectiveConfigLines = describeEffectiveConfig({
|
|
1320
1359
|
router,
|
|
@@ -1537,7 +1576,11 @@ async function cmdShip(issueNum, opts) {
|
|
|
1537
1576
|
catch (err) {
|
|
1538
1577
|
if (err instanceof IssueSkippedError)
|
|
1539
1578
|
return;
|
|
1540
|
-
|
|
1579
|
+
// A --no-pr run keeps its worktree either way: inspecting it is the point.
|
|
1580
|
+
if (ghRepo &&
|
|
1581
|
+
opts.pr !== false &&
|
|
1582
|
+
!(err instanceof InvalidWorkRequestInputError) &&
|
|
1583
|
+
!(err instanceof LaneEnvironmentPauseError)) {
|
|
1541
1584
|
await reapPrlessShipWorktree(issueNum, repoRoot, ghRepo, paths, branchPrefix);
|
|
1542
1585
|
}
|
|
1543
1586
|
// #2492: a pull request number is a usage error, not a failed ship.
|
|
@@ -1560,6 +1603,9 @@ async function cmdRunIssue(issueNum, opts) {
|
|
|
1560
1603
|
const branchPrefix = branchPrefixOrExit(opts.branchPrefix);
|
|
1561
1604
|
const sizeGate = sizeGateOrExit(rawSizeGate);
|
|
1562
1605
|
const maxSlices = maxSlicesOrExit(rawMaxSlices);
|
|
1606
|
+
if (opts.pr === false && ((sizeGate !== undefined && sizeGate !== 'off') || runChildren)) {
|
|
1607
|
+
throw new CliExitError('factory: --no-pr runs with the size gate off — drop --size-gate/--run-children or drop --no-pr', 2);
|
|
1608
|
+
}
|
|
1563
1609
|
await withRepoRunLock(paths, 'factory run-issue', async () => {
|
|
1564
1610
|
const ghRepo = await getGitHubRepo();
|
|
1565
1611
|
// Pre-flight: resolve the issue through the canonical work-request seam
|
|
@@ -1995,6 +2041,7 @@ async function cmdTriage(opts) {
|
|
|
1995
2041
|
const routesConfig = configLoaders.loadRoutesConfig();
|
|
1996
2042
|
const effective = resolveEffectiveConfig(repoConfig);
|
|
1997
2043
|
const router = new coreOps.ModelRouter(modelsConfig, routesConfig, false, undefined, effective.allowExperimental, effective.localOnly);
|
|
2044
|
+
router.setCodexFullAccessTasks?.(configLoaders.loadFactoryConfigForRepo(paths.config).sandbox.codexFullAccessTasks);
|
|
1998
2045
|
router.setLocalAuthSink?.(recordLocalAuthFailure);
|
|
1999
2046
|
router.skipMissingHarnessClis(coreOps.isCommandAvailable);
|
|
2000
2047
|
router.setCostSink((entry) => logCost(paths.costs, { ...entry, issue: '-' }));
|
|
@@ -2415,8 +2462,7 @@ async function cmdFactoryd(opts) {
|
|
|
2415
2462
|
const previousUpdate = readUpdateState(updateFile);
|
|
2416
2463
|
for (const stop of recoverStaleDrain(previousUpdate, (file) => {
|
|
2417
2464
|
try {
|
|
2418
|
-
|
|
2419
|
-
return true;
|
|
2465
|
+
return clearSelfUpdateStop(file);
|
|
2420
2466
|
}
|
|
2421
2467
|
catch {
|
|
2422
2468
|
return false;
|
|
@@ -2542,167 +2588,12 @@ export function laneQueueDeps(queue, lane) {
|
|
|
2542
2588
|
countRemaining: async () => (await queue.list(lane)).length,
|
|
2543
2589
|
};
|
|
2544
2590
|
}
|
|
2591
|
+
export { preflightQueuedIssue };
|
|
2545
2592
|
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),
|
|
2593
|
+
return makeQueuePreflightOps(octokit, owner, repo, {
|
|
2555
2594
|
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 };
|
|
2595
|
+
...opts,
|
|
2596
|
+
});
|
|
2706
2597
|
}
|
|
2707
2598
|
export async function planRunLanes(input) {
|
|
2708
2599
|
if (input.localQueue) {
|
|
@@ -2831,7 +2722,7 @@ export async function cmdResumeApproved(opts = {}) {
|
|
|
2831
2722
|
}
|
|
2832
2723
|
const USAGE_UNAVAILABLE_LINE = '[factory] supervise: usage signal unavailable — proceeding without resume gate (set FACTORY_USAGE_ESTIMATOR=1 to gate on the heuristic)';
|
|
2833
2724
|
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;
|
|
2725
|
+
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
2726
|
let cycle = 0;
|
|
2836
2727
|
if (resumeAtSource === 'flag') {
|
|
2837
2728
|
const detail = 'usage watchdog threshold set by flag: --usage-threshold';
|
|
@@ -2879,7 +2770,15 @@ export async function superviseLoop(deps) {
|
|
|
2879
2770
|
}
|
|
2880
2771
|
}
|
|
2881
2772
|
emitEvent(eventsFile, 'resumed', 'usage', `supervise cycle ${cycle}: trailing usage (${source}) at ${Math.round(pct * 100)}% of cap — starting run`);
|
|
2882
|
-
|
|
2773
|
+
const marker = readMarker(stopFile);
|
|
2774
|
+
if (marker?.source === 'usage-watchdog') {
|
|
2775
|
+
clearStop(stopFile);
|
|
2776
|
+
}
|
|
2777
|
+
else if (marker) {
|
|
2778
|
+
writeLine(`[factory] supervise: STOP set by ${marker.source}${marker.at ? ` at ${marker.at}` : ''}; waiting for factory resume`);
|
|
2779
|
+
while (pathExists(stopFile))
|
|
2780
|
+
await sleep(pollMs);
|
|
2781
|
+
}
|
|
2883
2782
|
if (ingest) {
|
|
2884
2783
|
const appended = await ingest();
|
|
2885
2784
|
if (appended > 0)
|
|
@@ -2958,7 +2857,7 @@ function logEventQuietly(eventsFile, type, msg) {
|
|
|
2958
2857
|
/** Creates the STOP file in one exclusive open (no check-then-write race). Returns false when it already existed. */
|
|
2959
2858
|
function createStopFile(stopFile) {
|
|
2960
2859
|
try {
|
|
2961
|
-
writeFileSync(stopFile, '', { flag: 'wx' });
|
|
2860
|
+
writeFileSync(stopFile, formatStopMarker('manual'), { flag: 'wx' });
|
|
2962
2861
|
return true;
|
|
2963
2862
|
}
|
|
2964
2863
|
catch (err) {
|
|
@@ -2993,6 +2892,18 @@ export async function cmdStop(opts = {}) {
|
|
|
2993
2892
|
}
|
|
2994
2893
|
console.log(STOP_SET_MESSAGE);
|
|
2995
2894
|
}
|
|
2895
|
+
/** `factory steer <ref>` (#3401): the handler lives in ./steer.ts; this binds its runtime. */
|
|
2896
|
+
function runSteerCommand(ref, opts = {}) {
|
|
2897
|
+
return cmdSteer(ref, opts, {
|
|
2898
|
+
getRepoRoot,
|
|
2899
|
+
gitUserName: async () => (await shellOps.exec('git config user.name')).stdout,
|
|
2900
|
+
env: process.env,
|
|
2901
|
+
now: () => new Date().toISOString(),
|
|
2902
|
+
CliExitError,
|
|
2903
|
+
errorDetail,
|
|
2904
|
+
log: console.log,
|
|
2905
|
+
});
|
|
2906
|
+
}
|
|
2996
2907
|
export async function cmdResume(issue, opts = {}) {
|
|
2997
2908
|
if (issue === undefined && opts.from !== undefined) {
|
|
2998
2909
|
throw new CliExitError('factory: resume --from needs an issue number: factory resume <issue> --from check', 2);
|
|
@@ -3158,6 +3069,8 @@ export const PROGRAM_HANDLERS = {
|
|
|
3158
3069
|
cmdRuns,
|
|
3159
3070
|
cmdKpis,
|
|
3160
3071
|
cmdClassifierReport,
|
|
3072
|
+
cmdReport,
|
|
3073
|
+
cmdRisk: runRisk,
|
|
3161
3074
|
cmdClassifierBacktestEntry,
|
|
3162
3075
|
cmdGarden,
|
|
3163
3076
|
cmdTui,
|
|
@@ -3196,6 +3109,7 @@ export const PROGRAM_HANDLERS = {
|
|
|
3196
3109
|
cmdSupervise: runSupervise,
|
|
3197
3110
|
cmdStop,
|
|
3198
3111
|
cmdResume,
|
|
3112
|
+
cmdSteer: runSteerCommand,
|
|
3199
3113
|
parseIssueArg,
|
|
3200
3114
|
getRepoRoot,
|
|
3201
3115
|
getCliVersion,
|