Luigit
repositories / dotfiles

dotfiles

bugabingas dorkfiles

owned by admin

neovim/tests/e2e/scenarios.lua

Raw
return function( directory )
  local results = {}
  local scenario = function( name, body )
    local select = vim.ui.select
    local session_dir = vim.env.PI_CODING_AGENT_SESSION_DIR
    local ok, failure = xpcall( body, debug.traceback )
    vim.ui.select = select
    vim.env.PI_CODING_AGENT_SESSION_DIR = session_dir
    table.insert( results, { name = name, ok = ok, error = failure and ( ': ' .. failure ) or nil, } )
    return ok
  end
  local await = function( predicate, label )
    assert( vim.wait( 120000, predicate, 10 ), label or 'predicate deadline' )
  end
  local pivi = require 'bugabinga.pivi'
  local current
  local request = function( command, target )
    local response
    ( target or current ).request( command, function( answer ) response = answer end )
    await( function() return response ~= nil end, command.type .. ' deadline' )
    assert( response.success, response.error )
    return response.data
  end
  local control = function( name, args )
    return request { type = 'prompt', message = '/' .. name .. ( args and ( ' ' .. args ) or '' ), }
  end
  local responses = function( steps ) control( 'e2e-responses', vim.json.encode( steps ) ) end
  local text = function() return table.concat( pivi.transcript.lines( current.buffer ), '\n' ) end
  local finish = function( command )
    local settled = false
    local unsubscribe = current.on( function( event ) if event.type == 'agent_settled' then settled = true end end )
    command()
    await( function() return settled end, 'agent_settled deadline' )
    unsubscribe()
    request { type = 'get_state', }
    assert( #current.client.failures() == 0, table.concat( current.client.failures(), '\n' ) )
    assert( current.status.run == 'idle' )
  end
  local call_count = function()
    control 'e2e-count'
    request { type = 'get_state', }
    return tonumber( pivi.dialog.statuses()['e2e-count'] )
  end
  local stop = function( live )
    live.stop()
    await( function() return not live.client.is_running() end, 'Pi shutdown deadline' )
  end
  local file = directory .. '/source.lua'
  vim.fn.writefile( { 'local value = 1', 'return value', }, file )
  vim.cmd.edit( file )
  local source = vim.api.nvim_get_current_buf()
  local working = vim.api.nvim_get_current_win()
  if not scenario( 'start: real Pi, additive user config, faux model, no model call', function()
    vim.cmd 'Pi start alpha'
    current = pivi.session.active()
    assert( current.name == 'alpha' )
    local state = request { type = 'get_state', }
    assert( state.model.provider == 'pivi-e2e', 'refusing metered provider' )
    assert( state.sessionId )
    local commands = request { type = 'get_commands', }
    assert( vim.iter( commands.commands ):any( function( item )
      return item.sourceInfo and item.sourceInfo.scope == 'user'
    end ), 'configured user resources not loaded' )
    assert( call_count() == 0 )
    assert( vim.api.nvim_win_get_buf( working ) == source )
  end ) then return results end
  scenario( 'ask: real streamed markdown, authored text, extmark decoration, cached status', function()
    local authored = '# Answer\n\n**native** `code`\n\n```lua\nreturn 42\n```'
    responses { { content = authored, }, }
    finish( function() vim.cmd 'Pi ask e2e question' end )
    assert( text():find( authored, 1, true ) )
    assert( not text():find( '## pi', 1, true ) )
    assert( #pivi.render.marks( current.buffer ) > 0 )
    assert( pivi.summary() == 'pi:idle' )
    local before = call_count()
    for _ = 1, 100 do pivi.summary() end
    assert( before == call_count() )
  end )
  scenario( 'prompt: native mapping submits, hides without stopping, draft survives', function()
    vim.cmd 'Pi prompt'
    local prompt = vim.api.nvim_get_current_buf()
    vim.api.nvim_buf_set_lines( prompt, 0, -1, false, { 'from prompt', } )
    responses { { content = 'prompt answer', }, }
    finish( function()
      local maps = vim.api.nvim_buf_get_keymap( prompt, 'n' )
      for _, map in ipairs( maps ) do if map.lhs == '<Plug>(pivi-submit)' then map.callback(); return end end
      error 'missing submit mapping'
    end )
    assert( text():find( 'prompt answer', 1, true ) )
    assert( vim.api.nvim_buf_get_lines( prompt, 0, -1, false )[1] == '' )
    vim.api.nvim_buf_set_lines( prompt, 0, -1, false, { 'saved draft', } )
    vim.api.nvim_win_close( vim.fn.bufwinid( prompt ), true )
    assert( current.client.is_running() )
    vim.cmd 'Pi prompt'
    assert( vim.api.nvim_get_current_buf() == prompt )
    assert( vim.api.nvim_get_current_line() == 'saved draft' )
  end )
  scenario( 'slash: discoverable command dialogs, generic status/widgets, no model call', function()
    local before = call_count()
    local original = vim.ui.select
    vim.ui.select = function( items, _, callback ) callback( items[2] ) end
    vim.cmd 'Pi command e2e-dialog'
    await( function()
      return pivi.dialog.statuses()['e2e-choice'] == 'second' and pivi.dialog.widgets()['e2e-widget'] ~= nil
    end )
    vim.ui.select = original
    assert( pivi.dialog.widgets()['e2e-widget'] )
    assert( call_count() == before )
    vim.cmd 'Pi command model'
    request { type = 'get_state', }
    assert( call_count() == before, 'terminal command became a model prompt' )
  end )
  scenario( 'queue: prompt accept steers, follow-up listed/cleared, abort settles', function()
    responses { { content = 'held answer', hold = true, }, }
    vim.cmd 'Pi ask hold the run'
    await( function() return current.status.run == 'running' end )
    vim.cmd 'Pi prompt'
    local prompt = vim.api.nvim_get_current_buf()
    vim.api.nvim_buf_set_lines( prompt, 0, -1, false, { 'steering message', } )
    for _, map in ipairs( vim.api.nvim_buf_get_keymap( prompt, 'n' ) ) do
      if map.lhs == '<Plug>(pivi-submit)' then map.callback() end
    end
    vim.cmd 'Pi follow-up follow-up message'
    await( function()
      return #current.status.queue.steering == 1 and #current.status.queue.follow_up == 1
    end )
    vim.cmd 'Pi queue'
    vim.cmd 'Pi clear'
    await( function() return #current.status.queue.steering == 0 and #current.status.queue.follow_up == 0 end )
    vim.cmd 'Pi abort'
    await( function() return current.status.run == 'idle' end )
    assert( request( { type = 'get_state', } ).isStreaming == false )
    vim.api.nvim_buf_set_lines( prompt, 0, -1, false, { 'saved draft', } )
  end )
  scenario( 'delivery: steering/follow-up both reach persisted conversation after release', function()
    responses {
      { content = 'initial answer', hold = true, },
      { content = 'steered answer', },
      { content = 'follow-up answer', },
    }
    local settled = false
    local unsubscribe = current.on( function( event ) if event.type == 'agent_settled' then settled = true end end )
    vim.cmd 'Pi ask delivery barrier'
    await( function() return current.status.run == 'running' end )
    vim.cmd 'Pi steer delivered steering'
    vim.cmd 'Pi follow-up delivered follow-up'
    await( function() return #current.status.queue.steering == 1 and #current.status.queue.follow_up == 1 end )
    control 'e2e-release'
    await( function() return settled end )
    unsubscribe()
    local history = vim.json.encode( request { type = 'get_messages', } )
    assert( history:find( 'delivered steering', 1, true ) )
    assert( history:find( 'delivered follow-up', 1, true ) )
    assert( text():find( 'follow-up answer', 1, true ) )
  end )
  scenario( 'help: real independent faux run while working, live docs, history/queue/status untouched', function()
    responses { { content = 'held working answer', hold = true, }, }
    vim.cmd 'Pi ask independent help barrier'
    await( function() return current.status.run == 'running' end )
    local history_before = #request( { type = 'get_entries', } ).entries
    local queue_before = vim.deepcopy( current.status.queue )
    local revision = vim.api.nvim_buf_get_changedtick( source )
    vim.cmd 'Pi help what is quickfix?'
    local helpbuf = pivi.transcript.ensure( 'help' )
    await( function()
      return table.concat( pivi.transcript.lines( helpbuf ), '\n' ):find( 'Live quickfix documentation fetched.', 1, true ) ~= nil
    end )
    assert( current.status.run == 'running' )
    assert( #request( { type = 'get_entries', } ).entries == history_before )
    assert( vim.deep_equal( queue_before, current.status.queue ) )
    assert( vim.api.nvim_buf_get_changedtick( source ) == revision )
    assert( not text():find( 'what is quickfix?', 1, true ) )
    vim.cmd 'Pi abort'
    await( function() return current.status.run == 'idle' end )
  end )
  scenario( 'help safety: real malicious faux Lua/write calls cannot change editor/files', function()
    local revision = vim.api.nvim_buf_get_changedtick( source )
    local original = vim.fn.readfile( file )
    local run = pivi.help.ask( 'attempt mutation', { env = vim.tbl_extend( 'force', pivi.runtime.bridge_environment(), {
      PIVI_E2E_ATTACK = 'vim.api.nvim_buf_set_lines(' .. source .. ', 0, -1, false, {"mutated"})',
      PIVI_E2E_ATTACK_FILE = file,
    } ), } )
    await( function() return run.done end )
    await( function() return not run.client.is_running() end )
    assert( run.answer == 'Mutation attempts finished.' )
    assert( vim.api.nvim_buf_get_changedtick( source ) == revision, 'help Lua modified buffer' )
    assert( vim.deep_equal( original, vim.fn.readfile( file ) ), 'help wrote file' )
  end )
  scenario( 'tools: unsaved reads authoritative, editor state/Lua cross-process bridge', function()
    vim.api.nvim_buf_set_lines( source, 0, 1, false, { 'local value = 7', } )
    vim.api.nvim_buf_call( source, function() vim.cmd 'let &undolevels = &undolevels' end )
    responses {
      { content = {
        { type = 'toolCall', id = 'read-live', name = 'read', arguments = { path = file, }, },
      }, stopReason = 'toolUse', },
      { content = {
        { type = 'toolCall', id = 'lua-live', name = 'nvim_lua', arguments = { code = 'return vim.api.nvim_buf_get_lines(' .. source .. ', 0, -1, false)', }, },
      }, stopReason = 'toolUse', },
      { content = 'tools finished', },
    }
    finish( function() vim.cmd 'Pi ask inspect live editor' end )
    assert( text():find( 'local value = 7', 1, true ) )
    local tools = vim.tbl_filter( function( item ) return item.kind == 'tool' end, pivi.entry.list( current.buffer ) )
    assert( #tools >= 2 )
    for _, item in ipairs( tools ) do assert( not item.meta.failed, vim.inspect( item.meta ) ) end
  end )
  scenario( 'edit: real Pi tool, inert streaming ghost, local mutation, disk coherence, trail, one undo', function()
    local revision = vim.api.nvim_buf_get_changedtick( source )
    local ghost = false
    local ghost_error
    local unsubscribe = current.on( function( event )
      if event.type == 'message_update' and #pivi.activity.ghost_marks( source ) > 0 then
        ghost = true
        if vim.api.nvim_buf_get_changedtick( source ) ~= revision then ghost_error = 'streaming arguments mutated buffer' end
      end
    end )
    responses {
      { content = { { type = 'toolCall', id = 'edit-live', name = 'edit', arguments = {
        path = file, oldText = 'local value = 7', newText = 'local value = 8',
      }, }, }, stopReason = 'toolUse', },
      { content = 'edit finished', },
    }
    finish( function() vim.cmd 'Pi ask edit live buffer' end )
    unsubscribe()
    assert( ghost, 'real streamed arguments never showed ghost text' )
    assert( not ghost_error, ghost_error )
    assert( vim.api.nvim_buf_get_lines( source, 0, 1, false )[1] == 'local value = 8' )
    assert( vim.fn.readfile( file )[1] == 'local value = 8' )
    assert( #pivi.activity.trail( source ) > 0 )
    local item = pivi.entry.list( current.buffer )
    local edited
    for _, candidate in ipairs( item ) do if candidate.meta.call == 'edit-live' then edited = candidate end end
    assert( edited and edited.meta.result.details.diff )
    local win = vim.fn.bufwinid( current.buffer )
    vim.api.nvim_win_set_cursor( win, { edited.first, 0, } )
    local recorded = #request( { type = 'get_entries', } ).entries
    vim.api.nvim_win_call( win, function()
      assert( pivi.fold.closed( edited.first ) )
      vim.cmd 'normal! zo'
      assert( not pivi.fold.closed( edited.first ) )
      vim.cmd 'normal! zc'
      vim.cmd 'Pi diff'
    end )
    assert( vim.api.nvim_win_get_buf( working ) == source )
    assert( #request( { type = 'get_entries', } ).entries == recorded )
    pivi.comparison.close()
    vim.api.nvim_buf_call( source, function() vim.cmd 'undo' end )
    assert( vim.api.nvim_buf_get_lines( source, 0, 1, false )[1] == 'local value = 7' )
  end )
  scenario( 'findings/follow/failure: real tools, native qf, dedicated viewport, red outcome', function()
    vim.api.nvim_set_current_win( working )
    vim.cmd 'Pi follow window'
    responses {
      { content = { { type = 'toolCall', id = 'findings-live', name = 'nvim_findings', arguments = {
        title = 'e2e findings', items = { { path = file, line = 2, text = 'finding', }, },
      }, }, }, stopReason = 'toolUse', },
      { content = { { type = 'toolCall', id = 'follow-live', name = 'read', arguments = { path = file, }, }, }, stopReason = 'toolUse', },
      { content = { { type = 'toolCall', id = 'failed-live', name = 'read', arguments = { path = directory .. '/absent.txt', }, }, }, stopReason = 'toolUse', },
      { content = 'findings finished', },
    }
    finish( function() vim.cmd 'Pi ask report findings' end )
    assert( vim.fn.getqflist()[1].text == 'finding' )
    assert( pivi.follow.window() and pivi.follow.window() ~= working )
    assert( vim.api.nvim_get_current_win() == working )
    local failed
    for _, item in ipairs( pivi.entry.list( current.buffer ) ) do
      if item.meta.call == 'failed-live' then failed = item end
    end
    assert( failed and failed.meta.failed and failed.first == failed.last, vim.inspect( failed ) )
    vim.cmd 'Pi follow'
  end )
  scenario( 'native entry motions/detail/location: real restored-capable tool surfaces preserve source', function()
    local edited
    for _, item in ipairs( pivi.entry.list( current.buffer ) ) do
      if item.meta.call == 'edit-live' then edited = item end
    end
    local win = pivi.transcript.show( current.buffer )
    vim.api.nvim_set_current_win( win )
    vim.api.nvim_win_set_cursor( win, { edited.first, 0, } )
    local invoke = function( intent )
      for _, map in ipairs( vim.api.nvim_buf_get_keymap( current.buffer, 'n' ) ) do
        if map.lhs == intent then map.callback(); return end
      end
      error( 'missing intent ' .. intent )
    end
    invoke '<Plug>(pivi-next-tool)'
    assert( vim.api.nvim_win_get_cursor( win )[1] > edited.first )
    invoke '<Plug>(pivi-previous-tool)'
    assert( vim.api.nvim_win_get_cursor( win )[1] == edited.first )
    invoke '<Plug>(pivi-inspect-entry)'
    local detail
    for _, candidate in ipairs( vim.api.nvim_list_wins() ) do
      if vim.api.nvim_win_get_config( candidate ).relative ~= '' then detail = candidate end
    end
    assert( detail and vim.api.nvim_buf_get_name( vim.api.nvim_win_get_buf( detail ) ):find( '/detail', 1, true ) )
    vim.api.nvim_win_close( detail, true )
    invoke '<Plug>(pivi-goto-location)'
    assert( pivi.follow.window() ~= working )
    assert( vim.api.nvim_win_get_cursor( pivi.follow.window() )[1] == edited.meta.result.details.firstChangedLine )
    assert( vim.api.nvim_win_get_buf( working ) == source )
    pivi.follow.reset()
  end )
  scenario( 'selection: unsaved queued source/revision retained through mutation and delivery', function()
    vim.api.nvim_set_current_win( working )
    vim.api.nvim_buf_set_lines( source, 0, 1, false, { 'local selected = "unsaved original"', } )
    responses { { content = 'selection held', hold = true, }, { content = 'selection delivered', }, }
    vim.cmd '1,1Pi ask selection snapshot'
    await( function() return current.status.run == 'running' end )
    local shot = current.submissions[#current.submissions].snapshot
    assert( shot.selected_text == 'local selected = "unsaved original"' and shot.modified )
    local revision = shot.revision
    current.submit( 'queued selection', { delivery = 'follow_up', source = {
      bufnr = source, win = working, range = { kind = 'range', first = { 1, 0, }, last = { 1, 0, }, },
    }, } )
    await( function() return #current.status.queue.follow_up == 1 end )
    local queued = current.submissions[#current.submissions].snapshot
    vim.api.nvim_buf_set_lines( source, 0, 1, false, { 'local selected = "later mutation"', } )
    assert( queued.revision == revision and queued.selected_text == shot.selected_text )
    control 'e2e-release'
    await( function() return current.status.run == 'idle' end )
    local persisted = vim.json.encode( request { type = 'get_messages', } )
    assert( persisted:find( 'unsaved original', 1, true ) and not persisted:find( 'later mutation', 1, true ) )
    vim.api.nvim_buf_set_lines( source, 0, -1, false, { 'local value = 7', 'return value', } )
  end )
  scenario( 'loclist/diagnostics/plugin docs: real companion tools preserve unrelated namespaces', function()
    local foreign = vim.api.nvim_create_namespace( 'e2e-foreign-diagnostics' )
    vim.diagnostic.set( foreign, source, { { lnum = 0, col = 0, message = 'foreign', }, } )
    local tags = vim.fn.getcompletion( 'plenary', 'help' )
    assert( #tags > 0, 'installed plenary docs unavailable' )
    responses {
      { content = { { type = 'toolCall', id = 'native-lists', name = 'nvim_lua', arguments = { code =
        'local a = require("bugabinga.pivi.activity"); a.window_findings(' .. working .. ', {{buffer=' .. source ..
        ',line=2,text="local finding"}}); a.diagnose(' .. source .. ', {{line=2,text="agent finding"}}); return true',
      }, }, }, stopReason = 'toolUse', },
      { content = { { type = 'toolCall', id = 'plugin-doc', name = 'nvim_help', arguments = { tag = tags[1], }, }, },
        stopReason = 'toolUse', },
      { content = 'native lists and plugin docs finished', },
    }
    finish( function() vim.cmd 'Pi ask native lists and plugin documentation' end )
    assert( vim.fn.getloclist( working )[1].text == 'local finding' )
    assert( pivi.activity.diagnostics( source )[1].source == 'pi' )
    assert( vim.diagnostic.get( source, { namespace = foreign, } )[1].message == 'foreign' )
    vim.api.nvim_win_call( working, function() vim.cmd 'lfirst' end )
    assert( vim.api.nvim_win_get_cursor( working )[1] == 2 )
    local docs
    for _, item in ipairs( pivi.entry.list( current.buffer ) ) do if item.meta.call == 'plugin-doc' then docs = item end end
    assert( docs and not docs.meta.failed and pivi.tool.produced( docs.meta ):find( 'plenary', 1, true ) )
    vim.diagnostic.reset( foreign, source )
    pivi.activity.clear_diagnostics( source )
  end )
  scenario( 'help payload: originating visible region/layout/selection/messages/error bounded on real wire', function()
    vim.api.nvim_set_current_win( working )
    local lines = { 'visible-origin-sentinel', }
    for index = 2, 600 do lines[index] = 'hidden-line-' .. index end
    lines[600] = 'hidden-tail-sentinel'
    vim.api.nvim_buf_set_lines( source, 0, -1, false, lines )
    vim.api.nvim_win_set_cursor( working, { 1, 0, } )
    vim.api.nvim_win_call( working, function() vim.cmd 'normal! zt' end )
    vim.fn.setpos( "'<", { source, 1, 1, 0, } )
    vim.fn.setpos( "'>", { source, 1, 5, 0, } )
    vim.api.nvim_echo( { { 'recent-message-sentinel', }, }, true, {} )
    vim.v.errmsg = 'last-error-sentinel'
    local run = pivi.help.ask( 'bounded live help' )
    local message = run.client.sent[1].message
    assert( message:find( 'visible-origin-sentinel', 1, true ) )
    assert( not message:find( 'hidden-tail-sentinel', 1, true ) )
    assert( message:find( 'selection', 1, true ) and message:find( 'windows:', 1, true ) )
    assert( message:find( 'recent-message-sentinel', 1, true ) and message:find( 'last-error-sentinel', 1, true ) )
    assert( #message < 20000 )
    await( function() return run.done and not run.client.is_running() end )
    vim.v.errmsg = ''
    vim.api.nvim_buf_set_mark( source, '<', 0, 0, {} )
    vim.api.nvim_buf_set_mark( source, '>', 0, 0, {} )
    vim.api.nvim_buf_set_lines( source, 0, -1, false, { 'local value = 7', 'return value', } )
  end )
  scenario( 'search: real upstream grep grouped decoration, authored output, native quickfix', function()
    local second = directory .. '/second.lua'
    vim.fn.writefile( { 'local value = 2', 'return value', }, second )
    responses {
      { content = { { type = 'toolCall', id = 'grep-live', name = 'grep', arguments = {
        pattern = 'value', path = directory, glob = '*.lua', literal = true,
      }, }, }, stopReason = 'toolUse', },
      { content = 'search finished', },
    }
    finish( function() vim.cmd 'Pi ask search values' end )
    local searched
    for _, item in ipairs( pivi.entry.list( current.buffer ) ) do if item.meta.call == 'grep-live' then searched = item end end
    assert( searched and not searched.meta.failed )
    assert( #pivi.tool.matches( searched.meta ) >= 4 )
    assert( #vim.fn.getqflist() >= 4 )
    local headings = 0
    for _, mark in ipairs( pivi.render.marks( current.buffer ) ) do
      if mark[2] >= searched.first and mark[2] < searched.last and mark[4].virt_lines then headings = headings + 1 end
    end
    assert( headings >= 2, 'missing file groups' )
    vim.api.nvim_win_call( working, function() vim.cmd 'cfirst' end )
    assert( vim.api.nvim_win_get_cursor( working )[1] >= 1 )
    vim.api.nvim_win_set_buf( working, source )
  end )
  scenario( 'bash: native RPC progressive deltas, truncation/full-output navigation without model', function()
    local release = directory .. '/shell-release'
    local script = directory .. '/shell.cjs'
    vim.fn.writefile( {
      'const fs = require("node:fs"); console.log("shell-first-sentinel");',
      'function poll() { if (!fs.existsSync(process.argv[2])) return setImmediate(poll);',
      'for (let i = 0; i < 3000; i++) console.log("line-" + i + " " + "x".repeat(80)); } poll();',
    }, script )
    local ended, answer = false, nil
    assert( current.bash( 'node "' .. vim.fs.normalize( script ) .. '" "' .. vim.fs.normalize( release ) .. '"', true,
      function( response ) ended, answer = true, response end ) )
    local streamed = vim.wait( 20000, function() return ended or text():find( 'shell-first-sentinel', 1, true ) ~= nil end, 10 )
    vim.fn.writefile( { 'release', }, release )
    assert( streamed and not ended, vim.inspect( answer or current.client.failures() ) )
    vim.fn.writefile( { 'release', }, release )
    await( function() return ended end )
    local items = pivi.entry.list( current.buffer )
    local shell = items[#items]
    assert( not shell.meta.failed, vim.inspect( shell.meta ) )
    assert( pivi.tool.truncated( shell.meta ) )
    local full = pivi.tool.full_output( shell.meta )
    assert( full and vim.fn.filereadable( full ) == 1 )
    assert( #vim.fn.readfile( full ) >= 3001 )
    local win = pivi.transcript.show( current.buffer )
    vim.api.nvim_set_current_win( win )
    vim.api.nvim_win_set_cursor( win, { shell.first, 0, } )
    pivi.navigate.locate( current.buffer )
    assert( vim.fs.normalize( vim.api.nvim_buf_get_name( vim.api.nvim_win_get_buf( pivi.follow.window() ) ) ) == vim.fs.normalize( full ) )
    pivi.follow.reset()
    vim.fn.delete( full )
  end )
  scenario( 'model/thinking/name/stats: native RPC commands without global persistence', function()
    vim.cmd 'Pi model pivi-e2e/faux-1'
    vim.cmd 'Pi thinking off'
    vim.cmd 'Pi name persisted task'
    vim.cmd 'Pi session'
    await( function()
      local data = request { type = 'get_state', }
      return data.sessionName == 'persisted task'
    end )
    assert( request( { type = 'get_session_stats', } ).cost == 0 )
  end )
  local identity, history, old_client
  scenario( 'restart: real child replaced, identity/history/place/draft retained, prompt rebound', function()
    identity = request( { type = 'get_state', } ).sessionId
    history = request( { type = 'get_entries', } ).entries
    old_client = current.client
    local transcript = current.buffer
    local win = vim.fn.bufwinid( transcript )
    vim.api.nvim_win_set_cursor( win, { 2, 0, } )
    vim.cmd 'Pi restart'
    await( function()
      pivi = require 'bugabinga.pivi'
      local resumed = pivi.session.active()
      if resumed and resumed ~= current and resumed.restored then current = resumed; return true end
      return false
    end, 'restart restoration deadline' )
    assert( not old_client.is_running(), 'old Pi child remains alive' )
    assert( current.buffer == transcript )
    assert( request( { type = 'get_state', } ).sessionId == identity )
    assert( #request( { type = 'get_entries', } ).entries == #history )
    assert( vim.api.nvim_win_get_cursor( win )[1] == 2 )
    assert( call_count() == 0, 'restart called model' )
    local tool_entries = vim.tbl_filter( function( item ) return item.kind == 'tool' end, pivi.entry.list( current.buffer ) )
    assert( #tool_entries >= 3, 'tool history missing' )
    assert( vim.iter( tool_entries ):any( function( item )
      return item.meta.call == 'edit-live' and item.meta.result.details and item.meta.result.details.diff ~= nil
    end ), 'recorded edit details lost during restoration' )
    vim.cmd 'Pi prompt'
    local prompt = vim.api.nvim_get_current_buf()
    assert( vim.api.nvim_get_current_line() == 'saved draft' )
    responses { { content = 'rebound answer', }, }
    vim.api.nvim_buf_set_lines( prompt, 0, -1, false, { 'after restart', } )
    finish( function()
      for _, map in ipairs( vim.api.nvim_buf_get_keymap( prompt, 'n' ) ) do
        if map.lhs == '<Plug>(pivi-submit)' then map.callback(); return end
      end
    end )
    assert( text():find( 'rebound answer', 1, true ) )
  end )
  scenario( 'sessions: create/switch active, source buffer independent, stop explicit', function()
    vim.cmd 'Pi start beta'
    local beta = pivi.session.active()
    request( { type = 'get_state', }, beta )
    assert( beta.name == 'beta' )
    local original = vim.ui.select
    vim.ui.select = function( items, _, callback ) callback( 'alpha' ) end
    vim.cmd 'Pi sessions'
    vim.ui.select = original
    assert( pivi.session.active().name == 'alpha' )
    local alpha_id = request( { type = 'get_state', } ).sessionId
    local focused = vim.api.nvim_get_current_win()
    vim.cmd 'Pi restart'
    await( function()
      pivi = require 'bugabinga.pivi'
      local alpha, other = pivi.session.get( 'alpha' ), pivi.session.get( 'beta' )
      if alpha and other and alpha.restored and other.restored then
        current, beta = alpha, other
        return true
      end
      return false
    end )
    assert( pivi.session.active().name == 'alpha' )
    assert( vim.api.nvim_get_current_win() == focused )
    assert( request( { type = 'get_state', } ).sessionId == alpha_id )
    stop( beta )
    assert( current.client.is_running() )
  end )
  scenario( 'resume latest: persistent identity/history, no new conversation/model call', function()
    identity = request( { type = 'get_state', } ).sessionId
    stop( current )
    vim.cmd 'Pi resume'
    current = pivi.session.active()
    await( function() return current.restored end )
    assert( request( { type = 'get_state', } ).sessionId == identity )
    assert( text():find( 'rebound answer', 1, true ) )
    assert( call_count() == 0 )
  end )
  scenario( 'resume identity: editor completion reads real Pi store, history no model call', function()
    stop( current )
    local stored = pivi.store.sessions( directory )
    assert( #stored > 0 )
    assert( vim.list_contains( vim.fn.getcompletion( 'Pi resume ', 'cmdline' ), identity ) )
    vim.cmd( 'Pi resume ' .. identity )
    current = pivi.session.active()
    await( function() return current.restored end )
    assert( request( { type = 'get_state', } ).sessionId == identity )
    assert( call_count() == 0 )
  end )
  scenario( 'prior picker: choose real persisted identity, no model call', function()
    stop( current )
    local original = vim.ui.select
    local picked
    vim.ui.select = function( items, options, callback )
      assert( options.prompt == 'pi prior session' )
      for _, item in ipairs( items ) do if item.id == identity then picked = item; callback( item ); return end end
      error 'saved identity absent from picker'
    end
    local ok, failure = pcall( vim.cmd, 'Pi prior' )
    vim.ui.select = original
    assert( ok, failure )
    assert( picked )
    current = pivi.session.active()
    await( function() return current.restored end )
    assert( request( { type = 'get_state', } ).sessionId == identity and call_count() == 0 )
  end )
  scenario( 'descriptor missing identity: quiet no restoration, no fallback child', function()
    local before = #_G.PIVI_E2E_CHILDREN
    assert( #pivi.restore( { { name = 'absent', session = 'missing-id', cwd = directory, }, } ) == 0 )
    assert( #pivi.restore( nil ) == 0 and #_G.PIVI_E2E_CHILDREN == before )
    assert( pivi.session.get( 'absent' ) == nil )
  end )
  scenario( 'descriptor: idempotent native-session consumer restore', function()
    local descriptor = pivi.descriptor( directory )
    assert( #descriptor == 1 and descriptor[1].session == identity )
    assert( #pivi.restore( descriptor ) == 0 )
    stop( current )
    assert( #pivi.restore( descriptor ) == 1 )
    current = pivi.session.active()
    await( function() return current.restored end )
    assert( request( { type = 'get_state', } ).sessionId == identity )
  end )
  scenario( 'branched restore: recorded native tree selects active ancestry and tool details only', function()
    local state = request { type = 'get_state', }
    local records = vim.fn.readfile( state.sessionFile )
    local header = vim.json.decode( records[1] )
    header.id = 'e2e-branched-history'
    local output = { vim.json.encode( header ), }
    local originals = {}
    for index = 2, #records do originals[#originals + 1] = vim.json.decode( records[index] ) end
    local previous = vim.NIL
    local append = function( original )
      local record = vim.deepcopy( original )
      record.id = string.format( '%08x', #output )
      record.parentId = previous
      previous = record.id
      output[#output + 1] = vim.json.encode( record )
    end
    local naming
    for _, record in ipairs( originals ) do if record.type == 'session_info' and record.name then naming = record end end
    assert( naming, 'native recorded session name required' )
    append( naming )
    local shared = previous
    for _, original in ipairs( originals ) do
      local record = vim.deepcopy( original )
      if record.type == 'message' then
        if record.message.role == 'user' then record.message.content = 'DISCARDED_BRANCH_MARKER' end
        if record.message.role == 'toolResult' then
          record.message.content = { { type = 'text', text = 'DISCARDED_TOOL_MARKER', }, }
          record.message.details = { diff = 'DISCARDED_DIFF_MARKER', }
        end
      end
      append( record )
    end
    previous = shared
    for _, record in ipairs( originals ) do append( record ) end
    local path = directory .. '/branched.jsonl'
    vim.fn.writefile( output, path )
    local live = pivi.session.create { name = 'branched', resume = path, }
    await( function() return live.restored end )
    local data = request( { type = 'get_entries', }, live )
    assert( #data.entries == #output - 1 and data.leafId == previous )
    local expected = require( 'bugabinga.pivi.backfill' ).plan( data )
    local actual = pivi.entry.list( live.buffer )
    assert( #actual == #expected and #actual > 0 )
    local rendered = table.concat( vim.api.nvim_buf_get_lines( live.buffer, 0, -1, false ), '\n' )
    assert( not rendered:find( 'DISCARDED_', 1, true ), 'discarded branch rendered' )
    local paired = false
    for _, item in ipairs( actual ) do
      if item.kind == 'tool' and item.meta.call == 'edit-live' then
        assert( item.meta.result.details.diff and item.meta.result.details.diff ~= 'DISCARDED_DIFF_MARKER' )
        paired = true
      end
    end
    assert( paired, 'selected native tool details missing' )
    request( { type = 'prompt', message = '/e2e-count', }, live )
    request( { type = 'get_state', }, live )
    local counted
    for _, event in ipairs( live.client.events ) do
      if event.type == 'extension_ui_request' and event.statusKey == 'e2e-count' then counted = event.statusText end
    end
    assert( tonumber( counted ) == 0, 'branched restoration called model' )
    stop( live )
    pivi.session.set_active( current.name )
  end )
  scenario( 'large restore: 2000 real message copies, main-loop yields, addressable history, no model', function()
    local state = request { type = 'get_state', }
    local records = vim.fn.readfile( state.sessionFile )
    local header = vim.json.decode( records[1] )
    header.id = 'e2e-large-history'
    local prompt, answer, model, naming
    for _, line in ipairs( records ) do
      local record = vim.json.decode( line )
      if record.type == 'session_info' and record.name then naming = record end
      if record.type == 'model_change' then model = record end
      if record.type == 'message' and record.message.role == 'user' then prompt = record end
      if record.type == 'message' and record.message.role == 'assistant' and record.message.stopReason == 'stop' then answer = record end
    end
    assert( prompt and answer )
    local output = { vim.json.encode( header ), }
    local previous = vim.NIL
    local append = function( original )
      local record = vim.deepcopy( original )
      record.id = string.format( '%08x', #output )
      record.parentId = previous
      previous = record.id
      table.insert( output, vim.json.encode( record ) )
    end
    assert( naming, 'recorded session name required to avoid user auto-naming model calls' )
    append( naming )
    if model then append( model ) end
    for _ = 1, 1000 do append( prompt ); append( answer ) end
    local path = directory .. '/large.jsonl'
    vim.fn.writefile( output, path )
    local live, timer
    local ok, failure = xpcall( function()
      live = pivi.session.create { name = 'large', resume = path, }
      local yields = 0
      local rejected_new = false
      timer = assert( vim.uv.new_timer() )
      timer:start( 1, 1, vim.schedule_wrap( function()
        if #pivi.entry.list( live.buffer ) > 0 and not live.restored then
          yields = yields + 1
          if not rejected_new then
            pivi.session.set_active( live.name )
            local before = #live.client.sent
            vim.cmd 'Pi new'
            assert( #live.client.sent == before, 'new_session sent during yielded history restoration' )
            rejected_new = true
          end
        end
      end ) )
      local started = vim.uv.hrtime()
      local completed = vim.wait( 120000, function() return live.restored end, 10 )
      timer:stop()
      timer:close()
      assert( completed, 'large history restoration deadline' )
      local elapsed = ( vim.uv.hrtime() - started ) / 1e6
      assert( yields > 1, 'restoration blocked main loop' )
      assert( rejected_new, 'new during yielded restoration never exercised' )
      assert( #pivi.entry.list( live.buffer ) == 2000 )
      local restored = request( { type = 'get_entries', }, live ).entries
      assert( #restored >= #output - 1 )
      assert( #vim.tbl_filter( function( item )
        return item.type == 'message' and ( item.message.role == 'user' or item.message.role == 'assistant' )
      end, restored ) == 2000 )
      request( { type = 'prompt', message = '/e2e-count', }, live )
      request( { type = 'get_state', }, live )
      local counted
      for _, event in ipairs( live.client.events ) do
        if event.type == 'extension_ui_request' and event.statusKey == 'e2e-count' then counted = event.statusText end
      end
      assert( tonumber( counted ) == 0, 'restoration model calls: ' .. tostring( counted ) )
      assert( elapsed < 10000, 'large restoration exceeded 10s budget: ' .. elapsed .. 'ms' )
      local saved = pivi.descriptor( directory )
      local descriptor
      for _, item in ipairs( saved ) do if item.name == 'large' then descriptor = item end end
      assert( descriptor and vim.fs.normalize( descriptor.file ) == vim.fs.normalize( path ), vim.inspect( descriptor ) )
      vim.env.PI_CODING_AGENT_SESSION_DIR = nil
      vim.cmd 'Pi restart'
      await( function()
        pivi = require 'bugabinga.pivi'
        local resumed, other = pivi.session.get( 'large' ), pivi.session.get( current.name )
        if resumed and other and resumed.restored and other.restored then
          live, current = resumed, other
          return true
        end
        return false
      end, 'arbitrary file restart deadline' )
      assert( request( { type = 'get_state', }, live ).sessionId == header.id )
      assert( #request( { type = 'get_entries', }, live ).entries == #restored )
      assert( #pivi.entry.list( live.buffer ) == 2000 )
    end, debug.traceback )
    if timer and not timer:is_closing() then timer:stop(); timer:close() end
    pivi = require 'bugabinga.pivi'
    live = pivi.session.get( 'large' ) or live
    local cleaned, cleanup_error = pcall( function() if live then stop( live ) end end )
    current = pivi.session.get( current.name ) or current
    pivi.session.set_active( current.name )
    if vim.api.nvim_win_is_valid( working ) then vim.api.nvim_set_current_win( working ) end
    assert( cleaned, cleanup_error )
    assert( ok, failure )
  end )
  scenario( 'new/export: fresh history in same process, HTML export temp only', function()
    local client = current.client
    vim.cmd 'Pi new'
    await( function() return #request( { type = 'get_messages', } ).messages == 0 end )
    assert( current.client == client )
    responses { { content = 'fresh answer', }, }
    finish( function() vim.cmd 'Pi ask fresh question' end )
    assert( not text():find( 'rebound answer', 1, true ) )
    local output = directory .. '/export.html'
    vim.cmd( 'Pi export ' .. output )
    await( function() return vim.fn.filereadable( output ) == 1 end )
  end )
  scenario( 'stop: owned Pi child exits, published state removed', function()
    if vim.env.PIVI_E2E_INJECT_HANDLER_FAILURE == '1' then
      local handled = false
      current.request( { type = 'get_state', }, function()
        handled = true
        error 'injected owned RPC handler failure'
      end )
      await( function() return handled end )
      request { type = 'get_state', }
    end
    vim.cmd 'Pi stop'
    await( function() return not current.client.is_running() end )
    assert( #pivi.session.list() == 0 )
    assert( pivi.summary() == '' )
  end )
  return results
end