fix: resolve workflow editor bugs and improve creation UX#9
fix: resolve workflow editor bugs and improve creation UX#9tsmarvin merged 11 commits intorefactor/additional-actionsfrom
Conversation
- Rename ControlStatement.Sequential to Default with backward-compatible DB converter that reads legacy Sequential values as Default - Replace freeform condition input with structured ConditionBuilder component supporting exit code comparisons and success status checks, with raw/advanced mode and regex validation - Add TargetTags (string[] as JSON) to Workflow entity with custom ValueComparer; expose via TagInput in workflow editor - Add schedule association UI to workflow editor with associate/ disassociate actions and available-schedule dropdown - Add TaskCreateModal for inline task creation from workflow editor with full form support (action types, arguments, tags, success criteria, timeout) - Update DagView node coloring to match renamed default control type - Regenerate Postgres and SQLite InitialCreate migrations with target_tags column on workflows table
There was a problem hiding this comment.
Pull request overview
This PR fixes and improves the workflow editor experience while evolving workflow semantics and persistence (control statement rename/back-compat, structured condition editing, workflow-level targeting tags, schedule association UI, and inline task creation).
Changes:
- Renames the default workflow step control statement to
Defaultand updates UI coloring and DTO defaults accordingly. - Adds workflow-level
TargetTagspersisted as JSON and surfaced in the workflow editor (and used for schedule-to-agent matching). - Adds workflow-schedule association endpoints/UI and introduces inline task creation via a modal, plus a structured
ConditionBuilderUI.
Reviewed changes
Copilot reviewed 40 out of 40 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| src/Werkr.Server/Components/Shared/TaskCreateModal.razor | Adds a modal UI to create tasks inline from the workflow editor. |
| src/Werkr.Server/Components/Shared/DagView.razor | Updates node coloring to match the renamed Default control type. |
| src/Werkr.Server/Components/Shared/ConditionBuilder.razor | Adds a structured + raw editor for workflow step condition expressions with regex validation. |
| src/Werkr.Server/Components/Pages/Workflows/Edit.razor | Adds workflow Target Tags editing, schedule association UI, and inline task creation entrypoint; swaps condition input to ConditionBuilder. |
| src/Werkr.Data/WerkrDbContext.cs | Adds JSON conversion + comparer for Workflow.TargetTags; updates ControlStatement string converter for back-compat. |
| src/Werkr.Data/Entities/Workflows/Workflow.cs | Adds TargetTags to workflow entity. |
| src/Werkr.Data/Entities/Workflows/WorkflowStep.cs | Changes default control statement to Default and updates docs accordingly. |
| src/Werkr.Data/Entities/Workflows/ControlStatement.cs | Renames Sequential to Default and updates semantics description. |
| src/Werkr.Api/Endpoints/WorkflowEndpoints.cs | Adds workflow-schedule association endpoints (list/associate/disassociate). |
| src/Werkr.Api/Services/ScheduleSyncGrpcService.cs | Uses workflow-level TargetTags for agent matching when present, with task-level fallback. |
| src/Werkr.Api/Models/WorkflowMapper.cs | Maps workflow TargetTags into/from API models/DTOs. |
| src/Werkr.Core/Workflows/WorkflowService.cs | Persists workflow TargetTags on update. |
| src/Werkr.Common/Models/* | Extends workflow DTOs/requests for TargetTags; updates step defaults; adds schedule associate request model. |
| src/Werkr.Agent/Scheduling/WorkflowExecutionService.cs | Executes parallelizable steps concurrently and uses concurrent dictionaries for shared state. |
| src/Werkr.Data/Migrations/* + src/Werkr.Data.Identity/Migrations/* | Regenerates initial migrations/snapshots to include target_tags and EF version bump. |
| src/Test/* | Adds/updates integration/unit/component tests for TargetTags, schedule association, ConditionBuilder, TaskCreateModal, and converter behavior. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
## Workflow UI & Interactive DAG Editor
This PR delivers a complete UI refactor: a TypeScript/Node.js build pipeline, real-time run monitoring via SignalR, a vis-timeline Gantt view, and a full AntV X6-based DAG renderer and interactive workflow editor - replacing the previous server-rendered SVG `DagView.razor` entirely.
---
### Interactive Visual Workflow Editor
**Why:** The form-based step/dependency management was not intuitive or visually appealing. The best visual workflow editors share a common set of critical UX patterns: composable architecture (separating mapping, operations, layout, and traversal concerns), connection-drop-to-node-creator, undo/redo with grouped history, node dirtiness tracking, fixed connection points with visual feedback state machines (green/blue port indicators), and searchable, categorized shape palettes.
The editor uses a **batch-save architecture**: all mutations modify local X6 graph state and an in-memory changeset. An explicit "Save" syncs to the API. Undo/redo operates purely on local state - avoiding the complexity of reversing per-action API calls.
- **DagEditor.razor** - Top-level editor page at `/workflows/{Id}/dag-editor`. Three-column layout: `StepPalette` | `DagEditorCanvas` | `NodeConfigPanel`. Manages draft restore, metadata editing, DAG validation. Authorized for Admin/Operator roles.
- **DagEditorCanvas.razor** - Interactive X6 canvas with minimap
- **DagEditorJsInterop** - Editor interop: `InitEditorAsync`, `LoadGraphAsync`, `AddNodeAsync`, `UpdateNodeDataAsync`, `GetChangesetJsonAsync`, `UndoAsync`/`RedoAsync`, `SetLayoutDirectionAsync`, `ZoomToAsync`/`ZoomToFitAsync`. JS→.NET callbacks: `OnNodeSelected`, `OnNodeDropped`, `OnSaveRequested`, `OnCycleDetected`, `OnGraphDirtyChanged`.
- **StepPalette.razor** - Categorized, searchable sidebar with drag-to-canvas via X6 Dnd. Collapsible categories with filtered step creation for rapid discovery.
- **NodeConfigPanel.razor** - Right-side property editor for task info, control flow, conditions, variable bindings, dependencies. Enables task-level I/O inspection - the #1 debugging capability for workflow operators.
- **EditorToolbar.razor** - Save, undo/redo, validate, zoom, export controls
- **Undo/redo** via X6 History plugin - tracks all mutations with grouped operations (e.g., delete node + edges = one undo step)
- **Keyboard shortcuts** via X6 Keyboard: Delete, Ctrl+A, Ctrl+C/V, Ctrl+Z/Y, Escape, arrow keys
- **Clipboard** via X6 Clipboard: copy/paste subgraphs with preserved internal edges
- **changeset.ts** - Tracks add/delete/move mutations locally. Debounced: rapid operations accumulate, Save sends a single batch.
- **cycle-detection.ts** - Client-side DFS cycle detection O(V+E) with visual feedback on invalid connections. Most diagramming tools don't perform cycle detection; this is a Werkr addition required for DAG integrity.
- **draft-storage.ts** - Auto-save drafts to localStorage. `beforeunload` warning for unsaved changes.
- **export-handler.ts** - PNG and SVG export via `graph.toSVG()` / `graph.toPNG()`
- **dnd-handler.ts** - Drag-and-drop from palette to canvas
- **annotation-node.ts** - Free-form sticky note annotations on canvas for documenting workflow sections
---
### AntV X6 DAG Renderer - Replacing Server-Rendered SVG
**Why:** The previous `DagView.razor` was a server-rendered SVG using Kahn's algorithm with fixed-size nodes (160x52) and click-to-select only. SVG + foreignObject rendering with Dagre layout is the proven approach for rich node content (full HTML/CSS) while maintaining SVG coordinates for pan, zoom, and edges. AntV X6 provides all visual interactions client-side at 0ms latency, with built-in undo/redo, clipboard, drag-from-toolbar, minimap, and snapline alignment.
- **DagCanvas.razor** - Read-only X6 DAG component with toolbar (LR/TB layout switching)
- **DagJsInterop** - Typed C# wrapper inheriting `GraphJsInteropBase`. Methods: `CreateGraphAsync`, `LoadGraphAsync`, `ZoomToFitAsync`, `ApplyStatusOverlayAsync`. No scattered `IJSRuntime.InvokeAsync("eval", ...)` calls.
- **TypeScript core:** create-graph.ts, dag-readonly.ts, werkr-node.ts, werkr-edge.ts
- **Dagre hierarchical layout** via layout-engine.ts - configurable TB/LR rank direction. The `tight-tree` ranker was chosen over `network-simplex` for dramatically better performance at scale (minutes → seconds for large graphs).
- **foreignObject HTML nodes** with Bootstrap styling: cards, badges, action-type indicators - enabling full HTML/CSS inside graph nodes rather than flat SVG text
- **parallel-lanes.ts** - Light background lanes behind same-level parallel nodes for visual concurrency indication
- **status-overlay.ts** - Color-coded execution status per selected run via `cell.setData()`. Consistent color language: Green=Success, Red=Failed, Amber=Running, Purple=Pending, Gray=Skipped.
- **X6 plugins:** Selection, Snapline, Scroller, Transform, MiniMap - all built into X6 3.x core
- **Type definitions:** dag-types.ts (node/edge DTOs), dotnet-interop.d.ts (.NET callback interface)
---
### Real-Time Run Monitoring & Execution Views
**Why:** The workflow debugging experience is the primary functionality for the product - building workflows happens once; debugging them happens daily.
- **Three-view tiered complexity** - different users need different detail levels (Compact for quick glance, Timeline for duration analysis, Detail for debugging)
- **Steps × Runs grid view** - the most effective at-a-glance cross-run pattern recognition, with color-coded cells making failures immediately visible
- **Gantt charts** - essential for bottleneck identification and understanding parallelism
- **Sparkline bar charts** - quick pattern recognition for duration and failure trends without drilling in
- **Event grouping** - collapsing related events (Scheduled→Started→Completed) into one logical unit reduces cognitive load
- **Real-time liveness** - all views update as events stream, rather than polling on a timer
- **Task-level I/O inspection** - see exactly what went in/out at every task boundary
- **Saved filter views** - operators managing many workflows need personalized, filtered views by status, agent, tag, and owner
**Workflow event types** in `Werkr.Core/Communication/`:
- **WorkflowEvent.cs** - Six new event types: `StepStartedEvent`, `StepCompletedEvent`, `StepFailedEvent`, `StepSkippedEvent`, `RunCompletedEvent`, `LogAppendedEvent`
- **WorkflowEventBroadcaster** - Singleton fan-out via `System.Threading.Channels`. Published from gRPC service handlers (`JobReportingGrpcService`, `OutputStreamingService`, `VariableService`) when agents report execution progress.
- **WorkflowEventSubscription** - Consumer subscription pattern for SSE endpoints
**Real-time data pipeline:**
```
Agent → gRPC → API WorkflowEventBroadcaster → SSE stream → JobEventRelayService → SignalR WorkflowRunHub → Browser
```
- **WorkflowRunHub** - SignalR hub at `/hubs/workflow-run`. Authorized clients join groups keyed by run ID to receive `StepStatusChanged`, `RunCompleted`, and `LogAppended` events.
- **JobEventRelayService** - `BackgroundService` + `IHealthCheck` that opens a long-lived SSE connection to `GET /api/events/workflow-runs` via the `"ApiServiceSse"` named `HttpClient`, deserializes events, and relays them to the hub via `IHubContext<WorkflowRunHub>`. Auto-reconnects with exponential backoff (1s → 30s). Tracks active run subscriptions in a `ConcurrentDictionary`. Registered as health check (`"sse-relay"`) for operational visibility into SSE connection status.
- **ConnectionStatus.razor** - Visual indicator (Live / Reconnecting / Polling) in the UI
- **RunDetail.razor** - Multi-view run detail page (Compact / Timeline / Log tabs)
- **GanttTab.razor** + **TimelineJsInterop** - vis-timeline Gantt chart showing horizontal bars per task with duration and status. Live bars grow during active runs via SignalR push.
- **RunSparkline.razor** - Server-side SVG mini bar charts (bar height = duration, bar color = status) inline on the workflow list
- **RunGridView.razor** - Steps × Runs 2D status matrix for cross-run comparison
- **StepDetailPanel.razor** - Step-level log preview and error messages
- **TimelineView.razor** - Chronological step execution list with attempt tracking and event grouping
- **SavedFilterService** + **FilterBar.razor** - Named saved filter views persisted via localStorage. Backed by a full filter model ecosystem: `FilterField`, `FilterDefinition`, `FilterCriteria`, `FilterFieldType`, `SavedFilterView` in `Werkr.Common/Models/`.
- **SignalR DTOs** - `StepStatusDto`, `RunStatusDto`, `LogLineDto` in `WorkflowRunHubDtos.cs` - typed payloads for each hub broadcast method
- **Additional DTOs** - `RunSparklineDto`, `StepStatusSummaryDto`, `AnnotationDto`, `DagValidationResult` in `Werkr.Common/Models/`
- Dedicated SSE `HttpClient` (`"ApiServiceSse"`) configured with infinite timeout and no resilience handlers in Program.cs
---
### API Endpoints & Batch Operations
**Batch save endpoint** - The server-side contract for the editor's batch-save architecture:
- `POST /api/workflows/{workflowId}/steps/batch` - Atomic batch operations for editor save. Accepts a `WorkflowStepBatchRequest` containing ordered step operations.
- **Sign convention**: negative `StepId` = temp ID (new nodes created in the editor), positive = real ID (existing nodes). The response includes `StepIdMapping[]` for temp→real ID resolution after save.
- **WorkflowStepBatchModels.cs** - `WorkflowStepBatchRequest`, `StepBatchOperation`, `DependencyBatchItem`, `WorkflowStepBatchResponse`, `StepIdMapping`
**Run management:**
- `POST /api/workflows/{id}/runs/{runId}/retry-from/{stepId}` - Retry from a failed step with optional variable overrides via `RetryFromFailedRequest`
**SSE event streams:**
- `GET /api/events/workflow-runs` - Dedicated SSE stream for workflow run events (consumed by `JobEventRelayService`)
- `GET /api/workflows/runs/{runId}/events` - Per-run filtered event stream
- `GET /api/events/jobs` - Existing job event stream (backward-compatible)
**Saved filter persistence:**
- Full CRUD at `/api/filters/{pageKey}` (GET list, POST create, PUT update, DELETE) with page keys for runs, workflows, jobs, agents, schedules, tasks. Admin-only share toggle.
---
### Consistent Color Standard & Theme System
**Why:** A consistent color language across all views - where the same semantic colors map to the same states throughout the entire UI - is essential for quick visual parsing.
- **CSS custom properties** in theme.css defining a standardized palette with dark/light theme support:
- Status: `--werkr-success` (#28a745), `--werkr-failed` (#dc3545), `--werkr-running` (#ffc107), `--werkr-pending` (#6f42c1), `--werkr-skipped` (#6c757d), `--werkr-not-run`
- Node types: `--werkr-node-default`, `--werkr-node-conditional`, `--werkr-node-fallback`, `--werkr-node-loop`
- Structural: `--werkr-edge-color`, `--werkr-node-stroke`, `--werkr-parallel-group-bg`, `--werkr-snapline`
- Applied consistently across DAG nodes, sparklines, grid view cells, Gantt bars, and status badges
---
### JS/TS Build Infrastructure
**Why:** The X6 graph engine, vis-timeline, and Dagre layout all require a proper JS/TS build pipeline. Keeping all drag, zoom, pan, and connection drawing in the browser while reserving SignalR round-trips for state commits.
- **TypeScript 5.9** project with strict mode under `src/Werkr.Server/graph-ui/`
- **esbuild** bundler (~50ms incremental builds) with code-split output to `wwwroot/js/dist/`: `dag-readonly.js`, `dag-editor.js`, `timeline-view.js`, plus shared vendor chunks. Editor bundle lazy-loaded only when entering edit mode.
- **MSBuild integration** in Werkr.Server.csproj: `EnsureNode` → `NpmInstall` → `BuildGraphUi` targets fire automatically during `dotnet build`. No separate npm steps needed during development.
- **Vitest 3.0** test runner with 7 unit tests covering changeset operations, cycle detection, draft storage, clipboard handling, timeline item mapping, and status styling
- **Blazor<->JS interop hierarchy:** `GraphJsInteropBase<T>` manages `IJSObjectReference` + `DotNetObjectReference` lifecycle with `IAsyncDisposable`. Three derived classes: `DagJsInterop` (read-only), `DagEditorJsInterop` (editor), `TimelineJsInterop` (Gantt).
- `.nvmrc` pins Node.js 22. `.gitignore` excludes `node_modules/` and `wwwroot/js/dist/`.
---
### CI/CD & Dependency Updates
- **CI workflow** (ci.yml): Added Node.js 22 setup → `npm ci` → Vitest → `npm run build:prod` → bundle size check via check-bundle-size.mjs (250 KB gzipped budget)
- **DocFX workflow** (DocFX_gh-pages.yml): Simplified from multi-job Windows workflow to single Ubuntu-based job
- **NuGet dependency updates** in Directory.Packages.props: Microsoft.Extensions, ASP.NET Core, EF Core, SignalR, and test packages updated to latest patch versions
- **Lock file updates** across all projects to match new package versions
- **Cross-platform test fix** in UrlValidatorTests.cs: `RelativeUrl_Rejected` test now handles Unix vs. Windows URI parsing differences
- **`System.Text.Json` transitive dependency** update in installer project
---
### New Dependencies
| Package | Version | Purpose |
|---------|---------|---------|
| `@antv/x6` | 3.1.6 | Graph visualization & editing engine (MIT) |
| `dagre` | 0.8.5 | Hierarchical DAG layout (MIT) |
| `vis-timeline` | 7.7.3 | Gantt timeline rendering (Apache-2.0 / MIT) |
| `vis-data` | 7.1.9 | Reactive data containers for vis-timeline (Apache-2.0 / MIT) |
| `typescript` | 5.9.x | TypeScript compiler |
| `esbuild` | 0.27.x | Bundler |
| `vitest` | 3.0.x | Test runner |
No new NuGet packages - all required .NET packages already existed in Directory.Packages.props.
### New Prerequisite
- **Node.js 22+** required on dev machines and CI agents
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 165 out of 224 changed files in this pull request and generated 9 comments.
Files not reviewed (4)
- src/Werkr.Data.Identity/Migrations/Postgres/20260312050754_InitialCreate.Designer.cs: Language not supported
- src/Werkr.Data.Identity/Migrations/Sqlite/20260312050800_InitialCreate.Designer.cs: Language not supported
- src/Werkr.Data/Migrations/Postgres/20260314004316_InitialCreate.Designer.cs: Language not supported
- src/Werkr.Data/Migrations/Sqlite/20260314004323_InitialCreate.Designer.cs: Language not supported
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
- Remove .user files
- Run IDE Code Cleanup and dotnet format
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 158 out of 233 changed files in this pull request and generated 10 comments.
Files not reviewed (4)
- src/Werkr.Data.Identity/Migrations/Postgres/20260312050754_InitialCreate.Designer.cs: Language not supported
- src/Werkr.Data.Identity/Migrations/Sqlite/20260312050800_InitialCreate.Designer.cs: Language not supported
- src/Werkr.Data/Migrations/Postgres/20260314004316_InitialCreate.Designer.cs: Language not supported
- src/Werkr.Data/Migrations/Sqlite/20260314004323_InitialCreate.Designer.cs: Language not supported
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 155 out of 238 changed files in this pull request and generated 9 comments.
Files not reviewed (4)
- src/Werkr.Data.Identity/Migrations/Postgres/20260312050754_InitialCreate.Designer.cs: Language not supported
- src/Werkr.Data.Identity/Migrations/Sqlite/20260312050800_InitialCreate.Designer.cs: Language not supported
- src/Werkr.Data/Migrations/Postgres/20260314004316_InitialCreate.Designer.cs: Language not supported
- src/Werkr.Data/Migrations/Sqlite/20260314004323_InitialCreate.Designer.cs: Language not supported
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 154 out of 238 changed files in this pull request and generated 10 comments.
Files not reviewed (4)
- src/Werkr.Data.Identity/Migrations/Postgres/20260312050754_InitialCreate.Designer.cs: Language not supported
- src/Werkr.Data.Identity/Migrations/Sqlite/20260312050800_InitialCreate.Designer.cs: Language not supported
- src/Werkr.Data/Migrations/Postgres/20260314004316_InitialCreate.Designer.cs: Language not supported
- src/Werkr.Data/Migrations/Sqlite/20260314004323_InitialCreate.Designer.cs: Language not supported
Comments suppressed due to low confidence (1)
src/Werkr.Core/Scheduling/RetryFromFailedService.cs:1
RetryFromFailedServiceintroduces critical workflow state transitions (CAS status update, downstream reset, variable override versioning, schedule creation) but no automated tests are shown for this behavior. Adding unit/integration coverage for (1) downstream-step closure, (2) attempt increment semantics, (3) variable override version increments, and (4) transactional rollback on failure would help prevent regressions in a high-impact path.
using Microsoft.EntityFrameworkCore;
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 154 out of 239 changed files in this pull request and generated 8 comments.
Files not reviewed (4)
- src/Werkr.Data.Identity/Migrations/Postgres/20260312050754_InitialCreate.Designer.cs: Language not supported
- src/Werkr.Data.Identity/Migrations/Sqlite/20260312050800_InitialCreate.Designer.cs: Language not supported
- src/Werkr.Data/Migrations/Postgres/20260314004316_InitialCreate.Designer.cs: Language not supported
- src/Werkr.Data/Migrations/Sqlite/20260314004323_InitialCreate.Designer.cs: Language not supported
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 154 out of 239 changed files in this pull request and generated 6 comments.
Files not reviewed (4)
- src/Werkr.Data.Identity/Migrations/Postgres/20260312050754_InitialCreate.Designer.cs: Language not supported
- src/Werkr.Data.Identity/Migrations/Sqlite/20260312050800_InitialCreate.Designer.cs: Language not supported
- src/Werkr.Data/Migrations/Postgres/20260314004316_InitialCreate.Designer.cs: Language not supported
- src/Werkr.Data/Migrations/Sqlite/20260314004323_InitialCreate.Designer.cs: Language not supported
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 152 out of 237 changed files in this pull request and generated 7 comments.
Files not reviewed (4)
- src/Werkr.Data.Identity/Migrations/Postgres/20260312050754_InitialCreate.Designer.cs: Language not supported
- src/Werkr.Data.Identity/Migrations/Sqlite/20260312050800_InitialCreate.Designer.cs: Language not supported
- src/Werkr.Data/Migrations/Postgres/20260314004316_InitialCreate.Designer.cs: Language not supported
- src/Werkr.Data/Migrations/Sqlite/20260314004323_InitialCreate.Designer.cs: Language not supported
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Workflow UI Refactor, Interactive DAG Editor & Editor UX Improvements
This PR delivers a complete UI refactor: a TypeScript/Node.js build pipeline, real-time run monitoring via SignalR, a vis-timeline Gantt view, and a full AntV X6-based DAG renderer and interactive workflow editor — replacing the previous server-rendered SVG
DagView.razorentirely. It also includes targeted workflow editor bug fixes and creation UX improvements: renamingControlStatement.SequentialtoDefault, adding structured condition editing, workflow target tags, schedule association, and inline task creation.Workflow Editor Bug Fixes & Creation UX
Why: The original workflow editor had usability gaps — freeform condition input was error-prone, the
Sequentialenum name was misleading, and creating workflows required excessive navigation between pages.ControlStatement.Sequentialrenamed toControlStatement.Defaultwith a backward-compatible DB value converter that reads legacySequential,Parallel,ConditionalIf,ConditionalElseIf,ConditionalWhile, andConditionalDovalues as their modern equivalents. Unrecognized strings fall back toDefault. Full round-trip test coverage inControlStatementConverterTests.TargetTags(string[]stored as JSON) to theWorkflowentity with customValueComparer; exposed viaTagInputcomponent in the workflow editor.TaskSetupModal) — Inline task creation from the workflow editor with full form support (action types, arguments, tags, success criteria, timeout). Supports both create (POST) and edit (PUT) modes. bUnit tests cover modal visibility, form rendering, submission, cancellation, and edit mode.Defaultcontrol type.InitialCreatemigrations withtarget_tagscolumn on the workflows table.ControlStatementConverterTests(round-trip, legacy value mapping, unknown fallback),WorkflowServiceTests(diamond DAG, three-independent-steps, mixed control statements),TaskSetupModalTests(modal lifecycle, edit mode, HTTP methods, callbacks).Interactive Visual Workflow Editor
Why: The form-based step/dependency management was not intuitive or visually appealing. The best visual workflow editors share a common set of critical UX patterns: composable architecture (separating mapping, operations, layout, and traversal concerns), connection-drop-to-node-creator, undo/redo with grouped history, node dirtiness tracking, fixed connection points with visual feedback state machines (green/blue port indicators), and searchable, categorized shape palettes.
The editor uses a batch-save architecture: all mutations modify local X6 graph state and an in-memory changeset. An explicit "Save" syncs to the API. Undo/redo operates purely on local state - avoiding the complexity of reversing per-action API calls.
/workflows/{Id}/dag-editor. Three-column layout:StepPalette|DagEditorCanvas|NodeConfigPanel. Manages draft restore, metadata editing, DAG validation. Authorized for Admin/Operator roles.InitEditorAsync,LoadGraphAsync,AddNodeAsync,UpdateNodeDataAsync,GetChangesetJsonAsync,UndoAsync/RedoAsync,SetLayoutDirectionAsync,ZoomToAsync/ZoomToFitAsync. JS→.NET callbacks:OnNodeSelected,OnNodeDropped,OnSaveRequested,OnCycleDetected,OnGraphDirtyChanged.beforeunloadwarning for unsaved changes.graph.toSVG()/graph.toPNG()AntV X6 DAG Renderer - Replacing Server-Rendered SVG
Why: The previous
DagView.razorwas a server-rendered SVG using Kahn's algorithm with fixed-size nodes (160x52) and click-to-select only. SVG + foreignObject rendering with Dagre layout is the proven approach for rich node content (full HTML/CSS) while maintaining SVG coordinates for pan, zoom, and edges. AntV X6 provides all visual interactions client-side at 0ms latency, with built-in undo/redo, clipboard, drag-from-toolbar, minimap, and snapline alignment.GraphJsInteropBase. Methods:CreateGraphAsync,LoadGraphAsync,ZoomToFitAsync,ApplyStatusOverlayAsync. No scatteredIJSRuntime.InvokeAsync("eval", ...)calls.tight-treeranker was chosen overnetwork-simplexfor dramatically better performance at scale (minutes → seconds for large graphs).cell.setData(). Consistent color language: Green=Success, Red=Failed, Amber=Running, Purple=Pending, Gray=Skipped.Real-Time Run Monitoring & Execution Views
Why: The workflow debugging experience is the primary functionality for the product - building workflows happens once; debugging them happens daily.
Workflow event types in
Werkr.Core/Communication/:StepStartedEvent,StepCompletedEvent,StepFailedEvent,StepSkippedEvent,RunCompletedEvent,LogAppendedEventSystem.Threading.Channels. Published from gRPC service handlers (JobReportingGrpcService,OutputStreamingService,VariableService) when agents report execution progress.Real-time data pipeline:
/hubs/workflow-run. Authorized clients join groups keyed by run ID to receiveStepStatusChanged,RunCompleted, andLogAppendedevents.BackgroundService+IHealthCheckthat opens a long-lived SSE connection toGET /api/events/workflow-runsvia the"ApiServiceSse"namedHttpClient, deserializes events, and relays them to the hub viaIHubContext<WorkflowRunHub>. Auto-reconnects with exponential backoff (1s → 30s). Tracks active run subscriptions in aConcurrentDictionary. Registered as health check ("sse-relay") for operational visibility into SSE connection status.FilterField,FilterDefinition,FilterCriteria,FilterFieldType,SavedFilterViewinWerkr.Common/Models/.StepStatusDto,RunStatusDto,LogLineDtoinWorkflowRunHubDtos.cs- typed payloads for each hub broadcast methodRunSparklineDto,StepStatusSummaryDto,AnnotationDto,DagValidationResultinWerkr.Common/Models/HttpClient("ApiServiceSse") configured with infinite timeout and no resilience handlers in Program.csAPI Endpoints & Batch Operations
Batch save endpoint - The server-side contract for the editor's batch-save architecture:
POST /api/workflows/{workflowId}/steps/batch- Atomic batch operations for editor save. Accepts aWorkflowStepBatchRequestcontaining ordered step operations.StepId= temp ID (new nodes created in the editor), positive = real ID (existing nodes). The response includesStepIdMapping[]for temp→real ID resolution after save.WorkflowStepBatchRequest,StepBatchOperation,DependencyBatchItem,WorkflowStepBatchResponse,StepIdMappingRun management:
POST /api/workflows/{id}/runs/{runId}/retry-from/{stepId}- Retry from a failed step with optional variable overrides viaRetryFromFailedRequestSSE event streams:
GET /api/events/workflow-runs- Dedicated SSE stream for workflow run events (consumed byJobEventRelayService)GET /api/workflows/runs/{runId}/events- Per-run filtered event streamGET /api/events/jobs- Existing job event stream (backward-compatible)Saved filter persistence:
/api/filters/{pageKey}(GET list, POST create, PUT update, DELETE) with page keys for runs, workflows, jobs, agents, schedules, tasks. Admin-only share toggle.Consistent Color Standard & Theme System
Why: A consistent color language across all views - where the same semantic colors map to the same states throughout the entire UI - is essential for quick visual parsing.
--werkr-success(#28a745),--werkr-failed(#dc3545),--werkr-running(#ffc107),--werkr-pending(#6f42c1),--werkr-skipped(#6c757d),--werkr-not-run--werkr-node-default,--werkr-node-conditional,--werkr-node-fallback,--werkr-node-loop--werkr-edge-color,--werkr-node-stroke,--werkr-parallel-group-bg,--werkr-snaplineJS/TS Build Infrastructure
Why: The X6 graph engine, vis-timeline, and Dagre layout all require a proper JS/TS build pipeline. Keeping all drag, zoom, pan, and connection drawing in the browser while reserving SignalR round-trips for state commits.
wwwroot/js/dist/:dag-readonly.js,dag-editor.js,timeline-view.js, plus shared vendor chunks. Editor bundle lazy-loaded only when entering edit mode.EnsureNode→NpmInstall→BuildGraphUitargets fire automatically duringdotnet build. No separate npm steps needed during development.GraphJsInteropBase<T>managesIJSObjectReference+DotNetObjectReferencelifecycle withIAsyncDisposable. Three derived classes:DagJsInterop(read-only),DagEditorJsInterop(editor),TimelineJsInterop(Gantt)..nvmrcpins Node.js 22. .gitignore excludesnode_modules/andwwwroot/js/dist/.CI/CD & Dependency Updates
npm ci→ Vitest →npm run build:prod→ bundle size check via check-bundle-size.mjs (250 KB gzipped budget)RelativeUrl_Rejectedtest now handles Unix vs. Windows URI parsing differencesSystem.Text.Jsontransitive dependency update in installer projectNew Dependencies
@antv/x6dagrevis-timelinevis-datatypescriptesbuildvitestNo new NuGet packages - all required .NET packages already existed in Directory.Packages.props.
New Prerequisite