@eldrforge/kodrdriv 1.2.29 → 1.2.124
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/application.js +16 -13
- package/dist/application.js.map +1 -1
- package/dist/arguments.js +5 -5
- package/dist/arguments.js.map +1 -1
- package/dist/commands/audio-review.js +2 -5
- package/dist/commands/audio-review.js.map +1 -1
- package/dist/commands/clean.js +2 -4
- package/dist/commands/clean.js.map +1 -1
- package/dist/commands/commit.js +3 -6
- package/dist/commands/commit.js.map +1 -1
- package/dist/commands/development.js +7 -7
- package/dist/commands/development.js.map +1 -1
- package/dist/commands/link.js +3 -7
- package/dist/commands/link.js.map +1 -1
- package/dist/commands/precommit.js +99 -0
- package/dist/commands/precommit.js.map +1 -0
- package/dist/commands/publish.js +47 -32
- package/dist/commands/publish.js.map +1 -1
- package/dist/commands/release.js +3 -7
- package/dist/commands/release.js.map +1 -1
- package/dist/commands/review.js +4 -6
- package/dist/commands/review.js.map +1 -1
- package/dist/commands/tree.js +213 -84
- package/dist/commands/tree.js.map +1 -1
- package/dist/commands/unlink.js +3 -7
- package/dist/commands/unlink.js.map +1 -1
- package/dist/commands/updates.js +2 -4
- package/dist/commands/updates.js.map +1 -1
- package/dist/commands/versions.js +3 -7
- package/dist/commands/versions.js.map +1 -1
- package/dist/constants.js +4 -2
- package/dist/constants.js.map +1 -1
- package/dist/content/files.js +2 -4
- package/dist/content/files.js.map +1 -1
- package/dist/execution/CommandValidator.js +33 -1
- package/dist/execution/CommandValidator.js.map +1 -1
- package/dist/execution/ResourceMonitor.js +26 -1
- package/dist/execution/ResourceMonitor.js.map +1 -1
- package/dist/execution/TreeExecutionAdapter.js +2 -2
- package/dist/execution/TreeExecutionAdapter.js.map +1 -1
- package/dist/util/checkpointManager.js +2 -4
- package/dist/util/checkpointManager.js.map +1 -1
- package/dist/util/dependencyGraph.js +2 -4
- package/dist/util/dependencyGraph.js.map +1 -1
- package/dist/util/general.js +8 -219
- package/dist/util/general.js.map +1 -1
- package/dist/util/gitMutex.js +63 -18
- package/dist/util/gitMutex.js.map +1 -1
- package/dist/util/precommitOptimizations.js +310 -0
- package/dist/util/precommitOptimizations.js.map +1 -0
- package/dist/util/storageAdapter.js +2 -6
- package/dist/util/storageAdapter.js.map +1 -1
- package/dist/util/validation.js +3 -3
- package/dist/util/validation.js.map +1 -1
- package/dist/utils/branchState.js +178 -45
- package/dist/utils/branchState.js.map +1 -1
- package/package.json +6 -5
- package/AI-FRIENDLY-LOGGING-GUIDE.md +0 -237
- package/AI-LOGGING-MIGRATION-COMPLETE.md +0 -371
- package/ALREADY-PUBLISHED-PACKAGES-FIX.md +0 -264
- package/AUDIT-BRANCHES-PROGRESS-FIX.md +0 -90
- package/AUDIT-EXAMPLE-OUTPUT.md +0 -113
- package/CHECKPOINT-RECOVERY-FIX.md +0 -450
- package/LOGGING-MIGRATION-STATUS.md +0 -186
- package/MONOREPO-PUBLISH-IMPROVEMENTS.md +0 -281
- package/PARALLEL-EXECUTION-FIXES.md +0 -132
- package/PARALLEL-PUBLISH-DEBUGGING-GUIDE.md +0 -441
- package/PARALLEL-PUBLISH-FIXES-IMPLEMENTED.md +0 -405
- package/PARALLEL-PUBLISH-IMPROVEMENTS-IMPLEMENTED.md +0 -439
- package/PARALLEL-PUBLISH-LOGGING-FIXES.md +0 -274
- package/PARALLEL-PUBLISH-QUICK-REFERENCE.md +0 -375
- package/PARALLEL_EXECUTION_FIX.md +0 -146
- package/PUBLISH_IMPROVEMENTS_IMPLEMENTED.md +0 -294
- package/RECOVERY-FIXES.md +0 -72
- package/SUBMODULE-LOCK-FIX.md +0 -132
- package/VERSION-AUDIT-FIX.md +0 -333
- package/WORKFLOW-PRECHECK-IMPLEMENTATION.md +0 -239
- package/WORKFLOW-SKIP-SUMMARY.md +0 -121
- package/dist/util/safety.js +0 -166
- package/dist/util/safety.js.map +0 -1
- package/dist/util/stdin.js +0 -133
- package/dist/util/stdin.js.map +0 -1
- package/dist/util/storage.js +0 -187
- package/dist/util/storage.js.map +0 -1
|
@@ -1,90 +0,0 @@
|
|
|
1
|
-
# Branch Audit Progress Feedback Improvements
|
|
2
|
-
|
|
3
|
-
## Problem
|
|
4
|
-
|
|
5
|
-
The `kodrdriv tree publish --audit-branches` command takes a long time to run (several minutes for large monorepos) with no progress feedback, making it difficult to know if the command is actually running or stuck.
|
|
6
|
-
|
|
7
|
-
## Root Cause
|
|
8
|
-
|
|
9
|
-
The `auditBranchState` function in `src/utils/branchState.ts` performs extensive checks on every package:
|
|
10
|
-
- Git operations (fetch, ls-remote, rev-list, merge-tree)
|
|
11
|
-
- GitHub API calls to check for existing PRs
|
|
12
|
-
- Version validation
|
|
13
|
-
- Target branch sync checks
|
|
14
|
-
|
|
15
|
-
For a monorepo with 50+ packages, these sequential operations can take 2-3 seconds per package (100-150 seconds total) with no feedback during execution.
|
|
16
|
-
|
|
17
|
-
## Changes Made
|
|
18
|
-
|
|
19
|
-
### 1. Phase-based Progress Reporting
|
|
20
|
-
|
|
21
|
-
Added two-phase execution with clear messaging:
|
|
22
|
-
|
|
23
|
-
```
|
|
24
|
-
Phase 1/2: Detecting most common branch across packages...
|
|
25
|
-
[1/50] Checking branch: package-a
|
|
26
|
-
[2/50] Checking branch: package-b
|
|
27
|
-
...
|
|
28
|
-
✓ Most common branch: development (48/50 packages)
|
|
29
|
-
|
|
30
|
-
Phase 2/2: Auditing package state (checking git status, conflicts, PRs, versions)...
|
|
31
|
-
[1/50] Auditing: package-a
|
|
32
|
-
[2/50] Auditing: package-b
|
|
33
|
-
...
|
|
34
|
-
✓ Audit complete: 45/50 packages have no issues
|
|
35
|
-
Issues found in 5 package(s)
|
|
36
|
-
```
|
|
37
|
-
|
|
38
|
-
### 2. Per-Package Progress Counters
|
|
39
|
-
|
|
40
|
-
Each package now shows its position in the queue `[N/Total]` so users can track overall progress.
|
|
41
|
-
|
|
42
|
-
### 3. Verbose Operation Logging
|
|
43
|
-
|
|
44
|
-
Added verbose logging for expensive operations within each package check:
|
|
45
|
-
- "Fetching latest from origin..."
|
|
46
|
-
- "Checking for merge conflicts..."
|
|
47
|
-
- "Checking GitHub for existing PRs..."
|
|
48
|
-
- "Found existing PR #123..."
|
|
49
|
-
|
|
50
|
-
These only appear when `--verbose` or `--debug` flags are used, providing more detailed feedback without cluttering default output.
|
|
51
|
-
|
|
52
|
-
### 4. Completion Summary
|
|
53
|
-
|
|
54
|
-
Added clear completion message showing:
|
|
55
|
-
- Number of packages with no issues
|
|
56
|
-
- Number of packages with issues (if any)
|
|
57
|
-
|
|
58
|
-
## Impact
|
|
59
|
-
|
|
60
|
-
Users can now:
|
|
61
|
-
1. **See that the command is running** - immediate feedback with progress counters
|
|
62
|
-
2. **Estimate completion time** - `[15/50]` indicates 30% complete
|
|
63
|
-
3. **Identify slow operations** - verbose mode shows which operation is taking time
|
|
64
|
-
4. **Know when it's done** - clear completion message
|
|
65
|
-
|
|
66
|
-
## Files Modified
|
|
67
|
-
|
|
68
|
-
- `src/utils/branchState.ts` - Added progress logging to `auditBranchState` and `checkBranchStatus` functions
|
|
69
|
-
|
|
70
|
-
## Testing
|
|
71
|
-
|
|
72
|
-
Compile check: ✅ Passed (`tsc --noEmit`)
|
|
73
|
-
|
|
74
|
-
Manual testing recommended:
|
|
75
|
-
```bash
|
|
76
|
-
kodrdriv tree publish --audit-branches
|
|
77
|
-
kodrdriv tree publish --audit-branches --verbose
|
|
78
|
-
kodrdriv tree publish --audit-branches --debug
|
|
79
|
-
```
|
|
80
|
-
|
|
81
|
-
## Future Optimizations (Not Implemented)
|
|
82
|
-
|
|
83
|
-
Potential future improvements for faster execution:
|
|
84
|
-
1. **Parallel package checks** - Process multiple packages simultaneously (requires careful handling of git operations)
|
|
85
|
-
2. **Batch GitHub API calls** - Use GraphQL to query multiple PRs at once
|
|
86
|
-
3. **Cache git fetch results** - Avoid fetching the same remote multiple times
|
|
87
|
-
4. **Skip checks for packages with no changes** - Use git status to detect unchanged packages early
|
|
88
|
-
|
|
89
|
-
These optimizations would require more significant refactoring and testing.
|
|
90
|
-
|
package/AUDIT-EXAMPLE-OUTPUT.md
DELETED
|
@@ -1,113 +0,0 @@
|
|
|
1
|
-
# Example Output: Branch Audit with Progress Feedback
|
|
2
|
-
|
|
3
|
-
## Before (No Feedback)
|
|
4
|
-
|
|
5
|
-
```bash
|
|
6
|
-
$ kodrdriv tree publish --audit-branches
|
|
7
|
-
🔍 Auditing branch state across all packages...
|
|
8
|
-
BRANCH_STATE_AUDIT: Auditing branch state for packages | Package Count: 50 | Purpose: Verify synchronization
|
|
9
|
-
Checking for merge conflicts with 'main' and existing pull requests...
|
|
10
|
-
|
|
11
|
-
[... 2-3 minutes of silence ...]
|
|
12
|
-
|
|
13
|
-
✅ All 45 package(s) are in good state!
|
|
14
|
-
```
|
|
15
|
-
|
|
16
|
-
**Problem:** Users don't know if the command is running or stuck.
|
|
17
|
-
|
|
18
|
-
---
|
|
19
|
-
|
|
20
|
-
## After (With Progress Feedback)
|
|
21
|
-
|
|
22
|
-
### Default Output
|
|
23
|
-
|
|
24
|
-
```bash
|
|
25
|
-
$ kodrdriv tree publish --audit-branches
|
|
26
|
-
🔍 Auditing branch state across all packages...
|
|
27
|
-
BRANCH_STATE_AUDIT: Auditing branch state for packages | Package Count: 50 | Purpose: Verify synchronization
|
|
28
|
-
Checking for merge conflicts with 'main' and existing pull requests...
|
|
29
|
-
|
|
30
|
-
📋 Phase 1/2: Detecting most common branch across packages...
|
|
31
|
-
[1/50] Checking branch: @myorg/core
|
|
32
|
-
[2/50] Checking branch: @myorg/utils
|
|
33
|
-
[3/50] Checking branch: @myorg/api
|
|
34
|
-
...
|
|
35
|
-
[48/50] Checking branch: @myorg/tests
|
|
36
|
-
[49/50] Checking branch: @myorg/docs
|
|
37
|
-
[50/50] Checking branch: @myorg/cli
|
|
38
|
-
✓ Most common branch: development (48/50 packages)
|
|
39
|
-
|
|
40
|
-
📋 Phase 2/2: Auditing package state (checking git status, conflicts, PRs, versions)...
|
|
41
|
-
[1/50] Auditing: @myorg/core
|
|
42
|
-
[2/50] Auditing: @myorg/utils
|
|
43
|
-
[3/50] Auditing: @myorg/api
|
|
44
|
-
...
|
|
45
|
-
[48/50] Auditing: @myorg/tests
|
|
46
|
-
[49/50] Auditing: @myorg/docs
|
|
47
|
-
[50/50] Auditing: @myorg/cli
|
|
48
|
-
✓ Audit complete: 45/50 packages have no issues
|
|
49
|
-
Issues found in 5 package(s)
|
|
50
|
-
|
|
51
|
-
[... detailed issue report ...]
|
|
52
|
-
|
|
53
|
-
⚠️ Found issues in 5 package(s). Review the fixes above.
|
|
54
|
-
```
|
|
55
|
-
|
|
56
|
-
### Verbose Output (`--verbose`)
|
|
57
|
-
|
|
58
|
-
```bash
|
|
59
|
-
$ kodrdriv tree publish --audit-branches --verbose
|
|
60
|
-
🔍 Auditing branch state across all packages...
|
|
61
|
-
BRANCH_STATE_AUDIT: Auditing branch state for packages | Package Count: 50 | Purpose: Verify synchronization
|
|
62
|
-
Checking for merge conflicts with 'main' and existing pull requests...
|
|
63
|
-
|
|
64
|
-
📋 Phase 1/2: Detecting most common branch across packages...
|
|
65
|
-
[1/50] Checking branch: @myorg/core
|
|
66
|
-
[2/50] Checking branch: @myorg/utils
|
|
67
|
-
...
|
|
68
|
-
✓ Most common branch: development (48/50 packages)
|
|
69
|
-
|
|
70
|
-
📋 Phase 2/2: Auditing package state (checking git status, conflicts, PRs, versions)...
|
|
71
|
-
[1/50] Auditing: @myorg/core
|
|
72
|
-
Fetching latest from origin for /path/to/core...
|
|
73
|
-
Checking for merge conflicts with main...
|
|
74
|
-
Checking GitHub for existing PRs...
|
|
75
|
-
[2/50] Auditing: @myorg/utils
|
|
76
|
-
Fetching latest from origin for /path/to/utils...
|
|
77
|
-
Checking for merge conflicts with main...
|
|
78
|
-
Checking GitHub for existing PRs...
|
|
79
|
-
Found existing PR #123: https://github.com/myorg/utils/pull/123
|
|
80
|
-
[3/50] Auditing: @myorg/api
|
|
81
|
-
Fetching latest from origin for /path/to/api...
|
|
82
|
-
Checking for merge conflicts with main...
|
|
83
|
-
⚠️ Merge conflicts detected with main
|
|
84
|
-
Checking GitHub for existing PRs...
|
|
85
|
-
...
|
|
86
|
-
✓ Audit complete: 45/50 packages have no issues
|
|
87
|
-
Issues found in 5 package(s)
|
|
88
|
-
```
|
|
89
|
-
|
|
90
|
-
## Benefits
|
|
91
|
-
|
|
92
|
-
1. **Immediate Feedback:** Users see progress start immediately
|
|
93
|
-
2. **Progress Tracking:** `[N/Total]` shows exactly where you are
|
|
94
|
-
3. **Time Estimation:** If `[10/50]` takes 30 seconds, expect ~2.5 minutes total
|
|
95
|
-
4. **Debug Support:** Verbose mode shows which operations are slow
|
|
96
|
-
5. **Clear Completion:** Final summary shows results at a glance
|
|
97
|
-
|
|
98
|
-
## Testing the Changes
|
|
99
|
-
|
|
100
|
-
```bash
|
|
101
|
-
# Test in a monorepo with multiple packages
|
|
102
|
-
cd /path/to/your/monorepo
|
|
103
|
-
|
|
104
|
-
# Default output (progress with package names)
|
|
105
|
-
kodrdriv tree publish --audit-branches
|
|
106
|
-
|
|
107
|
-
# Verbose output (shows git operations)
|
|
108
|
-
kodrdriv tree publish --audit-branches --verbose
|
|
109
|
-
|
|
110
|
-
# Debug output (most detailed)
|
|
111
|
-
kodrdriv tree publish --audit-branches --debug
|
|
112
|
-
```
|
|
113
|
-
|
|
@@ -1,450 +0,0 @@
|
|
|
1
|
-
# Checkpoint Recovery Fix - Critical Bug Resolution
|
|
2
|
-
|
|
3
|
-
## Issue Summary
|
|
4
|
-
|
|
5
|
-
**Date**: 2025-12-12
|
|
6
|
-
**Version Fixed**: kodrdriv 1.2.29-dev.0
|
|
7
|
-
**Priority**: CRITICAL
|
|
8
|
-
**Impact**: Parallel publish checkpoint recovery was completely broken for the primary use case
|
|
9
|
-
|
|
10
|
-
### The Problem
|
|
11
|
-
|
|
12
|
-
The parallel publish checkpoint/recovery system failed to proceed to dependent packages after marking a root dependency as completed, resulting in an infinite loop where all dependent packages remained "Skipped due to dependencies" even though the dependency was successfully marked as completed.
|
|
13
|
-
|
|
14
|
-
This made recovery from ANY partial publish failure essentially impossible with parallel mode, defeating the entire purpose of the checkpoint/recovery system.
|
|
15
|
-
|
|
16
|
-
### Real-World Scenario
|
|
17
|
-
|
|
18
|
-
Publishing all packages in a monorepo after one package was already published:
|
|
19
|
-
|
|
20
|
-
1. **Initial State**: Package `common-config` already published to npm as v1.1.36, working branch has v1.1.37-dev.0
|
|
21
|
-
2. **Attempt 1**: Run `kodrdriv tree publish --parallel` → `common-config` fails (expected - already published), all 15 dependent packages skipped
|
|
22
|
-
3. **Recovery Attempt**: Run `kodrdriv tree publish --parallel --mark-completed "common-config" --continue`
|
|
23
|
-
4. **Expected**: System marks `common-config` as completed, proceeds to level 2 packages (`logging`, `docs-template`)
|
|
24
|
-
5. **Actual**: All 15 packages remain perpetually skipped, execution completes immediately without attempting any publishes
|
|
25
|
-
|
|
26
|
-
## Root Cause Analysis
|
|
27
|
-
|
|
28
|
-
### The Bug
|
|
29
|
-
|
|
30
|
-
The issue was in two places where checkpoint state was loaded without reassessing which packages could now proceed:
|
|
31
|
-
|
|
32
|
-
#### 1. `DynamicTaskPool.loadCheckpoint()` (src/execution/DynamicTaskPool.ts)
|
|
33
|
-
|
|
34
|
-
```typescript
|
|
35
|
-
// BEFORE (BROKEN)
|
|
36
|
-
private async loadCheckpoint(): Promise<void> {
|
|
37
|
-
const checkpoint = await this.checkpointManager.load();
|
|
38
|
-
// ... restore metadata ...
|
|
39
|
-
|
|
40
|
-
this.state = checkpoint.state; // Directly assigns checkpoint state
|
|
41
|
-
|
|
42
|
-
// Clear running state
|
|
43
|
-
for (const running of this.state.running) {
|
|
44
|
-
this.state.pending.push(running.name);
|
|
45
|
-
}
|
|
46
|
-
this.state.running = [];
|
|
47
|
-
}
|
|
48
|
-
```
|
|
49
|
-
|
|
50
|
-
**Problem**: When the checkpoint state is directly assigned, packages in the `skipped` array remain there. The subsequent `updateReadyQueue()` call only checks packages in the `pending` array, so skipped packages are never reassessed even though their dependencies might now be complete.
|
|
51
|
-
|
|
52
|
-
#### 2. `RecoveryManager.updateReadyState()` (src/execution/RecoveryManager.ts)
|
|
53
|
-
|
|
54
|
-
```typescript
|
|
55
|
-
// BEFORE (BROKEN)
|
|
56
|
-
private updateReadyState(): void {
|
|
57
|
-
// Move packages from pending to ready if dependencies met
|
|
58
|
-
const nowReady: string[] = [];
|
|
59
|
-
|
|
60
|
-
for (const pkg of this.checkpoint.state.pending) { // Only checks pending!
|
|
61
|
-
const deps = this.graph.edges.get(pkg) || new Set();
|
|
62
|
-
const allDepsCompleted = Array.from(deps).every(dep =>
|
|
63
|
-
this.checkpoint.state.completed.includes(dep)
|
|
64
|
-
);
|
|
65
|
-
|
|
66
|
-
if (allDepsCompleted) {
|
|
67
|
-
nowReady.push(pkg);
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
// Move to ready
|
|
72
|
-
for (const pkg of nowReady) {
|
|
73
|
-
this.checkpoint.state.pending = this.checkpoint.state.pending.filter(p => p !== pkg);
|
|
74
|
-
this.checkpoint.state.ready.push(pkg);
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
```
|
|
78
|
-
|
|
79
|
-
**Problem**: This method only evaluated packages in the `pending` state. Packages that were previously skipped due to failed dependencies were never reevaluated, even after their dependencies were marked as completed via `--mark-completed`.
|
|
80
|
-
|
|
81
|
-
### Execution Flow Showing the Bug
|
|
82
|
-
|
|
83
|
-
```
|
|
84
|
-
Initial Run:
|
|
85
|
-
common-config → FAILS
|
|
86
|
-
[Dependency checker blocks all dependents]
|
|
87
|
-
→ logging, docs-template, core, ... → all moved to SKIPPED
|
|
88
|
-
|
|
89
|
-
User runs --mark-completed "common-config":
|
|
90
|
-
RecoveryManager.markCompleted():
|
|
91
|
-
✓ Adds common-config to completed array
|
|
92
|
-
✓ Calls updateReadyState()
|
|
93
|
-
✓ updateReadyState() only checks pending array (empty!)
|
|
94
|
-
✓ Skipped packages never reevaluated
|
|
95
|
-
✓ Checkpoint saved with skipped=[logging, docs-template, ...]
|
|
96
|
-
|
|
97
|
-
User runs --continue:
|
|
98
|
-
DynamicTaskPool.loadCheckpoint():
|
|
99
|
-
✓ Loads checkpoint
|
|
100
|
-
✓ this.state = checkpoint.state (skipped array restored!)
|
|
101
|
-
✓ Calls updateReadyQueue()
|
|
102
|
-
✓ updateReadyQueue() only checks pending array (empty!)
|
|
103
|
-
✓ Skipped packages never reevaluated
|
|
104
|
-
|
|
105
|
-
Main execution loop:
|
|
106
|
-
✓ pending.length === 0
|
|
107
|
-
✓ ready.length === 0
|
|
108
|
-
✓ runningTasks.size === 0
|
|
109
|
-
✓ isComplete() returns true
|
|
110
|
-
✓ Execution exits immediately
|
|
111
|
-
|
|
112
|
-
Result: "✅ Published (1): common-config, ⊘ Skipped (15): [all others]"
|
|
113
|
-
```
|
|
114
|
-
|
|
115
|
-
## The Fix
|
|
116
|
-
|
|
117
|
-
### 1. Dynamic Dependency Resolution in `DynamicTaskPool.loadCheckpoint()`
|
|
118
|
-
|
|
119
|
-
Added logic to re-evaluate skipped packages after loading checkpoint:
|
|
120
|
-
|
|
121
|
-
```typescript
|
|
122
|
-
// AFTER (FIXED)
|
|
123
|
-
private async loadCheckpoint(): Promise<void> {
|
|
124
|
-
const checkpoint = await this.checkpointManager.load();
|
|
125
|
-
// ... restore metadata ...
|
|
126
|
-
|
|
127
|
-
this.state = checkpoint.state;
|
|
128
|
-
|
|
129
|
-
// Clear running state
|
|
130
|
-
for (const running of this.state.running) {
|
|
131
|
-
this.state.pending.push(running.name);
|
|
132
|
-
}
|
|
133
|
-
this.state.running = [];
|
|
134
|
-
|
|
135
|
-
// CRITICAL FIX: Re-evaluate skipped packages
|
|
136
|
-
// After loading checkpoint (especially with --mark-completed), packages that were
|
|
137
|
-
// skipped due to failed dependencies might now be eligible to run if those
|
|
138
|
-
// dependencies are now completed. Move them back to pending for reassessment.
|
|
139
|
-
const unblocked: string[] = [];
|
|
140
|
-
for (const packageName of this.state.skipped) {
|
|
141
|
-
// Check if all dependencies are now completed
|
|
142
|
-
const dependencies = this.graph.edges.get(packageName) || new Set();
|
|
143
|
-
const allDepsCompleted = Array.from(dependencies).every(dep =>
|
|
144
|
-
this.state.completed.includes(dep) || this.state.skippedNoChanges.includes(dep)
|
|
145
|
-
);
|
|
146
|
-
|
|
147
|
-
// Check if any dependencies are still failed
|
|
148
|
-
const anyDepsFailed = Array.from(dependencies).some(dep =>
|
|
149
|
-
this.state.failed.some(f => f.name === dep)
|
|
150
|
-
);
|
|
151
|
-
|
|
152
|
-
if (allDepsCompleted && !anyDepsFailed) {
|
|
153
|
-
unblocked.push(packageName);
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
// Move unblocked packages back to pending
|
|
158
|
-
if (unblocked.length > 0) {
|
|
159
|
-
this.logger.info(`✓ Unblocked ${unblocked.length} package(s): ${unblocked.join(', ')}`);
|
|
160
|
-
for (const packageName of unblocked) {
|
|
161
|
-
this.state.skipped = this.state.skipped.filter(p => p !== packageName);
|
|
162
|
-
this.state.pending.push(packageName);
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
```
|
|
167
|
-
|
|
168
|
-
### 2. Enhanced `RecoveryManager.updateReadyState()`
|
|
169
|
-
|
|
170
|
-
Modified to check skipped packages first, then move eligible ones back to pending:
|
|
171
|
-
|
|
172
|
-
```typescript
|
|
173
|
-
// AFTER (FIXED)
|
|
174
|
-
private updateReadyState(): void {
|
|
175
|
-
// CRITICAL FIX: First, re-evaluate skipped packages
|
|
176
|
-
// Packages that were skipped due to failed dependencies might now be eligible
|
|
177
|
-
// to run if those dependencies have been completed (e.g., via --mark-completed)
|
|
178
|
-
const unblocked: string[] = [];
|
|
179
|
-
for (const pkg of this.checkpoint.state.skipped) {
|
|
180
|
-
const deps = this.graph.edges.get(pkg) || new Set();
|
|
181
|
-
const allDepsCompleted = Array.from(deps).every(dep =>
|
|
182
|
-
this.checkpoint.state.completed.includes(dep) ||
|
|
183
|
-
this.checkpoint.state.skippedNoChanges.includes(dep)
|
|
184
|
-
);
|
|
185
|
-
|
|
186
|
-
// Check if any dependencies are still failed
|
|
187
|
-
const anyDepsFailed = Array.from(deps).some(dep =>
|
|
188
|
-
this.checkpoint.state.failed.some(f => f.name === dep)
|
|
189
|
-
);
|
|
190
|
-
|
|
191
|
-
if (allDepsCompleted && !anyDepsFailed) {
|
|
192
|
-
unblocked.push(pkg);
|
|
193
|
-
}
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
// Move unblocked packages back to pending
|
|
197
|
-
for (const pkg of unblocked) {
|
|
198
|
-
this.checkpoint.state.skipped = this.checkpoint.state.skipped.filter(p => p !== pkg);
|
|
199
|
-
this.checkpoint.state.pending.push(pkg);
|
|
200
|
-
this.logger.info(`↻ ${pkg} unblocked (dependencies now satisfied)`);
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
// Move packages from pending to ready if dependencies met
|
|
204
|
-
const nowReady: string[] = [];
|
|
205
|
-
|
|
206
|
-
for (const pkg of this.checkpoint.state.pending) {
|
|
207
|
-
const deps = this.graph.edges.get(pkg) || new Set();
|
|
208
|
-
const allDepsCompleted = Array.from(deps).every(dep =>
|
|
209
|
-
this.checkpoint.state.completed.includes(dep) ||
|
|
210
|
-
this.checkpoint.state.skippedNoChanges.includes(dep)
|
|
211
|
-
);
|
|
212
|
-
|
|
213
|
-
if (allDepsCompleted) {
|
|
214
|
-
nowReady.push(pkg);
|
|
215
|
-
}
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
for (const pkg of nowReady) {
|
|
219
|
-
this.checkpoint.state.pending = this.checkpoint.state.pending.filter(p => p !== pkg);
|
|
220
|
-
this.checkpoint.state.ready.push(pkg);
|
|
221
|
-
}
|
|
222
|
-
}
|
|
223
|
-
```
|
|
224
|
-
|
|
225
|
-
### 3. Improved User Feedback in `markCompleted()`
|
|
226
|
-
|
|
227
|
-
Enhanced to show what was unblocked:
|
|
228
|
-
|
|
229
|
-
```typescript
|
|
230
|
-
// Update ready queue and count what got unblocked
|
|
231
|
-
const beforeSkipped = this.checkpoint.state.skipped.length;
|
|
232
|
-
const beforeReady = this.checkpoint.state.ready.length;
|
|
233
|
-
|
|
234
|
-
this.updateReadyState();
|
|
235
|
-
|
|
236
|
-
const afterSkipped = this.checkpoint.state.skipped.length;
|
|
237
|
-
const afterReady = this.checkpoint.state.ready.length;
|
|
238
|
-
|
|
239
|
-
const unblockedCount = beforeSkipped - afterSkipped;
|
|
240
|
-
const newReadyCount = afterReady - beforeReady;
|
|
241
|
-
|
|
242
|
-
// Save checkpoint
|
|
243
|
-
await this.saveCheckpoint();
|
|
244
|
-
|
|
245
|
-
this.logger.info('State updated successfully');
|
|
246
|
-
|
|
247
|
-
if (unblockedCount > 0) {
|
|
248
|
-
this.logger.info(`✓ Unblocked ${unblockedCount} package(s)`);
|
|
249
|
-
}
|
|
250
|
-
if (newReadyCount > 0) {
|
|
251
|
-
this.logger.info(`✓ ${newReadyCount} package(s) ready to execute`);
|
|
252
|
-
}
|
|
253
|
-
if (unblockedCount === 0 && newReadyCount === 0 && this.checkpoint.state.skipped.length > 0) {
|
|
254
|
-
this.logger.warn(`⚠️ No packages unblocked. ${this.checkpoint.state.skipped.length} packages still blocked by dependencies.`);
|
|
255
|
-
this.logger.warn(' Use --status to see what\'s blocking them.');
|
|
256
|
-
}
|
|
257
|
-
```
|
|
258
|
-
|
|
259
|
-
### 4. Enhanced Status Display
|
|
260
|
-
|
|
261
|
-
Improved `showStatus()` to show detailed dependency states:
|
|
262
|
-
|
|
263
|
-
```typescript
|
|
264
|
-
// Skipped packages with dependency details
|
|
265
|
-
if (skipped > 0) {
|
|
266
|
-
lines.push('🔒 Blocked Packages (dependency issues):');
|
|
267
|
-
for (const pkgName of this.checkpoint.state.skipped) {
|
|
268
|
-
const deps = this.graph.edges.get(pkgName) || new Set();
|
|
269
|
-
const depStatus = Array.from(deps).map(dep => {
|
|
270
|
-
if (this.checkpoint.state.completed.includes(dep) ||
|
|
271
|
-
this.checkpoint.state.skippedNoChanges.includes(dep)) {
|
|
272
|
-
return `${dep} ✓`;
|
|
273
|
-
} else if (this.checkpoint.state.failed.some(f => f.name === dep)) {
|
|
274
|
-
return `${dep} ❌`;
|
|
275
|
-
} else if (this.checkpoint.state.running.some(r => r.name === dep)) {
|
|
276
|
-
return `${dep} ⏳`;
|
|
277
|
-
} else if (this.checkpoint.state.skipped.includes(dep)) {
|
|
278
|
-
return `${dep} 🔒`;
|
|
279
|
-
} else if (this.checkpoint.state.pending.includes(dep) ||
|
|
280
|
-
this.checkpoint.state.ready.includes(dep)) {
|
|
281
|
-
return `${dep} ⏳`;
|
|
282
|
-
} else {
|
|
283
|
-
return `${dep} ❓`;
|
|
284
|
-
}
|
|
285
|
-
});
|
|
286
|
-
|
|
287
|
-
lines.push(` • ${pkgName}`);
|
|
288
|
-
if (depStatus.length > 0) {
|
|
289
|
-
lines.push(` Dependencies: ${depStatus.join(', ')}`);
|
|
290
|
-
}
|
|
291
|
-
}
|
|
292
|
-
lines.push('');
|
|
293
|
-
lines.push('Legend: ✓ = complete, ❌ = failed, ⏳ = pending/running, 🔒 = blocked');
|
|
294
|
-
lines.push('');
|
|
295
|
-
}
|
|
296
|
-
|
|
297
|
-
// Ready to execute
|
|
298
|
-
if (this.checkpoint.state.ready.length > 0) {
|
|
299
|
-
lines.push('⏳ Ready to Execute:');
|
|
300
|
-
for (const pkgName of this.checkpoint.state.ready) {
|
|
301
|
-
const deps = this.graph.edges.get(pkgName) || new Set();
|
|
302
|
-
if (deps.size === 0) {
|
|
303
|
-
lines.push(` • ${pkgName} (no dependencies)`);
|
|
304
|
-
} else {
|
|
305
|
-
const depList = Array.from(deps).join(', ');
|
|
306
|
-
lines.push(` • ${pkgName} (depends on: ${depList})`);
|
|
307
|
-
}
|
|
308
|
-
}
|
|
309
|
-
lines.push('');
|
|
310
|
-
}
|
|
311
|
-
```
|
|
312
|
-
|
|
313
|
-
## Test Coverage
|
|
314
|
-
|
|
315
|
-
Added comprehensive test cases in `tests/execution/RecoveryManager.test.ts`:
|
|
316
|
-
|
|
317
|
-
### Test Case 1: Basic Unblocking
|
|
318
|
-
```typescript
|
|
319
|
-
it('should unblock skipped packages when dependencies are marked completed', async () => {
|
|
320
|
-
// Simulates: A fails → B,C,D skipped → A marked complete → B,C move to ready, D stays blocked
|
|
321
|
-
// Verifies packages with satisfied dependencies are immediately unblocked
|
|
322
|
-
});
|
|
323
|
-
```
|
|
324
|
-
|
|
325
|
-
### Test Case 2: Multi-Level Dependencies
|
|
326
|
-
```typescript
|
|
327
|
-
it('should handle multi-level dependency unblocking', async () => {
|
|
328
|
-
// Tests cascading unblock: A → B → C
|
|
329
|
-
// When A completes, B should be ready
|
|
330
|
-
// When B completes, C should be ready
|
|
331
|
-
});
|
|
332
|
-
```
|
|
333
|
-
|
|
334
|
-
### Test Case 3: Partial Dependency Satisfaction
|
|
335
|
-
```typescript
|
|
336
|
-
it('should not unblock packages if some dependencies still failed', async () => {
|
|
337
|
-
// Ensures packages stay blocked if ANY dependency is still failed
|
|
338
|
-
// C depends on A and B, A completes but B is failed → C stays blocked
|
|
339
|
-
});
|
|
340
|
-
```
|
|
341
|
-
|
|
342
|
-
All 23 tests pass, including the 3 new critical test cases.
|
|
343
|
-
|
|
344
|
-
## Expected Behavior After Fix
|
|
345
|
-
|
|
346
|
-
### Scenario: Fjell Monorepo (16 packages)
|
|
347
|
-
|
|
348
|
-
```bash
|
|
349
|
-
# Initial attempt - common-config already published
|
|
350
|
-
$ kodrdriv tree publish --parallel --model "gpt-5-mini"
|
|
351
|
-
❌ @fjell/common-config (already published)
|
|
352
|
-
⊘ Skipped due to dependencies (15): [all others]
|
|
353
|
-
|
|
354
|
-
# Recovery with fix
|
|
355
|
-
$ kodrdriv tree publish --parallel --mark-completed "common-config" --continue
|
|
356
|
-
✓ Marked @fjell/common-config as completed
|
|
357
|
-
↻ @fjell/logging unblocked (dependencies now satisfied)
|
|
358
|
-
↻ @fjell/docs-template unblocked (dependencies now satisfied)
|
|
359
|
-
✓ Unblocked 2 package(s)
|
|
360
|
-
✓ 2 package(s) ready to execute
|
|
361
|
-
|
|
362
|
-
📦 Executing 2 packages in parallel
|
|
363
|
-
[1/15] ✅ @fjell/logging
|
|
364
|
-
[2/15] ✅ @fjell/docs-template
|
|
365
|
-
[3/15] ✅ @fjell/core
|
|
366
|
-
[4/15] ✅ @fjell/http-api
|
|
367
|
-
... continues publishing all remaining packages ...
|
|
368
|
-
```
|
|
369
|
-
|
|
370
|
-
### Status Output Example
|
|
371
|
-
|
|
372
|
-
```bash
|
|
373
|
-
$ kodrdriv tree --status-parallel
|
|
374
|
-
|
|
375
|
-
═══════════════════════════════════════
|
|
376
|
-
Parallel Execution Status
|
|
377
|
-
═══════════════════════════════════════
|
|
378
|
-
|
|
379
|
-
Execution ID: 27ea3c44-e707-46d6-b411-bfaef689d240
|
|
380
|
-
Started: 12/12/2025, 6:20:00 AM
|
|
381
|
-
Last Updated: 12/12/2025, 6:21:30 AM
|
|
382
|
-
|
|
383
|
-
📊 Progress:
|
|
384
|
-
Completed: 3/16 (18%)
|
|
385
|
-
Skipped (no changes): 0
|
|
386
|
-
Running: 2
|
|
387
|
-
Pending: 8
|
|
388
|
-
Failed: 0
|
|
389
|
-
Skipped (dependency failed): 3
|
|
390
|
-
|
|
391
|
-
Progress: [███░░░░░░░░░░░░░░░░░] 18%
|
|
392
|
-
|
|
393
|
-
🔄 Currently Running:
|
|
394
|
-
• @fjell/core (1m 23s)
|
|
395
|
-
• @fjell/http-api (45s)
|
|
396
|
-
|
|
397
|
-
⏳ Ready to Execute:
|
|
398
|
-
• @fjell/registry (depends on: @fjell/core, @fjell/common-config)
|
|
399
|
-
• @fjell/lib (depends on: @fjell/core)
|
|
400
|
-
|
|
401
|
-
🔒 Blocked Packages (dependency issues):
|
|
402
|
-
• @fjell/client-api
|
|
403
|
-
Dependencies: @fjell/core ⏳, @fjell/lib ⏳, @fjell/common-config ✓
|
|
404
|
-
• @fjell/sample-app
|
|
405
|
-
Dependencies: @fjell/client-api 🔒, @fjell/core ⏳
|
|
406
|
-
• @fjell/express-router
|
|
407
|
-
Dependencies: @fjell/http-api ⏳, @fjell/lib ⏳
|
|
408
|
-
|
|
409
|
-
Legend: ✓ = complete, ❌ = failed, ⏳ = pending/running, 🔒 = blocked
|
|
410
|
-
```
|
|
411
|
-
|
|
412
|
-
## Impact
|
|
413
|
-
|
|
414
|
-
### Before Fix
|
|
415
|
-
- ❌ Recovery from partial failures completely broken
|
|
416
|
-
- ❌ `--mark-completed` effectively useless
|
|
417
|
-
- ❌ Any scenario where a package was already published blocked entire tree
|
|
418
|
-
- ❌ 20+ hours wasted on manual workarounds
|
|
419
|
-
|
|
420
|
-
### After Fix
|
|
421
|
-
- ✅ Recovery works as designed
|
|
422
|
-
- ✅ Packages unblock as soon as dependencies are satisfied
|
|
423
|
-
- ✅ Clear feedback showing what was unblocked
|
|
424
|
-
- ✅ Detailed status showing exactly why packages are blocked
|
|
425
|
-
- ✅ Parallel publishing viable for production use
|
|
426
|
-
|
|
427
|
-
## Files Modified
|
|
428
|
-
|
|
429
|
-
- `src/execution/DynamicTaskPool.ts` - Added checkpoint state reassessment
|
|
430
|
-
- `src/execution/RecoveryManager.ts` - Enhanced updateReadyState() and markCompleted()
|
|
431
|
-
- `tests/execution/RecoveryManager.test.ts` - Added 3 critical test cases
|
|
432
|
-
|
|
433
|
-
## Backward Compatibility
|
|
434
|
-
|
|
435
|
-
✅ **Fully backward compatible** - No breaking changes to API or command-line interface.
|
|
436
|
-
|
|
437
|
-
Existing checkpoints will automatically benefit from the fix on next `--continue` attempt.
|
|
438
|
-
|
|
439
|
-
## Related Issues
|
|
440
|
-
|
|
441
|
-
This fix resolves the core issue described in the user's detailed bug report, addressing:
|
|
442
|
-
- ✅ Infinite loop after `--mark-completed`
|
|
443
|
-
- ✅ All dependent packages remaining perpetually skipped
|
|
444
|
-
- ✅ Checkpoint system defeating its own purpose
|
|
445
|
-
- ✅ Impossible recovery from partial publish failures
|
|
446
|
-
|
|
447
|
-
## Version
|
|
448
|
-
|
|
449
|
-
Fixed in: **kodrdriv 1.2.29-dev.0**
|
|
450
|
-
|