@telora/factory 0.4.5
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/audit.d.ts +69 -0
- package/dist/audit.d.ts.map +1 -0
- package/dist/audit.js +376 -0
- package/dist/audit.js.map +1 -0
- package/dist/builder-completion.d.ts +35 -0
- package/dist/builder-completion.d.ts.map +1 -0
- package/dist/builder-completion.js +375 -0
- package/dist/builder-completion.js.map +1 -0
- package/dist/builder-spawner.d.ts +40 -0
- package/dist/builder-spawner.d.ts.map +1 -0
- package/dist/builder-spawner.js +493 -0
- package/dist/builder-spawner.js.map +1 -0
- package/dist/completion-gate.d.ts +52 -0
- package/dist/completion-gate.d.ts.map +1 -0
- package/dist/completion-gate.js +336 -0
- package/dist/completion-gate.js.map +1 -0
- package/dist/completion-report.d.ts +36 -0
- package/dist/completion-report.d.ts.map +1 -0
- package/dist/completion-report.js +348 -0
- package/dist/completion-report.js.map +1 -0
- package/dist/completion.d.ts +58 -0
- package/dist/completion.d.ts.map +1 -0
- package/dist/completion.js +287 -0
- package/dist/completion.js.map +1 -0
- package/dist/config.d.ts +16 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +57 -0
- package/dist/config.js.map +1 -0
- package/dist/context-manager.d.ts +152 -0
- package/dist/context-manager.d.ts.map +1 -0
- package/dist/context-manager.js +421 -0
- package/dist/context-manager.js.map +1 -0
- package/dist/crash-detection.d.ts +70 -0
- package/dist/crash-detection.d.ts.map +1 -0
- package/dist/crash-detection.js +123 -0
- package/dist/crash-detection.js.map +1 -0
- package/dist/crash-recovery.d.ts +83 -0
- package/dist/crash-recovery.d.ts.map +1 -0
- package/dist/crash-recovery.js +522 -0
- package/dist/crash-recovery.js.map +1 -0
- package/dist/crash-resolution.d.ts +34 -0
- package/dist/crash-resolution.d.ts.map +1 -0
- package/dist/crash-resolution.js +382 -0
- package/dist/crash-resolution.js.map +1 -0
- package/dist/escalation.d.ts +150 -0
- package/dist/escalation.d.ts.map +1 -0
- package/dist/escalation.js +352 -0
- package/dist/escalation.js.map +1 -0
- package/dist/execution-target.d.ts +31 -0
- package/dist/execution-target.d.ts.map +1 -0
- package/dist/execution-target.js +71 -0
- package/dist/execution-target.js.map +1 -0
- package/dist/execution-unit-init.d.ts +28 -0
- package/dist/execution-unit-init.d.ts.map +1 -0
- package/dist/execution-unit-init.js +115 -0
- package/dist/execution-unit-init.js.map +1 -0
- package/dist/execution.d.ts +17 -0
- package/dist/execution.d.ts.map +1 -0
- package/dist/execution.js +20 -0
- package/dist/execution.js.map +1 -0
- package/dist/factory-engine.d.ts +100 -0
- package/dist/factory-engine.d.ts.map +1 -0
- package/dist/factory-engine.js +243 -0
- package/dist/factory-engine.js.map +1 -0
- package/dist/gap-detection.d.ts +43 -0
- package/dist/gap-detection.d.ts.map +1 -0
- package/dist/gap-detection.js +149 -0
- package/dist/gap-detection.js.map +1 -0
- package/dist/gate-context.d.ts +23 -0
- package/dist/gate-context.d.ts.map +1 -0
- package/dist/gate-context.js +63 -0
- package/dist/gate-context.js.map +1 -0
- package/dist/gate-engine.d.ts +55 -0
- package/dist/gate-engine.d.ts.map +1 -0
- package/dist/gate-engine.js +191 -0
- package/dist/gate-engine.js.map +1 -0
- package/dist/gates/adversarial.d.ts +59 -0
- package/dist/gates/adversarial.d.ts.map +1 -0
- package/dist/gates/adversarial.js +426 -0
- package/dist/gates/adversarial.js.map +1 -0
- package/dist/gates/adversary-spawner.d.ts +35 -0
- package/dist/gates/adversary-spawner.d.ts.map +1 -0
- package/dist/gates/adversary-spawner.js +286 -0
- package/dist/gates/adversary-spawner.js.map +1 -0
- package/dist/gates/adversary-test-dir.d.ts +41 -0
- package/dist/gates/adversary-test-dir.d.ts.map +1 -0
- package/dist/gates/adversary-test-dir.js +150 -0
- package/dist/gates/adversary-test-dir.js.map +1 -0
- package/dist/gates/behavioral-parser.d.ts +32 -0
- package/dist/gates/behavioral-parser.d.ts.map +1 -0
- package/dist/gates/behavioral-parser.js +190 -0
- package/dist/gates/behavioral-parser.js.map +1 -0
- package/dist/gates/behavioral-runner.d.ts +36 -0
- package/dist/gates/behavioral-runner.d.ts.map +1 -0
- package/dist/gates/behavioral-runner.js +306 -0
- package/dist/gates/behavioral-runner.js.map +1 -0
- package/dist/gates/behavioral.d.ts +37 -0
- package/dist/gates/behavioral.d.ts.map +1 -0
- package/dist/gates/behavioral.js +485 -0
- package/dist/gates/behavioral.js.map +1 -0
- package/dist/gates/deterministic.d.ts +24 -0
- package/dist/gates/deterministic.d.ts.map +1 -0
- package/dist/gates/deterministic.js +186 -0
- package/dist/gates/deterministic.js.map +1 -0
- package/dist/git-factory.d.ts +59 -0
- package/dist/git-factory.d.ts.map +1 -0
- package/dist/git-factory.js +102 -0
- package/dist/git-factory.js.map +1 -0
- package/dist/guard-evaluation.d.ts +48 -0
- package/dist/guard-evaluation.d.ts.map +1 -0
- package/dist/guard-evaluation.js +416 -0
- package/dist/guard-evaluation.js.map +1 -0
- package/dist/index.d.ts +30 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +39 -0
- package/dist/index.js.map +1 -0
- package/dist/instance-completion.d.ts +34 -0
- package/dist/instance-completion.d.ts.map +1 -0
- package/dist/instance-completion.js +366 -0
- package/dist/instance-completion.js.map +1 -0
- package/dist/instance-lifecycle.d.ts +15 -0
- package/dist/instance-lifecycle.d.ts.map +1 -0
- package/dist/instance-lifecycle.js +18 -0
- package/dist/instance-lifecycle.js.map +1 -0
- package/dist/instance-phase-dispatch.d.ts +75 -0
- package/dist/instance-phase-dispatch.d.ts.map +1 -0
- package/dist/instance-phase-dispatch.js +674 -0
- package/dist/instance-phase-dispatch.js.map +1 -0
- package/dist/instance-poll-loop.d.ts +43 -0
- package/dist/instance-poll-loop.d.ts.map +1 -0
- package/dist/instance-poll-loop.js +360 -0
- package/dist/instance-poll-loop.js.map +1 -0
- package/dist/instance-state-machine.d.ts +52 -0
- package/dist/instance-state-machine.d.ts.map +1 -0
- package/dist/instance-state-machine.js +235 -0
- package/dist/instance-state-machine.js.map +1 -0
- package/dist/log-manager.d.ts +28 -0
- package/dist/log-manager.d.ts.map +1 -0
- package/dist/log-manager.js +71 -0
- package/dist/log-manager.js.map +1 -0
- package/dist/pipeline-evaluator.d.ts +61 -0
- package/dist/pipeline-evaluator.d.ts.map +1 -0
- package/dist/pipeline-evaluator.js +107 -0
- package/dist/pipeline-evaluator.js.map +1 -0
- package/dist/pipeline-metrics.d.ts +52 -0
- package/dist/pipeline-metrics.d.ts.map +1 -0
- package/dist/pipeline-metrics.js +40 -0
- package/dist/pipeline-metrics.js.map +1 -0
- package/dist/pipeline-traversal.d.ts +43 -0
- package/dist/pipeline-traversal.d.ts.map +1 -0
- package/dist/pipeline-traversal.js +68 -0
- package/dist/pipeline-traversal.js.map +1 -0
- package/dist/plan-parser.d.ts +76 -0
- package/dist/plan-parser.d.ts.map +1 -0
- package/dist/plan-parser.js +223 -0
- package/dist/plan-parser.js.map +1 -0
- package/dist/planning-phase.d.ts +52 -0
- package/dist/planning-phase.d.ts.map +1 -0
- package/dist/planning-phase.js +444 -0
- package/dist/planning-phase.js.map +1 -0
- package/dist/planning-prompt.d.ts +64 -0
- package/dist/planning-prompt.d.ts.map +1 -0
- package/dist/planning-prompt.js +251 -0
- package/dist/planning-prompt.js.map +1 -0
- package/dist/planning.d.ts +16 -0
- package/dist/planning.d.ts.map +1 -0
- package/dist/planning.js +17 -0
- package/dist/planning.js.map +1 -0
- package/dist/process-runner.d.ts +41 -0
- package/dist/process-runner.d.ts.map +1 -0
- package/dist/process-runner.js +81 -0
- package/dist/process-runner.js.map +1 -0
- package/dist/product-config.d.ts +34 -0
- package/dist/product-config.d.ts.map +1 -0
- package/dist/product-config.js +43 -0
- package/dist/product-config.js.map +1 -0
- package/dist/queries/cycle-evaluations.d.ts +23 -0
- package/dist/queries/cycle-evaluations.d.ts.map +1 -0
- package/dist/queries/cycle-evaluations.js +37 -0
- package/dist/queries/cycle-evaluations.js.map +1 -0
- package/dist/queries/escalations.d.ts +30 -0
- package/dist/queries/escalations.d.ts.map +1 -0
- package/dist/queries/escalations.js +42 -0
- package/dist/queries/escalations.js.map +1 -0
- package/dist/queries/execution-units.d.ts +76 -0
- package/dist/queries/execution-units.d.ts.map +1 -0
- package/dist/queries/execution-units.js +109 -0
- package/dist/queries/execution-units.js.map +1 -0
- package/dist/queries/gate-results.d.ts +32 -0
- package/dist/queries/gate-results.d.ts.map +1 -0
- package/dist/queries/gate-results.js +44 -0
- package/dist/queries/gate-results.js.map +1 -0
- package/dist/queries/instances.d.ts +51 -0
- package/dist/queries/instances.d.ts.map +1 -0
- package/dist/queries/instances.js +77 -0
- package/dist/queries/instances.js.map +1 -0
- package/dist/queries/sessions.d.ts +50 -0
- package/dist/queries/sessions.d.ts.map +1 -0
- package/dist/queries/sessions.js +81 -0
- package/dist/queries/sessions.js.map +1 -0
- package/dist/queries/shared.d.ts +38 -0
- package/dist/queries/shared.d.ts.map +1 -0
- package/dist/queries/shared.js +119 -0
- package/dist/queries/shared.js.map +1 -0
- package/dist/queries/specs.d.ts +12 -0
- package/dist/queries/specs.d.ts.map +1 -0
- package/dist/queries/specs.js +21 -0
- package/dist/queries/specs.js.map +1 -0
- package/dist/queries/strategies.d.ts +14 -0
- package/dist/queries/strategies.d.ts.map +1 -0
- package/dist/queries/strategies.js +18 -0
- package/dist/queries/strategies.js.map +1 -0
- package/dist/queries/work-units.d.ts +42 -0
- package/dist/queries/work-units.d.ts.map +1 -0
- package/dist/queries/work-units.js +57 -0
- package/dist/queries/work-units.js.map +1 -0
- package/dist/queries/workflows.d.ts +29 -0
- package/dist/queries/workflows.d.ts.map +1 -0
- package/dist/queries/workflows.js +103 -0
- package/dist/queries/workflows.js.map +1 -0
- package/dist/remediation-units.d.ts +40 -0
- package/dist/remediation-units.d.ts.map +1 -0
- package/dist/remediation-units.js +263 -0
- package/dist/remediation-units.js.map +1 -0
- package/dist/replanning.d.ts +72 -0
- package/dist/replanning.d.ts.map +1 -0
- package/dist/replanning.js +403 -0
- package/dist/replanning.js.map +1 -0
- package/dist/resource-limits.d.ts +62 -0
- package/dist/resource-limits.d.ts.map +1 -0
- package/dist/resource-limits.js +322 -0
- package/dist/resource-limits.js.map +1 -0
- package/dist/scheduler.d.ts +98 -0
- package/dist/scheduler.d.ts.map +1 -0
- package/dist/scheduler.js +203 -0
- package/dist/scheduler.js.map +1 -0
- package/dist/session-adapter.d.ts +89 -0
- package/dist/session-adapter.d.ts.map +1 -0
- package/dist/session-adapter.js +108 -0
- package/dist/session-adapter.js.map +1 -0
- package/dist/sop-generator.d.ts +29 -0
- package/dist/sop-generator.d.ts.map +1 -0
- package/dist/sop-generator.js +235 -0
- package/dist/sop-generator.js.map +1 -0
- package/dist/spec-profiles.d.ts +41 -0
- package/dist/spec-profiles.d.ts.map +1 -0
- package/dist/spec-profiles.js +131 -0
- package/dist/spec-profiles.js.map +1 -0
- package/dist/strategy-design-graph.d.ts +23 -0
- package/dist/strategy-design-graph.d.ts.map +1 -0
- package/dist/strategy-design-graph.js +205 -0
- package/dist/strategy-design-graph.js.map +1 -0
- package/dist/strategy-design-prompt.d.ts +28 -0
- package/dist/strategy-design-prompt.d.ts.map +1 -0
- package/dist/strategy-design-prompt.js +108 -0
- package/dist/strategy-design-prompt.js.map +1 -0
- package/dist/strategy-design-schema.d.ts +767 -0
- package/dist/strategy-design-schema.d.ts.map +1 -0
- package/dist/strategy-design-schema.js +126 -0
- package/dist/strategy-design-schema.js.map +1 -0
- package/dist/strategy-design.d.ts +69 -0
- package/dist/strategy-design.d.ts.map +1 -0
- package/dist/strategy-design.js +411 -0
- package/dist/strategy-design.js.map +1 -0
- package/dist/strategy-gating.d.ts +31 -0
- package/dist/strategy-gating.d.ts.map +1 -0
- package/dist/strategy-gating.js +276 -0
- package/dist/strategy-gating.js.map +1 -0
- package/dist/team-prompt-builder.d.ts +47 -0
- package/dist/team-prompt-builder.d.ts.map +1 -0
- package/dist/team-prompt-builder.js +362 -0
- package/dist/team-prompt-builder.js.map +1 -0
- package/dist/trace-engine.d.ts +40 -0
- package/dist/trace-engine.d.ts.map +1 -0
- package/dist/trace-engine.js +344 -0
- package/dist/trace-engine.js.map +1 -0
- package/dist/types.d.ts +612 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +9 -0
- package/dist/types.js.map +1 -0
- package/dist/unit-session-lifecycle.d.ts +78 -0
- package/dist/unit-session-lifecycle.d.ts.map +1 -0
- package/dist/unit-session-lifecycle.js +141 -0
- package/dist/unit-session-lifecycle.js.map +1 -0
- package/dist/unit-session.d.ts +30 -0
- package/dist/unit-session.d.ts.map +1 -0
- package/dist/unit-session.js +370 -0
- package/dist/unit-session.js.map +1 -0
- package/dist/watchdogs.d.ts +33 -0
- package/dist/watchdogs.d.ts.map +1 -0
- package/dist/watchdogs.js +170 -0
- package/dist/watchdogs.js.map +1 -0
- package/dist/work-unit-scheduler.d.ts +34 -0
- package/dist/work-unit-scheduler.d.ts.map +1 -0
- package/dist/work-unit-scheduler.js +91 -0
- package/dist/work-unit-scheduler.js.map +1 -0
- package/dist/workflow-transition.d.ts +90 -0
- package/dist/workflow-transition.d.ts.map +1 -0
- package/dist/workflow-transition.js +340 -0
- package/dist/workflow-transition.js.map +1 -0
- package/package.json +65 -0
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Factory crash recovery: orphaned worktree detection, stale session cleanup,
|
|
3
|
+
* and stuck instance recovery.
|
|
4
|
+
*
|
|
5
|
+
* When the factory orchestrator crashes (SIGKILL, OOM, power loss), worktrees
|
|
6
|
+
* and DB records persist with no in-memory tracking. On restart, this module:
|
|
7
|
+
*
|
|
8
|
+
* 1. Scans `.telora/factory-worktrees/` for directories not tracked in memory.
|
|
9
|
+
* 2. Resets factory_sessions stuck in 'running' to 'failed'.
|
|
10
|
+
* 3. Resets factory_instances stuck in 'building'/'gating' to 'paused' with
|
|
11
|
+
* an escalation about unclean shutdown.
|
|
12
|
+
* 4. Commits WIP changes in orphaned worktrees to preserve in-flight work.
|
|
13
|
+
*
|
|
14
|
+
* Runs after config init and before the poll loop starts, so the factory
|
|
15
|
+
* starts in a clean, known state.
|
|
16
|
+
*
|
|
17
|
+
* Mirrors the recovery patterns from packages/daemon/src/crash-recovery.ts.
|
|
18
|
+
*/
|
|
19
|
+
import type { FactoryConfig } from './types.js';
|
|
20
|
+
export interface OrphanedWorktree {
|
|
21
|
+
/** Absolute path to the worktree directory on disk. */
|
|
22
|
+
worktreePath: string;
|
|
23
|
+
/** Git branch name derived from the worktree entry (if available). */
|
|
24
|
+
branchName: string | null;
|
|
25
|
+
}
|
|
26
|
+
export interface RecoveryAction {
|
|
27
|
+
type: 'worktree_wip_committed' | 'session_reset' | 'instance_paused' | 'escalation_created' | 'execution_unit_reset';
|
|
28
|
+
/** Entity ID (worktree path, session ID, instance ID, or execution unit ID). */
|
|
29
|
+
entityId: string;
|
|
30
|
+
/** Human-readable description of what was done. */
|
|
31
|
+
description: string;
|
|
32
|
+
}
|
|
33
|
+
export interface RecoveryResult {
|
|
34
|
+
/** Number of orphaned worktrees found on disk. */
|
|
35
|
+
orphanedWorktreesFound: number;
|
|
36
|
+
/** Number of WIP commits created for orphaned worktrees. */
|
|
37
|
+
wipCommitsCreated: number;
|
|
38
|
+
/** Number of sessions reset from 'running' to 'failed'. */
|
|
39
|
+
sessionsReset: number;
|
|
40
|
+
/** Number of instances reset from 'building'/'gating' to 'paused'. */
|
|
41
|
+
instancesPaused: number;
|
|
42
|
+
/** Number of escalations created for paused instances. */
|
|
43
|
+
escalationsCreated: number;
|
|
44
|
+
/** Number of execution units reset from 'assigned'/'running' to 'idle'. */
|
|
45
|
+
executionUnitsReset: number;
|
|
46
|
+
/** Detailed log of all recovery actions taken. */
|
|
47
|
+
actions: RecoveryAction[];
|
|
48
|
+
/** Non-fatal errors encountered during recovery. */
|
|
49
|
+
errors: string[];
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Parse `git worktree list --porcelain` output to build a map of
|
|
53
|
+
* worktree path -> branch name for all registered worktrees.
|
|
54
|
+
*/
|
|
55
|
+
export declare function parseWorktreeList(repoPath: string): Map<string, string>;
|
|
56
|
+
/**
|
|
57
|
+
* Scan the factory worktree directory for directories that exist on disk.
|
|
58
|
+
*
|
|
59
|
+
* Returns all subdirectories found in `factoryWorktreeDir`, each with
|
|
60
|
+
* its git branch name (if git tracks it) or null.
|
|
61
|
+
*
|
|
62
|
+
* The caller determines which worktrees are "orphaned" by comparing
|
|
63
|
+
* against the in-memory active instances map (which is empty on fresh start).
|
|
64
|
+
*/
|
|
65
|
+
export declare function scanFactoryWorktrees(config: FactoryConfig): OrphanedWorktree[];
|
|
66
|
+
/**
|
|
67
|
+
* Run full crash recovery on factory orchestrator startup.
|
|
68
|
+
*
|
|
69
|
+
* Should be called after config init and API client setup, but before
|
|
70
|
+
* the poll loop starts. Safe to call multiple times -- idempotent by
|
|
71
|
+
* design. A second run with no crashed state finds nothing to recover.
|
|
72
|
+
*
|
|
73
|
+
* Recovery steps:
|
|
74
|
+
* 1. Scan for orphaned worktrees and WIP-commit uncommitted changes.
|
|
75
|
+
* 2. Reset factory_sessions stuck in 'running' to 'failed'.
|
|
76
|
+
* 3. Reset factory_instances stuck in 'building'/'gating' to 'paused'.
|
|
77
|
+
* 4. Create escalations for paused instances.
|
|
78
|
+
*
|
|
79
|
+
* @param config - Factory configuration.
|
|
80
|
+
* @returns A RecoveryResult summarizing all actions taken.
|
|
81
|
+
*/
|
|
82
|
+
export declare function recoverFromCrash(config: FactoryConfig): Promise<RecoveryResult>;
|
|
83
|
+
//# sourceMappingURL=crash-recovery.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"crash-recovery.d.ts","sourceRoot":"","sources":["../src/crash-recovery.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAKH,OAAO,KAAK,EAAE,aAAa,EAAmB,MAAM,YAAY,CAAC;AAYjE,MAAM,WAAW,gBAAgB;IAC/B,uDAAuD;IACvD,YAAY,EAAE,MAAM,CAAC;IACrB,sEAAsE;IACtE,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3B;AAED,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,wBAAwB,GAAG,eAAe,GAAG,iBAAiB,GAAG,oBAAoB,GAAG,sBAAsB,CAAC;IACrH,gFAAgF;IAChF,QAAQ,EAAE,MAAM,CAAC;IACjB,mDAAmD;IACnD,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,cAAc;IAC7B,kDAAkD;IAClD,sBAAsB,EAAE,MAAM,CAAC;IAC/B,4DAA4D;IAC5D,iBAAiB,EAAE,MAAM,CAAC;IAC1B,2DAA2D;IAC3D,aAAa,EAAE,MAAM,CAAC;IACtB,sEAAsE;IACtE,eAAe,EAAE,MAAM,CAAC;IACxB,0DAA0D;IAC1D,kBAAkB,EAAE,MAAM,CAAC;IAC3B,2EAA2E;IAC3E,mBAAmB,EAAE,MAAM,CAAC;IAC5B,kDAAkD;IAClD,OAAO,EAAE,cAAc,EAAE,CAAC;IAC1B,oDAAoD;IACpD,MAAM,EAAE,MAAM,EAAE,CAAC;CAClB;AAyDD;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,GAAG,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAiBvE;AAED;;;;;;;;GAQG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,aAAa,GAAG,gBAAgB,EAAE,CA6C9E;AAgWD;;;;;;;;;;;;;;;GAeG;AACH,wBAAsB,gBAAgB,CAAC,MAAM,EAAE,aAAa,GAAG,OAAO,CAAC,cAAc,CAAC,CA0FrF"}
|
|
@@ -0,0 +1,522 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Factory crash recovery: orphaned worktree detection, stale session cleanup,
|
|
3
|
+
* and stuck instance recovery.
|
|
4
|
+
*
|
|
5
|
+
* When the factory orchestrator crashes (SIGKILL, OOM, power loss), worktrees
|
|
6
|
+
* and DB records persist with no in-memory tracking. On restart, this module:
|
|
7
|
+
*
|
|
8
|
+
* 1. Scans `.telora/factory-worktrees/` for directories not tracked in memory.
|
|
9
|
+
* 2. Resets factory_sessions stuck in 'running' to 'failed'.
|
|
10
|
+
* 3. Resets factory_instances stuck in 'building'/'gating' to 'paused' with
|
|
11
|
+
* an escalation about unclean shutdown.
|
|
12
|
+
* 4. Commits WIP changes in orphaned worktrees to preserve in-flight work.
|
|
13
|
+
*
|
|
14
|
+
* Runs after config init and before the poll loop starts, so the factory
|
|
15
|
+
* starts in a clean, known state.
|
|
16
|
+
*
|
|
17
|
+
* Mirrors the recovery patterns from packages/daemon/src/crash-recovery.ts.
|
|
18
|
+
*/
|
|
19
|
+
import { execFileSync } from 'node:child_process';
|
|
20
|
+
import { existsSync, readdirSync, statSync } from 'node:fs';
|
|
21
|
+
import { join, resolve } from 'node:path';
|
|
22
|
+
import { commitWip } from './git-factory.js';
|
|
23
|
+
import { updateInstanceStatus, getActiveInstances } from './queries/instances.js';
|
|
24
|
+
import { updateFactorySession } from './queries/sessions.js';
|
|
25
|
+
import { createFactoryEscalation } from './queries/escalations.js';
|
|
26
|
+
import { getExecutionUnitsByInstance, updateExecutionUnit } from './queries/execution-units.js';
|
|
27
|
+
import { productLabel } from './product-config.js';
|
|
28
|
+
// ============================================================================
|
|
29
|
+
// Git helpers (synchronous, same pattern as git-factory.ts)
|
|
30
|
+
// ============================================================================
|
|
31
|
+
/** Default timeout for git CLI operations (30 seconds -- read-only ops are fast). */
|
|
32
|
+
const GIT_TIMEOUT_MS = 30_000;
|
|
33
|
+
/**
|
|
34
|
+
* Run a git command synchronously and return a structured result.
|
|
35
|
+
* Never throws -- callers inspect `result.success`.
|
|
36
|
+
*/
|
|
37
|
+
function runGit(args, cwd) {
|
|
38
|
+
try {
|
|
39
|
+
const output = execFileSync('git', args, {
|
|
40
|
+
cwd,
|
|
41
|
+
encoding: 'utf-8',
|
|
42
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
43
|
+
timeout: GIT_TIMEOUT_MS,
|
|
44
|
+
});
|
|
45
|
+
return { success: true, output: output.trim(), error: '' };
|
|
46
|
+
}
|
|
47
|
+
catch (err) {
|
|
48
|
+
const execErr = err;
|
|
49
|
+
if (execErr.killed || execErr.signal === 'SIGTERM') {
|
|
50
|
+
return {
|
|
51
|
+
success: false,
|
|
52
|
+
output: execErr.stdout?.toString() ?? '',
|
|
53
|
+
error: `Git operation timed out after ${GIT_TIMEOUT_MS}ms: git ${args.join(' ')}`,
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
return {
|
|
57
|
+
success: false,
|
|
58
|
+
output: execErr.stdout?.toString() ?? '',
|
|
59
|
+
error: execErr.stderr?.toString() ?? execErr.message ?? 'Unknown git error',
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
// ============================================================================
|
|
64
|
+
// Worktree scanning
|
|
65
|
+
// ============================================================================
|
|
66
|
+
/**
|
|
67
|
+
* Parse `git worktree list --porcelain` output to build a map of
|
|
68
|
+
* worktree path -> branch name for all registered worktrees.
|
|
69
|
+
*/
|
|
70
|
+
export function parseWorktreeList(repoPath) {
|
|
71
|
+
const result = runGit(['worktree', 'list', '--porcelain'], repoPath);
|
|
72
|
+
const map = new Map();
|
|
73
|
+
if (!result.success)
|
|
74
|
+
return map;
|
|
75
|
+
let currentPath = null;
|
|
76
|
+
for (const line of result.output.split('\n')) {
|
|
77
|
+
if (line.startsWith('worktree ')) {
|
|
78
|
+
currentPath = line.slice('worktree '.length);
|
|
79
|
+
}
|
|
80
|
+
else if (line.startsWith('branch refs/heads/') && currentPath) {
|
|
81
|
+
map.set(currentPath, line.slice('branch refs/heads/'.length));
|
|
82
|
+
currentPath = null;
|
|
83
|
+
}
|
|
84
|
+
else if (line === '') {
|
|
85
|
+
currentPath = null;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
return map;
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Scan the factory worktree directory for directories that exist on disk.
|
|
92
|
+
*
|
|
93
|
+
* Returns all subdirectories found in `factoryWorktreeDir`, each with
|
|
94
|
+
* its git branch name (if git tracks it) or null.
|
|
95
|
+
*
|
|
96
|
+
* The caller determines which worktrees are "orphaned" by comparing
|
|
97
|
+
* against the in-memory active instances map (which is empty on fresh start).
|
|
98
|
+
*/
|
|
99
|
+
export function scanFactoryWorktrees(config) {
|
|
100
|
+
// Parse git worktree list from all configured product repos
|
|
101
|
+
const pathToBranch = new Map();
|
|
102
|
+
for (const product of config.products) {
|
|
103
|
+
const worktreeMap = parseWorktreeList(product.repoPath);
|
|
104
|
+
for (const [wtPath, branch] of worktreeMap) {
|
|
105
|
+
pathToBranch.set(resolve(wtPath), branch);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
// Scan each product's factory worktree directory
|
|
109
|
+
const orphans = [];
|
|
110
|
+
for (const product of config.products) {
|
|
111
|
+
const factoryDir = resolve(product.repoPath, '.telora', 'factory-worktrees');
|
|
112
|
+
if (!existsSync(factoryDir)) {
|
|
113
|
+
continue;
|
|
114
|
+
}
|
|
115
|
+
const entries = readdirSync(factoryDir);
|
|
116
|
+
const subdirs = entries.filter((entry) => {
|
|
117
|
+
const fullPath = join(factoryDir, entry);
|
|
118
|
+
try {
|
|
119
|
+
return statSync(fullPath).isDirectory();
|
|
120
|
+
}
|
|
121
|
+
catch {
|
|
122
|
+
return false;
|
|
123
|
+
}
|
|
124
|
+
});
|
|
125
|
+
for (const subdir of subdirs) {
|
|
126
|
+
const fullPath = resolve(join(factoryDir, subdir));
|
|
127
|
+
const branchName = pathToBranch.get(fullPath) ?? null;
|
|
128
|
+
orphans.push({
|
|
129
|
+
worktreePath: fullPath,
|
|
130
|
+
branchName,
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
if (orphans.length === 0) {
|
|
135
|
+
console.log('[crash-recovery] No orphaned factory worktrees found across any product');
|
|
136
|
+
}
|
|
137
|
+
return orphans;
|
|
138
|
+
}
|
|
139
|
+
// ============================================================================
|
|
140
|
+
// Recovery: orphaned worktrees
|
|
141
|
+
// ============================================================================
|
|
142
|
+
/**
|
|
143
|
+
* WIP-commit any uncommitted changes in orphaned factory worktrees.
|
|
144
|
+
*
|
|
145
|
+
* This preserves in-flight work that was not committed before the crash.
|
|
146
|
+
* The worktrees are left in place -- they will be reused when the instance
|
|
147
|
+
* resumes (after being reset to 'paused').
|
|
148
|
+
*/
|
|
149
|
+
function recoverOrphanedWorktrees(orphans) {
|
|
150
|
+
let wipCommitsCreated = 0;
|
|
151
|
+
const actions = [];
|
|
152
|
+
for (const orphan of orphans) {
|
|
153
|
+
const label = orphan.branchName ?? 'unknown-branch';
|
|
154
|
+
try {
|
|
155
|
+
const sha = commitWip(orphan.worktreePath, `crash-recovery: ${label}`);
|
|
156
|
+
if (sha) {
|
|
157
|
+
wipCommitsCreated++;
|
|
158
|
+
actions.push({
|
|
159
|
+
type: 'worktree_wip_committed',
|
|
160
|
+
entityId: orphan.worktreePath,
|
|
161
|
+
description: `WIP commit ${sha.slice(0, 8)} created for orphaned worktree (branch: ${label})`,
|
|
162
|
+
});
|
|
163
|
+
console.log(`[crash-recovery] WIP commit ${sha.slice(0, 8)} for orphaned worktree: ${orphan.worktreePath}`);
|
|
164
|
+
}
|
|
165
|
+
else {
|
|
166
|
+
console.log(`[crash-recovery] Orphaned worktree has no uncommitted changes: ${orphan.worktreePath}`);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
catch (err) {
|
|
170
|
+
console.warn(`[crash-recovery] Failed to WIP-commit orphaned worktree ${orphan.worktreePath}: ` +
|
|
171
|
+
`${err instanceof Error ? err.message : String(err)}`);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
return { wipCommitsCreated, actions };
|
|
175
|
+
}
|
|
176
|
+
// ============================================================================
|
|
177
|
+
// Recovery: stale sessions
|
|
178
|
+
// ============================================================================
|
|
179
|
+
/**
|
|
180
|
+
* Reset factory sessions stuck in 'running' or 'starting' status to 'failed'.
|
|
181
|
+
*
|
|
182
|
+
* After a crash, any sessions that were 'running' are no longer actually
|
|
183
|
+
* running -- their processes died with the orchestrator. We query the API
|
|
184
|
+
* for all instances that might have active sessions and reset them.
|
|
185
|
+
*/
|
|
186
|
+
async function recoverStaleSessions(config) {
|
|
187
|
+
const sessionsReset = 0;
|
|
188
|
+
const actions = [];
|
|
189
|
+
const errors = [];
|
|
190
|
+
// We need to find sessions stuck in 'running' status. Since we don't have
|
|
191
|
+
// a direct "get all running sessions" query, we use getActiveSessionsForInstance
|
|
192
|
+
// for each instance that might have stale sessions. We get the instances from
|
|
193
|
+
// the pending query (which returns instances that aren't in terminal states).
|
|
194
|
+
//
|
|
195
|
+
// However, getPendingInstances only returns 'pending' status. For crash
|
|
196
|
+
// recovery we need instances in 'building' and 'gating' states too.
|
|
197
|
+
// Those will be handled via the instance recovery below, which also
|
|
198
|
+
// resets their sessions. But we still want a catch-all for any sessions
|
|
199
|
+
// that might be orphaned.
|
|
200
|
+
//
|
|
201
|
+
// The approach: reset sessions for instances we know about from worktree
|
|
202
|
+
// scanning. The instance recovery step will handle the rest.
|
|
203
|
+
// Note: Session reset for specific instances happens in recoverStuckInstances.
|
|
204
|
+
// This function serves as documentation of the strategy. The actual per-instance
|
|
205
|
+
// session reset is co-located with the instance status reset for atomicity.
|
|
206
|
+
void config; // Config may be used in future for direct session queries
|
|
207
|
+
return { sessionsReset, actions, errors };
|
|
208
|
+
}
|
|
209
|
+
// ============================================================================
|
|
210
|
+
// Recovery: stuck instances
|
|
211
|
+
// ============================================================================
|
|
212
|
+
/**
|
|
213
|
+
* Find and recover factory instances stuck in 'building' or 'gating' status.
|
|
214
|
+
*
|
|
215
|
+
* After an unclean shutdown, these instances had active processing that
|
|
216
|
+
* was interrupted. We:
|
|
217
|
+
* 1. Reset their status to 'paused'.
|
|
218
|
+
* 2. Mark their active sessions as 'failed'.
|
|
219
|
+
* 3. Create an escalation to notify the human operator.
|
|
220
|
+
*/
|
|
221
|
+
async function recoverStuckInstances(config) {
|
|
222
|
+
let instancesPaused = 0;
|
|
223
|
+
let sessionsReset = 0;
|
|
224
|
+
let escalationsCreated = 0;
|
|
225
|
+
const actions = [];
|
|
226
|
+
const errors = [];
|
|
227
|
+
// Query non-terminal instances (designing, planning, building, paused) and
|
|
228
|
+
// filter for those stuck in active processing states after the crash.
|
|
229
|
+
// Query instances across all configured products (per-product isolation)
|
|
230
|
+
const instances = [];
|
|
231
|
+
for (const product of config.products) {
|
|
232
|
+
const pLabel = productLabel(product);
|
|
233
|
+
try {
|
|
234
|
+
const productInstances = await getActiveInstances(product.id);
|
|
235
|
+
if (productInstances.length > 0) {
|
|
236
|
+
console.log(`[crash-recovery] [${pLabel}] Found ${productInstances.length} active instance(s)`);
|
|
237
|
+
}
|
|
238
|
+
instances.push(...productInstances);
|
|
239
|
+
}
|
|
240
|
+
catch (err) {
|
|
241
|
+
const msg = `[${pLabel}] Failed to query instances for crash recovery: ${err instanceof Error ? err.message : String(err)}`;
|
|
242
|
+
console.warn(`[crash-recovery] ${msg}`);
|
|
243
|
+
errors.push(msg);
|
|
244
|
+
// continue to next product
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
// Filter for instances that are stuck in active processing states
|
|
248
|
+
// Note: 'gating' was removed as an instance-level status -- gates run
|
|
249
|
+
// inline per work unit during the 'building' phase.
|
|
250
|
+
const stuckStatuses = new Set(['building']);
|
|
251
|
+
const stuckInstances = instances.filter((inst) => stuckStatuses.has(inst.status));
|
|
252
|
+
if (stuckInstances.length === 0) {
|
|
253
|
+
console.log('[crash-recovery] No stuck instances found in building status');
|
|
254
|
+
return { instancesPaused, sessionsReset, escalationsCreated, actions, errors };
|
|
255
|
+
}
|
|
256
|
+
console.log(`[crash-recovery] Found ${stuckInstances.length} instance(s) stuck in building/gating status`);
|
|
257
|
+
// Build a reverse map: instance product_id -> product label for per-instance logging
|
|
258
|
+
const productLabelByProductId = new Map();
|
|
259
|
+
for (const product of config.products) {
|
|
260
|
+
productLabelByProductId.set(product.id, productLabel(product));
|
|
261
|
+
}
|
|
262
|
+
for (const instance of stuckInstances) {
|
|
263
|
+
const instLabel = productLabelByProductId.get(instance.productId ?? '') ?? '';
|
|
264
|
+
const instPrefix = instLabel ? `[crash-recovery] [${instLabel}]` : '[crash-recovery]';
|
|
265
|
+
// Step 1: Reset active sessions to 'failed'
|
|
266
|
+
try {
|
|
267
|
+
const { getActiveSessionsForInstance } = await import('./queries/sessions.js');
|
|
268
|
+
const activeSessions = await getActiveSessionsForInstance(instance.id);
|
|
269
|
+
for (const session of activeSessions) {
|
|
270
|
+
try {
|
|
271
|
+
await updateFactorySession(session.id, {
|
|
272
|
+
status: 'failed',
|
|
273
|
+
endedAt: new Date().toISOString(),
|
|
274
|
+
});
|
|
275
|
+
sessionsReset++;
|
|
276
|
+
actions.push({
|
|
277
|
+
type: 'session_reset',
|
|
278
|
+
entityId: session.id,
|
|
279
|
+
description: `Session reset from '${session.status}' to 'failed' (instance ${instance.id}, crash recovery)`,
|
|
280
|
+
});
|
|
281
|
+
}
|
|
282
|
+
catch (err) {
|
|
283
|
+
const msg = `Failed to reset session ${session.id}: ${err instanceof Error ? err.message : String(err)}`;
|
|
284
|
+
console.warn(`${instPrefix} ${msg}`);
|
|
285
|
+
errors.push(msg);
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
catch (err) {
|
|
290
|
+
const msg = `Failed to query sessions for instance ${instance.id}: ${err instanceof Error ? err.message : String(err)}`;
|
|
291
|
+
console.warn(`${instPrefix} ${msg}`);
|
|
292
|
+
errors.push(msg);
|
|
293
|
+
}
|
|
294
|
+
// Step 2: Reset instance status to 'paused'
|
|
295
|
+
try {
|
|
296
|
+
await updateInstanceStatus(instance.id, 'paused', {
|
|
297
|
+
escalationReason: 'Unclean shutdown recovery',
|
|
298
|
+
escalationMessage: `Factory orchestrator crashed or was killed while instance was in '${instance.status}' status. ` +
|
|
299
|
+
'Instance has been paused for manual review. Active sessions have been marked as failed. ' +
|
|
300
|
+
'Resume or cancel this instance from the control panel.',
|
|
301
|
+
escalatedAt: new Date().toISOString(),
|
|
302
|
+
});
|
|
303
|
+
instancesPaused++;
|
|
304
|
+
actions.push({
|
|
305
|
+
type: 'instance_paused',
|
|
306
|
+
entityId: instance.id,
|
|
307
|
+
description: `Instance reset from '${instance.status}' to 'paused' (crash recovery)`,
|
|
308
|
+
});
|
|
309
|
+
console.log(`${instPrefix} Instance ${instance.id} reset: ${instance.status} -> paused`);
|
|
310
|
+
}
|
|
311
|
+
catch (err) {
|
|
312
|
+
const msg = `Failed to pause instance ${instance.id}: ${err instanceof Error ? err.message : String(err)}`;
|
|
313
|
+
console.error(`${instPrefix} ${msg}`);
|
|
314
|
+
errors.push(msg);
|
|
315
|
+
continue; // Skip escalation if we couldn't even pause the instance
|
|
316
|
+
}
|
|
317
|
+
// Step 3: Create escalation
|
|
318
|
+
try {
|
|
319
|
+
await createFactoryEscalation(instance.id, 'Unclean shutdown: factory orchestrator crashed', `Instance was in '${instance.status}' status when the factory orchestrator shut down unexpectedly. ` +
|
|
320
|
+
`The instance has been paused and its active sessions marked as failed. ` +
|
|
321
|
+
`Please review the instance state and either resume or cancel it.`);
|
|
322
|
+
escalationsCreated++;
|
|
323
|
+
actions.push({
|
|
324
|
+
type: 'escalation_created',
|
|
325
|
+
entityId: instance.id,
|
|
326
|
+
description: `Escalation created for instance paused during crash recovery (was '${instance.status}')`,
|
|
327
|
+
});
|
|
328
|
+
console.log(`${instPrefix} Created escalation for instance ${instance.id}`);
|
|
329
|
+
}
|
|
330
|
+
catch (err) {
|
|
331
|
+
const msg = `Failed to create escalation for instance ${instance.id}: ${err instanceof Error ? err.message : String(err)}`;
|
|
332
|
+
console.warn(`${instPrefix} ${msg}`);
|
|
333
|
+
errors.push(msg);
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
return { instancesPaused, sessionsReset, escalationsCreated, actions, errors };
|
|
337
|
+
}
|
|
338
|
+
// ============================================================================
|
|
339
|
+
// Recovery: orphaned execution units
|
|
340
|
+
// ============================================================================
|
|
341
|
+
/**
|
|
342
|
+
* Reset execution units stuck in 'assigned' or 'running' status to 'idle'.
|
|
343
|
+
*
|
|
344
|
+
* After a crash, these units had active sessions that died with the daemon.
|
|
345
|
+
* We query ALL active (non-terminal) instances and reset their orphaned units.
|
|
346
|
+
* This runs BEFORE instance adoption so the scheduler sees clean state.
|
|
347
|
+
*/
|
|
348
|
+
async function recoverOrphanedExecutionUnits(config) {
|
|
349
|
+
let executionUnitsReset = 0;
|
|
350
|
+
const actions = [];
|
|
351
|
+
const errors = [];
|
|
352
|
+
// Query instances across all configured products (per-product isolation)
|
|
353
|
+
const instances = [];
|
|
354
|
+
// Build a reverse map: product id -> product label for per-instance logging
|
|
355
|
+
const productLabelById = new Map();
|
|
356
|
+
for (const product of config.products) {
|
|
357
|
+
const pLabel = productLabel(product);
|
|
358
|
+
productLabelById.set(product.id, pLabel);
|
|
359
|
+
try {
|
|
360
|
+
const productInstances = await getActiveInstances(product.id);
|
|
361
|
+
instances.push(...productInstances);
|
|
362
|
+
}
|
|
363
|
+
catch (err) {
|
|
364
|
+
const msg = `[${pLabel}] Failed to query instances for execution unit recovery: ${err instanceof Error ? err.message : String(err)}`;
|
|
365
|
+
console.warn(`[crash-recovery] ${msg}`);
|
|
366
|
+
errors.push(msg);
|
|
367
|
+
// continue to next product
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
// Only building instances have execution units
|
|
371
|
+
const buildingInstances = instances.filter((inst) => inst.status === 'building');
|
|
372
|
+
if (buildingInstances.length === 0) {
|
|
373
|
+
console.log('[crash-recovery] No building instances found -- skipping execution unit recovery');
|
|
374
|
+
return { executionUnitsReset, actions, errors };
|
|
375
|
+
}
|
|
376
|
+
for (const instance of buildingInstances) {
|
|
377
|
+
const instLabel = productLabelById.get(instance.productId ?? '') ?? '';
|
|
378
|
+
const instPrefix = instLabel ? `[crash-recovery] [${instLabel}]` : '[crash-recovery]';
|
|
379
|
+
let units;
|
|
380
|
+
try {
|
|
381
|
+
units = await getExecutionUnitsByInstance(instance.id);
|
|
382
|
+
}
|
|
383
|
+
catch (err) {
|
|
384
|
+
const msg = `Failed to query execution units for instance ${instance.id}: ${err instanceof Error ? err.message : String(err)}`;
|
|
385
|
+
console.warn(`${instPrefix} ${msg}`);
|
|
386
|
+
errors.push(msg);
|
|
387
|
+
continue;
|
|
388
|
+
}
|
|
389
|
+
const orphanedUnits = units.filter((u) => u.status === 'assigned' || u.status === 'running');
|
|
390
|
+
for (const unit of orphanedUnits) {
|
|
391
|
+
try {
|
|
392
|
+
await updateExecutionUnit(unit.id, {
|
|
393
|
+
status: 'idle',
|
|
394
|
+
assignedStrategyId: null,
|
|
395
|
+
claudeSessionId: null,
|
|
396
|
+
assignedAt: null,
|
|
397
|
+
});
|
|
398
|
+
executionUnitsReset++;
|
|
399
|
+
actions.push({
|
|
400
|
+
type: 'execution_unit_reset',
|
|
401
|
+
entityId: unit.id,
|
|
402
|
+
description: `Execution unit reset from '${unit.status}' to 'idle' ` +
|
|
403
|
+
`(instance ${instance.id}, slot ${unit.slotIndex}, crash recovery)`,
|
|
404
|
+
});
|
|
405
|
+
console.log(`${instPrefix} Reset execution unit ${unit.id} (slot ${unit.slotIndex}) ` +
|
|
406
|
+
`from '${unit.status}' to 'idle' (instance ${instance.id})`);
|
|
407
|
+
}
|
|
408
|
+
catch (err) {
|
|
409
|
+
const msg = `Failed to reset execution unit ${unit.id}: ${err instanceof Error ? err.message : String(err)}`;
|
|
410
|
+
console.warn(`${instPrefix} ${msg}`);
|
|
411
|
+
errors.push(msg);
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
if (executionUnitsReset > 0) {
|
|
416
|
+
console.log(`[crash-recovery] Reset ${executionUnitsReset} orphaned execution unit(s) to idle`);
|
|
417
|
+
}
|
|
418
|
+
else {
|
|
419
|
+
console.log('[crash-recovery] No orphaned execution units found');
|
|
420
|
+
}
|
|
421
|
+
return { executionUnitsReset, actions, errors };
|
|
422
|
+
}
|
|
423
|
+
// ============================================================================
|
|
424
|
+
// Main recovery orchestrator
|
|
425
|
+
// ============================================================================
|
|
426
|
+
/**
|
|
427
|
+
* Run full crash recovery on factory orchestrator startup.
|
|
428
|
+
*
|
|
429
|
+
* Should be called after config init and API client setup, but before
|
|
430
|
+
* the poll loop starts. Safe to call multiple times -- idempotent by
|
|
431
|
+
* design. A second run with no crashed state finds nothing to recover.
|
|
432
|
+
*
|
|
433
|
+
* Recovery steps:
|
|
434
|
+
* 1. Scan for orphaned worktrees and WIP-commit uncommitted changes.
|
|
435
|
+
* 2. Reset factory_sessions stuck in 'running' to 'failed'.
|
|
436
|
+
* 3. Reset factory_instances stuck in 'building'/'gating' to 'paused'.
|
|
437
|
+
* 4. Create escalations for paused instances.
|
|
438
|
+
*
|
|
439
|
+
* @param config - Factory configuration.
|
|
440
|
+
* @returns A RecoveryResult summarizing all actions taken.
|
|
441
|
+
*/
|
|
442
|
+
export async function recoverFromCrash(config) {
|
|
443
|
+
console.log('[crash-recovery] Starting factory crash recovery scan...');
|
|
444
|
+
const result = {
|
|
445
|
+
orphanedWorktreesFound: 0,
|
|
446
|
+
wipCommitsCreated: 0,
|
|
447
|
+
sessionsReset: 0,
|
|
448
|
+
instancesPaused: 0,
|
|
449
|
+
escalationsCreated: 0,
|
|
450
|
+
executionUnitsReset: 0,
|
|
451
|
+
actions: [],
|
|
452
|
+
errors: [],
|
|
453
|
+
};
|
|
454
|
+
// Step 1: Scan for orphaned worktrees and WIP-commit uncommitted changes
|
|
455
|
+
try {
|
|
456
|
+
const orphans = scanFactoryWorktrees(config);
|
|
457
|
+
result.orphanedWorktreesFound = orphans.length;
|
|
458
|
+
if (orphans.length > 0) {
|
|
459
|
+
console.log(`[crash-recovery] Found ${orphans.length} worktree(s) across factory-worktrees directories`);
|
|
460
|
+
const worktreeResult = recoverOrphanedWorktrees(orphans);
|
|
461
|
+
result.wipCommitsCreated = worktreeResult.wipCommitsCreated;
|
|
462
|
+
result.actions.push(...worktreeResult.actions);
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
catch (err) {
|
|
466
|
+
const msg = `Failed during worktree scan: ${err instanceof Error ? err.message : String(err)}`;
|
|
467
|
+
console.error(`[crash-recovery] ${msg}`);
|
|
468
|
+
result.errors.push(msg);
|
|
469
|
+
}
|
|
470
|
+
// Step 2: Reset stale sessions (catch-all for sessions not tied to instances)
|
|
471
|
+
try {
|
|
472
|
+
const sessionResult = await recoverStaleSessions(config);
|
|
473
|
+
result.sessionsReset += sessionResult.sessionsReset;
|
|
474
|
+
result.actions.push(...sessionResult.actions);
|
|
475
|
+
result.errors.push(...sessionResult.errors);
|
|
476
|
+
}
|
|
477
|
+
catch (err) {
|
|
478
|
+
const msg = `Failed during stale session recovery: ${err instanceof Error ? err.message : String(err)}`;
|
|
479
|
+
console.error(`[crash-recovery] ${msg}`);
|
|
480
|
+
result.errors.push(msg);
|
|
481
|
+
}
|
|
482
|
+
// Step 3: Reset orphaned execution units (assigned/running -> idle)
|
|
483
|
+
// This runs BEFORE instance adoption so the scheduler sees clean state.
|
|
484
|
+
try {
|
|
485
|
+
const euResult = await recoverOrphanedExecutionUnits(config);
|
|
486
|
+
result.executionUnitsReset = euResult.executionUnitsReset;
|
|
487
|
+
result.actions.push(...euResult.actions);
|
|
488
|
+
result.errors.push(...euResult.errors);
|
|
489
|
+
}
|
|
490
|
+
catch (err) {
|
|
491
|
+
const msg = `Failed during execution unit recovery: ${err instanceof Error ? err.message : String(err)}`;
|
|
492
|
+
console.error(`[crash-recovery] ${msg}`);
|
|
493
|
+
result.errors.push(msg);
|
|
494
|
+
}
|
|
495
|
+
// Step 4: Reset stuck instances (building/gating -> paused) and their sessions
|
|
496
|
+
try {
|
|
497
|
+
const instanceResult = await recoverStuckInstances(config);
|
|
498
|
+
result.instancesPaused = instanceResult.instancesPaused;
|
|
499
|
+
result.sessionsReset += instanceResult.sessionsReset;
|
|
500
|
+
result.escalationsCreated = instanceResult.escalationsCreated;
|
|
501
|
+
result.actions.push(...instanceResult.actions);
|
|
502
|
+
result.errors.push(...instanceResult.errors);
|
|
503
|
+
}
|
|
504
|
+
catch (err) {
|
|
505
|
+
const msg = `Failed during stuck instance recovery: ${err instanceof Error ? err.message : String(err)}`;
|
|
506
|
+
console.error(`[crash-recovery] ${msg}`);
|
|
507
|
+
result.errors.push(msg);
|
|
508
|
+
}
|
|
509
|
+
// Summary
|
|
510
|
+
console.log(`[crash-recovery] Factory recovery complete: ` +
|
|
511
|
+
`${result.orphanedWorktreesFound} worktree(s) found, ` +
|
|
512
|
+
`${result.wipCommitsCreated} WIP commit(s), ` +
|
|
513
|
+
`${result.sessionsReset} session(s) reset, ` +
|
|
514
|
+
`${result.executionUnitsReset} execution unit(s) reset, ` +
|
|
515
|
+
`${result.instancesPaused} instance(s) paused, ` +
|
|
516
|
+
`${result.escalationsCreated} escalation(s) created`);
|
|
517
|
+
if (result.errors.length > 0) {
|
|
518
|
+
console.warn(`[crash-recovery] ${result.errors.length} non-fatal error(s) during recovery`);
|
|
519
|
+
}
|
|
520
|
+
return result;
|
|
521
|
+
}
|
|
522
|
+
//# sourceMappingURL=crash-recovery.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"crash-recovery.js","sourceRoot":"","sources":["../src/crash-recovery.ts"],"names":[],"mappings":"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"}
|