@on-par/factory-cli 0.9.18-next.2 → 0.9.18-next.4

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.
Files changed (49) hide show
  1. package/dist/cli/awaiting-ci-recheck.d.ts +31 -0
  2. package/dist/cli/awaiting-ci-recheck.d.ts.map +1 -0
  3. package/dist/cli/awaiting-ci-recheck.js +61 -0
  4. package/dist/cli/awaiting-ci-recheck.js.map +1 -0
  5. package/dist/cli/awaiting-ci-wiring.d.ts +6 -0
  6. package/dist/cli/awaiting-ci-wiring.d.ts.map +1 -0
  7. package/dist/cli/awaiting-ci-wiring.js +45 -0
  8. package/dist/cli/awaiting-ci-wiring.js.map +1 -0
  9. package/dist/cli/daemon.d.ts.map +1 -1
  10. package/dist/cli/daemon.js +3 -3
  11. package/dist/cli/daemon.js.map +1 -1
  12. package/dist/cli/errors.d.ts +6 -0
  13. package/dist/cli/errors.d.ts.map +1 -0
  14. package/dist/cli/errors.js +11 -0
  15. package/dist/cli/errors.js.map +1 -0
  16. package/dist/cli/index.d.ts +4 -14
  17. package/dist/cli/index.d.ts.map +1 -1
  18. package/dist/cli/index.js +50 -77
  19. package/dist/cli/index.js.map +1 -1
  20. package/dist/cli/land-pr.d.ts +11 -0
  21. package/dist/cli/land-pr.d.ts.map +1 -1
  22. package/dist/cli/land-pr.js +27 -1
  23. package/dist/cli/land-pr.js.map +1 -1
  24. package/dist/cli/land.d.ts.map +1 -1
  25. package/dist/cli/land.js +3 -3
  26. package/dist/cli/land.js.map +1 -1
  27. package/dist/cli/queue-startup.d.ts +2 -0
  28. package/dist/cli/queue-startup.d.ts.map +1 -1
  29. package/dist/cli/repo-live-run-lock.d.ts +7 -0
  30. package/dist/cli/repo-live-run-lock.d.ts.map +1 -0
  31. package/dist/cli/repo-live-run-lock.js +14 -0
  32. package/dist/cli/repo-live-run-lock.js.map +1 -0
  33. package/dist/cli/repo-run-lock.d.ts +14 -0
  34. package/dist/cli/repo-run-lock.d.ts.map +1 -0
  35. package/dist/cli/repo-run-lock.js +27 -0
  36. package/dist/cli/repo-run-lock.js.map +1 -0
  37. package/dist/cli/run-lane-deps.d.ts +7 -1
  38. package/dist/cli/run-lane-deps.d.ts.map +1 -1
  39. package/dist/cli/run-lane.d.ts.map +1 -1
  40. package/dist/cli/run-lane.js +31 -3
  41. package/dist/cli/run-lane.js.map +1 -1
  42. package/dist/cli/run.d.ts.map +1 -1
  43. package/dist/cli/run.js +4 -0
  44. package/dist/cli/run.js.map +1 -1
  45. package/dist/cli/self-update-apply.d.ts +1 -1
  46. package/dist/cli/self-update-apply.d.ts.map +1 -1
  47. package/dist/cli/self-update-apply.js +4 -5
  48. package/dist/cli/self-update-apply.js.map +1 -1
  49. package/package.json +4 -4
package/dist/cli/index.js CHANGED
@@ -12,7 +12,10 @@ 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, 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, 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, RunLockHeldError, runOvernightQueue, parseRemoteSlug, setupWorktree, shellEscape, listBuiltInCheckers, sweepWorktrees, watchChecks, withFileLock, withGitLock, withRunLock, writePortFile, buildTriagePrompt, parseTriageCandidates, laneSlug, formatStopMarker, readStopMarker, detectClaudePlan, } 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, } from '@on-par/factory-core/internal';
16
+ import { CliExitError } from './errors.js';
17
+ import { withRepoRunLock } from './repo-run-lock.js';
18
+ export { CliExitError, withRepoRunLock };
16
19
  import { formatSteerabilityReport } from '@on-par/factory-core/internal';
17
20
  import { runTui } from '@on-par/factory-tui';
