Workflow Templates

Applies to: ChengOS v0.1.0+ | Last verified: 2026-08-12 | Source: crates/cheng-core/src/domain/workflow_template.rs, crates/cheng-api/src/rest/handlers/workflow_template/mod.rs, deploy/workflow-templates/, chengflow-ui/src/api/workflowTemplateSync.ts

A workflow template is a built-in workflow that ChengOS installs into your workspace as an ordinary, editable workflow. Once installed the copy is yours: edit it freely. What the template system adds is the ability to tell you, later, that upstream changed — and to let you decide what to do about it.

Installed copies are private workspace workflows, never public templates.

The catalog

Templates ship under deploy/workflow-templates/, one directory per template, each containing template.json (the manifest) and workflow.json (the graph).

Nine templates ship today:

KeySortWhat it demonstrates
create-workflow10Building a workflow from a description
http-tools20Calling external HTTP APIs
main-chat30Intent routing to three sub-workflows
memory-chat40Conversation with history
no-memory-chat50Single-turn exchange
scheduled-task60Time-triggered execution
skills-run70Running a skill
skills-importer80Importing a skill package
tools-user90A ReAct agent doing file operations

main-chat is the one to read first: a utils/condition_router evaluates user intent and dispatches to memory-chat, no-memory-chat, or tools-user as subflows. It is a working example of the routing pattern most real deployments end up needing.

The manifest

{
  "schemaVersion": 1,
  "key": "main-chat",
  "version": "1.0.0",
  "name": "main_chat",
  "description": "…",
  "sortOrder": 30,
  "enabledByDefault": true
}

key must equal the directory name, and must be stable, unique, and lowercase kebab-case.

version is display and audit metadata only. Change detection uses an identity-independent content hash (sha256:…) of the canonical workflow file — with workflowId deliberately omitted, so the hash describes the graph rather than the installation. A template whose version string never changes still gets a correct update signal.

Update states

Installation is tracked by a durable record linking (workspace_id, template_key) to one workflow_id, and storing applied_hash — the upstream revision last installed or synced. Comparing three things — upstream hash, applied_hash, and the current installed file's hash — gives a three-way state per template:

StateMeaning
newNo installation record in this workspace
up_to_dateNeither upstream nor your copy changed
locally_modifiedYou edited your copy; upstream is unchanged
update_availableUpstream changed; your copy is still clean
conflictBoth changed
destination_conflictNot installed, but the canonical destination is occupied
missing_fileInstallation record exists but the file is gone
invalid_copyThe destination exists but cannot be decoded or validated
inconsistentInstallation record, workflow row, path, and workflow id disagree
orphanedInstalled from a template key that no longer exists in the catalog

The distinction between locally_modified and conflict is the whole point of storing applied_hash separately from the current file hash. Without it, any edited workflow would look like a conflict and the system would nag you forever.

Syncing

Two modes:

ModeBehaviour
add_missingInstall new and explicitly selected templates; never overwrite
overwrite_allInstall missing defaults and overwrite existing installations

The flow is preview-then-confirm: GET /updates returns the per-template states, you choose, and POST /sync applies. The sync is guarded optimistically — if the state changed between the preview and the confirmation, that item comes back as state_changed rather than being applied against a stale view.

Per-template outcomes:

OutcomeMeaning
installedA new installation was created
overwrittenAn existing installation took upstream content
already_installedLeft untouched (add_missing on an existing install)
skippedNot eligible for the requested mode
failedSee error_code / error_message
state_changedState moved between preview and confirmation
not_foundThe key is not in the current catalog
blockedA destination conflict or unresolved inconsistency

Error codes are stable machine-readable strings — TEMPLATE_NOT_FOUND, INVALID_TEMPLATE_SELECTION, DESTINATION_CONFLICT, STATE_CHANGED, … — shared by the sync service and the HTTP layer, so a client can branch on them rather than parsing messages.

overwrite_all discards local edits on installed templates. Where you have customised a template and want to keep the changes, detach it first.

Detaching

DELETE /installations/:template_key

Detaching removes the installation record while leaving the workflow itself intact. The workflow becomes an ordinary workspace workflow with no template link, so it stops appearing in update checks and can never be overwritten by a sync. This is the right move once a template copy has diverged into something of your own.

Detach is also optimistically guarded: if the installation changed since the confirmation was captured, it returns a conflict and asks you to refresh.

REST surface

GET    /workflows/templates          list catalog templates
GET    /workflows/templates/:id      one template
PUT    /workflows/:id/template       mark a workflow as a template
DELETE /workflows/:id/template       unmark it

GET    /updates                      per-template update states (workspace-scoped)
POST   /sync                         apply a sync batch
DELETE /installations/:template_key  detach

The catalog projection exposed by the API never includes the workflow graph or any filesystem path — only key, name, description, category, version, sort order, and enabledByDefault.

Next steps