126 lines
7.0 KiB
Markdown
126 lines
7.0 KiB
Markdown
---
|
|
spec: docs/specs/tasks/requirements/task-create-executor-default.md
|
|
created: 2026-08-01
|
|
status: complete
|
|
---
|
|
|
|
# Implementation Plan: Task Create Executor Default
|
|
|
|
## Overview
|
|
|
|
Change the task-create selection order so source/workspace policy resolves the executor before a
|
|
portable last-used profile is considered. Lock the behavior down first with focused Vitest
|
|
regressions, then prove the backend-settings/cross-browser path through the existing Create Task
|
|
Playwright flow. No backend, schema, API, or rendered-layout change is required.
|
|
|
|
## Confirmed root cause
|
|
|
|
`pickDefaultExecutorProfileId` accepts any eligible `lastUsedExecutorProfileId` before calling
|
|
`pickDefaultExecutorId`. For an ordinary repository-backed task, a valid saved Local profile is
|
|
therefore returned before the no-workspace-default Worktree fallback is resolved. The executor-ID
|
|
effect also waits for any valid saved profile and then derives the executor from that profile,
|
|
making the Local selection portable across browsers and updates by design.
|
|
|
|
## Frontend
|
|
|
|
### Executor policy and profile restoration
|
|
|
|
- Update `apps/web/components/task-create-dialog-effects.ts` so
|
|
`pickDefaultExecutorProfileId` first obtains the executor selected by `pickDefaultExecutorId`.
|
|
- Restore `lastUsedExecutorProfileId` only when its flattened profile candidate belongs to that
|
|
resolved executor.
|
|
- Prefer the resolved executor's first profile when the saved profile belongs to another executor.
|
|
Preserve the current first-eligible fallback if the resolved executor has no usable profile.
|
|
- Update `shouldWaitForLastUsedExecutorProfile` so executor-ID auto-pick waits only for a saved
|
|
profile compatible with the resolved executor. A saved Local profile must not suppress the
|
|
Worktree executor-ID fallback for an ordinary repository-backed task.
|
|
- Preserve the current task-source rules in `pickDefaultExecutorId`: explicit unmanaged local
|
|
paths and repository-less tasks prefer direct Local; a valid workspace default wins; otherwise
|
|
`DEFAULT_LOCAL_EXECUTOR_TYPE` (`worktree`) is preferred.
|
|
- Keep task-create persistence and submit handlers unchanged. A manual selection still applies to
|
|
the current task and is still recorded by the backend.
|
|
|
|
### Mobile design contract
|
|
|
|
- **Desktop and mobile outcome:** the existing Executor Profile selector shows the same
|
|
policy-resolved profile for the same task source and workspace settings.
|
|
- **Nearest shipped mobile exemplar:** the existing mobile Create Task dialog remains the
|
|
composition and interaction baseline.
|
|
- **Presentation and hierarchy:** no markup, overlay, scrolling, safe-area, focus, or touch target
|
|
changes; only the shared selection state changes.
|
|
- **Shared logic:** desktop and mobile already call the same task-create effects, so the fix stays
|
|
in that shared hook.
|
|
- **Mobile proof:** focused unit coverage is sufficient because this is state normalization inside
|
|
the unchanged dialog. Existing mobile task-create E2E remains the layout/touch coverage; no new
|
|
mobile-only interaction is introduced.
|
|
|
|
## Tests
|
|
|
|
- **What:** a saved Local profile cannot override the Worktree fallback for an ordinary
|
|
repository-backed task with no workspace default.
|
|
- **File:** `apps/web/components/task-create-dialog-effects-executor.test.ts`
|
|
- **How:** render `useDefaultSelectionsEffect` with Local and Worktree executors, loaded backend
|
|
settings, and `lastUsedExecutorProfileId` set to Local; assert Worktree profile and executor ID.
|
|
- **What:** an explicit Local workspace default remains authoritative.
|
|
- **File:** `apps/web/components/task-create-dialog-effects-executor.test.ts`
|
|
- **How:** use the same executor set with `default_executor_id` set to Local and a saved Worktree
|
|
profile; assert Local profile and executor ID.
|
|
- **What:** source constraints remain authoritative for an explicit local path and repository-less
|
|
task.
|
|
- **File:** `apps/web/components/task-create-dialog-effects-executor.test.ts`
|
|
- **How:** extend the existing local-path/no-repository cases with a saved Worktree profile and
|
|
assert a direct Local profile.
|
|
- **What:** a saved alternative Worktree profile is still restored when Worktree is the resolved
|
|
executor, and missing-profile fallback behavior remains unchanged.
|
|
- **File:** `apps/web/components/task-create-dialog-effects-executor.test.ts`
|
|
- **How:** retain the existing restoration case and add/adjust table cases around resolved
|
|
executor membership.
|
|
|
|
## E2E Tests
|
|
|
|
- **Scenario:** **GIVEN** backend user settings contain a last-used Local profile and the workspace
|
|
has no executor default, **WHEN** Create Task opens for the seeded repository in a browser
|
|
context, **THEN** the Executor Profile selector shows Worktree.
|
|
- **File:** `apps/web/e2e/tests/task/create-task-executor-default.spec.ts`
|
|
- **What to verify:** seed the Local profile through `saveUserSettings`, navigate through the
|
|
regular Kanban UI, open the dialog, and assert the visible Worktree selection.
|
|
- **Scenario:** **GIVEN** the workspace explicitly defaults to Local, **WHEN** Create Task opens for
|
|
the seeded repository, **THEN** the selector shows Local even when the saved profile is Worktree.
|
|
- **File:** `apps/web/e2e/tests/task/create-task-executor-default.spec.ts`
|
|
- **What to verify:** set and later restore `default_executor_id`, assert the visible Local
|
|
profile, and restore the backend last-used profile to Worktree so the worker-scoped fixture
|
|
does not leak state.
|
|
|
|
## Implementation Tasks
|
|
|
|
- [x] [Task 01: Enforce executor-first profile selection](task-01-executor-first-selection.md)
|
|
- [x] [Task 02: Prove portable settings behavior in Create Task](task-02-create-task-e2e.md)
|
|
|
|
Execution is sequential in the primary conversation. No subagent delegation is planned or
|
|
authorized.
|
|
|
|
## Validation
|
|
|
|
Run from the repository root unless noted otherwise:
|
|
|
|
- `cd apps/web && pnpm test -- --run components/task-create-dialog-effects-executor.test.ts components/task-create-dialog-executor-wait.test.ts`
|
|
- `cd apps/web && pnpm run typecheck`
|
|
- `cd apps/web && pnpm e2e:run --project chromium tests/task/create-task-executor-default.spec.ts`
|
|
|
|
Completed validation: 14 focused Vitest tests passed, the web typecheck passed, targeted ESLint and
|
|
Prettier checks passed, and the managed Chromium run passed both Create Task scenarios.
|
|
|
|
The managed E2E runner performs the required production build before Playwright. This worktree ran
|
|
`cd apps && pnpm install --frozen-lockfile` before the recorded validation; fresh worktrees must run
|
|
that prerequisite if frontend dependencies are missing.
|
|
|
|
## Risks
|
|
|
|
- The executor-ID and executor-profile effects run independently and settle through microtasks.
|
|
Tests must assert the final paired selection, not only one setter call, so a transient mismatch
|
|
cannot become a regression.
|
|
- Existing E2E fixtures preserve task-create last-used fields on partial settings writes. New tests
|
|
must restore a Worktree profile, and tests that set a workspace default must clear it in cleanup.
|
|
- The single stored last-used profile cannot retain a preferred profile for multiple executor
|
|
types; adding per-executor history remains explicitly out of scope.
|