Read the real prompt at the cursor; make the lane modes mean something - #3
Merged
Merged
Conversation
The per-row token column is marginal cost, chars/4 for anything the provider did not cost, and it never includes the system prompt or the tool definitions — so it reads as "too small" and cannot answer "how full was I here?". The provider already reports that number: an assistant message's tokens.input (+ cache read/write) is the whole prompt that produced it, system prompt and tool definitions included. contextSizeOf already sums it that way for the header gauge, and tokenFieldsOf already carries it onto every step row for the inspector; it was just never on screen without pressing i. The tree's status line now ends with it, right-aligned directly under the `ctx …` gauge so the two stack in one column and are read against each other: the gauge is now, this is the cursor, the gap between them is everything after that point. core/tree.ts#promptAtRow resolves which report applies to a row — a step carries its own message's, a user turn takes the reply to it (the first prompt that included that turn), a branch header has none because its column is already a subtree total. Nothing sent yet reads "not sent yet" rather than a zero, and the field is dropped whole rather than wrapped when the terminal cannot hold it. Deliberately not a second per-row column: it is history — an older row's figure is what went out then, not what a later crop would send now — and one number the user is inspecting can carry that caveat where forty scrolling past it cannot. Noted in ? help, docs/USAGE.md and DESIGN.md §6.7. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BrwAnTfTmJSmFwVn3DDFxr
Turns and Calls drew the same events in the same lanes with the same glyphs, differing by exactly one blank cell at each turn boundary — so switching between them looked like nothing happened. Measured on the fixture: 22 cells vs 19, identical pills. The magnitude model that did distinguish them (buildLanes: one column per turn vs one per tool call) had been superseded by the event strip and was unreachable from the route. Its tests kept passing, which is how this shipped. DSH settled the shape. Its ui-trajectory Overview has no mode toggle at all: always duration-proportional, "Thick rules mark Turn boundaries, compact inline markers identify Steps", and "just the tool calls" comes from wheel-zoom, drag-to-focus and search rather than a mode. So the toggle now carries only what it can honestly carry: - 1 / 2 select the x-axis — Duration (wall clock) or Turns (one cell per event). 3 is gone; a stored "calls" falls back to Turns. - A turn boundary is a │ rule across all three lanes, in both modes, drawn in a gap widened to hold it rather than implied by that gap. - Which events are on the strip is the row filter, not a mode. `f` gains tools-only, and layoutEventStrip takes the filter, so tools-only thins rows and lanes together and no-tools/user-only do too. eventAllowed documents its two deliberate mismatches with stepAllowed: labeled is a row annotation with no event of its own, and reasoning stays on the Model lane because the strip is a timeline. Deletes buildLanes, sparkline, fitColumns, durationWeighted, columnFor and buildEventStrip. test/lanes.test.ts is ported onto layoutEventStrip, keeping the assertions that carried real value (shared-axis occupancy, error glyph, compaction-as-context, empty lanes) and adding the rules and filter coupling. DESIGN.md §7.3 is rewritten around this, and §7.1's claim that the three-lane split is "as in DSH" is softened: the primary README describes a single combined Overview, a hands-on review describes three lanes, and the original research was a screenshot, not the source. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BrwAnTfTmJSmFwVn3DDFxr
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.
Two fixes to the trajectory half of
/tree, both from questions about what the numbers and the chart were actually saying.1. The prompt really sent at the cursor (
151ff73)The per-row token column is marginal cost, chars/4 for anything the provider didn't cost, and it never includes the system prompt or the tool definitions — so it reads as "too small" and can't answer "how full was I here?".
The provider already reports that number, and we already read it: an assistant message's
tokens.input(+ cache) is the whole prompt that produced it.contextSizeOfsums it that way for the header gauge andtokenFieldsOfcarries it onto every step row for the inspector — it was just never on screen without pressingi.The status line now ends with it, right-aligned directly under the
ctx …gauge so the two stack in one column and are read against each other:core/tree.ts#promptAtRowresolves which report applies: a step carries its own message's, a user turn takes the reply to it (the first prompt that included that turn), a branch header has none because its column is already a subtree total. Nothing sent yet readsnot sent yetrather than a zero, and the field is dropped whole rather than wrapped when the terminal can't hold it.Deliberately not a second per-row column: it is history — an older row's figure is what went out then, not what a later crop would send now — and one number you're inspecting can carry that caveat where forty scrolling past it cannot.
2. Turns and Calls drew the same chart (
2336ba6)Reported from real use: switching lane modes changed almost nothing. Measured on the fixture, that was exactly right — the two modes drew the same events in the same lanes with the same glyphs, differing by one blank cell per turn boundary:
layoutEventStripbranched on mode in only two places: duration widths, and a 2-cell gap at turn boundaries inturns. The model that did distinguish them (buildLanes— one column per turn vs one per tool call) had been superseded by the event strip and was unreachable from the route. Its tests kept passing, which is how this shipped.DSH settled the shape. Its
ui-trajectoryREADME describes no mode toggle at all: always duration-proportional ("projects real record start/duration timing from left to right"), turns marked with rules ("Thick rules mark Turn boundaries, compact inline markers identify Steps"), and "just the tool calls" reached through wheel-zoom, drag-to-focus and search rather than a mode.So the toggle now carries only what it can honestly carry:
1/2select the x-axis — Duration (wall clock) or Turns (one cell per event).3is gone; a stored"calls"falls back to Turns.│rule across all three lanes, in both modes, drawn in a gap widened to hold it rather than implied by that gap.fgainstools-only, andlayoutEventStriptakes the filter — so it thins the rows and the lanes together, andno-tools/user-onlydo too.eventAlloweddocuments its two deliberate mismatches with the rows'stepAllowed:labeledis a row annotation with no event of its own, and reasoning stays on the Model lane under every filter because the strip is a timeline — a minute of thinking is a thing that happened.Deleted
buildLanes,sparkline,fitColumns,durationWeighted,columnFor, andbuildEventStrip(superseded by the windowed layout in 0.2.2). None was reachable from the route; all were kept alive by tests, which is precisely how the bug shipped — leaving a second instance would have been inconsistent.test/lanes.test.tsis ported ontolayoutEventStrip, keeping the assertions that carried real value (shared-axis occupancy, error glyph, compaction-as-context, empty lanes, the whole windowing/overview suite).Docs
DESIGN.md §7.3 is rewritten as "The lane x-axis, and why there is no Calls mode", with the DSH quotes as rationale. §7.1's claim that the three-lane split is "as in DSH" is softened: the primary README describes a single combined Overview, a hands-on review describes three lanes, and Appendix A shows the original research was a screenshot rather than the source. The three-lane split is ours; the event pills, duration axis and turn rule are DSH's.
Tests
243 pass, 0 fail (net +4 after removing ~9 dead-model tests). New coverage:
promptAtRow/formatPromptAt, the turn rules in both modes, the filter↔lane coupling, and atools-onlyrow test asserting it andno-toolspartition the steps exactly. Typecheck and build clean.Not verified here: the e2e suite —
opencode servedoes not bind in this container (reproduced with no plugin installed at all). Both changes are unit-covered, but the e2e/treetest drives the real TUI and should cover this before release.🤖 Generated with Claude Code
https://claude.ai/code/session_01BrwAnTfTmJSmFwVn3DDFxr
Generated by Claude Code