Repository navigation
Commands target grid-only sessions — dispatch agents recurringly fall through every command path #94
Description
What happened
The grid (tile-tree) vs Dispatch (detached) split exposes an inconsistency in how commands and selectors resolve their target session. Some paths already handle both placements correctly — dispatch hotkeys, the Active-Tab Transcripts template (after PR #83), `dispatchSessionIdsForTab` and `detachedDispatchSessionIdsForTab` selectors all exist and work. Others quietly fall through to grid-only logic and miss detached agents entirely. The bug class is the inconsistency: there's no single canonical resolver every command surface routes through, so each new feature decides for itself, and a fraction of them get it wrong by default.
PR #83 (Active-Tab Transcripts) was the most recent example — it had to combine `collectLeaves(tab.root)` with `detachedDispatchSessionIdsForTab` by hand, because the obvious-looking `collectLeaves` walks only the grid. Anyone writing a similar feature without remembering that footnote will trip the same bug.
Repro
Depends on which command. The shape is always: focus on (or operate on) a Dispatch agent, run a command, observe it acts on a grid pane instead — or no-ops — or only sees a subset of the tab's agents.
Concrete known-good vs known-bad needs an audit (see Sketch below).
Where
The fragmentation is in the resolvers, not in any one feature. Patterns in active use:
- `src/renderer/src/workspace/tile-tree/treeOps.ts` — `collectLeaves(tab.root)` walks only the visible tree. Used directly by some features (the bug pattern).
- `src/renderer/src/workspace/dispatch/dispatchSelectors.ts` — exposes `detachedDispatchSessionIdsForTab`, `dispatchSessionIdsForTab`, `buildDispatchGroups`. `dispatchSessionIdsForTab` strips pinned agents (a UI-display concern leaking into a scoping primitive — see PR Include detached Dispatch agents in active-tab transcripts template #83's WHY comment for why this matters).
- `src/renderer/src/workspace/hook/selectors/commandTargetSessionId.ts` — the single-session resolver. Audit needed to confirm whether it correctly considers dispatch focus.
- `src/renderer/src/workspace/sessionOwnership.ts` — adjacent ownership tracking.
- Every command runner under `src/renderer/src/features/workspace/commands/` and `src/renderer/src/features/feed/commands/` independently decides how to look up "the session" or "sessions for this tab". This is where the inconsistencies live.
Output
No fatal trace — this is a silent-miss class. Symptom is "command did nothing" or "command acted on the wrong session".
State
The inconsistency has been latent for a while. Each individual fix (PR #83 being the most recent) addresses one surface but doesn't prevent the next feature author from re-tripping it.
Sketch
Two steps. The first is the load-bearing one.
Step 1 — Audit. Grep every command runner, every "for each session in tab" loop, every workspace command, and classify:
- ✅ Handles both grid + detached correctly (uses `dispatchSessionIdsForTab` or composes `collectLeaves` + `detachedDispatchSessionIdsForTab`).
⚠️ Grid-only by way of `collectLeaves(tab.root)` directly.⚠️ Single-session via `commandTargetSessionId` — confirm it knows about dispatch focus.⚠️ Some other ad-hoc walk through `state.sessions` or `activeTab.focusedSessionId`.
This produces a concrete list of bug locations vs. correct locations. Without that, we can't tell whether the resolver layer below is even right.
Step 2 — Three canonical resolvers, used everywhere.
- `resolveTargetSession(workspace): SessionId | null` — answers "which session does THIS command target?". Knows about Dispatch focus, grid focus, fallback rules. Replaces every ad-hoc `workspace.activeTab?.focusedSessionId ?? collectLeaves(root)[0]` chain.
- `resolveTabSessions(workspace, tabId): SessionId[]` — answers "every session in this tab, regardless of placement". Composes `collectLeaves` + `detachedDispatchSessionIdsForTab`. Does NOT strip pinned (pinning is a display concern; see WHY comment in `templates.ts` after PR Include detached Dispatch agents in active-tab transcripts template #83).
- `resolveAllSessions(workspace): SessionId[]` — for global scope. Adds buried + detached across tabs.
Then route every command-target lookup through one of those three. The unit work is small (grep + refactor); the load-bearing part is committing to the resolver layer as the API for placement-aware queries so future features don't re-fragment.
Could be done as one big refactor PR (riskier, faster) or feature-by-feature as each command surface gets touched (safer, slower). Recommend the audit first (cheap), then one consolidation PR (the resolver layer + migrations of the
Activity
Juliusolsson05 commentedon May 13, 2026
Resolved by #107 (and #83 earlier).
Current state of the four primitives this issue called out:
Audit of `features/workspace/commands/` (the directory this issue flagged):
The `features/feed/commands/` directory the issue mentioned doesn't exist (and apparently never did — no command files anywhere under `features/feed/`).
No outstanding migration work; closing.
Juliusolsson05 commentedon May 13, 2026
Reopening — closure was premature. Re-reading the issue carefully.