* feat(workflow): ship explicit completion signal toggle + YAML round-trip
ADR 0015's "Wait for agent completion signal" UI shipped as a deferral
comment in PR #1276 instead of a working toggle, and the
auto_advance_requires_signal flag never reached the workflow export
format. Add the pipeline-editor sub-toggle and propagate the field
through StepPortable so the setting survives workflow export/import.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* fix(workflow): guard completion-signal toggle + cover import in tests
Address PR review:
- hide ExplicitCompletionToggle when on_turn_complete has no transition
(flag has no effect there; visible checkbox was misleading)
- add service-level import test for auto_advance_requires_signal so the
field has explicit ImportWorkflows coverage, mirroring the existing
show_in_command_panel pattern
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* fix(workflow): drop omitempty from AutoAdvanceRequiresSignal tags
Match the sibling bool fields (IsStartStep, ShowInCommandPanel,
AllowManualMove) so a `false` value still appears in exported YAML/JSON.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
---------
Co-authored-by: Kandev Agent <agent@kandev.dev>
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
* feat(workflow): explicit step-completion signal for auto-advance
Adds `step_complete_kandev` MCP tool + `WorkflowStep.auto_advance_requires_signal`
column so workflow steps can gate their `on_turn_complete` transitions on an
agent-emitted completion signal instead of inferring "done" from any turn-end.
See docs/decisions/0015-explicit-completion-signal-for-auto-advance.md for the
motivation, alternatives considered, and trade-offs.
* fix(workflow): address PR review feedback for explicit completion signal
- Dedup `LoadPendingStepSignal` into `internal/task/models` so the MCP
handler (write site) and the orchestrator (read site) share one decoder.
- Gate the out-of-band subscriber on `WorkflowStep.AutoAdvanceRequiresSignal`
so `step_complete_kandev` is not an unintended manual-advance path for
signalless steps.
- Fail closed on step-load errors in `processOnTurnCompleteViaEngine`: a
loader hiccup must block the transition rather than silently bypass the
gate.
- Reject `step_complete_kandev` for terminal sessions
(Completed / Failed / Cancelled) so the agent gets a clear error instead
of `accepted: true` followed by a no-op.
- Surface bus-publish failures back to the agent instead of swallowing
them — the idempotency guard makes the retry safe and prevents the
session from getting stuck.
- New `TestOnStepCompletionSignaled` covers the subscriber paths
(RUNNING / WAITING+match / stale step / not signal-gated).
- ADR doc fixes: payload field list now matches the wire shape, writer
ownership is documented, the unsubstantiated 250ms-debounce claim is
removed, and the late-signal re-close race is recorded as a deferred
open question.
- Drop the new toggle's HelpTip in favour of an inline description — the
shadcn tooltip portal was intercepting clicks on the agent-profile
dropdown options in the workflow settings E2E.
* fix(workflow): round-2 review fixes for explicit completion signal
- Split `handleStepComplete` into `resolveStepCompleteTarget` +
`publishStepCompletionEvent` helpers so the entrypoint clears funlen
(88 → under 80 lines) and the two halves are independently testable.
- Classify session/task load errors instead of returning `NotFound` for
every failure: use the exported `taskrepo.ErrTaskNotFound` sentinel for
task lookups, substring-match for the session repo (no sentinel today),
and surface anything else as `InternalError` with the raw error logged
but not leaked to the agent — so a transient DB hiccup triggers a retry
rather than session abandonment.
- Pin `step_complete_kandev` explicitly in the mode-specific tool-count
tests: `Contains` in `ModeTask`, `NotContains` in `ModeConfig` and
`ModeOffice`, so accidental dereg or cross-mode registration is caught
by the diff instead of by a still-equal count.
* fix(workflow): round-3 review fixes + defer pipeline-editor toggle
- Defer the "Wait for agent completion signal" sub-toggle to a follow-up
PR. Adding it to the step config card shifts the layout enough that the
agent profile select's Radix popper opens into a sibling tooltip's hover
path, breaking workflow-agent-profile.spec.ts on shard 7. The backend
column ships in this PR — workflows can opt in via the YAML import or
the MCP `update_workflow_step_kandev` tool — and the UI affordance gets
its own iteration where the test interference can be investigated
without blocking the rest of the change.
- Re-publish the bus event on the `already_signaled` short-circuit when
the session is WAITING_FOR_INPUT. Without this, an agent's retry after
the first call's publish failed would short-circuit on the persisted
bag, never fire the subscriber, and leave the session stuck.
- handleStepComplete docstring: drop the non-existent feature-flag claim
and the "audit entry" wording — gating is purely
`WorkflowStep.AutoAdvanceRequiresSignal` and the bag is cleared on the
next turn start with no separate audit trail.
- `step_complete_kandev` tool description: same fix — clarify that the
call succeeds for non-gated steps but the signal is discarded on the
next turn start (no audit history to query later).
* test(workflow): cover handleStepComplete + reword defer-comment
- Add `TestHandleStepComplete_*` (6 subtests, all passing) covering the
uncovered handler paths flagged by Claude/cubic on round 3:
missing-fields validation, session-does-not-belong-to-task ownership
guard, terminal-session rejection (Completed/Failed/Cancelled),
first-call happy path (bag persisted + bus payload shape pinned), dedup
while RUNNING (no re-publish), and dedup while WAITING_FOR_INPUT (the
retry-after-publish-failure path — must re-publish so the subscriber
fires).
- Reword the deferred-toggle comment to drop the YAML-export claim:
the workflow export model doesn't yet serialize
`auto_advance_requires_signal`, so MCP `update_workflow_step_kandev`
is the only configuration path that round-trips today. Note the
follow-up needs to extend the export schema alongside the UI toggle.
* test(workflow): pin signaled_at + drop redundant GetTaskSession on bag-clear
- Assert `signaled_at` is present and parses as RFC3339Nano in
`TestHandleStepComplete_FirstCallAccepted`. The field is part of the
handler's documented response contract; pinning it catches accidental
drop/rename regressions.
- Add `clearPendingStepSignalByID(ctx, sessionID)` as a thin DB-only
variant of `clearPendingStepSignal` and use it from
`executeStepTransition`, where the session struct isn't read after the
clear. Avoids an extra `GetTaskSession` round-trip per transition.
`clearPendingStepSignal(session)` still exists for the
in-memory-mutation case (subscriber's stale-step path).
* fix(workflow): subscriber stale-clear guard + ADR/comment drift fixes
- onStepCompletionSignaled's stale-step path now reads the bag and only
clears it when its current StepID matches the (stale) event's stepID.
Previously the unconditional clear could destroy a valid signal that
the new step's agent had just written when sessions are reused across
steps without auto_start_agent. (Greptile P1.)
- Drop the "feature flag" phrase from WorkflowStep.AutoAdvanceRequiresSignal
docstring — leftover from an earlier draft. The gating is purely the
boolean field; the global flag was removed two rounds ago.
- Merge the two adjacent `if triggerOnEnter` blocks in
executeStepTransition so the full triggerOnEnter=true path reads top-
to-bottom in one place. (CodeRabbit + cubic.)
- ADR 0015 Mechanism section: re-label the SessionStepHistory.Metadata
audit annotation, the `step_complete_signaled` system message, and the
expvar telemetry counters as deferred. They're planned shapes, not
current behaviour — calling them "Audit" / "Chat visibility" /
"Telemetry" in present tense was misleading for future readers.
* fix(workflow): drop subscriber complexity + reload session on stale-clear
- Extract `parseStepCompletionEvent` and `stepIsSignalGated` helpers from
`onStepCompletionSignaled`. Cyclomatic complexity goes 17 → under 15
(cyclop on new code, CI lint regression after the round-6
stale-clear guard).
- The stale-step branch now re-loads the session from the DB before
deciding whether to clear the bag. The previous round-6 fix checked
the local `session.Metadata` snapshot, which can be older than a
concurrent write from the new step's agent — that check could see a
stale signal that had since been overwritten with a valid one and
erase the new bag entry. The narrower load-on-clear is cheap and
closes the cubic P1 hole. (The broader race in
`processOnTurnCompleteViaEngine` reading session.Metadata is the
monotonic-marker open question in ADR 0015; out of scope here.)
* test(workflow): pin stale-event guard's preserve-current-bag side + dedup StringFromAny
- Add `TestOnStepCompletionSignaled` subtest "stale event → valid bag
for CURRENT step is preserved" — fires a stale step-A event while
the bag holds a valid step-B signal, asserts the bag still contains
StepID=step_b after the subscriber runs. Pins the negative side of
the round-6 `existing.StepID == stepID` guard, so a regression that
drops or inverts the guard (and would silently leave signal-gated
steps stuck) fails the test instead of slipping through.
- Promote `pendingSignalString` to exported `models.StringFromAny`
and reuse it from the orchestrator subscriber's
`parseStepCompletionEvent`. Eliminates the byte-for-byte duplicate
helper across the two packages; the bag-shape decoder and the bus-
event decoder now share one source of truth for "missing string".
---------
Co-authored-by: Kandev Agent <agent@kandev.dev>
* feat(web): split file search from command panel and add inline task search
Separate the command panel (Cmd+K) from file search (Cmd+Shift+K) to
prevent file results from overpowering commands. Add inline task search
directly in the command panel — tasks appear in a "Tasks" group when
typing ≥2 characters, with workflow step badges and archived state
indicators. Archived tasks are sorted last with reduced opacity.
- Add "search-files" mode to CommandPanelMode
- Add FILE_SEARCH shortcut (Cmd/Ctrl+Shift+K)
- Move file search to dedicated mode, remove from commands mode
- Add useInlineTaskSearchEffect for commands mode task search
- Remove "Search Tasks" sub-mode entry point (now inline)
- Show file search shortcut hint in command panel footer
* feat(web): add configurable keyboard shortcuts in general settings
Add localStorage-based shortcut override system for the command panel
and file search keybindings. Users can record new key combos via the
settings UI and reset to defaults.
- Add shortcut-overrides module (get/set/reset via localStorage)
- Add KeyboardShortcutsCard with shortcut recorder UI
- Add "Keyboard Shortcuts" section to general settings page
* test(web): add e2e tests for command panel and file search split
Cover the key use cases: Cmd+K opens commands without files,
Cmd+Shift+K opens file search, inline task search with workflow step
badges, escape/backspace navigation, and panel toggle behavior.
* fix(test): remove unused seedData variable in command panel e2e test
* feat(settings): persist keyboard shortcuts to user settings API
Replace localStorage-based shortcut overrides with server-persisted
storage via the user settings API. Adds keyboard_shortcuts field to
the backend model, DTO, store, service, and controller. Frontend
reads overrides from Zustand store (hydrated from SSR/WS) and saves
via PATCH /api/v1/user/settings.
* fix(ui): prioritize tasks over commands, add step colors, fix shortcut display
- Move task results above command results in cmd+K for faster task switching
- Replace magnifier icon with task icon (IconSubtask) for task results
- Apply workflow step color to task badges
- Fix formatShortcut early return bug that dropped Shift modifier
when ctrlOrCmd was set (e.g. Cmd+Shift+K showed as Cmd+K)
- Fix same bug in shortcutToCodeMirrorKeybinding
* fix(ui): use clipboard icon for task results in command panel
* style: format command panel footer
* fix(ui): use hammer icon and map step colors to hex for task badges
* feat(ui): show repo directory and agent name on task results
Display secondary details (repository directory, agent name, last
updated) on the right side of task results in the command panel.
* fix: avoid inline selector closure to prevent getServerSnapshot loop
Select the raw repositories map from the store instead of using an
inline selector that closes over workspaceId and returns new array
references, which triggers React's getServerSnapshot infinite loop.
* feat(task): expose agent name and working directory in task DTO
Add primary_agent_name (from AgentProfileSnapshot) and
primary_working_directory (from RepositorySnapshot) to TaskDTO.
Extend the primary session info query to fetch agent_profile_snapshot
and repository_snapshot columns, and extract the new fields in the
handler layer.
* feat(ui): show agent name and working directory on command panel tasks
Replace executor name with agent name from the primary session and
display the working directory (with repository path fallback) in the
task result details.
* fix(ui): add Commands heading to separate tasks from commands in search
* fix(task): use profile display name instead of agent slug
* feat(workflow): add show_in_command_panel per-step configuration
Add a configurable boolean to each workflow step that controls whether
tasks in that step appear in the command panel (Cmd+K). Default
templates mark backlog/done steps as hidden, active steps as visible.
- Add show_in_command_panel to WorkflowStep, StepDefinition, StepPortable
- Add column with idempotent migration (DEFAULT 1 for backwards compat)
- Include field in DTO serialization and export/import round-trip
- Update all default templates (Plan & Build, Architecture, Kanban, PR Review)
- Add tests for export preservation and import round-trip
* feat(ui): wire show_in_command_panel through frontend and settings
- Add field to HTTP types (WorkflowStep, WorkflowStepDTO, StepDefinition)
- Add to kanban state types and KanbanUpdatePayload
- Map in SSR snapshot mapper and WS kanban handler
- Add "Show in command panel" checkbox to workflow step settings
- Extract StepCheckboxRow component to stay within lint limits
* feat(ui): filter command panel tasks by show_in_command_panel flag
Replace position-based heuristic (resolveExcludedStepIds) with the
per-step show_in_command_panel configuration. Tasks in steps where
the flag is false are hidden from the default Cmd+K view but still
accessible via search.
- Replace resolveExcludedStepIds with resolveVisibleStepIds
- Consolidate useInlineTaskSearchEffect params into options object
* fix(ui): differentiate task sections in command panel
Rename the inline task results heading to "Active Tasks" when showing
the default view, distinguishing it from the "Tasks" command group
that contains "Create New Task".
* fix(ui): keep auto-archive checkbox inline with other step options
* fix(ui): include show_in_command_panel in step update action
The server action was missing show_in_command_panel from its Pick type
and body builder, so the checkbox change was silently dropped.
* fix(ui): pass is_start_step and show_in_command_panel from template steps
Both normalizeWorkflowTemplate mappers (workflow-api.ts and
workspaces.ts) were only picking name/position/color/prompt/events
from backend template steps, dropping is_start_step and
show_in_command_panel. This caused all template steps to appear
with show_in_command_panel checked when creating a new workflow.
* fix(ui): fix command panel empty tasks and step update losing checkbox state
Two fixes:
- Command panel: when kanban steps haven't loaded yet, don't filter
tasks by visibility (resolveVisibleStepIds returns null for empty
steps instead of an empty Set that rejects everything)
- Workflow settings: BackendWorkflowStep type and transformWorkflowStep
were missing show_in_command_panel, so refreshing steps after an
update dropped the field and the checkbox always reverted
* fix(ui): prevent back button from stealing focus in command panel
The back button was focusable and appeared before CommandInput, so
opening file search mode focused the button instead of the input.
Add tabIndex={-1} to keep focus on the search input.
* feat(web): show archive icon for archived tasks in command panel
Replace the text badge (e.g. "COMPLETED") with an IconArchive for
archived tasks in the command panel search results. Active tasks
keep the existing IconHammer icon.
* style: fix Prettier formatting and extract useStepMaps hook
Fix Prettier formatting issues in command-panel.tsx and
workflow-pipeline-editor-panels.tsx. Extract useStepMaps hook
from useInlineTaskSearchEffect to stay within the 100-line
function limit.
* fix: address PR review feedback
- Validate keyboard shortcut payload shape before persisting (service.go)
- Normalize KeyboardShortcuts to {} on read path for legacy rows (sqlite.go)
- Deduplicate getRepoDir into getFileName (command-panel-footer.tsx)
- Show toast on shortcut persistence failure instead of swallowing (keyboard-shortcuts-card.tsx)
- Reset keyboard_shortcuts in E2E test fixture for deterministic tests (test-base.ts)