Files
kandev/docs/plans/task-create-workflow-memory/plan.md
T
2026-08-23 17:34:52 +00:00

12 KiB

spec, created, status
spec created status
docs/specs/tasks/requirements/task-create-workflow-memory.md 2026-08-08 done

Implementation Plan: Task Create Workflow Memory

Overview

Extend the existing backend-owned task-create preference with a targeted per-workspace workflow map, then make the shared dialog resolver prefer that map over unlocked board/list context. Backend contract and merge semantics land first, frontend mapping and resolution follow, and a production-build E2E test proves persistence across workspace switches and conflicting filters.

Confirmed root cause

TaskCreateLastUsed and every wire/store mapper omit workflow, while resetTaskForm initializes selectedWorkflowId only from the caller's workflowId. The selector's lastUsedWorkflowId is component-local and only sorts menu choices. Consequently, a dialog opened with workflowId=null has no durable workflow default when multiple workflows exist, and a dialog opened under a filter inherits that filter rather than the workflow that created the previous task.

Backend

Per-workspace preference contract

  • Add WorkflowIDsByWorkspace map[string]string with JSON key workflow_ids_by_workspace to apps/backend/internal/user/models/models.go:TaskCreateLastUsed.
  • Keep the field inside the existing users.settings.task_create_last_used JSON object; no table or column migration is required.
  • Treat a non-empty workflow map as a meaningful task-create patch in apps/backend/internal/user/service/service.go and preserve it through DTO, HTTP response, WebSocket event, and boot-state mapping.
  • Extend apps/backend/internal/backendapp/boot_state_routes.go so the boot payload exposes the complete map and marks the task-create state synced when the map is the only populated preference.

Targeted SQLite and PostgreSQL merge

  • Extend makeTaskCreateLastUsedJSONSetArgs in apps/backend/internal/user/store/sqlite.go to update one nested workflow_ids_by_workspace.<workspace_id> entry at a time.
  • Extend applyPostgresTaskCreateLastUsedPatch with equivalent parameterized jsonb_set paths. Do not serialize and replace the whole map: alternating or concurrent task creation in two workspaces must preserve both entries.
  • Preserve the existing targeted-update behavior for repository, branch, agent profile, and executor profile, including broad user-settings writes racing with task creation.

Record the effective workflow

  • Extend buildTaskCreateLastUsedPatch in apps/backend/internal/task/handlers/task_http_handlers.go to add body.WorkflowID under body.WorkspaceID after successful creation.
  • Update the WebSocket task-create bridge in apps/backend/internal/task/handlers/task_ws_handlers.go to pass req.WorkspaceID and req.WorkflowID into the same recorder path.
  • Preserve the current warning-only behavior when preference recording fails; successful task creation is not rolled back.

Frontend

Wire mapping and queued overlay

  • Add workflow_ids_by_workspace?: Record<string, string> to TaskCreateLastUsedApi and workflowIdsByWorkspace: Record<string, string> to the settings store state.
  • Update apps/web/lib/ssr/user-settings.ts, boot/HTTP/WS mapping tests, and default settings so absent maps normalize to {} and existing installations remain compatible.
  • Extend the task-create queued overlay in apps/web/components/task-create-dialog-handlers.ts to derive the entry from submitted workspace_id and workflow_id. Merge queued workflow entries into, rather than replace, the authoritative map and clear the overlay only after every queued entry matches backend settings.
  • Update apps/web/components/state-provider.tsx equality logic for map contents so settings publication clears the overlay deterministically.

Workflow default resolution

  • Add a pure resolver in apps/web/components/task-create-dialog-defaults.ts for the confirmed precedence: locked workflow; current-dialog manual choice; valid visible per-workspace last-used workflow; valid visible unlocked context; sole visible workflow; otherwise no selection.
  • Keep unlocked board/list workflowId as fallback context rather than seeding it as an authoritative form selection. Preserve explicit locked flows by passing the existing lockedFields.workflow signal through form reset and late-value synchronization.
  • Thread taskCreateLastUsed.workflowIdsByWorkspace[workspaceId] and user settings readiness through useTaskCreateDialogData / useDialogComputed. Validate every candidate against the visible workflows in the current workspace before returning effectiveWorkflowId.
  • Make workflow-step loading and workflow-agent override effects consume the resolved effective workflow so a restored default receives the same steps and agent lock as a manual selection.
  • Remove the misleading component-local lastUsedWorkflowId sorting state; durable task-create recency now comes from backend settings and option order remains workflow sort_order.

Mobile design contract

  • Desktop and mobile outcome: standard Create Task restores the same workspace-specific workflow regardless of viewport or current filter.
  • Nearest shipped mobile exemplar: the existing Kanban FAB → Create Task dialog remains the entry point and presentation baseline.
  • Presentation: no overlay, navigation, hierarchy, scroll owner, safe-area, focus, or touch-target changes. Exactly-one-workflow suppression remains shared across viewports.
  • Shared logic: wire mapping, queued overlay, and default resolution stay in shared state/hooks; no mobile-specific preference is introduced.
  • Mobile proof: focused unit tests cover the shared resolver and the existing mobile Kanban workflow-context E2E continues to cover dialog reachability. No new mobile-only Playwright scenario is required because the change is state normalization inside unchanged composition.

