# DESIGN-0.35.0 — Workflow Product Transforms the workflow engine from a linear stage-runner into a product-grade automation tool. Seven changesets covering conditional routing, AI-triggered routing, data pipeline hooks, progressive forms, conditional fields, structured review, and a monitoring dashboard. Depends on: v0.34.0 (Data Portability), v0.31.2 (Team Workflows), v0.29.0 (Starlark Sandbox). ## Schema Changes Migrations modified in-place (DB rebuild required): **005_channels.sql** — add `stage_entered_at` (PG: `TIMESTAMPTZ`, SQLite: `TEXT`). Tracks when the current stage was entered for SLA computation. Also add index `idx_channels_workflow_active` on `(workflow_id, workflow_status)` for monitoring queries. **018_workflows.sql** — add `sla_seconds INTEGER` to `workflow_stages`. Add `review_comments` (PG: `JSONB DEFAULT '[]'`, SQLite: `TEXT DEFAULT '[]'`) to `workflow_assignments`. No new migration files. No new tables. ## Conditional Routing Engine **File:** `server/workflow/routing.go` The existing advance logic hardcoded `nextStage = currentStage + 1`. The routing engine replaces this with condition evaluation: 1. Parse `transition_rules.conditions[]` from the departing stage 2. Evaluate each condition against accumulated `stage_data` 3. First match → resolve `target_stage` (by name or ordinal) 4. No match → fallback to `ordinal + 1` **Expression format:** `{field, op, value, target_stage}`. Operators: `eq`, `neq`, `gt`, `lt`, `gte`, `lte`, `in`, `contains`, `exists`, `not_exists`. Loose equality via `fmt.Sprintf("%v")` comparison. Numeric comparison extracts `float64` from both sides. **Wiring:** `ResolveNextStage()` called in all 4 advance sites: handler (`workflow_instances.go`), tool (`tools/workflow.go`), forms auto-advance (`workflow_forms.go`), Starlark module (`sandbox/workflow_module.go`). **Backward compatible:** no conditions = `ordinal + 1`. ## AI-Triggered Routing **Tool:** `workflow_route` — persona calls with `{target_stage, reason}`. Uses `ResolveStageByName()` (case-insensitive). Records decision in `stage_data._route_history_latest`. Allows forward and backward jumps (loop stages for iterative correction). **Starlark:** `workflow.route(channel_id, target_stage, reason)` for extension-driven escalation patterns. Requires `workflow.access`. ## on_advance Hook **File:** `server/handlers/workflow_hooks.go` Fires synchronously after `AdvanceWorkflowStage` succeeds. Configured per-stage in `transition_rules.on_advance: {package_id, entry_point}`. Hook receives: `{stage_data, previous_stage, current_stage, channel_id}`. Returns: `{stage_data: {...}}` (enriched), `None` (no change), or `{error: "msg"}` (logged, not rolled back — future: rollback support). The existing `http` Starlark module (v0.29.1) is available in hooks, enabling external API enrichment without new infrastructure. ## Progressive Forms **Model:** `TypedFormTemplate.Fieldsets []FormFieldset` — optional array of `{label, fields[]}`. When present, top-level `fields` is replaced by flattened fieldset fields in `ParseTypedFormTemplate()`. **Frontend:** `workflow.html` renders one fieldset at a time with step indicator, next/back navigation. All fields submitted together on the final step. ## Conditional Fields **Model:** `FormField.Condition *FieldCondition` — `{when, op, value}`. Operators: `eq` (default), `neq`, `in`, `exists`. **Server-side:** `ValidateFormData()` calls `evaluateFieldCondition()` before each field. Hidden fields are skipped (no required check, no type validation). **Client-side:** `workflow.html` attaches `change`/`input` listeners to source fields. Target fields toggle `cond-hidden` CSS class. ## Structured Review **Review comments:** `review_comments JSONB` on `workflow_assignments`. `POST /workflow-assignments/:id/comment` appends `{text, user_id, created_at}`. `GET /workflow-assignments/:id` returns full assignment. **Review surface:** Side-by-side layout in `workflow.html` — left panel shows structured `stage_data` table (internal `_`-prefixed keys hidden), right panel has comment textarea + approve/reject buttons. Keyboard shortcuts: `Ctrl+Enter` approve, `Ctrl+Shift+Enter` reject. ## Monitoring Dashboard **File:** `server/handlers/workflow_monitor.go` **Endpoints:** - `GET /admin/workflows/monitor/instances` — all active instances - `GET /admin/workflows/monitor/funnel/:id` — stage counts - `GET /admin/workflows/monitor/stale?threshold_hours=N` - `GET /teams/:teamId/workflows/monitor/instances` — team-scoped **SLA computation:** `stage_entered_at + sla_seconds` vs `NOW()`. Returned as `sla_remaining_seconds` (negative = breached) and `sla_breached` boolean. Computed at query time, not stored. **Store:** New `ListByType(ctx, "workflow")` method on `ChannelStore` (both PG and SQLite). Monitoring queries iterate active channels, joining workflow definitions and stage metadata. **Frontend:** `src/js/workflow-monitor.js` — auto-refresh every 30s, SLA color indicators (green > 50%, yellow > 20%, red), stale instance alert section. ## Workflow Branding `WorkflowPageData.BrandingJSON` passes the workflow's `branding` JSON to the visitor template. Applied as: - `--accent` CSS custom property (from `accent_color`) - Logo image in header (from `logo_url`) - Tagline text below header (from `tagline`) Previously branding was only on the landing page; now also on the active workflow surface. ## New Files | File | Purpose | |------|---------| | `server/workflow/routing.go` | Conditional routing engine | | `server/handlers/workflow_hooks.go` | on_advance hook dispatch | | `server/handlers/workflow_monitor.go` | Monitoring dashboard | | `src/js/workflow-monitor.js` | Admin monitoring frontend | | `packages/icd-test-runner/js/crud/workflow-product.js` | E2E tests |