@@ -384,7 +384,7 @@ describe("wireGates", () => {
384384 } )
385385 } )
386386
387- test ( "gate content reaches the transcript only after the operator decides " , async ( ) => {
387+ test ( "gate decisions do not replay the request into the transcript " , async ( ) => {
388388 await withTestRenderer ( async ( h ) => {
389389 const shell = createAppShell ( h . renderer , {
390390 terminal : { columns : 96 , rows : 30 } ,
@@ -401,20 +401,15 @@ describe("wireGates", () => {
401401 const dispose = wireGates ( emitter , shell )
402402 emitter . emit ( "permission.gate" , { request, resolve : ( ) => { } } )
403403
404- // The overlay is showing this text; a transcript copy directly above it
405- // reads as a second, unrelated request.
406404 expect (
407405 shell . streamLog . filter ( ( r ) => r . meta === "permission" ) ,
408406 ) . toHaveLength ( 0 )
409407
410408 acceptOverlaySelection ( shell )
411409
412- const recorded = shell . streamLog
413- . filter ( ( r ) => r . meta === "permission" )
414- . map ( ( r ) => r . text )
415- . join ( "\n" )
416- expect ( recorded ) . toContain ( "ls -la ~/.corbits/projects" )
417- expect ( recorded ) . toContain ( "Reject" )
410+ expect (
411+ shell . streamLog . filter ( ( r ) => r . meta === "permission" ) ,
412+ ) . toHaveLength ( 0 )
418413
419414 dispose ( )
420415 } finally {
@@ -424,7 +419,7 @@ describe("wireGates", () => {
424419 } )
425420} )
426421
427- describe ( "each gate decision appends exactly one transcript row " , ( ) => {
422+ describe ( "gate decisions stay out of the transcript " , ( ) => {
428423 test ( "permission accept" , async ( ) => {
429424 await withTestRenderer ( async ( h ) => {
430425 const shell = createAppShell ( h . renderer , {
@@ -438,7 +433,7 @@ describe("each gate decision appends exactly one transcript row", () => {
438433
439434 const before = shell . streamLog . length
440435 acceptOverlaySelection ( shell )
441- expect ( shell . streamLog . length - before ) . toBe ( 1 )
436+ expect ( shell . streamLog . length - before ) . toBe ( 0 )
442437 } finally {
443438 shell . dispose ( )
444439 }
@@ -458,7 +453,7 @@ describe("each gate decision appends exactly one transcript row", () => {
458453
459454 const before = shell . streamLog . length
460455 closeInsetOverlay ( shell )
461- expect ( shell . streamLog . length - before ) . toBe ( 1 )
456+ expect ( shell . streamLog . length - before ) . toBe ( 0 )
462457 } finally {
463458 shell . dispose ( )
464459 }
@@ -482,7 +477,7 @@ describe("each gate decision appends exactly one transcript row", () => {
482477
483478 const before = shell . streamLog . length
484479 acceptOverlaySelection ( shell )
485- expect ( shell . streamLog . length - before ) . toBe ( 1 )
480+ expect ( shell . streamLog . length - before ) . toBe ( 0 )
486481 } finally {
487482 shell . dispose ( )
488483 }
@@ -506,7 +501,7 @@ describe("each gate decision appends exactly one transcript row", () => {
506501
507502 const before = shell . streamLog . length
508503 closeInsetOverlay ( shell )
509- expect ( shell . streamLog . length - before ) . toBe ( 1 )
504+ expect ( shell . streamLog . length - before ) . toBe ( 0 )
510505 } finally {
511506 shell . dispose ( )
512507 }
@@ -546,7 +541,7 @@ describe("each gate decision appends exactly one transcript row", () => {
546541 meta : false ,
547542 option : false ,
548543 } as unknown as KeyEvent )
549- expect ( shell . streamLog . length - before ) . toBe ( 1 )
544+ expect ( shell . streamLog . length - before ) . toBe ( 0 )
550545 } finally {
551546 shell . dispose ( )
552547 }
@@ -569,7 +564,7 @@ describe("each gate decision appends exactly one transcript row", () => {
569564 timeoutMs : 5 ,
570565 } )
571566 await new Promise ( ( r ) => setTimeout ( r , 20 ) )
572- expect ( shell . streamLog . length - before ) . toBe ( 1 )
567+ expect ( shell . streamLog . length - before ) . toBe ( 0 )
573568 } finally {
574569 shell . dispose ( )
575570 }
@@ -593,20 +588,19 @@ describe("each gate decision appends exactly one transcript row", () => {
593588 signal : controller . signal ,
594589 } )
595590 controller . abort ( )
596- expect ( shell . streamLog . length - before ) . toBe ( 1 )
591+ expect ( shell . streamLog . length - before ) . toBe ( 0 )
597592 } finally {
598593 shell . dispose ( )
599594 }
600595 } )
601596 } )
602597
603- // The queue (settle-once guard) and the transcript recorder (record-once
604- // per decision) are two independent mechanisms layered on the same set of
605- // terminal paths. Racing a timeout against an abort on the same request
606- // exercises both at once: clearTimers must retire the loser before it can
607- // run autoDeny a second time, so ev.resolve fires exactly once and exactly
608- // one row lands, no matter which trigger wins.
609- test ( "a timeout and an abort racing the same request settle once and record once" , async ( ) => {
598+ // The queue's settle-once guard is independent of the transcript: racing a
599+ // timeout against an abort on the same request exercises that guard.
600+ // clearTimers must retire the loser before it can run autoDeny a second
601+ // time, so ev.resolve fires exactly once no matter which trigger wins, and
602+ // neither path writes a recap row.
603+ test ( "a timeout and an abort racing the same request settle once" , async ( ) => {
610604 await withTestRenderer ( async ( h ) => {
611605 const shell = createAppShell ( h . renderer , {
612606 terminal : { columns : 80 , rows : 24 } ,
@@ -633,14 +627,14 @@ describe("each gate decision appends exactly one transcript row", () => {
633627 controller . abort ( )
634628
635629 expect ( resolveCount ) . toBe ( 1 )
636- expect ( shell . streamLog . length - before ) . toBe ( 1 )
630+ expect ( shell . streamLog . length - before ) . toBe ( 0 )
637631 } finally {
638632 shell . dispose ( )
639633 }
640634 } )
641635 } )
642636
643- test ( "a queued gate's timeout settles once and records once , only after it is displayed" , async ( ) => {
637+ test ( "a queued gate's timeout settles once, only after it is displayed" , async ( ) => {
644638 await withTestRenderer ( async ( h ) => {
645639 const shell = createAppShell ( h . renderer , {
646640 terminal : { columns : 80 , rows : 24 } ,
@@ -673,7 +667,7 @@ describe("each gate decision appends exactly one transcript row", () => {
673667 await new Promise ( ( r ) => setTimeout ( r , 20 ) )
674668
675669 expect ( resolveCount ) . toBe ( 1 )
676- expect ( shell . streamLog . length - before ) . toBe ( 2 ) // first gate's row + the queued gate's timeout row
670+ expect ( shell . streamLog . length - before ) . toBe ( 0 ) // first gate + queued timeout both silent
677671 } finally {
678672 shell . dispose ( )
679673 }
@@ -682,8 +676,8 @@ describe("each gate decision appends exactly one transcript row", () => {
682676
683677 // reconcile() (src/permission/queue.ts) settles a queued request directly
684678 // when a grant covers it, with no accept/cancel/autoDeny callback of its
685- // own to hang a row on — this is the one terminal path that has no natural
686- // call site, so it needs its own coverage .
679+ // own. Coverage here is that the queued request still resolves, without
680+ // ever opening and without writing a recap row .
687681 test ( "a grant draining a queued request without ever displaying it" , async ( ) => {
688682 await withTestRenderer ( async ( h ) => {
689683 const shell = createAppShell ( h . renderer , {
@@ -717,17 +711,14 @@ describe("each gate decision appends exactly one transcript row", () => {
717711
718712 expect ( resolveCount ) . toBe ( 1 )
719713 expect ( resolved ) . toEqual ( { allow : true } )
720- expect ( shell . streamLog . length - before ) . toBe ( 1 )
721- expect ( shell . streamLog . at ( - 1 ) ?. text ) . toContain (
722- "Auto-approved (already granted)" ,
723- )
714+ expect ( shell . streamLog . length - before ) . toBe ( 0 )
724715 } finally {
725716 shell . dispose ( )
726717 }
727718 } )
728719 } )
729720
730- test ( "a grant draining the currently displayed request closes it and records once " , async ( ) => {
721+ test ( "a grant draining the currently displayed request closes it without a recap " , async ( ) => {
731722 await withTestRenderer ( async ( h ) => {
732723 const shell = createAppShell ( h . renderer , {
733724 terminal : { columns : 80 , rows : 24 } ,
@@ -753,10 +744,7 @@ describe("each gate decision appends exactly one transcript row", () => {
753744
754745 expect ( resolveCount ) . toBe ( 1 )
755746 expect ( shell . overlayList ) . toBeNull ( )
756- expect ( shell . streamLog . length - before ) . toBe ( 1 )
757- expect ( shell . streamLog . at ( - 1 ) ?. text ) . toContain (
758- "Auto-approved (already granted)" ,
759- )
747+ expect ( shell . streamLog . length - before ) . toBe ( 0 )
760748 } finally {
761749 shell . dispose ( )
762750 }
@@ -765,19 +753,18 @@ describe("each gate decision appends exactly one transcript row", () => {
765753
766754 // drain() (src/permission/queue.ts) denies whatever is still queued on
767755 // teardown — the same no-call-site path as a grant drain, but the
768- // opposite outcome. Mislabeling this "Auto-approved" would tell the
769- // operator a request ran when it was actually dropped unanswered .
770- test ( "disposing with a request still queued records it as denied, not approved " , async ( ) => {
756+ // opposite outcome. Coverage is the deny itself; neither path writes a
757+ // recap row .
758+ test ( "disposing with a request still queued denies it without a recap " , async ( ) => {
771759 await withTestRenderer ( async ( h ) => {
772760 const shell = createAppShell ( h . renderer , {
773761 terminal : { columns : 80 , rows : 24 } ,
774762 run : "idle" ,
775763 } )
776764 const emitter = new EventEmitter ( )
777765 // The currently-open request has no accept/cancel/autoDeny call site
778- // triggered before teardown either, so dispose must record it too —
779- // both entries go through the same no-call-site fallback as the
780- // queued one.
766+ // triggered before teardown either, so dispose must settle it too —
767+ // both entries go through drain() without writing a recap.
781768 let openResolveCount = 0
782769 let queuedResolveCount = 0
783770 let queuedResolved : unknown
@@ -804,11 +791,7 @@ describe("each gate decision appends exactly one transcript row", () => {
804791 expect ( openResolveCount ) . toBe ( 1 )
805792 expect ( queuedResolveCount ) . toBe ( 1 )
806793 expect ( queuedResolved ) . toEqual ( { allow : false } )
807- expect ( shell . streamLog . length - before ) . toBe ( 2 )
808- for ( const row of shell . streamLog . slice ( - 2 ) ) {
809- expect ( row . text ) . toContain ( "Denied (session ended)" )
810- expect ( row . text ) . not . toContain ( "Auto-approved" )
811- }
794+ expect ( shell . streamLog . length - before ) . toBe ( 0 )
812795 shell . dispose ( )
813796 } )
814797 } )
@@ -1148,7 +1131,7 @@ describe("operator.gate auto-cancel", () => {
11481131 } )
11491132 } )
11501133
1151- test ( "each terminal path writes exactly one transcript row" , async ( ) => {
1134+ test ( "each terminal path settles without writing a transcript row" , async ( ) => {
11521135 await withTestRenderer ( async ( h ) => {
11531136 const shell = createAppShell ( h . renderer , {
11541137 terminal : { columns : 80 , rows : 24 } ,
@@ -1165,7 +1148,7 @@ describe("operator.gate auto-cancel", () => {
11651148 timeoutMs : 5 ,
11661149 } )
11671150 await new Promise ( ( r ) => setTimeout ( r , 20 ) )
1168- expect ( shell . streamLog . length - before ) . toBe ( 1 )
1151+ expect ( shell . streamLog . length - before ) . toBe ( 0 )
11691152 } finally {
11701153 shell . dispose ( )
11711154 }
@@ -1208,10 +1191,7 @@ describe("operator.gate auto-cancel", () => {
12081191
12091192 expect ( openResolved ) . toEqual ( operatorCancelResult ( ) )
12101193 expect ( queuedResolved ) . toEqual ( operatorCancelResult ( ) )
1211- expect ( shell . streamLog . length - before ) . toBe ( 2 )
1212- for ( const row of shell . streamLog . slice ( - 2 ) ) {
1213- expect ( row . text ) . toContain ( "Cancelled (session ended)" )
1214- }
1194+ expect ( shell . streamLog . length - before ) . toBe ( 0 )
12151195 shell . dispose ( )
12161196 } )
12171197 } )
0 commit comments