# Workflow Console IDE Design Date: 2026-08-04 Status: Approved umbrella design. Implementation will proceed through bounded vertical slices. Related: - [Workflow console foundation](2026-07-01-workflow-console-foundation-design.md) - [Workflow console lifecycle explorer](2026-07-02-workflow-console-lifecycle-explorer.md) - [Workflow contract manifest](2026-08-01-workflow-contract-manifest-design.md) - [Current roadmap](../../current_roadmap.md) ## Goal Turn the existing Workflow Console into a workflow IDE and operations console. A human operator should be able to discover capabilities, author and validate a typed workflow graph, freeze an artifact, bind a deployment, run and resume it, inspect its trace and output, and compare prior runs without leaving the web client. The console is a client of the canonical workflow platform. It must expose real backend state and diagnostics rather than recreate workflow semantics in the browser. The same Python operations that support the CLI and agents must power the UI. ## Product Position The console is organized around the workflow lifecycle: ```text Discover -> Draft -> Artifact -> Deploy -> Run -> Results ``` This lifecycle is the primary navigation model. Global artifact, deployment, run, source, and administration indexes remain available, but they are secondary ways to enter a lifecycle context. The console is not one page per JSON-RPC method and is not a canvas with the rest of the product attached as dialogs. It is a lifecycle workspace in which the graph, forms, diagnostics, records, and execution evidence change emphasis with the selected stage. ## Decisions ### Backend Support Powers Frontend Features The browser does not invent durable workflow state. Draft revisions, artifacts, deployments, bindings, runs, interruptions, traces, outputs, and future result collections are persisted by backend modules before the console treats them as durable. Browser-only state is limited to presentation concerns such as selection, viewport, panel visibility, pending form edits, and unsent mutation queues. ### Human IDE First The first authoring interface is a human-operated graph IDE. A later agent or chat authoring surface must invoke the same domain commands and consume the same diagnostics. It must not introduce a second workflow mutation path. ### Desktop Authoring, Mobile Operations Graph construction and wiring are desktop-first. Mobile supports: - read-only graph inspection; - run status and output inspection; - diagnostics and evidence inspection; and - typed interrupt responses. Complex graph dragging, wiring, and schema editing are not mobile acceptance requirements. ### Migrate, Do Not Preserve Two Consoles The existing lifecycle explorer, graph, evidence, connection, and execution modules are migration inputs. Useful behavior should move into the new workspace. The old console layout should be removed once equivalent routes are available rather than maintained as a compatibility UI. ## Workspace Architecture The UI workflow workspace is a composition over canonical records, not a new persisted backend `project` entity. Initial routes are: ```text /console/discover /console/drafts /console/drafts/:workspaceId /console/artifacts /console/artifacts/:artifactId/:version /console/deployments /console/deployments/:deploymentId /console/runs /console/runs/:runId /console/results /console/admin/sources ``` The collection routes are searchable indexes and creation entry points. The command palette may open them or jump directly to a known identifier, but it is not the only way to reach existing records. Selecting a record moves to its identity route without hiding the relevant collection navigation. The persistent shell contains: - lifecycle navigation; - active server target and connection status; - breadcrumbs for the selected records; - a command palette for route and lifecycle actions; and - an activity/evidence surface for operation receipts and raw protocol data. Each route owns one deep module: | Module | Responsibility | | --- | --- | | `DiscoveryModule` | Search capabilities and node specifications for authoring. | | `DraftEditorModule` | Edit a revisioned draft graph, contracts, mappings, and routes. | | `ArtifactModule` | Inspect immutable versions, requirements, provenance, and diffs. | | `DeploymentModule` | Bind requirements, validate readiness, and prepare a run. | | `RunDebuggerModule` | Launch, monitor, interrupt, resume, trace, and inspect output. | | `ResultsModule` | Search and compare immutable run results and evidence. | | `SourceAdminModule` | Inspect source registry, auth summaries, and health; later host approved admin mutations. | These modules communicate through route identifiers and domain results. They must not import one another's internal React state. ## State Ownership State has one owner at each level: - the URL owns the durable selected lifecycle stage and record identity; - the server owns drafts, revisions, artifacts, deployments, runs, and traces; - the route module owns loaded data, request state, and mutations; - local UI state owns node selection, viewport, open panels, and pending forms; - the draft mutation queue owns ordering, debounce, and revision conflicts; and - the evidence ledger records operations but is not an application-state store. The design rejects a global mega-store. Cross-route state that must survive navigation belongs in the URL or backend. Connection configuration may remain in the existing connection module. ## Domain Client Seam React modules do not call string-named JSON-RPC operations directly. They use small domain-client interfaces: - `CapabilityClient`; - `DraftWorkspaceClient`; - `ArtifactClient`; - `DeploymentClient`; - `RunClient`; and - `SourceAdminClient`. The clients are adapters over the generated and authored RPC contract. They decode wire results, lower UI commands to concrete workflow operations, and return domain results suitable for route modules. Slice 1 introduces only the read interface it needs: ```ts interface DraftWorkspaceClient { list(input: ListDraftWorkspacesInput): Promise; load(workspaceId: string): Promise; } ``` Slice 2 extends that module when mutation behavior exists: ```ts interface DraftWorkspaceEditor extends DraftWorkspaceClient { create(input: CreateDraftWorkspaceInput): Promise; mutate(command: DraftMutationCommand): Promise; validate(workspaceId: string): Promise; compile(workspaceId: string): Promise; createArtifact(input: CreateArtifactInput): Promise; } ``` `DraftMutationCommand` is a tagged UI command. Its adapter lowers commands to the specific `workflow.draft_workspaces.*` operation. This hides operation-name selection, revision threading, and wire decoding from React while preserving typed, auditable behavior. The domain-client seam must earn its depth. It should centralize contract decoding, operation metadata, revision handling, and error normalization rather than become a pass-through wrapper for every RPC method. ## Discovery Discovery presents the information needed to construct a node: - capability and node type; - input and output schemas; - declared outcomes; - examples and description; - provider/source availability; and - constraints relevant to validation or deployment. Users can search and filter the catalog, inspect a capability, and add it to an open draft. If a capability is unavailable, the builder links to source health or administration without exposing credentials inline. Provider configuration, authentication summaries, registry reloads, and enable/disable actions live under Administration. Discovery may report their status but does not own those mutations. The initial route is read-only for auth records and never reveals secret values. A user-facing credential editor, OAuth flow, or production secret-store integration requires a separate backend design before it appears here; the existence of admin auth RPC operations does not by itself make a browser credential UI safe. ## Draft Graph Editor The draft editor is graph-first and follows established workflow-builder conventions rather than exposing raw JSON as the primary interface. ### Canvas The canvas includes: - compact nodes with type icon, name, capability/source, and outcomes; - explicit start and terminal semantics; - outcome-labelled handles and routes; - pan, zoom, fit, minimap, and keyboard selection; - validation markers attached to affected nodes, routes, and bindings; and - graph-level selection for workflow contracts and settings. Opening a node reveals a structured inspector. The node remains compact on the canvas; forms and verbose schemas do not expand inside the graph. ### Inspector The inspector supports: - identity and description; - step-specific configuration; - typed input mappings and literals; - node-output-to-state bindings; - outcome routing; - retry and timeout policy; - diagnostics and suggested repairs; and - an advanced raw-document escape hatch. Graph selection opens workflow-level contracts, start state, exposed workflow output, metadata, and validation. End nodes expose workflow completion and output projection, not ordinary step execution fields. The canonical `__end__` route target remains shorthand in the draft document. The canvas renders each routed workflow outcome as a synthetic terminal node so the graph is readable and wireable without adding a fake step to the persisted document. Explicit end steps remain ordinary persisted steps when a workflow uses them. Serialization maps synthetic terminal edges back to `__end__` and never stores the synthetic node itself. ### Draft Operation Path The editor uses `workflow.draft_workspaces.*` as its primary authoring surface. Legacy `workflow.drafts.*` operations remain compatibility behavior and do not receive a parallel editor. ### Autosave And Revision Conflicts Draft changes use server-backed, debounced autosave: 1. a form or graph gesture emits a tagged mutation command; 2. the queue sends the current `workspace_id` and `revision`; 3. mutations are serialized for each workspace; 4. the canonical returned draft and revision replace local canonical state; 5. a revision conflict stops the queue and offers reload plus reapply or discard; and 6. validation diagnostics update without discarding semantically invalid work. The browser may optimistically display a pending edit, but it must distinguish pending, saved, invalid, and conflicted states. It must never label an edit saved before backend confirmation. Structurally valid but semantically invalid drafts may persist. Compilation and artifact creation remain disabled until their backend gates pass. ## Artifact Inspection Artifacts are immutable workflow versions. The artifact route provides: - version selector and status; - read-only workflow graph and contracts; - logical requirements; - provenance and catalog version; - diagnostics; - version comparison; and - actions to create a deployment, export, or fork when supported by backend operations. Any future fork action must create a real draft workspace. It must not mutate an artifact or create an unsaved browser copy presented as durable. ## Deployment Binding The deployment route makes the binding matrix primary and the graph supporting context. It displays: - pinned artifact identity and version; - each logical requirement mapped to a concrete source; - source, schema, and catalog drift; - static and live validation status; - diagnostics grouped as missing, incompatible, unavailable, or drifted; - suggested valid sources; and - explicit save and delete actions. The run form appears only when the deployment is runnable. Validation failure does not erase attempted bindings; it returns actionable diagnostics. ## Run Debugger ### Launch The launch surface derives a typed form from the workflow input contract and also offers advanced raw JSON. It displays deployment identity and readiness, then records an explicit operation receipt after the backend accepts or rejects the start request. ### Execution The run debugger correlates persisted runtime facts: - run id, status, deployment, artifact, and version; - graph nodes marked observed, interrupted, failed, or not yet observed when those states can be supported by persisted run and trace facts; - bounded trace pages synchronized with the graph; - selected frame input, output, state changes, outcome, and next node; - diagnostics and operation evidence; and - the final workflow output as a prominent inspectable result. Initial updates use bounded polling. Streaming is introduced only after the backend exposes a real event interface; the browser must not emulate streaming by inventing intermediate states. There is no per-node checkpoint stream. Node presentation is therefore an explicit projection: returned trace frames prove observed nodes and outcomes; the persisted interrupt identifies the interrupted point; and a top-level run error may identify failure even when no frame exists. The UI may infer that remaining graph nodes are not yet observed, but it must not label a node currently executing or completed without supporting persisted evidence. When a bounded trace page is insufficient to reconstruct the path, the graph says `state unavailable` and offers the next trace page rather than guessing. ### Interrupt And Resume An interrupted run displays the persisted interrupt request and generates a typed response form from `resume_schema`. Available outcomes come from the interrupt contract. Submitting the form invokes the canonical resume operation and preserves the same run identity when the backend contract does so. Cancellation, revision requests, and successful resumes must use the backend's actual outcome language. The UI must not substitute presentation terminology for persisted runtime facts. ## Results The initial Results route is a projection over immutable runs rather than a new result entity. It supports: - filtering and searching runs; - comparing input, output, status, artifact version, duration, and trace; - exporting an evidence bundle; and - starting a new run with a prior input. Bookmarks, tags, collections, and reusable result assets are deferred until a backend persistence model exists. They must not be stored only in the browser and represented as shared platform records. ## Errors And Recovery Errors are normalized at the domain-client seam into categories the UI can act on: - connection unavailable; - contract or decode failure; - not found; - permission or browser-policy rejection; - validation failure with diagnostics; - revision conflict; - operation failure; and - unexpected server failure with evidence id. Each route distinguishes initial loading, empty, stale, unavailable, and failed states. A stale view may remain readable during reconnect, but mutations are disabled until the target is ready. Retry repeats idempotent reads automatically only when safe. Writes require an explicit retry unless the operation contract carries an idempotency key. A late response from a previous route selection cannot overwrite newer state. ## Accessibility And Responsive Behavior The graph has a keyboard-accessible node list or equivalent non-canvas navigation. Inspectors and dialogs restore focus, expose labelled regions, and do not require color to communicate state. Forms associate diagnostics with their fields and provide a summary for graph-level failures. Desktop layouts prioritize canvas plus inspector. Narrow layouts collapse to a single primary pane. Mobile users can inspect graphs and records, monitor runs, read output and diagnostics, and answer typed interrupts. Authoring controls that require wiring are unavailable rather than rendered as unusable miniature canvas gestures. ## Testing Strategy ### Domain Client Contract Tests Each domain client is tested through its public interface against representative generated-schema payloads. Tests cover correct lowering, decoding, normalized errors, and operation metadata. React tests mock domain clients rather than raw JSON-RPC calls. ### Draft State Tests Pure queue and reducer tests cover: - serialized mutations and debounce; - canonical revision replacement; - optimistic pending state; - validation failure that preserves the draft; - conflict stop, reload, reapply, and discard; - stale response rejection; and - compile and artifact gate calculation. ### Route Integration Tests Route tests cover direct URLs, loading and empty states, record changes, connection loss, decode errors, and lifecycle navigation. Critical vertical paths include: 1. discover capability -> create draft -> add and configure node; 2. validate draft -> compile -> create artifact; 3. bind deployment -> validate -> start run; 4. inspect interrupt -> submit typed resume -> inspect output; and 5. select trace frame -> correlate it with a graph node. ### Backend And Browser Acceptance Every implemented vertical slice has an acceptance test against a real local `wf-rpc-server` fixture. Browser smoke tests verify desktop authoring and mobile inspection/approval at supported viewports. Tests assert persisted server state after mutations instead of only checking rendered success messages. ## Delivery Slices This umbrella design is intentionally not one implementation plan. Work proceeds through backend-complete vertical slices: 1. **Workspace foundation:** routes, lifecycle shell, domain-client pattern, connection/evidence integration, and read-only draft loading. 2. **Draft graph authoring:** discovery, graph canvas, node inspector, mutation queue, revision conflicts, validation, and compile gates. 3. **Artifact and deployment:** immutable artifact inspection, version diff, binding matrix, readiness, and deployment save. 4. **Run debugger:** typed launch, polling, graph execution state, interrupts, resume, output, and bounded trace inspection. 5. **Results and administration:** run comparison/export plus separate source administration. Each slice extends the transport stack before the UI when required: 1. Python operation and model support; 2. contract manifest and generated runtime schema coverage; 3. operation metadata and interpreted output; 4. Effect `RpcGroup` exposure; 5. Hono browser authorization; 6. browser response decoding; 7. domain-client behavior; and 8. route UI and acceptance tests. The first executable implementation plan covers only Slice 1. Later slices get their own plans after the preceding interface is exercised in the browser. ### Slice 1 Operation Matrix Slice 1 intentionally expands the current browser policy. It requires only: | Capability | Operations | | --- | --- | | Connection | `workflow.health` | | Discovery | `workflow.capabilities.list`, `workflow.capabilities.inspect` | | Draft index and detail | `workflow.draft_workspaces.list`, `workflow.draft_workspaces.get` | | Existing lifecycle indexes | Current artifact, deployment, run, trace, and source read operations | Before a route can call a newly listed operation, the slice must provide its generated runtime schema, authored operation metadata where needed, Effect RPC exposure, response decoding, and explicit Hono browser authorization. Browser authorization remains an independent allowlist: generated contract coverage must never authorize an operation automatically. Draft mutation and admin write operations are not authorized in Slice 1. ## Non-Goals This design does not add: - a new backend project/workspace aggregate above existing records; - browser-only durable workflow state; - simultaneous multi-user draft editing; - arbitrary JSON-RPC access from React; - a second legacy-draft editor; - mobile graph wiring; - fake event streaming; - persisted result collections without backend support; or - agent/chat authoring before the human command path is stable. ## Success Criteria The design is realized when a human can use the web console to complete the canonical lifecycle against a real server without raw JSON as the primary interface: ```text discover -> draft -> validate -> artifact -> deploy -> run -> interrupt/resume -> output/trace -> compare result ``` At every stage, the console shows backend-confirmed identity, revision, status, diagnostics, and evidence. The UI remains a client of the workflow platform, and the same operations remain usable by the CLI and external agents.