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]stringwith JSON keyworkflow_ids_by_workspacetoapps/backend/internal/user/models/models.go:TaskCreateLastUsed. - Keep the field inside the existing
users.settings.task_create_last_usedJSON 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.goand preserve it through DTO, HTTP response, WebSocket event, and boot-state mapping. - Extend
apps/backend/internal/backendapp/boot_state_routes.goso 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
makeTaskCreateLastUsedJSONSetArgsinapps/backend/internal/user/store/sqlite.goto update one nestedworkflow_ids_by_workspace.<workspace_id>entry at a time. - Extend
applyPostgresTaskCreateLastUsedPatchwith equivalent parameterizedjsonb_setpaths. 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
buildTaskCreateLastUsedPatchinapps/backend/internal/task/handlers/task_http_handlers.goto addbody.WorkflowIDunderbody.WorkspaceIDafter successful creation. - Update the WebSocket task-create bridge in
apps/backend/internal/task/handlers/task_ws_handlers.goto passreq.WorkspaceIDandreq.WorkflowIDinto 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>toTaskCreateLastUsedApiandworkflowIdsByWorkspace: 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.tsto derive the entry from submittedworkspace_idandworkflow_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.tsxequality 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.tsfor 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
workflowIdas fallback context rather than seeding it as an authoritative form selection. Preserve explicit locked flows by passing the existinglockedFields.workflowsignal through form reset and late-value synchronization. - Thread
taskCreateLastUsed.workflowIdsByWorkspace[workspaceId]and user settings readiness throughuseTaskCreateDialogData/useDialogComputed. Validate every candidate against the visible workflows in the current workspace before returningeffectiveWorkflowId. - 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
lastUsedWorkflowIdsorting state; durable task-create recency now comes from backend settings and option order remains workflowsort_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.
- File:
- 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.
- File:
- What: service, DTO, event, and boot payload treat workflow-only history as
loaded task-create state.
- File:
apps/backend/internal/user/service/service_test.goandapps/backend/internal/backendapp/boot_state_routes_test.go. - How: record a workflow-only patch and assert the published/boot map and
syncedstate.
- File:
- 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, andapps/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.
- File:
- 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.tsand focused dialog state/effect tests. - How: table-driven pure resolver cases plus hook coverage proving steps and workflow-agent overrides follow the effective workflow.
- File:
- What: one visible workflow remains implicit with no selector.
- File: existing
apps/web/components/task-create-dialog-form-body.test.tsxandapps/web/e2e/tests/task/create-task.spec.tsregressions. - How: retain the current unit and E2E assertions unchanged unless wiring changes require fixture updates.
- File: existing
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.
- File:
- 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.
- File:
- 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.tsscenario. - What to verify: keep the existing hidden-workflow-plus-one-visible regression green.
- File: existing
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.