@@ -2042,6 +2042,83 @@ describe("SessionManager.open", () => {
20422042 expect ( sessionManager . getEntries ( ) ) . toHaveLength ( entryCount ) ;
20432043 } ) ;
20442044
2045+ it ( "rejects an unowned side leaf that resets a non-root side cursor" , async ( ) => {
2046+ const dir = await makeTempDir ( ) ;
2047+ const sessionManager = SessionManager . create ( dir , dir ) ;
2048+ sessionManager . appendMessage ( { role : "user" , content : "question" , timestamp : 1 } ) ;
2049+ const activeLeafId = sessionManager . appendMessage ( buildAssistantMessage ( "base answer" ) ) ;
2050+ const entryCount = sessionManager . getEntries ( ) . length ;
2051+ const sideEntry = {
2052+ type : "message" as const ,
2053+ id : "side-delivery-before-root-reset" ,
2054+ parentId : activeLeafId ,
2055+ timestamp : "2026-07-05T00:00:01.000Z" ,
2056+ message : buildAssistantMessage ( "side delivery" ) ,
2057+ } ;
2058+
2059+ expect ( ( ) =>
2060+ sessionManager . mergePromptReleasedSessionEntries ( [
2061+ sideEntry ,
2062+ {
2063+ type : "prompt_released_opaque" ,
2064+ preserveActiveLeaf : true ,
2065+ record : {
2066+ type : "leaf" ,
2067+ id : "unowned-root-reset-leaf" ,
2068+ parentId : sideEntry . id ,
2069+ timestamp : "2026-07-05T00:00:02.000Z" ,
2070+ targetId : activeLeafId ,
2071+ appendParentId : null ,
2072+ appendMode : "side" ,
2073+ } ,
2074+ } ,
2075+ ] ) ,
2076+ ) . toThrow ( "prompt-released side leaf changed the active branch" ) ;
2077+ expect ( sessionManager . getLeafId ( ) ) . toBe ( activeLeafId ) ;
2078+ expect ( sessionManager . getEntries ( ) ) . toHaveLength ( entryCount ) ;
2079+ } ) ;
2080+
2081+ it ( "accepts an explicit root side cursor when it matches the current side branch" , async ( ) => {
2082+ const dir = await makeTempDir ( ) ;
2083+ const sessionManager = SessionManager . create ( dir , dir ) ;
2084+ sessionManager . appendMessage ( { role : "user" , content : "question" , timestamp : 1 } ) ;
2085+ const activeLeafId = sessionManager . appendMessage ( buildAssistantMessage ( "base answer" ) ) ;
2086+
2087+ sessionManager . mergePromptReleasedSessionEntries ( [
2088+ {
2089+ type : "prompt_released_opaque" ,
2090+ record : {
2091+ type : "leaf" ,
2092+ id : "owned-root-side-leaf" ,
2093+ parentId : activeLeafId ,
2094+ timestamp : "2026-07-05T00:00:01.000Z" ,
2095+ targetId : activeLeafId ,
2096+ appendParentId : null ,
2097+ appendMode : "side" ,
2098+ } ,
2099+ } ,
2100+ ] ) ;
2101+
2102+ expect ( ( ) =>
2103+ sessionManager . mergePromptReleasedSessionEntries ( [
2104+ {
2105+ type : "prompt_released_opaque" ,
2106+ preserveActiveLeaf : true ,
2107+ record : {
2108+ type : "leaf" ,
2109+ id : "unowned-root-side-leaf" ,
2110+ parentId : null ,
2111+ timestamp : "2026-07-05T00:00:02.000Z" ,
2112+ targetId : activeLeafId ,
2113+ appendParentId : null ,
2114+ appendMode : "side" ,
2115+ } ,
2116+ } ,
2117+ ] ) ,
2118+ ) . not . toThrow ( ) ;
2119+ expect ( sessionManager . getLeafId ( ) ) . toBe ( activeLeafId ) ;
2120+ } ) ;
2121+
20452122 it ( "applies merged leaf controls across separate callbacks" , async ( ) => {
20462123 const dir = await makeTempDir ( ) ;
20472124 const sessionManager = SessionManager . create ( dir , dir ) ;
0 commit comments