# Agent Note: Web Turn process folding

Status: implemented

English | [中文](2026-08-14-web-turn-process-folding.zh.md)

## Problem

A model Turn can expose System prompt, Context injection, reasoning, several Assistant replies, Tool calls, and Retry rows before its final answer. Keeping that whole trajectory at full height obscures the answer, while moving independent Chat Nodes under a parent disclosure would remount stateful Tool renderers and disturb chronological evidence. The compact view must hide completed process work without hiding the only evidence available while a Turn is still thinking, using a Tool, retrying, or ending without an answer.

## Decision

The Host-backed `ui-chat.transcriptView` preference selects `normal` or `compact` and defaults to `compact`. Normal leaves every process row visible and renders no Turn-process control. Compact applies the disclosure rules below. Switching modes changes wrapper visibility without reparenting or unmounting Chat Node renderers, and the preference remains outside the Session log.

In Compact mode, a Turn remains fully expanded while it is open. At `turn/end`, its latest Step becomes a final-answer boundary only when it contains user-facing Assistant reply content—non-blank text, an image, or an unknown visible block—and contains no Tool-call block. The answer remains visible. Context injection, reasoning, earlier Assistant material, Tool rows, and Retry rows before that boundary form one process disclosure. System prompt, User, and steering rows remain independent and never join the group. Completed, aborted, interrupted, failed, and max-token Turns use the same terminal projection; error, max-token, and turn-tail rows remain outside the group. A closed Turn with no final answer keeps all process evidence visible.

The Turn-scoped `turn-process` Definition derives the first model or Tool evidence, the latest Step's finalized answer boundary, reply-bearing durable Assistant-message count before that answer, and Tool-call counts from log events plus Step Location data. Shipped subagent delegation names (`subagent` and `subagent_*`) increment the subagent count instead of the ordinary Tool-call count, so the categories never overlap. Context injection remains process evidence without incrementing a summary count; System prompt is independent, stays visible, and remains before the opening User. It publishes an encoded scalar so unchanged facts retain value identity during streaming and contributes one stable control Chat Node. The Chat target positions opening User or steering input before process candidates from their first projection, then inserts the synthetic control between that input and the process rows. Without opening human input, the control stays before the earliest process candidate from its first appearance. Answer finalization, Retry, later Steps, completion, and manual expansion therefore change visibility without changing existing nodes' relative order, as specified by [stable Turn-process ordering](../bug-fix/2026-08-26-stable-turn-process-order.md). Each process member classifies itself from the Turn specification. Only the control and answer Seats subscribe to their Turn's content-revisioned Location key list and derive external process evidence plus independent-input spacing from that bounded list, so data-only updates refresh layout without rebuilding the global order. Turn status and loaded-window completeness then gate foldability: an open Turn never folds, and a partial history exposes neither the control nor hidden members. The control Node exists from the first process evidence onward but its Seat remains hidden until the closed Turn has a final answer and complete history. Once visible, it omits every zero-valued segment, uses `Thought for a while` when all three counts are zero, and places a full-width divider below the summary.

The Chat target binds the durable transcript preference through the shared settings scope and keeps per-Turn interaction state in its session-scoped Chat store. `ChatView` renders each business Node through one stable keyed `ChatNodeSeat`; adding the process control does not reorder existing keys, and Compact mode changes the Seat wrapper's `hidden` attribute without reparenting or unmounting a Tool, Assistant, Context, or Retry renderer. The Seat passes the same process state through `ChatNodeOwnerProps`, so the final Assistant renderer hides reasoning blocks from its own Step while leaving reply blocks visible; wrapper visibility and inline reasoning therefore share one UI-state source.

Closed process members use `hidden="until-found"`. A `beforematch` event on any member opens the shared group in supporting browsers. The Chat column applies spacing only between visible siblings because hidden-until-found members retain searchable zero-height boxes; the control's divider spans the content width, and a closed process control uses an 8px answer gap only when no independent input intervenes, while expansion restores the ordinary 16px row spacing. In Compact mode, the non-persisted session store contains only manually expanded Turn-and-answer-Step generations; absence means collapsed, and a different answer generation starts collapsed. Every eligible closed Turn therefore uses the same default regardless of whether it completed live, appeared after Load earlier, or closed while the reader was away from the tail. This can reflow content above the reader when a Turn closes or history becomes complete. An automatic collapse that would hide a focused process descendant opens the shared group instead, leaving keyboard focus in place; a manual close focuses the process control before hiding its members. If Load earlier is present, every process remains expanded and its control stays hidden; once history is complete, eligible groups immediately use the collapsed default. A fresh page load restores the durable Normal or Compact preference; per-Turn manual expansion survives only view remounts within the same page lifetime. Switching to Normal reveals every process row, while switching back to Compact reapplies the page-lifetime manual overrides over the collapsed default.

This presentation composes with [Conversation Node assembly](../architecture/2026-08-09-client-conversation-node-assembly.md): Definitions own deterministic process facts, the Seat owns shared interaction state, and keyed renderers remain independent. The [log-ordered human transcript](../bug-fix/2026-07-30-web-transcript-log-ordered-projection.md) remains complete because folding changes no session event or model input.

## Alternatives considered

**Fold only earlier Assistant replies.** Rejected because a common `Think → Tool → answer` Turn has only one reply-bearing Step and would expose no compact control, leaving the user's requested process content at full height.

**Keep Context injection outside the process.** Rejected because injected runtime context is part of the pre-answer trajectory rather than a new human instruction. Its own disclosure and label remain intact when the process is expanded. System prompt is kept outside because moving or hiding the request-wide instruction changes the visible frame around the opening User.

**Reparent the whole Turn under one summary row.** Rejected because eligibility changes while the Turn runs, and moving existing Chat Nodes across React parents remounts stateful Tool views. Stable Seats provide one disclosure without moving their children.

**Store manual expansion in Turn Location data.** Rejected because Location data is a deterministic projection of session events and has no browser-action write path. UI gestures belong to a declared, non-persisted store.

**Reuse `DisclosureRow` and unmount closed members.** Rejected because browser find could not discover their text and reopening would reconstruct stateful renderer subtrees.

**Fold a live answer candidate before `turn/end`.** Rejected because streamed text can still be followed by a Tool call, Retry, or later thinking Step. Waiting for the terminal boundary prevents automatic collapse–expand–collapse cycles and the resulting layout jumps.

**Defer a newly eligible collapse while the reader is away from the tail.** Rejected because it requires transient completion tracking and deferred state, and makes identical closed Turns start in different states depending on how they entered the viewport. Closed Turns use one deterministic default; scroll anchoring preserves position, while the focus guard preserves an active interaction.

## Consequences

Compact mode keeps the final answer prominent even when the Turn contains only injected Context, reasoning, or Tools before it, while expansion restores every process row in original order. Normal mode preserves the complete transcript without Turn-level controls. Hidden wrappers and Markdown subtrees remain mounted, trading browser memory for stable Tool state, manual expansion across view remounts, and browser-find recovery. Ordinary cross-message selection excludes closed members only in Compact mode; users expand the group before selecting them or choose Normal. Browsers without `hidden="until-found"` and `beforematch` retain manual disclosure but cannot reveal closed process text through page search. Unit coverage pins finalized answer boundaries, Retry and interruption, shared cross-kind expansion, final-Step reasoning, mode switching, manual expansion, immediate history-completion folding, off-tail folding, focus preservation, and content-only final-page revisions; assembled browser snapshots pin running, aborted, completed, paged-history, and persisted-setting trajectories.
