Phase 1: Workflow versioning + typed flow inputs #5
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Context
Hero_logic currently treats every variation of a flow as a separate
Workflowrecord. Whenoptimize_flowfinds a better model configuration, it mutates the live hero_proc action configs directly — there's no version history, no rollback, no way to compareservice_agent_v3baseline vs an optimized variant side by side.Additionally, flow inputs are implicit: whatever JSON you pass to
play_start.input_databecomes a dict that nodes can reference via{{prompt}},{{scripts_dir}}etc. The convention is{{inputs.x}}but there's no schema, no validation, no editor autocomplete, and no way to know what a workflow expects without reading its action scripts.This issue is the foundation for Phases 2-4 (Examples rework, Benchmark flow, Version router). Ship this before touching anything else.
Goal
Workflowhas manyWorkflowVersionsnapshots. Each version owns its own nodes/edges/actions. Acurrent_version_sidpoints to the active version. Plays run against a specific version.{name, type, description, required, default}entries. Nodes reference them via the existing{{name}}templating, now validated at play_start.OSchema changes
File:
crates/hero_logic/schemas/logic/logic.oschemaAdd new types:
Refactor existing
Workflow:The
Playschema gets a new field pinning it to a version:Actions configs (model, prompts) for AI nodes continue to live in hero_proc. A version's nodes carry an
action_namereference; the action config itself is NOT stored per-version yet. This means switching active version doesn't automatically change model configs —optimize_flowneeds to produce versions with distinct action_name suffixes (e.g.,logic.agent_v3.code_generation@v2) OR a future issue addresses this. For now, phase 1 just does structural versioning; action-config versioning is a follow-up.Code changes
crates/hero_logic/src/engine/executor.rsPlayExecutor::execute(play_sid)currently doesworkflow_get(play.workflow_sid). Change to: resolveplay.workflow_version_sid, thenworkflow_version_get(version_sid)to get nodes/edges.play.workflow_version_sidis empty (old play), fall back to workflow'scurrent_version_sid.crates/hero_logic/src/logic/server/rpc.rsExisting RPC method changes:
workflow_from_template: Load template JSON. Create Workflow record with declared inputs. Create WorkflowVersion v1 with the nodes/edges. Setcurrent_version_sid. Return the Workflow.play_start: Accept optionalworkflow_version_sid; if absent, use workflow'scurrent_version_sid. Validateinput_dataagainst the workflow's declaredinputs(required inputs present, types roughly match). Reject with clear error on missing required.New RPC methods:
workflow_create_version(workflow_sid, from_version_sid?, notes) -> WorkflowVersion: Clone an existing version (or current if unspecified). Returns the new version. Does not set as current.workflow_set_current_version(workflow_sid, version_sid): Switch the active version.workflow_version_get(sid): Fetch a specific version.workflow_version_list_for_workflow(workflow_sid): List all versions for a workflow.crates/hero_logic/src/engine/template_loader.rsTemplate JSON schema changes (additive; old format still loads):
inputs: [{name, type, description, required, default}](optional; defaults to auto-inferred from first node's input_schema for compat)nodes,edges,actionsstill work, but now they're loaded into an initialv1WorkflowVersionWhen loading a template:
Template JSON files
Update both templates to declare inputs:
crates/hero_logic/templates/service_agent_v3.json:crates/hero_logic/templates/optimize_flow.json: declaretarget_template,num_tests,test_promptsas typed inputs.Migration
For existing Workflow records without
versions/current_version_sid:nodes/edges); synthesize a v1 WorkflowVersion on the fly; write the migrated structure back on next save.workflow_migrate_legacy(workflow_sid)that converts one record and is idempotent.UI changes
File:
crates/hero_logic_ui/templates/workflow_editor.html+ JS{wf.name} — {wf.versions.len()} versions.Acceptance criteria
Workflowrecord hasinputs,versions,current_version_sidfields;nodes/edgesremoved from WorkflowWorkflowVersionroot object exists with its own SID spaceservice_agent_v3template creates a Workflow with declared inputs + v1 WorkflowVersion with nodes/edgesplay_startvalidates input_data against declared inputs (rejects missing required with clear error)workflow_create_versionclones the current version successfully; each version is independently executableworkflow_version_sidand execute against that specific versionoptimize_flowstill runs against service_agent_v3 (may need compat shim if it callsworkflow_from_templatewhich now returns a different shape)Dependencies
None. This is the foundation for Phases 2-4.
Out of scope (follow-up)
Core foundation landed:
d5b3c95Implemented the backend schema + executor changes for Phase 1. Still TODO: UI (version selector, inputs panel, typed run form) — tracked as a remaining checklist item below.
What's live
OSchema:
FlowInputtype:{name, input_type, description, required, default}WorkflowVersionroot object with its own SID spaceWorkflowrefactored (no more inline nodes/edges; hasinputs,current_version_sid,versions)Play.workflow_version_sidpins plays to a specific versionRPC methods:
workflow_create_version(workflow_sid, from_version_sid, notes)— clones a version (default: current), auto-picks next label (v1→v2→v3…)workflow_set_current_version(workflow_sid, version_sid)— verifies the version belongs to this workflow before switchingworkflow_version_list_for_workflow(workflow_sid)— returns SIDs newest-firstworkflow_migrate_legacy(workflow_sid)— idempotent; creates v1 if noneBehavior changes:
workflow_from_templatecreates Workflow + initial WorkflowVersion atomicallyworkflow_save_from_jsoncreates a NEW version on update (preserves history)play_startvalidatesinput_dataagainst declaredinputs; rejects missing required with a clear error; pins the Play to the workflow'scurrent_version_sidplay.workflow_version_sidfirst, falls back toworkflow.current_version_sid(defensive for pre-Phase-1 plays)validate_workflow(workflow, version)now takes bothTemplates updated:
service_agent_v3.json: declaresprompt(required),model(optional)optimize_flow.json: declarestarget_template,num_tests,test_promptsVerified end-to-end
Remaining for this issue
Workflowschema changeWorkflowVersionroot objectworkflow_from_templatecreates both atomicallyplay_startvalidates againstinputs, pins versionworkflow_create_versionclones currentworkflow_version_sid{n} versionsI'll open a follow-up issue for the UI work if it blocks moving to Phase 2, since #6/#7/#8 don't hard-depend on the UI changes (they're all backend).
Phase 1 UI landed (commit
68b8153)Editor at
/workflows/:sidnow exposes versioning + typed inputs:current_version_sidviaworkflow_set_current_version, reloads editor against the chosen version).FlowInputs (name, type, required badge, default).logicservice.play_startwith properly-typedinput_data.routes.rs::workflow_editor_handlernow fetches the currentWorkflowVersionviaworkflow_version_fetch(JSON-native wrapper around OSIS OTOML) and passesinputs,current_version_sid,versions_metainto the template alongside the flattened nodes/edges the existing Cytoscape code already expects.current_version_sid,workflow_version_sid,input_values,input_data) now declare= ""defaults so legacy pre-Phase-1 records deserialize cleanly instead of erroring withmissing field.Verified:
workflow_from_template("service_agent_v3")→ workflow persists withcurrent_version_sid+versions:[v1]+inputs:[prompt, model]workflow_version_fetchreturnsWorkflowVersionwith 7 nodes / 9 edges as JSONwindow.INITIAL_WORKFLOW.{inputs, current_version_sid, versions_meta}Still open for this issue:
workflow_save_from_jsonedits target the current version (not the Workflow container)