Workflow Workbench: Integrated AI Developer Experience — Design
Date: 2026-10-08
Status: Approved (brainstorming)
Related: Apitomy Flow spec apitomy-flow/docs/superpowers/specs/2026-10-08-ai-assisted-editing-design.md
(commit c20d5af), handoff notes in agentdocs/handoff/.
Context
Axiom's Configuration Assistant is a chat that writes JSON files into a private directory and applies them at the end. The Workflow Workbench is an integrated, AI-based experience for composing a workflow together with the action types and tools that support it. The user and the AI edit the same live drafts, and their edits interleave throughout the session.
Decisions
| Topic | Decision |
|---|---|
| Audience | Goal-to-workflow creation first, then continued iteration in the same session ("A-first, then B"). |
| Concurrency | Interleaved turns. The user edits freely; AI changes land as discrete, highlighted, undoable revisions. While the AI works, only the action types and tools it touches in that turn are locked. |
| Draft state | Server-side composition drafts in the database, with a revision history. Publish is partial and the session continues. |
| AI runtime | One long-running interactive session (AssistantSession + InteractiveSessionDriver, claude-code and opencode). The agent edits only through MCP tools. It has no file-write tools. |
| Workflow edits by AI | Flow ChangeSets, validated server-side with Flow's Java ChangeSets.apply, accumulated per turn into one combined change set, delivered to the editor. |
| Layout | A new three-pane workbench page: composition tree, tabbed editors, chat. Focus mode and break-out window. |
| Testing in the loop | v1: run draft action types and tools with sample inputs, and evaluate EL expressions against real events. Follow-up: whole-workflow dry run. |
| Event stream | The agent can list and read real stream events and evaluate EL against them. No redaction in v1. |
| Configuration Assistant | Kept. Both features share a refactored foundation built on template capabilities. |
| v1 item types | Workflow (exactly one per composition), action types, tools. Event sources, subscriptions, toolsets and session templates stay with the Configuration Assistant. |
Dependencies on Apitomy Flow
The workbench uses Apitomy Flow 3.0.1 (io.apitomy:apitomy-flow-engine and @apitomy/flow-ui, both
published). Flow's user guide docs/user-guide/ai-assisted-editing.md is the reference. What Axiom uses:
- Content revisions:
computeContentRevision(workflow)(TS) andContentRevision.of(JsonNode)(Java),"sha256:" + hex(SHA-256(JCS(stripLayout(workflow)))). - Change sets:
ChangeSet/ChangeOp(withunset) and the error codestarget-missing,duplicate-id,edge-endpoint-missing,malformed,read-only,stale. Java:ChangeSets.apply(JsonNode, JsonNode)/applyChecked(...)returningChangeSetResult.Applied(ObjectNode)orRejected(ChangeSetError). Java does not position added nodes. - Editor handle (React 19
ref):propose,apply,withdraw,replace(workflow, origin?),clearHighlights,getSnapshot(). No method throws. - Editor props:
onChange(workflow, { contentRevision, origin }),onProposalResolved(id, outcome),onSelectionChange({ nodeIds, edgeIds }),highlightApplied(default true) andappliedChangeLayout: 'place' | 'tidy'(default'place'). - SPI:
actionTypes(a new array or provider reloads the catalog without a remount; unknown action types show as unresolved),openActionType({ value, resolved })(Open / Create… in the Properties panel),contextActions(FlowContext)(host menu items on canvas, node, edge, selection and Problems rows), andvalidate.
Axiom rules:
- The server computes contentRevision over exactly the Flow Workflow JSON it sends to the editor, with no
Axiom-specific fields. It hashes and sends the stored JSON as-is (ContentRevision.of(JsonNode)), never a
re-serialized Java object, so that null values and key sets match what the browser hashes.
- Change sets the agent writes use author: "agent:axiom-workbench".
- Nodes added by the agent have no position on the server until the editor places them and onChange
saves the placed document.
§1 Architecture
Composition model (new database tables, added to V1__baseline.sql)
CompositionEntity:id,name,goal(the user's first request, kept for recaps),engine,model;assistantSessionId;turnState(idle|ai-working) andcurrentTurnId;pendingChangeSet(JSON, nullable) andpendingTurnId;applyMode(live|review), defaulting tolive;createdAt,updatedAt.CompositionItemEntity:id,compositionId,type(workflow|action-type|tool),name;baseItemIdandbaseContentRevision(the hash of the published item's JSON when it was loaded or last published), set when the draft edits an existing item;content(JSON) andcontentRevision;lastAuthor,lastTurnId,publishedAt,publishedRevision.CompositionRevisionEntity:id,itemId,content,contentRevision;author(user|ai),turnId(null for user edits),createdAt.
For workflows, content is the Flow Workflow document and contentRevision is Flow's hash. For action
types and tools, content is the item JSON and contentRevision is the same hash scheme applied to the item
JSON (SHA-256 of the JCS form).
Shared assistant foundation (refactor)
- Template capabilities replace every check of the template ID
axiom-config-assistant: config-items: the current file-based items, validate and apply endpoints, and the working-directory subfolders.validation-feedback: the current listener that re-validates files after each tool result.composition: binds the session to a composition and enables the workbench MCP server.- The Configuration Assistant template declares
config-itemsandvalidation-feedback, and its behaviour does not change. The UI'sisConfigAssistantbecomes a capability check. - Persisted conversation history: every normalized event is appended to
${axiom.home}/assistant/sessions/<id>/history.jsonl. After a restart the chat is rebuilt from that file. Composition sessions are not deleted at startup. - MCP authentication: each session gets a random token, passed to its MCP server process in an
environment variable and sent as an
X-Axiom-Session-Tokenheader. Composition and workbench endpoints require it when the call comes from MCP. Calls from the UI are unaffected.
MCP server axiom-workbench (new)
Location: app/src/main/resources/templates/axiom-workbench-mcp/server.js. It is installed like
axiom-assistant and receives AXIOM_COMPOSITION_ID and the token. Its tools:
| Tool | Purpose |
|---|---|
get_composition |
Items with names, types, status and problems, plus the workflow document and its contentRevision. |
get_item(type, name) |
Full draft content. |
propose_workflow_changes(changeSet) |
Validate the change set and add it to this turn's accumulated change set. Returns the resulting contentRevision and any problems, or a ChangeSetError. |
upsert_action_type(item), upsert_tool(item) |
Save a whole-item draft revision (author ai, current turn). Returns the problems. |
remove_item(type, name) |
Remove a draft from the composition. It does not delete the published item. |
validate_composition |
Validate all drafts against the drafts plus the database. |
test_action_type(name, inputs, subjectId?), test_tool(name, params, subjectId?) |
Run the draft and return the outputs, stdout, stderr, exit code, duration and output-validation problems. |
list_stream_events(type?, eventSourceId?, ref?, limit<=20) |
Compact summaries: id, type, source, ref, actor, time and top-level payload keys. |
get_stream_event(eventId, path?) |
The normalized event, optionally narrowed to path. Large values are truncated with a marker. |
evaluate_expression(expression, scope, eventId?, context?) |
Evaluate EL with the engine's evaluator. Returns the value or the error. |
| Read-only discovery | The list and get tools of axiom-assistant, for database items and secret names. |
Within a turn, propose_workflow_changes builds on the turn's accumulated workflow: the first change set's
baseRevision is the revision at the start of the turn, and each later one uses the revision returned by the
previous call. The server stores the combined change set as the concatenation of the ops, with the base
revision from the start of the turn.
Template axiom-workbench (built in)
- Capabilities:
composition. - MCP:
@axiom-workbench. - Allowed tools: all
mcp__workbench__*tools, read-only shell (ls,cat,jq), and no Write or Edit. - System prompt: the working loop, then the generated reference (reused from
AssistantReferenceGeneratorand extended with theChangeSetop reference), then the prompt rules. The rules include: - Edit workflows only with change sets, and read
get_compositionfirst. - Before writing an EL expression that reads event data, inspect a real event of that type with
get_stream_eventand check the expression withevaluate_expression. If no event of that type exists, say so and use the documented event schema. - Test action types and tools you create with
test_action_typeortest_toolbefore finishing a turn. - The existing rules: prefer
node, declare inputs, correlate receive-event nodes, do not describe the output format in prompts.
WorkbenchReferenceTest asserts that each rule is present.
The turn loop
- The user sends a message. The client flushes its pending workflow
onChangesave, then posts the message. - The server sets
turnState = ai-workingand creates aturnId. It builds a preamble that includes: - the workflow's
contentRevision; - a summary of user edits since the AI's last turn, built from diffs of user-authored revisions;
- the current selection, if the client sent one.
- The agent works through MCP:
- Action type and tool upserts are saved as revisions immediately and pushed to the UI over SSE.
- Workflow change sets accumulate on the server.
- On
turn_complete, if a combined change set exists, the server publishes acomposition-changesetSSE event. The client then callshandle.apply(cs)(live mode) orhandle.propose(cs)(review mode): - On success,
onChangewith anagent:origin saves the revision with the turn'sturnId. - If the result is
stale, the server re-prompts the agent once with the diff and the new revision. A second stale result ends the turn with a chat notice. - The server sets
turnState = idleand releases the locks.
Only the editor commits workflow change sets: the server never writes a combined change set into the draft
itself, because Java does not position added nodes. If no editor is open when a turn ends (for example, the
browser was closed), the combined change set is stored on the composition as the pending change set and
sent to the editor the next time the workbench opens. The editor applies or proposes it as usual; a stale
result discards it and posts a chat notice offering to ask the agent again. A newer turn replaces any pending
change set.
Publish
POST /compositions/{id}/publish {items[]}builds a configuration pack from the selected drafts and runsImportExportService.importOrUpdatePackin a single transaction. Workflows are published as part of it.- Checks before publishing:
- Every selected draft is valid. Warnings are allowed.
- A selected workflow's new action-type dependencies are also selected or already exist.
- The published item's current JSON still hashes to the draft's
baseContentRevision. If it does not, the item was changed outside the workbench, and the server returns a conflict that the user confirms withforce. - On success, the server sets
publishedAtandpublishedRevisionon the items. The session continues.
§2 UI
Routes and entry points
/workbenchlists compositions and has a New composition button./workbench/:idis the workbench. With?chrome=none, it is used for break-out windows.- Open in workbench… on
WorkflowDefinitionDetailPagecreates a composition seeded with the workflow, its action types, and the tools those action types reference inallowedTools. - A Workbench entry under Configuration in
AppSidebar.
Left pane: composition tree
- The workflow, then action types, then tools. Each item shows:
- a status icon (valid, warnings or errors);
- an unpublished-changes dot;
- an AI badge if the last turn changed it.
- + Action type and + Tool create a blank draft or add an existing item.
- A revision list for the selected item, with author and turn. Selecting a revision shows a diff and offers Restore, which creates a new user revision.
- A Publish… dialog with per-item checkboxes. Changed, valid items are preselected, and required dependencies are checked automatically.
Centre pane: tabbed editors
- Workflow tab: the Flow
WorkflowEditorwith a ref handle. onChangesaves a revision, debounced by 500 ms and flushed before each AI turn.onSelectionChangerecords the selection as context for the AI.spi.openActionTypeopens the action type's tab (Open), or creates a blank draft with that name and opens it (Create…) when the value is unresolved.spi.validatecalls draft validation on the server.spi.actionTypesis an array of database action types merged with draft ones. It is replaced whenever a draft action type is created, renamed or has its inputs or outputs changed, so the palette and the unresolved markers update without a remount.spi.contextActionsadds Ask AI about this… on node, edge, selection and canvas menus, and Ask AI to fix this on Problems rows. Each pre-fills the chat input with a description of the target (ids, names, the problem text) and records the menu'scontentRevisionwith the message. The items are disabled while the AI is working and hidden whencontext.readOnly.appliedChangeLayoutis a workbench preference (Place new nodes or Tidy after AI changes), stored inlocalStorage, default'place'.- Action type tab: the new
ActionTypeEditorcomponent, extracted fromActionTypeDetailPage. It takesvalueandonChange.ActionTypeDetailPageis refactored to use it. - Tool tab: the new
ToolEditorcomponent, extracted from the tool detail page in the same way. - During an AI turn: items the AI has touched in the current turn are read-only, with a banner, and refresh live. All other items stay editable.
- Bottom strip (collapsible):
- Problems for the active item. Clicking one jumps to the node or field.
- Test output: test runs and expression evaluations by the user or the AI.
- A Test… button on action type and tool tabs opens a sample-input form.
Right pane: chat
- It reuses
AssistantChatPanel. - Compact cards for workbench tool calls, for example "Updated action type
add-comment" or "Proposed 4 workflow changes". Each card links to its tab. - A turn header with Undo AI turn:
- It restores the previous revision of every action type and tool the turn changed.
- For the workflow, it calls
handle.replace(<workflow revision saved before the turn>, 'host'). This is a new undoable step, so Ctrl+Z restores the AI's version. It is offered only when the workflow's latest saved revision is the one the turn produced (no user or hostonChangesince). Otherwise it leaves the workflow alone and says so in chat. Flow has no host-callable undo, and none is needed. - A mode toggle, Apply live (the default) or Review changes.
Maximizing space
- Focus mode: a toolbar toggle (
Ctrl+Shift+F) hides the app masthead and sidebar. It is persisted inlocalStorage. - Break out: opens
/workbench/:id?chrome=nonein a new window. The original tab shows a "Bring back here" notice. - Resizable, collapsible panes: drag splitters between the panes. The side panes collapse to rails, and the chat rail shows an AI-working or unread indicator. Sizes are persisted. A Maximize canvas action collapses both side panes.
Session lifecycle
Compositions persist. After a restart, the chat is restored from history.jsonl. On the next message, the
agent process starts again with a recap: the goal, a composition summary and the last 20 messages. Deleting a
composition discards its unpublished drafts, after a confirmation.
§3 REST API (openapi-first, tag Compositions)
GET/POST /compositionsandGET/PATCH/DELETE /compositions/{id}.POSTaccepts{name, goal?, engine?, seedWorkflowId?}.PATCHacceptsnameandapplyMode.GET /compositions/{id}/itemsandPOST /compositions/{id}/items.POSTcreates a blank draft or adds an existing item.GET/PUT/DELETE /compositions/{id}/items/{itemId}.PUTtakes{content, baseRevision}and returns 409 ifbaseRevisionis not current.GET /compositions/{id}/items/{itemId}/revisionsandPOST .../revisions/{revId}/restore.- UI turn routes:
POST /compositions/{id}/messages, with{message, nodeIds?, edgeIds?}. Starts a turn; 409 if one is running.POST /compositions/{id}/interrupt.POST /compositions/{id}/changeset-resolution, with{turnId, outcome}.outcomeis one ofapplied,accepted,rejectedorstale.- Agent routes, for MCP. Each requires the
X-Axiom-Session-Tokenheader of the composition's live workbench session and returns 401 otherwise: GET /compositions/{id}/agent/composition: the turn, goal, current workflow and its revision, and a summary of every item with its problems.GET /compositions/{id}/agent/items/{itemType}/{itemName}andDELETEon the same path.PUT /compositions/{id}/agent/items, with{type, content}.POST /compositions/{id}/agent/workflow/changesets. The body is aChangeSet. It returns{applied, contentRevision?, problems[], error?}, whereerroris aChangeSetError.POST /compositions/{id}/validate.POST /compositions/{id}/test/action-typeandPOST /compositions/{id}/test/tool.POST /compositions/{id}/turns/{turnId}/undo.POST /compositions/{id}/publish.- Conversation: reuse the assistant session endpoints via
assistantSessionId. POST /expressions/evaluate, with body{expression, scope, eventId?, context?}.scopeis one of:edge-condition;action-input;human-task-title;correlation-subscription-key;correlation-event-key;receive-event-match;receive-event-output.
Each scope builds the same EL variables the engine provides at that point.
- New SSE event types:
- composition-item-changed, with {compositionId, itemId, contentRevision, author, turnId};
- composition-turn, with {compositionId, turnId, state};
- composition-changeset, with {compositionId, turnId, changeSet}.
§4 Error handling
- Change set rejected during a turn: the MCP tool returns the
ChangeSetErrorand the validation problems, and the agent fixes them within the turn. Nothing is sent to the client. - Stale at the end of a turn: the server re-prompts the agent once. A second stale result ends the turn with a notice.
- Client
applyfails for another reason: the client shows an error card with Reload workflow from server, which callshandle.replace. - Two windows on one composition: saves carry
baseRevision, and a mismatch returns 409 with the notice "Changed elsewhere — reload". Only the window holding the workflow editor lease (a heartbeat, 30 seconds) can edit the workflow. Other windows show it read-only. - Agent error or process exit: the turn ends,
turnStateresets toidle, and the locks are released. A Restart agent button resumes the session with a recap. - Server restart during a turn: at startup, compositions in
ai-workingreset toidle, and any accumulated change set is discarded. Action type and tool revisions saved during the turn keep theirturnId, so Undo AI turn still works. - Publish: it is all-or-nothing. Failures return 422 with problems per item, and the session is kept. Missing dependencies are blocked in the dialog and on the server. A base conflict needs confirmation.
- Test runs:
- They use the existing timeouts and truncate output.
- They run in a scratch workspace, unless the user chooses a subject.
- Tests of
agent-mode action types cost money and are recorded inAiUsageEntity. - Event tools: an unknown event or path returns a clear message. Payloads are truncated.
§5 Testing
All Java tests use JUnit 5 and the isolated axiom.home.
- Composition service:
- revisions and turn tagging;
- undo of a turn;
- 409 on a stale
baseRevision; - restart recovery from
ai-working. - Change-set endpoint:
- delegation to Flow
ChangeSets.apply; - validation against drafts plus the database;
- accumulation and the base revision used for the combined set;
- a single stale re-prompt.
contentRevisionparity: Axiom's stored hash equals Flow's for the documents it sends. The test reuses Flow's conformance fixtures.- Publish:
- partial selection;
- dependency blocking;
- base conflict;
- a single transaction.
/expressions/evaluate: every scope, againstStreamEventEntityfixtures.- Capabilities refactor: the existing Configuration Assistant tests pass unchanged.
WorkbenchReferenceTest: the prompt rules are present, and the change-set examples are valid according to Flow.- MCP server: a Node test of the mapping from tools to REST calls and of the token header.
- UI (vitest, pure logic):
- tree status;
- publish dependency selection;
- the turn and lock reducer;
- the undo-AI-turn decision;
- persistence of pane sizes and focus mode.
- Manual (Chrome DevTools MCP): go from a goal to a workflow with an action type and a tool, interleave manual edits, trigger the stale path, publish, and use a break-out window.
§6 Phasing
- Foundation: template capabilities, persisted history, MCP token. The Configuration Assistant is unchanged.
- Composition backend, in two plans:
- 2a: the
apitomy-flow-engine3.0.1 upgrade, entities and revisions, the UI-facing REST API, draft validation and publish. - 2b: the turn loop, change-set endpoint and pending change sets, MCP-only routes with the token
guard, session restore,
/expressions/evaluate, the stream-event tools, and theaxiom-workbenchMCP server, template and reference. Scope: - entities, REST, revisions, draft validation and publish;
/expressions/evaluateand the stream-event tools;- the
axiom-workbenchMCP server, template and reference. - Carried over from the Phase 1 review: one shared session-token guard for every MCP-called endpoint;
MCP-only routes, so MCP calls never share a route with UI calls; startup cleanup must keep
COMPOSITIONsession directories (and theirhistory.jsonl); restoring a session passes its token and capabilities at construction and normalizes missing eventdata; move journal writes out of the event lock. - Flow 3.0.1 upgrade (UI): upgrade
@apitomy/flow-uito 3.0.1 and adapt the existing editor, viewer and diff usages; wireopenActionTypeonWorkflowDefinitionDetailPage. The Javaapitomy-flow-engineupgrade to 3.0.1 is done at the start of Phase 2, because the change-set endpoint needsChangeSetsandContentRevision. This phase can run before or alongside Phase 2. - Workbench UI:
- the page, tree and tabs;
- the extracted
ActionTypeEditorandToolEditor; - chat integration, apply and review modes, and undo of an AI turn;
- focus mode, break-out and resizable panes.
- Testing in the loop: test runs from the UI and from MCP, and the test output strip.
Out of scope for v1 (planned follow-ups)
- Whole-workflow dry run using flow-ui's simulator.
- Inline one-shot AI helpers in the editors, including a helper for node
codeTemplate. - Event sources, subscriptions and toolsets in the workbench.
- The copilot engine. It needs interactive session support.
- Simultaneous (non-turn-based) editing by the user and the AI.