-
- {visiblePlanStepRows.map(({ key, step }) => (
-
+ {visiblePlanStepRows.map(({ key, step }, index) => (
+
}
+ nodeOffset={TASK_STEP_NODE_OFFSET_PX}
+ connectTop={index > 0}
+ connectBottom={index < visiblePlanStepRows.length - 1}
+ style={
+ liveStepIndex >= 0
+ ? spineAccentRowStyle(Math.abs(index - liveStepIndex))
+ : undefined
+ }
>
-
{taskStatusIcon(step.status)}
+ {taskStatusLabel(step.status)}:
{step.step}
-
- {taskStatusLabel(step.status)}
-
-
+
))}
{shouldCollapsePlanSteps ? (
diff --git a/apps/web/src/components/ui/threadline.tsx b/apps/web/src/components/ui/threadline.tsx
index 2713f5d63..b0cc06c82 100644
--- a/apps/web/src/components/ui/threadline.tsx
+++ b/apps/web/src/components/ui/threadline.tsx
@@ -152,6 +152,49 @@ function SpineRow({
);
}
+type SpineNodeKind = "done" | "running" | "warning" | "error" | "group" | "pending";
+
+const TONE_SPINE_DOT_CLASS_NAME = {
+ warning: "size-[6px] rounded-full bg-warning",
+ error: "size-[6px] rounded-full bg-destructive",
+} as const satisfies Record<"warning" | "error", string>;
+
+/** The glyph that sits on a spine for one row. The accent halo is reserved for
+ * the surface's single live node; a still-running step gets a small accent
+ * tick, settled steps are quiet solid dots, warnings/errors are compact tone
+ * dots, a collapsed group of steps is a hollow ring (same family, reads as
+ * "openable"), and a step not yet started is a fainter hollow ring. */
+function SpineNode({ kind }: { kind: SpineNodeKind }) {
+ if (kind === "running") {
+ return (
+
+ );
+ }
+ if (kind === "warning" || kind === "error") {
+ return
;
+ }
+ if (kind === "group" || kind === "pending") {
+ return (
+
+ );
+ }
+ return (
+
+ );
+}
+
/**
* Accent fade for the connectors approaching a live terminus: the spine warms
* toward accent as it nears the row where work is happening now, and settles
@@ -198,4 +241,13 @@ function CurrentMarker({ className, ...props }: React.ComponentPropsWithoutRef<"
);
}
-export { CurrentMarker, LiveNode, SectionLabel, SectionTick, SpineRow, spineAccentRowStyle };
+export {
+ CurrentMarker,
+ LiveNode,
+ SectionLabel,
+ SectionTick,
+ SpineNode,
+ SpineRow,
+ spineAccentRowStyle,
+ type SpineNodeKind,
+};
From 72f197a755f187e8d614bc315e711e8a78521508 Mon Sep 17 00:00:00 2001
From: badcuban <108198679+badcuban@users.noreply.github.com>
Date: Thu, 3 Sep 2026 01:45:57 -0400
Subject: [PATCH 2/2] fix(server): task titles no longer carry a leaked
tool-call tail
When the model mis-closes the subject parameter of a TaskCreate call
(`` instead of ``), Claude Code's lenient parser folds
the description and activeForm into the subject, so the plan step in the
activity popover showed the whole tool call.
Strip a stray closing tag that is followed by another parameter or the end of
the string wherever a task subject is read: create, update, TaskCreate and
TaskList results, and the activity feed labels.
---
.../src/provider/Layers/ClaudeAdapter.test.ts | 4 +++-
.../src/provider/Layers/ClaudeAdapter.ts | 24 ++++++++++++++-----
2 files changed, 21 insertions(+), 7 deletions(-)
diff --git a/apps/server/src/provider/Layers/ClaudeAdapter.test.ts b/apps/server/src/provider/Layers/ClaudeAdapter.test.ts
index a0d3bc38a..e7e21c1d6 100644
--- a/apps/server/src/provider/Layers/ClaudeAdapter.test.ts
+++ b/apps/server/src/provider/Layers/ClaudeAdapter.test.ts
@@ -2568,8 +2568,10 @@ describe("ClaudeAdapterLive", () => {
index: 1,
delta: {
type: "input_json_delta",
+ // The model sometimes mis-closes the subject tag and the harness
+ // folds the next parameters into it; the plan step must stay clean.
partial_json:
- '{"subject":"Investigate flaky login test","description":"Find the race in the login spec"}',
+ '{"subject":"Investigate flaky login test\\n
Find the race in the login spec\\n
Investigating","description":"Find the race in the login spec"}',
},
},
} as unknown as SDKMessage);
diff --git a/apps/server/src/provider/Layers/ClaudeAdapter.ts b/apps/server/src/provider/Layers/ClaudeAdapter.ts
index 8741c3ea2..1f5a25af4 100644
--- a/apps/server/src/provider/Layers/ClaudeAdapter.ts
+++ b/apps/server/src/provider/Layers/ClaudeAdapter.ts
@@ -1179,6 +1179,18 @@ function nonEmptyString(value: unknown): string | undefined {
return typeof value === "string" && value.trim().length > 0 ? value.trim() : undefined;
}
+/** When the model mis-closes the subject parameter of a task tool call
+ * (`` instead of ``), Claude Code's lenient tool-call
+ * parser folds the following parameters into the subject string, so the whole
+ * description lands in the plan step. Cut a stray closing tag that is followed
+ * by another parameter or by the end of the string. */
+const LEAKED_PARAMETER_TAIL_PATTERN = /<\/[\w-]+>\s*(?: