Problem
Junior's dashboard currently reads turn-session checkpoints as if they were the user-facing transcript. That leaks an internal recovery shape into the UI and makes turns look wrong:
- a single turn can appear to contain prior conversation history
- checkpointed runtime context such as
<runtime-turn-context> appears as normal user transcript text
- active turns can look empty/expired until a checkpoint is materialized
- checkpoint snapshots are doing double duty as resume state and dashboard history
The result is confusing: operators expect conversations to be ordered collections of turns, and each turn to show only what happened during that turn.
Current State
Junior already has the better primitive under the checkpoint metadata:
- Pi session messages are persisted incrementally in
pi-session-message-store
- turn-session checkpoint metadata stores
messageCount, state, checkpoint version, slice id, resume reason, etc.
getAgentTurnSessionCheckpoint materializes a checkpoint by loading the Pi message prefix through that cursor
The dashboard problem is that reporting consumes the materialized checkpoint as the visible transcript.
Recommended Direction
Make the append-only Pi/session message log the canonical record for dashboard transcript/history, and keep checkpoints as small resume metadata only.
The model should be:
conversation: metadata and ordered turn ids
turn transcript/event log: append-only records for user messages, assistant messages, tool calls/results, usage, timestamps, and partial/live progress
checkpoint: cursor/lease metadata for resumability, not a visible transcript
Checkpoint metadata should remain useful for:
- active/idle state
- stale callback rejection via
checkpointVersion
- timeout/auth resume
- slice tracking
- last safe committed Pi message cursor
- dynamic restore metadata such as loaded skill names
But dashboard transcript rendering should read the transcript/event log, not checkpoint recovery state.
Prior Art
Pi coding-agent documents sessions as append-only JSONL conversation trees with message/custom/compaction entries, and builds context by walking the current branch:
Codex resumes sessions by replaying persisted thread/session events and restoring state from history rather than treating a checkpoint blob as the UI transcript:
Both point toward an append-only event/history log as the source of truth, with resumability metadata layered on top.
Proposed Work
- Define a turn transcript/event schema for dashboard consumption.
- Append transcript events in real time during a turn:
- turn started
- user prompt received
- assistant message started
- text deltas or committed assistant message
- tool call started
- tool result committed
- usage/timing updates
- turn paused/resumed/failed/ended
- Store enough ordering data to render conversations oldest-to-newest and turns in exact event order.
- Use the last contiguous valid event/message prefix for resume and UI repair when the tail is incomplete.
- Keep checkpoint metadata as a cursor/version over committed Pi/session state.
- Update dashboard reporting to read transcript/event logs and use checkpoint state only for active/idle/resume metadata.
- Hide or separately label runtime context blocks so internal injected context does not render as normal user-authored transcript text.
Acceptance Criteria
- Conversation detail renders turns oldest-to-newest.
- A turn transcript contains only that turn's events/messages.
- Active turns update before completion without relying on completed checkpoints.
- Runtime context is not displayed as ordinary user transcript text.
- Checkpoint metadata remains sufficient to resume timeout/auth-paused turns.
- Dashboard APIs do not expose raw checkpoint payloads as transcript.
Problem
Junior's dashboard currently reads turn-session checkpoints as if they were the user-facing transcript. That leaks an internal recovery shape into the UI and makes turns look wrong:
<runtime-turn-context>appears as normal user transcript textThe result is confusing: operators expect conversations to be ordered collections of turns, and each turn to show only what happened during that turn.
Current State
Junior already has the better primitive under the checkpoint metadata:
pi-session-message-storemessageCount, state, checkpoint version, slice id, resume reason, etc.getAgentTurnSessionCheckpointmaterializes a checkpoint by loading the Pi message prefix through that cursorThe dashboard problem is that reporting consumes the materialized checkpoint as the visible transcript.
Recommended Direction
Make the append-only Pi/session message log the canonical record for dashboard transcript/history, and keep checkpoints as small resume metadata only.
The model should be:
conversation: metadata and ordered turn idsturn transcript/event log: append-only records for user messages, assistant messages, tool calls/results, usage, timestamps, and partial/live progresscheckpoint: cursor/lease metadata for resumability, not a visible transcriptCheckpoint metadata should remain useful for:
checkpointVersionBut dashboard transcript rendering should read the transcript/event log, not checkpoint recovery state.
Prior Art
Pi coding-agent documents sessions as append-only JSONL conversation trees with message/custom/compaction entries, and builds context by walking the current branch:
Codex resumes sessions by replaying persisted thread/session events and restoring state from history rather than treating a checkpoint blob as the UI transcript:
Both point toward an append-only event/history log as the source of truth, with resumability metadata layered on top.
Proposed Work
Acceptance Criteria