Skip to content

Commit 619a2b7

Browse files
committed
Park unused chrome task and agents strips
1 parent e0c5e00 commit 619a2b7

3 files changed

Lines changed: 39 additions & 55 deletions

File tree

src/tui/chrome-state.test.ts

Lines changed: 8 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -30,15 +30,15 @@ describe("formatChromeZones", () => {
3030
})
3131
})
3232

33-
test("partial: task rows only", () => {
33+
test("partial: task rows do not auto-paint (zones parked)", () => {
3434
const out = formatChromeZones({
3535
task: [{ title: "cutover readiness", status: "doing" }],
3636
})
37-
expect(out.task).toEqual([{ label: "cutover readiness", status: "doing" }])
37+
expect(out.task).toBeNull()
3838
expect(out.agents).toBeNull()
3939
})
4040

41-
test("running agents: agents zone stays null; checklist suppressed", () => {
41+
test("running agents: both zones stay null", () => {
4242
const state: ChromeLiveState = {
4343
task: [
4444
{ title: "chrome live helper", status: "doing" },
@@ -64,13 +64,12 @@ describe("formatChromeZones", () => {
6464
],
6565
}
6666
const out = formatChromeZones(state, NOW)
67-
// Transcript Task rows own live lane status — no FLEET board / agents zone.
68-
// Checklist is suppressed while any lane is running.
67+
// Both strips parked — live work stays on transcript ● Task rows.
6968
expect(out.task).toBeNull()
7069
expect(out.agents).toBeNull()
7170
})
7271

73-
test("idle with checklist still formats tasks when no lane is running", () => {
72+
test("idle with open checklist still returns null (zones parked)", () => {
7473
const out = formatChromeZones(
7574
{
7675
task: [
@@ -89,10 +88,7 @@ describe("formatChromeZones", () => {
8988
NOW,
9089
)
9190
expect(out.agents).toBeNull()
92-
expect(out.task).toEqual([
93-
{ label: "chrome live helper", status: "doing" },
94-
{ label: "wire chrome zone", status: "todo" },
95-
])
91+
expect(out.task).toBeNull()
9692
})
9793

9894
test("observe does not force an agents panel via formatChromeZones", () => {
@@ -113,8 +109,7 @@ describe("formatChromeZones", () => {
113109
},
114110
NOW,
115111
)
116-
// Agents zone is always null from formatChromeZones; observe is not a
117-
// chrome-zone surface here (agents zone stays empty; stack-only layout).
112+
// Both zones always null from formatChromeZones (parked pending rebuild).
118113
expect(out.agents).toBeNull()
119114
expect(out.task).toBeNull()
120115
})
@@ -364,7 +359,7 @@ describe("chromeFromSession", () => {
364359
])
365360

366361
const zones = formatChromeZones(state, NOW)
367-
// Running lanes suppress checklist; agents zone stays null (no FLEET board).
362+
// Both chrome strips parked pending rebuild.
368363
expect(zones.task).toBeNull()
369364
expect(zones.agents).toBeNull()
370365
})

src/tui/chrome-state.ts

Lines changed: 20 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,16 @@
11
/**
22
* Live chrome zone formatter for setChromeZones.
33
*
4-
* Pure: structured session state → one-line task / agents strings.
5-
* Heights stay with geometry (zones max 1 row each); this module never
6-
* invents row budgets.
4+
* Pure: structured session state → task / agents zone rows.
5+
* Heights stay with geometry; this module never invents row budgets.
6+
*
7+
* ## Parked auto-paint
8+
*
9+
* `formatChromeZones` currently always returns `{ task: null, agents: null }`
10+
* — both chrome strips are parked pending rebuild. Live work stays on
11+
* transcript `● Task …` rows. `formatTasksPanel` / `formatAgentsPanel` remain
12+
* for demos, tests, and a future rebuild; manual `setChromeZones` can still
13+
* feed preformatted rows.
714
*
815
* ## Product host push contract
916
*
@@ -19,7 +26,8 @@
1926
*
2027
* Always pass the full snapshot so absent zones clear (`null` hides the zone).
2128
* Partial object fields mean “no data” → that zone line is null, not left
22-
* stale. Observe mode can override the agents line via `state.observe`.
29+
* stale. Observe mode can override the agents line via `state.observe` when
30+
* agents chrome is rebuilt.
2331
*/
2432

2533
import {
@@ -129,28 +137,20 @@ export type FormattedChromeZones = {
129137
/**
130138
* Format structured live state into chrome zone rows for setChromeZones.
131139
*
132-
* Empty / partial / inactive inputs yield null for the corresponding zone
133-
* so geometry collapses that strip (idleDefault 0).
134-
*
135-
* Live sub-agent work paints as `● Task …` transcript rows (runtime-bridge),
136-
* not as a standing FLEET board or dual-rail agents zone — those restated the
137-
* same lanes above the chat and made progress harder to read. The agents zone
138-
* stays empty so dual layout never engages. The manage_tasks checklist is
139-
* also suppressed while any lane is running; it returns once the fleet is dry.
140+
* Both chrome strips (task checklist + agents/fleet board) are parked pending
141+
* rebuild: this always returns `{ task: null, agents: null }` so nothing
142+
* auto-paints in those zones. Live work stays on transcript `● Task …` rows
143+
* (runtime-bridge). `formatTasksPanel` / `formatAgentsPanel` stay intact for
144+
* demos, tests, and a future rebuild; manual `setChromeZones` / Alt+T can still
145+
* feed preformatted rows into the shell.
140146
*/
141147
export function formatChromeZones(
142148
state: ChromeLiveState,
143149
nowMs: number = Date.now(),
144150
): FormattedChromeZones {
151+
void state
145152
void nowMs
146-
const hasLiveAgents =
147-
state.agents !== null &&
148-
state.agents !== undefined &&
149-
state.agents.some((s) => s.status === "running")
150-
// Fleet board chrome is off: transcript Task rows own live lane status.
151-
const agents = null
152-
const task = hasLiveAgents ? null : formatTasksPanel(state.task)
153-
return { task, agents }
153+
return { task: null, agents: null }
154154
}
155155

156156
/**

src/tui/runner-host.test.ts

Lines changed: 11 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import type { KeyEvent } from "@opentui/core"
66
import type { CostSummary } from "../cost/cost-summary.js"
77
import type { SubAgentSession } from "../subagent/session-store.js"
88
import { createHarness } from "./harness.js"
9-
import { acceptOverlaySelection, closeInsetOverlay, runOverlayAction, toggleTasksPanel } from "./shell.js"
9+
import { acceptOverlaySelection, closeInsetOverlay, runOverlayAction } from "./shell.js"
1010
import {
1111
mountRunnerHost,
1212
observeSessionFromSubAgents,
@@ -129,19 +129,11 @@ describe("observeSessionFromSubAgents", () => {
129129
})
130130

131131
describe("mountRunnerHost chrome wiring", () => {
132-
// CL-5731: the task-change callback was built (director writes tasks,
133-
// getTasks()/onTasksChange exist) but had no live consumer — the chrome
134-
// push mechanism type-checked fine with `subscribeChrome` omitted, so a
135-
// director's task update never reached the shell. `subscribeChrome` is now
136-
// a required dep (not optional) so that regression cannot type-check
137-
// again, but the type alone does not prove the wiring actually runs: this
138-
// test drives a real notify() call through mountRunnerHost end to end and
139-
// asserts the task panel painted from it, the way the real runner's
140-
// `emitter.emit("tasks", ...)` -> `subscribeChrome` -> `pushChrome` chain
141-
// does. If `subscribeChrome`'s notify callback were ever dropped again
142-
// (e.g. `deps.subscribeChrome?.(pushChrome)` silently no-op on undefined),
143-
// this test fails because the second push never reaches the panel.
144-
test("a live chrome push (subscribeChrome notify) repaints the task panel", async () => {
132+
// CL-5731: subscribeChrome must stay wired end-to-end. formatChromeZones
133+
// now parks both chrome strips (always null), so a tasks push must not
134+
// paint the checklist — this test asserts the notify path still runs and
135+
// leaves the task panel empty (rebuild later; live work is ● Task rows).
136+
test("a live chrome push (subscribeChrome notify) does not auto-paint the task panel", async () => {
145137
const harness = await createHarness({ width: 80, height: 24 })
146138
let liveTasks: readonly { title: string; status: "todo" | "doing" | "done" | "cancelled" }[] = []
147139
let notify: (() => void) | undefined
@@ -168,20 +160,17 @@ describe("mountRunnerHost chrome wiring", () => {
168160
expect(host.shell.taskBox.visible).toBe(false)
169161
expect(notify).toBeDefined()
170162

171-
// Mirrors createChatDirector's onTasksChange firing after a
172-
// manage_tasks tool call: the live source changes, then the runner
173-
// notifies the host — it does not push the new snapshot itself.
163+
// Mirrors createChatDirector's onTasksChange: live source changes, then
164+
// the runner notifies the host. formatChromeZones parks the checklist.
174165
liveTasks = [{ title: "wire task panel", status: "doing" }]
175166
notify?.()
176167

177-
// CL-5847: the panel is hidden by default, so the live push lands in
178-
// tasksRaw underneath without showing. It stays hidden until opt-in.
179168
expect(host.shell.taskBox.visible).toBe(false)
180-
toggleTasksPanel(host.shell)
181-
expect(host.shell.taskBox.visible).toBe(true)
182169
await harness.renderOnce()
183170
const frame = harness.captureCharFrame()
184-
expect(frame).toContain("wire task panel")
171+
expect(frame).not.toContain("wire task panel")
172+
// Notify callback stayed registered — subscribe path ran without error.
173+
expect(notify).toBeDefined()
185174
} finally {
186175
host.dispose()
187176
harness.destroy()

0 commit comments

Comments
 (0)