ubiquitous-invention/plans/Plan-agent-coordination/Epic-task-as-runnable-unit/Task-deep-link-runs-to-task-detail.md
Randall Stillwell 6581f6a482 wip: agent-coordination iteration (runs+backlog+plans UI + 2 task plans + db-drift SQL)
In-flight work from 2026-06-05 that was sitting uncommitted on
feat/agent-pipeline-bridge for 10 days. Moved here to a clean branch
off main so the bridge branch can stay at its committed state (the
DEFERRED Phase 2a snapshot, see stwl-labs/ubiquitous-invention#3).

Touched areas:

- apps/web/app/(app)/[workspaceSlug]/settings/runs/page.tsx — runs page UI iteration
- apps/web/server/routers/{backlog,runs}.ts — router updates
- apps/web/app/(app)/[workspaceSlug]/plans/[planSlug]/[epicSlug]/[taskSlug]/page.tsx — new deep-link route
- apps/web/components/backlog/workflow-prompt-section.tsx — new component
- config/CursorSync.md — coordination doc tweak
- docs/operations/README.md + 2026-06-05-db-drift-reconcile-oauth-tables.sql — operations ops note + reconcile script
- plans/Plan-agent-coordination/Epic-task-as-runnable-unit/Task-{workflow-prompt-task-detail-ui,deep-link-runs-to-task-detail}.md — 2 task specs

This is a working-tree snapshot, not a finished PR. Rebase, split, or
amend as needed when picking it back up.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-15 15:35:39 -05:00

5.4 KiB

kind slug title plan_slug epic_slug status priority tenant_id owner cursor_todo_id updated_at
task deep-link-runs-to-task-detail Deep-link agent runs to task detail + document MCP stdio staleness agent-coordination task-as-runnable-unit done P2 global unassigned null 2026-06-03

Task summary

Two small follow-ups from Task-workflow-prompt-task-detail-ui so the editor surface is actually reachable, and from Task-export-db-to-markdown-frontmatter so the next operator doesn't spend an hour debugging a stale MCP process.

Description

(1) Linkify /settings/runs rows → task detail page

Today the task title cell in /settings/runs is plain text. The route I just shipped lives at /[workspaceSlug]/plans/[planSlug]/[epicSlug]/[taskSlug] but you can only reach it by typing the URL. Closing this gap unlocks the dogfood loop: claim a task via MCP → run appears in /settings/runs → click through to inspect / edit the workflow prompt.

Concretely:

  • runs.listRecent and runs.listForTask already join markdown_backlog_items; add planSlug, epicSlug, and slug to the selects.
  • In app/(app)/[workspaceSlug]/settings/runs/page.tsx, wrap the task title cell in a next/link <Link> when the joined row has all three slugs and taskKind === "task". Plans/epics don't have a detail page yet, so render those titles unlinked.
  • Stop the link click from also toggling the row's expand/collapse handler (e.g. e.stopPropagation()).

(2) Document MCP stdio process staleness

Symptom: complete_task returned without an export field and the on-disk .md file stayed status: draft even though the DB row flipped to done. Direct calls to exportBacklogItemToMarkdown work fine.

Root cause: the MCP server is launched by Cursor as tsx --env-file=../../.env src/index.ts (NOT tsx watch) and lives for the duration of the chat session. tsx imports the module graph once at startup; subsequent edits to apps/mcp-server/**/*.ts or any imported library (packages/database/src/markdown-backlog/**) don't take effect until the process is killed and Cursor reconnects. Worse, multiple stale MCP processes can accumulate from prior sessions; if Cursor binds the new session to one of those, you'll be talking to even older code.

This is a process-lifecycle gotcha, not a code bug — but it bit me right after we shipped the export feature, which means the next operator will hit it too.

Document in config/CursorSync.md under a new "MCP server lifecycle" section:

  • The server is spawned once per Cursor chat session via .cursor/mcp.json.
  • Changes to apps/mcp-server/** or any imported packages/database/** library require restarting the Cursor session (or killing the stale tsx ... src/index.ts process and starting a new chat).
  • How to find and kill stale processes: pgrep -fa "apps/mcp-server.*src/index.ts".
  • Why we don't run tsx watch: a process restart would drop the stdio JSON-RPC connection mid-call.

Also add a one-liner pointer from AGENTS.md (the dev-loop section) so it's discoverable without grepping.

Out of scope for this task: actually fixing the staleness (would need a graceful-reload protocol or a switch to tsx watch with reconnection logic in the client). Documenting is the cheap, correct first step.

Subtasks

  • Add planSlug/epicSlug/slug to runs.listRecent and runs.listForTask selects.
  • Wrap the task title cell in /settings/runs in a Link when all three slugs are present and taskKind === "task".
  • Update config/CursorSync.md with an "MCP server lifecycle" section.
  • Add a one-line pointer in AGENTS.md.

Acceptance criteria

  • Clicking the task title in /settings/runs navigates to the task detail page (and does NOT also toggle the row's expand/collapse).
  • pnpm lint && pnpm type-check && pnpm test clean.
  • config/CursorSync.md explains MCP staleness and how to recover, and AGENTS.md points at it.

Implementation notes

  • Selects: added taskSlug / taskPlanSlug / taskEpicSlug to both runs.listRecent and runs.listForTask (same shape on both for client-side reuse).
  • Link wiring: new buildTaskHref() helper in the runs page returns null for plans/epics, for rows missing any path segment, and during the initial render before workspaceSlug is known. The cell renders row.taskTitle unlinked in those cases so the table doesn't flicker between linked/unlinked.
  • Click stop: onClick={(e) => e.stopPropagation()} on the <Link> so the surrounding row's expand/collapse handler doesn't fire on link clicks. Verified via the AC.
  • One real surprise during verification: pnpm type-check failed against a stale .next/types/... generated file (Next.js's per-page type validator was tripping on an unrelated pre-existing export function formatDuration in the runs page). Removing apps/web/.next/types and re-running cleared it. Logged because the next operator may hit it after the dev server has been running across a refactor.

Follow-ups

  • The exported helper formatDuration in the runs page should either be moved to a shared util (it's now informally part of the runs-row contract) or have its export keyword dropped. Out of scope here.
  • Linking plan/epic titles from the runs page once a plans-tree browser exists.
  • Parent surface: ./Task-workflow-prompt-task-detail-ui.md
  • Sync contract: ../../config/CursorSync.md
  • Epic: ./Epic-task-as-runnable-unit.md