pi-background-tasks 2.1.3 → 2.1.4
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 +2 -2
- package/TESTING.md +1 -1
- package/TEST_PLAN.md +2 -2
- package/docs/INDEX.md +2 -2
- package/docs/manifest.json +4 -4
- package/docs/reference/runtime-contracts.md +23 -22
- package/docs/subsystems/docs-freshness-gate.md +2 -2
- package/docs/subsystems/fusion.md +4 -0
- package/docs/tools/bg_delegate.md +1 -1
- package/docs/tools/bg_result.md +8 -1
- package/package.json +1 -1
- package/src/core/fusion/artifacts.ts +265 -3
- package/src/core/fusion/orchestrator.ts +47 -57
- package/src/core/fusion/result-package.ts +550 -3
- package/src/core/fusion/types.ts +87 -0
- package/src/delegate-extension.ts +66 -5
|
@@ -5,6 +5,8 @@ import { FUSION_BUDGET_POLICY, FusionBudget } from './budget.js';
|
|
|
5
5
|
import { assertChildOutputWithinContract } from './output-contract.js';
|
|
6
6
|
import {
|
|
7
7
|
FusionArtifactStore,
|
|
8
|
+
buildFusionFailureSummary,
|
|
9
|
+
buildFusionRunProgress as deriveFusionRunProgress,
|
|
8
10
|
type CreateFusionArtifactStoreOptions,
|
|
9
11
|
type RecordFusionFailedAttemptInput,
|
|
10
12
|
} from './artifacts.js';
|
|
@@ -40,9 +42,7 @@ import {
|
|
|
40
42
|
FUSION_VALIDATE_CANDIDATE_SCHEMA_VERSION,
|
|
41
43
|
FusionError,
|
|
42
44
|
addFusionUsage,
|
|
43
|
-
cloneFusionUsage,
|
|
44
45
|
createEmptyFusionUsage,
|
|
45
|
-
type FusionArtifactManifest,
|
|
46
46
|
type FusionCalibrationViolation,
|
|
47
47
|
type FusionCapability,
|
|
48
48
|
type FusionCanonicalInputV3,
|
|
@@ -176,58 +176,7 @@ function asFusionError(error: unknown, artifactDir: string, messageOverride?: st
|
|
|
176
176
|
});
|
|
177
177
|
}
|
|
178
178
|
|
|
179
|
-
|
|
180
|
-
manifest: FusionArtifactManifest,
|
|
181
|
-
stage: FusionStage,
|
|
182
|
-
): FusionRunProgress['candidates'] {
|
|
183
|
-
const attempts = manifest.attempts.filter((attempt) => attempt.stage === stage);
|
|
184
|
-
const created = attempts.filter((attempt) => attempt.child_created).length;
|
|
185
|
-
const completed = attempts.filter(
|
|
186
|
-
(attempt) => attempt.child_created && attempt.status === 'completed',
|
|
187
|
-
).length;
|
|
188
|
-
const failed = attempts.filter(
|
|
189
|
-
(attempt) => attempt.child_created && attempt.status === 'failed',
|
|
190
|
-
).length;
|
|
191
|
-
const cancelled = attempts.filter(
|
|
192
|
-
(attempt) => attempt.child_created && attempt.status === 'cancelled',
|
|
193
|
-
).length;
|
|
194
|
-
const completedByState =
|
|
195
|
-
stage === 'candidate'
|
|
196
|
-
? completed >= 3
|
|
197
|
-
: stage === 'evaluation'
|
|
198
|
-
? manifest.artifacts['evaluation.json'] !== undefined ||
|
|
199
|
-
manifest.state === 'evaluation_complete' ||
|
|
200
|
-
manifest.state === 'merging' ||
|
|
201
|
-
manifest.state === 'completed'
|
|
202
|
-
: manifest.artifacts['merged.md'] !== undefined || manifest.state === 'completed';
|
|
203
|
-
const progress: FusionRunProgress['candidates'] = {
|
|
204
|
-
status: completedByState ? 'completed' : created === 0 ? 'not_started' : 'incomplete',
|
|
205
|
-
attempts_recorded: attempts.length,
|
|
206
|
-
children_created: created,
|
|
207
|
-
children_completed: completed,
|
|
208
|
-
children_failed: failed,
|
|
209
|
-
children_cancelled: cancelled,
|
|
210
|
-
};
|
|
211
|
-
if (stage === 'candidate') {
|
|
212
|
-
const createdSlots = new Set(
|
|
213
|
-
attempts.flatMap((attempt) =>
|
|
214
|
-
attempt.child_created && attempt.slot !== undefined ? [attempt.slot] : [],
|
|
215
|
-
),
|
|
216
|
-
);
|
|
217
|
-
progress.not_started_slots = 3 - createdSlots.size;
|
|
218
|
-
}
|
|
219
|
-
return progress;
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
export function buildFusionRunProgress(manifest: FusionArtifactManifest): FusionRunProgress {
|
|
223
|
-
return {
|
|
224
|
-
manifest_state: manifest.state,
|
|
225
|
-
candidates: fusionStageProgress(manifest, 'candidate'),
|
|
226
|
-
evaluation: fusionStageProgress(manifest, 'evaluation'),
|
|
227
|
-
merge: fusionStageProgress(manifest, 'merge'),
|
|
228
|
-
usage_so_far: cloneFusionUsage(manifest.usage),
|
|
229
|
-
};
|
|
230
|
-
}
|
|
179
|
+
export { buildFusionRunProgress } from './artifacts.js';
|
|
231
180
|
|
|
232
181
|
function formatFusionRunStage(name: string, stage: FusionRunProgress['candidates']): string {
|
|
233
182
|
const notStarted =
|
|
@@ -237,7 +186,31 @@ function formatFusionRunStage(name: string, stage: FusionRunProgress['candidates
|
|
|
237
186
|
return `${name}=${stage.status} (${String(stage.children_created)} created, ${String(stage.children_completed)} completed, ${String(stage.children_failed)} failed, ${String(stage.children_cancelled)} cancelled${notStarted})`;
|
|
238
187
|
}
|
|
239
188
|
|
|
240
|
-
export function
|
|
189
|
+
export function summaryUnavailableNote(error: unknown): string {
|
|
190
|
+
const detail = errorText(error);
|
|
191
|
+
const detailBytes = Buffer.from(detail, 'utf8');
|
|
192
|
+
if (detailBytes.length > 1024) {
|
|
193
|
+
return 'failure-summary.json unavailable after terminal publication; write failure detail omitted because it exceeds the 1024-byte diagnostic cap.';
|
|
194
|
+
}
|
|
195
|
+
return `failure-summary.json unavailable after terminal publication: ${detail}`;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
function withSummaryUnavailableNote(error: FusionError, summaryError: unknown): FusionError {
|
|
199
|
+
const details: FusionErrorDetails = {
|
|
200
|
+
code: error.code,
|
|
201
|
+
transient: error.transient,
|
|
202
|
+
childCreated: error.childCreated,
|
|
203
|
+
};
|
|
204
|
+
if (error.artifactDir !== undefined) details.artifactDir = error.artifactDir;
|
|
205
|
+
if (error.stage !== undefined) details.stage = error.stage;
|
|
206
|
+
if (error.slot !== undefined) details.slot = error.slot;
|
|
207
|
+
if (error.attempt !== undefined) details.attempt = error.attempt;
|
|
208
|
+
if (error.budget !== undefined) details.budget = error.budget;
|
|
209
|
+
if (error.runProgress !== undefined) details.runProgress = error.runProgress;
|
|
210
|
+
return new FusionError(`${error.message}\n${summaryUnavailableNote(summaryError)}`, details);
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
function formatFusionRunProgress(progress: FusionRunProgress): string {
|
|
241
214
|
const usage = progress.usage_so_far;
|
|
242
215
|
const optionalUsage = [
|
|
243
216
|
usage.cacheWrite1h === undefined ? undefined : `cacheWrite1h=${String(usage.cacheWrite1h)}`,
|
|
@@ -962,9 +935,26 @@ export class FusionOrchestrator {
|
|
|
962
935
|
terminalError = withRunProgress(
|
|
963
936
|
error,
|
|
964
937
|
store.artifactDir,
|
|
965
|
-
|
|
938
|
+
deriveFusionRunProgress(store.snapshot()),
|
|
966
939
|
);
|
|
967
|
-
|
|
940
|
+
const terminalState = cancelled ? 'cancelled' : 'failed';
|
|
941
|
+
await store.writeError(terminalState, terminalError.message);
|
|
942
|
+
// The terminal manifest/error are authoritative. Summary persistence is
|
|
943
|
+
// subordinate and intentionally attempted once from that fresh snapshot.
|
|
944
|
+
try {
|
|
945
|
+
const terminalManifest = store.snapshot();
|
|
946
|
+
await store.writeFailureSummary(
|
|
947
|
+
buildFusionFailureSummary({
|
|
948
|
+
manifest: terminalManifest,
|
|
949
|
+
terminalError,
|
|
950
|
+
progress: deriveFusionRunProgress(terminalManifest),
|
|
951
|
+
terminalState,
|
|
952
|
+
createdAt: terminalManifest.updated_at,
|
|
953
|
+
}),
|
|
954
|
+
);
|
|
955
|
+
} catch (summaryError) {
|
|
956
|
+
terminalError = withSummaryUnavailableNote(terminalError, summaryError);
|
|
957
|
+
}
|
|
968
958
|
} catch (artifactError) {
|
|
969
959
|
throw withTerminalArtifactFailure(error, store.artifactDir, artifactError);
|
|
970
960
|
}
|