v0.18.0: the sessions release — rail, epochs, compaction honesty#45
Merged
Conversation
…ty UX Session reconstruction (devlog 2026-07-17): - Per-turn attribution is index-first, content-verified against the pair's assembled response (turnContentSig), content-scan on mismatch. Claude Code repacks history with ephemeral notice turns, so raw indices land on the wrong turns — the 5-model test session had three silently unattributed replies and two attributed by luck. - Pairs matching nothing classify REWOUND (prefix-divergent: /rewind or an edited turn) or UNATTRIBUTED — marked in the conversation, never silently blank. Rewound exchanges mark their divergence point and link to the kept wire pair. - A thread's model is a set (per-model requests/tokens/cost); the face model is the one with the most output tokens. "fable-5 · 63 turns" for five models' work becomes "fable-5 +4 models · 63 turns". - compact rewind guard: a request whose final history message appears nowhere in the keeper's packing holds the only copy of an erased exchange — kept full, never stubbed. Verified pre/post-compact: identical turns and attribution on the real trace. - Regression fixture cut from the controlled 5-model + /rewind session, sanitized with equality-preserving hash tokens (10KB zst, leak-swept). Launch & continuity UX: - cctrace is silent while the traced client owns the terminal: all output funnels through src/termlog.ts, buffered from spawn to exit, flushed after — mid-session continuity merges and crash-guard notes no longer corrupt the TUI. - --continue/--resume preloads the resumed session speculatively (newest prior session id, or the explicit --resume id), so the UI opens populated; the first live request confirms the guess or evicts the preload with a client re-init. - --version and the banner print the commit hash: baked in by make build --define, git fallback on source runs (guarded so an npm install inside another repo never reports that repo's HEAD). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…n tab The Session tab gains the third grouping level (session-tab.md decision 1): messages -> threads -> sessions -> project. - Threads are session-scoped: the grouping key is <sid>|<wire key>, so two sessions whose first message is identical (/clear mid-run) never merge into one thread. "" stays an honest no-session-id bucket. - Threads pane: >=2 session ids render collapsible per-session sections, newest activity first — header carries short sid (click = copy full), wall-clock range, request count, error rollup. Fold state survives live re-renders keyed by sid. Single-session traces render flat: zero new chrome, the common case pays nothing. - Routes: #/session/<sid8>[/<thread-key>] resolves sid-prefix after the thread-key back-compat check; old links keep working. [ / ] switch sessions from the keyboard. - Default focus is the newest session's main thread; a NEW session id appearing mid-run follows only while tailing — reading history is never yanked. - Request rows (the selected thread's list) mark rewound / compact-folded / failed requests inline; thread meta counts rewound exchanges; assistant turns that switch models prefix "→ model". - grok recap classification now reads the conv header instead of a key prefix (the sid prefix broke the old check — caught by tests). Verified through the dom-stub (the real page script executes): zero-chrome single-session, ordered sections, sid-prefix routing, grammar-clean markup. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The default thread label was "fable-5 +4 models · 63 turns" — the model
occupied the identity slot, which read as "threads are grouped by model"
(they aren't; a thread is a conversation that can span models via
/model). Clean split:
- label names the conversation: "63 turns" / "[explore] search for..." /
"prewarm"; the kind badge already says chat/agent/utility
- the model becomes a quiet right-aligned chip on the card ("fable-5 +4"
after mid-thread switches, per-model split in the tooltip — one
modelTitle helper shared with the convo-pane chip)
- selection emphasis moves up to the SESSION container: a faint accent
wash on the section summary and the selected thread head, replacing
the accent border (edges read as chrome — same rule that fixed
user-turn emphasis)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The session card takes the bordered-box treatment threads used to have; threads flatten to divided rows inside it; the selected thread's request rows nest below. Containment is drawn by structure, not indentation. Header gets real hierarchy instead of a flat mid-dot line: identity left (short sid, strong, click-to-copy + conversation turns), quiet attributes right-aligned (HH:MM range — no seconds, no degenerate ranges — request count, errors). The word "session" is gone: the box is the session. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…supersession Claude Code rewrites history under a live conversation: ephemeral recaps are injected, answered, and dropped; auto-compact folds old tool_use turns into text and shifts every index; a manual /compact replaces everything with a continuation summary — which also mints a new first-message sig, and the summary quotes old Task dispatch prompts verbatim, so the agent-linker could claim the post-compact MAIN CHAT as a subagent. The spine-only reconstruction flagged all of it as "rewound" and silently lost every post-compact turn. - anchor merge: requests following the spine align against it (the deepest shared turn, 2 verified neighbors — boilerplate turns like system notifications collide on sig alone) and append the genuinely new turns at their timeline position - full-append fallback: no anchor + a 10+ turn drop = a full rewrite; the whole post-compact packing appends — the summary turn is a real timeline event - compact gate: a 10+ turn drop below the running max marks the repack (notice wobbles are 1-3, a /rewind steps back a few); pre-compact pairs judged against a post-compact spine classify unattributed, never superseded - reunification: a continuation thread merges into the same session's deepest-history conversation, decided structurally — same real sid, same system identity block (first non-billing block; subagents and utilities differ), smaller start, parent quiet forever. The harness preamble text is one extra vote, not the gate, so a reworded or customized harness degrades gracefully - unique dispatch claiming: parallel Tasks with identical prompts pair 1:1 with their threads in wire order (all three used to match the first tool_use — wrong labels, collapsed branch links) - t.compactions exports each boundary (at/pairId/fromTurns/toTurns/ mode) for the display layer - turnSnippet: command blocks arrive in BOTH orders on the wire (/model puts <command-name> first, /codex:status and skill invocations put <command-message> first); args join the preview; the harness's "Base directory for this skill" expansion never wins - summarizeCache: a <90% cache hit is a warning, not a green — most of the prompt was re-billed at full input price Verified against two real traces: a 513->440 auto-compact (fold) and a triple-packing manual-/compact session (full rewrite + false agent claim). All thresholds regression-locked by synthetic fixtures; the sanitized /rewind fixture stays green — it caught two wrong designs along the way. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…oundaries
The session body was three unrelated row grammars stacked: epoch rows
(glyph + T0), turn rows (dot + ordinal), and detached thread CARDS for
subagents — even though the spawning turn is known. It didn't read as
one session.
One principle now: the session is a single rail. A continuous
accent-tinted line runs down a shared gutter (.rgut); every row is a
node on it:
- turn dots (existing cache/error verdict colors) sit ON the line,
punched out with a bg halo; user turns stay hollow rings
- epoch heads become hollow accent rings (.enode) — the 3-circle
git glyph is gone from rows; the rail itself is the branch metaphor
- subagent threads attach AT their spawn turn as branch rows: a CSS
elbow off the rail, label + outcome inline ("[explore] map the
repo · 2 turns · $0.0035"), content indented one outline level.
The detached card disappears while the parent is selected and
returns whenever it isn't — nothing becomes unreachable
- superseded exchanges stay grey nodes at the ordinal they occupied
- compact boundaries render explicitly: a break node (two slanted
hairlines, the axis-break glyph) + "COMPACTED · 513 -> 440 turns"
row at the boundary; hover spells the context collapse in turns AND
tokens, fold vs full rewrite, and the honest sentence — everything
above survives only in the summary/folded form; click opens the
first post-compact wire request. The convo pane draws a dashed
divider at the same index, and a rendered continuation-summary turn
is tagged (position-first via t.compactions, preamble fallback) —
never displayed as something the user typed
Grammar-checked against hostile wire content; verified through the
real page script on both compaction traces.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…e header Live runs, view serves, and snapshots all pass traceFile through PageMeta; view resolves the project from the log dir's parent when it's a standard ./.cctrace, so a served trace stops claiming the trace filename as its project name. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
session-tab.md gains the settled concept ("a thread is one
conversation as the user experiences it" + the event table: /model ->
epoch, compact -> boundary event, /clear -> new session), the session
rail, the compact boundary display, and the structural-over-strings
detection rationale (incl. why capture-time metadata stamping was
rejected and the request-diff refactor direction). CLAUDE.md/SKILL.md
updated to match.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
A sanitized cut of a real session that survived a fold compaction and two continuation packings, with parallel same-prompt subagents and the false-agent-claim hazard in one file. Pins every threshold calibrated against live wire mess (the 10+ turn drop, the 2-neighbor anchor, the system identity block, unique dispatch claiming) — the tripwire for when Claude Code changes its repacking behavior. tests/sanitize-trace.ts is the cutter, kept this time: equality- preserving hash tokens (same string -> same token, so grouping, attribution, and the anchor merge behave as on the original — whole- string hashing is strictly harsher than the runtime's capped-prefix compares), marker prefixes preserved (<system-reminder>, command wrappers, the continuation preamble, billing-header system blocks), sids mapped to deterministic fake uuids, zero original text. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
CHANGELOG entry for the sessions-layer arc (hierarchy, rail, epochs, compaction honesty, attribution), version bump, README session bullets (en + zh) brought up to the 0.18 story. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The sessions-layer arc, rounds 1–11 plus release prep. The Sessions tab
becomes a real reconstruction of what the harness did to the
conversation: sessions, threads, model epochs, subagent branches, and
compaction — all on one rail.
Highlights (full story in CHANGELOG 0.18.0):
session-scoped; one-conversation sessions absorb their card into the
header; short thread keys in URLs.
(a /model switch opens an epoch, never a thread), subagents attached
as branch rows at their spawn turn with outcome inline, superseded
exchanges grey at the ordinal they occupied.
back in (anchor merge for folds, full append for rewrites), full
/compact continuations reunify structurally (same sid + same system
identity block + parent quiet forever — harness strings are one vote,
not the gate), and every boundary renders a break node with the
context collapse in turns and tokens.
post-compact conversations splitting into a second thread (or being
claimed as a subagent via summary-quoted dispatch prompts), parallel
same-prompt Task spawns collapsing onto one dispatch, the t1+ jump
bug, masked-star session URLs, weak cache hits shown green.
tests/sanitize-trace.ts (kept in-tree). 456 tests.
🤖 Generated with Claude Code