@harness-engineering/orchestrator 0.3.2 → 0.4.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/dist/index.d.mts CHANGED
@@ -1,4 +1,5 @@
1
- import { Issue, AgentEvent, WorkflowConfig, IssueTrackerClient, TokenUsage, ConcernSignal, ScopeTier, EscalationConfig, RoutingDecision, Result, WorkflowDefinition, TrackerConfig, WorkspaceConfig, HooksConfig, AgentBackend, SessionStartParams, AgentSession, AgentError, TurnParams, TurnResult, BackendDef, RoutingConfig, RoutingUseCase, ContainerConfig, SecretConfig, AgentConfig } from '@harness-engineering/types';
1
+ import { Issue, AgentEvent, WorkflowConfig, TokenUsage, ConcernSignal, ScopeTier, EscalationConfig, RoutingDecision, Result, WorkflowDefinition, WorkspaceConfig, HooksConfig, AgentBackend, SessionStartParams, AgentSession, AgentError, TurnParams, TurnResult, BackendDef, RoutingConfig, RoutingUseCase, ContainerConfig, SecretConfig, AgentConfig } from '@harness-engineering/types';
2
+ import { IssueTrackerClient, Issue as Issue$1, TrackerConfig } from '@harness-engineering/core';
2
3
  import { EnrichedSpec, ComplexityScore, SimulationResult, IntelligencePipeline, WeightedRecommendation } from '@harness-engineering/intelligence';
3
4
  import { GraphStore } from '@harness-engineering/graph';
4
5
  import { execFile } from 'node:child_process';
@@ -243,7 +244,7 @@ interface PRDetectorLogger {
243
244
  * Function signature compatible with Node's `child_process.execFile`.
244
245
  * Allows injection for testing.
245
246
  */
246
- type ExecFileFn = typeof execFile;
247
+ type ExecFileFn$1 = typeof execFile;
247
248
  /**
248
249
  * Detects whether GitHub issues or branches already have open pull requests.
249
250
  *
@@ -257,7 +258,7 @@ declare class PRDetector {
257
258
  private projectRoot;
258
259
  constructor(opts: {
259
260
  logger: PRDetectorLogger;
260
- execFileFn?: ExecFileFn;
261
+ execFileFn?: ExecFileFn$1;
261
262
  projectRoot: string;
262
263
  });
263
264
  /**
@@ -830,7 +831,7 @@ declare class ClaimManager {
830
831
  * Returns true if the claim should be considered expired (the
831
832
  * owning orchestrator may have crashed).
832
833
  */
833
- isStale(issue: Issue, ttlMs: number): boolean;
834
+ isStale(issue: Issue$1, ttlMs: number): boolean;
834
835
  /**
835
836
  * Scans the tracker for "in-progress" issues assigned to this orchestrator
836
837
  * and releases any that are not currently running in memory.
@@ -878,13 +879,13 @@ declare class RoadmapTrackerAdapter implements IssueTrackerClient {
878
879
  /**
879
880
  * Fetches all issues that are in an "active" state according to the config.
880
881
  */
881
- fetchCandidateIssues(): Promise<Result<Issue[], Error>>;
882
+ fetchCandidateIssues(): Promise<Result<Issue$1[], Error>>;
882
883
  /**
883
884
  * Fetches issues that match any of the given state names.
884
885
  *
885
886
  * @param stateNames - List of statuses to filter by
886
887
  */
887
- fetchIssuesByStates(stateNames: string[]): Promise<Result<Issue[], Error>>;
888
+ fetchIssuesByStates(stateNames: string[]): Promise<Result<Issue$1[], Error>>;
888
889
  /**
889
890
  * Transitions a roadmap feature into the first configured terminal state
890
891
  * and rewrites the markdown file. Idempotent: if the feature is already
@@ -912,7 +913,7 @@ declare class RoadmapTrackerAdapter implements IssueTrackerClient {
912
913
  *
913
914
  * @param issueIds - List of issue IDs to fetch
914
915
  */
915
- fetchIssueStatesByIds(issueIds: string[]): Promise<Result<Map<string, Issue>, Error>>;
916
+ fetchIssueStatesByIds(issueIds: string[]): Promise<Result<Map<string, Issue$1>, Error>>;
916
917
  /**
917
918
  * Maps a raw RoadmapFeature from the parser to the unified Issue model.
918
919
  */
@@ -934,11 +935,35 @@ declare class LinearGraphQLStub implements LinearGraphQLExtension {
934
935
  query(query: string, _variables?: Record<string, unknown>): Promise<Result<unknown, Error>>;
935
936
  }
936
937
 
938
+ /**
939
+ * Structured event emitted when {@link WorkspaceManager.resolveBaseRef}
940
+ * falls back past `origin/HEAD` and `origin/main`/`origin/master` to a
941
+ * local-only ref. Operators see this in the dashboard's maintenance
942
+ * event stream when the remote is misconfigured or unreachable.
943
+ */
944
+ interface BaseRefFallbackEvent {
945
+ kind: 'baseref_fallback';
946
+ /** The ref that was selected — `'main'`, `'master'`, or `'HEAD'`. */
947
+ ref: string;
948
+ /** Absolute path to the git repository root. */
949
+ repoRoot: string;
950
+ }
951
+ /** Optional dependencies injected into {@link WorkspaceManager}. */
952
+ interface WorkspaceManagerOptions {
953
+ /**
954
+ * Synchronous fire-and-forget callback invoked when {@link
955
+ * WorkspaceManager.resolveBaseRef} falls back to a local-only ref.
956
+ * When omitted, fallback emission is silently skipped.
957
+ */
958
+ emitEvent?: (event: BaseRefFallbackEvent) => void;
959
+ }
937
960
  declare class WorkspaceManager {
938
961
  private config;
939
962
  /** Absolute path to the git repository root (resolved lazily). */
940
963
  private repoRoot;
941
- constructor(config: WorkspaceConfig);
964
+ /** Phase 3 (D6): emit baseref_fallback when fallback chain selects a local-only ref. */
965
+ private emitEvent;
966
+ constructor(config: WorkspaceConfig, options?: WorkspaceManagerOptions);
942
967
  /** Runs a git command and returns stdout. Extracted for testability. */
943
968
  protected git(args: string[], cwd: string): Promise<string>;
944
969
  /**
@@ -970,11 +995,22 @@ declare class WorkspaceManager {
970
995
  * Priority order:
971
996
  * 1. `config.baseRef` (explicit override). Throws if it doesn't resolve.
972
997
  * 2. Default branch via `git symbolic-ref --short refs/remotes/origin/HEAD`.
973
- * 3. Common fallbacks: `origin/main`, `origin/master`, `main`, `master`.
974
- * 4. `HEAD` as an ultimate fallback (preserves old behavior for unusual
975
- * repos without any of the above).
998
+ * 3. Remote fallbacks: `origin/main`, `origin/master`. (No event.)
999
+ * 4. Local-only fallbacks: `main`, `master`. (Emits `baseref_fallback`.)
1000
+ * 5. `HEAD` as ultimate fallback. (Emits `baseref_fallback`.)
1001
+ *
1002
+ * Phase 3 / spec D6 / R4: when the priority chain falls past `origin/*`
1003
+ * to a local-only ref, the optional `emitEvent` callback (if injected)
1004
+ * is invoked exactly once with `{ kind: 'baseref_fallback', ref, repoRoot }`
1005
+ * so operators are warned when the remote is misconfigured or unreachable.
976
1006
  */
977
1007
  private resolveBaseRef;
1008
+ /**
1009
+ * Phase 3 (D6): emit a `baseref_fallback` event via the injected
1010
+ * callback (if any). Errors from the callback are swallowed so a
1011
+ * broken emitter does not block worktree dispatch.
1012
+ */
1013
+ private emitFallback;
978
1014
  /** Returns true iff `git rev-parse --verify` accepts the ref. */
979
1015
  private refExists;
980
1016
  /**
@@ -1040,6 +1076,19 @@ declare class PromptRenderer {
1040
1076
  render(template: string, context: Record<string, unknown>): Promise<string>;
1041
1077
  }
1042
1078
 
1079
+ /**
1080
+ * Internal types for the maintenance module.
1081
+ * Public config types (MaintenanceConfig, TaskOverride) live in @harness-engineering/types.
1082
+ */
1083
+ /**
1084
+ * Classification of maintenance task execution strategy.
1085
+ *
1086
+ * - mechanical-ai: Run a check command first; dispatch AI agent only if fixable issues are found.
1087
+ * - pure-ai: Always dispatch an AI agent on schedule regardless of preconditions.
1088
+ * - report-only: Run a command and record metrics; never create branches or PRs.
1089
+ * - housekeeping: Run a mechanical command directly; no AI, no PR.
1090
+ */
1091
+ type TaskType = 'mechanical-ai' | 'pure-ai' | 'report-only' | 'housekeeping';
1043
1092
  /**
1044
1093
  * Result of a single maintenance task run.
1045
1094
  */
@@ -1069,6 +1118,16 @@ interface RunResult {
1069
1118
  interface ScheduleEntry {
1070
1119
  /** Task identifier */
1071
1120
  taskId: string;
1121
+ /**
1122
+ * Task type (mechanical-ai | pure-ai | report-only | housekeeping).
1123
+ *
1124
+ * Optional on the WIRE shape so that a newer dashboard rendering data from
1125
+ * an older orchestrator (which did not yet emit this field) does not
1126
+ * produce empty cells of unknown semantics. The current orchestrator
1127
+ * always populates this field via `MaintenanceScheduler.getStatus()`.
1128
+ * Newer dashboards must render `row.type ?? '—'` (or similar fallback).
1129
+ */
1130
+ type?: TaskType;
1072
1131
  /** ISO timestamp of the next scheduled run */
1073
1132
  nextRun: string;
1074
1133
  /** Result of the most recent run, or null if never run */
@@ -1184,7 +1243,7 @@ declare class Orchestrator extends EventEmitter {
1184
1243
  constructor(config: WorkflowConfig, promptTemplate: string, overrides?: {
1185
1244
  tracker?: IssueTrackerClient;
1186
1245
  backend?: AgentBackend;
1187
- execFileFn?: ExecFileFn;
1246
+ execFileFn?: ExecFileFn$1;
1188
1247
  });
1189
1248
  private createTracker;
1190
1249
  /**
@@ -1199,35 +1258,6 @@ declare class Orchestrator extends EventEmitter {
1199
1258
  */
1200
1259
  private initMaintenance;
1201
1260
  private createIntelligencePipeline;
1202
- /**
1203
- * Create the AnalysisProvider for an intelligence-pipeline layer
1204
- * (`sel` by default; `pesl` when constructing a distinct PESL
1205
- * provider per Spec 2 SC35).
1206
- *
1207
- * Spec 2 Phase 4 (SC31–SC36) — resolution order:
1208
- * 1. Explicit `intelligence.provider` config wins (preserves Phase 0–3 behavior; SC33).
1209
- * 2. Otherwise, consult `agent.routing.intelligence.<layer>` (or
1210
- * `routing.default`) to pick a `BackendDef` from `agent.backends`,
1211
- * then translate via `buildAnalysisProvider` (the per-type factory).
1212
- *
1213
- * Closes the Phase 2 deferral (P2-DEF-638): the legacy
1214
- * `this.config.agent.backend` read at the bottom of this method is
1215
- * removed; routing is the sole source for non-explicit configs.
1216
- *
1217
- * Cyclomatic complexity: pre-Phase-4 was 33 (factory dispatch was
1218
- * inlined here). Phase 4 extracts the per-type tree into
1219
- * `buildAnalysisProvider`, dropping this method to ≤ 5 branches
1220
- * (under the 15 threshold).
1221
- */
1222
- private createAnalysisProvider;
1223
- /**
1224
- * Look up the routed BackendDef for an intelligence layer, falling
1225
- * back through `routing.intelligence.<layer>` → `routing.default`
1226
- * → null. Returns the resolved name alongside the def so callers can
1227
- * key into the per-name resolver map.
1228
- */
1229
- private resolveRoutedBackendForIntelligence;
1230
- private createProviderFromExplicitConfig;
1231
1261
  /**
1232
1262
  * Lazily initializes the ClaimManager if it hasn't been created yet.
1233
1263
  * Called from both start() and asyncTick() to avoid duplicating the init block.
@@ -1309,7 +1339,7 @@ declare class Orchestrator extends EventEmitter {
1309
1339
  * Dispatch a work item immediately, bypassing the normal tick → roadmap cycle.
1310
1340
  * Used by the dashboard's "Dispatch Now" action.
1311
1341
  */
1312
- dispatchAdHoc(issue: Issue): Promise<void>;
1342
+ dispatchAdHoc(issue: Issue$1): Promise<void>;
1313
1343
  /**
1314
1344
  * Initialize the LocalModelResolver and intelligence pipeline.
1315
1345
  *
@@ -1481,29 +1511,6 @@ interface MigrationResult {
1481
1511
  config: AgentConfig;
1482
1512
  warnings: string[];
1483
1513
  }
1484
- /**
1485
- * Translate legacy `agent.backend` / `agent.localBackend` /
1486
- * `agent.escalation.autoExecute` into the new `agent.backends` +
1487
- * `agent.routing` shape (Spec 2 D3, D4, D8, D11).
1488
- *
1489
- * Behavior matrix:
1490
- * - `agent.backends` already set, no legacy fields:
1491
- * no-op; returns input config unchanged, warnings = [].
1492
- * - `agent.backends` already set + at least one legacy field:
1493
- * no-op on config; warnings name each ignored legacy field (D4).
1494
- * - No `agent.backends`, at least one of `agent.backend` /
1495
- * `agent.localBackend` / `agent.localEndpoint` / etc. set:
1496
- * synthesize `backends.primary` (always, from `agent.backend`)
1497
- * and `backends.local` (when `localBackend` is set), plus a
1498
- * `routing` map driven by `escalation.autoExecute` (D3).
1499
- * - No `agent.backends`, no legacy fields:
1500
- * no-op; the caller's downstream Zod validation surfaces the gap
1501
- * as a missing-required-field error.
1502
- *
1503
- * Throws on internal inconsistencies (e.g., `agent.backend = 'pi'`
1504
- * with no `localEndpoint`/`localModel`) — these are user-config bugs
1505
- * that would have produced a runtime crash today.
1506
- */
1507
1514
  declare function migrateAgentConfig(agent: AgentConfig): MigrationResult;
1508
1515
 
1509
1516
  /**
@@ -1513,4 +1520,45 @@ declare function migrateAgentConfig(agent: AgentConfig): MigrationResult;
1513
1520
  */
1514
1521
  declare function createBackend(def: BackendDef): AgentBackend;
1515
1522
 
1516
- export { type AgentUpdateEvent, AnalysisArchive, type AnalysisRecord, type ApplyEventResult, type ArtifactPresence, type AttemptStats, BackendRouter, type BackendRouterOptions, ClaimManager, type ClaimManagerConfig, type CleanWorkspaceEffect, type DispatchEffect, type EmitLogEffect, type EscalateEffect, type ExecFileFn, type Highlight, type HighlightsInfo, InteractionQueue, type LinearGraphQLExtension, LinearGraphQLStub, type LiveSession, type MigrationResult, MockBackend, ORCHESTRATOR_IDENTITY_FILE, Orchestrator, OrchestratorBackendFactory, type OrchestratorBackendFactoryOptions, type OrchestratorContext, type OrchestratorEvent, type OrchestratorState, PRDetector, type PRDetectorLogger, type PendingInteraction, PromptRenderer, type PublishedIndex, type RateLimitSnapshot as RateLimitComputeSnapshot, type RateLimitConfig, type RateLimitSnapshot$1 as RateLimitSnapshot, type ReleaseClaimEffect, type RetryEntry, type RetryFiredEvent, RoadmapTrackerAdapter, type RunAttemptPhase, type RunningEntry, type ScheduleRetryEffect, type SideEffect, type StallDetectedEvent, type StopEffect, type StreamManifest, StreamRecorder, type TickEvent, type TokenTotals, type TriageConfig, type TriageDecision, type TriageSignals, type TriageSkill, type UpdateTokensEffect, type WorkerExitEvent, WorkflowLoader, WorkspaceHooks, WorkspaceManager, applyEvent, artifactPresenceFromIssue, calculateRetryDelay, canDispatch, computeRateLimitDelay, createBackend, createEmptyState, detectScopeTier, extractHighlights, extractTitlePrefix, getAvailableSlots, getDefaultConfig, getPerStateCount, isEligible, launchTUI, loadPublishedIndex, migrateAgentConfig, reconcile, renderAnalysisComment, renderPRComment, resolveEscalationConfig, resolveOrchestratorId, routeIssue, savePublishedIndex, selectCandidates, sortCandidates, triageIssue, validateWorkflowConfig };
1523
+ /**
1524
+ * Function signature compatible with Node's `child_process.execFile`.
1525
+ * Allows injection for testing.
1526
+ */
1527
+ type ExecFileFn = typeof execFile;
1528
+ /** Reasons `syncMain()` declines to fast-forward (none of which are failures). */
1529
+ type SyncSkipReason = 'wrong-branch' | 'diverged' | 'dirty-conflict' | 'no-remote' | 'fetch-failed';
1530
+ /** Total result type for `syncMain()`. Discriminated by `status`. */
1531
+ type SyncMainResult = {
1532
+ status: 'updated';
1533
+ from: string;
1534
+ to: string;
1535
+ defaultBranch: string;
1536
+ } | {
1537
+ status: 'no-op';
1538
+ defaultBranch: string;
1539
+ } | {
1540
+ status: 'skipped';
1541
+ reason: SyncSkipReason;
1542
+ detail: string;
1543
+ defaultBranch: string;
1544
+ } | {
1545
+ status: 'error';
1546
+ message: string;
1547
+ };
1548
+ /** Options accepted by `syncMain()`. */
1549
+ interface SyncMainOptions {
1550
+ /** Override the `git` runner; defaults to `node:child_process.execFile`. */
1551
+ execFileFn?: ExecFileFn;
1552
+ /** Per-git-call timeout in ms. Defaults to 60_000ms. */
1553
+ timeoutMs?: number;
1554
+ }
1555
+ /**
1556
+ * Fast-forward the local default branch from `origin/<default>` if it can be
1557
+ * done safely. This function is total: it never throws under expected
1558
+ * conditions. Even unexpected errors are caught and converted to
1559
+ * `{ status: 'error', message }` so the maintenance scheduler does not back
1560
+ * off on transient git issues.
1561
+ */
1562
+ declare function syncMain(repoRoot: string, opts?: SyncMainOptions): Promise<SyncMainResult>;
1563
+
1564
+ export { type AgentUpdateEvent, AnalysisArchive, type AnalysisRecord, type ApplyEventResult, type ArtifactPresence, type AttemptStats, BackendRouter, type BackendRouterOptions, type BaseRefFallbackEvent, ClaimManager, type ClaimManagerConfig, type CleanWorkspaceEffect, type DispatchEffect, type EmitLogEffect, type EscalateEffect, type ExecFileFn$1 as ExecFileFn, type Highlight, type HighlightsInfo, InteractionQueue, type LinearGraphQLExtension, LinearGraphQLStub, type LiveSession, type MigrationResult, MockBackend, ORCHESTRATOR_IDENTITY_FILE, Orchestrator, OrchestratorBackendFactory, type OrchestratorBackendFactoryOptions, type OrchestratorContext, type OrchestratorEvent, type OrchestratorState, PRDetector, type PRDetectorLogger, type PendingInteraction, PromptRenderer, type PublishedIndex, type RateLimitSnapshot as RateLimitComputeSnapshot, type RateLimitConfig, type RateLimitSnapshot$1 as RateLimitSnapshot, type ReleaseClaimEffect, type RetryEntry, type RetryFiredEvent, RoadmapTrackerAdapter, type RunAttemptPhase, type RunningEntry, type ScheduleRetryEffect, type SideEffect, type StallDetectedEvent, type StopEffect, type StreamManifest, StreamRecorder, type SyncMainOptions, type SyncMainResult, type SyncSkipReason, type TickEvent, type TokenTotals, type TriageConfig, type TriageDecision, type TriageSignals, type TriageSkill, type UpdateTokensEffect, type WorkerExitEvent, WorkflowLoader, WorkspaceHooks, WorkspaceManager, type WorkspaceManagerOptions, applyEvent, artifactPresenceFromIssue, calculateRetryDelay, canDispatch, computeRateLimitDelay, createBackend, createEmptyState, detectScopeTier, extractHighlights, extractTitlePrefix, getAvailableSlots, getDefaultConfig, getPerStateCount, isEligible, launchTUI, loadPublishedIndex, migrateAgentConfig, reconcile, renderAnalysisComment, renderPRComment, resolveEscalationConfig, resolveOrchestratorId, routeIssue, savePublishedIndex, selectCandidates, sortCandidates, syncMain, triageIssue, validateWorkflowConfig };
package/dist/index.d.ts CHANGED
@@ -1,4 +1,5 @@
1
- import { Issue, AgentEvent, WorkflowConfig, IssueTrackerClient, TokenUsage, ConcernSignal, ScopeTier, EscalationConfig, RoutingDecision, Result, WorkflowDefinition, TrackerConfig, WorkspaceConfig, HooksConfig, AgentBackend, SessionStartParams, AgentSession, AgentError, TurnParams, TurnResult, BackendDef, RoutingConfig, RoutingUseCase, ContainerConfig, SecretConfig, AgentConfig } from '@harness-engineering/types';
1
+ import { Issue, AgentEvent, WorkflowConfig, TokenUsage, ConcernSignal, ScopeTier, EscalationConfig, RoutingDecision, Result, WorkflowDefinition, WorkspaceConfig, HooksConfig, AgentBackend, SessionStartParams, AgentSession, AgentError, TurnParams, TurnResult, BackendDef, RoutingConfig, RoutingUseCase, ContainerConfig, SecretConfig, AgentConfig } from '@harness-engineering/types';
2
+ import { IssueTrackerClient, Issue as Issue$1, TrackerConfig } from '@harness-engineering/core';
2
3
  import { EnrichedSpec, ComplexityScore, SimulationResult, IntelligencePipeline, WeightedRecommendation } from '@harness-engineering/intelligence';
3
4
  import { GraphStore } from '@harness-engineering/graph';
4
5
  import { execFile } from 'node:child_process';
@@ -243,7 +244,7 @@ interface PRDetectorLogger {
243
244
  * Function signature compatible with Node's `child_process.execFile`.
244
245
  * Allows injection for testing.
245
246
  */
246
- type ExecFileFn = typeof execFile;
247
+ type ExecFileFn$1 = typeof execFile;
247
248
  /**
248
249
  * Detects whether GitHub issues or branches already have open pull requests.
249
250
  *
@@ -257,7 +258,7 @@ declare class PRDetector {
257
258
  private projectRoot;
258
259
  constructor(opts: {
259
260
  logger: PRDetectorLogger;
260
- execFileFn?: ExecFileFn;
261
+ execFileFn?: ExecFileFn$1;
261
262
  projectRoot: string;
262
263
  });
263
264
  /**
@@ -830,7 +831,7 @@ declare class ClaimManager {
830
831
  * Returns true if the claim should be considered expired (the
831
832
  * owning orchestrator may have crashed).
832
833
  */
833
- isStale(issue: Issue, ttlMs: number): boolean;
834
+ isStale(issue: Issue$1, ttlMs: number): boolean;
834
835
  /**
835
836
  * Scans the tracker for "in-progress" issues assigned to this orchestrator
836
837
  * and releases any that are not currently running in memory.
@@ -878,13 +879,13 @@ declare class RoadmapTrackerAdapter implements IssueTrackerClient {
878
879
  /**
879
880
  * Fetches all issues that are in an "active" state according to the config.
880
881
  */
881
- fetchCandidateIssues(): Promise<Result<Issue[], Error>>;
882
+ fetchCandidateIssues(): Promise<Result<Issue$1[], Error>>;
882
883
  /**
883
884
  * Fetches issues that match any of the given state names.
884
885
  *
885
886
  * @param stateNames - List of statuses to filter by
886
887
  */
887
- fetchIssuesByStates(stateNames: string[]): Promise<Result<Issue[], Error>>;
888
+ fetchIssuesByStates(stateNames: string[]): Promise<Result<Issue$1[], Error>>;
888
889
  /**
889
890
  * Transitions a roadmap feature into the first configured terminal state
890
891
  * and rewrites the markdown file. Idempotent: if the feature is already
@@ -912,7 +913,7 @@ declare class RoadmapTrackerAdapter implements IssueTrackerClient {
912
913
  *
913
914
  * @param issueIds - List of issue IDs to fetch
914
915
  */
915
- fetchIssueStatesByIds(issueIds: string[]): Promise<Result<Map<string, Issue>, Error>>;
916
+ fetchIssueStatesByIds(issueIds: string[]): Promise<Result<Map<string, Issue$1>, Error>>;
916
917
  /**
917
918
  * Maps a raw RoadmapFeature from the parser to the unified Issue model.
918
919
  */
@@ -934,11 +935,35 @@ declare class LinearGraphQLStub implements LinearGraphQLExtension {
934
935
  query(query: string, _variables?: Record<string, unknown>): Promise<Result<unknown, Error>>;
935
936
  }
936
937
 
938
+ /**
939
+ * Structured event emitted when {@link WorkspaceManager.resolveBaseRef}
940
+ * falls back past `origin/HEAD` and `origin/main`/`origin/master` to a
941
+ * local-only ref. Operators see this in the dashboard's maintenance
942
+ * event stream when the remote is misconfigured or unreachable.
943
+ */
944
+ interface BaseRefFallbackEvent {
945
+ kind: 'baseref_fallback';
946
+ /** The ref that was selected — `'main'`, `'master'`, or `'HEAD'`. */
947
+ ref: string;
948
+ /** Absolute path to the git repository root. */
949
+ repoRoot: string;
950
+ }
951
+ /** Optional dependencies injected into {@link WorkspaceManager}. */
952
+ interface WorkspaceManagerOptions {
953
+ /**
954
+ * Synchronous fire-and-forget callback invoked when {@link
955
+ * WorkspaceManager.resolveBaseRef} falls back to a local-only ref.
956
+ * When omitted, fallback emission is silently skipped.
957
+ */
958
+ emitEvent?: (event: BaseRefFallbackEvent) => void;
959
+ }
937
960
  declare class WorkspaceManager {
938
961
  private config;
939
962
  /** Absolute path to the git repository root (resolved lazily). */
940
963
  private repoRoot;
941
- constructor(config: WorkspaceConfig);
964
+ /** Phase 3 (D6): emit baseref_fallback when fallback chain selects a local-only ref. */
965
+ private emitEvent;
966
+ constructor(config: WorkspaceConfig, options?: WorkspaceManagerOptions);
942
967
  /** Runs a git command and returns stdout. Extracted for testability. */
943
968
  protected git(args: string[], cwd: string): Promise<string>;
944
969
  /**
@@ -970,11 +995,22 @@ declare class WorkspaceManager {
970
995
  * Priority order:
971
996
  * 1. `config.baseRef` (explicit override). Throws if it doesn't resolve.
972
997
  * 2. Default branch via `git symbolic-ref --short refs/remotes/origin/HEAD`.
973
- * 3. Common fallbacks: `origin/main`, `origin/master`, `main`, `master`.
974
- * 4. `HEAD` as an ultimate fallback (preserves old behavior for unusual
975
- * repos without any of the above).
998
+ * 3. Remote fallbacks: `origin/main`, `origin/master`. (No event.)
999
+ * 4. Local-only fallbacks: `main`, `master`. (Emits `baseref_fallback`.)
1000
+ * 5. `HEAD` as ultimate fallback. (Emits `baseref_fallback`.)
1001
+ *
1002
+ * Phase 3 / spec D6 / R4: when the priority chain falls past `origin/*`
1003
+ * to a local-only ref, the optional `emitEvent` callback (if injected)
1004
+ * is invoked exactly once with `{ kind: 'baseref_fallback', ref, repoRoot }`
1005
+ * so operators are warned when the remote is misconfigured or unreachable.
976
1006
  */
977
1007
  private resolveBaseRef;
1008
+ /**
1009
+ * Phase 3 (D6): emit a `baseref_fallback` event via the injected
1010
+ * callback (if any). Errors from the callback are swallowed so a
1011
+ * broken emitter does not block worktree dispatch.
1012
+ */
1013
+ private emitFallback;
978
1014
  /** Returns true iff `git rev-parse --verify` accepts the ref. */
979
1015
  private refExists;
980
1016
  /**
@@ -1040,6 +1076,19 @@ declare class PromptRenderer {
1040
1076
  render(template: string, context: Record<string, unknown>): Promise<string>;
1041
1077
  }
1042
1078
 
1079
+ /**
1080
+ * Internal types for the maintenance module.
1081
+ * Public config types (MaintenanceConfig, TaskOverride) live in @harness-engineering/types.
1082
+ */
1083
+ /**
1084
+ * Classification of maintenance task execution strategy.
1085
+ *
1086
+ * - mechanical-ai: Run a check command first; dispatch AI agent only if fixable issues are found.
1087
+ * - pure-ai: Always dispatch an AI agent on schedule regardless of preconditions.
1088
+ * - report-only: Run a command and record metrics; never create branches or PRs.
1089
+ * - housekeeping: Run a mechanical command directly; no AI, no PR.
1090
+ */
1091
+ type TaskType = 'mechanical-ai' | 'pure-ai' | 'report-only' | 'housekeeping';
1043
1092
  /**
1044
1093
  * Result of a single maintenance task run.
1045
1094
  */
@@ -1069,6 +1118,16 @@ interface RunResult {
1069
1118
  interface ScheduleEntry {
1070
1119
  /** Task identifier */
1071
1120
  taskId: string;
1121
+ /**
1122
+ * Task type (mechanical-ai | pure-ai | report-only | housekeeping).
1123
+ *
1124
+ * Optional on the WIRE shape so that a newer dashboard rendering data from
1125
+ * an older orchestrator (which did not yet emit this field) does not
1126
+ * produce empty cells of unknown semantics. The current orchestrator
1127
+ * always populates this field via `MaintenanceScheduler.getStatus()`.
1128
+ * Newer dashboards must render `row.type ?? '—'` (or similar fallback).
1129
+ */
1130
+ type?: TaskType;
1072
1131
  /** ISO timestamp of the next scheduled run */
1073
1132
  nextRun: string;
1074
1133
  /** Result of the most recent run, or null if never run */
@@ -1184,7 +1243,7 @@ declare class Orchestrator extends EventEmitter {
1184
1243
  constructor(config: WorkflowConfig, promptTemplate: string, overrides?: {
1185
1244
  tracker?: IssueTrackerClient;
1186
1245
  backend?: AgentBackend;
1187
- execFileFn?: ExecFileFn;
1246
+ execFileFn?: ExecFileFn$1;
1188
1247
  });
1189
1248
  private createTracker;
1190
1249
  /**
@@ -1199,35 +1258,6 @@ declare class Orchestrator extends EventEmitter {
1199
1258
  */
1200
1259
  private initMaintenance;
1201
1260
  private createIntelligencePipeline;
1202
- /**
1203
- * Create the AnalysisProvider for an intelligence-pipeline layer
1204
- * (`sel` by default; `pesl` when constructing a distinct PESL
1205
- * provider per Spec 2 SC35).
1206
- *
1207
- * Spec 2 Phase 4 (SC31–SC36) — resolution order:
1208
- * 1. Explicit `intelligence.provider` config wins (preserves Phase 0–3 behavior; SC33).
1209
- * 2. Otherwise, consult `agent.routing.intelligence.<layer>` (or
1210
- * `routing.default`) to pick a `BackendDef` from `agent.backends`,
1211
- * then translate via `buildAnalysisProvider` (the per-type factory).
1212
- *
1213
- * Closes the Phase 2 deferral (P2-DEF-638): the legacy
1214
- * `this.config.agent.backend` read at the bottom of this method is
1215
- * removed; routing is the sole source for non-explicit configs.
1216
- *
1217
- * Cyclomatic complexity: pre-Phase-4 was 33 (factory dispatch was
1218
- * inlined here). Phase 4 extracts the per-type tree into
1219
- * `buildAnalysisProvider`, dropping this method to ≤ 5 branches
1220
- * (under the 15 threshold).
1221
- */
1222
- private createAnalysisProvider;
1223
- /**
1224
- * Look up the routed BackendDef for an intelligence layer, falling
1225
- * back through `routing.intelligence.<layer>` → `routing.default`
1226
- * → null. Returns the resolved name alongside the def so callers can
1227
- * key into the per-name resolver map.
1228
- */
1229
- private resolveRoutedBackendForIntelligence;
1230
- private createProviderFromExplicitConfig;
1231
1261
  /**
1232
1262
  * Lazily initializes the ClaimManager if it hasn't been created yet.
1233
1263
  * Called from both start() and asyncTick() to avoid duplicating the init block.
@@ -1309,7 +1339,7 @@ declare class Orchestrator extends EventEmitter {
1309
1339
  * Dispatch a work item immediately, bypassing the normal tick → roadmap cycle.
1310
1340
  * Used by the dashboard's "Dispatch Now" action.
1311
1341
  */
1312
- dispatchAdHoc(issue: Issue): Promise<void>;
1342
+ dispatchAdHoc(issue: Issue$1): Promise<void>;
1313
1343
  /**
1314
1344
  * Initialize the LocalModelResolver and intelligence pipeline.
1315
1345
  *
@@ -1481,29 +1511,6 @@ interface MigrationResult {
1481
1511
  config: AgentConfig;
1482
1512
  warnings: string[];
1483
1513
  }
1484
- /**
1485
- * Translate legacy `agent.backend` / `agent.localBackend` /
1486
- * `agent.escalation.autoExecute` into the new `agent.backends` +
1487
- * `agent.routing` shape (Spec 2 D3, D4, D8, D11).
1488
- *
1489
- * Behavior matrix:
1490
- * - `agent.backends` already set, no legacy fields:
1491
- * no-op; returns input config unchanged, warnings = [].
1492
- * - `agent.backends` already set + at least one legacy field:
1493
- * no-op on config; warnings name each ignored legacy field (D4).
1494
- * - No `agent.backends`, at least one of `agent.backend` /
1495
- * `agent.localBackend` / `agent.localEndpoint` / etc. set:
1496
- * synthesize `backends.primary` (always, from `agent.backend`)
1497
- * and `backends.local` (when `localBackend` is set), plus a
1498
- * `routing` map driven by `escalation.autoExecute` (D3).
1499
- * - No `agent.backends`, no legacy fields:
1500
- * no-op; the caller's downstream Zod validation surfaces the gap
1501
- * as a missing-required-field error.
1502
- *
1503
- * Throws on internal inconsistencies (e.g., `agent.backend = 'pi'`
1504
- * with no `localEndpoint`/`localModel`) — these are user-config bugs
1505
- * that would have produced a runtime crash today.
1506
- */
1507
1514
  declare function migrateAgentConfig(agent: AgentConfig): MigrationResult;
1508
1515
 
1509
1516
  /**
@@ -1513,4 +1520,45 @@ declare function migrateAgentConfig(agent: AgentConfig): MigrationResult;
1513
1520
  */
1514
1521
  declare function createBackend(def: BackendDef): AgentBackend;
1515
1522
 
1516
- export { type AgentUpdateEvent, AnalysisArchive, type AnalysisRecord, type ApplyEventResult, type ArtifactPresence, type AttemptStats, BackendRouter, type BackendRouterOptions, ClaimManager, type ClaimManagerConfig, type CleanWorkspaceEffect, type DispatchEffect, type EmitLogEffect, type EscalateEffect, type ExecFileFn, type Highlight, type HighlightsInfo, InteractionQueue, type LinearGraphQLExtension, LinearGraphQLStub, type LiveSession, type MigrationResult, MockBackend, ORCHESTRATOR_IDENTITY_FILE, Orchestrator, OrchestratorBackendFactory, type OrchestratorBackendFactoryOptions, type OrchestratorContext, type OrchestratorEvent, type OrchestratorState, PRDetector, type PRDetectorLogger, type PendingInteraction, PromptRenderer, type PublishedIndex, type RateLimitSnapshot as RateLimitComputeSnapshot, type RateLimitConfig, type RateLimitSnapshot$1 as RateLimitSnapshot, type ReleaseClaimEffect, type RetryEntry, type RetryFiredEvent, RoadmapTrackerAdapter, type RunAttemptPhase, type RunningEntry, type ScheduleRetryEffect, type SideEffect, type StallDetectedEvent, type StopEffect, type StreamManifest, StreamRecorder, type TickEvent, type TokenTotals, type TriageConfig, type TriageDecision, type TriageSignals, type TriageSkill, type UpdateTokensEffect, type WorkerExitEvent, WorkflowLoader, WorkspaceHooks, WorkspaceManager, applyEvent, artifactPresenceFromIssue, calculateRetryDelay, canDispatch, computeRateLimitDelay, createBackend, createEmptyState, detectScopeTier, extractHighlights, extractTitlePrefix, getAvailableSlots, getDefaultConfig, getPerStateCount, isEligible, launchTUI, loadPublishedIndex, migrateAgentConfig, reconcile, renderAnalysisComment, renderPRComment, resolveEscalationConfig, resolveOrchestratorId, routeIssue, savePublishedIndex, selectCandidates, sortCandidates, triageIssue, validateWorkflowConfig };
1523
+ /**
1524
+ * Function signature compatible with Node's `child_process.execFile`.
1525
+ * Allows injection for testing.
1526
+ */
1527
+ type ExecFileFn = typeof execFile;
1528
+ /** Reasons `syncMain()` declines to fast-forward (none of which are failures). */
1529
+ type SyncSkipReason = 'wrong-branch' | 'diverged' | 'dirty-conflict' | 'no-remote' | 'fetch-failed';
1530
+ /** Total result type for `syncMain()`. Discriminated by `status`. */
1531
+ type SyncMainResult = {
1532
+ status: 'updated';
1533
+ from: string;
1534
+ to: string;
1535
+ defaultBranch: string;
1536
+ } | {
1537
+ status: 'no-op';
1538
+ defaultBranch: string;
1539
+ } | {
1540
+ status: 'skipped';
1541
+ reason: SyncSkipReason;
1542
+ detail: string;
1543
+ defaultBranch: string;
1544
+ } | {
1545
+ status: 'error';
1546
+ message: string;
1547
+ };
1548
+ /** Options accepted by `syncMain()`. */
1549
+ interface SyncMainOptions {
1550
+ /** Override the `git` runner; defaults to `node:child_process.execFile`. */
1551
+ execFileFn?: ExecFileFn;
1552
+ /** Per-git-call timeout in ms. Defaults to 60_000ms. */
1553
+ timeoutMs?: number;
1554
+ }
1555
+ /**
1556
+ * Fast-forward the local default branch from `origin/<default>` if it can be
1557
+ * done safely. This function is total: it never throws under expected
1558
+ * conditions. Even unexpected errors are caught and converted to
1559
+ * `{ status: 'error', message }` so the maintenance scheduler does not back
1560
+ * off on transient git issues.
1561
+ */
1562
+ declare function syncMain(repoRoot: string, opts?: SyncMainOptions): Promise<SyncMainResult>;
1563
+
1564
+ export { type AgentUpdateEvent, AnalysisArchive, type AnalysisRecord, type ApplyEventResult, type ArtifactPresence, type AttemptStats, BackendRouter, type BackendRouterOptions, type BaseRefFallbackEvent, ClaimManager, type ClaimManagerConfig, type CleanWorkspaceEffect, type DispatchEffect, type EmitLogEffect, type EscalateEffect, type ExecFileFn$1 as ExecFileFn, type Highlight, type HighlightsInfo, InteractionQueue, type LinearGraphQLExtension, LinearGraphQLStub, type LiveSession, type MigrationResult, MockBackend, ORCHESTRATOR_IDENTITY_FILE, Orchestrator, OrchestratorBackendFactory, type OrchestratorBackendFactoryOptions, type OrchestratorContext, type OrchestratorEvent, type OrchestratorState, PRDetector, type PRDetectorLogger, type PendingInteraction, PromptRenderer, type PublishedIndex, type RateLimitSnapshot as RateLimitComputeSnapshot, type RateLimitConfig, type RateLimitSnapshot$1 as RateLimitSnapshot, type ReleaseClaimEffect, type RetryEntry, type RetryFiredEvent, RoadmapTrackerAdapter, type RunAttemptPhase, type RunningEntry, type ScheduleRetryEffect, type SideEffect, type StallDetectedEvent, type StopEffect, type StreamManifest, StreamRecorder, type SyncMainOptions, type SyncMainResult, type SyncSkipReason, type TickEvent, type TokenTotals, type TriageConfig, type TriageDecision, type TriageSignals, type TriageSkill, type UpdateTokensEffect, type WorkerExitEvent, WorkflowLoader, WorkspaceHooks, WorkspaceManager, type WorkspaceManagerOptions, applyEvent, artifactPresenceFromIssue, calculateRetryDelay, canDispatch, computeRateLimitDelay, createBackend, createEmptyState, detectScopeTier, extractHighlights, extractTitlePrefix, getAvailableSlots, getDefaultConfig, getPerStateCount, isEligible, launchTUI, loadPublishedIndex, migrateAgentConfig, reconcile, renderAnalysisComment, renderPRComment, resolveEscalationConfig, resolveOrchestratorId, routeIssue, savePublishedIndex, selectCandidates, sortCandidates, syncMain, triageIssue, validateWorkflowConfig };