@eldrforge/tree-execution 0.1.1 โ†’ 0.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/README.md +1507 -63
  2. package/package.json +8 -8
package/README.md CHANGED
@@ -1,18 +1,114 @@
1
1
  # @eldrforge/tree-execution
2
2
 
3
- Parallel execution framework and tree orchestration for monorepo workflows.
3
+ A sophisticated parallel execution framework designed for orchestrating complex dependency-aware workflows in monorepo environments. Execute tasks across multiple packages with intelligent scheduling, automatic error recovery, and checkpoint/resume capabilities.
4
4
 
5
- ## Features
5
+ ## Table of Contents
6
6
 
7
- - ๐Ÿš€ **Parallel Execution** - Run tasks concurrently with dependency awareness
8
- - ๐Ÿ”„ **Smart Scheduling** - Priority-based task scheduling
9
- - ๐Ÿ’พ **Checkpoint/Resume** - Save and restore execution state
10
- - ๐Ÿ›ก๏ธ **Error Recovery** - Sophisticated error handling and rollback
11
- - ๐Ÿ“Š **Progress Tracking** - Real-time execution progress
12
- - ๐ŸŽฏ **Resource Management** - CPU and memory-aware execution
13
- - โšก **Retry Logic** - Exponential backoff for transient failures
14
- - ๐ŸŽญ **Dependency Injection** - Flexible command integration
15
- - ๐Ÿงช **Fully Tested** - 140+ tests with excellent coverage
7
+ - [Overview](#overview)
8
+ - [Key Features](#key-features)
9
+ - [Installation](#installation)
10
+ - [Core Concepts](#core-concepts)
11
+ - [Quick Start](#quick-start)
12
+ - [API Reference](#api-reference)
13
+ - [Advanced Usage](#advanced-usage)
14
+ - [Configuration](#configuration)
15
+ - [Error Handling & Recovery](#error-handling--recovery)
16
+ - [Real-World Examples](#real-world-examples)
17
+ - [Testing](#testing)
18
+ - [Architecture](#architecture)
19
+ - [Contributing](#contributing)
20
+ - [License](#license)
21
+
22
+ ## Overview
23
+
24
+ `@eldrforge/tree-execution` provides a robust framework for executing tasks across interdependent packages in a monorepo. It handles:
25
+
26
+ - **Dependency-aware scheduling**: Automatically determines execution order based on package dependencies
27
+ - **Parallel execution**: Runs independent packages concurrently while respecting dependencies
28
+ - **Checkpoint/resume**: Save execution state and resume from where you left off
29
+ - **Error recovery**: Sophisticated retry logic with exponential backoff
30
+ - **Resource management**: CPU and memory-aware concurrency control
31
+ - **Progress tracking**: Real-time execution monitoring with detailed metrics
32
+
33
+ Originally developed as part of the kodrdriv toolkit, this library has been extracted for standalone use in any monorepo workflow.
34
+
35
+ ## Key Features
36
+
37
+ ### ๐Ÿš€ Intelligent Parallel Execution
38
+ Execute tasks across packages with automatic dependency resolution and optimal concurrency:
39
+
40
+ ```typescript
41
+ import { createTreeExecutor } from '@eldrforge/tree-execution';
42
+ import { buildDependencyGraph } from '@eldrforge/tree-core';
43
+
44
+ const graph = await buildDependencyGraph(['packages/*/package.json']);
45
+ const executor = createTreeExecutor();
46
+ await executor.execute({
47
+ tree: {
48
+ directories: ['packages'],
49
+ cmd: 'npm test',
50
+ parallel: true,
51
+ maxConcurrency: 4
52
+ }
53
+ });
54
+ ```
55
+
56
+ ### ๐Ÿ’พ Checkpoint & Resume
57
+ Save execution state to resume long-running operations:
58
+
59
+ ```typescript
60
+ import { DynamicTaskPool } from '@eldrforge/tree-execution';
61
+
62
+ const pool = new DynamicTaskPool({
63
+ graph,
64
+ maxConcurrency: 4,
65
+ command: 'npm publish',
66
+ config: runConfig,
67
+ checkpointPath: './checkpoints/publish.json'
68
+ });
69
+
70
+ // First run - saves checkpoints automatically
71
+ await pool.execute();
72
+
73
+ // Resume after interruption
74
+ await pool.execute({ continue: true });
75
+ ```
76
+
77
+ ### ๐Ÿ›ก๏ธ Sophisticated Error Recovery
78
+ Automatic retry with exponential backoff and smart error classification:
79
+
80
+ ```typescript
81
+ const result = await executor.execute({
82
+ tree: {
83
+ cmd: 'npm test',
84
+ parallel: true,
85
+ retry: {
86
+ maxAttempts: 3,
87
+ initialDelayMs: 1000,
88
+ maxDelayMs: 10000,
89
+ backoffMultiplier: 2,
90
+ retriableErrors: ['ECONNRESET', 'ETIMEDOUT']
91
+ }
92
+ }
93
+ });
94
+ ```
95
+
96
+ ### ๐Ÿ“Š Real-time Progress Tracking
97
+ Monitor execution with detailed metrics:
98
+
99
+ ```typescript
100
+ import { createParallelProgressLogger } from '@eldrforge/tree-execution';
101
+
102
+ const logger = createParallelProgressLogger(totalPackages);
103
+
104
+ pool.on('package:started', ({ packageName }) => {
105
+ logger.onPackageStarted(packageName);
106
+ });
107
+
108
+ pool.on('package:completed', ({ packageName, result }) => {
109
+ logger.onPackageCompleted(packageName, result);
110
+ });
111
+ ```
16
112
 
17
113
  ## Installation
18
114
 
@@ -20,111 +116,1459 @@ Parallel execution framework and tree orchestration for monorepo workflows.
20
116
  npm install @eldrforge/tree-execution
21
117
  ```
22
118
 
23
- ## Usage
119
+ ### Peer Dependencies
120
+
121
+ ```bash
122
+ npm install @eldrforge/tree-core @eldrforge/git-tools @eldrforge/shared
123
+ ```
124
+
125
+ ## Core Concepts
126
+
127
+ ### Dependency Graph
24
128
 
25
- ### Quick Start - TreeExecutor (Recommended)
129
+ The foundation of execution is a dependency graph built from package.json files:
130
+
131
+ ```typescript
132
+ import { buildDependencyGraph } from '@eldrforge/tree-core';
133
+
134
+ // Scan for packages
135
+ const graph = await buildDependencyGraph([
136
+ 'packages/*/package.json',
137
+ 'apps/*/package.json'
138
+ ]);
139
+
140
+ // Graph contains:
141
+ // - packages: Map<string, PackageInfo>
142
+ // - dependencies: Map<string, Set<string>>
143
+ // - dependents: Map<string, Set<string>>
144
+ ```
145
+
146
+ ### Execution State
147
+
148
+ The system tracks package states throughout execution:
149
+
150
+ ```typescript
151
+ interface ExecutionState {
152
+ pending: string[]; // Not yet started
153
+ ready: string[]; // Dependencies met, ready to run
154
+ running: RunningPackageSnapshot[]; // Currently executing
155
+ completed: string[]; // Successfully completed
156
+ failed: FailedPackageSnapshot[]; // Failed execution
157
+ skipped: string[]; // Skipped due to failed dependencies
158
+ skippedNoChanges: string[]; // Skipped (no code changes detected)
159
+ }
160
+ ```
161
+
162
+ ### TreeExecutor vs DynamicTaskPool
163
+
164
+ **TreeExecutor** (Recommended):
165
+ - High-level, class-based API
166
+ - Dependency injection for custom commands
167
+ - State management and thread safety built-in
168
+ - Ideal for applications integrating tree execution
169
+
170
+ **DynamicTaskPool** (Advanced):
171
+ - Low-level execution engine
172
+ - Direct control over task scheduling
173
+ - Fine-grained event handling
174
+ - Ideal for custom execution frameworks
175
+
176
+ ## Quick Start
177
+
178
+ ### Basic Usage with TreeExecutor
26
179
 
27
180
  ```typescript
28
181
  import { createTreeExecutor } from '@eldrforge/tree-execution';
29
182
 
30
- // Create executor with custom commands
183
+ // Create executor
31
184
  const executor = createTreeExecutor({
32
185
  commands: {
33
- commit: myCommitCommand,
34
- publish: myPublishCommand
186
+ // Optional: inject custom commands
187
+ commit: myCommitHandler,
188
+ publish: myPublishHandler
35
189
  }
36
190
  });
37
191
 
38
- // Execute tree command
39
- const result = await executor.execute(config);
192
+ // Execute a command across all packages
193
+ const result = await executor.execute({
194
+ tree: {
195
+ directories: ['packages'],
196
+ cmd: 'npm run build',
197
+ parallel: true,
198
+ maxConcurrency: 4
199
+ }
200
+ });
201
+
202
+ console.log(`Completed: ${result.completed.length}`);
203
+ console.log(`Failed: ${result.failed.length}`);
40
204
  ```
41
205
 
42
- ### Advanced - DynamicTaskPool
206
+ ### Advanced Usage with DynamicTaskPool
43
207
 
44
208
  ```typescript
45
209
  import { DynamicTaskPool } from '@eldrforge/tree-execution';
46
210
  import { buildDependencyGraph } from '@eldrforge/tree-core';
47
211
 
48
212
  // Build dependency graph
49
- const graph = await buildDependencyGraph(packagePaths);
213
+ const graph = await buildDependencyGraph(['packages/*/package.json']);
50
214
 
51
- // Create execution pool
215
+ // Create pool
52
216
  const pool = new DynamicTaskPool({
53
- graph,
54
- maxConcurrency: 4,
55
- command: 'npm test',
56
- config: runConfig
217
+ graph,
218
+ maxConcurrency: 4,
219
+ command: 'npm test',
220
+ config: {
221
+ tree: { parallel: true }
222
+ },
223
+ checkpointPath: './checkpoints',
224
+ maxRetries: 3,
225
+ initialRetryDelay: 1000
226
+ });
227
+
228
+ // Listen to events
229
+ pool.on('execution:started', ({ totalPackages }) => {
230
+ console.log(`Starting execution of ${totalPackages} packages`);
231
+ });
232
+
233
+ pool.on('package:started', ({ packageName }) => {
234
+ console.log(`Started: ${packageName}`);
235
+ });
236
+
237
+ pool.on('package:completed', ({ packageName, result }) => {
238
+ console.log(`Completed: ${packageName} in ${result.duration}ms`);
239
+ });
240
+
241
+ pool.on('package:failed', ({ packageName, error, retriable }) => {
242
+ console.error(`Failed: ${packageName}`, error);
57
243
  });
58
244
 
59
- // Execute with parallel coordination
245
+ // Execute
60
246
  const result = await pool.execute();
61
247
 
62
- console.log(`Completed: ${result.completed.length}`);
63
- console.log(`Failed: ${result.failed.length}`);
248
+ // Check results
249
+ if (result.success) {
250
+ console.log('All packages completed successfully');
251
+ console.log(`Total time: ${result.metrics.totalDuration}ms`);
252
+ console.log(`Average concurrency: ${result.metrics.averageConcurrency}`);
253
+ } else {
254
+ console.error(`${result.failed.length} packages failed`);
255
+ result.failed.forEach(f => {
256
+ console.error(`- ${f.name}: ${f.error}`);
257
+ });
258
+ }
259
+ ```
260
+
261
+ ## API Reference
262
+
263
+ ### TreeExecutor
264
+
265
+ High-level orchestration class with dependency injection.
266
+
267
+ #### Constructor
268
+
269
+ ```typescript
270
+ constructor(options?: TreeExecutorOptions)
271
+
272
+ interface TreeExecutorOptions {
273
+ commands?: CommandRegistry; // Custom command handlers
274
+ logger?: Logger; // Custom logger instance
275
+ }
276
+
277
+ interface CommandRegistry {
278
+ updates?: CommandExecutor;
279
+ commit?: CommandExecutor;
280
+ link?: CommandExecutor;
281
+ unlink?: CommandExecutor;
282
+ }
283
+
284
+ interface CommandExecutor {
285
+ execute(config: TreeExecutionConfig, mode?: string): Promise<any>;
286
+ }
287
+ ```
288
+
289
+ #### Methods
290
+
291
+ ```typescript
292
+ // Execute tree command
293
+ async execute(config: TreeExecutionConfig): Promise<string>
294
+
295
+ // Get published versions (thread-safe)
296
+ async getPublishedVersions(): Promise<PublishedVersion[]>
297
+
298
+ // Add published version (thread-safe)
299
+ async addPublishedVersion(version: PublishedVersion): Promise<void>
300
+
301
+ // Get execution context (thread-safe)
302
+ async getExecutionContext(): Promise<TreeExecutionContext | null>
303
+
304
+ // Set execution context (thread-safe)
305
+ async setExecutionContext(context: TreeExecutionContext | null): Promise<void>
306
+
307
+ // Reset state (for testing)
308
+ async reset(): Promise<void>
309
+
310
+ // Get/set command executors
311
+ getCommand(name: keyof CommandRegistry): CommandExecutor | undefined
312
+ setCommand(name: keyof CommandRegistry, executor: CommandExecutor): void
313
+ ```
314
+
315
+ #### Factory Function
316
+
317
+ ```typescript
318
+ import { createTreeExecutor } from '@eldrforge/tree-execution';
319
+
320
+ const executor = createTreeExecutor({
321
+ commands: {
322
+ commit: myCommitHandler
323
+ }
324
+ });
325
+ ```
326
+
327
+ ### DynamicTaskPool
328
+
329
+ Low-level parallel execution engine.
330
+
331
+ #### Constructor
332
+
333
+ ```typescript
334
+ constructor(config: PoolConfig)
335
+
336
+ interface PoolConfig {
337
+ graph: DependencyGraph; // Dependency graph from @eldrforge/tree-core
338
+ maxConcurrency: number; // Maximum parallel tasks
339
+ command: string; // Command to execute
340
+ config: TreeExecutionConfig; // Execution configuration
341
+ checkpointPath?: string; // Path for checkpoint files
342
+ continue?: boolean; // Resume from checkpoint
343
+ maxRetries?: number; // Max retry attempts (default: 3)
344
+ initialRetryDelay?: number; // Initial retry delay ms (default: 1000)
345
+ maxRetryDelay?: number; // Max retry delay ms (default: 10000)
346
+ backoffMultiplier?: number; // Backoff multiplier (default: 2)
347
+ }
348
+ ```
349
+
350
+ #### Methods
351
+
352
+ ```typescript
353
+ // Execute all packages
354
+ async execute(): Promise<ExecutionResult>
355
+
356
+ // Abort execution
357
+ async abort(reason?: string): Promise<void>
358
+
359
+ // Get current checkpoint
360
+ async getCheckpoint(): Promise<ParallelExecutionCheckpoint>
361
+
362
+ // Load checkpoint and resume
363
+ private async loadCheckpoint(): Promise<void>
364
+
365
+ // Save checkpoint
366
+ private async saveCheckpoint(): Promise<void>
367
+ ```
368
+
369
+ #### Events
370
+
371
+ ```typescript
372
+ // Execution lifecycle
373
+ pool.on('execution:started', ({ totalPackages }) => { });
374
+ pool.on('execution:completed', (result: ExecutionResult) => { });
375
+ pool.on('execution:failed', (error: Error) => { });
376
+ pool.on('execution:aborted', ({ reason }) => { });
377
+
378
+ // Package lifecycle
379
+ pool.on('package:started', ({ packageName, attemptNumber }) => { });
380
+ pool.on('package:completed', ({ packageName, result }) => { });
381
+ pool.on('package:failed', ({ packageName, error, retriable, attemptNumber }) => { });
382
+ pool.on('package:retry', ({ packageName, attemptNumber, delayMs, error }) => { });
383
+ pool.on('package:skipped', ({ packageName, reason }) => { });
384
+
385
+ // Progress tracking
386
+ pool.on('progress:update', ({ completed, total, percentage }) => { });
387
+ pool.on('concurrency:changed', ({ active, available }) => { });
388
+
389
+ // Checkpointing
390
+ pool.on('checkpoint:saved', ({ path, packages }) => { });
391
+ pool.on('checkpoint:loaded', ({ path, resumePoint }) => { });
392
+ ```
393
+
394
+ ### Helper Functions
395
+
396
+ #### TreeExecutionAdapter
397
+
398
+ Bridges DynamicTaskPool with custom execution functions:
399
+
400
+ ```typescript
401
+ import { TreeExecutionAdapter, ExecutePackageFunction } from '@eldrforge/tree-execution';
402
+
403
+ const executePackage: ExecutePackageFunction = async (
404
+ packageName,
405
+ packageInfo,
406
+ command,
407
+ config,
408
+ isDryRun,
409
+ index,
410
+ total,
411
+ allPackageNames,
412
+ isBuiltInCommand
413
+ ) => {
414
+ // Custom execution logic
415
+ return { success: true };
416
+ };
417
+
418
+ const adapter = new TreeExecutionAdapter(poolConfig, executePackage);
419
+ const result = await adapter.execute();
420
+ ```
421
+
422
+ #### Progress Logger
423
+
424
+ ```typescript
425
+ import { createParallelProgressLogger } from '@eldrforge/tree-execution';
426
+
427
+ const logger = createParallelProgressLogger(totalPackages);
428
+
429
+ pool.on('package:started', ({ packageName }) => {
430
+ logger.onPackageStarted(packageName);
431
+ });
432
+
433
+ pool.on('package:completed', ({ packageName, result }) => {
434
+ logger.onPackageCompleted(packageName, result);
435
+ });
436
+
437
+ pool.on('package:failed', ({ packageName, error }) => {
438
+ logger.onPackageFailed(packageName, error);
439
+ });
64
440
  ```
65
441
 
66
- ### Custom Logger
442
+ #### Result Formatter
443
+
444
+ ```typescript
445
+ import { formatParallelResult } from '@eldrforge/tree-execution';
446
+
447
+ const result = await pool.execute();
448
+ const formatted = formatParallelResult(result);
449
+ console.log(formatted); // Human-readable summary
450
+ ```
451
+
452
+ ### Component APIs
453
+
454
+ #### CheckpointManager
455
+
456
+ Manages execution state persistence:
457
+
458
+ ```typescript
459
+ import { CheckpointManager } from '@eldrforge/tree-execution';
460
+
461
+ const manager = new CheckpointManager('./checkpoints');
462
+
463
+ // Save checkpoint
464
+ await manager.saveCheckpoint(executionState);
465
+
466
+ // Load latest checkpoint
467
+ const checkpoint = await manager.loadLatestCheckpoint();
468
+
469
+ // List all checkpoints
470
+ const checkpoints = await manager.listCheckpoints();
471
+
472
+ // Clean old checkpoints
473
+ await manager.cleanOldCheckpoints(maxAge);
474
+ ```
475
+
476
+ #### RecoveryManager
477
+
478
+ Handles error recovery and state validation:
479
+
480
+ ```typescript
481
+ import { RecoveryManager, loadRecoveryManager } from '@eldrforge/tree-execution';
482
+
483
+ // Load from checkpoint
484
+ const manager = await loadRecoveryManager('./checkpoint.json');
485
+
486
+ // Validate state
487
+ const validation = await manager.validateState();
488
+ if (!validation.isValid) {
489
+ console.error('Invalid state:', validation.errors);
490
+ }
491
+
492
+ // Get recovery hints
493
+ const hints = manager.getRecoveryHints();
494
+ hints.forEach(hint => {
495
+ console.log(`[${hint.type}] ${hint.message}`);
496
+ if (hint.suggestedCommand) {
497
+ console.log(` Run: ${hint.suggestedCommand}`);
498
+ }
499
+ });
500
+
501
+ // Apply recovery options
502
+ await manager.applyRecoveryOptions({
503
+ skipPackages: ['pkg1'],
504
+ retryFailed: true
505
+ });
506
+
507
+ // Resume execution
508
+ const resumeConfig = await manager.getResumeConfig();
509
+ ```
510
+
511
+ #### Scheduler
512
+
513
+ Determines execution order based on dependencies:
514
+
515
+ ```typescript
516
+ import { Scheduler } from '@eldrforge/tree-execution';
517
+
518
+ const scheduler = new Scheduler(graph, dependencyChecker);
519
+
520
+ // Get next packages to execute
521
+ const next = scheduler.getNextPackages(
522
+ state,
523
+ resourceMonitor,
524
+ retryAttempts
525
+ );
526
+
527
+ // Check if package can run
528
+ const canRun = scheduler.canExecute(packageName, state);
529
+ ```
530
+
531
+ #### ResourceMonitor
532
+
533
+ Tracks available execution slots:
67
534
 
68
535
  ```typescript
69
- import { setLogger } from '@eldrforge/tree-execution';
536
+ import { ResourceMonitor } from '@eldrforge/tree-execution';
70
537
 
538
+ const monitor = new ResourceMonitor(maxConcurrency);
539
+
540
+ // Acquire slot
541
+ const success = monitor.acquire();
542
+
543
+ // Release slot
544
+ monitor.release();
545
+
546
+ // Check availability
547
+ if (monitor.isAvailable()) {
548
+ // Can start more tasks
549
+ }
550
+
551
+ // Get metrics
552
+ const metrics = monitor.getMetrics();
553
+ console.log(`Active: ${metrics.activeCount}, Available: ${metrics.availableSlots}`);
554
+ ```
555
+
556
+ #### DependencyChecker
557
+
558
+ Verifies package dependencies:
559
+
560
+ ```typescript
561
+ import { DependencyChecker } from '@eldrforge/tree-execution';
562
+
563
+ const checker = new DependencyChecker(graph);
564
+
565
+ // Check if package is ready
566
+ const ready = checker.areAllDependenciesCompleted(packageName, state);
567
+
568
+ // Check if package can run (dependencies not failed)
569
+ const canRun = checker.canPackageRun(packageName, state);
570
+ ```
571
+
572
+ #### CommandValidator
573
+
574
+ Validates commands for parallel execution:
575
+
576
+ ```typescript
577
+ import { CommandValidator } from '@eldrforge/tree-execution';
578
+
579
+ const validator = new CommandValidator();
580
+
581
+ // Validate command
582
+ const result = validator.validate('npm test', config);
583
+ if (!result.isValid) {
584
+ console.error('Validation failed:', result.errors);
585
+ result.warnings.forEach(w => console.warn(w));
586
+ }
587
+
588
+ // Check if command is safe for parallel execution
589
+ const isSafe = validator.isSafeForParallel('npm run build');
590
+ ```
591
+
592
+ ### Logger Integration
593
+
594
+ ```typescript
595
+ import { setLogger, getLogger } from '@eldrforge/tree-execution';
596
+
597
+ // Set custom logger
71
598
  setLogger({
72
- info: (...args) => myLogger.info(...args),
73
- error: (...args) => myLogger.error(...args),
74
- warn: (...args) => myLogger.warn(...args),
75
- verbose: (...args) => myLogger.verbose(...args),
76
- debug: (...args) => myLogger.debug(...args),
77
- silly: (...args) => myLogger.silly(...args)
599
+ info: (...args) => console.log('[INFO]', ...args),
600
+ error: (...args) => console.error('[ERROR]', ...args),
601
+ warn: (...args) => console.warn('[WARN]', ...args),
602
+ verbose: (...args) => console.log('[VERBOSE]', ...args),
603
+ debug: (...args) => console.log('[DEBUG]', ...args),
604
+ silly: (...args) => console.log('[SILLY]', ...args)
78
605
  });
606
+
607
+ // Get logger
608
+ const logger = getLogger();
609
+ logger.info('Execution started');
79
610
  ```
80
611
 
81
- ## Components
612
+ ## Advanced Usage
82
613
 
83
- ### TreeExecutor (New!)
84
- High-level class-based API with dependency injection. Encapsulates all state management and provides clean integration points.
614
+ ### Custom Command Integration
85
615
 
86
- ### DynamicTaskPool
87
- Orchestrates parallel execution with dependency awareness.
616
+ Integrate your own command handlers:
617
+
618
+ ```typescript
619
+ import { createTreeExecutor, CommandExecutor } from '@eldrforge/tree-execution';
620
+
621
+ // Define custom command
622
+ class MyTestCommand implements CommandExecutor {
623
+ async execute(config: TreeExecutionConfig, mode?: string) {
624
+ // Custom test logic
625
+ console.log('Running tests with custom logic');
626
+ return { success: true };
627
+ }
628
+ }
629
+
630
+ // Register command
631
+ const executor = createTreeExecutor({
632
+ commands: {
633
+ commit: new MyTestCommand()
634
+ }
635
+ });
636
+
637
+ // Execute
638
+ await executor.execute({
639
+ tree: {
640
+ directories: ['packages'],
641
+ builtInCommand: 'commit'
642
+ }
643
+ });
644
+ ```
645
+
646
+ ### Conditional Package Execution
647
+
648
+ Execute only packages matching certain criteria:
649
+
650
+ ```typescript
651
+ import { buildDependencyGraph } from '@eldrforge/tree-core';
652
+
653
+ // Build graph with exclusions
654
+ const graph = await buildDependencyGraph(
655
+ ['packages/*/package.json'],
656
+ ['node_modules/**', '**/dist/**']
657
+ );
658
+
659
+ // Filter packages
660
+ const filteredGraph = {
661
+ ...graph,
662
+ packages: new Map(
663
+ Array.from(graph.packages.entries())
664
+ .filter(([name, info]) => {
665
+ // Only include packages with tests
666
+ return info.scripts?.test !== undefined;
667
+ })
668
+ )
669
+ };
670
+
671
+ // Execute on filtered graph
672
+ const pool = new DynamicTaskPool({
673
+ graph: filteredGraph,
674
+ maxConcurrency: 4,
675
+ command: 'npm test',
676
+ config: {}
677
+ });
678
+
679
+ await pool.execute();
680
+ ```
681
+
682
+ ### Incremental Execution
683
+
684
+ Execute only packages with changes since last run:
685
+
686
+ ```typescript
687
+ import { getGitStatusSummary } from '@eldrforge/git-tools';
688
+ import { findAllDependents } from '@eldrforge/tree-core';
689
+
690
+ // Get changed packages
691
+ const status = await getGitStatusSummary();
692
+ const changedFiles = [...status.staged, ...status.modified];
693
+ const changedPackages = new Set<string>();
694
+
695
+ changedFiles.forEach(file => {
696
+ const match = file.match(/packages\/([^\/]+)\//);
697
+ if (match) {
698
+ changedPackages.add(match[1]);
699
+ }
700
+ });
701
+
702
+ // Include all dependents of changed packages
703
+ const affectedPackages = new Set<string>();
704
+ changedPackages.forEach(pkg => {
705
+ affectedPackages.add(pkg);
706
+ const dependents = findAllDependents(graph, pkg);
707
+ dependents.forEach(dep => affectedPackages.add(dep));
708
+ });
88
709
 
89
- ### RecoveryManager
90
- Handles error recovery, rollback, and state validation.
710
+ // Execute only affected packages
711
+ const incrementalGraph = {
712
+ ...graph,
713
+ packages: new Map(
714
+ Array.from(graph.packages.entries())
715
+ .filter(([name]) => affectedPackages.has(name))
716
+ )
717
+ };
91
718
 
92
- ### Scheduler
93
- Decides which packages to execute next based on dependencies and resources.
719
+ const pool = new DynamicTaskPool({
720
+ graph: incrementalGraph,
721
+ maxConcurrency: 4,
722
+ command: 'npm run build',
723
+ config: {}
724
+ });
725
+
726
+ await pool.execute();
727
+ ```
728
+
729
+ ### Progress Dashboard
730
+
731
+ Build a real-time progress dashboard:
732
+
733
+ ```typescript
734
+ import { DynamicTaskPool } from '@eldrforge/tree-execution';
735
+
736
+ const pool = new DynamicTaskPool(config);
737
+
738
+ // Track state
739
+ const state = {
740
+ total: 0,
741
+ completed: 0,
742
+ failed: 0,
743
+ running: new Set<string>()
744
+ };
745
+
746
+ pool.on('execution:started', ({ totalPackages }) => {
747
+ state.total = totalPackages;
748
+ updateDashboard();
749
+ });
750
+
751
+ pool.on('package:started', ({ packageName }) => {
752
+ state.running.add(packageName);
753
+ updateDashboard();
754
+ });
755
+
756
+ pool.on('package:completed', ({ packageName }) => {
757
+ state.running.delete(packageName);
758
+ state.completed++;
759
+ updateDashboard();
760
+ });
761
+
762
+ pool.on('package:failed', ({ packageName }) => {
763
+ state.running.delete(packageName);
764
+ state.failed++;
765
+ updateDashboard();
766
+ });
767
+
768
+ function updateDashboard() {
769
+ console.clear();
770
+ console.log('=== Execution Dashboard ===');
771
+ console.log(`Total: ${state.total}`);
772
+ console.log(`Completed: ${state.completed}`);
773
+ console.log(`Failed: ${state.failed}`);
774
+ console.log(`Running: ${state.running.size}`);
775
+ console.log(`Progress: ${((state.completed + state.failed) / state.total * 100).toFixed(1)}%`);
776
+ console.log('\nCurrently Running:');
777
+ state.running.forEach(pkg => console.log(` - ${pkg}`));
778
+ }
779
+
780
+ await pool.execute();
781
+ ```
782
+
783
+ ### Custom Retry Logic
784
+
785
+ Implement sophisticated retry strategies:
786
+
787
+ ```typescript
788
+ const pool = new DynamicTaskPool({
789
+ graph,
790
+ maxConcurrency: 4,
791
+ command: 'npm test',
792
+ config: {
793
+ tree: {
794
+ retry: {
795
+ maxAttempts: 5,
796
+ initialDelayMs: 500,
797
+ maxDelayMs: 30000,
798
+ backoffMultiplier: 2.5,
799
+ retriableErrors: [
800
+ 'ECONNRESET',
801
+ 'ETIMEDOUT',
802
+ 'ENOTFOUND',
803
+ 'Test failed: flaky_test'
804
+ ]
805
+ }
806
+ }
807
+ }
808
+ });
809
+
810
+ pool.on('package:retry', ({ packageName, attemptNumber, delayMs, error }) => {
811
+ console.log(`Retrying ${packageName} (attempt ${attemptNumber}) after ${delayMs}ms`);
812
+ console.log(`Reason: ${error.message}`);
813
+ });
814
+
815
+ await pool.execute();
816
+ ```
817
+
818
+ ### Recovery Workflow
819
+
820
+ Implement a complete recovery workflow:
821
+
822
+ ```typescript
823
+ import { loadRecoveryManager } from '@eldrforge/tree-execution';
824
+
825
+ async function recoverExecution(checkpointPath: string) {
826
+ // Load recovery manager
827
+ const recovery = await loadRecoveryManager(checkpointPath);
828
+
829
+ // Validate state
830
+ const validation = await recovery.validateState();
831
+ if (!validation.isValid) {
832
+ console.error('State validation failed:');
833
+ validation.errors.forEach(err => console.error(` - ${err}`));
834
+
835
+ // Apply fixes
836
+ console.log('\nApplying recovery options...');
837
+ await recovery.applyRecoveryOptions({
838
+ skipPackages: validation.suggestedSkips || [],
839
+ retryFailed: true
840
+ });
841
+ }
842
+
843
+ // Show recovery hints
844
+ const hints = recovery.getRecoveryHints();
845
+ if (hints.length > 0) {
846
+ console.log('\nRecovery Hints:');
847
+ hints.forEach(hint => {
848
+ console.log(`[${hint.type.toUpperCase()}] ${hint.message}`);
849
+ if (hint.suggestedCommand) {
850
+ console.log(` Suggested: ${hint.suggestedCommand}`);
851
+ }
852
+ });
853
+ }
854
+
855
+ // Get resume configuration
856
+ const resumeConfig = await recovery.getResumeConfig();
857
+
858
+ // Resume execution
859
+ const pool = new DynamicTaskPool({
860
+ ...resumeConfig,
861
+ continue: true
862
+ });
863
+
864
+ return await pool.execute();
865
+ }
866
+
867
+ // Use it
868
+ try {
869
+ const result = await recoverExecution('./checkpoints/publish.json');
870
+ console.log('Recovery successful!');
871
+ } catch (error) {
872
+ console.error('Recovery failed:', error);
873
+ }
874
+ ```
875
+
876
+ ## Configuration
877
+
878
+ ### TreeExecutionConfig
879
+
880
+ Complete configuration interface:
881
+
882
+ ```typescript
883
+ interface TreeExecutionConfig {
884
+ // Basic flags
885
+ dryRun?: boolean;
886
+ verbose?: boolean;
887
+ debug?: boolean;
888
+
889
+ // Tree-specific configuration
890
+ tree?: {
891
+ // Execution
892
+ directories?: string[]; // Directories to scan for packages
893
+ exclude?: string[]; // Patterns to exclude
894
+ cmd?: string; // Command to execute
895
+ builtInCommand?: string; // Built-in command name
896
+ packageArgument?: string; // Specific package to execute
897
+
898
+ // Parallel execution
899
+ parallel?: boolean; // Enable parallel execution
900
+ maxConcurrency?: number; // Max concurrent tasks (default: CPU cores)
901
+
902
+ // Retry configuration
903
+ retry?: {
904
+ maxAttempts?: number; // Max retry attempts (default: 3)
905
+ initialDelayMs?: number; // Initial delay (default: 1000)
906
+ maxDelayMs?: number; // Max delay (default: 10000)
907
+ backoffMultiplier?: number; // Backoff multiplier (default: 2)
908
+ retriableErrors?: string[]; // Retriable error patterns
909
+ };
910
+
911
+ // Recovery configuration
912
+ recovery?: {
913
+ checkpointInterval?: 'package' | 'batch'; // Checkpoint frequency
914
+ autoRetry?: boolean; // Auto-retry on failure
915
+ continueOnError?: boolean; // Continue on errors
916
+ };
917
+
918
+ // Monitoring configuration
919
+ monitoring?: {
920
+ showProgress?: boolean; // Show progress bar
921
+ showMetrics?: boolean; // Show metrics
922
+ logLevel?: 'minimal' | 'normal' | 'verbose'; // Log verbosity
923
+ };
924
+
925
+ // Recovery operations
926
+ continue?: boolean; // Resume from checkpoint
927
+ markCompleted?: string[]; // Mark packages as completed
928
+ skipPackages?: string[]; // Skip specific packages
929
+ retryFailed?: boolean; // Retry failed packages
930
+ skipFailed?: boolean; // Skip failed packages
931
+ resetPackage?: string; // Reset specific package state
932
+
933
+ // Advanced options
934
+ startFrom?: string; // Start from specific package
935
+ stopAt?: string; // Stop at specific package
936
+ status?: boolean; // Show execution status
937
+ validateState?: boolean; // Validate execution state
938
+ auditBranches?: boolean; // Audit git branches
939
+ };
940
+ }
941
+ ```
942
+
943
+ ### Environment Variables
944
+
945
+ Control execution through environment variables:
946
+
947
+ ```bash
948
+ # Concurrency
949
+ TREE_MAX_CONCURRENCY=4
950
+
951
+ # Retry configuration
952
+ TREE_MAX_RETRIES=3
953
+ TREE_RETRY_DELAY=1000
954
+ TREE_RETRY_BACKOFF=2
955
+
956
+ # Checkpoint configuration
957
+ TREE_CHECKPOINT_PATH=./checkpoints
958
+ TREE_CHECKPOINT_INTERVAL=package
959
+
960
+ # Logging
961
+ TREE_LOG_LEVEL=verbose
962
+ TREE_SHOW_METRICS=true
963
+ ```
964
+
965
+ ## Error Handling & Recovery
966
+
967
+ ### Error Classification
968
+
969
+ The system classifies errors as retriable or non-retriable:
970
+
971
+ ```typescript
972
+ // Retriable errors (will be retried automatically)
973
+ const retriableErrors = [
974
+ 'ECONNRESET', // Network connection reset
975
+ 'ETIMEDOUT', // Network timeout
976
+ 'ENOTFOUND', // DNS lookup failed
977
+ 'ECONNREFUSED', // Connection refused
978
+ 'Test.*flaky' // Flaky test patterns
979
+ ];
980
+
981
+ // Non-retriable errors (fail immediately)
982
+ const nonRetriableErrors = [
983
+ 'Syntax Error', // Code syntax errors
984
+ 'Type Error', // Type errors
985
+ 'Build failed', // Build failures
986
+ 'Lint failed' // Linting failures
987
+ ];
988
+ ```
989
+
990
+ ### Handling Failed Packages
991
+
992
+ ```typescript
993
+ const result = await pool.execute();
994
+
995
+ if (!result.success) {
996
+ console.error('Execution failed');
997
+
998
+ // Analyze failures
999
+ result.failed.forEach(failure => {
1000
+ console.error(`\n${failure.name}:`);
1001
+ console.error(` Error: ${failure.error}`);
1002
+ console.error(` Retriable: ${failure.isRetriable}`);
1003
+ console.error(` Attempts: ${failure.attemptNumber}`);
1004
+
1005
+ if (failure.errorDetails) {
1006
+ console.error(` Type: ${failure.errorDetails.type}`);
1007
+ console.error(` Context: ${failure.errorDetails.context}`);
1008
+ if (failure.errorDetails.suggestion) {
1009
+ console.error(` Suggestion: ${failure.errorDetails.suggestion}`);
1010
+ }
1011
+ }
1012
+
1013
+ // Show affected packages
1014
+ console.error(` Dependents (skipped): ${failure.dependents.join(', ')}`);
1015
+ });
1016
+
1017
+ // Save checkpoint for recovery
1018
+ const checkpoint = await pool.getCheckpoint();
1019
+ await fs.writeFile('./failed-execution.json', JSON.stringify(checkpoint, null, 2));
1020
+
1021
+ console.log('\nCheckpoint saved to failed-execution.json');
1022
+ console.log('Resume with: --continue');
1023
+ }
1024
+ ```
1025
+
1026
+ ### Recovery Strategies
1027
+
1028
+ #### 1. Skip Failed Packages
1029
+
1030
+ ```typescript
1031
+ const recovery = await loadRecoveryManager('./checkpoint.json');
1032
+ await recovery.applyRecoveryOptions({
1033
+ skipFailed: true
1034
+ });
1035
+ ```
1036
+
1037
+ #### 2. Retry Failed Packages
1038
+
1039
+ ```typescript
1040
+ await recovery.applyRecoveryOptions({
1041
+ retryFailed: true
1042
+ });
1043
+ ```
1044
+
1045
+ #### 3. Skip Specific Packages
1046
+
1047
+ ```typescript
1048
+ await recovery.applyRecoveryOptions({
1049
+ skipPackages: ['problematic-pkg1', 'problematic-pkg2']
1050
+ });
1051
+ ```
1052
+
1053
+ #### 4. Mark Packages as Completed
1054
+
1055
+ ```typescript
1056
+ await recovery.applyRecoveryOptions({
1057
+ markCompleted: ['manually-fixed-pkg']
1058
+ });
1059
+ ```
1060
+
1061
+ #### 5. Reset Package State
1062
+
1063
+ ```typescript
1064
+ await recovery.applyRecoveryOptions({
1065
+ resetPackage: 'pkg-to-reset'
1066
+ });
1067
+ ```
1068
+
1069
+ ## Real-World Examples
1070
+
1071
+ ### Example 1: Monorepo Test Suite
1072
+
1073
+ Run tests across all packages with intelligent parallelization:
1074
+
1075
+ ```typescript
1076
+ import { createTreeExecutor } from '@eldrforge/tree-execution';
1077
+ import { buildDependencyGraph } from '@eldrforge/tree-core';
1078
+
1079
+ async function runMonorepoTests() {
1080
+ // Build dependency graph
1081
+ const graph = await buildDependencyGraph(['packages/*/package.json']);
1082
+
1083
+ // Create executor
1084
+ const executor = createTreeExecutor();
1085
+
1086
+ // Run tests
1087
+ const result = await executor.execute({
1088
+ verbose: true,
1089
+ tree: {
1090
+ directories: ['packages'],
1091
+ cmd: 'npm test',
1092
+ parallel: true,
1093
+ maxConcurrency: 4,
1094
+ retry: {
1095
+ maxAttempts: 2,
1096
+ initialDelayMs: 1000,
1097
+ retriableErrors: ['Test.*flaky']
1098
+ },
1099
+ monitoring: {
1100
+ showProgress: true,
1101
+ showMetrics: true,
1102
+ logLevel: 'normal'
1103
+ }
1104
+ }
1105
+ });
1106
+
1107
+ console.log(`\nTests completed: ${result.completed.length}/${result.totalPackages}`);
1108
+ return result.success ? 0 : 1;
1109
+ }
1110
+
1111
+ runMonorepoTests().then(code => process.exit(code));
1112
+ ```
1113
+
1114
+ ### Example 2: Incremental Build System
1115
+
1116
+ Build only changed packages and their dependents:
1117
+
1118
+ ```typescript
1119
+ import { DynamicTaskPool } from '@eldrforge/tree-execution';
1120
+ import { buildDependencyGraph, findAllDependents } from '@eldrforge/tree-core';
1121
+ import { getGitStatusSummary } from '@eldrforge/git-tools';
1122
+
1123
+ async function incrementalBuild() {
1124
+ // Get changed packages
1125
+ const graph = await buildDependencyGraph(['packages/*/package.json']);
1126
+ const status = await getGitStatusSummary();
1127
+
1128
+ const changedPackages = new Set<string>();
1129
+ [...status.staged, ...status.modified].forEach(file => {
1130
+ const match = file.match(/packages\/([^\/]+)\//);
1131
+ if (match) changedPackages.add(match[1]);
1132
+ });
1133
+
1134
+ // Find all affected packages (changed + dependents)
1135
+ const affectedPackages = new Set<string>();
1136
+ changedPackages.forEach(pkg => {
1137
+ affectedPackages.add(pkg);
1138
+ findAllDependents(graph, pkg).forEach(dep => affectedPackages.add(dep));
1139
+ });
1140
+
1141
+ console.log(`Changed packages: ${Array.from(changedPackages).join(', ')}`);
1142
+ console.log(`Total affected: ${affectedPackages.size}`);
1143
+
1144
+ if (affectedPackages.size === 0) {
1145
+ console.log('No packages to build');
1146
+ return;
1147
+ }
1148
+
1149
+ // Build affected packages
1150
+ const filteredGraph = {
1151
+ ...graph,
1152
+ packages: new Map(
1153
+ Array.from(graph.packages).filter(([name]) => affectedPackages.has(name))
1154
+ )
1155
+ };
1156
+
1157
+ const pool = new DynamicTaskPool({
1158
+ graph: filteredGraph,
1159
+ maxConcurrency: 4,
1160
+ command: 'npm run build',
1161
+ config: { tree: { parallel: true } }
1162
+ });
1163
+
1164
+ const result = await pool.execute();
1165
+ console.log(`\nBuilt ${result.completed.length} packages in ${result.metrics.totalDuration}ms`);
1166
+ }
1167
+
1168
+ incrementalBuild().catch(console.error);
1169
+ ```
1170
+
1171
+ ### Example 3: Coordinated Package Publishing
1172
+
1173
+ Publish packages in dependency order with automatic version tracking:
1174
+
1175
+ ```typescript
1176
+ import { DynamicTaskPool, createParallelProgressLogger } from '@eldrforge/tree-execution';
1177
+ import { buildDependencyGraph } from '@eldrforge/tree-core';
1178
+
1179
+ async function publishMonorepo() {
1180
+ const graph = await buildDependencyGraph(['packages/*/package.json']);
1181
+
1182
+ const pool = new DynamicTaskPool({
1183
+ graph,
1184
+ maxConcurrency: 2, // Limit publishing concurrency
1185
+ command: 'npm publish',
1186
+ config: {
1187
+ tree: {
1188
+ parallel: true,
1189
+ retry: {
1190
+ maxAttempts: 3,
1191
+ initialDelayMs: 2000,
1192
+ retriableErrors: ['ECONNRESET', 'ETIMEDOUT']
1193
+ }
1194
+ }
1195
+ },
1196
+ checkpointPath: './checkpoints/publish.json'
1197
+ });
94
1198
 
95
- ### ResourceMonitor
96
- Tracks available execution slots and resource usage.
1199
+ // Create progress logger
1200
+ const logger = createParallelProgressLogger(graph.packages.size);
97
1201
 
98
- ### CheckpointManager
99
- Saves and restores execution state for resume capability.
1202
+ // Track published versions
1203
+ const published: Array<{ name: string; version: string }> = [];
100
1204
 
101
- ### CommandValidator
102
- Validates commands for parallel execution safety.
1205
+ pool.on('package:started', ({ packageName }) => {
1206
+ logger.onPackageStarted(packageName);
1207
+ });
1208
+
1209
+ pool.on('package:completed', ({ packageName, result }) => {
1210
+ logger.onPackageCompleted(packageName, result);
1211
+ if (result.publishedVersion) {
1212
+ published.push({
1213
+ name: packageName,
1214
+ version: result.publishedVersion
1215
+ });
1216
+ }
1217
+ });
1218
+
1219
+ pool.on('package:failed', ({ packageName, error }) => {
1220
+ logger.onPackageFailed(packageName, error);
1221
+ });
1222
+
1223
+ const result = await pool.execute();
1224
+
1225
+ if (result.success) {
1226
+ console.log('\n=== Published Packages ===');
1227
+ published.forEach(p => console.log(`${p.name}@${p.version}`));
1228
+ } else {
1229
+ console.error('\n=== Publish Failed ===');
1230
+ console.error('Checkpoint saved for recovery');
1231
+ console.error('Resume with: --continue');
1232
+ }
1233
+
1234
+ return result;
1235
+ }
1236
+
1237
+ publishMonorepo().catch(console.error);
1238
+ ```
103
1239
 
104
- ### DependencyChecker
105
- Verifies package dependencies and readiness.
1240
+ ### Example 4: Integration Test Suite
1241
+
1242
+ Run integration tests with environment setup/teardown:
1243
+
1244
+ ```typescript
1245
+ import { DynamicTaskPool } from '@eldrforge/tree-execution';
1246
+ import { buildDependencyGraph } from '@eldrforge/tree-core';
1247
+
1248
+ async function runIntegrationTests() {
1249
+ const graph = await buildDependencyGraph(['services/*/package.json']);
1250
+
1251
+ // Setup: Start shared services
1252
+ console.log('Starting shared services...');
1253
+ await startDatabase();
1254
+ await startRedis();
1255
+ await startMessageQueue();
1256
+
1257
+ try {
1258
+ const pool = new DynamicTaskPool({
1259
+ graph,
1260
+ maxConcurrency: 2, // Limit to avoid resource contention
1261
+ command: 'npm run test:integration',
1262
+ config: {
1263
+ tree: {
1264
+ parallel: true,
1265
+ recovery: {
1266
+ continueOnError: false // Stop on first failure
1267
+ }
1268
+ }
1269
+ }
1270
+ });
1271
+
1272
+ pool.on('package:failed', async ({ packageName, error }) => {
1273
+ // Cleanup on failure
1274
+ console.error(`Test failed: ${packageName}`);
1275
+ await pool.abort('Test failure detected');
1276
+ });
1277
+
1278
+ const result = await pool.execute();
1279
+ return result;
1280
+
1281
+ } finally {
1282
+ // Teardown: Stop shared services
1283
+ console.log('Stopping shared services...');
1284
+ await stopMessageQueue();
1285
+ await stopRedis();
1286
+ await stopDatabase();
1287
+ }
1288
+ }
1289
+
1290
+ // Stub functions for services
1291
+ async function startDatabase() { /* ... */ }
1292
+ async function stopDatabase() { /* ... */ }
1293
+ async function startRedis() { /* ... */ }
1294
+ async function stopRedis() { /* ... */ }
1295
+ async function startMessageQueue() { /* ... */ }
1296
+ async function stopMessageQueue() { /* ... */ }
1297
+
1298
+ runIntegrationTests().catch(console.error);
1299
+ ```
1300
+
1301
+ ### Example 5: Custom Build Pipeline
1302
+
1303
+ Implement a multi-stage build pipeline:
1304
+
1305
+ ```typescript
1306
+ import { DynamicTaskPool } from '@eldrforge/tree-execution';
1307
+ import { buildDependencyGraph } from '@eldrforge/tree-core';
1308
+
1309
+ async function buildPipeline() {
1310
+ const graph = await buildDependencyGraph(['packages/*/package.json']);
1311
+
1312
+ const stages = [
1313
+ { name: 'Lint', command: 'npm run lint', concurrency: 8 },
1314
+ { name: 'Type Check', command: 'npm run type-check', concurrency: 4 },
1315
+ { name: 'Build', command: 'npm run build', concurrency: 4 },
1316
+ { name: 'Test', command: 'npm test', concurrency: 4 }
1317
+ ];
1318
+
1319
+ for (const stage of stages) {
1320
+ console.log(`\n=== Stage: ${stage.name} ===`);
1321
+
1322
+ const pool = new DynamicTaskPool({
1323
+ graph,
1324
+ maxConcurrency: stage.concurrency,
1325
+ command: stage.command,
1326
+ config: { tree: { parallel: true } }
1327
+ });
1328
+
1329
+ const result = await pool.execute();
1330
+
1331
+ if (!result.success) {
1332
+ console.error(`\nStage '${stage.name}' failed`);
1333
+ console.error(`Failed packages: ${result.failed.map(f => f.name).join(', ')}`);
1334
+ return false;
1335
+ }
1336
+
1337
+ console.log(`Stage completed in ${result.metrics.totalDuration}ms`);
1338
+ }
1339
+
1340
+ console.log('\n=== Pipeline Complete ===');
1341
+ return true;
1342
+ }
1343
+
1344
+ buildPipeline().then(success => {
1345
+ process.exit(success ? 0 : 1);
1346
+ });
1347
+ ```
106
1348
 
107
1349
  ## Testing
108
1350
 
109
- This package includes 140+ comprehensive tests:
1351
+ This package includes comprehensive test coverage:
110
1352
 
111
1353
  ```bash
112
- npm test # Run tests
113
- npm run test:coverage # Run with coverage
1354
+ # Run all tests
1355
+ npm test
1356
+
1357
+ # Run with coverage
1358
+ npm run test:coverage
1359
+
1360
+ # Watch mode
1361
+ npm run test:watch
1362
+ ```
1363
+
1364
+ ### Test Structure
1365
+
1366
+ ```
1367
+ tests/
1368
+ โ”œโ”€โ”€ checkpoint/
1369
+ โ”‚ โ””โ”€โ”€ CheckpointManager.test.ts # Checkpoint persistence
1370
+ โ”œโ”€โ”€ execution/
1371
+ โ”‚ โ”œโ”€โ”€ CommandValidator.test.ts # Command validation
1372
+ โ”‚ โ”œโ”€โ”€ DependencyChecker.test.ts # Dependency checking
1373
+ โ”‚ โ”œโ”€โ”€ RecoveryManager.test.ts # Error recovery
1374
+ โ”‚ โ”œโ”€โ”€ ResourceMonitor.test.ts # Resource tracking
1375
+ โ”‚ โ””โ”€โ”€ Scheduler.test.ts # Task scheduling
1376
+ โ”œโ”€โ”€ integration/
1377
+ โ”‚ โ””โ”€โ”€ execution-flow.test.ts # End-to-end tests
1378
+ โ”œโ”€โ”€ TreeExecutor.test.ts # TreeExecutor API
1379
+ โ””โ”€โ”€ util/
1380
+ โ”œโ”€โ”€ logger.test.ts # Logging
1381
+ โ”œโ”€โ”€ mutex.test.ts # Thread safety
1382
+ โ””โ”€โ”€ treeUtils.test.ts # Utilities
1383
+ ```
1384
+
1385
+ ### Coverage Report
1386
+
1387
+ - **TreeExecutor**: 94.82%
1388
+ - **Checkpoint Management**: 85%+
1389
+ - **Execution Framework**: 60%+
1390
+ - **Utilities**: 80%+
1391
+
1392
+ ### Writing Tests
1393
+
1394
+ Example test for custom integration:
1395
+
1396
+ ```typescript
1397
+ import { describe, it, expect } from 'vitest';
1398
+ import { createTreeExecutor } from '@eldrforge/tree-execution';
1399
+ import { buildDependencyGraph } from '@eldrforge/tree-core';
1400
+
1401
+ describe('Custom Integration', () => {
1402
+ it('should execute custom command', async () => {
1403
+ const executor = createTreeExecutor();
1404
+
1405
+ const result = await executor.execute({
1406
+ tree: {
1407
+ directories: ['test-packages'],
1408
+ cmd: 'echo "test"',
1409
+ parallel: false
1410
+ }
1411
+ });
1412
+
1413
+ expect(result).toBeDefined();
1414
+ });
1415
+ });
1416
+ ```
1417
+
1418
+ ## Architecture
1419
+
1420
+ ### Component Overview
1421
+
1422
+ ```
1423
+ @eldrforge/tree-execution
1424
+ โ”œโ”€โ”€ TreeExecutor (High-level API)
1425
+ โ”‚ โ”œโ”€โ”€ State management
1426
+ โ”‚ โ”œโ”€โ”€ Command injection
1427
+ โ”‚ โ””โ”€โ”€ Thread safety
1428
+ โ”‚
1429
+ โ”œโ”€โ”€ DynamicTaskPool (Execution engine)
1430
+ โ”‚ โ”œโ”€โ”€ Task scheduling
1431
+ โ”‚ โ”œโ”€โ”€ Parallel coordination
1432
+ โ”‚ โ”œโ”€โ”€ Event emission
1433
+ โ”‚ โ””โ”€โ”€ Checkpoint management
1434
+ โ”‚
1435
+ โ”œโ”€โ”€ Execution Components
1436
+ โ”‚ โ”œโ”€โ”€ Scheduler (Task ordering)
1437
+ โ”‚ โ”œโ”€โ”€ ResourceMonitor (Concurrency control)
1438
+ โ”‚ โ”œโ”€โ”€ DependencyChecker (Dependency validation)
1439
+ โ”‚ โ””โ”€โ”€ CommandValidator (Command validation)
1440
+ โ”‚
1441
+ โ”œโ”€โ”€ Recovery Components
1442
+ โ”‚ โ”œโ”€โ”€ CheckpointManager (State persistence)
1443
+ โ”‚ โ””โ”€โ”€ RecoveryManager (Error recovery)
1444
+ โ”‚
1445
+ โ””โ”€โ”€ Utilities
1446
+ โ”œโ”€โ”€ Logger (Logging abstraction)
1447
+ โ”œโ”€โ”€ SimpleMutex (Thread safety)
1448
+ โ””โ”€โ”€ TreeUtils (Helper functions)
1449
+ ```
1450
+
1451
+ ### Execution Flow
1452
+
1453
+ ```
1454
+ 1. Build dependency graph
1455
+ โ””โ†’ @eldrforge/tree-core
1456
+
1457
+ 2. Initialize DynamicTaskPool
1458
+ โ”œโ†’ Create Scheduler
1459
+ โ”œโ†’ Create ResourceMonitor
1460
+ โ”œโ†’ Create DependencyChecker
1461
+ โ””โ†’ Load checkpoint (if continuing)
1462
+
1463
+ 3. Execution loop
1464
+ โ”œโ†’ Scheduler selects ready packages
1465
+ โ”œโ†’ ResourceMonitor allocates slots
1466
+ โ”œโ†’ Execute packages in parallel
1467
+ โ”œโ†’ Update state on completion/failure
1468
+ โ”œโ†’ Save checkpoints periodically
1469
+ โ””โ†’ Emit progress events
1470
+
1471
+ 4. Handle failures
1472
+ โ”œโ†’ Classify errors (retriable/non-retriable)
1473
+ โ”œโ†’ Retry with exponential backoff
1474
+ โ”œโ†’ Skip dependent packages on failure
1475
+ โ””โ†’ Save recovery checkpoint
1476
+
1477
+ 5. Complete execution
1478
+ โ”œโ†’ Calculate metrics
1479
+ โ”œโ†’ Generate summary
1480
+ โ””โ†’ Return ExecutionResult
114
1481
  ```
115
1482
 
116
- **Test Coverage**:
117
- - TreeExecutor: 94.82%
118
- - Utilities: 80%+
119
- - Execution framework: 60%+
1483
+ ### Thread Safety
1484
+
1485
+ All state mutations are protected by mutexes:
1486
+
1487
+ ```typescript
1488
+ import { SimpleMutex } from '@eldrforge/tree-execution';
1489
+
1490
+ class StatefulComponent {
1491
+ private mutex = new SimpleMutex();
1492
+ private state: any = {};
1493
+
1494
+ async updateState(updates: any) {
1495
+ await this.mutex.runExclusive(async () => {
1496
+ this.state = { ...this.state, ...updates };
1497
+ });
1498
+ }
1499
+ }
1500
+ ```
120
1501
 
121
1502
  ## Dependencies
122
1503
 
123
- - `@eldrforge/tree-core` - Dependency graph algorithms
124
- - `@eldrforge/git-tools` - Git operations
125
- - `@eldrforge/shared` - Shared utilities
1504
+ ### Required Dependencies
1505
+
1506
+ - **@eldrforge/tree-core**: Dependency graph algorithms
1507
+ - **@eldrforge/git-tools**: Git operations
1508
+ - **@eldrforge/shared**: Shared utilities
1509
+
1510
+ ### Peer Dependencies
1511
+
1512
+ These are automatically installed with the above packages:
1513
+
1514
+ - Node.js โ‰ฅ 18.0.0
1515
+ - TypeScript โ‰ฅ 5.0.0 (for development)
1516
+
1517
+ ## Contributing
1518
+
1519
+ Contributions are welcome! Please follow these guidelines:
1520
+
1521
+ 1. **Code Style**: Follow existing patterns and ESLint rules
1522
+ 2. **Tests**: Add tests for new features
1523
+ 3. **Documentation**: Update README and JSDoc comments
1524
+ 4. **Commits**: Use conventional commit format
1525
+
1526
+ ```bash
1527
+ # Setup development environment
1528
+ git clone https://github.com/calenvarek/tree-execution.git
1529
+ cd tree-execution
1530
+ npm install
1531
+
1532
+ # Run tests
1533
+ npm test
1534
+
1535
+ # Build
1536
+ npm run build
1537
+
1538
+ # Lint
1539
+ npm run lint
1540
+ ```
1541
+
1542
+ ### Development Scripts
1543
+
1544
+ ```json
1545
+ {
1546
+ "build": "tsc",
1547
+ "test": "vitest run",
1548
+ "test:watch": "vitest",
1549
+ "test:coverage": "vitest run --coverage",
1550
+ "lint": "eslint 'src/**/*.ts'",
1551
+ "clean": "rm -rf dist coverage"
1552
+ }
1553
+ ```
126
1554
 
127
1555
  ## License
128
1556
 
129
1557
  MIT ยฉ Calen Varek
130
1558
 
1559
+ ## Links
1560
+
1561
+ - **GitHub**: https://github.com/calenvarek/tree-execution
1562
+ - **Issues**: https://github.com/calenvarek/tree-execution/issues
1563
+ - **npm**: https://www.npmjs.com/package/@eldrforge/tree-execution
1564
+
1565
+ ## Related Projects
1566
+
1567
+ - **@eldrforge/tree-core**: Dependency graph algorithms
1568
+ - **@eldrforge/git-tools**: Git operations toolkit
1569
+ - **@eldrforge/shared**: Shared utilities
1570
+ - **kodrdriv**: Complete monorepo toolkit (uses tree-execution)
1571
+
1572
+ ---
1573
+
1574
+ Built with โค๏ธ for monorepo orchestration