Skip to content

Commands target grid-only sessions — dispatch agents recurringly fall through every command path #94

Closed

Description

@Juliusolsson05

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.

  1. `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.
  2. `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).
  3. `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 ⚠️-flagged sites).

Activity

  1. Juliusolsson05 commented on May 13, 2026

    @Juliusolsson05
    OwnerAuthor

    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):

    • `paneCommands.ts` — 13 sites route through `commandTargetSessionId` / `commandTargetSessionIdForState`. The remaining `focusedSessionId` reads are inside the `dispatchCommandTabId` helper (the Dispatch-aware resolver implementation itself) and in explanatory comments.
    • `sessionCommands.ts` — 15 sites, all on `commandTargetSessionId`.
    • `layoutCommands.ts`, `tabCommands.ts` — don't read session focus.

    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.

  2. Juliusolsson05 commented on May 13, 2026

    @Juliusolsson05
    OwnerAuthor

    Reopening — closure was premature. Re-reading the issue carefully.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions