feat: preview workflow moves before execution (#3677)

This commit is contained in:
Carlos Florêncio
2026-09-16 11:16:47 +01:00
committed by GitHub
parent 031c1b4b8d
commit 044a82367b
55 changed files with 5868 additions and 133 deletions
+263
View File
@@ -0,0 +1,263 @@
---
created: 2026-09-14
status: implemented
requirements:
- REQ-TASKS-WORKFLOW-MOVE-PREVIEW-001
- REQ-TASKS-WORKFLOW-MOVE-PREVIEW-002
system_design:
- ../../specs/tasks/system-design/workflow-move-preview.md
legacy_specs: []
---
# Implementation Plan: Workflow Move Preview
## Overview
Add compact task-specific recipient and model feedback before a step move.
Deliver a read-only shared backend decision first, then integrate the desktop
popover and phone drawer with end-to-end move verification. The backend boundary
needs isolated side-effect and routing-parity proof before any UI depends on it.
Confirmed user choices: two summary lines, current/other/new conversation,
unchanged or before/after model, additional settings count, optional details.
The preview includes conditional Codex model changes without profile changes.
## Scope
### In scope
- Task stepper and compact step disclosure, including task preview use sites.
- Read-only move prediction using runtime overrides and entry options.
- Conditional set, keep, restore-original and no-match behavior.
- Loading, retry, uncertainty, accessibility, five locales, and phone composition.
### Out of scope
- Changes to session routing policy, profile defaults, provider acceptance,
native subagents, workflow editing, or mandatory move confirmation.
- Board drag, bulk moves, and unrelated move menus.
- Production instance mutation or live provider probes during development.
## Technical approach
Follow the [design](../../specs/tasks/system-design/workflow-move-preview.md).
Extract shared read-only decisions from orchestrator workflow routing and
conditional configuration. Add the proposed task move-preview endpoint and
DTO, then a domain API client/hook and shared renderer. Keep execution-time
atomic checks and existing move semantics authoritative.
Nearest shipped mobile surface: `workflow-step-disclosure.tsx`.
Curated baseline: MobilePickerSheet and the mobile UI language's temporary
choice drawer. Use its fixed header, internal scroll, safe-area clearance,
focus return, and shared state logic.
No schema or persistent preview cache. The design carries the endpoint contract;
no separate architecture decision is needed for an advisory projection of the
existing lifecycle. Public documentation changes belong to implementation.
## ASCII UI preview
Structural requirements: exactly two centered collapsed summary rows below actions and capabilities;
an icon-only info button expands details below the summary; Options and existing capability/progress
content stay available. Spacing and abbreviated data below are illustrative.
All drawn strings become localized copy or model/session data.
### UI-01: Desktop step hover or keyboard disclosure
AC-TASKS-WORKFLOW-MOVE-PREVIEW-001.1 through .5 and -002.1, .4.
```text
+--------------------------------+
| -> Move here |
| Options |
| [step capabilities] |
|--------------------------------|
| Reuse current session |
| Astra -> Luna +2 (i) |
+--------------------------------+
Expanded details appear beneath the footer.
Other outcomes: New session; Reuse: Implementation; No session starts.
Unchanged model: Astra (override retained), or Luna without an override.
```
The footer layout was revised after implementation with user approval. The
next-step options form above chat uses the same footer below its Move action.
`+2` means reasoning and context reset here, excluding the already displayed
model change. A model-only change has no count. Details disclose uncertainty,
skipped rules, full labels, profile, and source retirement as applicable.
### UI-02: Phone step drawer
Entry: tap the existing current-step disclosure. The drawer keeps its fixed
header and one scroll body. AC-TASKS-WORKFLOW-MOVE-PREVIEW-002.2, .4.
```text
+--------------------------------+
| Move to x | fixed header
|--------------------------------|
| Analysis (current) |
|--------------------------------|
| Implement |
| [Options] [Move here] |
| Reuse current session |
| Astra -> Luna +1 (i) |
|--------------------------------|
| Review |
| [Options] [Move here] |
| Reuse: Analysis |
| Astra (i) |
|________________________________| safe-area clearance
```
Details expand in the same scroll body, immediately after the summary. All touch
actions have at least 44px hit areas. No stacked overlays or horizontal scrolling.
Same projection and move draft serve desktop and phone.
### UI-03: Loading, error, unknown
AC-TASKS-WORKFLOW-MOVE-PREVIEW-001.5, .7 and -002.3.
```text
+-------------------------+ +-------------------------+
| Move here | | Move here |
| Options | | Options |
|-------------------------| |-------------------------|
| Checking session... | | Preview unavailable |
| | | Retry |
+-------------------------+ +-------------------------+
+-------------------------+
| Move here |
| Options |
|-------------------------|
| New session |
| Model not known (i) |
+-------------------------+
```
Loading, error, or unknown alone does not disable Move here. Existing permission,
pending-move, and task gates still apply. Phone rows use these same statuses.
## Tests
| Criteria | Planned evidence |
| --- | --- |
| 001.1, 001.2 | workflow_move_preview_test.go: PreviewMatchesMove, SameProfileOverride, ExplicitTargets, TerminalCandidates, NewSessionDefaults |
| 001.3, 001.4, 001.5 | workflow_move_preview_test.go: ConditionalSetKeepRestore, OriginalEligibility, AmbiguousRules, UnknownCapabilities, DeduplicatedChanges |
| 001.6 | workflow_move_preview_test.go: NoSideEffects; workflow_move_preview_http_test.go: AuthorizationAndRedaction |
| 001.7, 002.3 | use-workflow-move-preview.test.ts: LateResponse, DraftChanges, ReopenReconnect, Retry; deferred-move backend fixture |
| 002.1, 002.2, 002.4 | workflow-move-preview.test.tsx and workflow-stepper.test.tsx: summary/details, keyboard, counts, localization; phone E2E |
## E2E tests
New `tests/workflow/workflow-move-preview.spec.ts` (chromium) covers a retained
session model override during an in-place move, reuse of another named session,
and creation of a fresh profiled session. Each scenario inspects the preview,
executes the move, and checks the resulting session identity or model against
isolated fixture API state. Unit and integration tests cover conditional
settings, reset and skipped states, no-session launch gates, authorization,
fresh-launch configuration projection, and stale-response handling.
New `tests/workflow/mobile-workflow-move-preview.spec.ts` (mobile-chrome) covers
the coarse-pointer touch drawer, inline details, 44px controls, focus return on
dismissal, and no horizontal overflow. The scenario uses the repository's
tablet context because the full phone task route uses `SessionMobileLayout` and
does not render the desktop task stepper. Existing step-targeting and move-option
suites provide the adjacent mobile regression evidence. No production DB or task
is used.
## Work orders
- [x] [Task 01: Read-only move decisions and API](task-01-move-decision-api.md)
- [x] [Task 02: Compact preview and move verification](task-02-compact-preview.md)
Order: 01 -> 02. Both are sequential. No delegation is authorized by this plan.
## Verification results
Design checks passed on 2026-09-14:
- `python3 scripts/list-docs.py validate`: 267 decisions and 911 specifications validated.
- `python3 scripts/lint-spec-files.py --all`: all specification files passed.
- `python3 scripts/lint-spec-files.test.py`: 36 tests passed.
- Relative-link, acceptance-reference, and whitespace checks passed for all five files.
- `git diff --check` passed; scoped git status confirmed all five new artifacts.
Implementation is complete and both work orders are complete.
- Backend package tests and the targeted race suite passed.
- Frontend focused tests passed with 44 tests across five files. Typecheck,
full lint, localization checks, and the new-code localization ratchet passed.
- `make -C apps/backend build` and `pnpm run build:e2e` passed.
- Desktop workflow E2E passed all 12 tests. Mobile workflow E2E passed all 5
tests after the final drawer exit-state assertion was stabilized.
- Specification, public-doc, and diff checks passed.
The mobile browser scenario uses the existing coarse-pointer tablet context to
exercise `CompactWorkflowStepDisclosure`. The full Pixel 5 task route uses the
separate `SessionMobileLayout` and does not render the desktop task stepper.
This is a test-surface detail; the shared touch drawer remains the production
mobile disclosure used by the task preview and tablet task surfaces.
### Review remediation addendum
The implementation evidence was extended after code-only review. Backend preview
recipient selection now uses the same no-session launch predicate as execution,
including the no-auto-start and skip-without-instructions gates. The preview
returns `no_session` without a recipient when the task remains idle, and keeps
the task-level profile fallback when an allowed fresh launch can occur.
The preview endpoint authorizes the task before reading task state. Existing
source sessions retain current-session routing when no destination profile is
configured, while profile changes can predict a fresh target session even when
the destination does not auto-start a prompt. Fresh launches project applicable
session settings, and context reset and mode changes report skipped states when
execution cannot apply them. Busy reusable targets report deferred dispatch,
and no-session dispatch has its own localized detail label.
Open previews now invalidate from live store revisions and connection changes,
including session model and fallback updates, clear stale success while
refreshing, and retain late-response guards. Every movable disclosure row can
receive a preview through a two-request queue. Typed diagnostics and Kandev-owned
fields and values render through the active locale; provider option labels remain
bounded display data.
Additional remediation checks passed on 2026-09-15:
- `go test ./internal/workflow/move ./internal/orchestrator`.
- `make lint` from `apps/backend`.
- Focused Vitest: 3 files, 13 tests passed.
- `pnpm run typecheck`, `pnpm run lint`, `pnpm run i18n:check`, and
`pnpm run i18n:ratchet` from `apps/web`.
Final post-remediation verification passed on 2026-09-15:
- Focused Vitest passed all 50 tests across six affected files.
- `make build` from `apps/backend` and `pnpm run build:e2e` from `apps/web`.
- The focused desktop move-preview spec passed 2 tests, and the focused mobile
move-preview spec passed 1 test.
- `go test -race ./internal/orchestrator ./internal/task/handlers -run 'Preview|WorkflowStepSession|WorkflowSessionConfig|WorkflowSessionTarget|SameProfile'`.
- `go test -race ./internal/orchestrator -count=1`, including the completed-task
follow-up admission path.
- `go test ./internal/orchestrator -run '^TestCompletedTaskFollowUpAdmissionIsConversationalOnly$' -count=100 -failfast`.
- Specification, public-doc, formatting, and localization checks passed after
trimming unrelated generated catalog churn.
### Approved footer revision (2026-09-15)
- Centered the preview below step actions and capabilities, with an info icon.
- Added the same live-draft footer to chat and passthrough next-step options.
- Passed 79 focused unit/component tests, typecheck, and changed-file lint.
- Passed two desktop and two touch browser scenarios covering footer placement,
next-step submission, inline details, and 44px touch targets.
- Updated the requirements, design, work order, and public workflow guide.
## Risks
- Selection helpers can write provenance or warnings; preview must isolate pure decisions.
- Live provider rejection and deferred moves cannot be guaranteed by a hover snapshot.
- Runtime configuration can differ from mutable profile rows.
- Read-only snapshots and WS updates race; stale-generation tests are required.
- Long translations can crowd the compact surface; full details stay accessible.
@@ -0,0 +1,141 @@
---
id: "01-move-decision-api"
title: "Read-only move decisions and API"
status: completed
wave: 1
depends_on: []
plan: "plan.md"
requirements:
- REQ-TASKS-WORKFLOW-MOVE-PREVIEW-001
- REQ-TASKS-WORKFLOW-MOVE-PREVIEW-002
acceptance_criteria:
- AC-TASKS-WORKFLOW-MOVE-PREVIEW-001.1
- AC-TASKS-WORKFLOW-MOVE-PREVIEW-001.2
- AC-TASKS-WORKFLOW-MOVE-PREVIEW-001.3
- AC-TASKS-WORKFLOW-MOVE-PREVIEW-001.4
- AC-TASKS-WORKFLOW-MOVE-PREVIEW-001.5
- AC-TASKS-WORKFLOW-MOVE-PREVIEW-001.6
- AC-TASKS-WORKFLOW-MOVE-PREVIEW-001.7
system_design:
- ../../specs/tasks/system-design/workflow-move-preview.md
---
# Task 01: Read-only move decisions and API
## Summary
Provide a task-specific preview of recipient and expected configuration without
executing routing or ACP operations. Reuse decisions in execution paths so the
endpoint and actual moves remain consistent.
## In scope
Shared recipient decision extraction, effective configuration and conditional
rule projection, normalized one-shot reset/dispatch effects, DTO, authorized
HTTP endpoint, dependency wiring, and focused backend regression fixtures.
## Out of scope
Rendered UI, schema migrations, changed lifecycle policy, provider probes,
and caching or reserving preview selections.
## Acceptance
1. Real fixture moves match their previews for same/other/new session cases,
explicit targets, overrides, and conditional settings; terminal exclusion holds.
2. Repeated previews leave session/task metadata, bindings, messages, and runtime
invocation counters unchanged. Authorization and safe DTO filtering hold.
3. Unknown/skipped/planned configuration is explicit; existing atomic route,
retry, deferred-move, and best-effort provider semantics remain intact.
## Verification
Run from repository root. Add named test files before running these commands;
use TDD to demonstrate the missing preview behavior first.
```bash
(cd apps/backend && go test ./internal/orchestrator ./internal/task/handlers ./internal/task/service ./internal/workflow/...)
(cd apps/backend && go test -race ./internal/orchestrator ./internal/task/handlers -run 'Preview|WorkflowStepSession|WorkflowSessionConfig|WorkflowSessionTarget|SameProfile')
python3 scripts/list-docs.py validate
python3 scripts/lint-spec-files.py --all
git diff --check
```
## Files likely touched
- `apps/backend/internal/orchestrator/workflow_move_preview.go`,
`workflow_move_preview_settings.go`, and `_test.go` (new).
- `apps/backend/internal/orchestrator/event_handlers_workflow.go`.
- `apps/backend/internal/orchestrator/workflow_session_target.go`.
- `apps/backend/internal/orchestrator/workflow_session_config.go`.
- `apps/backend/internal/task/handlers/workflow_move_preview_http.go` and `_test.go` (new).
- `apps/backend/internal/task/handlers/task_handlers.go`, `task_http_handlers.go`.
- `apps/backend/internal/task/dto/` and existing backendapp dependency wiring.
## Dependencies
None. Read both paired specifications and current lifecycle/conditional-settings
contracts. Existing same-profile policy and session-target tests are exemplars.
## Risks
A helper named resolve can mutate state. Review every reachable call; extract
pure inputs/outputs and keep warning publication in execution only.
## Parallelism
`sequential`
## Inputs
[Design](../../specs/tasks/system-design/workflow-move-preview.md), especially
Resolution, API, and Freshness and failures. [Plan test matrix](plan.md#tests).
## Results
Implemented the read-only workflow move decision layer and the authorized
`POST /api/v1/tasks/:id/move-preview` endpoint. Recipient selection shares the
pure reusable-session selector with execution, preserves explicit targets and
terminal-session exclusion, projects runtime configuration and conditional
rules without writes, and reports unknown or deferred outcomes explicitly.
Verification passed:
- `cd apps/backend && go test ./internal/orchestrator ./internal/task/handlers ./internal/task/service ./internal/workflow/...`
- `cd apps/backend && go test -race ./internal/orchestrator ./internal/task/handlers -run 'Preview|WorkflowStepSession|WorkflowSessionConfig|WorkflowSessionTarget|SameProfile'`
- `python3 scripts/list-docs.py validate`
- `python3 scripts/lint-spec-files.py --all`
- `git diff --check`
## Review remediation results
The preview now shares `workflowmove.ShouldAutoStartAgent` with the actual
no-session launch handler. A destination without `auto_start_agent`, or a
`skip_step_prompt` move without instructions, returns `no_session` with no
recipient. A permitted sessionless launch still resolves the task-level agent
profile when the destination has no step profile. Provider option labels are
bounded display labels, while Kandev-owned setting fields keep stable keys for
localization. The endpoint authorizes the task before repository access, keeps
current-session routing when only the task profile fallback exists, predicts
profile switches from an existing source session, and projects applicable
configure-session rules for a fresh launch. Busy reusable targets report
deferred dispatch, while fresh context and passthrough mode changes report
skipped applicability.
Regression coverage compares both idle launch gates with the actual move path,
checks task-profile fallback for an allowed launch, and verifies the setting
label DTO shape, task authorization ordering, typed configuration notices, and
busy-target dispatch.
Remediation verification passed on 2026-09-15:
- `cd apps/backend && go test ./internal/workflow/move ./internal/orchestrator`
- `cd apps/backend && make lint`
Final post-remediation verification also passed for the targeted backend race
suite and the managed backend build:
- `cd apps/backend && go test -race ./internal/orchestrator ./internal/task/handlers -run 'Preview|WorkflowStepSession|WorkflowSessionConfig|WorkflowSessionTarget|SameProfile'`
- `cd apps/backend && go test -race ./internal/orchestrator -count=1`
- `cd apps/backend && go test ./internal/orchestrator -run '^TestCompletedTaskFollowUpAdmissionIsConversationalOnly$' -count=100 -failfast`
- `cd apps/backend && make build`
@@ -0,0 +1,194 @@
---
id: "02-compact-preview"
title: "Compact preview and move verification"
status: completed
wave: 2
depends_on:
- "01-move-decision-api"
plan: "plan.md"
requirements:
- REQ-TASKS-WORKFLOW-MOVE-PREVIEW-001
- REQ-TASKS-WORKFLOW-MOVE-PREVIEW-002
acceptance_criteria:
- AC-TASKS-WORKFLOW-MOVE-PREVIEW-001.1
- AC-TASKS-WORKFLOW-MOVE-PREVIEW-001.2
- AC-TASKS-WORKFLOW-MOVE-PREVIEW-001.3
- AC-TASKS-WORKFLOW-MOVE-PREVIEW-001.4
- AC-TASKS-WORKFLOW-MOVE-PREVIEW-001.5
- AC-TASKS-WORKFLOW-MOVE-PREVIEW-001.6
- AC-TASKS-WORKFLOW-MOVE-PREVIEW-001.7
- AC-TASKS-WORKFLOW-MOVE-PREVIEW-002.1
- AC-TASKS-WORKFLOW-MOVE-PREVIEW-002.2
- AC-TASKS-WORKFLOW-MOVE-PREVIEW-002.3
- AC-TASKS-WORKFLOW-MOVE-PREVIEW-002.4
system_design:
- ../../specs/tasks/system-design/workflow-move-preview.md
---
# Task 02: Compact preview and move verification
## Summary
Render the shared prediction in the step hover and mobile drawer. Verify the
visible prediction against actual moves using isolated backend fixtures.
## In scope
Domain client/hook, scoped invalidation, late-response guards, two-line renderer,
inline details, same draft for preview/move, all locales, focused component and
browser coverage, and public workflow guidance.
## Out of scope
Workflow editing, new routing rules, board drag/bulk-move previews, mandatory
confirmation, or direct provider probing.
## Acceptance
1. UI-01 and UI-03 remain compact, keyboard accessible, localized, and accurate
through overrides, model changes, option changes, unknowns, and transport races.
2. UI-02 exposes equivalent predictions and inline details with 44px touch actions,
one scroll owner, focus return, and viewport containment.
3. Browser tests verify preview versus actual recipient/model, including new and
reused sessions. Documentation explains predictions and execution-time checks.
## ASCII UI preview
UI-01 desktop and UI-02 phone excerpts; see [full previews](plan.md#ascii-ui-preview)
for UI-03 loading/error, all variants, structural notes, and criterion mappings.
```text
Desktop Phone drawer
+---------------------------+ +------------------------------+
| Move here | | Move to x |
| Options | |------------------------------|
| [step capabilities] | | Implement |
|---------------------------| | [Options] [Move here] |
| Reuse current session | |------------------------------|
| Astra -> Luna +1 (i) | | Reuse current session |
+---------------------------+ | Astra -> Luna +1 (i) |
+------------------------------+
```
The approved footer also appears below the Move action in the next-step
options popover and drawer above chat. It uses that action's destination and draft.
Collapsed summaries stay two centered lines. Details expand inline; phone rows share
one internal scroll region. The +1 denotes a non-model setting change here.
AC-TASKS-WORKFLOW-MOVE-PREVIEW-002.1 through .4 apply.
## Verification
Commands run from repository root. Bootstrap this worktree before the first
pnpm check. Managed E2E commands build fresh artifacts and run sequentially.
The new test files below are implementation outputs, not existing tests.
```bash
(cd apps && pnpm install --frozen-lockfile)
(cd apps/web && pnpm test hooks/domains/kanban/use-workflow-move-preview.test.ts components/task/workflow-move-preview.test.tsx components/task/workflow-stepper.test.tsx)
(cd apps/web && pnpm run typecheck && pnpm run lint && pnpm run i18n:check && pnpm run i18n:ratchet)
(cd apps/web && pnpm e2e:run tests/workflow/workflow-move-preview.spec.ts tests/workflow/workflow-session-targeting.spec.ts tests/workflow/workflow-step-move-overrides.spec.ts -- --project=chromium)
(cd apps/web && pnpm e2e:run tests/workflow/mobile-workflow-move-preview.spec.ts tests/workflow/mobile-workflow-session-targeting.spec.ts tests/workflow/mobile-workflow-step-move-overrides.spec.ts -- --project=mobile-chrome)
python3 scripts/list-docs.py validate
python3 scripts/lint-spec-files.test.py
python3 scripts/lint-spec-files.py --all
git diff --check
```
Inspect rendered desktop and phone screenshots from these tests against UI-01
through UI-03. Record actual commands/results and any deviation. Add any other
changed unit suite to the targeted command before completion.
## Files likely touched
- `apps/web/lib/api/domains/kanban-api.ts`.
- `apps/web/hooks/domains/kanban/use-workflow-move-preview.ts`, its tests, and
`use-workflow-move-preview-revision.ts` (new).
- `apps/web/components/task/workflow-move-preview.tsx` and `.test.tsx` (new).
- `apps/web/components/task/workflow-stepper.tsx` and `.test.tsx`.
- `apps/web/components/task/workflow-step-disclosure.tsx` and disclosure actions.
- `apps/web/src/locales/` for en, pt-pt, zh-cn, zh-hk, zh-tw; generate Traditional Chinese with i18n:zh-hant.
- `apps/web/e2e/tests/workflow/workflow-move-preview.spec.ts` and `mobile-workflow-move-preview.spec.ts` (new).
- `docs/public/tasks-and-workflows.md` and paired feature documents/work-order results.
## Dependencies
Task 01 and its typed response. Reuse existing stepper, move options, session
targeting, and mobile drawer tests; do not simulate all routing in frontend mocks.
## Risks
Disclosure remounts, pinned viewed tabs, WS updates, and draft changes can race.
Preserve move-pending ownership and never render a response from another scope.
## Parallelism
`sequential`
## Inputs
[Design](../../specs/tasks/system-design/workflow-move-preview.md): Presentation,
Mobile, Freshness and failures. Use mobile-parity, TDD, E2E, and docs-maintainer
skills during implementation. No implementation delegation is authorized.
## Results
Implemented the shared preview client, debounced request hook, compact renderer,
desktop disclosure integration, and coarse-pointer drawer integration. The same
normalized options draft feeds the preview and the eventual move. All five web
locales include the new copy, and public workflow guidance describes the
advisory prediction and execution-time recheck.
Verification passed:
- `(cd apps && pnpm install --frozen-lockfile)`
- Focused Vitest: 5 files, 44 tests passed.
- `pnpm run typecheck`, `pnpm run lint`, `pnpm run i18n:check`, and `pnpm run i18n:ratchet`.
- `make -C apps/backend build` and `pnpm run build:e2e`.
- Desktop workflow E2E: 12 tests passed, including recipient reuse, fresh
sessions, retained model overrides, targeting, and move options.
- Mobile workflow E2E: 5 tests passed, including the preview drawer, session
targeting, and move-option regressions. The preview scenario uses the
repository's coarse-pointer tablet context because the Pixel 5 full-page task
route intentionally uses `SessionMobileLayout`, which has no desktop task
stepper. The exercised `CompactWorkflowStepDisclosure` branch is the shared
touch drawer used by the task preview and tablet task surfaces.
- `python3 scripts/list-docs.py validate`
- `python3 scripts/lint-spec-files.test.py`
- `python3 scripts/lint-spec-files.py --all`
- `node --test scripts/validate-public-docs.test.mjs`
- `node scripts/validate-public-docs.mjs`
- `git diff --check`
The managed build reports existing Darwin signing, deprecated Vite chunk
configuration, and large-chunk warnings; all build and browser commands exit
successfully.
## Review remediation results
The request hook now receives a store-derived revision containing relevant task,
source and destination step, session, workflow, profile, workspace, and
connection state, including per-session model and fallback data. It clears a
successful result before refetching on live updates and retains cancellation
and generation guards. Drawer rows all enqueue their previews; the shared queue
limits active requests to two and allows later rows to resolve after earlier
requests finish.
The renderer maps known notice, field, and built-in value codes to localized
copy, including a dedicated no-session dispatch state, while retaining a
generic fallback for unknown diagnostics and safe data labels for provider
options. A non-English DTO-shape test covers missing snapshot, ambiguous rule,
and inapplicable-session notices.
Remediation verification passed on 2026-09-15:
- Focused Vitest: 3 files, 13 tests passed.
- `cd apps/web && pnpm run typecheck && pnpm run lint && pnpm run i18n:check && pnpm run i18n:ratchet`
Final post-remediation verification passed on 2026-09-15:
- Focused Vitest: 6 files, 50 tests passed.
- `pnpm run build:e2e`, `pnpm run typecheck`, `pnpm run lint`,
`pnpm run i18n:check`, and `pnpm run i18n:ratchet`.
- Desktop workflow move-preview E2E: 2 tests passed.
- Mobile workflow move-preview E2E: 1 test passed.
+14
View File
@@ -48,6 +48,20 @@ Workflow position and runtime state are different. Moving a card changes its wor
During a move and while the destination agent is preparing or starting, the destination marker shows a spinner in the existing marker space. Open the existing step disclosure to see the lifecycle status and agent profile. On touch devices, these details appear in the existing **Move to** Drawer. A destination without auto-start settles after the move and remains available for a later agent start.
Before an allowed move, the step disclosure can show a compact preview of the
recipient and effective model. It identifies whether the move reuses the
current session, reuses another named session, creates a new session, or leaves
the task idle without a recipient when the destination has no launch turn. The
second line shows the current model or an expected model change and counts
additional settings changes. Use the info button for the session, profile, model,
context reset, source-session disposition, and prompt dispatch information.
The preview sits in a centered footer below the move controls. The next-step
button above chat shows the same footer in its options popover or touch drawer.
Changing the options refreshes that preview.
The preview is advisory. **Move here** checks routing, permissions, WIP, and
current session state again when the move runs. A preview can therefore change
while a turn is running or while another session becomes available.
## Move a task with one-time entry options
The normal **Move here** and next-step actions use the destination step's saved workflow defaults. When one transition needs an exception, open **Move with options** from the workflow stepper, Chat status bar, or passthrough toolbar. The options apply only to that entry and never rewrite the workflow step.
@@ -0,0 +1,110 @@
---
status: draft
system: tasks
created: 2026-09-14
owners:
- kandev
---
# Workflow Move Preview Requirements
## Overview
Before moving a task, users can see which conversation will receive the step
and which model and settings it is expected to use. The tasks system owns this
capability because it owns workflow recipient selection and step entry behavior.
A profile identity and a running model are different facts. A session launched
with a Luna profile can retain a later Astra model selection when reused.
## Terminology
- **Current session:** The conversation selected by the move's routing context,
not necessarily a separately viewed or pinned chat tab.
- **Expected configuration:** The selected session's effective settings after
applicable entry rules, subject to provider acceptance and execution-time state.
- **Preview:** A read-only prediction; it does not reserve a session or execute a move.
## Requirements
### REQ-TASKS-WORKFLOW-MOVE-PREVIEW-001: Session and settings prediction
**Intent:** Expose conversation continuity and model changes before a move.
#### Acceptance criteria
- **AC-TASKS-WORKFLOW-MOVE-PREVIEW-001.1:** Opening a movable step disclosure
shall show current-session reuse, other-session reuse with its name, new
session creation, or an idle task with no recipient according to task-specific
routing. Explicit initial and earlier-step targets, same-profile policies,
terminal candidates, and the destination's no-session launch gates shall
follow the existing routing contract.
- **AC-TASKS-WORKFLOW-MOVE-PREVIEW-001.2:** An unchanged session shall show its
effective model. Reuse of a Luna-profile session running Astra shall show
Astra and disclose the retained override. A new session shall show its
expected launch model without copying the source session's overrides.
- **AC-TASKS-WORKFLOW-MOVE-PREVIEW-001.3:** Applicable conditional settings shall
produce a before/after model summary without implying a new conversation.
Set shall retain unnamed fields; keep and no-match shall retain current
values; restore-original shall use the original effective snapshot. Rules
inapplicable to a different session or passthrough shall not predict application.
- **AC-TASKS-WORKFLOW-MOVE-PREVIEW-001.4:** Additional effective setting changes
shall appear as a count with accessible details showing field names and
before/after values. Context reset and source-session retirement shall be
disclosed. Identical values shall not count as changes. Model changes shall
not be counted again among additional changes.
- **AC-TASKS-WORKFLOW-MOVE-PREVIEW-001.5:** Missing original snapshots, ambiguous
rules, unavailable capability data, and provider-dependent outcomes shall
be identified with distinct reason states such as unavailable or skipped,
rather than one generic message. Unknown values shall not be presented as
confirmed models. Best-effort application remains best-effort.
- **AC-TASKS-WORKFLOW-MOVE-PREVIEW-001.6:** Opening or refreshing the preview
shall not create sessions, start agents, change settings, write conversation
warnings, or trigger transitions. Existing authorization shall prevent
disclosure of another workspace's sessions or sensitive configuration.
- **AC-TASKS-WORKFLOW-MOVE-PREVIEW-001.7:** Changing the task, destination, move
options, known routing/configuration state, or the connection state shall
invalidate the preview. Late responses shall not overwrite a newer selection.
Actual moves shall retain execution-time validation, including deferred moves.
### REQ-TASKS-WORKFLOW-MOVE-PREVIEW-002: Compact accessible disclosure
**Intent:** Provide useful feedback within the existing step move surface.
#### Acceptance criteria
- **AC-TASKS-WORKFLOW-MOVE-PREVIEW-002.1:** The collapsed desktop preview shall
use two centered summary lines below actions and capabilities: session outcome,
then model/settings with an icon-only details button.
Details shall remain opt-in. Long labels shall truncate with full accessible
text. Existing Options and capability/progress content shall remain usable.
The next-step options popover and touch drawer shall show the same footer
below their Move action, using the destination and draft of that action.
- **AC-TASKS-WORKFLOW-MOVE-PREVIEW-002.2:** Hover, keyboard focus, and activation
shall expose desktop feedback. Phones and coarse pointers shall expose the
same information and details in the step drawer, without requiring hover.
Touch actions shall measure at least 44 CSS pixels; content shall remain
within the viewport with one internal vertical scroll owner and focus return.
- **AC-TASKS-WORKFLOW-MOVE-PREVIEW-002.3:** Loading shall show Checking session.
Failure shall show Preview unavailable with Retry. Preview loading or failure
alone shall not disable an otherwise allowed move. Known move restrictions
shall continue to use the existing move gates. No stale prediction shall look current.
- **AC-TASKS-WORKFLOW-MOVE-PREVIEW-002.4:** All labels, details, statuses, and
accessible names shall support the five shipped locales, including field,
value, and diagnostic reason codes returned by the preview API. Model and
session names remain product data. The interface shall distinguish planned
settings from completed changes without adding a confirmation dialog.
## Existing contracts
- [Session lifecycle](workflow-profile-session-lifecycle.md)
- [Conditional settings](workflow-session-settings.md)
- [Design](../system-design/workflow-move-preview.md)
- [Implementation plan](../../../plans/workflow-move-preview/plan.md)
## Out of scope
Changing workflow defaults, editing profiles from the preview, automatically
repairing model overrides, routing native subagents, and adding mandatory
confirmation. This package targets the task stepper and its compact disclosure,
and the next-step control above chat, not every board drag, bulk move, or move menu.
@@ -0,0 +1,213 @@
---
status: draft
system: tasks
requirements:
- REQ-TASKS-WORKFLOW-MOVE-PREVIEW-001
- REQ-TASKS-WORKFLOW-MOVE-PREVIEW-002
---
# Workflow Move Preview System Design
## Purpose and boundaries
Add an advisory projection of the existing move decision. Preserve the routing
and best-effort conditional-setting contracts. The tasks system owns the
projection; the agent runtime owns actual provider acceptance.
[Requirements](../requirements/workflow-move-preview.md) define the outcome.
[Lifecycle design](workflow-profile-session-lifecycle.md) defines recipients.
[Conditional settings](../requirements/workflow-session-settings.md) defines
set, keep, restore-original, original-session eligibility, and partial failure.
## Requirement mapping
| Requirement | Sections |
| --- | --- |
| REQ-TASKS-WORKFLOW-MOVE-PREVIEW-001 | Resolution, API, Freshness and failures |
| REQ-TASKS-WORKFLOW-MOVE-PREVIEW-002 | Presentation, Mobile, Verification |
## Resolution
Introduce a read-only decision layer in `internal/orchestrator/workflow_move_preview.go`.
Extract shared selection decisions from `prepareWorkflowStepSession`,
`shouldKeepCurrentWorkflowStepSession`, and `workflow_session_target.go`.
The existing execution paths consume those decisions but retain their atomic
selection, route-ledger, retry, credential, and terminal-session checks.
Do not call mutating prepare/ensure helpers to implement preview.
Resolve the same authoritative move source as `httpMoveTask`; do not use the
browser's viewed session as routing authority. Read the destination, source,
sessions, explicit target bindings, profile configuration, and available runtime
configuration. Apply destination start policy and source end policy. No profile
override still follows existing workflow-default resolution. Reuse may select
a matching other session or create one if no eligible candidate exists and the
destination can actually launch a session for this move. When the destination
has no auto-start action, or when `skip_step_prompt` removes the only launch
turn because it has no instructions, report an idle `no_session` outcome
instead of claiming that a session will be created. The shared
`workflowmove.ShouldAutoStartAgent` predicate keeps this projection aligned
with the no-session execution path.
Preserve terminal-session exclusion, explicit target identity, deterministic
candidate ordering, and same-profile/new behavior. A zero-session task is
reported through the existing launch decision; if that decision cannot be
resolved without provisioning, return unknown rather than ensuring a session.
Dynamic profile selection without a concrete recipient reports its profile and
an unknown model; preview never probes or launches a provider to choose one.
For an existing recipient, use the runtime's effective configuration projection
and durable overrides with their existing precedence. Profile defaults alone
cannot describe a reused conversation. For new recipients, resolve launch
configuration without carrying overrides from the outgoing session.
Factor pure rule selection and configuration projection out of
`workflow_session_config.go`. Reuse `selectConfigureSessionRule`,
`sessionConfigurationTargetForRule`, and existing option normalization.
`resolveWorkflowSessionConfigTarget` currently emits conversation warnings;
preview must instead collect structured diagnostic codes without calling that
side-effecting path. Share decisions, not warning publication.
Evaluate original-session eligibility, canonical family matching and ambiguity,
keep/no-match, set's named fields, and restore's immutable original snapshot.
Report skipped rules accurately. Preserve restrictions on model/mode options.
Use available model-aware capability snapshots only. Provider acceptance remains
uncertain until application; do not synthesize capabilities or restore defaults.
Project applicable context-reset entry actions and the normalized one-shot reset
option using execution order. Include whether the configured prompt will run,
is suppressed, or waits for turn end/queue admission in expanded details.
Do not duplicate reset effects or treat instruction text as a configuration change.
## API
Proposed read-only POST `/api/v1/tasks/:id/move-preview` accepts the destination
`workflow_id`, `workflow_step_id`, and the existing `entry_options` shape.
It shares request validation and task/workspace authorization with the move
handler. It accepts no client-selected source session. POST carries the draft
without putting instructions in a URL. Neither instructions nor raw request
bodies are logged or echoed. Return `Cache-Control: no-store`.
Wire the handler through `task_handlers.go`, a narrow orchestrator preview
interface, and existing dependency construction. New DTO fields:
- `task_id`, `workflow_step_id`, `source_session_id`, `evaluated_at`.
- `outcome`: `reuse_current`, `reuse_other`, `create_new`, `no_session`, or
`unknown`.
- `recipient`: nullable session ID/name, profile ID/name, agent family.
- `model`: before/after IDs and labels, known/unknown flags, and source
(`runtime`, `override`, `profile`, `step_rule`, `original`).
- `changes`: normalized field key, safe label, before/after value, and
`planned`/`unchanged`/`skipped`/`unknown` applicability.
- `context_reset`, `source_disposition`: keep/park/complete/unknown,
`dispatch`: prompt/no_prompt/deferred/unknown.
- `notices`: closed reason codes with safe parameters, including retained
model override, missing snapshot, unsupported option, and stale capability.
Only selectable, non-sensitive model configuration enters this DTO. Never expose
credentials, environment, raw session metadata, prompts, or provider errors.
Unauthorized and missing tasks use existing API errors. Uncertain routing uses
an unknown projection; a `no_session` projection has no recipient, and no
fabricated session ID is returned for a new session.
This response is additive; no migration or durable preview ledger is needed.
## Freshness and failures
`use-workflow-move-preview.ts` under `hooks/domains/kanban/` owns requests through
`lib/api/domains/kanban-api.ts`. Fetch only while an applicable disclosure is open.
Use the existing hover delay. Every disclosed movable drawer row may enqueue a
request, while a shared queue keeps at most two requests in flight and cancels
rows that close or unmount. Do not request previews for every step across every
mounted task.
Deduplicate pending requests by task, destination, normalized options, and a local
invalidation generation. Abort on close/context change and reject late generations.
Invalidate on task placement/primary-session changes, relevant session runtime
updates, profile updates, workflow-step updates, reconnect, and draft changes.
Clear on close and fetch fresh on reopen. Debounce draft edits; typing instruction
text need not refresh unless its presence changes prompt dispatch semantics.
Keep two-line loading/error states compact. Retry is explicit and does not move
the task. Preview failure does not introduce a new move barrier. Show predictions
as planned, not guaranteed; details explain that execution rechecks current state.
Deferred moves show that settings are checked again at turn end. The actual move
continues through existing execution paths and handles provider partial failures.
No preview token is required to move and no confirmation flow is added.
## Presentation
Add `workflow-move-preview.tsx` as a shared projection renderer with no fetches.
`StepMoveControls` in `workflow-stepper.tsx` owns the draft and passes the same
normalized entry options to preview and move. Thread task/workflow identity down
from the stepper. Integrate the renderer in `workflow-step-disclosure.tsx` rows.
Collapsed layout is two centered lines below actions and capabilities. Line one distinguishes current,
other named, new conversations, and an idle task with no recipient. Line two shows an unchanged effective model
or a before/after arrow, followed by an optional additional-change count.
A retained model/profile mismatch uses the compact override label from UI-01.
Count actual non-model field changes plus one context-reset effect. Do not count
source retirement as a setting change. Deduplicate model and config-option model
representations. If only reasoning changes, show the unchanged model with +1.
Use the generic New session label for a permitted fresh launch. Use a separate
localized no-session label when the move leaves the task idle. Show the model on
line two for a fresh launch when known; profile name belongs in details when it
differs from the model label.
The API returns stable field, value, and diagnostic reason codes for Kandev-owned
settings. The renderer localizes those codes in the active locale and displays
provider-defined labels only when they are safe display data. Model and session
names remain data.
Center both footer lines and use an icon-only info button with a localized
accessible name. A separator divides actions from the muted footer.
The chat and passthrough next-step controls pass the destination directly from
`usePlanActions` through `WorkflowMoveProceedButton` to the options form. The
form previews its current draft below the Move action, on desktop and touch.
Mount the preview only while the options surface is open. Use the existing
revision, debounce, cancellation, and concurrency logic.
Use a keyboard/touch-operable details toggle with `aria-expanded`, not a nested
hover-only tooltip. Details list full session/profile identity, planned changes,
source retirement, dispatch, and applicable notices. Arrow summaries mean planned
changes; accessible text and expanded details explain this. Keep Options, existing
capability icons, progress, move errors, and move pending ownership intact.
Unknowns take priority over misleading before/after arrows. Status changes use
one localized status region without repeatedly announcing unchanged predictions.
## Mobile
Reuse `CompactWorkflowStepDisclosure` and its `useTouchDrawer` branch, including
its `max-h-[80dvh]` surface and fixed header. The curated MobilePickerSheet pattern
supplies a discoverable header entry, inset drawer, focus return, and safe areas.
This is a short temporary step choice, so it belongs in the existing drawer.
Each visible step row gains the same two-line summary. Details expand inline
inside the drawer's single scroll body; avoid nested popovers or drawers.
Move here, Options, Retry, and details have at least 44px touch hit areas.
Desktop keeps compact 24px action density. Long names truncate in the summary
and wrap in details. Verify 320px, the default mobile viewport, and breakpoint
neighbors 767/768px with the applicable pointer mode. No document horizontal
scroll or separate persisted mobile selection is introduced.
## Verification
Backend fixtures compare preview selection/configuration with actual move
outcomes on isolated tasks. Assert preview does not change rows, metadata,
route bindings, messages, or runtime call counters. Cover the historical
Luna-profile/Astra override scenario plus conditional settings, the two
no-session launch gates, task-profile fallback, and deferred moves. Frontend
tests cover live store/reconnect invalidation, queued rows beyond the first two,
and localized field/value/diagnostic rendering.
Browser tests inspect the preview, perform the move, then verify recipient and
model through fixture API state. See the [plan](../../../plans/workflow-move-preview/plan.md).
## Related decisions
- [Profile session lifecycle](../../../decisions/2026-08-31-workflow-profile-session-switch-policy.md)
- [Original configuration](../../../decisions/2026-08-01-workflow-session-original-configuration.md)
This projection preserves those decisions. Shared read-only resolution avoids
frontend routing duplication; an independent frontend approximation would drift
on explicit bindings and runtime overrides. A new ADR is unnecessary for this
additive feature because these boundaries and rationale fit the paired design.