diff --git a/packages/app/src/pages/session/timeline/thought-rail.tsx b/packages/app/src/pages/session/timeline/thought-rail.tsx index ef4d0a93e..8ab9769c9 100644 --- a/packages/app/src/pages/session/timeline/thought-rail.tsx +++ b/packages/app/src/pages/session/timeline/thought-rail.tsx @@ -58,9 +58,10 @@ // dot is decoration, not a sequence). A lone RUNNING step DOES rail — // its dot is the turn's only "working" signal (card sig / Livedot are // gone) and first steps are often the longest, so waiting for step two -// left the opening with no status. The lone live dot itself carries no -// line (first && last → 0px) so completion is just "dot fills" with no -// retraction. +// left the opening with no status. The lone live dot draws no line +// (0px) — every spine must end at a dot, so a single dot has no dangling +// half-spine above or below. A one-step completion still reads as "dot +// fills". // // Other implementation: the harness StepFrame rail on PR #216 slices by SDK // step-start/step-finish markers instead of assistant-part turns — different @@ -108,6 +109,12 @@ export function ThoughtRail(props: { const isRunning = () => props.last && props.running // Rule 3 — cap at dot centre; Rule 2 — NEG_STEP_GAP bridges the pt-3 gap. const dotCentre = DOT_TOP + NODE / 2 + // Lone running (Thinking before the first assistant part) is a single dot + // with no spine — every line must end AT a dot at both ends, like Claude + // Code. A lone dot has no line, so a one-step completion is just "dot + // fills" with no dangling half-spine above or below. The widget lab + // showed both dangles (dot→bottom, 0→dot) still leave one open end. + const isLoneRunning = () => props.first && props.last && props.running return ( <>