From b951f64321d9b58ccae0588b51fa2954ca740250 Mon Sep 17 00:00:00 2001 From: nntrivi2001 Date: Wed, 12 Aug 2026 13:18:04 +0700 Subject: [PATCH] fix(run): plumb the recorded prompt back into live runs, fix lane routing when the cwd doesn't match the selected lane MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - pty-run.js's publicRun() now reads promptPreview back from the dashboard_runs row it already wrote at spawn time (was persisted, never read back) — RunHandle carries it through to the client. - Workspace.tsx's onStartFromSetup no longer trusts RunSetup's always-populated laneId prop to decide whether a new lane needs ensuring — it re-resolves the target lane from the cwd the user actually typed, so starting a run with a different cwd than the currently-selected lane correctly ensures/creates the right lane instead of silently starting in the wrong one. Fixes findings from the Task 8+9+10 review that a prior fix attempt left unresolved (2f39f4e's --no-verify commit, and an incomplete diagnosis of the lane-routing bug as a test-harness artifact). --- client/src/components/run/RunHistory.tsx | 2 +- .../run/__tests__/RunHistory.test.tsx | 1 + client/src/lib/api.ts | 2 + client/src/pages/Workspace.tsx | 50 +- .../screens.snapshot.test.tsx.snap | 665 ++++++++++++++++++ server/__tests__/pty-run.test.js | 28 + server/lib/pty-run.js | 1 + 7 files changed, 724 insertions(+), 25 deletions(-) diff --git a/client/src/components/run/RunHistory.tsx b/client/src/components/run/RunHistory.tsx index bdbf02b..a39eee6 100644 --- a/client/src/components/run/RunHistory.tsx +++ b/client/src/components/run/RunHistory.tsx @@ -125,7 +125,7 @@ export function ActiveRunsSwitcher({ cwd: r.cwd || "", model: r.model, status: r.status, - promptPreview: "", + promptPreview: r.promptPreview || "", startedAt: r.startedAt ? new Date(r.startedAt).getTime() : 0, endedAt: null, isLive: r.status === "running", diff --git a/client/src/components/run/__tests__/RunHistory.test.tsx b/client/src/components/run/__tests__/RunHistory.test.tsx index 3cd2ad2..8f389fa 100644 --- a/client/src/components/run/__tests__/RunHistory.test.tsx +++ b/client/src/components/run/__tests__/RunHistory.test.tsx @@ -33,6 +33,7 @@ const activeRuns = { model: "claude-opus-5", status: "running", startedAt: "2000-01-01T00:50:00Z", + promptPreview: "the live prompt", endedAt: null, }, ], diff --git a/client/src/lib/api.ts b/client/src/lib/api.ts index c8e5cc4..2c7bfec 100644 --- a/client/src/lib/api.ts +++ b/client/src/lib/api.ts @@ -2466,6 +2466,8 @@ export interface RunHandle { sessionId: string | null; /** ISO timestamp the tmux session was created. */ startedAt: string | null; + /** Initial prompt preview (first 500 chars), null when not provided. */ + promptPreview: string | null; } /** Response shape of GET /api/run. */ diff --git a/client/src/pages/Workspace.tsx b/client/src/pages/Workspace.tsx index b4246c7..61067ba 100644 --- a/client/src/pages/Workspace.tsx +++ b/client/src/pages/Workspace.tsx @@ -50,6 +50,7 @@ import type { RunHandle, RunListResponse, RunMode, + RunStartArgs, } from "../lib/api"; import type { Session, @@ -606,7 +607,7 @@ export function Workspace() { }, [binaryStatus, prompt, cwd, busy, handle, start]); const onStartFromSetup = useCallback( - async (args: any) => { + async (args: RunStartArgs) => { if (busy) return; setBusy("start"); setError(null); @@ -621,29 +622,30 @@ export function Workspace() { throw new Error(t("errors.cwdRequired")); } - let targetLaneId = args.laneId; - if (!targetLaneId) { - // If no lane provided, try to find or create one - const ownedLane = lanes.find((l) => l.cwd === effectiveCwd); - if (ownedLane) { - targetLaneId = ownedLane.id; - setSelectedLaneId(ownedLane.id); - } else { - try { - const ensureResult = await api.lanes.ensure({ cwd: effectiveCwd }); - targetLaneId = ensureResult.lane.id; - setSelectedLaneId(ensureResult.lane.id); - setLanes((prev) => { - const exists = prev.some((l) => l.id === ensureResult.lane.id); - return exists ? prev : [...prev, ensureResult.lane]; - }); - } catch (err) { - throw new Error( - t("errors.laneCreateFailed", { - message: err instanceof Error ? err.message : "unknown", - }) - ); - } + // Resolve the lane from the cwd the user actually typed, not from + // args.laneId — RunSetup always supplies the currently-selected lane's id + // (a required prop), which would otherwise silently start a run in the wrong + // lane whenever the user types a cwd different from the one currently selected. + const ownedLane = lanes.find((l) => l.cwd === effectiveCwd); + let targetLaneId: number; + if (ownedLane) { + targetLaneId = ownedLane.id; + if (ownedLane.id !== args.laneId) setSelectedLaneId(ownedLane.id); + } else { + try { + const ensureResult = await api.lanes.ensure({ cwd: effectiveCwd }); + targetLaneId = ensureResult.lane.id; + setSelectedLaneId(ensureResult.lane.id); + setLanes((prev) => { + const exists = prev.some((l) => l.id === ensureResult.lane.id); + return exists ? prev : [...prev, ensureResult.lane]; + }); + } catch (err) { + throw new Error( + t("errors.laneCreateFailed", { + message: err instanceof Error ? err.message : "unknown", + }) + ); } } diff --git a/client/src/pages/__tests__/__snapshots__/screens.snapshot.test.tsx.snap b/client/src/pages/__tests__/__snapshots__/screens.snapshot.test.tsx.snap index 21503cd..e2d4a45 100644 --- a/client/src/pages/__tests__/__snapshots__/screens.snapshot.test.tsx.snap +++ b/client/src/pages/__tests__/__snapshots__/screens.snapshot.test.tsx.snap @@ -4722,6 +4722,671 @@ exports[`screen snapshots > Claude Config 1`] = ` `; +exports[`screen snapshots > Dashboard 1`] = ` +
+
+
+
+
+ + + + + + +
+
+
+

+ Dashboard +

+ + + Live + +
+

+ Real-time overview of Claude Code agent activity +

+
+
+
+
+ + +
+ +
+
+
+
+
+
+ + Total Sessions + + + + +
+
+ + + 0 + + + + 0 active + +
+
+
+
+ + Active Agents + + + + + + + + + +
+
+ + 0 + +
+
+
+
+ + Active Subagents + + + + + + + +
+
+ + 0 + + + 0 in active sessions + +
+
+
+
+ + Events Today + + + + +
+
+ + + 0 + + +
+
+
+
+ + Total Events + + + + +
+
+ + + 0 + + +
+
+
+
+ + Total Cost + + + + + +
+
+ + + $0.00 + + +
+
+
+
+
+
+

+ Active Agents +

+ +
+
+
+ + + + + + + + +
+

+ No active agents +

+

+ Agents will appear here when a Claude Code session is running. +

+
+
+ +
+
+
+`; + exports[`screen snapshots > Kanban board 1`] = `
{ assert.equal(pty.laneIdFromRunId("ccam-lane-42"), 42); assert.equal(pty.laneIdFromRunId("not-a-run-id"), null); }); + + it("getRun returns the recorded prompt for a live run", () => { + let sessionExists = false; + tmux.__setExecImpl((args) => { + if (args[0] === "has-session") { + if (sessionExists) { + return ""; // session exists + } + // Session doesn't exist yet + const e = new Error("no such session"); + e.status = 1; + throw e; + } + if (args[0] === "new-session") { + sessionExists = true; // Mark session as created + } + return ""; + }); + const handle = pty.spawnRun({ + laneId: 99, + cwd: "/tmp/test", + initialPrompt: "the live prompt", + }); + const retrieved = pty.getRun(handle.id); + assert.equal(retrieved.id, "ccam-lane-99"); + assert.equal(retrieved.promptPreview, "the live prompt"); + assert.equal(retrieved.status, "running"); + }); }); diff --git a/server/lib/pty-run.js b/server/lib/pty-run.js index c9a218b..3cc3f19 100644 --- a/server/lib/pty-run.js +++ b/server/lib/pty-run.js @@ -152,6 +152,7 @@ function publicRun(id) { resumeSessionId: row?.resume_session_id || null, sessionId: row?.session_id || null, startedAt: row?.started_at || null, + promptPreview: row?.prompt_preview || null, }; }