@@ -338,6 +338,52 @@ describe("the release and the nightly side by side", () => {
338338
339339 expect ( edits [ 0 ] ?. removed ) . toEqual ( [ ] ) ;
340340 } ) ;
341+
342+ // The scoping lives in `syncPermissions()`, but each harness finds and
343+ // edits its own format, and Codex rewrites its whole file.
344+ it . each ( [
345+ {
346+ harness : "Cursor" ,
347+ selected : [ ".cursor" ] ,
348+ files : [ ".cursor/permissions.json" , ".cursor/cli.json" ] ,
349+ } ,
350+ {
351+ harness : "OpenCode, object form" ,
352+ selected : [ ".opencode" ] ,
353+ files : [ ".opencode/opencode.json" ] ,
354+ } ,
355+ {
356+ harness : "OpenCode, array form" ,
357+ selected : [ ".opencode" ] ,
358+ files : [ "opencode.json" ] ,
359+ seed : '{ "permissions": [] }\n' ,
360+ } ,
361+ {
362+ harness : "Codex" ,
363+ selected : [ ".agents" ] ,
364+ codexDetected : true ,
365+ files : [ ".codex/rules/taskless.rules" ] ,
366+ } ,
367+ ] ) (
368+ "keeps each other's rules in $harness" ,
369+ async ( { selected, codexDetected = false , files, seed } ) => {
370+ if ( seed !== undefined ) await put ( files [ 0 ] ?? "" , seed ) ;
371+ const run = ( invocation : string ) =>
372+ sync ( { selected, codexDetected, invocation } ) ;
373+ await run ( V1 ) ;
374+ await run ( N1 ) ;
375+ await run ( N2 ) ;
376+
377+ expect ( await run ( V1 ) ) . toEqual ( [ ] ) ;
378+ expect ( await run ( N2 ) ) . toEqual ( [ ] ) ;
379+ for ( const file of files ) {
380+ const contents = await text ( file ) ;
381+ expect ( contents ) . toContain ( "@taskless/cli@0.13.0" ) ;
382+ expect ( contents ) . toContain ( "0.13.2-20261017000000xbbbbbbb" ) ;
383+ expect ( contents ) . not . toContain ( "0.13.2-20261016000000xaaaaaaa" ) ;
384+ }
385+ }
386+ ) ;
341387} ) ;
342388
343389describe ( "Cursor" , ( ) => {
0 commit comments