diff --git a/docs/design/quark-architecture-review.md b/docs/design/quark-architecture-review.md
deleted file mode 100644
index 28607534de..0000000000
--- a/docs/design/quark-architecture-review.md
+++ /dev/null
@@ -1,306 +0,0 @@
-# Quark Timeline Architecture — and How It Can Be Faster
-
-An architecture review of the `opencode-quark-timeline` experiment: what the layers are, why the design is sound, and the _mechanical_ reason it can beat the Solid Store timeline — with the actual code.
-
-## The Stack in One Picture
-
-Four small layers, each with one job. The reusable core remains about 200 lines.
-
-```mermaid
-flowchart TD
- E[Server events] --> D[DataProvider
Solid Store, unchanged]
- D --> R[createSessionRows
row reduction policy]
- R -->|"set / update / insert / remove"| K[Keyed<SessionRow, string>
packages/quark/src/keyed.ts]
- K -->|"slots — structure channel"| FOR["Solid <For>"]
- K -->|"slot — per-row value channel"| UV[useValue adapter
one Solid signal per row]
- K -->|"slots + immutable identity"| BM[boundaries memo]
- UV --> SRV[SessionRowView]
- FOR --> SRV
-```
-
-```tree
-packages/quark/src
-├── reactivity.ts # 59 lines — Readable/Writable/Computed/Transaction over alien-signals
-├── keyed.ts # Keyed collection (the whole idea lives here)
-├── solid.ts # 8 lines — useValue: one quark Readable → one Solid signal
-└── index.ts # 2 lines — exports
-```
-
-The experimental boundary is disciplined: `DataProvider`, the event protocol,
-rendered row policy, and `SessionRowView` are unchanged. Row ownership and the
-incremental mutation mechanics changed; batch and live grouping now share one
-policy helper. The boundary remains narrow enough to attribute regressions or
-improvements.
-
-## The Core Idea: Identity and Equivalence Are Inputs, Not Discoveries
-
-This is the entire architectural bet. Solid Store must _discover_ what changed; Quark is _told_ what identity and sameness mean, once, at construction:
-
-```typescript title="packages/tui/src/routes/session/rows.ts" caption="The complete reconciliation policy is two functions"
-const state = Keyed.make({ key: rowKey, equivalent: sameRow })
-```
-
-`rowKey` answers _"which slot is this?"_ — including the subtle case where a group's key is the ref that **created** it, so refs moving to `pending` can never change identity. Keys are precomputed once at row construction with length-prefixed segments, so reconciliation pays zero key-extraction work:
-
-```typescript title="packages/tui/src/routes/session/rows.ts" caption="Keys are built once, at construction — rowKey is a field read"
-export type SessionRow = { readonly id: string } & ( /* ...variants... */ )
-
-function partRow(ref: PartRef): SessionRow {
- return { id: `p${segment(ref.messageID)}${segment(ref.partID)}`, type: "part", ref }
-}
-
-function segment(value: string) {
- return `${value.length}:${value}` // unambiguous without trusting a delimiter
-}
-
-function rowKey(row: SessionRow) {
- return row.id
-}
-```
-
-`sameRow` answers a different question: _"can any consumer tell these two values apart?"_ It compares only render-relevant fields. If it says yes-they're-the-same, **nothing downstream runs at all**.
-
-## Two Channels Instead of One
-
-A Solid Store exposes one reactive graph; every consumer subscribes into the same proxy web. `Keyed` splits change into two independent surfaces:
-
-```typescript title="packages/quark/src/keyed.ts" start=4
-export interface Keyed {
- readonly slots: Readable[]> // fires ONLY on insert/remove/reorder
- readonly values: Readable // fires on any current value change
- has(key: Key): boolean
- get(key: Key): Readable | undefined
- set(values: readonly A[]): void
- update(value: A): boolean
- insert(value: A, position?: "end" | { before: Key } | { after: Key }): Readable
- remove(key: Key): boolean
- move(key: Key, position?: "end" | { before: Key } | { after: Key }): boolean
-}
-```
-
-```definitions
-[
- { "term": "slots", "definition": "The structure channel. An array of stable per-row readables. Its identity changes only when membership or order changes. subscribes here." },
- { "term": "slot", "definition": "The value channel. One writable signal per row identity. A streaming delta touches exactly one slot. The row's component subscribes here via useValue." },
- { "term": "values", "definition": "The aggregate channel. A lazy computed mapping slots to current values. The TUI uses it only for internal mutation snapshots and does not subscribe boundaries to it." }
-]
-```
-
-This separation is what a proxy-based store cannot give you for free: **a value change and a structure change are different events**, published to different audiences.
-
-## How It CAN Be Faster: One Unique Row Update, Step by Step
-
-The highest-frequency reducer workload is a repeated streaming ordinal, which
-the seen-part set rejects in expected `O(1)` before reading the aggregate. The
-next useful comparison is a unique event that extends or completes an existing
-group row. Trace that value update through both systems.
-
-### Before: Solid Store path
-
-```typescript caption="Old hot path — every access and write crosses a proxy"
-setRows(
- produce((draft) => {
- // 1. draft is a proxy — every property read is a trap
- // 2. finding the group row walks proxied array elements
- // 3. the mutation writes through proxy machinery
- // 4. Solid records fine-grained dependencies per touched path
- append(draft, ref, part, queuedStart(draft))
- }),
-)
-// ...and on reconnect / revert / rebuild:
-setRows(reconcile(reduce()))
-// reconcile must re-derive identity from item references,
-// walk every row's shape, and diff against the proxy graph
-```
-
-Solid's generality — arbitrary nested access, partial path writes, plain objects — is paid for on **every operation** with proxy traps, shape inspection, and dependency bookkeeping.
-
-### After: Quark path
-
-The whole collection is a closure over three pieces of state — the structure signal, the identity map, and the declared equivalence. `update` reads directly against them:
-
-```typescript title="packages/quark/src/keyed.ts" caption="The closure state update() operates on, plus the whole hot path"
-export function make(options: {
- readonly key: (value: A) => Key
- readonly equivalent?: (left: A, right: A) => boolean
-}): Keyed {
- const slots = State.make[]>([]) // structure channel: one signal holding the slot array
- const byKey = new Map>() // identity → slot address, maintained by set/insert/remove
- const equivalent = options.equivalent ?? Object.is // declared sameness (sameRow in the TUI)
- const values = Computed.make((previous) => {
- const next = slots().map((slot) => slot()) // aggregate channel, lazy until subscribed
- return same(previous, next) ? previous! : next
- })
-
- return {
- slots,
- values,
- // ...
- update(value) {
- const key = options.key(value) // 1. one key extraction (a field read: row.id)
- const slot = byKey.get(key) // 2. one Map lookup — an address, not a search
- if (!slot) throw new Error(`Keyed value does not exist: ${String(key)}`)
- if (equivalent(slot(), value)) return false // 3. one equivalence check — early cutoff
- slot.set(value) // 4. one signal write; slots is untouched
- return true
- },
- // ...
- }
-}
-```
-
-Note what `update` never touches: `slots`. A value change writes one slot signal and the structure channel stays reference-identical — that single fact is the structural cutoff below.
-
-Four operations. No proxies, no shape discovery, no graph diff. Then the adapter forwards the change to exactly one Solid signal:
-
-```typescript title="packages/quark/src/solid.ts" caption="The entire Solid boundary — 8 lines"
-export function useValue(readable: Readable): Accessor {
- const [value, setValue] = createSignal(readable())
- onCleanup(readable.subscribe((next) => setValue(() => next)))
- return value
-}
-```
-
-### The structural cutoff — why `` never even wakes up
-
-Because `slots` only fires on structural change, a value-only update leaves the outer array **reference-identical**. The `` signal never fires, so keyed-list diffing never runs and the component owner survives:
-
-```typescript caption="Value-only transition: structure untouched, owner preserved"
-const structure = rows.slots()
-const groupSlot = structure[0]
-
-rows.update({ ...initial, completed: true })
-
-rows.slots() === structure // true — receives no change at all
-rows.slots()[0] === groupSlot // true — component owner survives
-groupSlot().completed === true // true — the one subscribed row re-renders
-```
-
-This is also the **beauty** mechanism, not just speed: an expanded reasoning group keeps its local state through permission repartitioning, because refs moving between `refs` and `pending` is a value change on a stable identity — never a remount.
-
-### The membership cutoff — duplicate deltas avoid the aggregate entirely
-
-The compiled `parts` members index contains every visible part identity,
-including refs nested inside groups. A duplicate streaming delta dies at
-`state.hasMember("parts", id)` before reading `state.values()`. Duplicate
-message and footer events use `Keyed.has` against the collection's existing key
-map. For a replacement that reaches `modify`, generated equivalence remains the
-final publication cutoff.
-
-## More Before / After, From the Actual Diff
-
-### Wiring the renderer: one ``, two subscription levels
-
-Before, every row component read its row through the store proxy, and any reconcile could disturb the list:
-
-```tsx title="packages/tui/src/routes/session/index.tsx" caption="Before — one reactive graph for everything"
-
- {(row, index) => (
-
- )}
-
-```
-
-After, `KeyedFor` subscribes to the structure channel and each row component subscribes to exactly one slot:
-
-```tsx title="packages/tui/src/routes/session/index.tsx" caption="After — structure and value subscriptions are separate"
-
- {(row, index) => (
-
- )}
-
-```
-
-### Appending a footer row
-
-```typescript caption="Before — mutate a proxied draft; Solid infers what changed"
-setRows(
- produce((draft) => {
- if (draft.some((row) => row.type === "assistant-footer" && row.messageID === messageID)) return
- const index = queuedStart(draft)
- completePrevious(draft, index)
- draft.splice(index, 0, { type: "assistant-footer", messageID })
- }),
-)
-```
-
-```typescript caption="After — say exactly what happened: maybe one value update, then one insert"
-mutate(() => {
- if (state.has(footerRowID(messageID))) return
- const current = state.values()
- const index = queuedStart(current)
- complete(current, index) // one state.update on the previous group, if open
- insert(current, index, footerRow(messageID)) // one new slot + one structural publication
-})
-```
-
-Same policy, but the after version _names_ its effects: at most one slot value change plus one structural change, flushed together by `Transaction.run`. Nothing has to diff anything to figure that out afterward.
-
-### Removing a row
-
-```typescript caption="Before — linear search, then splice through the proxy"
-setRows(
- produce((draft) => {
- const index = draft.findIndex((row) => row.type === "assistant-footer" && row.messageID === messageID)
- if (index !== -1) draft.splice(index, 1)
- }),
-)
-```
-
-```typescript caption="After — remove by identity; the ownership law does the rest"
-mutate(() => {
- state.remove(footerRowID(messageID))
-})
-```
-
-### Full rebuild (reconnect, revert, compaction)
-
-```typescript caption="Before — reconcile re-derives identity from scratch"
-setRows(reconcile(reduce()))
-```
-
-```typescript caption="After — set() reconciles with declared identity and equivalence"
-setRows(reduce()) // → state.set(next): Map lookups + sameRow checks; unchanged rows publish nothing
-```
-
-This is the one place the two systems do comparable O(N) work — and it's exactly the workload the checked benchmark measured. Every path above it is where Quark does structurally _less_.
-
-## The Cost Ledger
-
-| Per operation | Solid Store (`produce`/`reconcile`) | Quark `Keyed` |
-| ------------------- | ------------------------------------------- | ------------------------------------------------- |
-| Find the row | proxied array walk / identity re-derivation | one `Map.get` |
-| Detect "no change" | proxy-graph diff per touched path | one `equivalent()` call |
-| Value change | proxy writes + per-path invalidation | one signal write → one Solid signal |
-| Structure unchanged | reconcile still walks the list | outer array identity preserved; `` silent |
-| Structure changed | full reconcile | one new array; retained slots reused by reference |
-| Batch of writes | store batching | one `Transaction.run` — settled publication |
-
-Same asymptotics — `O(N)` for a whole-array `set` — but far fewer instructions, allocations, and invalidations per unit of change. **The speedup is a constant-factor win purchased with a stronger contract** (unique stable keys, declared equivalence), not framework magic. The checked-in claim: ~4.7× on sparse value publication, ~6.4× on reorder, at 1,000 rows.
-
-## Architecture Verdict
-
-### What's genuinely good
-
-- **Deep module, tiny surface.** One small `keyed.ts` module carries the whole idea; the laws (unique key, stable key, equivalence, structural cutoff, settled publication, ownership) are explicit and testable.
-- **The adapter is honest.** `useValue` is 8 lines and creates no second reconciliation system — the failure mode the design doc itself warns about.
-- **Identity-as-input is the right call** for this domain: OpenCode _has_ real identities (messageID, partID) and was previously throwing that information away for Solid to rediscover.
-- **Falsifiable framing.** The docs predict where Quark should lose. That's rare and worth preserving.
-
-### Where the architecture leaked — all fixed during review
-
-Every leak identified in the first pass has since been closed:
-
-- ~~**`JSON.stringify` keys.**~~ Keys are precomputed `id` fields built once at row construction with length-prefixed segments (`rows.ts` smart constructors); `rowKey` is a field read.
-- ~~**The aggregate channel reintroduces O(N) per delta.**~~ `boundaries` now derives from the structure channel (`rows.slots()`) and reads slot values untracked (quark reads are invisible to Solid's tracker); the raw `values` readable is no longer mirrored into Solid. Per-delta boundary cost is zero; the O(N) recompute fires only on structural change or tracked `messages()` field changes.
-- ~~**Boundary staleness as an implicit invariant.**~~ The untracked-read trick is now type-enforced: `messageBoundaryIDs` accepts `readonly BoundaryRow[]`, a projection of `Pick`ed identity-immutable fields, so a future dependence on a mutable row field is a compile error.
-- ~~**Two grouping engines.**~~ Join-vs-insert policy is unified in `appendDecision` + `groupKind`; both the batch `reduce()` path and the incremental `appendPart` path consume it, and row construction goes through shared smart constructors.
-- ~~**Benchmark not checked in / wrong hot path.**~~ `packages/quark/bench/` now exists and covers incremental `keyed.update` — including against Solid's _direct path write_, the adversarial case — plus dense updates and reorders.
-- ~~**No work counters.**~~ `Keyed` accepts an optional `Metrics` sink; `OPENCODE_QUARK_METRICS=1` reports timeline counters on cleanup.
-- ~~**Opaque positioning and adapter boilerplate.**~~ `insert` and `move` now share an explicit `Position` vocabulary (`"end"`, `before`, or `after`), `get` exposes stable slot addresses, and `KeyedFor` encapsulates the two-level Solid subscription.
-
-The remaining asymmetry — `complete` (immutable `state.update`) vs `completePrevious` (in-place mutation on a plain array under construction) — is inherent to the two contexts, three lines each, and shares the same policy. Not worth abstracting.
-
-None of these are structural flaws — they are integration debts. The layering itself (laws → Keyed → thin adapter → unchanged renderer) is the right shape, and it's the shape worth keeping even if every number in the current doc had to be re-measured.
-
-See `quark-review-recommendations.md` for the prioritized fix list.
diff --git a/docs/design/quark-message-content.md b/docs/design/quark-message-content.md
deleted file mode 100644
index ea6344f6c3..0000000000
--- a/docs/design/quark-message-content.md
+++ /dev/null
@@ -1,94 +0,0 @@
-# Quark-Owned Message Content Slots
-
-Status: implemented; drive parity pending
-
-## Summary
-
-Assistant message content currently lives as unkeyed arrays inside the
-DataProvider Solid store. Streaming deltas mutate those arrays through
-`produce`, and every consumer that resolves a part re-scans `content`
-(`findLast`, `filter(type)[ordinal]`), so per-delta work scales with message
-size instead of delta size.
-
-This experiment changes only content ownership: each assistant message's parts
-become one Quark keyed collection with stable per-part slots. Streaming events
-already name the slot on the wire — `ordinal` for text and reasoning, `callID`
-for tools — so deltas address slots directly instead of being rediscovered.
-
-Message metadata (finish, usage, retry, error), the event protocol, timeline
-rows, and view components' rendering logic remain unchanged.
-
-The experiment succeeds only if all checks pass:
-
-1. Existing TUI data and row tests preserve their behavior.
-2. Per-delta work is O(1) in message size: one slot publication per delta,
- zero structural publications, one markdown recompute scoped to the part.
-3. The drive script passes unchanged.
-
-## Ownership Boundary
-
-```
-server events
- |
- v
-DataProvider
- ├── Solid store: session/message metadata (unchanged)
- └── SessionContent: Keyed per assistant message <- this experiment
- | |
- | slots (structure) | slot (value channel)
- v v
- part row resolution TextPart / ReasoningPart / ToolPart
-```
-
-- Part identity: `text:{ordinal}` and `reasoning:{ordinal}` from event
- ordinals, tool parts by `callID`. These match the synthetic part IDs the
- timeline already uses, so `resolvePart`'s positional bridge disappears.
-- Deltas are value-channel publications: `content.modify(key, ...)`. The
- parts ``/group structure never re-runs on a delta.
-- Part starts are structural insertions; `ended` events publish the
- authoritative final value into the same slot.
-- Full sync (`message.sync`) seeds each message's collection with `set(parts)`
- derived from the fetched payload, assigning ordinals per type in order.
-- Revert, session removal, and model-switch refetch drop or reseed collections.
-- Cold consumers (transcript export, copy-last-response, message navigation,
- permission lookup) read `values()` — plain arrays, same shapes as before.
-
-## Part Layout
-
-Tool identity fields are immutable; streamed fields are mutable. The field
-diff mask means a tool status change skips text-driven work and vice versa.
-
-```ts
-const PartLayout = Layout.keyedUnion({
- key: Layout.key("partID", Layout.string),
- tag: "type",
- variants: {
- text: Layout.struct({ text: Layout.string }),
- reasoning: Layout.struct({ text: Layout.string, state: ..., time: ... }),
- tool: Layout.struct({
- id: Layout.immutable(Layout.string),
- name: Layout.immutable(Layout.string),
- state: ..., time: ..., executed: ..., providerState: ...,
- }),
- },
-})
-```
-
-Store values are the client `SessionMessageAssistant*` content shapes plus the
-synthetic `partID`; `values()` strips nothing because consumers already ignore
-unknown fields.
-
-## What Is Not In Scope
-
-- Message metadata stays in the Solid store.
-- No changes to server events or the client package types.
-- No lazy or pull-based consumers; slots publish eagerly as today.
-- Timeline row ownership is the previous experiment and is unchanged.
-
-## Measurement
-
-Instrument with `Keyed.Metrics`: a streaming test drives N deltas into a
-message with M existing parts and asserts slot publications == N and
-structural publications == parts started, independent of M. Markdown
-recompute counts are asserted through the part component render counters in
-the existing test harness.
diff --git a/docs/design/quark-performance-model.md b/docs/design/quark-performance-model.md
deleted file mode 100644
index 3220fd4dba..0000000000
--- a/docs/design/quark-performance-model.md
+++ /dev/null
@@ -1,532 +0,0 @@
-# Why Quark Can Be Faster Than Solid Store
-
-Status: experimental performance model
-
-## Conclusion
-
-Quark can be faster than the current Solid Store timeline because it accepts a
-stronger contract and uses that contract to do less runtime work.
-
-The advantage does **not** come from TypeScript types by themselves. TypeScript
-types disappear at runtime. The useful information comes from explicit runtime
-inputs and API laws:
-
-- A row key is unique and cannot change during its ownership lifetime.
-- Row equivalence is supplied before updates begin.
-- A keyed list has one stable slot per key.
-- Value changes and structural changes publish through separate channels.
-- A transaction publishes only settled graph state.
-
-Those guarantees remove choices from the hot path. Quark does not need to
-rediscover identity, shape, equivalence, index membership, or ownership on
-every update. Less discovery means fewer comparisons, proxy traps,
-allocations, writes, and downstream invalidations.
-
-This is a conditional claim, not a claim that Quark is universally faster than
-Solid. Solid can be as fast or faster when an application performs a precise
-direct store write, when lists are tiny, or when Quark receives no useful
-identity information. The relevant comparison is the current OpenCode path:
-whole projected records and ordered rows are reconciled into a Solid Store.
-
-## The Physical Argument Is “Do Less Work”
-
-Software cannot escape the physical costs of instructions, memory reads,
-allocations, pointer writes, cache misses, and notification fan-out. An
-abstraction can only become faster by reducing those costs or arranging them
-more favorably.
-
-Quark's proposed advantage has four concrete sources:
-
-1. **Declare repeated decisions once.** Identity and equivalence become
- preconfigured functions rather than per-event discovery.
-2. **Replace searches with addresses.** Stable keys resolve to persistent
- slots through a `Map`.
-3. **Separate unrelated change dimensions.** An item value can change without
- publishing a new list structure.
-4. **Stop propagation early.** Schema equivalence and slot equivalence suppress
- writes before they reach Solid or the terminal renderer.
-
-These are ordinary computer costs, not framework mythology. If profiling shows
-that Quark executes the same work as Solid plus an adapter, the hypothesis is
-false. If counters show fewer structural publications, fewer component-owner
-reconciliations, and fewer proxy/store operations, the speedup has a mechanical
-explanation.
-
-## Solid Store Must Preserve Generality
-
-The current timeline asks Solid Store to accept ordinary JavaScript objects and
-reconcile new projections into a proxy-backed graph.
-
-```mermaid
-flowchart LR
- A[Projected rows] --> B[Solid reconcile]
- B --> C[Inspect keys and shape]
- C --> D[Proxy-backed Store writes]
- D --> E[Dependency invalidation]
- E --> F[For reconciliation]
- F --> G[SessionRowView]
-```
-
-Solid's generality is valuable. It can react to arbitrary nested property
-access, partial path writes, and plain objects without requiring a schema-owned
-model. That generality also means the runtime must maintain proxy metadata and
-interpret each reconciliation against the current store graph.
-
-Solid is not inherently inefficient. A precise call such as
-`setStore(index, "value", next)` can avoid whole-list reconciliation. OpenCode's
-timeline frequently receives whole projected messages or rebuilt row arrays,
-however, so its current implementation uses `produce` and `reconcile` to
-recover identity and preserve nested owners.
-
-## Quark Makes Identity an Input
-
-`Layout.collection` compiles identity, equivalence, and indexes once when the
-timeline is created. Every row carries a collision-safe primitive `id`. Groups
-also retain their immutable origin for message-boundary projection:
-
-```typescript
-const PartRefLayout = Layout.struct({
- messageID: Layout.string,
- partID: Layout.string,
-})
-
-const SessionRowLayout = Layout.keyedUnion({
- key: Layout.key("id", Layout.string),
- tag: "type",
- variants: {
- message: Layout.struct({ messageID: Layout.string }),
- "compaction-queued": Layout.struct({ inputID: Layout.string }),
- part: Layout.struct({ ref: PartRefLayout }),
- group: Layout.union({
- tag: "kind",
- variants: {
- reasoning: Layout.struct({
- origin: Layout.immutable(PartRefLayout),
- refs: Layout.array(PartRefLayout),
- completed: Layout.boolean,
- }),
- exploration: Layout.struct({
- origin: Layout.immutable(PartRefLayout),
- refs: Layout.array(PartRefLayout),
- pending: Layout.array(PartRefLayout),
- completed: Layout.boolean,
- }),
- },
- }),
- "assistant-footer": Layout.struct({ messageID: Layout.string }),
- },
-})
-
-type PartRef = Layout.Type
-type SessionRow = Layout.Type
-```
-
-Factories build the ID once with length-prefixed segments. Length prefixes
-avoid delimiter collisions without invoking a serializer:
-
-```typescript
-function segment(value: string) {
- return `${value.length}:${value}`
-}
-
-function groupRow(kind: "reasoning" | "exploration", origin: PartRef): SessionRow {
- const id = `g${kind === "reasoning" ? "r" : "e"}${segment(origin.messageID)}${segment(origin.partID)}`
- if (kind === "reasoning") return { id, type: "group", kind, origin, refs: [origin], completed: false }
- return { id, type: "group", kind, origin, refs: [origin], pending: [], completed: false }
-}
-```
-
-Generated equivalence answers a different question: can consumers distinguish
-these two values for the same identity? The layout compares rendered fields,
-dispatches through the row and group tags, and ignores immutable `origin`.
-
-```typescript
-const SessionRows = Layout.collection(
- SessionRowLayout,
- ({ members }) => ({
- parts: members(partIDs),
- }),
- { backend: "generated" },
-)
-
-const rows = SessionRows.make()
-```
-
-The `parts` index contains standalone and grouped part IDs. Duplicate stream
-deltas return through `hasMember` before aggregate projection. Arbitrary
-replacements derive membership automatically; the hot group-append path applies
-the exact one-member delta already known from the event.
-
-It exposes two reactive surfaces:
-
-```typescript
-rows.slots // changes only when keys are inserted, removed, or reordered
-rows.values // changes when any current value changes
-```
-
-Each slot is itself a readable value. Solid `` receives `slots`, so a
-group can grow or complete without replacing its component owner. Generic
-aggregate consumers can subscribe to `values` when they need every value
-change.
-
-The TUI's message-boundary projection deliberately does not subscribe to
-`values`. Boundary identity uses only immutable row fields, so it tracks the
-structural `slots` array and reads each slot value untracked. Value-only deltas
-therefore perform no boundary work; inserts, removals, reorders, or message
-changes recompute boundaries.
-
-Here is a complete value-only transition. The group changes, but its key and
-position do not:
-
-```typescript
-const origin = { messageID: "assistant-1", partID: "call-read" }
-const initial = groupRow("exploration", origin)
-
-rows.set([initial])
-
-const structure = rows.slots()
-const groupSlot = structure[0]
-
-rows.modify(initial.id, (group) => ({ ...group, completed: true }))
-
-rows.slots() === structure // true: receives no structural change
-rows.slots()[0] === groupSlot // true: component owner survives
-groupSlot().completed === true // true: row value updated
-```
-
-A structural insertion changes the outer list but preserves every retained
-slot:
-
-```typescript
-const footer: SessionRow = {
- id: "f11:assistant-1",
- type: "assistant-footer",
- messageID: "assistant-1",
-}
-
-const footerSlot = rows.insert(footer)
-
-rows.slots().length === 2 // true
-rows.slots()[0] === groupSlot // true
-rows.slots()[1] === footerSlot // true
-```
-
-Removing and later reinserting a key begins a new ownership lifetime:
-
-```typescript
-rows.remove(footer.id)
-const nextFooterSlot = rows.insert(footer)
-
-nextFooterSlot === footerSlot // false
-```
-
-```mermaid
-flowchart LR
- A[Next rows] --> B[Keyed.set]
- B --> C{Same keys and order?}
- C -->|yes| D[Keep slots array]
- C -->|no| E[Publish slots array]
- B --> F{Equivalent value?}
- F -->|yes| G[No slot write]
- F -->|no| H[Publish one slot]
- D --> I[Solid For unchanged]
- E --> J[Solid For reconciles structure]
- H --> K[Existing SessionRowView updates]
-```
-
-The key distinction is that **row identity is not inferred from row value**.
-The timeline gives every reasoning or exploration group an immutable creation
-key. Permission partitioning may move refs between `refs` and `pending`, but it
-cannot change the group slot or reset local expanded state.
-
-## The `Keyed.set` Algorithm
-
-The current algorithm is deliberately small:
-
-```text
-set(next):
- 1. Compute every next key and reject duplicates.
- 2. Look up retained slots in the persistent key map.
- 3. For each next value:
- a. Reuse the previous slot for its key, or create one.
- b. Run generated equivalence over previous and next values.
- c. Write only a changed slot.
- 4. Publish the outer slot array only if membership or order changed.
- 5. Synchronize declared collection indexes.
- 6. Flush slot, index, and structural writes in one transaction.
-```
-
-The aggregate `values` readable maps the current slots to their values. It is a
-computed node, so it remains lazy without subscribers and publishes one settled
-array when subscribed.
-
-The algorithm preflights duplicate keys before any slot mutation. A rejected
-set therefore cannot partially update the graph. `Map` and `Set` give keys
-SameValueZero semantics, including consistent treatment of `0`/`-0` and
-`NaN`.
-
-## The Laws That Unlock the Optimizations
-
-```definitions
-[
- {
- "term": "Unique key law",
- "definition": "No two current values have the same key. This permits one Map entry and one slot per identity."
- },
- {
- "term": "Stable key law",
- "definition": "An entity or logical row keeps its key for its lifetime. This permits component ownership and subscriptions to survive value changes."
- },
- {
- "term": "Equivalence law",
- "definition": "If equivalent(left, right) is true, publishing right cannot change any consumer-visible meaning. This permits early cutoff."
- },
- {
- "term": "Structural cutoff law",
- "definition": "Value-only updates preserve the exact outer slots-array identity. This prevents keyed-list reconciliation for non-structural changes."
- },
- {
- "term": "Settled publication law",
- "definition": "Subscribers observe the state after every slot and structural write in the operation, never a partial combination."
- },
- {
- "term": "Ownership law",
- "definition": "Removing a key removes its slot from the collection. Reinserting that key creates a fresh slot; old external references cannot attach to a new lifetime."
- }
-]
-```
-
-These laws are stronger than a plain `Array