feat(web): warn about workflow replay cycles (#1720)

This commit is contained in:
Carlos Florêncio
2026-07-17 18:47:27 +01:00
committed by GitHub
parent 16da501fc4
commit 34a031f9b9
22 changed files with 3667 additions and 342 deletions
@@ -0,0 +1,277 @@
---
spec: docs/specs/workflow-cycle-guardrails/spec.md
created: 2026-07-15
status: complete
---
# Implementation Plan: Workflow Cycle Guardrails
## Overview
Add a deterministic frontend graph analyzer first, then place a shared mutation
guard in the workflow card so both draft creation and existing immediate step
mutations use the same proposed-shape decision. Build the trace UI on that
contract and finish with desktop and mobile Playwright coverage. The existing
workflow HTTP contracts and runtime engine remain unchanged.
---
## Backend
No backend changes are planned. The feature is an authoring-time guard over the
complete client-visible `WorkflowStep[]`; it intentionally does not make replay
cycles a persistence invariant or alter `POST /api/v1/workflow/steps`,
`PUT /api/v1/workflow/steps/:id`, step deletion/reorder, or workflow import.
Relevant contracts verified during planning:
- `apps/backend/internal/workflow/models/models.go` defines `on_enter`,
`on_turn_start`, and `on_turn_complete` action semantics.
- `apps/backend/internal/workflow/engine/types.go` resolves the same three move
action kinds and applies first-eligible-transition behavior.
- `apps/backend/internal/orchestrator/event_handlers_workflow.go` confirms that
`on_turn_start` is user-message driven and an entered `auto_start_agent` step
starts the workflow prompt.
- `apps/backend/internal/orchestrator/task_operations.go` defines the three
prompt-source cases that the diagnostic describes.
---
## Frontend
### Replay-cycle analyzer
Files:
- `apps/web/lib/workflows/replay-cycle-analysis.ts` (new)
- `apps/web/lib/workflows/replay-cycle-analysis.test.ts` (new)
Changes:
- Add `analyzeWorkflowReplayCycles(steps: WorkflowStep[])` and exported
diagnostic/trace types matching the spec's Analysis Contract.
- Normalize order by `position`, resolve `move_to_next`,
`move_to_previous`, and valid `move_to_step` targets for both kanban
triggers, and find paths that return to each `on_enter.auto_start_agent`
step through an `on_turn_complete` replay edge. Treat `on_turn_start` entry
into an auto-start step as safe because the runtime skips destination
`on_enter` while dispatching the user's message.
- Classify a hop as automatic when its source auto-starts on entry and the
transition does not require approval. This includes `on_turn_start`
transitions fired by an auto-started prompt. A cycle is blocking only when
all of its hops are automatic.
- Produce stable diagnostic identities, affected step IDs, a deterministic
highest-severity/shortest trace per auto-start step, and the prompt-source
category.
- Ignore unresolved targets and cycles without an auto-start re-entry.
### Proposed-shape mutation guard
Files:
- `apps/web/components/settings/workflow-card-actions.ts`
- `apps/web/components/settings/workflow-card-actions.test.ts`
- `apps/web/components/settings/workflow-card.tsx`
Changes:
- Introduce a workflow-card guard state that compares analyzer diagnostics for
the current server-backed shape and a proposed shape.
- Route existing step update, add, remove, and reorder handlers through a
single guard before their current API call. Build proposed arrays with the
same step update/start-step demotion, add, delete/reindex, and reorder
semantics already used by the handlers.
- For a new draft, run the guard in `handleSaveWorkflow` before
`createWorkflowAction` so cancellation sends neither the workflow create nor
any follow-up step mutations.
- Hold one pending user-mediated operation until confirmation; execute it once
on **Apply anyway** or **Create anyway**, and discard it on cancel.
- Reject fully automatic proposals without exposing an override. Compare a
bounded identity inventory so alternate cycles are not hidden by the
preferred display trace, and fall back conservatively when that inventory is
exhausted. Always allow changes that remove diagnostics.
- Preserve current request errors, temp-step remapping, optimistic reorder
feedback, and workflow creation sequencing. Reconcile successful remote
mutations from their authoritative responses and roll failed reorders back.
### Diagnostic presentation
Files:
- `apps/web/components/settings/workflow-cycle-diagnostic.tsx` (new)
- `apps/web/components/settings/workflow-pipeline-editor.tsx`
- `apps/web/components/settings/workflow-card.tsx`
Changes:
- Add a reusable diagnostic view for the inline alert and the confirmation or
blocking dialog. Render severity, ordered trace, user-mediated hops, and the
prompt-source explanation without rendering prompt contents.
- Place the inline alert above the pipeline's horizontal scroll area and pass
affected step IDs into pipeline nodes for icon/text plus border treatment,
so affected steps are not communicated by color alone.
- Use an `AlertDialog` for a pending proposal. Blocking diagnostics provide
only a return action; warning diagnostics provide Cancel plus the context
appropriate **Apply anyway** or **Create anyway** action.
- Keep dialog actions and the full trace keyboard accessible. At narrow widths,
stack trace hops and actions, wrap long names, maintain touch-sized controls,
and prevent horizontal page overflow while leaving the pipeline's contained
horizontal scrolling intact.
No API-client or Zustand state changes are required; workflow cards already own
their loaded step arrays and mutation callbacks.
---
## Tests
- **What:** transition resolution for next, previous, and explicit targets on
both triggers, including reordered positions and dangling targets.
**File:** `apps/web/lib/workflows/replay-cycle-analysis.test.ts`
**How:** table-driven pure unit tests.
- **What:** fully automatic cycles are blocking only when every source step
auto-starts and every hop is unapproved `on_turn_complete`.
**File:** `apps/web/lib/workflows/replay-cycle-analysis.test.ts`
**How:** graph fixtures that vary auto-start, trigger, and approval config.
- **What:** user-mediated cycles, generic cycles without auto-start, stable
diagnostic identities, deterministic trace selection, and all three prompt
sources.
**File:** `apps/web/lib/workflows/replay-cycle-analysis.test.ts`
**How:** focused unit fixtures.
- **What:** the built-in Review `on_turn_start` feedback edge into In Progress
does not warn or replay auto-start behavior.
**File:** `apps/web/lib/workflows/replay-cycle-analysis.test.ts`
**How:** regression fixture matching the built-in Kanban transition shape.
- **What:** an existing step edit that introduces a blocking cycle does not call
`updateWorkflowStepAction`; a warning holds the call until confirmation and
calls it exactly once; cancel does not call it.
**File:** `apps/web/components/settings/workflow-card-actions.test.ts`
**How:** mocked action tests around the guard/controller helper extracted from
`useWorkflowStepActions`.
- **What:** add, remove, and reorder use proposed full shapes and existing
diagnostics are not repeatedly confirmed.
**File:** `apps/web/components/settings/workflow-card-actions.test.ts`
**How:** mocked action tests with baseline/proposed diagnostic identities.
- **What:** draft creation is blocked before `createWorkflowAction`, warning
cancellation sends no requests, and confirmation runs the existing creation
sequence once.
**File:** `apps/web/components/settings/workflow-card-actions.test.ts`
**How:** extend the current new-workflow save tests.
- **What:** the inline and dialog variants render the exact trace, prompt source,
affected-step accessibility text, and correct action labels.
**File:** `apps/web/components/settings/workflow-cycle-diagnostic.test.tsx`
**How:** Vitest/Testing Library component tests.
---
## E2E Tests
- **Scenario:** a persisted existing-workflow edit introduces a user-mediated
return path to an auto-start step; no PUT is observable until **Apply
anyway**, and the inline alert remains after persistence.
**File:** `apps/web/e2e/tests/workflow/workflow-cycle-guardrails.spec.ts`
**What to verify:** exact step/trigger trace, task-description prompt source,
cancellation behavior, confirmation, and persisted events through the API
helper.
- **Scenario:** a draft workflow contains a fully automatic cycle; saving is
blocked and no workflow is created.
**File:** `apps/web/e2e/tests/workflow/workflow-cycle-guardrails.spec.ts`
**What to verify:** blocking severity, no override action, and workflow absent
after reload/API list.
- **Scenario:** a cycle without auto-start remains allowed.
**File:** `apps/web/e2e/tests/workflow/workflow-cycle-guardrails.spec.ts`
**What to verify:** no diagnostic and normal persistence.
- **Scenario:** at a mobile viewport, a user-mediated draft warning exposes the
complete trace and prompt source and can be cancelled/confirmed by touch
without horizontal page overflow.
**File:** `apps/web/e2e/tests/workflow/mobile-workflow-cycle-guardrails.spec.ts`
**What to verify:** touch-reachable dialog actions, wrapped/visible trace,
successful confirmation, and document width not exceeding viewport width.
---
## Implementation Waves
Wave 1:
- [x] [task-01-cycle-analyzer](task-01-cycle-analyzer.md) (done)
Wave 2:
- [x] [task-02-mutation-guard](task-02-mutation-guard.md) (done)
Wave 3:
- [x] [task-03-diagnostic-ui](task-03-diagnostic-ui.md) (done)
Wave 4:
- [x] [task-04-e2e-verification](task-04-e2e-verification.md) (done)
---
## Verification Commands
Format first:
```bash
make fmt
```
Focused frontend unit tests:
```bash
(cd apps && pnpm --filter @kandev/web test -- lib/workflows/replay-cycle-analysis.test.ts components/settings/workflow-card-actions.test.ts components/settings/workflow-cycle-diagnostic.test.tsx)
```
Frontend typecheck and lint:
```bash
(cd apps/web && pnpm run typecheck)
(cd apps && pnpm --filter @kandev/web lint)
```
Focused desktop and mobile E2E (rebuilds the production web/backend artifacts
through the repo E2E harness):
```bash
(cd apps/web && pnpm e2e:run --host tests/workflow/workflow-cycle-guardrails.spec.ts)
(cd apps/web && pnpm e2e:run --host --no-build --project mobile-chrome tests/workflow/mobile-workflow-cycle-guardrails.spec.ts)
```
Full repository verification:
```bash
make typecheck test lint
```
## Risks
- `move_to_next` and `move_to_previous` change targets when steps are reordered;
every topology-changing handler must analyze its proposed array, not only
event updates.
- Existing step controls issue remote mutations immediately and some maintain
local debounced input. The pending-operation guard must avoid optimistic UI
that disagrees with the server after cancel or failure.
- The backend engine supports additional generic triggers and configurations.
The diagnostic must state its two-trigger scope and tests must prevent the UI
from implying that every possible workflow event has been analyzed.
- New workflow persistence is a multi-request sequence. Preflighting before the
first request is required so a cancelled warning cannot leave a partial
workflow.
## Open Questions
None for this implementation. The first iteration deliberately guards the
kanban-era `on_turn_start` and `on_turn_complete` paths agreed in the feature
spec and leaves direct API/import enforcement for separate work.
@@ -0,0 +1,54 @@
---
id: "01-cycle-analyzer"
title: "Workflow replay cycle analyzer"
status: done
wave: 1
depends_on: []
plan: "plan.md"
spec: "../../specs/workflow-cycle-guardrails/spec.md"
---
# Task 01: Workflow replay cycle analyzer
## Acceptance
- A pure deterministic analyzer returns the diagnostic contract from the spec
for cycles over `on_turn_start` and `on_turn_complete` move actions.
- Fully automatic versus user-mediated classification accounts for source-step
auto-start and approval, including automatic `on_turn_start` transitions,
and ignores cycles without auto-start
re-entry through `on_turn_complete`. An `on_turn_start` edge into an
auto-start step is covered as a safe non-replay regression.
- Trace choice, bounded identity inventory with conservative truncation,
affected steps, dangling targets, reorder behavior, and all three
prompt-source categories have focused unit coverage.
## Verification
```bash
(cd apps && pnpm --filter @kandev/web test -- lib/workflows/replay-cycle-analysis.test.ts)
(cd apps/web && pnpm run typecheck)
```
## Files Likely Touched
- `apps/web/lib/workflows/replay-cycle-analysis.ts`
- `apps/web/lib/workflows/replay-cycle-analysis.test.ts`
## Dependencies
None.
## Inputs
- Spec sections `What`, `Analysis Contract`, and `Scenarios`.
- `apps/web/lib/types/workflow-actions.ts` for action shapes.
- `apps/backend/internal/workflow/engine/types.go` for transition resolution
and trigger semantics.
- `apps/backend/internal/orchestrator/event_handlers_workflow.go` for the
auto-start/user-turn distinction.
## Output Contract
When finished, change this task's `status` to `done`, check it in `plan.md`, and
report the analyzer API, files changed, tests run, blockers, and residual risks.
@@ -0,0 +1,54 @@
---
id: "02-mutation-guard"
title: "Workflow mutation guard"
status: done
wave: 2
depends_on: ["01-cycle-analyzer"]
plan: "plan.md"
spec: "../../specs/workflow-cycle-guardrails/spec.md"
---
# Task 02: Workflow mutation guard
## Acceptance
- Existing update/add/delete/reorder handlers analyze their complete proposed
shape before sending the current API mutation.
- Blocking proposals send no request; warning proposals execute exactly once
only after confirmation; existing diagnostic identities and cycle-removing
edits are not gated unless identity comparison is conservatively truncated.
- Successful remote mutations reconcile from their authoritative responses;
reorder remains optimistic while pending and rolls back on request failure.
- Draft workflow save preflights before `createWorkflowAction`, with cancel
leaving the draft and sending no create or step requests.
## Verification
```bash
(cd apps && pnpm --filter @kandev/web test -- components/settings/workflow-card-actions.test.ts lib/workflows/replay-cycle-analysis.test.ts)
(cd apps/web && pnpm run typecheck)
```
## Files Likely Touched
- `apps/web/components/settings/workflow-card-actions.ts`
- `apps/web/components/settings/workflow-card-actions.test.ts`
- `apps/web/components/settings/workflow-card.tsx`
## Dependencies
Task 01.
## Inputs
- Spec scenarios for existing mutations, baseline/proposed comparison, and
draft creation.
- Analyzer and diagnostic identity contract from Task 01.
- Existing `useWorkflowStepActions` and `useWorkflowSaveActions` behavior,
including temp-step remapping and request-error feedback.
## Output Contract
When finished, change this task's `status` to `done`, check it in `plan.md`, and
report guarded operations, request behavior tests, files changed, blockers, and
residual risks.
@@ -0,0 +1,55 @@
---
id: "03-diagnostic-ui"
title: "Workflow cycle diagnostic UI"
status: done
wave: 3
depends_on: ["02-mutation-guard"]
plan: "plan.md"
spec: "../../specs/workflow-cycle-guardrails/spec.md"
---
# Task 03: Workflow cycle diagnostic UI
## Acceptance
- Existing and proposed diagnostics render severity, exact ordered trigger
trace, user-required hops, and prompt source through one reusable component.
- Blocking dialogs expose no override; warning dialogs use **Apply anyway** or
**Create anyway**, and affected pipeline steps are identified without color
alone.
- Desktop and narrow layouts keep text/actions usable, keyboard/touch
accessible, and page overflow contained.
## Verification
```bash
make fmt
(cd apps && pnpm --filter @kandev/web test -- components/settings/workflow-cycle-diagnostic.test.tsx components/settings/workflow-card-actions.test.ts)
(cd apps/web && pnpm run typecheck)
(cd apps && pnpm --filter @kandev/web lint)
```
## Files Likely Touched
- `apps/web/components/settings/workflow-cycle-diagnostic.tsx`
- `apps/web/components/settings/workflow-cycle-diagnostic.test.tsx`
- `apps/web/components/settings/workflow-pipeline-editor.tsx`
- `apps/web/components/settings/workflow-card.tsx`
- `apps/web/components/settings/workflow-card-dialogs.tsx`
## Dependencies
Task 02.
## Inputs
- Spec prompt-source language, trace requirements, and mobile behavior.
- Pending-operation state from Task 02.
- Existing `AlertDialog`, workflow pipeline scroll area, and workflow-card
dialog patterns.
## Output Contract
When finished, change this task's `status` to `done`, check it in `plan.md`, and
report responsive behavior, accessibility treatment, files changed, tests run,
blockers, and residual risks.
@@ -0,0 +1,55 @@
---
id: "04-e2e-verification"
title: "Workflow cycle guardrail E2E verification"
status: done
wave: 4
depends_on: ["03-diagnostic-ui"]
plan: "plan.md"
spec: "../../specs/workflow-cycle-guardrails/spec.md"
---
# Task 04: Workflow cycle guardrail E2E verification
## Acceptance
- Desktop Playwright covers warning cancel/confirm, blocking draft creation,
exact trace/prompt source, persisted diagnostics, and an allowed generic
cycle without auto-start.
- A `mobile-*.spec.ts` test completes the warning confirmation flow by touch
and asserts the trace/actions fit without horizontal page overflow.
- Focused and repository verification commands run after formatting, with any
environment-only blocker reported exactly.
## Verification
```bash
make fmt
(cd apps/web && pnpm e2e:run --host tests/workflow/workflow-cycle-guardrails.spec.ts)
(cd apps/web && pnpm e2e:run --host --no-build --project mobile-chrome tests/workflow/mobile-workflow-cycle-guardrails.spec.ts)
(cd apps/web && pnpm run typecheck)
(cd apps && pnpm --filter @kandev/web lint)
make typecheck test lint
```
## Files Likely Touched
- `apps/web/e2e/pages/workflow-settings-page.ts`
- `apps/web/e2e/tests/workflow/workflow-cycle-guardrails.spec.ts`
- `apps/web/e2e/tests/workflow/mobile-workflow-cycle-guardrails.spec.ts`
## Dependencies
Task 03.
## Inputs
- Every user-visible spec scenario.
- Existing workflow settings page object and API helper patterns.
- Mobile parity requirement that mobile tests use the `mobile-` filename
convention and real touch-accessible actions.
## Output Contract
When finished, change this task's `status` to `done`, check it in `plan.md`, and
report desktop/mobile scenarios, files changed, exact commands/results,
environment blockers, and residual risks.
+1
View File
@@ -124,6 +124,7 @@ System pages (Radarr/Sonarr-style) for status, disk usage, database maintenance,
| Spec | Status |
|---|---|
| [workflow-cycle-guardrails](workflow-cycle-guardrails/spec.md) | building |
| [improve-kandev](improve-kandev/spec.md) | draft |
| [homebrew-core](homebrew-core/spec.md) | draft |
| [native-kandev-cli](native-kandev-cli/spec.md) | draft |
@@ -0,0 +1,197 @@
---
status: building
created: 2026-07-15
owner: kandev
---
# Workflow Cycle Guardrails
## Why
Workflow authors can unintentionally connect transition actions into a cycle
that re-enters an auto-start step. The repeated agent prompt looks like a user
message and may start work the author did not intend, while the current linear
pipeline view makes the complete trigger path easy to miss.
## What
- The workflow settings editor analyzes `on_turn_start` and
`on_turn_complete` transition actions whenever it displays or proposes a
workflow shape.
- The analysis resolves `move_to_next`, `move_to_previous`, and `move_to_step`
against the workflow's ordered steps and reports only cycles whose
`on_turn_complete` edge re-enters a step whose `on_enter` actions include
`auto_start_agent`. An `on_turn_start` edge into that step does not replay
automation because the runtime deliberately skips destination `on_enter`
actions while dispatching the user's prompt.
- A cycle is **fully automatic** when every hop can occur without a user action:
every transition starts from a step that auto-starts an agent on entry, and
no hop requires approval. This includes `on_turn_start` transitions fired by
an auto-started prompt. Creating or applying a newly introduced fully
automatic cycle is blocked.
- A cycle is **user-mediated** when its `on_turn_complete` replay edge re-enters
an auto-start step but at least one hop needs a user action: a transition
from a step that does not auto-start its own turn, or a transition that
requires approval. Creating or applying a newly introduced user-mediated
cycle requires explicit confirmation.
- Transition guards are treated conservatively as possible paths. A guard does
not make a cycle safe merely because its condition is not currently met.
- Intentional cycles that do not re-enter an `auto_start_agent` step remain
valid and do not produce a warning.
- The diagnostic shows an ordered trace beginning and ending at the affected
auto-start step. Every hop names the source step, trigger, transition action,
and destination step, and indicates where a user action is required.
- The diagnostic identifies what the re-entered auto-start step sends:
- an empty step prompt replays the task description;
- a step prompt containing `{{task_prompt}}` sends the rendered step prompt
including the task description;
- any other non-empty step prompt replaces the task description.
- If several cycles re-enter the same auto-start step, the editor shows the
highest-severity diagnostic first, then the shortest trace. Diagnostics for
distinct auto-start steps remain separately identifiable.
- Existing persisted workflows with a replay cycle display the diagnostic as
soon as their steps load. This visibility does not retroactively stop active
tasks or prevent edits that remove the cycle.
- Existing workflows keep their current immediate step persistence. Before a
step update, add, delete, or reorder request is sent, the editor analyzes the
proposed complete shape:
- a newly introduced fully automatic cycle cancels the request and offers no
override;
- a newly introduced user-mediated cycle holds the request until the user
chooses **Apply anyway** or cancels it;
- a change that removes a cycle proceeds without confirmation.
- A mutation is considered newly introduced only when its resulting diagnostic
is absent from the currently persisted shape. Unrelated edits to a workflow
that already has a diagnostic do not repeatedly ask for confirmation.
- A draft workflow is analyzed locally before its first persistence. Fully
automatic cycles disable creation and explain the blocking trace;
user-mediated cycles require **Create anyway** confirmation. Cancelling keeps
the draft and sends no workflow or step request.
- The same diagnostic component is used for the inline workflow alert and the
blocking/confirmation dialog. Affected steps are visually identified in the
pipeline without relying on color alone.
- On mobile, the alert and trace stack vertically, dialog actions remain
reachable by touch, transition triggers and actions use human-readable labels,
long step names and prompt-source text wrap, and the page does not gain
horizontal overflow. Diagnostic content scrolls independently from the action
footer so every explanation can be read without being covered. The existing
pipeline may retain its own horizontal scrolling region.
- The guardrail is advisory for user-mediated cycles and blocking for fully
automatic cycles only in the workflow settings authoring surface. It does
not change workflow runtime execution or persisted workflow schemas.
## Analysis Contract
The analyzer accepts the complete proposed `WorkflowStep[]` and returns zero or
more diagnostics. Step order is ascending `position`, with input order as the
stable tie-breaker. A transition that cannot resolve to a step in the same
array is ignored by cycle analysis and remains subject to existing validation.
Each diagnostic contains:
- `severity`: `blocking` or `warning`;
- the re-entered auto-start step ID and name;
- the ordered affected step IDs;
- trace hops containing source ID/name, `on_turn_start` or
`on_turn_complete`, action kind, destination ID/name, and whether that hop
requires user involvement;
- prompt source: `task_description`, `step_prompt_with_task_description`, or
`step_prompt`.
Diagnostics have stable identities derived from the auto-start step and the
ordered trace. The authoring guard compares a bounded inventory of these
identities between current and proposed shapes so it can distinguish every
discovered introduced cycle from an existing one, even when the preferred
display trace is unchanged. The UI still presents one deterministic preferred
trace per auto-start step. If the bounded inventory is exhausted, the guard
conservatively presents that preferred diagnostic instead of allowing the
mutation based on an incomplete identity comparison. The analysis is
deterministic and has no persisted state.
## Failure Modes
- If workflow steps fail to load, the editor keeps its existing load error and
does not claim that the workflow is safe.
- If a confirmed remote mutation fails, the existing error toast remains the
source of truth and the editor retains its last server-backed shape. A
successful add, update, or reorder applies the authoritative mutation
response before the guard accepts another topology edit. Confirmation never
implies persistence succeeded.
- If a workflow already contains a fully automatic cycle, opening settings
surfaces the blocking diagnostic but does not interrupt running tasks. The
author must remove the cycle to prevent future re-entry.
- Invalid or dangling transition targets do not crash the editor and do not
create a false replay trace.
## Scenarios
- **GIVEN** `Build` and `Review` both auto-start on entry and each moves to the
other on turn completion, **WHEN** an author attempts to save the second
transition, **THEN** the request is blocked and the trace shows
`Build --on_turn_complete--> Review --on_turn_complete--> Build`.
- **GIVEN** `In Progress` auto-starts, a path includes an `on_turn_start`
transition from a step that does not auto-start, and its final
`on_turn_complete` edge returns to `In Progress`, **WHEN** an author applies
the final edit, **THEN** the editor requires **Apply anyway** and sends no
mutation before confirmation.
- **GIVEN** every source in the same path auto-starts, **WHEN** an
`on_turn_start` transition is fired by an auto-started prompt, **THEN** that
hop is automatic and a newly introduced fully automatic replay cycle is
blocked.
- **GIVEN** `Review` moves to auto-starting `In Progress` on `on_turn_start`,
**WHEN** that feedback cycle is analyzed, **THEN** the editor shows no replay
warning because the runtime sends the user's prompt without executing
`In Progress.on_enter`.
- **GIVEN** a cycle contains only `on_turn_complete` transitions but one source
step does not auto-start, **WHEN** the cycle is introduced, **THEN** it is a
user-mediated warning because a user must start that step's turn.
- **GIVEN** any transition in the cycle, including the replay edge, requires
approval, **WHEN** the cycle is introduced, **THEN** it is a user-mediated
warning rather than a fully automatic block.
- **GIVEN** a draft workflow has a user-mediated replay cycle, **WHEN** the user
clicks Save and then cancels the confirmation, **THEN** the draft remains and
no workflow or step creation request is sent.
- **GIVEN** a draft workflow has a user-mediated replay cycle, **WHEN** the user
confirms **Create anyway**, **THEN** the existing workflow creation sequence
proceeds once.
- **GIVEN** an existing workflow already has a replay-cycle diagnostic, **WHEN**
the settings page loads, **THEN** the inline alert shows the exact trace and
prompt source before the author edits it.
- **GIVEN** an existing workflow already has a replay-cycle diagnostic, **WHEN**
the author changes only its name or prompt without changing the diagnostic
identity, **THEN** the mutation is not reconfirmed and the inline alert
remains accurate.
- **GIVEN** an existing replay cycle, **WHEN** an edit removes the return path or
removes `auto_start_agent`, **THEN** the edit persists without confirmation
and the diagnostic disappears after refresh.
- **GIVEN** a workflow cycle that never re-enters an auto-start step, **WHEN**
the author creates or edits it, **THEN** the editor shows no replay warning and
does not block the mutation.
- **GIVEN** a re-entered auto-start step has no prompt, **WHEN** its diagnostic
renders, **THEN** the prompt source states that the task description will be
replayed.
- **GIVEN** a re-entered auto-start step prompt contains `{{task_prompt}}`,
**WHEN** its diagnostic renders, **THEN** the prompt source states that the
rendered step prompt includes the task description.
- **GIVEN** the warning is displayed at a mobile viewport, **WHEN** the user
reviews and confirms a user-mediated cycle, **THEN** the full trace and both
dialog actions are usable without horizontal page scrolling or hover.
- **GIVEN** multiple blocking diagnostics exceed a mobile viewport, **WHEN** the
user scrolls through them, **THEN** transition labels remain readable and the
final explanation can scroll fully above the always-reachable return action.
## Out of Scope
- Changing workflow runtime transition or auto-start behavior.
- Stopping tasks already cycling when the settings page opens.
- Rejecting workflow writes made directly through HTTP, WebSocket, YAML import,
seed configuration, or another client.
- Analyzing generic event-driven transitions such as
`on_children_completed`, `on_heartbeat`, or `on_agent_error` in this first
iteration.
- Warning about cycles that contain no auto-start replay.
- Displaying the full task description or fully rendered prompt in settings.
## Implementation Plan
[Implementation plan](../../plans/workflow-cycle-guardrails/plan.md)