@@ -1176,24 +1176,86 @@ describe('projectTraceSpansForSecrets whole-trace content budget', () => {
11761176 expect ( projected [ 1 ] . toolCalls ) . toHaveLength ( 50_000 )
11771177 } )
11781178
1179- it ( 'withholds the whole top-level span whose descendant crossed the budget' , async ( ) => {
1179+ it ( 'keeps checked content under a single workflow root when a descendant crosses the budget' , async ( ) => {
11801180 const projected = await projectTraceSpansForSecrets (
11811181 [
1182- heavySpan ( 'span-a' , 'first' ) ,
11831182 createSpan ( {
1184- id : 'span-b' ,
1185- output : { marker : 'parent top-secret' } ,
1186- children : [ heavySpan ( 'child-1' , 'child one' ) , heavySpan ( 'child-2' , 'child two' ) ] ,
1183+ id : 'workflow-execution' ,
1184+ output : { marker : 'root top-secret' } ,
1185+ children : [
1186+ heavySpan ( 'block-a' , 'first top-secret' ) ,
1187+ createSpan ( {
1188+ id : 'loop' ,
1189+ output : { marker : 'loop top-secret' } ,
1190+ children : [
1191+ heavySpan ( 'iteration-1' , 'second' ) ,
1192+ heavySpan ( 'iteration-2' , 'third top-secret' , {
1193+ children : [ createSpan ( { id : 'nested' , output : { marker : 'nested top-secret' } } ) ] ,
1194+ } ) ,
1195+ createSpan ( { id : 'iteration-3' , output : { marker : 'fourth top-secret' } } ) ,
1196+ ] ,
1197+ } ) ,
1198+ createSpan ( { id : 'block-b' , output : { marker : 'fifth top-secret' } } ) ,
1199+ ] ,
11871200 } ) ,
1201+ createSpan ( { id : 'after-root' , output : { marker : 'sixth top-secret' } } ) ,
11881202 ] ,
11891203 { registry : SECRET_REGISTRY , store : STORE , allowLargeValueWrites : false }
11901204 )
11911205
1192- expect ( projected [ 0 ] . output ?. marker ) . toBe ( 'first' )
1193- expect ( projected [ 1 ] ) . not . toHaveProperty ( 'output' )
1194- expect ( projected [ 1 ] . contentWithheld ) . toBe ( true )
1195- expect ( projected [ 1 ] . children ?. map ( ( child ) => child . id ) ) . toEqual ( [ 'child-1' , 'child-2' ] )
1196- expect ( projected [ 1 ] . children ?. every ( ( child ) => ! Object . hasOwn ( child , 'output' ) ) ) . toBe ( true )
1206+ const [ root , afterRoot ] = projected
1207+ const [ blockA , loop , blockB ] = root . children ?? [ ]
1208+ const [ iteration1 , iteration2 , iteration3 ] = loop . children ?? [ ]
1209+
1210+ for ( const [ kept , marker ] of [
1211+ [ root , 'root {{API_SECRET}}' ] ,
1212+ [ blockA , 'first {{API_SECRET}}' ] ,
1213+ [ loop , 'loop {{API_SECRET}}' ] ,
1214+ [ iteration1 , 'second' ] ,
1215+ ] as const ) {
1216+ expect ( kept . output ?. marker ) . toBe ( marker )
1217+ expect ( kept ) . not . toHaveProperty ( 'contentWithheld' )
1218+ }
1219+ for ( const withheld of [ iteration2 , iteration2 . children ?. [ 0 ] , iteration3 , blockB , afterRoot ] ) {
1220+ expect ( withheld ) . toMatchObject ( { contentWithheld : true } )
1221+ expect ( withheld ) . not . toHaveProperty ( 'output' )
1222+ }
11971223 expect ( JSON . stringify ( projected ) ) . not . toContain ( 'top-secret' )
1224+ expect ( warnMock ) . toHaveBeenCalledWith (
1225+ 'Trace content exceeded the projection budget; withholding the remaining content' ,
1226+ expect . objectContaining ( { retainedSpanCount : 4 , withheldSpanCount : 5 } )
1227+ )
1228+ } )
1229+
1230+ it ( 'withholds every span when a descendant fails for any reason but the budget' , async ( ) => {
1231+ const projected = await projectTraceSpansForSecrets (
1232+ [
1233+ createSpan ( {
1234+ id : 'workflow-execution' ,
1235+ output : { marker : 'root' } ,
1236+ children : [
1237+ createSpan ( { id : 'block-a' , output : { marker : 'first' } } ) ,
1238+ createSpan ( {
1239+ id : 'block-b' ,
1240+ toolCalls : Array . from ( { length : 100_001 } , ( _ , index ) => ( {
1241+ name : `tool-${ index } ` ,
1242+ duration : 0 ,
1243+ startTime : '2026-07-01T00:00:00.000Z' ,
1244+ endTime : '2026-07-01T00:00:00.000Z' ,
1245+ status : 'success' ,
1246+ } ) ) ,
1247+ } ) ,
1248+ ] ,
1249+ } ) ,
1250+ ] ,
1251+ { registry : SECRET_REGISTRY , store : STORE , allowLargeValueWrites : false }
1252+ )
1253+
1254+ expect ( projected [ 0 ] ) . not . toHaveProperty ( 'output' )
1255+ expect ( projected [ 0 ] . children ?. [ 0 ] ) . not . toHaveProperty ( 'output' )
1256+ expect ( warnMock ) . toHaveBeenCalledWith (
1257+ 'Trace secret projection failed; retaining structural spans only' ,
1258+ expect . objectContaining ( { executionId : 'execution-1' , phase : 'read' } )
1259+ )
11981260 } )
11991261} )
0 commit comments