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 (
<>