DEV Community

Voor AI
Voor AI

Posted on

How to Put an AI Media Workflow Under Version Control

Prompts are easy to save and hard to reproduce.

A production AI-media run depends on more than a prompt: the selected model, input fields, connections between steps, output routing, and sometimes the layout a teammate uses to understand the graph. If those decisions live only in a browser tab, “the workflow that worked” disappears as soon as somebody rebuilds it from memory.

A portable JSON graph gives the workflow a reviewable artifact. You can inspect it, import it, change it, export it, and put the semantic changes under Git.

This tutorial uses Voor AI’s public Film Agent workflow as a concrete example. The file is small enough to understand without a special SDK: format version 1, three nodes, and two edges.

1. Download the workflow as data

Create a clean folder and download the public example:

mkdir -p workflows
curl -fsS \
  https://voor.ai/workflow-examples/film-agent.workflow.json \
  -o workflows/film-agent.workflow.json
Enter fullscreen mode Exit fullscreen mode

Before importing anything, inspect the envelope:

jq '{
  formatVersion,
  title,
  description,
  graphKeys: (.graph | keys),
  nodeCount: (.graph.nodes | length),
  edgeCount: (.graph.edges | length)
}' workflows/film-agent.workflow.json
Enter fullscreen mode Exit fullscreen mode

At the time of writing, the result is:

{
  "formatVersion": 1,
  "title": "Film Agent",
  "description": "Brief → text-to-video → output. Use the sidebar Agent to write prompts and run.",
  "graphKeys": ["edges", "nodes", "viewport"],
  "nodeCount": 3,
  "edgeCount": 2
}
Enter fullscreen mode Exit fullscreen mode

That check is intentionally boring. It verifies that you downloaded a workflow object rather than an HTML error page or login response.

2. Read the graph without the visual editor

List the nodes that affect execution:

jq '.graph.nodes[] | {
  id,
  type,
  kind: .data.kind,
  modelId: (.data.modelId // null)
}' workflows/film-agent.workflow.json
Enter fullscreen mode Exit fullscreen mode

The graph contains:

input-1  → input node
model-1  → model node using bytedance/seedance-1.5-pro
output-1 → output node
Enter fullscreen mode Exit fullscreen mode

Now inspect the edges:

jq '.graph.edges[] | {
  source,
  sourceHandle,
  target,
  targetHandle
}' workflows/film-agent.workflow.json
Enter fullscreen mode Exit fullscreen mode

The first edge connects the input field theme to the model’s text prompt. The second routes the model’s video output into the output node.

The important idea is that an edge connects ports, not just boxes. A line from input-1 to model-1 is ambiguous until the graph also records field:theme → text:prompt.

3. Add a fast structural test

Fail early if the file is malformed:

jq -e '
  .formatVersion == 1 and
  (.graph.nodes | type == "array") and
  (.graph.edges | type == "array") and
  (.graph.nodes | length > 0)
' workflows/film-agent.workflow.json > /dev/null
Enter fullscreen mode Exit fullscreen mode

Put that command in a pre-commit hook or CI check if workflow files are part of a shared project.

Syntax validity is not full semantic validation. It will not prove that a model is currently available or that every port accepts the connected media type. It does catch corrupted files, missing arrays, and unsupported envelope versions before a teammate reaches the editor.

4. Import, change one thing, and export

Open the Voor AI Workflow Builder, sign in, and import film-agent.workflow.json.

Fork or copy the example before editing it. Then make one deliberate change in the visual editor. Good first changes include:

  • changing the runtime input field;
  • tightening the model node’s prompt wrapper;
  • switching to another compatible video model;
  • adding a second output for review.

Run only after checking the displayed model and estimated credits. A JSON file proves what was configured when it was exported; it does not guarantee that a third-party model catalog, price, or availability has stayed unchanged.

Export the updated graph as a new .workflow.json file. Keep the original beside it:

workflows/film-agent-v1.workflow.json
workflows/film-agent-v2.workflow.json
Enter fullscreen mode Exit fullscreen mode

5. Generate a semantic diff

Raw diffs often contain noise: export timestamps, viewport changes, or node positions caused by rearranging the canvas. Those details are useful for reproducing the editor layout, but they can hide the execution change you actually want to review.

Create a small semantic projection:

jq '{
  formatVersion,
  title,
  description,
  nodes: [
    .graph.nodes[] |
    {
      id,
      type,
      kind: .data.kind,
      modelId: (.data.modelId // null),
      promptWrap: (.data.promptWrap // null),
      fields: (.data.fields // null)
    }
  ] | sort_by(.id),
  edges: [
    .graph.edges[] |
    {source, sourceHandle, target, targetHandle}
  ] | sort_by(.source, .target, .sourceHandle, .targetHandle)
}' workflows/film-agent-v2.workflow.json \
  > workflows/film-agent-v2.semantic.json
Enter fullscreen mode Exit fullscreen mode

Generate the same projection for v1, then compare them:

git diff --no-index \
  workflows/film-agent-v1.semantic.json \
  workflows/film-agent-v2.semantic.json
Enter fullscreen mode Exit fullscreen mode

Keep the full export as the source of truth. Use the semantic file as a review aid, not as a replacement. Layout and viewport data may still matter to teammates who use the graph visually.

6. Review workflow changes like code

A useful pull-request description answers five questions:

  1. What input contract changed?
  2. Which model or model setting changed?
  3. Which edges or ports changed?
  4. Which previously approved outputs must be regenerated?
  5. What evidence shows that the new graph still passes review?

Avoid commit messages like update workflow. Prefer something causal:

workflow: route theme input through the video prompt wrapper
Enter fullscreen mode Exit fullscreen mode

or:

workflow: add review output before final export
Enter fullscreen mode Exit fullscreen mode

7. Do not store secrets in a portable graph

Treat exported workflow JSON as shareable project data.

Do not place API keys, passwords, private customer assets, signed URLs, personal data, or confidential prompts inside input defaults before exporting. Git history is persistent, and deleting a secret from the latest file does not remove it from earlier commits.

Keep secrets in your runtime’s credential system. Keep the graph focused on inputs, model configuration, connections, and reviewable metadata.

A workflow is more useful when it can leave the browser

Version control does not make AI output deterministic. It makes change visible.

With a portable graph you can answer: Which model was selected? Which input fed which port? What changed between the approved graph and today’s experiment? Can a teammate import the same structure without rebuilding it from a screenshot?

Start with the public Film Agent JSON, inspect it with jq, and then open the Voor AI Workflow Builder to import and modify the graph. The goal is not to make creative work look like software. It is to give creative work the same change history that makes software reviewable.

Top comments (0)