Skip to content

Commit 08c27c3

Browse files
fix(workflows): stop deriving outputs from traces
1 parent 2936dfc commit 08c27c3

2 files changed

Lines changed: 60 additions & 18 deletions

File tree

apps/sim/lib/logs/execution/trace-store.test.ts

Lines changed: 53 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,7 @@ describe('projectExecutionDataForDisplay', () => {
136136
expect(JSON.stringify([...materialized.blockOutputs])).not.toContain('12345678')
137137
})
138138

139-
it('falls back to projected trace output for a requested block missing from partial state', async () => {
139+
it('does not use trace output for a requested block missing from partial state', async () => {
140140
const emptyProvenance = {
141141
version: 1 as const,
142142
complete: true,
@@ -172,12 +172,59 @@ describe('projectExecutionDataForDisplay', () => {
172172
['state-only', 'trace-only']
173173
)
174174

175-
expect(materialized.blockOutputs).toEqual(
176-
new Map([
177-
['trace-only', { result: 'trace-output' }],
178-
['state-only', { result: 'state-output' }],
179-
])
175+
expect(materialized.blockOutputs).toEqual(new Map([['state-only', { result: 'state-output' }]]))
176+
})
177+
178+
it('does not derive block outputs from legacy trace spans', async () => {
179+
const materialized = await materializeExecutionDataForDisplayWithBlockOutputs(
180+
{
181+
traceSpans: [
182+
{
183+
id: 'span-1',
184+
blockId: 'function-1',
185+
name: 'Function 1',
186+
type: 'function',
187+
duration: 1,
188+
startTime: '2026-08-11T00:00:00.000Z',
189+
endTime: '2026-08-11T00:00:00.001Z',
190+
output: { token: 'raw-legacy-secret' },
191+
},
192+
],
193+
},
194+
CONTEXT,
195+
['function-1']
196+
)
197+
198+
expect(materialized.blockOutputs).toEqual(new Map())
199+
})
200+
201+
it('does not mix legacy trace output into partial execution state', async () => {
202+
const materialized = await materializeExecutionDataForDisplayWithBlockOutputs(
203+
{
204+
traceSpans: [
205+
{
206+
id: 'span-1',
207+
blockId: 'trace-only',
208+
name: 'Trace-only block',
209+
type: 'function',
210+
duration: 1,
211+
startTime: '2026-08-11T00:00:00.000Z',
212+
endTime: '2026-08-11T00:00:00.001Z',
213+
output: { token: 'raw-legacy-secret' },
214+
},
215+
],
216+
executionState: {
217+
blockStates: {
218+
'state-only': { output: { result: 'unproven-state-output' } },
219+
},
220+
},
221+
},
222+
CONTEXT,
223+
['state-only', 'trace-only']
180224
)
225+
226+
expect(materialized.blockOutputs).toEqual(new Map())
227+
expect(JSON.stringify([...materialized.blockOutputs])).not.toContain('raw-legacy-secret')
181228
})
182229

183230
it('omits state-only block outputs that lack usable secret provenance', async () => {

apps/sim/lib/logs/execution/trace-store.ts

Lines changed: 7 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,7 @@ import { toError } from '@sim/utils/errors'
33
import { omit } from '@sim/utils/object'
44
import { isLargeValueRef } from '@/lib/execution/payloads/large-value-ref'
55
import { materializeLargeValueRef, storeLargeValue } from '@/lib/execution/payloads/store'
6-
import {
7-
collectFunctionalBlockOutputs,
8-
type FunctionalExecutionDataSource,
9-
} from '@/lib/logs/execution/functional-outputs'
6+
import { FunctionalOutputsUnavailableError } from '@/lib/logs/execution/functional-outputs'
107
import { projectTraceSpansForSecrets } from '@/lib/logs/execution/trace-secret-projection'
118
import type { TraceSpan } from '@/lib/logs/types'
129
import {
@@ -280,8 +277,8 @@ export async function materializeExecutionDataForDisplay(
280277

281278
/**
282279
* Materializes one trusted row into its display envelope plus secret-safe functional outputs.
283-
* Execution-state output remains authoritative when present, but only requested blocks are
284-
* projected and returned; the raw execution state never crosses the display boundary.
280+
* Only requested execution-state outputs are projected and returned; trace spans remain display
281+
* data and the raw execution state never crosses the display boundary.
285282
*/
286283
export async function materializeExecutionDataForDisplayWithBlockOutputs(
287284
executionData: Record<string, unknown> | null | undefined,
@@ -296,27 +293,25 @@ export async function materializeExecutionDataForDisplayWithBlockOutputs(
296293

297294
const executionState = readRecord(materialized.executionState)
298295
const blockStates = readRecord(executionState?.blockStates)
299-
const displaySource = displayData as FunctionalExecutionDataSource
300296
if (!blockStates) {
301-
return {
302-
executionData: displayData,
303-
blockOutputs: collectFunctionalBlockOutputs(displaySource),
297+
if (materialized.executionDataTruncated === true) {
298+
throw new FunctionalOutputsUnavailableError()
304299
}
300+
return { executionData: displayData, blockOutputs: new Map() }
305301
}
306302

307303
const runRegistry = await importResolvedSecretTraceRegistry(
308304
materialized[RESOLVED_SECRET_PROVENANCE_KEY] ??
309305
executionState?.[RESOLVED_SECRET_PROVENANCE_KEY],
310306
'traceStore.blockOutputRunProvenance'
311307
)
312-
const blockOutputs = collectFunctionalBlockOutputs({ traceSpans: displaySource.traceSpans })
308+
const blockOutputs = new Map<string, unknown>()
313309
const projectionStore = createReadOnlyProjectionStore(context)
314310

315311
for (const blockId of new Set(blockIds)) {
316312
const blockState = readRecord(blockStates[blockId])
317313
if (!blockState || blockState.output === undefined) continue
318314

319-
blockOutputs.delete(blockId)
320315
const hasExactProvenance = Object.hasOwn(blockState, RESOLVED_SECRET_PROVENANCE_KEY)
321316
const registry = hasExactProvenance
322317
? await importResolvedSecretTraceRegistry(

0 commit comments

Comments
 (0)