id: NVIM-PLAN-LAJ8PGRK
type: plan
title: Deliver pivi transcript presentation and navigation
spec: NVIM-SPEC-GMWDCMJZ
status: approved
depends_on:
NVIM-PLAN-CEJPO_XF
Deliver pivi transcript presentation and navigation
Intent
Deliver the transcript presentation and navigation behavior of
NVIM-SPEC-GMWDCMJZ.
NVIM-PLAN-CEJPO_XF delivered a working transcript that appends plain text and mixes pivi's own framing into the content.
This plan makes the transcript the conversation document it already is: addressable entries, message text as authored, rendered formatting, and ordinary motion.
Placement continues from the earlier plan: lua/bugabinga/pivi/, with module-level testing rules in that directory's AGENTS.md.
Measured constraint, not assumption: a transcript that reparses on every streamed delta costs roughly five hundred times a transcript that lets redraw drive parsing.
Presentation must therefore never force parsing per delta.
Buffer-local mappings in pivi buffers plus <Plug> mappings; no global keys
---
id: NVIM-PLAN-LAJ8PGRK
type: plan
title: Deliver pivi transcript presentation and navigation
spec: NVIM-SPEC-GMWDCMJZ
status: approved
depends_on:
- NVIM-PLAN-CEJPO_XF
---
# Deliver pivi transcript presentation and navigation
## Intent
Deliver the transcript presentation and navigation behavior of
[NVIM-SPEC-GMWDCMJZ](../../specs/NVIM-SPEC-GMWDCMJZ-pivi-native-neovim-integration/index.md).
[NVIM-PLAN-CEJPO_XF](../NVIM-PLAN-CEJPO_XF-deliver-pivi-native-neovim-integration/index.md) delivered a working transcript that appends plain text and mixes pivi's own framing into the content.
This plan makes the transcript the conversation document it already is: addressable entries, message text as authored, rendered formatting, and ordinary motion.
Placement continues from the earlier plan: `lua/bugabinga/pivi/`, with module-level testing rules in that directory's `AGENTS.md`.
Measured constraint, not assumption: a transcript that reparses on every streamed delta costs roughly five hundred times a transcript that lets redraw drive parsing.
Presentation must therefore never force parsing per delta.
## Module map
```mermaid
flowchart TB
session["bugabinga.pivi.session"] --> entry["bugabinga.pivi.entry"]
entry --> transcript["bugabinga.pivi.transcript"]
transcript --> render["bugabinga.pivi.render"]
entry --> fold["bugabinga.pivi.fold"]
entry --> navigate["bugabinga.pivi.navigate"]
navigate --> follow["bugabinga.pivi.follow"]
navigate --> detail["bugabinga.pivi.detail"]
command["bugabinga.pivi.command"] --> navigate
command --> fold
```
| Module | Responsibility |
| --- | --- |
| `bugabinga.pivi.entry` | Entry identity, kind, bounds, metadata, and lifecycle over a transcript buffer |
| `bugabinga.pivi.transcript` | Buffer ownership; message text only, with framing delegated to presentation |
| `bugabinga.pivi.render` | Formatting, embedded language presentation, and framing decoration |
| `bugabinga.pivi.fold` | Collapsed and expanded presentation of an entry |
| `bugabinga.pivi.navigate` | Motion between entries and tool calls, and reaching a referenced location |
| `bugabinga.pivi.detail` | Detailed inspection of one entry without leaving the transcript |
| `bugabinga.pivi.session` | Emits entries instead of writing framing text |
| `bugabinga.pivi.command` | `:Pi` subcommands and `<Plug>` mappings for the new behavior |
## Slice order
```mermaid
flowchart LR
s1["1 Addressable entries"] --> s2["2 Pure message text"]
s1 --> s3["3 Collapse and expand"]
s2 --> s4["4 Rendered formatting"]
s1 --> s5["5 Motion between entries"]
s5 --> s6["6 Reach a referenced location"]
s3 --> s7["7 Detailed inspection"]
```
| # | Observable behavior | Modules touched | Verified by |
| --- | --- | --- | --- |
| 1 | Every appended submission, answer, tool call, result, and error is an addressable entry with stable bounds that survive later appends | `bugabinga.pivi.entry`, `bugabinga.pivi.transcript`, `bugabinga.pivi.session` | Entry bounds spec under streaming appends and buffer growth |
| 2 | Transcript text is the message as authored; copying an answer yields the original text without framing | `bugabinga.pivi.transcript`, `bugabinga.pivi.render`, `bugabinga.pivi.session` | Copy spec comparing buffer text with recorded message content |
| 3 | A tool result arrives collapsed to one readable line and expands on request; entry state never alters session history | `bugabinga.pivi.fold`, `bugabinga.pivi.entry`, `bugabinga.pivi.command` | Collapse spec asserting unchanged session entries and message state |
| 4 | Message formatting renders rather than showing markup, embedded code appears in its own language, and unknown formats stay plain | `bugabinga.pivi.render` | Rendering spec plus a streaming budget assertion over a recorded answer |
| 5 | Ordinary motions move between entries and between tool calls in both directions | `bugabinga.pivi.navigate`, `bugabinga.pivi.command` | Motion spec over a replayed recorded run |
| 6 | An entry that names a location navigates there, reusing the existing follow surfaces | `bugabinga.pivi.navigate`, `bugabinga.pivi.follow` | Navigation spec asserting the working window is preserved |
| 7 | An entry can be inspected in full, including tool arguments and results, without leaving the transcript | `bugabinga.pivi.detail`, `bugabinga.pivi.entry` | Inspection spec asserting the transcript window and cursor are unchanged |
Slice 1 is the walking skeleton; every later slice keeps the whole suite green before the next begins.
## Critical paths
- Entry bounds must be extmark-backed, because text appended later shifts every fixed line number.
- Presentation is driven by redraw, never by a forced parse per streamed delta.
- Framing lives in decoration, so the buffer stays copyable message text.
- Collapsing is presentation only; the session transcript and Pi history are untouched.
- Location navigation reuses follow-mode surfaces rather than introducing a second window policy.
## Risks
| Risk | Slice | Mitigation |
| --- | --- | --- |
| Entry bounds drift as the transcript grows | 1 | Extmark-backed ranges, asserted after appends and after edits to earlier entries |
| Moving framing out of the text loses scannability | 2 | Replace it with decoration in the same slice, and assert both text purity and visible framing |
| Rendering cost grows with a long answer | 4 | Assert a streaming budget over a recorded answer; keep parsing redraw-driven |
| Collapsed entries hide errors the user needs | 3 | Collapse voluminous results only; keep errors expanded |
| New mappings collide with user keys | 3, 5, 7 | Buffer-local mappings in pivi buffers plus `<Plug>` mappings; no global keys |