sequant 2.1.1 → 2.1.2

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.
@@ -0,0 +1,167 @@
1
+ /**
2
+ * ConfigResolver — 4-layer configuration merge for sequant run.
3
+ *
4
+ * Priority: defaults < settings < env < explicit (CLI flags).
5
+ * Handles Commander.js --no-X boolean negation at the CLI boundary.
6
+ *
7
+ * @module
8
+ */
9
+ import { DEFAULT_CONFIG, DEFAULT_PHASES, } from "./types.js";
10
+ import { getEnvConfig } from "./batch-executor.js";
11
+ /**
12
+ * Coerce an env-var string to the type of the default value.
13
+ * Returns the string as-is if no default exists for type inference.
14
+ */
15
+ function coerceEnvValue(value, defaultValue) {
16
+ if (typeof value !== "string")
17
+ return value;
18
+ if (typeof defaultValue === "number") {
19
+ const n = Number(value);
20
+ return isNaN(n) ? value : n;
21
+ }
22
+ if (typeof defaultValue === "boolean") {
23
+ return value === "true";
24
+ }
25
+ return value;
26
+ }
27
+ /**
28
+ * Generic 4-layer priority merge.
29
+ * For each key across all layers: explicit > env > settings > defaults.
30
+ * Env strings are coerced to match the type of the default value.
31
+ */
32
+ export class ConfigResolver {
33
+ layers;
34
+ constructor(layers) {
35
+ this.layers = layers;
36
+ }
37
+ /**
38
+ * Resolve all layers into a single merged config object.
39
+ * Priority: explicit > env > settings > defaults.
40
+ */
41
+ resolve() {
42
+ const { defaults, settings, env, explicit } = this.layers;
43
+ // Collect all keys across layers
44
+ const allKeys = new Set([
45
+ ...Object.keys(defaults),
46
+ ...Object.keys(settings),
47
+ ...Object.keys(env),
48
+ ...Object.keys(explicit),
49
+ ]);
50
+ const result = {};
51
+ for (const key of allKeys) {
52
+ // Check each layer in reverse priority (lowest first)
53
+ const layers = [
54
+ { value: defaults[key] },
55
+ { value: settings[key] },
56
+ { value: env[key] },
57
+ { value: explicit[key] },
58
+ ];
59
+ // Walk from highest to lowest priority, take first defined value
60
+ let resolved = undefined;
61
+ const defaultVal = defaults[key];
62
+ for (const layer of layers) {
63
+ if (layer.value !== undefined) {
64
+ resolved = layer.value;
65
+ }
66
+ }
67
+ // Coerce env values if the winning value came from env layer
68
+ if (resolved !== undefined &&
69
+ explicit[key] === undefined &&
70
+ env[key] !== undefined &&
71
+ settings[key] === undefined) {
72
+ // Only env contributed — coerce
73
+ resolved = coerceEnvValue(resolved, defaultVal);
74
+ }
75
+ else if (resolved !== undefined &&
76
+ explicit[key] === undefined &&
77
+ env[key] !== undefined) {
78
+ // env is present and wins over settings — coerce the env value
79
+ resolved = coerceEnvValue(env[key], defaultVal);
80
+ }
81
+ result[key] = resolved;
82
+ }
83
+ return result;
84
+ }
85
+ }
86
+ /**
87
+ * Normalize Commander.js --no-X flags into RunOptions negation fields.
88
+ * This is a thin adapter at the CLI boundary — not used by programmatic callers.
89
+ */
90
+ export function normalizeCommanderOptions(options) {
91
+ const raw = options;
92
+ return {
93
+ ...options,
94
+ ...(raw.log === false && { noLog: true }),
95
+ ...(raw.smartTests === false && { noSmartTests: true }),
96
+ ...(raw.mcp === false && { noMcp: true }),
97
+ ...(raw.retry === false && { noRetry: true }),
98
+ ...(raw.rebase === false && { noRebase: true }),
99
+ ...(raw.pr === false && { noPr: true }),
100
+ };
101
+ }
102
+ /**
103
+ * Resolve RunOptions + settings + env into a fully merged RunOptions.
104
+ * This replaces the inline merging logic previously in run.ts.
105
+ */
106
+ export function resolveRunOptions(cliOptions, settings) {
107
+ const normalized = normalizeCommanderOptions(cliOptions);
108
+ const envConfig = getEnvConfig();
109
+ // Strip undefined keys so programmatic callers don't clobber env/settings values
110
+ const defined = Object.fromEntries(Object.entries(normalized).filter(([, v]) => v !== undefined));
111
+ const merged = {
112
+ // Settings defaults
113
+ sequential: defined.sequential ?? settings.run.sequential,
114
+ concurrency: defined.concurrency ?? settings.run.concurrency,
115
+ timeout: defined.timeout ?? settings.run.timeout,
116
+ logPath: defined.logPath ?? settings.run.logPath,
117
+ qualityLoop: defined.qualityLoop ?? settings.run.qualityLoop,
118
+ maxIterations: defined.maxIterations ?? settings.run.maxIterations,
119
+ noSmartTests: defined.noSmartTests ?? !settings.run.smartTests,
120
+ // Agent settings
121
+ isolateParallel: defined.isolateParallel ?? settings.agents.isolateParallel,
122
+ // Env overrides
123
+ ...envConfig,
124
+ // CLI explicit options override all
125
+ ...defined,
126
+ };
127
+ // Auto-detect phases from labels unless --phases explicitly set
128
+ const autoDetectPhases = !cliOptions.phases && settings.run.autoDetectPhases;
129
+ merged.autoDetectPhases = autoDetectPhases;
130
+ return merged;
131
+ }
132
+ /**
133
+ * Build an ExecutionConfig from merged RunOptions and settings.
134
+ * Extracts the phase-timeout, MCP, retry, and mode resolution logic
135
+ * that was previously inline in run.ts.
136
+ */
137
+ export function buildExecutionConfig(mergedOptions, settings, issueCount) {
138
+ const explicitPhases = mergedOptions.phases
139
+ ? mergedOptions.phases.split(",").map((p) => p.trim())
140
+ : null;
141
+ const mcpEnabled = mergedOptions.noMcp
142
+ ? false
143
+ : (settings.run.mcp ?? DEFAULT_CONFIG.mcp);
144
+ const retryEnabled = mergedOptions.noRetry
145
+ ? false
146
+ : (settings.run.retry ?? true);
147
+ const isSequential = mergedOptions.sequential ?? false;
148
+ const isParallel = !isSequential && issueCount > 1;
149
+ return {
150
+ ...DEFAULT_CONFIG,
151
+ phases: explicitPhases ?? DEFAULT_PHASES,
152
+ sequential: isSequential,
153
+ concurrency: mergedOptions.concurrency ?? DEFAULT_CONFIG.concurrency,
154
+ parallel: isParallel,
155
+ dryRun: mergedOptions.dryRun ?? false,
156
+ verbose: mergedOptions.verbose ?? false,
157
+ phaseTimeout: mergedOptions.timeout ?? DEFAULT_CONFIG.phaseTimeout,
158
+ qualityLoop: mergedOptions.qualityLoop ?? false,
159
+ maxIterations: mergedOptions.maxIterations ?? DEFAULT_CONFIG.maxIterations,
160
+ noSmartTests: mergedOptions.noSmartTests ?? false,
161
+ mcp: mcpEnabled,
162
+ retry: retryEnabled,
163
+ agent: mergedOptions.agent ?? settings.run.agent,
164
+ aiderSettings: settings.run.aider,
165
+ isolateParallel: mergedOptions.isolateParallel,
166
+ };
167
+ }
@@ -1,16 +1,26 @@
1
1
  /**
2
2
  * Error classifier — categorizes phase failures from stderr content.
3
3
  *
4
- * Pattern-matches stderr lines against known error signatures to produce
5
- * a structured category for analytics and debugging.
4
+ * Refactored (AC-7): Returns typed SequantError instances instead of string
5
+ * categories. Exit codes are the primary signal; stderr patterns are secondary.
6
6
  */
7
- /** All recognized error categories (AC-7: defined as constants). */
7
+ import { SequantError } from "../errors.js";
8
+ /** All recognized error categories (kept for backwards compatibility). */
8
9
  export declare const ERROR_CATEGORIES: readonly ["context_overflow", "api_error", "hook_failure", "build_error", "timeout", "unknown"];
9
10
  export type ErrorCategory = (typeof ERROR_CATEGORIES)[number];
10
11
  /**
11
- * Classify stderr lines into an error category.
12
+ * Map from error type name to legacy category string.
13
+ * Used for backwards-compatible log storage (AC-8).
14
+ */
15
+ export declare function errorTypeToCategory(error: SequantError): ErrorCategory;
16
+ /**
17
+ * Classify stderr lines into a typed SequantError instance (AC-7).
18
+ *
19
+ * Exit codes are the primary signal; stderr patterns are secondary.
20
+ * Returns a typed error instance with structured metadata.
12
21
  *
13
- * Scans lines in order; the first classifier whose pattern matches any line wins.
14
- * Returns "unknown" if no patterns match.
22
+ * @param stderrLines - Lines from stderr
23
+ * @param exitCode - Process exit code (primary signal)
24
+ * @returns Typed SequantError subclass instance
15
25
  */
16
- export declare function classifyError(stderrLines: string[]): ErrorCategory;
26
+ export declare function classifyError(stderrLines: string[], exitCode?: number): SequantError;
@@ -1,10 +1,11 @@
1
1
  /**
2
2
  * Error classifier — categorizes phase failures from stderr content.
3
3
  *
4
- * Pattern-matches stderr lines against known error signatures to produce
5
- * a structured category for analytics and debugging.
4
+ * Refactored (AC-7): Returns typed SequantError instances instead of string
5
+ * categories. Exit codes are the primary signal; stderr patterns are secondary.
6
6
  */
7
- /** All recognized error categories (AC-7: defined as constants). */
7
+ import { ContextOverflowError, ApiError, HookFailureError, BuildError, TimeoutError, SubprocessError, } from "../errors.js";
8
+ /** All recognized error categories (kept for backwards compatibility). */
8
9
  export const ERROR_CATEGORIES = [
9
10
  "context_overflow",
10
11
  "api_error",
@@ -13,6 +14,26 @@ export const ERROR_CATEGORIES = [
13
14
  "timeout",
14
15
  "unknown",
15
16
  ];
17
+ /**
18
+ * Map from error type name to legacy category string.
19
+ * Used for backwards-compatible log storage (AC-8).
20
+ */
21
+ export function errorTypeToCategory(error) {
22
+ switch (error.name) {
23
+ case "ContextOverflowError":
24
+ return "context_overflow";
25
+ case "ApiError":
26
+ return "api_error";
27
+ case "HookFailureError":
28
+ return "hook_failure";
29
+ case "BuildError":
30
+ return "build_error";
31
+ case "TimeoutError":
32
+ return "timeout";
33
+ default:
34
+ return "unknown";
35
+ }
36
+ }
16
37
  /**
17
38
  * Ordered list of classifiers. First match wins (highest priority first).
18
39
  */
@@ -30,6 +51,16 @@ const CLASSIFIERS = [
30
51
  {
31
52
  category: "timeout",
32
53
  patterns: [/timeout/i, /timed?\s*out/i, /SIGTERM/, /deadline exceeded/i],
54
+ extract: (line) => {
55
+ const match = line.match(/(\d+)\s*(?:s|ms|seconds?|milliseconds?)/i);
56
+ if (match) {
57
+ const value = parseInt(match[1], 10);
58
+ // If the unit looks like seconds (or no unit after number), convert to ms
59
+ const isMs = /ms|milliseconds?/i.test(match[0]);
60
+ return { timeoutMs: isMs ? value : value * 1000 };
61
+ }
62
+ return {};
63
+ },
33
64
  },
34
65
  {
35
66
  category: "api_error",
@@ -43,6 +74,10 @@ const CLASSIFIERS = [
43
74
  /\b502\b/,
44
75
  /overloaded/i,
45
76
  ],
77
+ extract: (line) => {
78
+ const statusMatch = line.match(/\b(429|502|503|401|403)\b/);
79
+ return statusMatch ? { statusCode: parseInt(statusMatch[1], 10) } : {};
80
+ },
46
81
  },
47
82
  {
48
83
  category: "hook_failure",
@@ -52,6 +87,10 @@ const CLASSIFIERS = [
52
87
  /HOOK_BLOCKED/i,
53
88
  /blocked by hook/i,
54
89
  ],
90
+ extract: (line) => {
91
+ const hookMatch = line.match(/(?:hook|pre-?commit|HOOK_BLOCKED)[:\s]*(.{0,50})/i);
92
+ return hookMatch ? { hook: hookMatch[1]?.trim() || "unknown" } : {};
93
+ },
55
94
  },
56
95
  {
57
96
  category: "build_error",
@@ -65,26 +104,85 @@ const CLASSIFIERS = [
65
104
  /eslint/i,
66
105
  /npm ERR!/,
67
106
  ],
107
+ extract: (line) => {
108
+ if (/TS\d{4,5}:/.test(line)) {
109
+ const codeMatch = line.match(/(TS\d{4,5}):/);
110
+ return { toolchain: "tsc", errorCode: codeMatch?.[1] };
111
+ }
112
+ if (/eslint/i.test(line))
113
+ return { toolchain: "eslint" };
114
+ if (/npm ERR!/.test(line))
115
+ return { toolchain: "npm" };
116
+ return { toolchain: "unknown" };
117
+ },
68
118
  },
69
119
  ];
70
120
  /**
71
- * Classify stderr lines into an error category.
121
+ * Classify stderr lines into a typed SequantError instance (AC-7).
72
122
  *
73
- * Scans lines in order; the first classifier whose pattern matches any line wins.
74
- * Returns "unknown" if no patterns match.
123
+ * Exit codes are the primary signal; stderr patterns are secondary.
124
+ * Returns a typed error instance with structured metadata.
125
+ *
126
+ * @param stderrLines - Lines from stderr
127
+ * @param exitCode - Process exit code (primary signal)
128
+ * @returns Typed SequantError subclass instance
75
129
  */
76
- export function classifyError(stderrLines) {
77
- if (!stderrLines || stderrLines.length === 0) {
78
- return "unknown";
130
+ export function classifyError(stderrLines, exitCode) {
131
+ const combinedStderr = stderrLines?.join(" ") ?? "";
132
+ // Primary signal: exit code (AC-7)
133
+ if (exitCode !== undefined) {
134
+ // 143 = SIGTERM, often timeout
135
+ if (exitCode === 143 || exitCode === 137) {
136
+ return new TimeoutError(`Process killed with signal (exit code ${exitCode})`, { timeoutMs: undefined, phase: undefined });
137
+ }
79
138
  }
80
- for (const { category, patterns } of CLASSIFIERS) {
81
- for (const line of stderrLines) {
82
- for (const pattern of patterns) {
83
- if (pattern.test(line)) {
84
- return category;
139
+ // Secondary signal: stderr pattern matching
140
+ if (stderrLines && stderrLines.length > 0) {
141
+ for (const { category, patterns, extract } of CLASSIFIERS) {
142
+ for (const line of stderrLines) {
143
+ for (const pattern of patterns) {
144
+ if (pattern.test(line)) {
145
+ const metadata = extract?.(line) ?? {};
146
+ return createErrorForCategory(category, line, metadata, exitCode);
147
+ }
85
148
  }
86
149
  }
87
150
  }
88
151
  }
89
- return "unknown";
152
+ // Fallback: SubprocessError with exit code
153
+ return new SubprocessError(combinedStderr || "Unknown error", {
154
+ exitCode,
155
+ command: undefined,
156
+ stderr: combinedStderr || undefined,
157
+ });
158
+ }
159
+ /**
160
+ * Create a typed error instance from a legacy category string.
161
+ */
162
+ function createErrorForCategory(category, message, metadata, exitCode) {
163
+ switch (category) {
164
+ case "context_overflow":
165
+ return new ContextOverflowError(message, metadata);
166
+ case "api_error":
167
+ return new ApiError(message, {
168
+ statusCode: metadata.statusCode,
169
+ endpoint: metadata.endpoint,
170
+ });
171
+ case "hook_failure":
172
+ return new HookFailureError(message, {
173
+ hook: metadata.hook,
174
+ reason: metadata.reason,
175
+ });
176
+ case "build_error":
177
+ return new BuildError(message, {
178
+ toolchain: metadata.toolchain,
179
+ errorCode: metadata.errorCode,
180
+ });
181
+ case "timeout":
182
+ return new TimeoutError(message, {
183
+ timeoutMs: metadata.timeoutMs,
184
+ });
185
+ default:
186
+ return new SubprocessError(message, { exitCode });
187
+ }
90
188
  }
@@ -11,6 +11,8 @@ import chalk from "chalk";
11
11
  import { execSync } from "child_process";
12
12
  import { readAgentsMd } from "../agents-md.js";
13
13
  import { getDriver } from "./drivers/index.js";
14
+ import { classifyError } from "./error-classifier.js";
15
+ import { ApiError } from "../errors.js";
14
16
  /**
15
17
  * Natural language prompts for each phase.
16
18
  * Claude Code invokes the corresponding skills via natural language.
@@ -490,9 +492,19 @@ delayFn = (ms) => new Promise((resolve) => setTimeout(resolve, ms))) {
490
492
  return lastResult;
491
493
  }
492
494
  // Genuine failure (took long enough to be real work) → skip cold-start retries.
493
- // For spec phase, break to allow Phase 3 (spec-specific retry) to run.
494
- // For other phases, return immediately no further retries.
495
+ // Use error classification (AC-9): if the error is retryable (e.g., API
496
+ // rate limit, transient 503), allow one more attempt even for genuine failures.
495
497
  if (duration >= COLD_START_THRESHOLD_SECONDS) {
498
+ const typedError = classifyError(lastResult.stderrTail ?? [], lastResult.exitCode);
499
+ if (typedError.isRetryable && attempt < COLD_START_MAX_RETRIES) {
500
+ if (config.verbose) {
501
+ const label = typedError instanceof ApiError
502
+ ? `API error (status ${typedError.metadata.statusCode ?? "unknown"})`
503
+ : typedError.name;
504
+ console.log(chalk.yellow(`\n ⟳ Retryable error: ${label}, retrying... (attempt ${attempt + 2}/${COLD_START_MAX_RETRIES + 1})`));
505
+ }
506
+ continue;
507
+ }
496
508
  if (phase === "spec") {
497
509
  break;
498
510
  }
@@ -90,6 +90,9 @@ export declare const ErrorContextSchema: z.ZodObject<{
90
90
  hook_failure: "hook_failure";
91
91
  build_error: "build_error";
92
92
  }>;
93
+ errorType: z.ZodOptional<z.ZodString>;
94
+ errorMetadata: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
95
+ isRetryable: z.ZodOptional<z.ZodBoolean>;
93
96
  }, z.core.$strip>;
94
97
  export type ErrorContext = z.infer<typeof ErrorContextSchema>;
95
98
  /**
@@ -177,6 +180,9 @@ export declare const PhaseLogSchema: z.ZodObject<{
177
180
  hook_failure: "hook_failure";
178
181
  build_error: "build_error";
179
182
  }>;
183
+ errorType: z.ZodOptional<z.ZodString>;
184
+ errorMetadata: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
185
+ isRetryable: z.ZodOptional<z.ZodBoolean>;
180
186
  }, z.core.$strip>>;
181
187
  }, z.core.$strip>;
182
188
  export type PhaseLog = z.infer<typeof PhaseLogSchema>;
@@ -260,6 +266,9 @@ export declare const IssueLogSchema: z.ZodObject<{
260
266
  hook_failure: "hook_failure";
261
267
  build_error: "build_error";
262
268
  }>;
269
+ errorType: z.ZodOptional<z.ZodString>;
270
+ errorMetadata: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
271
+ isRetryable: z.ZodOptional<z.ZodBoolean>;
263
272
  }, z.core.$strip>>;
264
273
  }, z.core.$strip>>;
265
274
  totalDurationSeconds: z.ZodNumber;
@@ -404,6 +413,9 @@ export declare const RunLogSchema: z.ZodObject<{
404
413
  hook_failure: "hook_failure";
405
414
  build_error: "build_error";
406
415
  }>;
416
+ errorType: z.ZodOptional<z.ZodString>;
417
+ errorMetadata: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
418
+ isRetryable: z.ZodOptional<z.ZodBoolean>;
407
419
  }, z.core.$strip>>;
408
420
  }, z.core.$strip>>;
409
421
  totalDurationSeconds: z.ZodNumber;
@@ -79,7 +79,7 @@ export const ErrorContextSchema = z.object({
79
79
  stdoutTail: z.array(z.string()),
80
80
  /** Process exit code */
81
81
  exitCode: z.number().int().optional(),
82
- /** Classified error category */
82
+ /** Classified error category (legacy, kept for backwards compatibility) */
83
83
  category: z.enum([
84
84
  "context_overflow",
85
85
  "api_error",
@@ -88,6 +88,12 @@ export const ErrorContextSchema = z.object({
88
88
  "timeout",
89
89
  "unknown",
90
90
  ]),
91
+ /** Typed error class name (AC-8), e.g. "ApiError", "BuildError" */
92
+ errorType: z.string().optional(),
93
+ /** Structured error metadata (AC-8) */
94
+ errorMetadata: z.record(z.string(), z.unknown()).optional(),
95
+ /** Whether this error type is retryable (AC-9) */
96
+ isRetryable: z.boolean().optional(),
91
97
  });
92
98
  /**
93
99
  * Condensed QA verdict summary for structured log output (#434).
@@ -0,0 +1,124 @@
1
+ /**
2
+ * RunOrchestrator — CLI-free execution engine for sequant workflows.
3
+ *
4
+ * Owns the full lifecycle: config → issue discovery → dispatch → results.
5
+ * Importable and usable without Commander.js or CLI context.
6
+ *
7
+ * @module
8
+ */
9
+ import type { ExecutionConfig, IssueResult, RunOptions, ProgressCallback } from "./types.js";
10
+ import type { WorktreeInfo } from "./worktree-manager.js";
11
+ import { LogWriter } from "./log-writer.js";
12
+ import { StateManager } from "./state-manager.js";
13
+ import { ShutdownManager } from "../shutdown.js";
14
+ import type { SequantSettings } from "../settings.js";
15
+ /**
16
+ * Injectable services for RunOrchestrator.
17
+ * All optional — orchestrator degrades gracefully when services are absent.
18
+ */
19
+ export interface OrchestratorServices {
20
+ logWriter?: LogWriter | null;
21
+ stateManager?: StateManager | null;
22
+ shutdownManager?: ShutdownManager;
23
+ }
24
+ /**
25
+ * CLI-free configuration for RunOrchestrator.
26
+ * No Commander.js types leak into this interface.
27
+ */
28
+ export interface OrchestratorConfig {
29
+ /** Execution settings (phases, timeouts, mode flags) */
30
+ config: ExecutionConfig;
31
+ /** Merged run options (post-resolution, no raw CLI types) */
32
+ options: RunOptions;
33
+ /** Issue metadata keyed by issue number */
34
+ issueInfoMap: Map<number, {
35
+ title: string;
36
+ labels: string[];
37
+ }>;
38
+ /** Worktree paths keyed by issue number */
39
+ worktreeMap: Map<number, WorktreeInfo>;
40
+ /** Injectable services */
41
+ services: OrchestratorServices;
42
+ /** Package manager name (e.g. "npm", "pnpm") */
43
+ packageManager?: string;
44
+ /** Base branch for rebase/PR targets */
45
+ baseBranch?: string;
46
+ /** Per-phase progress callback (parallel mode) */
47
+ onProgress?: ProgressCallback;
48
+ }
49
+ /**
50
+ * High-level init config for full lifecycle execution.
51
+ * Used by RunOrchestrator.run() — the entry point for programmatic callers.
52
+ */
53
+ export interface RunInit {
54
+ /** Raw CLI options (pre-merge) */
55
+ options: RunOptions;
56
+ /** Resolved settings */
57
+ settings: SequantSettings;
58
+ /** Manifest metadata */
59
+ manifest: {
60
+ stack: string;
61
+ packageManager: string;
62
+ };
63
+ /** Explicit base branch override */
64
+ baseBranch?: string;
65
+ /** Per-phase progress callback */
66
+ onProgress?: ProgressCallback;
67
+ }
68
+ /**
69
+ * Structured result of a full orchestrator run.
70
+ */
71
+ export interface RunResult {
72
+ /** Per-issue results */
73
+ results: IssueResult[];
74
+ /** Log file path (if logging enabled) */
75
+ logPath: string | null;
76
+ /** Non-zero if any issue failed */
77
+ exitCode: number;
78
+ /** Worktree map (for summary display) */
79
+ worktreeMap: Map<number, WorktreeInfo>;
80
+ /** Issue info map (for summary display) */
81
+ issueInfoMap: Map<number, {
82
+ title: string;
83
+ labels: string[];
84
+ }>;
85
+ /** Resolved execution config */
86
+ config: ExecutionConfig;
87
+ /** Resolved merged options */
88
+ mergedOptions: RunOptions;
89
+ /** Log writer (for reflection access) */
90
+ logWriter: LogWriter | null;
91
+ }
92
+ /**
93
+ * CLI-free workflow execution engine.
94
+ *
95
+ * Two usage modes:
96
+ * 1. Full lifecycle: `RunOrchestrator.run(init, issueNumbers)` — handles
97
+ * services, worktrees, state guard, execution, and metrics.
98
+ * 2. Low-level: `new RunOrchestrator(config).execute(issueNumbers)` — caller
99
+ * manages setup/teardown.
100
+ */
101
+ export declare class RunOrchestrator {
102
+ private readonly cfg;
103
+ constructor(config: OrchestratorConfig);
104
+ /**
105
+ * Full lifecycle execution — the primary entry point for programmatic use.
106
+ *
107
+ * Handles: config resolution → services setup → state guard →
108
+ * issue discovery → worktree creation → execution → metrics → cleanup.
109
+ */
110
+ static run(init: RunInit, issueArgs: string[], batches?: number[][] | null): Promise<RunResult>;
111
+ /**
112
+ * Execute workflow for the given issue numbers.
113
+ * Returns one IssueResult per issue.
114
+ */
115
+ execute(issueNumbers: number[]): Promise<IssueResult[]>;
116
+ private validate;
117
+ private buildBatchContext;
118
+ private executeSequential;
119
+ private executeParallel;
120
+ private executeOneIssue;
121
+ private static recordMetrics;
122
+ }
123
+ /** Log a non-fatal warning: one-line summary always, detail in verbose. */
124
+ export declare function logNonFatalWarning(message: string, error: unknown, verbose: boolean): void;