@on-par/factory-cli 0.9.25-next.17 → 0.9.25-next.19

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli/index.js CHANGED
@@ -12,7 +12,7 @@ import { startupSweeps, warnQueueDiagnostics } from './queue-startup.js';
12
12
  import { proposeTriageQueue } from './triage-open-prs.js';
13
13
  import { makeQueuePreflightOps, preflightQueuedIssue, } from './queue-preflight.js';
14
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, InvalidWorkRequestInputError, isCommandAvailable, isProxyRunning, kpiBranchPrefixes, kpisToHistoryRecord, laneBaseUrl, laneEnv, laneHostLabel, loadFactoryConfigForRepo, loadModelsConfig, loadRepoConfig, loadRoutesConfig, 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, resolveMergePolicy, resolveSkipCI, resolveTimeouts, resolveUsageCap, resolveWatchdogPolicy, runAutoIngest, shipPhase, formatCostTotal, validateQueue, watchUsage, writeLocalRunReport, workKeyFromIssue, writeProxyState, } from '@on-par/factory-core';
15
- import { acquirePidFile, createCheckResumeStore, DEFAULT_GARDEN_MAX_CLUSTERS, DEFAULT_GARDEN_SINCE, capGardenClusters, clusterGarden, dedupGardenClusters, filterGardenOnlyNew, filterGardenWindow, markGardenTrackingUnknown, parseGardenDuration, readHarvestEvents, renderGardenJson, renderGardenReport, laneWorktreePath, migrateRepoConfigToYaml, readRepoConfigFile, rewriteRepoConfigFile, 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, listStatelessLaneIssues, formatGcReport, gitFetch, listMicroVms, listOrphanContainers, logCost, logEvent, planQueueMigration, pollBlockedParks, readCosts, readGithubQueueSnapshot, reapCleanShipWorktree, reapLaneWorktree, reapOrphanContainers, reapOrphanMicroVm, releaseRuntimeFiles, releaseStaleClaims, resolveBranchPrefix, resolveEffectiveConfig, resolveExperimental, resolveLocalOnly, describeDraftPrPolicy, resolveDraftPrPolicy, resolvePrClassifierPolicy, resolveReviewFloorRules, runOvernightQueue, parseRemoteSlug, setupWorktree, shellEscape, listBuiltInCheckers, sweepWorktrees, watchChecks, withFileLock, withGitLock, writePortFile, buildTriagePrompt, parseTriageCandidates, laneSlug, formatStopMarker, readStopMarker, detectClaudePlan, findSlicePlanComment, listBlockedBy, } from '@on-par/factory-core/internal';
15
+ import { acquirePidFile, createCheckResumeStore, DEFAULT_GARDEN_MAX_CLUSTERS, DEFAULT_GARDEN_SINCE, capGardenClusters, clusterGarden, dedupGardenClusters, filterGardenOnlyNew, filterGardenWindow, markGardenTrackingUnknown, parseGardenDuration, readHarvestEvents, renderGardenJson, renderGardenReport, laneWorktreePath, migrateRepoConfigToYaml, readRepoConfigFile, rewriteRepoConfigFile, 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, listStatelessLaneIssues, formatGcReport, gitFetch, listMicroVms, listOrphanContainers, logCost, logEvent, planQueueMigration, pollBlockedParks, readCosts, readGithubQueueSnapshot, reapCleanShipWorktree, reapLaneWorktree, reapOrphanContainers, reapOrphanMicroVm, releaseRuntimeFiles, releaseStaleClaims, resolveBranchPrefix, resolveEffectiveConfig, resolveExperimental, resolveLocalOnly, describeDraftPrPolicy, resolveDraftPrPolicy, resolvePrClassifierPolicy, resolveReviewFloorRules, runOvernightQueue, parseRemoteSlug, setupWorktree, shellEscape, listBuiltInCheckers, sweepWorktrees, watchChecks, withFileLock, withGitLock, writePortFile, buildTriagePrompt, parseTriageCandidates, laneSlug, formatStopMarker, readStopMarker, detectClaudePlan, findSlicePlanComment, listBlockedBy, aliasDeprecatedSizeGateMode, } from '@on-par/factory-core/internal';
16
16
  import { CliExitError } from './errors.js';
