The Playbook clip picker builds its harness rows from state.harnesses:
function playbookHarnessCandidates() {
return state.harnesses.map((h) => { … clip: `@{harness:${name}}` … });
}
function playbookClipRootRows(query) {
…
const harnessCount = state.harnesses.length;
if (harnessCount) cats.push({ type: "category", group: "harness", count: harnessCount });
…
}
(crates/daemon/assets/index.html ~lines 8873–8908 @ ad47152)
But state.harnesses is only ever populated inside the New Session dialog:
async function openNewSessionDialog() {
…
harnesses = await rpc("harness.list", null);
state.harnesses = Array.isArray(harnesses) ? harnesses : [];
(index.html ~line 15255)
On a fresh page load it is [], so the picker shows sessions only: no harness rows in the relevance section, and the harness ▸ category row is suppressed entirely — which also makes the harness submenu unreachable. @{harness:…} cannot be inserted from the web UI at all, even though the empty-state help right above the editor advertises it ("@{harness:name} delegates").
Repro
- Load the web UI fresh, open a session's Playbook.
- Type
@.
Observed — root rows are sessions plus one category:
["clip","clip","separator","category:session"]
state.harnesses.length === 0
- Open + new session, then cancel it. Type
@ again:
["clip","clip","clip","clip","clip","separator","category:session","category:harness"]
state.harnesses → ["agy","claude","codex","grok","hermes","kimi","opencode","pi","shell","smith"]
The TUI's picker shows both categories unconditionally (session (3) ▸, harness (10) ▸).
Expected
harness.list is fleet state, not New-Session-dialog state. Fetch it on connect (alongside the session list) and refresh it on reconnect, so any consumer — the clip picker today, others later — sees it.
Acceptance criteria
Found during a hands-on UX audit of the Playbook in the TUI and web UI.
The Playbook clip picker builds its harness rows from
state.harnesses:(
crates/daemon/assets/index.html~lines 8873–8908 @ ad47152)But
state.harnessesis only ever populated inside the New Session dialog:(
index.html~line 15255)On a fresh page load it is
[], so the picker shows sessions only: no harness rows in the relevance section, and theharness ▸category row is suppressed entirely — which also makes the harness submenu unreachable.@{harness:…}cannot be inserted from the web UI at all, even though the empty-state help right above the editor advertises it ("@{harness:name}delegates").Repro
@.Observed — root rows are sessions plus one category:
@again:The TUI's picker shows both categories unconditionally (
session (3) ▸,harness (10) ▸).Expected
harness.listis fleet state, not New-Session-dialog state. Fetch it on connect (alongside the session list) and refresh it on reconnect, so any consumer — the clip picker today, others later — sees it.Acceptance criteria
@in a freshly loaded web UI offers harness clips and theharness ▸category, with no prior visit to the New Session sheet.Found during a hands-on UX audit of the Playbook in the TUI and web UI.