v2.2.0 - Smart Tickets, Group Sessions & Terminal Superpowers
Dispatch has always pulled tickets from Linear. This release makes tickets flow the other way too, and stops pretending every piece of work maps to exactly one ticket. You can now write a rough prompt and get a proper ticket drafted for you, promote it to Linear when it turns out to matter, and start one Claude session that covers several tickets at once. The embedded terminal also finally behaves like a real one: Shift+Enter inserts a newline instead of submitting, and Cmd+Click opens the links Claude prints.
See it
Higher-quality video: dispatch-v2.2.0-smart-tickets-groups.webm
Local tickets, drafted for you
There's a + button in the header now. Click it, describe what you want in plain words, and a headless Claude call turns that into a title and a proper markdown description. You review and edit the draft before anything is created; accepting it lands a LOCAL-1 style card at the top of To Do. Generation is the only async step and you can cancel it mid-flight (the server actually kills the subprocess, and an immediate retry works instead of hitting a stale in-flight lock).
Local tickets are not second-class. They start sessions with playbooks, route status markers and hooks, and show up in the activity feed like everything else. The Linear poller can't touch them: sync is scoped by source, so a local card survives every poll cycle untouched. Titles over 300 characters, descriptions over 20,000, and anything containing the reserved status marker string get rejected at accept time with copy that says exactly what's wrong.
Sync to Linear
When a local ticket turns out to be real work, there's a Sync Linear button in its detail panel. This is the first write path to Linear that Dispatch has ever had, and it deliberately does not use the stored API key. The sync runs through your own Claude and its connected Linear MCP, so the issue is created as you, with your defaults, in your workspace. The stored key stays read-only.
After a successful sync the card adopts the real Linear identity in one atomic step: identifier, URL, issue id, source. The card id never changes, the next poll refreshes the card in place, and no duplicate ever appears. Failures are retry-safe by construction: every synced issue carries a hidden token, and a retry searches for that token before creating anything, so even the ugly case (issue created but the response lost) converges on the same issue instead of minting a second one. A failed sync puts a clear error on the card and leaves it local.
One implementation note worth knowing: the Linear MCP never reveals an issue's internal UUID, only the short identifier. Dispatch resolves the real id server-side with a read-only lookup before adopting, and refuses adoption if the issue doesn't carry the sync token it expects.
Group sessions
Some work spans three tickets and one branch. Cmd+Click tickets in To Do to select them, and a floating bar offers to start them as a group: one card, one workspace, one Claude session. The kickoff references Linear members through the Linear MCP and inlines local members' descriptions, with a single extra-direction field for the whole group.
The group card is the unit of lifecycle. Markers and hooks route to it alone, members mirror its column moves in the same transaction, and everything completes together at Done. Members are hidden from the board columns while grouped and render nested inside the group card instead, expandable, with a link out for Linear tickets. You cannot start, move, resume, sync, or clean up a grouped member individually; every single-card route answers 409 for grouped members, so the invariant holds server-side, not just in the UI. Cleanup runs exactly once, on the group.
Group creation validates members atomically inside the store queue: everything must still be in To Do and ungrouped at the moment of the write, otherwise nothing is created and the error names the tickets that fell out of eligibility.
A terminal that behaves
Shift+Enter now inserts a newline in the Claude Code input instead of submitting. This took a browser-side patch that feeds a raw line feed through ttyd's own send path, plus swallowing the keypress event that was double-firing the submit. Plain Enter, paste, and IME composition are untouched.
Cmd+Click on URLs works on real Claude output now. It turns out Claude Code emits its links as OSC 8 hyperlinks, which take a completely different path through the terminal stack than plain text URLs, and tmux drops them entirely unless the hyperlinks terminal feature is granted. Dispatch grants it at boot and again after every session creation, so a cold start works too. Plain clicks never navigate, and a long URL that wraps across two rows opens correctly from either row.
All of this ships as three named patches to ttyd's served page, each with its own anchor. If a future ttyd update drifts one anchor, only that one feature degrades to stock behavior with a named warning in the log; the terminal itself keeps working.
Leaner kickoffs
Kickoff prompts for Linear tickets no longer embed the whole description. They carry the identifier, title, and URL plus an instruction to read the ticket (description and comments) through the Linear MCP. Sessions without a working MCP still have enough to proceed: the URL is right there, and Claude falls back to fetching it or asking you. Local tickets keep the inline description, since there is nothing to look up.
A flatter Orca sidebar
The Orca view's sidebar dropped its collapse and expand sections. It is now a flat list under slim static labels, every session visible at once, with attention state readable at the top level: a dot per row, a warning triangle when a session hit an error or got lost, and a green dot for unseen activity (which correctly stays off for the row you're currently watching). Groups show as one entry with a member count. Clicking a row opens the same docked terminal as before, without remounting it.
Full changelog: v2.1.0...v2.2.0
Requires Node ≥ 22.22, tmux, ttyd, git, and the Claude Code CLI. Setup is in the README.
