@@ -285,6 +285,11 @@ function boardLaneState(
285285 * zone's box — rows land on top of each other and on whatever is below. So the
286286 * granted height is the last word, and the lanes it costs are disclosed rather
287287 * than dropped in silence.
288+ *
289+ * When the formatter already folded a fan-out (`+N more lanes` or header
290+ * `+N hidden`), that prior count is carried into the re-clamp total so the
291+ * operator still sees every running lane accounted for — not only the ones
292+ * still present as row objects after the first fold.
288293 */
289294export function clampBoardRows (
290295 rows : readonly AgentPanelRow [ ] ,
@@ -296,27 +301,68 @@ export function clampBoardRows(
296301 const header = rows [ 0 ]
297302 if ( header === undefined ) return [ ]
298303 const lanes = rows . filter ( ( r ) => r . kind === "lane" )
304+ const priorHidden = priorHiddenCount ( rows )
305+ // Drop any prior disclosure on the header; we restate the total below.
306+ const cleanHeader = stripHiddenTail ( header )
299307
300308 // Below a few rows the disclosure line costs more than the lane it displaces,
301309 // so the header carries the count instead — the same trade the formatter makes.
302310 if ( height < 4 ) {
303- const shown = lanes . slice ( 0 , height - 1 )
304- return [ withHiddenCount ( header , lanes . length - shown . length ) , ...shown ]
311+ const shown = lanes . slice ( 0 , Math . max ( 0 , height - 1 ) )
312+ const hidden = priorHidden + ( lanes . length - shown . length )
313+ return [ withHiddenCount ( cleanHeader , hidden ) , ...shown ]
305314 }
306315
307- const shown = lanes . slice ( 0 , height - 2 )
308- const hidden = lanes . length - shown . length
316+ const shown = lanes . slice ( 0 , Math . max ( 0 , height - 2 ) )
317+ const hidden = priorHidden + ( lanes . length - shown . length )
309318 return [
310- header ,
319+ cleanHeader ,
311320 ...shown ,
312321 { label : `+${ hidden } more lanes` , tail : "" , stalled : false , kind : "more" } ,
313322 ]
314323}
315324
325+ /** Lanes already disclosed by a prior format/clamp fold on these rows. */
326+ function priorHiddenCount ( rows : readonly AgentPanelRow [ ] ) : number {
327+ let hidden = 0
328+ for ( const row of rows ) {
329+ if ( row . kind === "more" ) {
330+ const match = / ^ \+ ( \d + ) m o r e l a n e s $ / . exec ( row . label )
331+ if ( match ?. [ 1 ] !== undefined ) hidden += Number ( match [ 1 ] )
332+ continue
333+ }
334+ if ( row . kind === "header" ) {
335+ const match = / · \+ ( \d + ) h i d d e n $ / . exec ( row . tail )
336+ if ( match ?. [ 1 ] !== undefined ) hidden += Number ( match [ 1 ] )
337+ }
338+ }
339+ return hidden
340+ }
341+
342+ function stripHiddenTail ( header : AgentPanelRow ) : AgentPanelRow {
343+ const tail = header . tail . replace ( / · \+ \d + h i d d e n $ / , "" )
344+ return tail === header . tail ? header : { ...header , tail }
345+ }
346+
316347function withHiddenCount ( header : AgentPanelRow , hidden : number ) : AgentPanelRow {
317348 return hidden > 0 ? { ...header , tail : ` · +${ hidden } hidden` } : header
318349}
319350
351+ /**
352+ * Operator-facing state word. Machine `LaneState` stays snake_case for code;
353+ * the board never paints that vocabulary into the terminal.
354+ */
355+ function laneStateWord ( state : LaneState ) : string {
356+ switch ( state ) {
357+ case "in_tool" :
358+ return "in tool"
359+ case "stalled" :
360+ return "stalled"
361+ case "working" :
362+ return "working"
363+ }
364+ }
365+
320366/**
321367 * The one-line answer to "is everything fine". Counts run worst-first so that
322368 * a narrow terminal ellipsizes away the routine tail rather than the trouble.
@@ -366,12 +412,12 @@ function formatAgentRow(
366412
367413 // Prefer main's agentProgress for tool-clock / in_tool / quiet clocks so the
368414 // board never invents a second stall path. Board presentation still prefixes
369- // the state word (and kind: lane) the way the fleet board reads.
415+ // the operator-facing state word (and kind: lane) the way the fleet board reads.
370416 const progress = agentProgress ( progressSession , nowMs , stallMs )
371417 if ( progress !== null ) {
372418 return {
373419 label,
374- tail : ` · ${ state } · ${ progress . stat } ` ,
420+ tail : ` · ${ laneStateWord ( state ) } · ${ progress . stat } ` ,
375421 stalled,
376422 kind : "lane" ,
377423 }
0 commit comments