Skip to content

feat(tasks): task dependency field in kanban (T-57) - #22

Merged
jaesbit merged 2 commits into
mainfrom
feat/t57-task-dependencies
Jul 6, 2026
Merged

feat(tasks): task dependency field in kanban (T-57)#22
jaesbit merged 2 commits into
mainfrom
feat/t57-task-dependencies

Conversation

@jaesbit

@jaesbit jaesbit commented Jul 5, 2026

Copy link
Copy Markdown
Owner

Why

Auto-work needs to know which tasks can run without waiting for others. A task with unresolved dependencies must not be queued.

Scope

  • dependsOn: string[] added to the task protocol (packages/protocol/src/index.ts).
  • DB migration 007-task-dependencies.sql: task_dependencies join table (task_id, depends_on_task_id), FK ON DELETE CASCADE both ways, CHECK (task_id != depends_on_task_id).
  • REST API: GET /tasks, POST /tasks, PATCH /tasks/:id all read/write dependsOn.
  • UI: dependency picker in TaskModal — add/remove deps as chips showing T-<id>, title, and current state (color dot, or a check for a "done"-named state).

This is task 1 of an 11-task "Auto Work" chain; later tasks stack their own worktrees/branches on top of feat/t57-task-dependencies.

Non-goals

The "engine skips a blocked task" part of T-57's original acceptance is explicitly deferred to T-64 (not built yet, not in scope here). This PR only ships the data model, API, and UI.

Files changed

  • packages/protocol/src/index.ts
  • apps/server/src/db/migrations/007-task-dependencies.sql (new)
  • apps/server/src/db/tasks.ts, apps/server/src/db/tasks.test.ts
  • apps/server/src/routes-tasks.ts
  • apps/web/src/components/tasks/TaskModal.tsx, apps/web/src/views/TasksView.tsx
  • apps/web/src/lib/task-dependencies.ts, apps/web/src/lib/task-dependencies.test.ts (new)

