Skip to content

Commit f3b9f52

Browse files
MaxwellCalkinclaude
andcommitted
fix: add truncation awareness to loop output and warn on data loss
Address review feedback: the sliding window on allIterationOutputs silently truncated the final loop output passed to downstream blocks. - Track totalIterationCount on LoopScope so the true iteration count is preserved even after pruning - Include totalIterations and truncated flag in the loop block output so downstream blocks can detect when data was pruned - Log a warning with discarded/retained counts when truncation occurs Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 1365a5f commit f3b9f52

File tree

2 files changed

+15
-1
lines changed

2 files changed

+15
-1
lines changed

apps/sim/executor/execution/state.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ export interface LoopScope {
1616
skipFirstConditionCheck?: boolean
1717
/** Error message if loop validation failed (e.g., exceeded max iterations) */
1818
validationError?: string
19+
/** Total number of iterations completed (may exceed allIterationOutputs.length when truncated) */
20+
totalIterationCount?: number
1921
}
2022

2123
export interface ParallelScope {

apps/sim/executor/orchestrators/loop.ts

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -248,11 +248,18 @@ export class LoopOrchestrator {
248248

249249
if (iterationResults.length > 0) {
250250
scope.allIterationOutputs.push(iterationResults)
251+
scope.totalIterationCount = (scope.totalIterationCount ?? 0) + 1
251252

252253
// Sliding window: discard oldest iteration outputs to bound memory (fixes #2525)
253254
if (scope.allIterationOutputs.length > DEFAULTS.MAX_LOOP_ITERATION_HISTORY) {
254255
const excess = scope.allIterationOutputs.length - DEFAULTS.MAX_LOOP_ITERATION_HISTORY
255256
scope.allIterationOutputs.splice(0, excess)
257+
logger.warn('Loop iteration history truncated to prevent OOM', {
258+
loopId,
259+
totalIterations: scope.totalIterationCount,
260+
retained: DEFAULTS.MAX_LOOP_ITERATION_HISTORY,
261+
discarded: scope.totalIterationCount - DEFAULTS.MAX_LOOP_ITERATION_HISTORY,
262+
})
256263
}
257264
}
258265

@@ -281,7 +288,12 @@ export class LoopOrchestrator {
281288
scope: LoopScope
282289
): LoopContinuationResult {
283290
const results = scope.allIterationOutputs
284-
const output = { results }
291+
const totalIterations = scope.totalIterationCount ?? results.length
292+
const output = {
293+
results,
294+
totalIterations,
295+
...(totalIterations > results.length && { truncated: true }),
296+
}
285297
this.state.setBlockOutput(loopId, output, DEFAULTS.EXECUTION_TIME)
286298

287299
if (this.contextExtensions?.onBlockComplete) {

0 commit comments

Comments
 (0)