TermAl

Feature Brief: Conversation Markers

Status

In progress. The current tree has the shared marker data model, session persistence, marker CRUD routes, marker SSE deltas, frontend API helpers, live delta application, overview-rail marker pins, transcript chips, marker list/previous-next navigation, and a quick checkpoint action on transcript messages. Full marker edit/delete UI and agent workflow actions are still pending.

Conversation markers are durable, user-visible anchors inside an agent conversation. They let the user mark important points in a long transcript, name and color those points, jump between them, and optionally ask agents to use them as context boundaries.

Related feature: Conversation Overview Map.

Problem

TermAl conversations can become long quickly: streamed reasoning, tool output, diffs, code review notes, approvals, retries, and parallel-agent summaries can all land in the same transcript. The existing transcript is chronological, but it has no first-class way to preserve the user’s mental landmarks.

Common user needs:

Today the fallback is plain text in the conversation or an external note. Both are lossy: they are hard to navigate, not typed, not stable under transcript virtualization, and not available to future UI workflows.

Goals

Non-goals for v1

Core Model

A marker is a durable annotation attached to a session and anchored to one or more messages.

type ConversationMarkerKind =
  | "checkpoint"
  | "decision"
  | "review"
  | "bug"
  | "question"
  | "handoff"
  | "custom";

type ConversationMarker = {
  id: string;
  sessionId: string;
  kind: ConversationMarkerKind;
  name: string;
  body?: string | null;
  color: string;
  messageId: string;
  messageIndexHint: number;
  endMessageId?: string | null;
  endMessageIndexHint?: number | null;
  createdAt: string;
  updatedAt: string;
  createdBy: "user" | "agent" | "system";
};

Rules:

Marker Types

V1 should ship with a small fixed set:

Type-specific behavior should stay minimal in v1. The type mostly controls icon, default color, default name suggestion, and filter grouping. The user can override marker color without changing the marker kind.

User Experience

Creating a marker

Entry points:

Creation form:

Default name/color suggestions:

Rendering in the transcript

Markers should be visible but not noisy.

Recommended v1 rendering:

Actions:

Marker list

Add a compact floating marker window inside the session pane. It should stay within the tab pane view, not portal to global app chrome.

List item fields:

List behavior:

Marker-to-marker navigation

The conversation toolbar should expose marker navigation independent of search.

Controls:

Ordering:

Behavior:

Jumping to a marker must work with transcript virtualization.

Flow:

  1. Resolve marker anchor by messageId.
  2. If the message is currently loaded, scroll to it and highlight it.
  3. If the transcript is summary-only or not loaded, hydrate the session first.
  4. If the message is outside the mounted virtualized range, ask the virtualizer to scroll to the message index.
  5. If the message cannot be found, show unresolved marker state.

Backend Storage

Markers should persist with the session state in the same local persistence domain as sessions.

Suggested Rust shape:

#[derive(Clone, Debug, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
struct ConversationMarker {
    id: String,
    session_id: String,
    kind: ConversationMarkerKind,
    name: String,
    body: Option<String>,
    color: String,
    message_id: String,
    message_index_hint: usize,
    end_message_id: Option<String>,
    end_message_index_hint: Option<usize>,
    created_at: String,
    updated_at: String,
    created_by: MarkerAuthor,
}

Storage options:

Markers are metadata. They should not be inserted into messages, because that would affect prompt history, transcript count, and agent-visible content.

API Plan

Initial routes:

Optional later routes:

Mutation behavior:

SSE And Delta Model

Marker changes should be delta-friendly.

type SessionMarkerDelta =
  | {
      type: "conversationMarkerCreated";
      revision: number;
      sessionId: string;
      marker: ConversationMarker;
      sessionMutationStamp?: number | null;
    }
  | {
      type: "conversationMarkerUpdated";
      revision: number;
      sessionId: string;
      marker: ConversationMarker;
      sessionMutationStamp?: number | null;
    }
  | {
      type: "conversationMarkerDeleted";
      revision: number;
      sessionId: string;
      markerId: string;
      sessionMutationStamp?: number | null;
    };

Frontend application rules:

Agent Integration

V1 markers are user-created, but they should be useful to agents.

Recommended prompt insertion actions:

These actions should produce explicit user prompts that reference marker title, session id, and anchor message id. They should not silently alter hidden context.

Example handoff prompt:

Continue from conversation marker "Accepted architecture decision"
in session session-42 at message msg-88. Use the marker note as the
handoff boundary and review messages after that point.

Search And Filtering

Markers should integrate with transcript search as metadata results:

Do not force full transcript hydration just to list markers. The marker list should be available from session metadata.

Persistence And Compatibility

Rules:

Implementation Phases

Phase 1: data model and static rendering

Phase 2: marker CRUD

Phase 3: virtualized transcript integration

Phase 4: agent workflow actions

Testing Plan

Backend:

Frontend:

Virtualization:

Agent workflow:

Acceptance Criteria