Tests

  • What: HTTP and WebSocket task creation record the effective workflow under the correct workspace key.
    • File: apps/backend/internal/task/handlers/task_http_handlers_test.go (the existing file covers both HTTP and WebSocket task-create handlers).
    • How: extend recorder-capture tests for both transports and assert the workspace-to-workflow entry alongside existing profile/repository fields.
  • What: targeted task-create preference writes preserve workflow entries for other workspaces and all existing last-used fields on SQLite and PostgreSQL query paths.
    • File: apps/backend/internal/user/store/sqlite_test.go.
    • How: real SQLite repository test plus PostgreSQL query-builder assertions using two workspace keys and partial patches.
  • What: service, DTO, event, and boot payload treat workflow-only history as loaded task-create state.
    • File: apps/backend/internal/user/service/service_test.go and apps/backend/internal/backendapp/boot_state_routes_test.go.
    • How: record a workflow-only patch and assert the published/boot map and synced state.
  • What: the frontend mapper and queued overlay preserve multiple workspace entries and clear only after backend convergence.
    • File: apps/web/lib/ssr/user-settings.test.ts, apps/web/hooks/use-ensure-user-settings.test.ts, apps/web/components/state-provider.test.tsx, and apps/web/components/task-create-dialog-handlers.test.ts.
    • How: map missing and populated wire values, merge two workspace entries, and exercise stale/fresh settings publication.
  • What: workflow resolution prefers per-workspace last-used state over an unlocked conflicting filter, while manual and locked selections win and invalid history falls through.
    • File: apps/web/components/task-create-dialog-defaults.test.ts and focused dialog state/effect tests.
    • How: table-driven pure resolver cases plus hook coverage proving steps and workflow-agent overrides follow the effective workflow.
  • What: one visible workflow remains implicit with no selector.
    • File: existing apps/web/components/task-create-dialog-form-body.test.tsx and apps/web/e2e/tests/task/create-task.spec.ts regressions.
    • How: retain the current unit and E2E assertions unchanged unless wiring changes require fixture updates.

E2E Tests

  • Scenario: GIVEN workspace A remembers Dev while its board is filtered to PR Review, WHEN standard Create Task opens, THEN Dev is selected.
    • File: apps/web/e2e/tests/task/create-task.spec.ts.
    • What to verify: create the remembered task through the real HTTP API, persist the conflicting filter, open the dialog through the regular UI, and assert the visible workflow trigger text.
  • Scenario: GIVEN workspace A remembers Dev and workspace B remembers Support, WHEN the user opens standard Create Task in each workspace, THEN each workspace restores its own workflow.
    • File: apps/web/e2e/tests/task/create-task.spec.ts.
    • What to verify: seed both histories through successful task creation, navigate between workspaces, and assert each dialog independently after a page reload so the Go boot payload and frontend mapper are exercised.
  • Scenario: GIVEN a single visible workflow, WHEN Create Task opens, THEN no workflow selector appears.
    • File: existing apps/web/e2e/tests/task/create-task.spec.ts scenario.
    • What to verify: keep the existing hidden-workflow-plus-one-visible regression green.

Verification Results

All planned checks pass:

  • Backend: cd apps/backend && go test ./internal/user/store ./internal/user/service ./internal/task/handlers ./internal/backendapp — all four packages passed.
  • Frontend: cd apps/web && pnpm test -- --run lib/ssr/user-settings.test.ts lib/ws/handlers/users.test.ts hooks/use-ensure-user-settings.test.ts components/state-provider.test.tsx components/task-create-dialog-handlers.test.ts components/task-create-dialog-defaults.test.ts components/task-create-dialog-state.test.ts components/task-create-dialog-effects.test.ts components/task-create-dialog-form-body.test.tsx — 9 files, 158 tests passed.
  • Frontend typecheck: cd apps/web && pnpm run typecheck — passed.
  • E2E focused production-build run: cd apps/web && pnpm e2e:run tests/task/create-task.spec.ts -- --grep "remembered workflow" — 2 tests passed.
  • E2E selector/regression run: cd apps/web && pnpm e2e:run --no-build tests/task/create-task.spec.ts -- --grep "single visible workflow|remembered workflow" — 3 tests passed.
  • E2E full task-create spec: cd apps/web && pnpm e2e:run --no-build tests/task/create-task.spec.ts — 15 tests passed in 39 seconds.
  • Managed E2E runners exited cleanly and removed their isolated /tmp/kandev-e2e-* worker directories; no failure artifacts were produced.

Implementation Waves And Parallel Candidates

Execution is sequential in the primary conversation because the frontend wire contract depends on the backend field and the E2E scenario depends on both. No task is marked parallel-safe and no subagent delegation is authorized.

Wave 1:

Wave 2:

Wave 3:

Risks And Out Of Scope

  • Nested JSON path construction must remain parameterized and safe for both SQLite and PostgreSQL; replacing the complete map would reintroduce lost updates across workspaces.
  • Unlocked caller context currently doubles as form state. Separating fallback context from manual/locked selection must preserve edit and Improve Kandev locked flows.
  • User settings can arrive after the dialog opens. The resolver must not overwrite a manual selection made in the current open cycle when settings settle.
  • Workflow deletion does not eagerly prune history; validation ignores stale IDs and the next successful creation overwrites that workspace entry.
  • Changing the board/list filter, dialog composition, workflow ordering, and cancelled-dialog persistence remain out of scope.