18
21
  import { clearSelfUpdateStop } from './self-update-stop.js';
@@ -22,7 +25,7 @@ import { buildSteerabilityReport } from './steerability-report.js';
22
25
  import { runHarnessCheckCommand } from './harness-check.js';
23
26
  import { landIssue } from './land.js';
24
27
  import { STOP_SET_MESSAGE } from './stop-sentinel.js';
25
- import { AwaitingReviewError, CiFailedError, CiUnverifiedError, LandConflictError, LandFailureError, MergeGatedError, } from './land-pr.js';
28
+ import { AwaitingReviewError, CiFailedError, isCiNotGreen, LandConflictError, LandFailureError, MergeGatedError, } from './land-pr.js';
26
29
  import { setLandRuntime } from './land-runtime.js';
27
30
  import { cmdRun } from './run.js';
28
31
  import { shipIssue } from './ship.js';
@@ -578,15 +581,6 @@ async function cmdMigrate(opts = {}) {
578
581
  }
579
582
  export class InvalidProductNameError extends Error {
580
583
  }
581
- /** Expected user-facing CLI failure. Thrown by command helpers; only main() maps it to a process exit code. */
582
- export class CliExitError extends Error {
583
- code;
584
- constructor(message, code) {
585
- super(message);
586
- this.code = code;
587
- this.name = 'CliExitError';
588
- }
589
- }
590
584
  /** Parse a CLI <issue> argument; rejects unsafe integers (#2836), also used by reset. Throws CliExitError(2) before any git/GitHub work runs. */
591
585
  export function parseIssueArg(raw) {
592
586
  const n = /^\d+$/.test(raw.trim()) ? Number(raw) : NaN;
@@ -1597,7 +1591,7 @@ async function cmdShip(issueNum, opts) {
1597
1591
  throw new CliExitError(`factory: ${err.message}`, 2);
1598
1592
  throw new CliExitError(`Ship failed for issue #${issueNum}: ${err.message}`, 1);
1599
1593
  }
1600
- });
1594
+ }, { scope: { kind: 'issue', key: workKeyFromIssue(issueNum) } });
1601
1595
  }
1602
1596
  async function cmdRunIssue(issueNum, opts) {
1603
1597
  if (availableAgentClis(coreOps.isCommandAvailable).length === 0) {
@@ -1659,7 +1653,7 @@ async function cmdRunIssue(issueNum, opts) {
1659
1653
  }
1660
1654
  throw new CliExitError(`Run failed for issue #${issueNum}: ${err.message}`, 1);
1661
1655
  }
1662
- });
1656
+ }, { scope: { kind: 'issue', key: workKeyFromIssue(issueNum) } });
1663
1657
  }
1664
1658
  /** Runs each decomposed child through the one-shot pipeline in build order, inside the caller's
1665
1659
  * run lock, then prints a per-child summary. Nested decompositions expand in place and closed
@@ -1684,14 +1678,14 @@ async function runDecomposedChildren(parent, children, opts, env) {
1684
1678
  console.log(styleText('cyan', `one-shot: resolved ${work.id} — running the pipeline (queue untouched)`));
1685
1679
  let outcome;
1686
1680
  try {
1687
- await shipIssue(child, opts, {
1681
+ await withRepoRunLock(paths, 'factory run-issue', () => shipIssue(child, opts, {
1688
1682
  repoRoot: env.repoRoot,
1689
1683
  ghRepo: env.ghRepo,
1690
1684
  workRequest: work,
1691
1685
  onOutcome: (o) => {
1692
1686
  outcome = o;
1693
1687
  },
1694
- });
1688
+ }), { scope: { kind: 'issue', key: workKeyFromIssue(child) } });
1695
1689
  }
1696
1690
  catch (err) {
1697
1691
  if (err instanceof IssueDecomposedError) {
@@ -1731,51 +1725,51 @@ async function cmdRunBrief(briefPath, opts) {
1731
1725
  throw new CliExitError(`factory: ${notInitializedMessage()}`, 2);
1732
1726
  }
1733
1727
  const branchPrefix = branchPrefixOrExit(opts.branchPrefix);
1734
- await withRepoRunLock(paths, 'factory run-brief', async () => {
1735
- recordDraftPrFlag(paths.runFlags, opts.draftPr);
1736
- let localOnly;
1737
- if (opts.workspace !== undefined) {
1738
- try {
1739
- localOnly = resolveLocalOnlyPolicy(opts.workspace);
1740
- }
1741
- catch (err) {
1742
- if (err instanceof InvalidWorkspaceError)
1743
- throw new CliExitError(`factory: ${err.message}`, 2);
1744
- throw err;
1745
- }
1728
+ let localOnly;
1729
+ if (opts.workspace !== undefined) {
1730
+ try {
1731
+ localOnly = resolveLocalOnlyPolicy(opts.workspace);
1746
1732
  }
1747
- let artifactsDir;
1748
- if (opts.artifacts !== undefined) {
1749
- if (!localOnly) {
1750
- throw new CliExitError('factory: --artifacts requires --workspace (local-only runs only)', 2);
1751
- }
1752
- try {
1753
- artifactsDir = resolveArtifactsDir(opts.artifacts);
1754
- }
1755
- catch (err) {
1756
- if (err instanceof InvalidArtifactsDirError)
1757
- throw new CliExitError(`factory: ${err.message}`, 2);
1758
- throw err;
1759
- }
1760
- console.log(styleText('cyan', `local-only: benchmark artifacts will be written to ${artifactsDir}`));
1733
+ catch (err) {
1734
+ if (err instanceof InvalidWorkspaceError)
1735
+ throw new CliExitError(`factory: ${err.message}`, 2);
1736
+ throw err;
1737
+ }
1738
+ }
1739
+ let artifactsDir;
1740
+ if (opts.artifacts !== undefined) {
1741
+ if (!localOnly) {
1742
+ throw new CliExitError('factory: --artifacts requires --workspace (local-only runs only)', 2);
1761
1743
  }
1762
- // Local-only runs never touch GitHub — no remote required, no mutation possible.
1763
- const ghRepo = localOnly ? 'local/workspace' : await getGitHubRepo();
1764
- // Pre-flight: resolve the brief through the canonical work-request seam
1765
- // BEFORE any worktree or PR exists — a malformed brief exits here, so BUILD never starts.
1766
- const workSources = createDefaultWorkSourceRegistry({ octokit: getOctokit() });
1767
- let work;
1768
1744
  try {
1769
- work = await workSources.resolve(LOCAL_BRIEF_SOURCE, { path: briefPath });
1745
+ artifactsDir = resolveArtifactsDir(opts.artifacts);
1770
1746
  }
1771
1747
  catch (err) {
1772
- if (err instanceof InvalidWorkRequestInputError) {
1748
+ if (err instanceof InvalidArtifactsDirError)
1773
1749
  throw new CliExitError(`factory: ${err.message}`, 2);
1774
- }
1775
- throw new CliExitError(`factory: could not resolve brief at ${briefPath} (${errorDetail(err)}) — no worktree or PR was created`, 2);
1750
+ throw err;
1751
+ }
1752
+ console.log(styleText('cyan', `local-only: benchmark artifacts will be written to ${artifactsDir}`));
1753
+ }
1754
+ // Local-only runs never touch GitHub — no remote required, no mutation possible.
1755
+ const ghRepo = localOnly ? 'local/workspace' : await getGitHubRepo();
1756
+ // Pre-flight: resolve the brief through the canonical work-request seam
1757
+ // BEFORE any worktree or PR exists — a malformed brief exits here, so BUILD never starts.
1758
+ const workSources = createDefaultWorkSourceRegistry({ octokit: getOctokit() });
1759
+ let work;
1760
+ try {
1761
+ work = await workSources.resolve(LOCAL_BRIEF_SOURCE, { path: briefPath });
1762
+ }
1763
+ catch (err) {
1764
+ if (err instanceof InvalidWorkRequestInputError) {
1765
+ throw new CliExitError(`factory: ${err.message}`, 2);
1776
1766
  }
1777
- const digest = work.reference?.externalId ?? '';
1778
- const runNum = briefRunNumber(digest);
1767
+ throw new CliExitError(`factory: could not resolve brief at ${briefPath} (${errorDetail(err)}) — no worktree or PR was created`, 2);
1768
+ }
1769
+ const digest = work.reference?.externalId ?? '';
1770
+ const runNum = briefRunNumber(digest);
1771
+ await withRepoRunLock(paths, 'factory run-brief', async () => {
1772
+ recordDraftPrFlag(paths.runFlags, opts.draftPr);
1779
1773
  console.log(styleText('cyan', `one-shot: resolved ${work.id} — running the pipeline (queue untouched)`));
1780
1774
  logEvent(paths.events, 'work-source', runNum, `inline local brief ${briefPath} (sha256 ${digest})`);
1781
1775
  if (localOnly) {
@@ -1801,7 +1795,7 @@ async function cmdRunBrief(briefPath, opts) {
1801
1795
  catch (err) {
1802
1796
  throw new CliExitError(`Run failed for brief ${briefPath}: ${err.message}`, 1);
1803
1797
  }
1804
- });
1798
+ }, { scope: { kind: 'issue', key: workKeyFromIssue(runNum) } });
1805
1799
  }
1806
1800
  async function cmdLocalSmallDryRun(issueNum, opts) {
1807
1801
  const repoRoot = await getRepoRoot();
@@ -1891,27 +1885,6 @@ function worktreePathFor(repoRoot, ghRepo, issueNum, prefix, loaders = configLoa
1891
1885
  prefix,
1892
1886
  });
1893
1887
  }
1894
- /** Fences a whole run behind the checkout's `.factory/run.lock` (#598). A live holder is
1895
- * refused immediately — never queued behind a multi-hour run — and a dead holder's lock is
1896
- * reclaimed by withRunLock. Nested acquisition inside one process is a pass-through, so
1897
- * `factory supervise` can hold this across its loop. */
1898
- export async function withRepoRunLock(paths, command, fn, deps = {}) {
1899
- const { runLock = withRunLock, emitEvent = logEvent } = deps;
1900
- try {
1901
- return await runLock(paths.runLock, fn, {
1902
- command,
1903
- onReclaim: (pid) => emitEvent(paths.events, 'lock-stolen', '-', `reclaimed ${paths.runLock} from dead holder pid ${pid ?? 'unknown'}`),
1904
- });
1905
- }
1906
- catch (err) {
1907
- if (err instanceof RunLockHeldError) {
1908
- emitEvent(paths.events, 'run_lock_conflict', '-', `${command} refused: ${err.message}`);
1909
- throw new CliExitError(`factory: ${err.message} — stop that run (or wait for it to finish) before starting another. ` +
1910
- `A dead run's lock is reclaimed automatically; remove ${paths.runLock} only if you are sure no factory run is live.`, 2);
1911
- }
1912
- throw err;
1913
- }
1914
- }
1915
1888
  export async function reapParkedLaneWorktree(issue, repoRoot, ghRepo, paths, prefixOverride) {
1916
1889
  await reapParkedLane({ configLoaders, worktreePathFor }, reapLaneWorktree, [
1917
1890
  issue,
@@ -2033,7 +2006,7 @@ export async function cmdLand(issueNum, opts = {}, deps = {}) {
2033
2006
  console.log(styleText('yellow', `⏸ PR #${err.prNumber} for issue #${issueNum} awaiting human review — left open`));
2034
2007
  return;
2035
2008
  }
2036
- if (err instanceof CiUnverifiedError) {
2009
+ if (isCiNotGreen(err)) {
2037
2010
  console.log(styleText('yellow', `⏸ PR #${err.prNumber} for issue #${issueNum} — CI never reached a green verdict — left open, not merged`));
2038
2011
  return;
2039
2012
  }
@@ -3026,7 +2999,7 @@ async function resumeFromCheck(issueNum) {
3026
2999
  return;
3027
3000
  throw new CliExitError(`Resume failed for issue #${issueNum}: ${err.message}`, 1);
3028
3001
  }
3029
- });
3002
+ }, { scope: { kind: 'issue', key: workKeyFromIssue(issueNum) } });
3030
3003
  }
3031
3004
  /** index.ts bindings the commander registrations in program*.ts call (#2793); type-only imports there avoid a runtime cycle. */
3032
3005
  /** `factory supervise` (#2797): the handler lives in ./supervise.ts; this binds its runtime. */