Workflow UI Primitive Catalog¶
Mozaiks needs a workflow UI catalog that is narrower and more intentional than "generate arbitrary React."
The catalog serves two different jobs:
- Shell-owned workflow status UI
- Workflow-owned interaction and review UI
Those are not the same surface contract and should not be generated the same way.
Why This Exists¶
AG-UI and CopilotKit feel clean because they expose a small number of obvious UI patterns. Mozaiks needs the same discipline, but without collapsing persistent app pages, workflow UI, and shell execution status into one contract.
The canonical rule is:
- plain natural-language workflow reply uses the chat composer
- structured workflow interaction uses a workflow UI primitive
- workflow execution status uses shell-owned status primitives
- persistent app UI stays in page schemas and
workflow_touchpoints
Catalog Split¶
Shell-Owned Workflow Status Primitives¶
These are framework-owned. They should not be generated as workflow-local React components.
| Primitive | Purpose | Canonical owner | Realization |
|---|---|---|---|
run_status_banner | Show running, paused, failed, completed | shell | built-in |
progress_stepper | Show named workflow stages and current progress | shell | built-in |
agent_activity_feed | Show background agent activity, handoffs, and work in progress | shell | built-in |
awaiting_reply_banner | Show that the next composer reply goes to a pending workflow checkpoint | shell | built-in |
Rules:
- These are driven by runtime status, workflow stages, and execution telemetry.
- AgentGenerator should not emit them inside
ToolPlanning.ui_requirements. - AppGenerator should not try to recreate them as persistent page widgets.
Plannable Workflow Interaction Primitives¶
These represent actual workflow checkpoints or review surfaces.
| Primitive | Default display | Use when |
|---|---|---|
composer_reply | composer | The user just needs to answer in natural language |
approval_card | inline | Approve, reject, or request revision |
choice_picker | inline | Choose from bounded options |
confirmation_summary | inline | Confirm captured facts or request edits |
form_card | inline | Structured multi-field input |
record_picker | artifact | Search/select records or entities |
file_upload_prompt | inline | Upload one or more files |
diff_review | artifact | Review before/after changes |
action_plan_review | artifact | Review and approve a generated plan |
artifact_workbench | artifact | Review a multi-pane artifact workspace with preview/export/actions |
document_preview | artifact | Read-only document/report preview |
download_center | artifact | Download generated files or bundles |
comparison_board | artifact | Compare options or variants side by side |
diagram_viewer | artifact | Review diagrams, sequences, or maps |
table_review | artifact | Review row-based data with actions |
Rules:
composer_replyis shell-owned input UX but still a plannable workflow checkpoint.- All other entries are workflow interaction patterns.
- The primitive id is the canonical planning contract.
ui.realizationis the realization contract.componentis the concrete runtime identifier for that realization.ui.workflow_primitiveis required on every emittedUI_ToolandUI_Surfacemanifest entry.ui.realizationis required on every emittedUI_ToolandUI_Surfacemanifest entry.- Only renderable workflow primitives may appear in
tools.yaml; shell status primitives andcomposer_replymust not.
Shipped Shared Workflow Components¶
Some renderable workflow primitives now resolve to shipped shared components in chat-ui/src/core/ui/.
| Primitive | Canonical shared component | Realization |
|---|---|---|
approval_card | ApprovalCard | shipped component |
choice_picker | ChoicePicker | shipped component |
confirmation_summary | ConfirmationSummary | shipped component |
form_card | FormCard | shipped component |
artifact_workbench | ArtifactWorkbench | shipped component |
download_center | DownloadCenter | shipped component |
diagram_viewer | DiagramViewer | shipped component |
Rules:
- Prefer the canonical shipped component name directly in
ui.component. - Do not generate bespoke React for these when the shipped component already fits.
- Generate only a thin wrapper or re-export when workflow-local naming or light customization is required.
- Keep
primitives_hintempty when no workflow-local wrapper is needed. - Treat
ui_contractas the canonical runtime contract for shipped shared components. - Shipped shared components should read action semantics from
ui_contract.actions_schema;payload.actionsis only a runtime projection for convenience.
Build Workflow Integration¶
The build path is intentionally split by ownership.
1. ToolPlanningAgent¶
ToolPlanningAgent is where workflow interaction shape is chosen.
Canonical output:
ToolPlanning.ui_requirements[*].workflow_primitiveToolPlanning.ui_requirements[*].realizationdisplaycomponentprimitives_hint
Rules:
- Use
composer_replyfor plain text feedback. - Use a structured workflow primitive for bounded interaction.
- Do not emit shell status primitives here.
This is the decision point for workflow UI.
2. tool_planning(...) Normalization¶
factory_app/workflows/AgentGenerator/tools/tool_planning.py validates and caches:
primitives_hintagainst the shipped component primitive registryworkflow_primitiveagainst the canonical workflow UI catalogcomposer_replynormalization:- force
realization=shell_builtin - force
display=composer - force
component=null - clear
primitives_hint - shipped component normalization:
- default
componentto the canonical shipped component name when the primitive maps to one - set
realization=shipped_componentwhen the canonical shipped component is used directly - set
realization=workflow_wrapperwhen a workflow-local wrapper intentionally sits on top of a shipped primitive - set
realization=generated_componentwhen the primitive has no shipped shared implementation - clear
primitives_hintwhen the canonical shipped component is used directly
This is the contract enforcement point.
3. ToolsManagerAgent¶
ToolsManagerAgent converts planning into tools.yaml / lifecycle_tools.
Rules:
- Match
ui_requirementsto tool entries bytool. - If
workflow_primitive=composer_reply: - do not emit
UI_Tool - do not emit generated React
- let the runtime input-request/composer lane handle the checkpoint
- Otherwise:
- emit
ui.component - emit
ui.mode - emit
ui.workflow_primitive - emit
ui.realization - emit
ui_contractwhen the surface is interactive
For shipped shared components:
- it is valid for
ui.componentto be the canonical shipped component name - no workflow-local React file is required unless a wrapper is intentionally introduced
ui_contract.actions_schemashould carry the action ids and button metadata (label,variant, optionalapproved) needed by the shared component at runtime
This is the manifest synthesis point.
4. UIFileGenerator¶
UIFileGenerator consumes ToolsManifest.
Rules:
- Treat
ui.workflow_primitiveas the source of truth for behavior. - Treat
ui.componentas either a canonical shipped shared component name or a workflow-local implementation name. - Generate React only for entries that actually produce workflow-local UI.
- If
ui.realization=shipped_component, skip React generation entirely. - If
ui.realization=workflow_wrapper, generate only a thin wrapper/re-export. - If
ui.realization=generated_component, generate the full workflow-local UI. - Do not generate files for
composer_reply.
This is the component realization point.
5. Runtime / Shell¶
The runtime and shell own:
chat.tool_calltool_call_response- composer-mode input routing
- progress/status/activity shell UI
- payload enrichment from
tools.yamlso workflow UI events carry canonicalworkflow_primitive,ui_contract, and projected action metadata
These are not AppGenerator responsibilities.
6. Acceptance Harness¶
Before treating the primitive catalog as stable for generated workflows, the repo should prove one deterministic path that exercises the canonical lanes together.
That first-party target now lives at:
factory_app/workflows/WorkflowPrimitiveAcceptance
It intentionally validates:
composer_replyvia the shell-owned composer laneapproval_cardvia a real interactiveUI_Tooldiagram_viewervia a real one-wayUI_Surface
Canonical smoke command:
python scripts/run_live_mfj_smoke.py \
--workflow WorkflowPrimitiveAcceptance \
--workflows-root factory_app/workflows \
--tool-response-file factory_app/workflows/WorkflowPrimitiveAcceptance/smoke_responses.json
This acceptance workflow belongs in the generator/frontend validation loop, not in the live app bundle. When AgentGenerator prompt, manifest, or runtime contracts change, this workflow should keep passing before those changes are considered promotion-ready.
The stable real-AG2 regression target is:
factory_app/workflows/AgentGenerator
Use it when validating:
- multi-turn AG2 interview flows
- composer fallback handling via
default_input_reply - workflow-local review components such as
ActionPlan - shipped shared workflow primitives such as
DownloadCenter - final generated-bundle handoff
Canonical smoke command:
python scripts/run_live_mfj_smoke.py \
--workflow AgentGenerator \
--workflows-root factory_app/workflows \
--prompt-file factory_app/workflows/AgentGenerator/smoke_prompt.txt \
--tool-response-file factory_app/workflows/AgentGenerator/smoke_responses.json \
--timeout-seconds 300
Relationship To AppGenerator¶
AppGenerator should not generate workflow UI primitives directly.
AppGenerator owns:
- persistent page schemas
workflow_touchpoints- page actions with
action_type: workflow
AgentGenerator owns:
- workflow-local interactive UI
- workflow-local artifact UI
- generated workflow React stubs when needed
The page layer may launch a workflow, but the session UX after launch belongs to the shell and AgentGenerator lane.
Current Canonical Source¶
The current source of truth is split across:
mozaiksai/core/workflow/workflow_ui_catalog.pyfactory_app/workflows/AgentGenerator/tools/tool_planning.pyfactory_app/workflows/AgentGenerator/agents.yaml
If those contracts change, update the docs, prompts, validators, and tests together.