documentize.cloud

Decision 03

One grammar for pipelines

A user recipe and a built-in template are the same YAML file, so the visual builder and the DSL stay a single object.

The decision

A recipe built by clicking through the UI and a recipe written by hand are the same YAML file. There is no separate, prettier user-facing format.

This matters because the alternative — a bespoke UI-only representation that gets translated into the internal template format — is exactly how the orchestrator ended up with two YAML dialects (see The engine already exists). One grammar means the "save as recipe" button in the builder produces something that could ship as a built-in template, and a built-in template can be opened and forked by a user.

What already parses

This is a real, shipping pipeline definition — not a mockup.

# Workflow/Template/zip-office-to-zip-pdf.yaml — real, shipping
taskType: "zip-office-to-zip-pdf"
description: "Convert a zip of office documents to a zip of PDFs"
settings:
  maxPagesPerPart: 1
  supportsSplitting: false
  requiresMerging: false

steps:
  - name: "extract_archive"
    pluginName: "archive-extract"
    dependsOn: []
    priority: 0
  - name: "convert_to_pdf"
    pluginName: "soffice-convert"
    dependsOn: ["extract_archive"]
    priority: 100
  - name: "create_archive"
    pluginName: "archive-create"
    dependsOn: ["convert_to_pdf"]
    priority: 200

Named steps, a dependency graph, parallelism and priority — that is already a real pipeline DSL, authored today only by us as a shipped template. Three additions make it authorable by a workspace.

The proposed recipe grammar

New fields only. Nothing existing changes shape.

# A workspace recipe. New fields marked with ← new.
version: 1                          ← new — lets the grammar evolve
name: "Contracts to archived PDF/A"
inputs:                             ← new — named library files, not "the task's files"
  from: library
  match: "contracts/**/*.docx"
  limit: 500
surface: serverless                 ← new — where it runs

settings:
  maxPagesPerPart: 100
  supportsSplitting: true
  requiresMerging: true

steps:
  - name: to_pdf
    pluginName: soffice-convert
    with:                            ← new — named, validated options
      output-format: pdf
    dependsOn: []

  - name: archive_quality
    pluginName: pdf-saveas
    with: { format: pdfa2b }
    dependsOn: [to_pdf]

  - name: stamp
    pluginName: esign
    with: { reason: "Archived copy" }
    dependsOn: [archive_quality]
AdditionWhy it's neededCost
versionThe grammar will change over time; nothing on disk today carries a version marker.Trivial
inputsToday the input set is implicit — whatever the task happened to carry. A recipe must name its own inputs to be re-runnable on demand.Needs a workspace-scoped file library
withA step currently receives the same task payload as every other step in its pipeline, with only the id rewritten — there is no way to give two steps different settings.Carry per-step options through the plan builder
surfaceWhich execution surface a run is submitted to.Small — a queue selector

One schema, three surfaces

The mechanic that keeps the DSL, the web form and the agent tool arguments from drifting apart.

Every plugin already declares its parameters as a small, typed options class — that declaration is the single source for the plugin's command-line flags, its JSON binding and its generated help text. A build-time pass over every plugin's options type can publish that declaration as one document, listing each plugin's cost, concurrency and parameters together.

ConsumerWhat it gets
Run builder UIGenerated form fields with labels, help text, defaults and validation — no per-plugin front-end code.
Recipe YAMLValidation of every with: block against the same schema.
MCP toolsArgument descriptions generated from the same source instead of written by hand per tool.
This documentationThe plugin reference table, generated rather than maintained.
Why this matters more than it looks

Without a shared schema, adding a plugin means separately updating a form, a YAML validator, an MCP tool signature and a documentation page. With one, adding a plugin means adding a plugin.