feat(desktop,service,database): start a task from an issue and list it on the issue's page
Merged
DA
11d ago+14446+14446+01234567890123456789012345678901234567890123456789-82-82-01234567890123456789
Summary
This is the first of three PRs for "Link issues to tasks and agents" in the Improve Issues board plan: the link itself and Start task…. Start agent… and the move to In progress come in (b), and PR state on the card in (c).
- Database:
task.issue_idis a nullable reference toissue(id),ON DELETE SET NULL, with an indextask_issue. A task links at most one issue, and an issue can have many tasks. Moving a task to another workspace clears its link, because an issue belongs to its workspace.StoredTaskcarries the issue as it reads now: number, title, description, and the workspace's name and slug. - Wire:
taskCreateRequestgains an optionalissueId, andTaskSummarygains an optionalissueId. - Service:
POST /planswithissueIdreads the issue as the account sees it. If the account can't read it, or it belongs to another workspace than the one the task starts in, the request is refused asnot-foundand nothing is written. The window shows "That issue is gone, or it belongs to another workspace." - Brief and brain: an agent handed a plan started from an issue reads it after the plan, under "## The issue this task was started from". It gets the identifier, the title, a plain Luke link (
https://tryluke.dev/<ws>/issue/<id>, since issue links aren't a mention kind), and the description fenced as data in<issue-description>. The Linear block and this block now share one shape (startingIssueBrief); the Linear block's text is unchanged. The brain's standing context gets a[workspace issue the task was started from]JSON line beside the Linear one. - Window:
- The issue page's ⋯ menu and each card's or row's menu offer Start task…. It opens New task on the issue: titled "New task from ACME-7", named by the issue's title, fixed to the issue's workspace, with no Linear search.
- The issue page lists the tasks started from it under its status and priority, one pill per task, which opens that task. The list comes from the task list the window already holds (every task in the reader's workspaces), so there's no new query.
Migration notes
20261018164541_task_issue_link:
- The column is added with
ADD COLUMN IF NOT EXISTS. - The index is built with
CREATE INDEX IF NOT EXISTSinside the migration, with no manualCONCURRENTLYstep. Production'stasktable held 161 rows (152 kB) when this was written (a read-only count on Neon), so the index scan is shorter than a request. - The FK is added by drop-if-exists and then add with
NOT VALID, followed byVALIDATE CONSTRAINT. The validate step takes a lock that lets writes through. - There is no backfill.
Evidence
pnpm check:affected: lint, knip, discarded-effect, and the affected tests all passed. Three tasks failed in this sandbox for reasons outside this change, and each passed when re-run alone:@luke/desktop#typecheckand@luke/service#typecheckwere killed for running out of memory (exit 137) in the 8 GB sandbox.discarded-effectran out of heap.apps/web/tests/build-output.test.tshit its 180 s timeout under load. It passed in the previous full run.
- New tests, each failing without this change:
packages/database/src/models/__tests__/task-store.test.ts: a task started from an issue names it to every member and to no account outside the workspace (list and read). A move to another workspace lets it go. A deleted issue lets it go and the task stays.apps/web/tests/tasks-app.test.ts: a start from an issue of the workspace links it. A start from an issue of a workspace the account isn't in, or of another of its own workspaces, is refused as not found and starts no task.packages/service/src/agents/coder-host/coder-start.test.ts: the agent's brief carries the issue as it reads at the start, with its identifier and link, and with the fence escaped.packages/service/src/agents/brain-host/brain-host-standing-context.test.ts: the brain's context carries the issue as it reads now.apps/desktop/src/renderer/desktop/issues-page.test.ts: Start task… from the issue's ⋯ and from a card's menu; the issue page lists only its own tasks and opens one; an issue with none draws no row.apps/desktop/src/renderer/tasks/new-task-dialog.test.ts: opened on an issue, the card says so, is named by the issue, starts in its workspace, searches no Linear, and sendsissueId.
- JSON Schema goldens updated with
LUKE_UPDATE_FIXTURES=1:task-wire-taskCreateRequestSchemagains the optionalissueId.task-wire-taskSummarySchema,taskSchema,taskListAnswerSchema, andtaskAnswerSchemagain the optionalissueIdon the task.plan-edit-planEditAnswerSchemacarries the task, so it gains the same field.
apps/web/tests/inbox-item-read-migration.test.tsnow writes its task with SQL, as the schema of then holds it. Today's store writesissue_id, which doesn't exist before this migration.
Invariants
-
./scripts/check.shgreen. (CI) - JSON Schema goldens: diff explained above.
- Every behavior change has, in this PR, the test that fails without it; no change-detector test added.
- Gateway envelope goldens unchanged.
- No new
astype assertion. - No
Effect.runPromise/runSync/runForkoutside the edges. - No new raw async primitives.
- No discarded Effect.
- Test count rises by the 11 tests listed above.
- No hand-rolled file replaced.
- No
CLAUDE.md/AGENTS.mdsentence names a changed part. -
PRIVACY.mdunchanged. - Package deps unchanged.
- Barrel/door rule unchanged.
Notes
- This changes what the hosted service builds, so it needs a
./scripts/run.sh --previewrun on a Mac before merge. Don't arm auto-merge until that run is done. - Agents started with no task (automations) can't link an issue.
Made with Luke
Summary by cubic
Tasks can now be started from an issue and stay linked to it. The issue's page, and its card's or row's menu, offers Start task…, which opens New task named by the issue's title and fixed to its workspace, and the issue's page lists the tasks started from it, archived ones included, newest started first whatever the sidebar's pins put first. Previously a task could only reference a Linear issue, not the workspace's own issues.
Migration
taskgains a nullableissue_idreferencingissuewithON DELETE SET NULL, cleared when the issue is deleted or the task moves to another workspace.- The FK is added unvalidated then validated, so no writes are held back during the scan; no backfill.
Service and wire
POST /planswithissueIdrefuses as not found, writing nothing, when the account can't read the issue or it isn't in the task's workspace; the window says "That issue is gone, or it belongs to another workspace."TaskSummaryand the create request carry the optionalissueId, and agents and the brain read the issue as it stands, with its identifier and Luke link.