17
17
  import { baseBranchConflict, baseBranchOrExit } from './ship-options.js';
18
18
  import { runIssueStack } from './stack-run.js';
@@ -68,7 +68,6 @@ import { selectOctokit } from './github-stub.js';
68
68
  import { createFactoryOctokit } from './octokit.js';
69
69
  import { runDraftCommand } from './draft-command.js';
70
70
  import { checkErrorExit, checkReportExit, createDeepModelRunner, runIssueCheck } from './ready-check.js';
71
- import { childRunSucceeded, formatChildRunSummary, runChildrenInOrder } from './run-children.js';
72
71
  import { cmdRunBrief as cmdRunBriefWith } from './run-brief.js';
73
72
  import { readRunFlagOverrides, recordDraftPrFlag } from './run-flags.js';
74
73
  import { runStalenessGuard } from './staleness.js';
@@ -670,7 +669,7 @@ function sizeGateOrExit(raw) {
670
669
  if (mode === undefined) {
671
670
  throw new CliExitError(`factory: invalid --size-gate '${raw}' — expected one of: ${SIZE_GATE_MODES.join(', ')}`, 2);
672
671
  }
673
- return mode;
672
+ return aliasDeprecatedSizeGateMode(mode);
674
673
  }
675
674
  function maxSlicesOrExit(raw) {
676
675
  if (raw === undefined)
@@ -1606,6 +1605,8 @@ async function cmdShip(issueNum, opts) {
1606
1605
  }, { scope: { kind: 'issue', key: workKeyFromIssue(issueNum) } });
1607
1606
  }
1608
1607
  async function cmdRunIssue(issueNum, opts) {
1608
+ if (opts.runChildren)
1609
+ throw new CliExitError('--run-children was removed; oversized issues stack with --stack', 2);
1609
1610
  if (availableAgentClis(coreOps.isCommandAvailable).length === 0) {
1610
1611
  throw new CliExitError(`factory: ${missingAgentCliMessage()}`, 2);
1611
1612
  }
@@ -1614,22 +1615,24 @@ async function cmdRunIssue(issueNum, opts) {
1614
1615
  if (!existsSync(paths.root)) {
1615
1616
  throw new CliExitError(`factory: ${notInitializedMessage()}`, 2);
1616
1617
  }
1617
- const { runChildren, sizeGate: rawSizeGate, maxSlices: rawMaxSlices, stack, ...shipOpts } = opts;
1618
+ const { runChildren: _removed, sizeGate: rawSizeGate, maxSlices: rawMaxSlices, stack, ...shipOpts } = opts;
1618
1619
  const branchPrefix = branchPrefixOrExit(opts.branchPrefix, paths.config);
1619
- if (stack && rawSizeGate !== undefined && rawSizeGate !== 'stack') {
1620
+ const parsedSizeGate = sizeGateOrExit(rawSizeGate);
1621
+ if (stack && parsedSizeGate !== undefined && parsedSizeGate !== 'stack') {
1620
1622
  throw new CliExitError('factory: --stack is --size-gate stack — drop one of them', 2);
1621
1623
  }
1622
- const sizeGate = stack ? 'stack' : sizeGateOrExit(rawSizeGate);
1624
+ const sizeGate = stack ? 'stack' : parsedSizeGate;
1625
+ const explicitStack = stack === true || rawSizeGate === 'stack';
1623
1626
  const maxSlices = maxSlicesOrExit(rawMaxSlices);
1624
1627
  const baseBranch = baseBranchOrExit(opts.baseBranch);
1625
- const conflict = baseBranchConflict({ baseBranch, sizeGate, runChildren });
1628
+ const conflict = baseBranchConflict({ baseBranch, sizeGate });
1626
1629
  if (conflict)
1627
1630
  throw new CliExitError(conflict, 2);
1628
1631
  if (sizeGate === 'stack' && opts.pr === false) {
1629
1632
  throw new CliExitError('factory: --stack opens a PR per slice — drop --no-pr or drop --stack', 2);
1630
1633
  }
1631
- if (opts.pr === false && ((sizeGate !== undefined && sizeGate !== 'off') || runChildren)) {
1632
- throw new CliExitError('factory: --no-pr runs with the size gate off — drop --size-gate/--run-children or drop --no-pr', 2);
1634
+ if (opts.pr === false && sizeGate !== undefined && sizeGate !== 'off') {
1635
+ throw new CliExitError('factory: --no-pr runs with the size gate off — drop --size-gate or drop --no-pr', 2);
1633
1636
  }
1634
1637
  await withRepoRunLock(paths, 'factory run-issue', async () => {
1635
1638
  recordDraftPrFlag(paths.runFlags, opts.draftPr);
@@ -1667,98 +1670,42 @@ async function cmdRunIssue(issueNum, opts) {
1667
1670
  const memberWork = issue === issueNum
1668
1671
  ? work
1669
1672
  : await workSources.resolve(GITHUB_ISSUE_SOURCE, { repo: ghRepo, issue });
1670
- await shipIssue(issue, { ...shipOpts, branchPrefix, sizeGate, maxSlices, baseBranch: base, parkOverBudget: true }, { repoRoot, ghRepo, workRequest: memberWork, onOutcome });
1673
+ await shipIssue(issue, {
1674
+ ...shipOpts,
1675
+ branchPrefix,
1676
+ sizeGate,
1677
+ explicitStack,
1678
+ maxSlices,
1679
+ baseBranch: base,
1680
+ parkOverBudget: true,
1681
+ }, { repoRoot, ghRepo, workRequest: memberWork, onOutcome });
1671
1682
  },
1672
1683
  readSlicePlan: async () => {
1673
1684
  const lookup = await findSlicePlanComment({ octokit: getOctokit(), repo: ghRepo, issue: issueNum });
1674
1685
  return lookup.status === 'found' ? lookup.plan : undefined;
1675
1686
  },
1676
1687
  ship: async (base, onOutcome) => {
1677
- await shipIssue(issueNum, { ...shipOpts, branchPrefix, sizeGate, maxSlices, baseBranch: base }, { repoRoot, ghRepo, workRequest: work, onOutcome });
1688
+ await shipIssue(issueNum, { ...shipOpts, branchPrefix, sizeGate, explicitStack, maxSlices, baseBranch: base }, { repoRoot, ghRepo, workRequest: work, onOutcome });
1678
1689
  },
1679
1690
  });
1680
1691
  return;
1681
1692
  }
1682
- await shipIssue(issueNum, { ...shipOpts, branchPrefix, sizeGate, maxSlices }, { repoRoot, ghRepo, workRequest: work });
1693
+ await shipIssue(issueNum, { ...shipOpts, branchPrefix, sizeGate, explicitStack, maxSlices }, { repoRoot, ghRepo, workRequest: work });
1683
1694
  }
1684
1695
  catch (err) {
1685
1696
  if (err instanceof CliExitError)
1686
1697
  throw err;
1687
1698
  if (err instanceof IssueSkippedError)
1688
1699
  return;
1689
- if (err instanceof IssueDecomposedError && runChildren) {
1690
- await runDecomposedChildren(issueNum, err.childIssues, { ...shipOpts, branchPrefix, sizeGate, maxSlices }, { repoRoot, ghRepo, workSources });
1691
- return;
1692
- }
1693
- if (err instanceof IssueDecomposedError && !runChildren) {
1700
+ if (err instanceof IssueDecomposedError) {
1694
1701
  const children = err.childIssues.map((n) => `#${n}`).join(', ');
1695
1702
  throw new CliExitError(`Run failed for issue #${issueNum}: the size gate decomposed it into ${children} — no child issue was run. ` +
1696
- `Re-run with \`factory run-issue ${issueNum} --run-children\` to run them.`, 1);
1703
+ `Oversized issues stack: re-run with \`factory run-issue ${issueNum} --stack\`.`, 1);
1697
1704
  }
1698
1705
  throw new CliExitError(`Run failed for issue #${issueNum}: ${err.message}`, 1);
1699
1706
  }
1700
1707
  }, { scope: { kind: 'issue', key: workKeyFromIssue(issueNum) } });
