ppef 1.0.0 → 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +172 -0
- package/dist/__tests__/framework-pipeline.integration.test.d.ts +7 -0
- package/dist/__tests__/framework-pipeline.integration.test.d.ts.map +1 -0
- package/dist/__tests__/framework-pipeline.integration.test.js +413 -0
- package/dist/__tests__/framework-pipeline.integration.test.js.map +1 -0
- package/dist/__tests__/registry-executor.integration.test.d.ts +5 -0
- package/dist/__tests__/registry-executor.integration.test.d.ts.map +1 -0
- package/dist/__tests__/registry-executor.integration.test.js +349 -0
- package/dist/__tests__/registry-executor.integration.test.js.map +1 -0
- package/dist/__tests__/test-helpers.d.ts +94 -0
- package/dist/__tests__/test-helpers.d.ts.map +1 -0
- package/dist/__tests__/test-helpers.js +271 -0
- package/dist/__tests__/test-helpers.js.map +1 -0
- package/dist/aggregation/aggregators.d.ts +54 -0
- package/dist/aggregation/aggregators.d.ts.map +1 -0
- package/dist/aggregation/aggregators.js +228 -0
- package/dist/aggregation/aggregators.js.map +1 -0
- package/dist/aggregation/index.d.ts +8 -0
- package/dist/aggregation/index.d.ts.map +1 -0
- package/dist/aggregation/index.js +8 -0
- package/dist/aggregation/index.js.map +1 -0
- package/dist/aggregation/pipeline.d.ts +38 -0
- package/dist/aggregation/pipeline.d.ts.map +1 -0
- package/dist/aggregation/pipeline.js +198 -0
- package/dist/aggregation/pipeline.js.map +1 -0
- package/dist/claims/evaluator.d.ts +33 -0
- package/dist/claims/evaluator.d.ts.map +1 -0
- package/dist/claims/evaluator.js +174 -0
- package/dist/claims/evaluator.js.map +1 -0
- package/dist/claims/index.d.ts +7 -0
- package/dist/claims/index.d.ts.map +1 -0
- package/dist/claims/index.js +7 -0
- package/dist/claims/index.js.map +1 -0
- package/dist/collector/index.d.ts +8 -0
- package/dist/collector/index.d.ts.map +1 -0
- package/dist/collector/index.js +8 -0
- package/dist/collector/index.js.map +1 -0
- package/dist/collector/result-collector.d.ts +159 -0
- package/dist/collector/result-collector.d.ts.map +1 -0
- package/dist/collector/result-collector.js +213 -0
- package/dist/collector/result-collector.js.map +1 -0
- package/dist/collector/schema.d.ts +34 -0
- package/dist/collector/schema.d.ts.map +1 -0
- package/dist/collector/schema.js +145 -0
- package/dist/collector/schema.js.map +1 -0
- package/dist/executor/__tests__/checkpoint-hash-bug.diagnostic.test.d.ts +10 -0
- package/dist/executor/__tests__/checkpoint-hash-bug.diagnostic.test.d.ts.map +1 -0
- package/dist/executor/__tests__/checkpoint-hash-bug.diagnostic.test.js +122 -0
- package/dist/executor/__tests__/checkpoint-hash-bug.diagnostic.test.js.map +1 -0
- package/dist/executor/__tests__/checkpoint-manager.integration.test.d.ts +7 -0
- package/dist/executor/__tests__/checkpoint-manager.integration.test.d.ts.map +1 -0
- package/dist/executor/__tests__/checkpoint-manager.integration.test.js +330 -0
- package/dist/executor/__tests__/checkpoint-manager.integration.test.js.map +1 -0
- package/dist/executor/__tests__/checkpoint-manager.unit.test.d.ts +7 -0
- package/dist/executor/__tests__/checkpoint-manager.unit.test.d.ts.map +1 -0
- package/dist/executor/__tests__/checkpoint-manager.unit.test.js +449 -0
- package/dist/executor/__tests__/checkpoint-manager.unit.test.js.map +1 -0
- package/dist/executor/__tests__/checkpoint-merge-bug.diagnostic.test.d.ts +11 -0
- package/dist/executor/__tests__/checkpoint-merge-bug.diagnostic.test.d.ts.map +1 -0
- package/dist/executor/__tests__/checkpoint-merge-bug.diagnostic.test.js +224 -0
- package/dist/executor/__tests__/checkpoint-merge-bug.diagnostic.test.js.map +1 -0
- package/dist/executor/__tests__/checkpoint-merge-bug.unit.test.d.ts +8 -0
- package/dist/executor/__tests__/checkpoint-merge-bug.unit.test.d.ts.map +1 -0
- package/dist/executor/__tests__/checkpoint-merge-bug.unit.test.js +164 -0
- package/dist/executor/__tests__/checkpoint-merge-bug.unit.test.js.map +1 -0
- package/dist/executor/__tests__/checkpoint-storage.unit.test.d.ts +7 -0
- package/dist/executor/__tests__/checkpoint-storage.unit.test.d.ts.map +1 -0
- package/dist/executor/__tests__/checkpoint-storage.unit.test.js +386 -0
- package/dist/executor/__tests__/checkpoint-storage.unit.test.js.map +1 -0
- package/dist/executor/__tests__/executor.unit.test.d.ts +7 -0
- package/dist/executor/__tests__/executor.unit.test.d.ts.map +1 -0
- package/dist/executor/__tests__/executor.unit.test.js +134 -0
- package/dist/executor/__tests__/executor.unit.test.js.map +1 -0
- package/dist/executor/__tests__/parallel-checkpoint-merge.integration.test.d.ts +12 -0
- package/dist/executor/__tests__/parallel-checkpoint-merge.integration.test.d.ts.map +1 -0
- package/dist/executor/__tests__/parallel-checkpoint-merge.integration.test.js +196 -0
- package/dist/executor/__tests__/parallel-checkpoint-merge.integration.test.js.map +1 -0
- package/dist/executor/__tests__/parallel-executor.integration.test.d.ts +7 -0
- package/dist/executor/__tests__/parallel-executor.integration.test.d.ts.map +1 -0
- package/dist/executor/__tests__/parallel-executor.integration.test.js +249 -0
- package/dist/executor/__tests__/parallel-executor.integration.test.js.map +1 -0
- package/dist/executor/__tests__/parallel-executor.unit.test.d.ts +7 -0
- package/dist/executor/__tests__/parallel-executor.unit.test.d.ts.map +1 -0
- package/dist/executor/__tests__/parallel-executor.unit.test.js +203 -0
- package/dist/executor/__tests__/parallel-executor.unit.test.js.map +1 -0
- package/dist/executor/checkpoint-manager.d.ts +231 -0
- package/dist/executor/checkpoint-manager.d.ts.map +1 -0
- package/dist/executor/checkpoint-manager.js +395 -0
- package/dist/executor/checkpoint-manager.js.map +1 -0
- package/dist/executor/checkpoint-storage.d.ts +230 -0
- package/dist/executor/checkpoint-storage.d.ts.map +1 -0
- package/dist/executor/checkpoint-storage.js +370 -0
- package/dist/executor/checkpoint-storage.js.map +1 -0
- package/dist/executor/checkpoint-types.d.ts +48 -0
- package/dist/executor/checkpoint-types.d.ts.map +1 -0
- package/dist/executor/checkpoint-types.js +8 -0
- package/dist/executor/checkpoint-types.js.map +1 -0
- package/dist/executor/executor.d.ts +164 -0
- package/dist/executor/executor.d.ts.map +1 -0
- package/dist/executor/executor.js +408 -0
- package/dist/executor/executor.js.map +1 -0
- package/dist/executor/index.d.ts +11 -0
- package/dist/executor/index.d.ts.map +1 -0
- package/dist/executor/index.js +11 -0
- package/dist/executor/index.js.map +1 -0
- package/dist/executor/memory-monitor.d.ts +115 -0
- package/dist/executor/memory-monitor.d.ts.map +1 -0
- package/dist/executor/memory-monitor.js +168 -0
- package/dist/executor/memory-monitor.js.map +1 -0
- package/dist/executor/parallel-executor.d.ts +53 -0
- package/dist/executor/parallel-executor.d.ts.map +1 -0
- package/dist/executor/parallel-executor.js +194 -0
- package/dist/executor/parallel-executor.js.map +1 -0
- package/dist/executor/run-id.d.ts +71 -0
- package/dist/executor/run-id.d.ts.map +1 -0
- package/dist/executor/run-id.js +67 -0
- package/dist/executor/run-id.js.map +1 -0
- package/dist/executor/worker-entry.d.ts +8 -0
- package/dist/executor/worker-entry.d.ts.map +1 -0
- package/dist/executor/worker-entry.js +67 -0
- package/dist/executor/worker-entry.js.map +1 -0
- package/dist/index.cjs +11 -0
- package/dist/index.d.ts +15 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +24 -0
- package/dist/index.js.map +1 -0
- package/dist/registry/case-registry.d.ts +113 -0
- package/dist/registry/case-registry.d.ts.map +1 -0
- package/dist/registry/case-registry.js +160 -0
- package/dist/registry/case-registry.js.map +1 -0
- package/dist/registry/index.d.ts +8 -0
- package/dist/registry/index.d.ts.map +1 -0
- package/dist/registry/index.js +8 -0
- package/dist/registry/index.js.map +1 -0
- package/dist/registry/sut-registry.d.ts +96 -0
- package/dist/registry/sut-registry.d.ts.map +1 -0
- package/dist/registry/sut-registry.js +126 -0
- package/dist/registry/sut-registry.js.map +1 -0
- package/dist/renderers/index.d.ts +10 -0
- package/dist/renderers/index.d.ts.map +1 -0
- package/dist/renderers/index.js +9 -0
- package/dist/renderers/index.js.map +1 -0
- package/dist/renderers/latex-renderer.d.ts +84 -0
- package/dist/renderers/latex-renderer.d.ts.map +1 -0
- package/dist/renderers/latex-renderer.js +208 -0
- package/dist/renderers/latex-renderer.js.map +1 -0
- package/dist/renderers/types.d.ts +106 -0
- package/dist/renderers/types.d.ts.map +1 -0
- package/dist/renderers/types.js +23 -0
- package/dist/renderers/types.js.map +1 -0
- package/dist/robustness/analyzer.d.ts +61 -0
- package/dist/robustness/analyzer.d.ts.map +1 -0
- package/dist/robustness/analyzer.js +191 -0
- package/dist/robustness/analyzer.js.map +1 -0
- package/dist/robustness/index.d.ts +8 -0
- package/dist/robustness/index.d.ts.map +1 -0
- package/dist/robustness/index.js +8 -0
- package/dist/robustness/index.js.map +1 -0
- package/dist/robustness/perturbations.d.ts +46 -0
- package/dist/robustness/perturbations.d.ts.map +1 -0
- package/dist/robustness/perturbations.js +184 -0
- package/dist/robustness/perturbations.js.map +1 -0
- package/dist/statistical/index.d.ts +8 -0
- package/dist/statistical/index.d.ts.map +1 -0
- package/dist/statistical/index.js +8 -0
- package/dist/statistical/index.js.map +1 -0
- package/dist/statistical/mann-whitney-u.d.ts +62 -0
- package/dist/statistical/mann-whitney-u.d.ts.map +1 -0
- package/dist/statistical/mann-whitney-u.js +127 -0
- package/dist/statistical/mann-whitney-u.js.map +1 -0
- package/dist/types/aggregate.d.ts +124 -0
- package/dist/types/aggregate.d.ts.map +1 -0
- package/dist/types/aggregate.js +9 -0
- package/dist/types/aggregate.js.map +1 -0
- package/dist/types/case.d.ts +105 -0
- package/dist/types/case.d.ts.map +1 -0
- package/dist/types/case.js +10 -0
- package/dist/types/case.js.map +1 -0
- package/dist/types/claims.d.ts +122 -0
- package/dist/types/claims.d.ts.map +1 -0
- package/dist/types/claims.js +14 -0
- package/dist/types/claims.js.map +1 -0
- package/dist/types/index.d.ts +12 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +7 -0
- package/dist/types/index.js.map +1 -0
- package/dist/types/perturbation.d.ts +105 -0
- package/dist/types/perturbation.d.ts.map +1 -0
- package/dist/types/perturbation.js +9 -0
- package/dist/types/perturbation.js.map +1 -0
- package/dist/types/result.d.ts +150 -0
- package/dist/types/result.d.ts.map +1 -0
- package/dist/types/result.js +12 -0
- package/dist/types/result.js.map +1 -0
- package/dist/types/sut.d.ts +128 -0
- package/dist/types/sut.d.ts.map +1 -0
- package/dist/types/sut.js +12 -0
- package/dist/types/sut.js.map +1 -0
- package/package.json +283 -7
|
@@ -0,0 +1,395 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Checkpoint Manager for Experiment Execution
|
|
3
|
+
*
|
|
4
|
+
* Enables resumable experiment execution by:
|
|
5
|
+
* 1. Writing incremental checkpoints after each completed run
|
|
6
|
+
* 2. Detecting configuration changes to invalidate stale checkpoints
|
|
7
|
+
* 3. Skipping completed runs when resuming
|
|
8
|
+
* 4. Supporting sharded checkpoints for parallel workers
|
|
9
|
+
*
|
|
10
|
+
* Supports pluggable storage backends:
|
|
11
|
+
* - FileStorage: JSON files (fast, local)
|
|
12
|
+
* - GitStorage: Git notes (version controlled, shareable)
|
|
13
|
+
*
|
|
14
|
+
* Dependency Injection:
|
|
15
|
+
* - Storage backend can be injected for testing
|
|
16
|
+
* - Lock implementation can be injected for custom concurrency control
|
|
17
|
+
*
|
|
18
|
+
* Usage:
|
|
19
|
+
* ```typescript
|
|
20
|
+
* // Direct instantiation with injected storage
|
|
21
|
+
* const storage = new FileStorage("results/execute/checkpoint.json");
|
|
22
|
+
* const lock = new InMemoryLock();
|
|
23
|
+
* const checkpoint = new CheckpointManager({ storage, lock });
|
|
24
|
+
*
|
|
25
|
+
* // Sharded mode for parallel workers
|
|
26
|
+
* const shardStorage = new FileStorage("results/execute/checkpoint-worker-0.json");
|
|
27
|
+
* const checkpoint = new CheckpointManager({
|
|
28
|
+
* storage: shardStorage,
|
|
29
|
+
* lock,
|
|
30
|
+
* workerIndex: 0,
|
|
31
|
+
* totalWorkers: 4,
|
|
32
|
+
* basePath: "results/execute"
|
|
33
|
+
* });
|
|
34
|
+
*
|
|
35
|
+
* await checkpoint.load();
|
|
36
|
+
*
|
|
37
|
+
* if (checkpoint.isStale(suts, cases, config)) {
|
|
38
|
+
* checkpoint.invalidate();
|
|
39
|
+
* }
|
|
40
|
+
*
|
|
41
|
+
* const remainingRuns = plannedRuns.filter(
|
|
42
|
+
* run => !checkpoint.isCompleted(run.runId)
|
|
43
|
+
* );
|
|
44
|
+
*
|
|
45
|
+
* executor.execute({...}, {
|
|
46
|
+
* onResult: (result) => checkpoint.saveIncremental(result)
|
|
47
|
+
* });
|
|
48
|
+
*
|
|
49
|
+
* // After all workers complete, merge shards
|
|
50
|
+
* await checkpoint.mergeShards([
|
|
51
|
+
* "results/execute/checkpoint-worker-0.json",
|
|
52
|
+
* "results/execute/checkpoint-worker-1.json",
|
|
53
|
+
* ...
|
|
54
|
+
* ]);
|
|
55
|
+
* ```
|
|
56
|
+
*/
|
|
57
|
+
import { createHash } from "node:crypto";
|
|
58
|
+
import { FileStorage, InMemoryLock } from "./checkpoint-storage.js";
|
|
59
|
+
export { InMemoryLock } from "./checkpoint-storage.js";
|
|
60
|
+
/**
|
|
61
|
+
* Checkpoint manager for resumable execution.
|
|
62
|
+
*
|
|
63
|
+
* Uses dependency injection for storage and lock.
|
|
64
|
+
* Enables sharded checkpoints for parallel worker execution.
|
|
65
|
+
*/
|
|
66
|
+
export class CheckpointManager {
|
|
67
|
+
storage;
|
|
68
|
+
lock;
|
|
69
|
+
workerIndex;
|
|
70
|
+
totalWorkers;
|
|
71
|
+
basePath;
|
|
72
|
+
data = null;
|
|
73
|
+
dirty = false;
|
|
74
|
+
/**
|
|
75
|
+
* Create a new CheckpointManager.
|
|
76
|
+
*
|
|
77
|
+
* @param options - CheckpointManagerOptions with injected storage
|
|
78
|
+
*/
|
|
79
|
+
constructor(options) {
|
|
80
|
+
this.storage = options.storage;
|
|
81
|
+
this.lock = options.lock ?? new InMemoryLock();
|
|
82
|
+
this.workerIndex = options.workerIndex;
|
|
83
|
+
this.totalWorkers = options.totalWorkers;
|
|
84
|
+
this.basePath = options.basePath;
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Get the storage backend being used.
|
|
88
|
+
*/
|
|
89
|
+
getStorageType() {
|
|
90
|
+
return this.storage.type;
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Load checkpoint from storage if it exists.
|
|
94
|
+
* @returns true if checkpoint was loaded, false if not found or invalid
|
|
95
|
+
*/
|
|
96
|
+
async load() {
|
|
97
|
+
try {
|
|
98
|
+
const loaded = await this.storage.load();
|
|
99
|
+
if (loaded) {
|
|
100
|
+
this.data = loaded;
|
|
101
|
+
this.dirty = false;
|
|
102
|
+
return true;
|
|
103
|
+
}
|
|
104
|
+
this.data = null;
|
|
105
|
+
this.dirty = false;
|
|
106
|
+
return false;
|
|
107
|
+
}
|
|
108
|
+
catch {
|
|
109
|
+
this.data = null;
|
|
110
|
+
this.dirty = false;
|
|
111
|
+
return false;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Save checkpoint to storage.
|
|
116
|
+
*/
|
|
117
|
+
async save() {
|
|
118
|
+
if (!this.dirty || !this.data) {
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
try {
|
|
122
|
+
await this.storage.save(this.data);
|
|
123
|
+
this.dirty = false;
|
|
124
|
+
}
|
|
125
|
+
catch (error) {
|
|
126
|
+
console.warn(`Failed to save checkpoint: ${String(error)}`);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Save checkpoint incrementally (after each result).
|
|
131
|
+
* Uses the injected lock to prevent concurrent writes.
|
|
132
|
+
* @param result
|
|
133
|
+
*/
|
|
134
|
+
async saveIncremental(result) {
|
|
135
|
+
// Acquire lock to prevent concurrent writes
|
|
136
|
+
await this.lock.acquire();
|
|
137
|
+
try {
|
|
138
|
+
// Always reload from file to get latest state (prevents stale memory corruption)
|
|
139
|
+
const currentData = await this.storage.load();
|
|
140
|
+
// Use loaded data or initialize if empty
|
|
141
|
+
const data = currentData ?? {
|
|
142
|
+
configHash: "pending",
|
|
143
|
+
createdAt: new Date().toISOString(),
|
|
144
|
+
updatedAt: new Date().toISOString(),
|
|
145
|
+
completedRunIds: [],
|
|
146
|
+
results: {},
|
|
147
|
+
totalPlanned: 0,
|
|
148
|
+
workerIndex: this.workerIndex,
|
|
149
|
+
totalWorkers: this.totalWorkers,
|
|
150
|
+
};
|
|
151
|
+
// Record the result (only if not already present)
|
|
152
|
+
if (!data.completedRunIds.includes(result.run.runId)) {
|
|
153
|
+
data.completedRunIds.push(result.run.runId);
|
|
154
|
+
}
|
|
155
|
+
data.results[result.run.runId] = result;
|
|
156
|
+
data.updatedAt = new Date().toISOString();
|
|
157
|
+
// Save immediately
|
|
158
|
+
await this.storage.save(data);
|
|
159
|
+
}
|
|
160
|
+
finally {
|
|
161
|
+
this.lock.release();
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* Check if checkpoint exists and is valid.
|
|
166
|
+
*/
|
|
167
|
+
exists() {
|
|
168
|
+
return this.data !== null;
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* Check if a specific run has been completed.
|
|
172
|
+
* @param runId - Run identifier
|
|
173
|
+
*/
|
|
174
|
+
isCompleted(runId) {
|
|
175
|
+
return this.data?.completedRunIds.includes(runId) ?? false;
|
|
176
|
+
}
|
|
177
|
+
/**
|
|
178
|
+
* Get all completed results.
|
|
179
|
+
*/
|
|
180
|
+
getResults() {
|
|
181
|
+
if (!this.data) {
|
|
182
|
+
return [];
|
|
183
|
+
}
|
|
184
|
+
return Object.values(this.data.results);
|
|
185
|
+
}
|
|
186
|
+
/**
|
|
187
|
+
* Get progress information.
|
|
188
|
+
*/
|
|
189
|
+
getProgress() {
|
|
190
|
+
if (!this.data) {
|
|
191
|
+
return { completed: 0, total: 0, percent: 0 };
|
|
192
|
+
}
|
|
193
|
+
const completed = this.data.completedRunIds.length;
|
|
194
|
+
const total = this.data.totalPlanned;
|
|
195
|
+
const percent = total > 0 ? (completed / total) * 100 : 0;
|
|
196
|
+
return { completed, total, percent: Math.round(percent) };
|
|
197
|
+
}
|
|
198
|
+
/**
|
|
199
|
+
* Check if checkpoint is stale (configuration has changed).
|
|
200
|
+
* @param suts - Current SUT definitions
|
|
201
|
+
* @param cases - Current case definitions
|
|
202
|
+
* @param config - Current executor config
|
|
203
|
+
* @param totalRuns - Total planned runs
|
|
204
|
+
*/
|
|
205
|
+
isStale(suts, cases, config, totalRuns) {
|
|
206
|
+
if (!this.data) {
|
|
207
|
+
return false; // No checkpoint, not stale
|
|
208
|
+
}
|
|
209
|
+
const currentHash = this.computeConfigHash(suts, cases, config, totalRuns);
|
|
210
|
+
return this.data.configHash !== currentHash;
|
|
211
|
+
}
|
|
212
|
+
/**
|
|
213
|
+
* Invalidate checkpoint (delete and start fresh).
|
|
214
|
+
*/
|
|
215
|
+
invalidate() {
|
|
216
|
+
this.data = null;
|
|
217
|
+
this.dirty = true;
|
|
218
|
+
}
|
|
219
|
+
/**
|
|
220
|
+
* Initialize empty checkpoint data.
|
|
221
|
+
* @param suts
|
|
222
|
+
* @param cases
|
|
223
|
+
* @param config
|
|
224
|
+
* @param totalRuns
|
|
225
|
+
* @param gitCommit
|
|
226
|
+
*/
|
|
227
|
+
initializeEmpty(suts, cases, config, totalRuns, gitCommit) {
|
|
228
|
+
const configHash = suts && cases && config && totalRuns
|
|
229
|
+
? this.computeConfigHash(suts, cases, config, totalRuns)
|
|
230
|
+
: "pending";
|
|
231
|
+
this.data = {
|
|
232
|
+
configHash,
|
|
233
|
+
createdAt: new Date().toISOString(),
|
|
234
|
+
updatedAt: new Date().toISOString(),
|
|
235
|
+
completedRunIds: [],
|
|
236
|
+
results: {},
|
|
237
|
+
totalPlanned: totalRuns ?? 0,
|
|
238
|
+
gitCommit,
|
|
239
|
+
workerIndex: this.workerIndex,
|
|
240
|
+
totalWorkers: this.totalWorkers,
|
|
241
|
+
};
|
|
242
|
+
this.dirty = true;
|
|
243
|
+
}
|
|
244
|
+
/**
|
|
245
|
+
* Compute hash of execution configuration.
|
|
246
|
+
* Used to detect when configuration changes invalidate checkpoint.
|
|
247
|
+
* @param suts
|
|
248
|
+
* @param cases
|
|
249
|
+
* @param config
|
|
250
|
+
* @param totalRuns
|
|
251
|
+
*/
|
|
252
|
+
computeConfigHash(suts, cases, config, totalRuns) {
|
|
253
|
+
const signature = {
|
|
254
|
+
suts: suts.map((s) => ({
|
|
255
|
+
id: s.registration.id,
|
|
256
|
+
version: s.registration.version,
|
|
257
|
+
})),
|
|
258
|
+
cases: cases.map((c) => ({
|
|
259
|
+
id: c.case.caseId,
|
|
260
|
+
version: c.case.version ?? "1.0.0",
|
|
261
|
+
})),
|
|
262
|
+
executorConfig: {
|
|
263
|
+
repetitions: config.repetitions ?? 1,
|
|
264
|
+
seedBase: config.seedBase ?? 42,
|
|
265
|
+
timeoutMs: config.timeoutMs ?? 0,
|
|
266
|
+
},
|
|
267
|
+
totalRuns,
|
|
268
|
+
};
|
|
269
|
+
return createHash("sha256").update(JSON.stringify(signature)).digest("hex").slice(0, 16);
|
|
270
|
+
}
|
|
271
|
+
/**
|
|
272
|
+
* Get remaining planned runs (excluding completed).
|
|
273
|
+
* @param plannedRuns - All planned runs
|
|
274
|
+
*/
|
|
275
|
+
filterRemaining(plannedRuns) {
|
|
276
|
+
if (!this.data) {
|
|
277
|
+
return plannedRuns;
|
|
278
|
+
}
|
|
279
|
+
const completedSet = new Set(this.data.completedRunIds);
|
|
280
|
+
return plannedRuns.filter((run) => !completedSet.has(run.runId));
|
|
281
|
+
}
|
|
282
|
+
/**
|
|
283
|
+
* Merge new results into checkpoint (for batch updates).
|
|
284
|
+
* @param results - New results to add
|
|
285
|
+
*/
|
|
286
|
+
mergeResults(results) {
|
|
287
|
+
if (!this.data) {
|
|
288
|
+
this.initializeEmpty();
|
|
289
|
+
}
|
|
290
|
+
// Guard against null after initialization
|
|
291
|
+
if (!this.data) {
|
|
292
|
+
return;
|
|
293
|
+
}
|
|
294
|
+
for (const result of results) {
|
|
295
|
+
if (!this.data.completedRunIds.includes(result.run.runId)) {
|
|
296
|
+
this.data.completedRunIds.push(result.run.runId);
|
|
297
|
+
this.data.results[result.run.runId] = result;
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
this.dirty = true;
|
|
301
|
+
}
|
|
302
|
+
/**
|
|
303
|
+
* Merge multiple worker checkpoint shards into a single aggregated checkpoint.
|
|
304
|
+
*
|
|
305
|
+
* This is called after all parallel workers complete to aggregate their results.
|
|
306
|
+
* Each worker writes to its own shard file to avoid race conditions.
|
|
307
|
+
*
|
|
308
|
+
* @param shardPaths - Array of checkpoint shard file paths
|
|
309
|
+
* @returns The merged checkpoint data
|
|
310
|
+
*/
|
|
311
|
+
async mergeShards(shardPaths) {
|
|
312
|
+
// Start with main checkpoint data, or empty if no checkpoint exists yet
|
|
313
|
+
const mainData = this.data;
|
|
314
|
+
const merged = {
|
|
315
|
+
configHash: mainData?.configHash ?? "",
|
|
316
|
+
createdAt: mainData?.createdAt ?? new Date().toISOString(),
|
|
317
|
+
updatedAt: new Date().toISOString(),
|
|
318
|
+
completedRunIds: [...(mainData?.completedRunIds ?? [])],
|
|
319
|
+
results: { ...mainData?.results },
|
|
320
|
+
totalPlanned: mainData?.totalPlanned ?? 0,
|
|
321
|
+
gitCommit: mainData?.gitCommit,
|
|
322
|
+
};
|
|
323
|
+
// Load and merge each shard
|
|
324
|
+
for (const shardPath of shardPaths) {
|
|
325
|
+
const shardStorage = new FileStorage(shardPath);
|
|
326
|
+
const shardData = await shardStorage.load();
|
|
327
|
+
if (shardData) {
|
|
328
|
+
// Merge completed run IDs
|
|
329
|
+
merged.completedRunIds.push(...shardData.completedRunIds);
|
|
330
|
+
// Merge results
|
|
331
|
+
Object.assign(merged.results, shardData.results);
|
|
332
|
+
// Keep the maximum totalPlanned
|
|
333
|
+
if (shardData.totalPlanned > merged.totalPlanned) {
|
|
334
|
+
merged.totalPlanned = shardData.totalPlanned;
|
|
335
|
+
}
|
|
336
|
+
// Keep the earliest createdAt
|
|
337
|
+
if (shardData.createdAt < merged.createdAt) {
|
|
338
|
+
merged.createdAt = shardData.createdAt;
|
|
339
|
+
}
|
|
340
|
+
// Preserve config hash from first valid shard
|
|
341
|
+
if (merged.configHash === "" && shardData.configHash !== "pending") {
|
|
342
|
+
merged.configHash = shardData.configHash;
|
|
343
|
+
}
|
|
344
|
+
// Preserve git commit if available
|
|
345
|
+
if (shardData.gitCommit && !merged.gitCommit) {
|
|
346
|
+
merged.gitCommit = shardData.gitCommit;
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
// Deduplicate completedRunIds (in case of any overlap)
|
|
351
|
+
merged.completedRunIds = [...new Set(merged.completedRunIds)];
|
|
352
|
+
merged.updatedAt = new Date().toISOString();
|
|
353
|
+
// Save merged checkpoint
|
|
354
|
+
await this.storage.save(merged);
|
|
355
|
+
// Update internal state
|
|
356
|
+
this.data = merged;
|
|
357
|
+
this.dirty = false;
|
|
358
|
+
return merged;
|
|
359
|
+
}
|
|
360
|
+
/**
|
|
361
|
+
* Get checkpoint summary for logging.
|
|
362
|
+
*/
|
|
363
|
+
getSummary() {
|
|
364
|
+
if (!this.data) {
|
|
365
|
+
return "No checkpoint";
|
|
366
|
+
}
|
|
367
|
+
const progress = this.getProgress();
|
|
368
|
+
return `Checkpoint: ${progress.completed}/${progress.total} runs (${progress.percent}%)`;
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
/**
|
|
372
|
+
* Get git commit hash for reproducibility.
|
|
373
|
+
*/
|
|
374
|
+
export const getGitCommit = async () => {
|
|
375
|
+
try {
|
|
376
|
+
const { execSync } = await import("node:child_process");
|
|
377
|
+
return execSync("git rev-parse HEAD", { encoding: "utf-8" }).trim();
|
|
378
|
+
}
|
|
379
|
+
catch {
|
|
380
|
+
return undefined;
|
|
381
|
+
}
|
|
382
|
+
};
|
|
383
|
+
/**
|
|
384
|
+
* Create a checkpoint manager with default file storage.
|
|
385
|
+
*
|
|
386
|
+
* Convenience factory for simple cases.
|
|
387
|
+
*
|
|
388
|
+
* @param path - Checkpoint file path
|
|
389
|
+
* @param lock - Optional lock implementation
|
|
390
|
+
*/
|
|
391
|
+
export const createFileCheckpointManager = (path = "results/execute/checkpoint.json", lock) => {
|
|
392
|
+
const storage = new FileStorage(path);
|
|
393
|
+
return new CheckpointManager({ storage, lock });
|
|
394
|
+
};
|
|
395
|
+
//# sourceMappingURL=checkpoint-manager.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"checkpoint-manager.js","sourceRoot":"","sources":["../../src/executor/checkpoint-manager.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Checkpoint Storage Backends
|
|
3
|
+
*
|
|
4
|
+
* Pluggable storage for experiment checkpoints:
|
|
5
|
+
* - FileStorage: JSON files in local filesystem (fast, no git required)
|
|
6
|
+
* - GitStorage: Git notes attached to commits (version controlled, shareable)
|
|
7
|
+
*
|
|
8
|
+
* Dependency Injection:
|
|
9
|
+
* - FileSystem interface enables mocking for tests
|
|
10
|
+
* - Lock interface enables pluggable concurrency control
|
|
11
|
+
*
|
|
12
|
+
* Usage:
|
|
13
|
+
* ```typescript
|
|
14
|
+
* // File-based (default)
|
|
15
|
+
* const fileStorage = new FileStorage("results/execute/checkpoint.json");
|
|
16
|
+
*
|
|
17
|
+
* // Git-based (stores as git notes)
|
|
18
|
+
* const gitStorage = new GitStorage("results-execute");
|
|
19
|
+
*
|
|
20
|
+
* // With mock file system for testing
|
|
21
|
+
* const mockFs = new MockFileSystem();
|
|
22
|
+
* const fileStorage = new FileStorage("checkpoint.json", mockFs);
|
|
23
|
+
*
|
|
24
|
+
* // Auto-detect based on mode
|
|
25
|
+
* const storage = createCheckpointStorage("file", "results/execute/checkpoint.json");
|
|
26
|
+
* const storage = createCheckpointStorage("git", "results-execute");
|
|
27
|
+
* ```
|
|
28
|
+
*/
|
|
29
|
+
import type { CheckpointData } from "./checkpoint-manager.js";
|
|
30
|
+
/**
|
|
31
|
+
* Interface for file system operations.
|
|
32
|
+
* Enables mocking for testing and alternative storage backends.
|
|
33
|
+
*/
|
|
34
|
+
export interface FileSystem {
|
|
35
|
+
/**
|
|
36
|
+
* Read file contents as UTF-8 string.
|
|
37
|
+
* @param path - Absolute file path
|
|
38
|
+
* @returns File contents or throws if not found
|
|
39
|
+
*/
|
|
40
|
+
readFile(path: string): Promise<string>;
|
|
41
|
+
/**
|
|
42
|
+
* Write string content to a file.
|
|
43
|
+
* @param path - Absolute file path
|
|
44
|
+
* @param content - Content to write
|
|
45
|
+
*/
|
|
46
|
+
writeFile(path: string, content: string): Promise<void>;
|
|
47
|
+
/**
|
|
48
|
+
* Create directory recursively if it doesn't exist.
|
|
49
|
+
* @param path - Directory path
|
|
50
|
+
* @param options - Options including recursive flag
|
|
51
|
+
*/
|
|
52
|
+
mkdir(path: string, options: {
|
|
53
|
+
recursive: boolean;
|
|
54
|
+
}): Promise<void>;
|
|
55
|
+
/**
|
|
56
|
+
* Delete a file if it exists.
|
|
57
|
+
* @param path - File path to delete
|
|
58
|
+
*/
|
|
59
|
+
unlink(path: string): Promise<void>;
|
|
60
|
+
/**
|
|
61
|
+
* Check if file/directory exists.
|
|
62
|
+
* @param path - Path to check
|
|
63
|
+
*/
|
|
64
|
+
access(path: string): Promise<void>;
|
|
65
|
+
/**
|
|
66
|
+
* List directory contents.
|
|
67
|
+
* @param path - Directory path
|
|
68
|
+
* @returns Array of entry names
|
|
69
|
+
*/
|
|
70
|
+
readdir(path: string): Promise<string[]>;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Production file system implementation using node:fs/promises.
|
|
74
|
+
*/
|
|
75
|
+
export declare class NodeFileSystem implements FileSystem {
|
|
76
|
+
readFile(path: string): Promise<string>;
|
|
77
|
+
writeFile(path: string, content: string): Promise<void>;
|
|
78
|
+
mkdir(path: string, options: {
|
|
79
|
+
recursive: boolean;
|
|
80
|
+
}): Promise<void>;
|
|
81
|
+
unlink(path: string): Promise<void>;
|
|
82
|
+
access(path: string): Promise<void>;
|
|
83
|
+
readdir(path: string): Promise<string[]>;
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Interface for concurrency control.
|
|
87
|
+
* Enables pluggable locking mechanisms for checkpoint saves.
|
|
88
|
+
*/
|
|
89
|
+
export interface Lock {
|
|
90
|
+
/**
|
|
91
|
+
* Acquire the lock, waiting if necessary.
|
|
92
|
+
*/
|
|
93
|
+
acquire(): Promise<void>;
|
|
94
|
+
/**
|
|
95
|
+
* Release the lock.
|
|
96
|
+
*/
|
|
97
|
+
release(): void;
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* In-memory lock for single-process use.
|
|
101
|
+
* Provides queue-based locking for concurrent saves within a process.
|
|
102
|
+
*/
|
|
103
|
+
export declare class InMemoryLock implements Lock {
|
|
104
|
+
private locked;
|
|
105
|
+
private queue;
|
|
106
|
+
acquire(): Promise<void>;
|
|
107
|
+
release(): void;
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Checkpoint storage mode.
|
|
111
|
+
*/
|
|
112
|
+
export type CheckpointMode = "file" | "git" | "auto";
|
|
113
|
+
/**
|
|
114
|
+
* Abstract checkpoint storage interface.
|
|
115
|
+
*/
|
|
116
|
+
export interface CheckpointStorage {
|
|
117
|
+
/**
|
|
118
|
+
* Load checkpoint data from storage.
|
|
119
|
+
* @returns Checkpoint data or null if not found
|
|
120
|
+
*/
|
|
121
|
+
load(): CheckpointData | null | Promise<CheckpointData | null>;
|
|
122
|
+
/**
|
|
123
|
+
* Save checkpoint data to storage.
|
|
124
|
+
* @param data - Checkpoint data to save
|
|
125
|
+
*/
|
|
126
|
+
save(data: CheckpointData): Promise<void>;
|
|
127
|
+
/**
|
|
128
|
+
* Delete checkpoint from storage.
|
|
129
|
+
*/
|
|
130
|
+
delete(): void | Promise<void>;
|
|
131
|
+
/**
|
|
132
|
+
* Check if checkpoint exists.
|
|
133
|
+
*/
|
|
134
|
+
exists(): boolean | Promise<boolean>;
|
|
135
|
+
/**
|
|
136
|
+
* Get storage type identifier.
|
|
137
|
+
*/
|
|
138
|
+
readonly type: string;
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* File-based checkpoint storage.
|
|
142
|
+
* Stores checkpoints as JSON files in the local filesystem.
|
|
143
|
+
*
|
|
144
|
+
* Supports dependency injection of FileSystem for testing.
|
|
145
|
+
*/
|
|
146
|
+
export declare class FileStorage implements CheckpointStorage {
|
|
147
|
+
readonly type = "file";
|
|
148
|
+
private readonly path;
|
|
149
|
+
private readonly fs;
|
|
150
|
+
constructor(path: string, fs?: FileSystem);
|
|
151
|
+
load(): Promise<CheckpointData | null>;
|
|
152
|
+
save(data: CheckpointData): Promise<void>;
|
|
153
|
+
delete(): Promise<void>;
|
|
154
|
+
exists(): Promise<boolean>;
|
|
155
|
+
getPath(): string;
|
|
156
|
+
/**
|
|
157
|
+
* Find all worker checkpoint shard files in a directory.
|
|
158
|
+
* Looks for files matching the pattern "checkpoint-worker-*.json".
|
|
159
|
+
*
|
|
160
|
+
* @param baseDir - Directory to search for shard files
|
|
161
|
+
* @param fs - FileSystem implementation (defaults to NodeFileSystem)
|
|
162
|
+
* @returns Sorted array of shard file paths
|
|
163
|
+
*/
|
|
164
|
+
static findShards(baseDir: string, fs?: FileSystem): Promise<string[]>;
|
|
165
|
+
/**
|
|
166
|
+
* Generate a shard file path for a specific worker.
|
|
167
|
+
*
|
|
168
|
+
* @param baseDir - Base directory for checkpoints
|
|
169
|
+
* @param workerIndex - Worker index (0-based)
|
|
170
|
+
* @returns Path to the shard checkpoint file
|
|
171
|
+
*/
|
|
172
|
+
static shardPath(baseDir: string, workerIndex: number): string;
|
|
173
|
+
}
|
|
174
|
+
/**
|
|
175
|
+
* Git-based checkpoint storage using git notes.
|
|
176
|
+
*
|
|
177
|
+
* Checkpoints are stored as git notes attached to the current HEAD.
|
|
178
|
+
* This provides:
|
|
179
|
+
* - Version control: Checkpoints tracked in git history
|
|
180
|
+
* - Shareability: Team members can access each other's checkpoints
|
|
181
|
+
* - Reproducibility: Linked to specific commits
|
|
182
|
+
* - Safety: No accidental deletion through git clean
|
|
183
|
+
*
|
|
184
|
+
* Each checkpoint is stored as a note with a unique ref based on the namespace.
|
|
185
|
+
*/
|
|
186
|
+
export declare class GitStorage implements CheckpointStorage {
|
|
187
|
+
readonly type = "git";
|
|
188
|
+
private readonly namespace;
|
|
189
|
+
private readonly repoRoot;
|
|
190
|
+
constructor(namespace: string, repoRoot?: string);
|
|
191
|
+
/**
|
|
192
|
+
* Get the full notes ref for this checkpoint.
|
|
193
|
+
*/
|
|
194
|
+
private getNotesRef;
|
|
195
|
+
/**
|
|
196
|
+
* Check if git is available and we're in a git repo.
|
|
197
|
+
*/
|
|
198
|
+
private checkGitAvailable;
|
|
199
|
+
load(): CheckpointData | null;
|
|
200
|
+
save(data: CheckpointData): Promise<void>;
|
|
201
|
+
delete(): void;
|
|
202
|
+
exists(): boolean;
|
|
203
|
+
/**
|
|
204
|
+
* List all checkpoints for this namespace across git history.
|
|
205
|
+
* Returns a map of commit SHA to checkpoint metadata.
|
|
206
|
+
*/
|
|
207
|
+
listHistory(): {
|
|
208
|
+
commit: string;
|
|
209
|
+
checkpoint: CheckpointData;
|
|
210
|
+
}[];
|
|
211
|
+
/**
|
|
212
|
+
* Restore checkpoint from a specific commit.
|
|
213
|
+
* @param commitSha
|
|
214
|
+
*/
|
|
215
|
+
restoreFromCommit(commitSha: string): CheckpointData | null;
|
|
216
|
+
}
|
|
217
|
+
/**
|
|
218
|
+
* Create a checkpoint storage instance based on mode.
|
|
219
|
+
* @param mode - Storage mode ("file", "git", or "auto")
|
|
220
|
+
* @param pathOrNamespace - File path or git namespace
|
|
221
|
+
* @param repoRoot - Git repository root (for git storage)
|
|
222
|
+
*/
|
|
223
|
+
export declare const createCheckpointStorage: (mode: CheckpointMode, pathOrNamespace: string, repoRoot?: string) => CheckpointStorage;
|
|
224
|
+
/**
|
|
225
|
+
* Get default checkpoint namespace for git storage.
|
|
226
|
+
* Based on results directory path.
|
|
227
|
+
* @param resultsDir
|
|
228
|
+
*/
|
|
229
|
+
export declare const getGitNamespace: (resultsDir: string) => string;
|
|
230
|
+
//# sourceMappingURL=checkpoint-storage.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"checkpoint-storage.d.ts","sourceRoot":"","sources":["../../src/executor/checkpoint-storage.ts"],"names":[],"mappings":"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"}
|