Cori
Guides

Author a workflow

The complete eight-step procedure for turning a conversation into a Cori workflow — whether done by you or by a coding agent.

This is the core authoring procedure. A coding agent with the cori_save_workflow skill follows these steps automatically; you can also follow them manually.

Prerequisite: you've completed a multi-step task in a conversation (or have a clear plan). See Capture from an agent conversation for the typical flow.

Re-read the conversation

Go through the conversation and categorize each action:

  • Productive — an action that contributed to the final result
  • Dead end — a failed attempt, a retried command, a wrong direction
  • Scaffolding — one-time setup that isn't part of the repeatable workflow

Only productive actions become workflow steps. Dead ends and scaffolding are excluded.

Decide where the folder lives

The workflow folder lives in your repository (or wherever you want to version it). Pick a location and a name that describes what the workflow does:

repos/my-workflows/translate_product_sheets_fr/

You'll run it with:

cori run ./translate_product_sheets_fr

Or publish it to a git repo and run by ref:

cori run github.com/org/workflows/translate_product_sheets_fr@v1.0.0

Decide parameters

For each value the workflow uses, ask: would this change on the next run?

  • Yes → it's a parameter (declared in the manifest, passed on the CLI)
  • No → it's a constant (hardcoded in the step)

Common parameters: input file paths, target languages, spreadsheet IDs, environment flags (like dry_run). Avoid over-parameterizing — if something never changes in practice, make it a constant.

See Define parameters for the full guide.

Decompose into activity kinds

For each productive action, pick the right kind:

What happenedKind
Shell command or CLI toolcli
MCP tool call (Notion, Sheets, GitHub…)mcp_tool
Pure TypeScript computation, no I/Ocode
LLM call for new runtime datallm
Fan-out, branch, or waitbuiltin ⚠️ deferred

See Choose an activity kind for the decision tree and rules.

builtin steps are deferred in v1. If your workflow needs branching or fan-out, model it as sequential steps for now.

Write the step files

Create one file per step, numbered with a two-digit prefix:

steps/
  01_read_source_rows.ts
  02_translate_rows.ts
  03_check_gpsr.ts

Each file exports a typed input Zod schema, a typed output Zod schema, and a default step.* export. See Reference: SDK for the step constructors and Reference: Activity kinds for templates.

steps/01_read_source_rows.ts
import { step } from '@cori-do/sdk';
import { z } from 'zod';

export const input = z.object({ input_file: z.string() });
export const output = z.object({ rows: z.array(z.record(z.string())) });

export default step.cli({
  description: 'Read rows from the source CSV',
  command: ({ input }) => `csvkit --csv ${input.input_file}`,
  parse_output: (stdout) => ({ rows: JSON.parse(stdout) }),
});

Also create types.ts for any shared types used across steps.

Write the manifest

Create manifest.md in the workflow root. The frontmatter declares the workflow's identity, parameters, and required tools:

---
id: translate-product-sheets-fr
name: Translate product sheets to French
description: Reads a product CSV, translates the description column to French using an LLM, and writes results to a Google Sheet.
version: "1.0.0"
created: "2025-01-15"
parameters:
  - name: input_file
    type: path
    description: Path to the source CSV file
  - name: spreadsheet_id
    type: string
    description: Google Sheets spreadsheet ID
  - name: dry_run
    type: boolean
    default: false
    description: Skip the write step if true
tools_required:
  - csvkit
mcp_servers:
  - google-sheets
---

See Reference: Manifest for the full field reference.

Review before disk write

Before the folder is written (or if you're reviewing an agent-written folder), check:

  • Parameters are correct — not too many, not too few
  • Each step's kind matches what it actually does
  • Required tools and MCP servers are declared in the manifest
  • Step file names are numbered sequentially
  • types.ts covers all shared types

Nothing is written until you approve. When using the agent skill, this review happens interactively.

Run cori check, then suggest cori run --dry-run

cori check ./translate_product_sheets_fr

This validates the manifest, compiles the step files, and verifies declared tools and MCP servers — without executing. Fix any errors it reports.

Then do a dry run to confirm the workflow structure without executing the destructive steps:

cori run --dry-run ./translate_product_sheets_fr input_file=products.csv spreadsheet_id=1BxiM...

When that looks right, run for real:

cori run ./translate_product_sheets_fr input_file=products.csv spreadsheet_id=1BxiM...

See the cookbook example for a complete worked-through workflow that follows this procedure.

On this page