1701
1708
  }
1702
- /** Runs each decomposed child through the one-shot pipeline in build order, inside the caller's
1703
- * run lock, then prints a per-child summary. Nested decompositions expand in place and closed
1704
- * children are skipped. A failed or escalated child is recorded and the next one still runs;
1705
- * `.factory/STOP` is checked before each child starts (read only, ADR-0111). Throws CliExitError(1)
1706
- * unless every child is ready or skipped (#1747, #1748, #1749). */
1707
- async function runDecomposedChildren(parent, children, opts, env) {
1708
- const list = children.map((n) => `#${n}`).join(', ');
1709
- console.log(styleText('cyan', `run-children: #${parent} decomposed into ${list} — running them in build order`));
1710
- const paths = getFactoryPaths(env.repoRoot);
1711
- const results = await runChildrenInOrder(children, async (child) => {
1712
- let work;
1713
- try {
1714
- work = await env.workSources.resolve(GITHUB_ISSUE_SOURCE, {
1715
- repo: env.ghRepo,
1716
- issue: child,
1717
- });
1718
- }
1719
- catch (err) {
1720
- return { issue: child, status: 'failed', detail: `could not resolve issue #${child} (${errorDetail(err)})` };
1721
- }
1722
- console.log(styleText('cyan', `one-shot: resolved ${work.id} — running the pipeline (queue untouched)`));
1723
- let outcome;
1724
- try {
1725
- await withRepoRunLock(paths, 'factory run-issue', () => shipIssue(child, opts, {
1726
- repoRoot: env.repoRoot,
1727
- ghRepo: env.ghRepo,
1728
- workRequest: work,
1729
- onOutcome: (o) => {
1730
- outcome = o;
1731
- },
1732
- }), { scope: { kind: 'issue', key: workKeyFromIssue(child) } });
1733
- }
1734
- catch (err) {
1735
- if (err instanceof IssueDecomposedError) {
1736
- const again = err.childIssues.map((n) => `#${n}`).join(', ');
1737
- console.log(styleText('cyan', `run-children: #${child} decomposed into ${again} — running them in its place`));
1738
- return { issue: child, status: 'decomposed', children: err.childIssues };
1739
- }
1740
- if (err instanceof IssueSkippedError) {
1741
- return { issue: child, status: 'skipped', detail: err.message };
1742
- }
1743
- return { issue: child, status: 'failed', detail: err instanceof Error ? err.message : String(err) };
1744
- }
1745
- const done = outcome;
1746
- return { issue: child, status: 'ready', prNumber: done && 'prNumber' in done ? done.prNumber : undefined };
1747
- }, [parent], (next) => {
1748
- if (!existsSync(paths.stop))
1749
- return false;
1750
- logEvent(paths.events, 'stopped', next, 'STOP file present');
1751
- console.log(styleText('yellow', `run-children: .factory/STOP present — not starting #${next} or any later child`));
1752
- return true;
1753
- });
1754
- const ok = childRunSucceeded(results);
1755
- for (const line of formatChildRunSummary(parent, results)) {
1756
- console.log(ok ? styleText('green', line) : styleText('yellow', line));
1757
- }
1758
- if (!ok) {
1759
- throw new CliExitError(`Run failed for issue #${parent}: not every decomposed child reached ready-for-review or was skipped as closed (failed or not-run children are listed above)`, 1);
1760
- }
1761
- }
1762
1709
  async function cmdLocalSmallDryRun(issueNum, opts) {
1763
1710
  const repoRoot = await getRepoRoot();
1764
1711
  const ghRepo = await getGitHubRepo();