TaskDrawer.tsx (listed in the ticket's surface area) is confirmed dead code — not imported anywhere in the app — so it was left untouched rather than adding UI to a component nothing renders.

Validation

  • bun run --filter '@omp-deck/protocol' typecheck — clean
  • bun run --filter '@omp-deck/server' typecheck — clean
  • bun run --filter '@omp-deck/web' typecheck — clean
  • cd apps/server && bun test — 229/229 pass (11 new dependency cases)
  • cd apps/web && bun test — 149/149 pass (5 new dependency-helper cases)
  • bun run --filter '@omp-deck/web' build — succeeds (pre-existing >500kB chunk warning, unrelated)

Acceptance

  • Can add/remove dependencies via UI and PATCH /tasks/:id.
  • GET /tasks includes dependsOn on each task.
  • Engine treats a task with an unresolved dependency as blocked — deferred to T-64 per the ticket note.

Adds a dependsOn: string[] field to kanban tasks so auto-work (later
tasks in the chain) can determine whether a task's prerequisites are
resolved before queueing it. This task only builds the data model, API,
and UI — no scheduling/engine logic (that's T-64).

- packages/protocol: Task.dependsOn (always present), CreateTaskRequest/
  UpdateTaskRequest.dependsOn (optional; PATCH replaces the full set).
- apps/server/src/db/migrations/007-task-dependencies.sql: task_dependencies
  join table (task_id, depends_on_task_id) with FK ON DELETE CASCADE so
  deleting either side of an edge cleans up automatically, plus a CHECK
  against self-reference at the schema level.
- apps/server/src/db/tasks.ts: listTasks batch-loads all dependency edges
  in one query; getTask/findTaskByDisplayOrId load per-task. createTask/
  updateTask validate the dependency set (dedupe, reject self-reference,
  reject unknown task ids, reject a cycle via BFS over existing edges)
  before mutating, and updateTask's dependsOn replaces the full set only
  when the patch includes the key (undefined leaves it untouched).
- apps/server/src/routes-tasks.ts: POST/PATCH /tasks validate dependsOn
  is string[] before hitting the DB layer.
- apps/web: TaskModal gained a "Depends on" section — chips showing each
  dependency's T-<id>, title, and current state (color dot, or a check
  for a "done"-named state) with a remove button, plus a select to add
  a new dependency from every other non-archived task. The id-resolution/
  filtering logic lives in the new apps/web/src/lib/task-dependencies.ts
  (resolveDependencyTasks/candidateDependencyTasks) so it has plain unit
  tests instead of needing a hook-rendering harness — same rationale as
  T-56's groupModels() extraction. TaskDrawer.tsx is confirmed dead code
  (no imports anywhere) and was left untouched.

Tests: apps/server/src/db/tasks.test.ts gained 11 cases covering default/
explicit/replace/clear/untouched dependsOn, self-reference, unknown id,
direct + transitive cycle rejection, list/get round-trip, and delete
cascade. apps/web/src/lib/task-dependencies.test.ts covers resolve/
candidate filtering and sort order.
If B depends on A, A now shows a read-only 'Required by' section listing
B (and any other tasks that depend on it). Same chip style as 'Depends on'
but no remove button — the relationship is owned by the dependent, not the
blocker.

- resolveDependentTasks(): new pure helper in task-dependencies.ts
  (tasks whose dependsOn[] includes task.id, sorted by displayId)
- 5 new unit tests covering: basic resolution, empty case, sort order,
  archived dependents included, self-exclusion guard
- TaskModal: useMemo(dependentTasks), section rendered only when non-empty
@jaesbit
jaesbit merged commit 423c345 into main Jul 6, 2026
@jaesbit
jaesbit deleted the feat/t57-task-dependencies branch July 6, 2026 10:00
jaesbit added a commit that referenced this pull request Jul 6, 2026
Adds an autoWork: boolean opt-in flag to kanban tasks so the auto-work
engine (T-64) only ever considers tasks the user explicitly marked
eligible. Not every card should be touched by unattended automation —
this ticket only builds the data model, API, and UI toggle; no
scheduling logic.

Stacked on feat/t57-task-dependencies (PR #22) since both tickets touch
the same task protocol/DB/routes/UI files — this diff is the
incremental change on top of T-57's dependsOn work, not a rebase onto
main.

- packages/protocol: Task.autoWork (always present, default false),
  CreateTaskRequest/UpdateTaskRequest.autoWork (optional).
- apps/server/src/db/migrations/008-task-autowork.sql: ALTER TABLE
  tasks ADD COLUMN auto_work INTEGER NOT NULL DEFAULT 0, same pattern
  as 006-task-priority.sql. Indexed for the future T-64 engine query
  (WHERE auto_work = 1).
- apps/server/src/db/tasks.ts: TaskRow/rowToTask carry auto_work
  through every read path (listTasks, getTask, findTaskByDisplayOrId).
  createTask/updateTask accept autoWork, persisted independently of
  the dependsOn set added by T-57.
- apps/server/src/routes-tasks.ts: POST/PATCH /tasks validate autoWork
  is a boolean before hitting the DB layer, same shape as the existing
  dependsOn/priority checks.
- apps/web: TaskCard shows a small Zap indicator next to the priority
  badge when autoWork is true. TaskModal gained an "Eligible for Auto
  Work" checkbox in the metadata grid, next to cwd. TaskDrawer.tsx is
  confirmed dead code (no imports anywhere, per T-57's findings) and
  was left untouched.

Tests: apps/server/src/db/tasks.test.ts gained 6 cases covering
default/explicit/toggle/untouched autoWork and independence from
dependsOn on create+update+list+get round-trips.
jaesbit added a commit that referenced this pull request Jul 6, 2026
Adds an autoWork: boolean opt-in flag to kanban tasks so the auto-work
engine (T-64) only ever considers tasks the user explicitly marked
eligible. Not every card should be touched by unattended automation —
this ticket only builds the data model, API, and UI toggle; no
scheduling logic.

Stacked on feat/t57-task-dependencies (PR #22) since both tickets touch
the same task protocol/DB/routes/UI files — this diff is the
incremental change on top of T-57's dependsOn work, not a rebase onto
main.

- packages/protocol: Task.autoWork (always present, default false),
  CreateTaskRequest/UpdateTaskRequest.autoWork (optional).
- apps/server/src/db/migrations/008-task-autowork.sql: ALTER TABLE
  tasks ADD COLUMN auto_work INTEGER NOT NULL DEFAULT 0, same pattern
  as 006-task-priority.sql. Indexed for the future T-64 engine query
  (WHERE auto_work = 1).
- apps/server/src/db/tasks.ts: TaskRow/rowToTask carry auto_work
  through every read path (listTasks, getTask, findTaskByDisplayOrId).
  createTask/updateTask accept autoWork, persisted independently of
  the dependsOn set added by T-57.
- apps/server/src/routes-tasks.ts: POST/PATCH /tasks validate autoWork
  is a boolean before hitting the DB layer, same shape as the existing
  dependsOn/priority checks.
- apps/web: TaskCard shows a small Zap indicator next to the priority
  badge when autoWork is true. TaskModal gained an "Eligible for Auto
  Work" checkbox in the metadata grid, next to cwd. TaskDrawer.tsx is
  confirmed dead code (no imports anywhere, per T-57's findings) and
  was left untouched.

Tests: apps/server/src/db/tasks.test.ts gained 6 cases covering
default/explicit/toggle/untouched autoWork and independence from
dependsOn on create+update+list+get round-trips.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant