Plan panel
When an agent (Claude Code, pi, a custom script) maintains a multi-step plan — Explore → Implement → Test → Commit — τ-mux renders that plan in a dedicated sidebar widget rather than each agent scribbling it into its terminal output. The widget is read-only by design: the agent owns the plan, the panel shows it.
What it does
Section titled “What it does”- One source of truth. Plans live in the bun-side
PlanStore, keyed by(workspaceId, agentId?).ht plan set/update/complete/clearmutate it; the panel listens via therestorePlanspush channel. - Step states with glyphs. Each step renders as
✓ done·● active·○ waiting·✗ err. Active steps animate so the user sees progress at a glance. - Click to focus. Clicking a plan card switches to the originating workspace.
- Audit ring. Every auto-continue decision (fired / dry-run / skipped / paused / resumed) appears below the plan. Cap 50 entries in memory, debounced 100 ms over the wire.
- Web mirror parity. The same panel renders in the web mirror, reading
plansSnapshotandautoContinueAuditenvelopes off the WebSocket. - Status-key bridge. Agents publishing plan-shaped checklists via
ht set-status <key-with-"plan"> '<json-array>'light up the panel without changing their publishing code — the smart-key sidebar rendering keeps working too.
Quick example
Section titled “Quick example”# Inside a τ-mux pane HT_SURFACE is auto-set, so the workspace is# resolved server-side — no --workspace flag needed.ht plan set --agent claude:1 --json '[ {"id":"M1","title":"Explore","state":"active"}, {"id":"M2","title":"Implement","state":"waiting"}, {"id":"M3","title":"Test","state":"waiting"}, {"id":"M4","title":"Commit","state":"waiting"}]'
# As work progresses:ht plan update M1 --state doneht plan update M2 --state active
# When done:ht plan completeht plan clear
# From outside τ-mux, pass --workspace explicitly:# ht plan set --workspace ws:5 --json '[…]'The sidebar widget shows the card the moment set lands; updates animate in 100 ms after each update.
Anatomy of a plan card
Section titled “Anatomy of a plan card”ws:5 claude:1 × ← header (workspace · agent · clear)0/3 done · 1 active · updated 2m ago ← progress summary + freshness stamp▓▓▓▓▓░░░░░░░░░░░░░░░ ← progress bar● M1 Explore ⌄ ← step rows (⌄ = has description) Read the poller and map every… ← expanded description○ M2 Implement○ M3 TestAUTO-CONTINUE · LAST 3 ← audit ring headerfired next plan step: M2skipped cooldown — 1842msdry-run would continue: M2Clearing a card. The × appears on hover while work is in flight, and is promoted to a labelled Clear button — with the card outlined in the success colour — once every step is done. It routes through the same handler as ht plan clear, so the CLI, the native panel and the web mirror can never disagree about what exists. The panel doesn’t optimistically remove the card: the store’s broadcast is what repaints, so a clear that didn’t land can’t leave you with a panel that looks clean but isn’t.
Step detail. Steps that carry a description — every mirrored Claude Code task does — are toggles. Click to expand the full text under the row; click again to collapse. Expanded rows stop truncating their title. Expansion is local view state and never goes on the wire, so the native panel and the mirror can legitimately show different rows expanded.
Empty plans are hidden — when nothing is published in any workspace, the native panel collapses to zero height.
In the web mirror the agent-plans widget instead renders a “No active agent plans” placeholder once the first plansSnapshot envelope arrives — even if it’s empty. This way users discover the widget exists before any agent posts a plan, rather than waiting in vain for it to appear.
How the bridge works
Section titled “How the bridge works”The status-key smart system (Plan #02) renders any ht set-status value with a known kind (pct, lineGraph, etc.). Plan #09 commit C adds a tap on that pipeline:
ht set-status build_plan '[…steps…]'lands in bun’s dispatch.- The smart-key sidebar broadcast fires unchanged.
- The
planStatusBridgeinspects the same payload — if the key contains “plan” and the value parses as a JSON array of{id, title, state?}objects, it callsPlanStore.setwithagentId: status:<surfaceId>. - The plan panel re-renders.
The match is intentionally narrow (key name must contain “plan”, value must be a JSON-string array). Anything outside that contract passes through silently.
| Use this if | … |
|---|---|
| You’re writing a new agent | Call ht plan set directly — typed, attribution-aware, supports multiple agents per workspace. |
| You have an agent that already emits status keys | Rename the key to include “plan” and use the right payload shape — both the sidebar and the panel light up. |
How auto-continue uses the plan
Section titled “How auto-continue uses the plan”The auto-continue engine reads the most-recently-updated plan in the surface’s owning workspace on every turn-end notification. The heuristic decides:
- Plan has any
waitingoractivestep → continue (Continue M3-style instruction). - Every step
done→ wait (the agent finished). - No plan published → wait (no anchor; ambiguous).
A confident wait blocks; an ambiguous wait can escalate to the model in hybrid mode. The audit ring on the panel reflects each decision.
Source files
Section titled “Source files”src/bun/plan-store.ts— keyed in-memory store;set/update/complete/clear/list/subscribe.src/bun/rpc-handlers/plan.ts—plan.*JSON-RPC handlers.src/bun/plan-status-bridge.ts—plan_arraytranslator.src/shared/plan-panel-render.ts— pure HTML helpers shared by native + mirror.src/views/terminal/plan-panel.ts— native sidebar widget.src/web-client/plan-panel-mirror.ts— web-mirror rendering.bin/ht plan— CLI entry point.tests/plan-store.test.ts,tests/plan-panel-renderer.test.ts,tests/auto-continue-bridge.test.ts— unit coverage.
Read more
Section titled “Read more”ht planCLI reference — every subcommand with examples.- Auto-continue — the engine that reads plans and decides whether to send
Continue. ht autocontinueCLI reference — driver for the engine.