SFN Specification
Step Flow Notation
Section titled “Step Flow Notation”A concise format for describing multi-step workflows with branching and convergence. Designed for mobile input and LLM interpretation.
Step format
Section titled “Step format”N. type[:param] [args...] ["prompt"] ([after X[,Y...]][, if condition][, goto N][, => output_name])- N: step number, starting at 1
- type: one of
tool,llm, orwait_human - :param: for
tool, the CLI tool name such ascurlorjq - args…: shell-style arguments, including interpolated named outputs like
{page} - “prompt”: optional inline instruction for an
llmstep - after X: dependencies for non-linear execution
- if condition: a condition evaluated against the triggering parent output
- goto N: jump target used to create loops
- => output_name: binds the step output for reuse in later steps
Implied steps
Section titled “Implied steps”Every flow has two implied steps:
- Step 0 (start): the entry point
- Step 9999 (end): the terminal step
These steps are never written explicitly. They exist to anchor the workflow
graph and make constructs like after 0 possible.
Tool arguments
Section titled “Tool arguments”Arguments after tool:name follow shell conventions and are passed through as a
shell command.
| Form | Meaning | Example |
|---|---|---|
bareword | Positional argument | tool:curl https://example.com |
{var} | Interpolated named output | tool:curl {page_url} |
-f | Boolean flag | tool:curl -s |
-f value | Flag with a value | tool:jq -r '.name' |
--flag=value | Long flag with a value | tool:curl --output=file.html |
Quote values that contain spaces, for example:
tool:echo "hello world"Defaults and shortcuts
Section titled “Defaults and shortcuts”- Steps without
afterare sequential by default. - Step 1 implicitly depends on step 0.
- Steps without outgoing dependents and without
gotofall through to step 9999. wait_humanpauses execution until the user responds, and that response becomes the step output.
Outputs
Section titled “Outputs”Use => name to bind a step output, then reference it later with {name}.
1. tool:curl -s https://example.com => page2. llm "summarize {page}" => summary3. tool:save_note --text={summary}If a step has no => name, the output can still exist for the executor, but it
cannot be referenced by name in later steps.
Conditions
Section titled “Conditions”Conditions decide whether a step runs when its dependency completes.
Condition subject
Section titled “Condition subject”- With one dependency, the condition is evaluated against that dependency’s output.
- With multiple dependencies, the condition is evaluated against the dependency whose completion makes the step runnable.
- To target a specific named output explicitly, qualify the predicate with the
output name:
review contains("approved").
Condition language
Section titled “Condition language”Preferred short forms:
- Status tokens:
succeeded,failed - Text predicates:
contains("text"),match(/regex/) - Qualified predicates:
output_name contains("text") - Field predicates:
has(key),eq(key,"value") - Boolean operators:
and,or,not
Examples:
4. llm "fix failing tests" (after 3, if failed, goto 3)5. tool:save_db (after 2, if contains("approved"))6. llm "ask for missing info" (after 2, if not has("email"))Failure detection
Section titled “Failure detection”failed and succeeded apply across all step types. Use a sibling if failed
branch when a downstream step depends on a result that might not exist.
Branching
Section titled “Branching”Multiple steps can depend on the same parent with different conditions:
3. tool:save_db (after 2, if contains("approved"))4. llm "explain rejection" (after 2, if contains("rejected"))Convergence
Section titled “Convergence”Multiple dependencies create an AND-join:
5. llm "summarize both results" (after 3, 4)That step runs only after both parent steps complete.
Use goto N to create cycles:
3. tool:run_tests => tests4. llm "fix failing tests" (after 3, if failed, goto 3)The loop continues only while the condition is met. Otherwise execution moves forward normally.
Edge cases
Section titled “Edge cases”- Two steps with the same
afterand no conditions run in parallel. - If conditional siblings exist and one step has no condition, it acts as the default branch.
- A conditional
gotoloops only when its condition matches.
Examples
Section titled “Examples”Linear: fetch and summarize
Section titled “Linear: fetch and summarize”1. tool:curl -s https://example.com => page2. llm "summarize {page}" => summary3. wait_human4. tool:save_note --text={summary}Branching: review gate
Section titled “Branching: review gate”1. tool:curl -s https://example.com => page2. llm "analyze {page}, is it relevant?" => review3. wait_human => decision4. tool:save_db --payload={review} (after 3, if contains("approved"))5. llm "draft rejection reason" (after 3, if contains("rejected"))Parallel with convergence
Section titled “Parallel with convergence”1. tool:curl -s https://site-a.com => a2. tool:curl -s https://site-b.com (after 0) => b3. llm "compare both results: {a} vs {b}" (after 1, 2) => diff4. wait_human5. tool:send_report --text={diff}Extractive LLM with failure handling
Section titled “Extractive LLM with failure handling”1. tool:curl -s https://example.com => page2. llm "extract the pricing table from {page}" => pricing3. tool:save_note --text={pricing}4. llm "pricing not found, describe what the page contains instead" (after 2, if failed)Loop: iterative dev cycle
Section titled “Loop: iterative dev cycle”1. llm "Read PRD.md, split to tasks, save to TASKS.md" => tasks2. llm "Implement next task from TASKS.md, mark done" => impl3. tool:run_tests => tests4. llm "Fix failing tests" (after 3, if failed, goto 3)5. llm "Prepare implementation summary" (after 3, if succeeded and contains("tasks remain"), goto 2)Quick reference
Section titled “Quick reference”| Concept | Syntax | Example |
|---|---|---|
| Sequential step | N. type ... | 2. llm "summarize {page}" |
| Named output | => name | 1. tool:curl url => page |
| Dependency | after X | (after 3) |
| Parallel start | after 0 | (after 0) |
| Convergence | after X, Y | (after 1, 2) |
| Condition | if ... | (after 2, if contains("yes")) |
| Loop | goto N | (after 3, if failed, goto 2) |
| Human gate | wait_human | 3. wait_human => decision |