@@ -242,7 +242,7 @@ describe("a run of identical calls", () => {
242242 expect ( rows [ 0 ] ?. outstanding ) . toBe ( 0 ) ;
243243 } ) ;
244244
245- test ( "a lane keeps every member id while the count climbs " , ( ) => {
245+ test ( "a lane caps member ids and labels together at the run cap " , ( ) => {
246246 const rows : StreamRow [ ] = [ ] ;
247247 for ( let i = 1 ; i <= 33 ; i ++ ) {
248248 pushToolCall ( rows , {
@@ -253,7 +253,10 @@ describe("a run of identical calls", () => {
253253 }
254254 expect ( rows . length ) . toBe ( 1 ) ;
255255 expect ( rows [ 0 ] ?. callCount ) . toBe ( 33 ) ;
256- expect ( rows [ 0 ] ?. memberIds ?. length ) . toBe ( 33 ) ;
256+ // The lane's memory is bounded alongside the detail cap, oldest-first
257+ // like the answers — and both arrays together, never one alone.
258+ expect ( rows [ 0 ] ?. memberIds ?. length ) . toBe ( 30 ) ;
259+ expect ( rows [ 0 ] ?. memberLabels ?. length ) . toBe ( 30 ) ;
257260 expect ( rows [ 0 ] ?. memberIds ?. [ 0 ] ) . toBe ( "c1" ) ;
258261 expect ( pendingCallIndex ( rows , "grep" , "c33" ) ) . toBe ( 0 ) ;
259262 expect ( pendingCallIndex ( rows , "grep" , "c1" ) ) . toBe ( 0 ) ;
@@ -274,16 +277,24 @@ describe("a run of identical calls", () => {
274277 expect ( rows . length ) . toBe ( 1 ) ;
275278 expect ( rows [ 0 ] ?. pending ) . toBe ( true ) ;
276279 expect ( rows [ 0 ] ?. outstanding ) . toBe ( 32 ) ;
277- for ( let i = 2 ; i <= 33 ; i ++ ) {
280+ // Members past the run cap kept no id on the lane, so their answers
281+ // cannot pair back to it — except the newest, found by the lane's own id.
282+ for ( let i = 2 ; i <= 30 ; i ++ ) {
278283 pushToolResult ( rows , {
279284 name : "grep" ,
280285 content : `r${ i } ` ,
281286 callId : `c${ i } ` ,
282287 } ) ;
283288 }
289+ pushToolResult ( rows , { name : "grep" , content : "r33" , callId : "c33" } ) ;
284290 expect ( rows . length ) . toBe ( 1 ) ;
285- expect ( rows [ 0 ] ?. pending ) . toBeUndefined ( ) ;
286- expect ( rows [ 0 ] ?. outstanding ) . toBe ( 0 ) ;
291+ expect ( rows [ 0 ] ?. pending ) . toBe ( true ) ;
292+ expect ( rows [ 0 ] ?. outstanding ) . toBe ( 2 ) ;
293+ pushToolResult ( rows , { name : "grep" , content : "r31" , callId : "c31" } ) ;
294+ pushToolResult ( rows , { name : "grep" , content : "r32" , callId : "c32" } ) ;
295+ expect ( rows . length ) . toBe ( 3 ) ;
296+ expect ( rows [ 0 ] ?. pending ) . toBe ( true ) ;
297+ expect ( rows [ 0 ] ?. outstanding ) . toBe ( 2 ) ;
287298 } ) ;
288299
289300 test ( "a different tool breaks the lane" , ( ) => {
@@ -370,6 +381,70 @@ describe("a run of identical calls", () => {
370381 ] ) ;
371382 } ) ;
372383
384+ test ( "four members each name their own target" , ( ) => {
385+ const rows : StreamRow [ ] = [ ] ;
386+ const issues = [ "CL-7386" , "CL-7390" , "CL-7399" , "CL-7401" ] ;
387+ issues . forEach ( ( issueId , i ) => {
388+ pushToolCall ( rows , {
389+ name : "mcp__linear__save_comment" ,
390+ arguments : JSON . stringify ( { issueId, body : `note ${ i } ` } ) ,
391+ callId : `m${ i } ` ,
392+ } ) ;
393+ } ) ;
394+ issues . forEach ( ( _ , i ) => {
395+ pushToolResult ( rows , {
396+ name : "mcp__linear__save_comment" ,
397+ content : "" ,
398+ callId : `m${ i } ` ,
399+ } ) ;
400+ } ) ;
401+ expect ( rows . length ) . toBe ( 1 ) ;
402+ expect ( rows [ 0 ] ?. memberLabels ) . toEqual ( issues ) ;
403+ expect ( runLines ( rows [ 0 ] ) ) . toEqual ( issues . map ( ( id ) => `${ id } — answered` ) ) ;
404+ } ) ;
405+
406+ test ( "a pre-PR lane with ids but no labels coalesces with aligned placeholders" , ( ) => {
407+ const rows : StreamRow [ ] = [ ] ;
408+ pushToolCall ( rows , {
409+ name : "mcp__linear__save_comment" ,
410+ arguments : JSON . stringify ( { issueId : "CL-7386" , body : "first" } ) ,
411+ callId : "m1" ,
412+ } ) ;
413+ pushToolCall ( rows , {
414+ name : "mcp__linear__save_comment" ,
415+ arguments : JSON . stringify ( { issueId : "CL-7390" , body : "second" } ) ,
416+ callId : "m2" ,
417+ } ) ;
418+ // Lanes persisted before per-call labels carry memberIds without
419+ // memberLabels; only the tail's own label is still recoverable.
420+ const { memberLabels : _dropped , ...legacy } = defined ( rows [ 0 ] ) ;
421+ rows [ 0 ] = legacy ;
422+ pushToolCall ( rows , {
423+ name : "mcp__linear__save_comment" ,
424+ arguments : JSON . stringify ( { issueId : "CL-7399" , body : "third" } ) ,
425+ callId : "m3" ,
426+ } ) ;
427+ expect ( rows [ 0 ] ?. memberIds ) . toEqual ( [ "m1" , "m2" , "m3" ] ) ;
428+ expect ( rows [ 0 ] ?. memberLabels ) . toEqual ( [ "" , "CL-7390" , "CL-7399" ] ) ;
429+ pushToolResult ( rows , {
430+ name : "mcp__linear__save_comment" ,
431+ content : "" ,
432+ callId : "m2" ,
433+ } ) ;
434+ // Without the placeholder backfill the second result would read the
435+ // third member's label.
436+ expect ( runLines ( rows [ 0 ] ) ) . toEqual ( [ "CL-7390 — answered" ] ) ;
437+ pushToolResult ( rows , {
438+ name : "mcp__linear__save_comment" ,
439+ content : "" ,
440+ callId : "m3" ,
441+ } ) ;
442+ expect ( runLines ( rows [ 0 ] ) ) . toEqual ( [
443+ "CL-7390 — answered" ,
444+ "CL-7399 — answered" ,
445+ ] ) ;
446+ } ) ;
447+
373448 test ( "a member settled before the lane keeps the call's label, not the payload's" , ( ) => {
374449 const rows : StreamRow [ ] = [ ] ;
375450 pushToolCall ( rows , {
0 commit comments