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.
@@ -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
- function fusionStageProgress(
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 formatFusionRunProgress(progress: FusionRunProgress): string {
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
- buildFusionRunProgress(store.snapshot()),
938
+ deriveFusionRunProgress(store.snapshot()),
966
939
  );
967
- await store.writeError(cancelled ? 'cancelled' : 'failed', terminalError.message);
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
  }