@@ -334,6 +334,34 @@ describe("CL-9469 unbounded-root predicate", () => {
334334 ) ;
335335 }
336336 } ) ;
337+ // A `..` after the first `**` is never collapsed: hasLiteralBefore only folds
338+ // the prefix *before* the first `**`, so a `..` in unanchored recursive
339+ // descent does not pop the pin. And because rg is cwd-anchored, `a/**/../**`
340+ // descends from `a` and steps back up a relative level — it cannot escape the
341+ // workspace root (there is no `..` above the anchor), so it stays a bounded
342+ // subtree walk of `a`.
343+ test ( "interior `..` after the pin is a bounded cwd-anchored descent" , ( ) => {
344+ for ( const pattern of [
345+ "a/**/../**" ,
346+ "src/**/../**" ,
347+ "sub/**/../**/*.ts" ,
348+ "docs/**/../**" ,
349+ "a/src/**/../**" ,
350+ "a/**/../src/**" ,
351+ ] ) {
352+ expect ( isUnboundedRootSearch ( { path : undefined , pattern, cwd } ) ) . toBe (
353+ false ,
354+ ) ;
355+ }
356+ // An all-wildcard pin followed by `..` leaves no name to anchor, so it is
357+ // still an unbounded root walk; a `../../` that folds the prefix to the
358+ // root is refused (already covered as "collapse to the root").
359+ for ( const pattern of [ "*/**/../**" , "a/src/../../**" ] ) {
360+ expect ( isUnboundedRootSearch ( { path : undefined , pattern, cwd } ) ) . toBe (
361+ true ,
362+ ) ;
363+ }
364+ } ) ;
337365 test ( "refusal message carries scope guidance and never reads as a timeout" , ( ) => {
338366 const message = formatUnboundedSearchMessage ( "search_files" , "**/*.ts" ) ;
339367 expect ( message ) . toContain ( "narrow `path`" ) ;
0 commit comments