The WORKFLOW.md guide
How a project is configured, how to edit it in the app, and what happens when you save.
Each project is configured by one file, WORKFLOW.md, in the root of its repository (or another path you choose). It has two parts: YAML front matter between --- lines, which configures the project, and a Markdown body, which is the prompt the agent gets on its first turn.
---
tracker:
kind: local
active_states: [Todo, In Progress]
terminal_states: [Done, Canceled]
workspace:
root: ~/HarnessWorkspaces/my-app
docker:
enabled: true
git:
pull_request: draft
---
You are an autonomous senior engineer working on **{{ issue.identifier }}: {{ issue.title }}**.
{{ issue.description | default: "_No description provided._" }}
When the work is complete and verified, move the issue to **Human Review** and comment with a summary.Because the file lives in the repository, it's versioned with your code, reviewed like code, and the same file works in Local and Remote mode.
Create one#
The Add Project wizard writes a complete, commented WORKFLOW.md for you, with your tracker, runner, model, Docker and pull-request choices filled in and the optional sections commented out. You rarely write one from scratch. See Example workflows for complete files.
Edit it in the app#
Open the project's Workflow tab. It has three views of the same draft:
- Settings: a form for tracker states, agents and models, branch and PR options, limits, sandbox, browser and preview, links, gateway and hooks.
- Prompt: the Liquid template, with a live preview rendered against a sample task (
HAR-42: Add dark mode toggle to settings). Toggle Retry to preview what a retry attempt sees. Copy a variable inserts{{ issue.identifier }},{{ attempt }}and the others. - Source: the raw file.
Next to the editor, the Effective configuration inspector shows what SI Fabrik will actually use, after defaults and $VAR resolution:
| Group | Shows |
|---|---|
| Variables | Every $NAME the workflow references, and docker.env names, with where each value comes from: project, global, the SI Fabrik process, or not set |
| Tracker | Kind, active and terminal states, required labels, poll interval |
| Agents | Runner, concurrency (and per-state caps), max turns, max backoff, stall and silence timeouts |
| Claude Code / Codex | Command, model, permission mode or approval policy, budget per session |
| Model gateway | Whether it's on, the base URL, where the key comes from, model slots |
| Branch & PR | Managed clone or after_create hook, base branch, save, hand-off action, PR provider, where git runs |
| Limits | Daily budget and tokens, cost cap per task, parking and pausing thresholds |
| Links | Packages, services, who it's shared with, who may file tasks here, and any link problems |
| Isolation | Where agents run, the image, the workspace root, where hooks run, and security warnings |
How form edits are written#
Form edits change only the lines for that key. Comments, ordering and your own formatting are kept, and commented-out template keys such as # daily_budget_usd: 50 are filled in place. Every edit is checked by re-parsing the file; an edit that wouldn't read back exactly is rejected. If the YAML doesn't parse, the form is replaced by a message and you fix it in Source.
Save and hot reload#
- Review the diff
Press Save… (⌘S). SI Fabrik shows a diff of your changes. If the file changed on disk since you opened it, the sheet warns you that saving replaces those changes.
- Save
Confirm. The running orchestrator reloads the workflow right away; no restart needed. Edits you make in your own editor are picked up the same way.
For a project on a harnessd server, the Workflow tab reads the file from the server (operator role) and Save… writes it there (admin role). The server refuses a save if the file changed since you opened it (409 changed) or if it doesn't validate (422 invalid_workflow).
Validation#
SI Fabrik validates the workflow when it loads and before every dispatch. A project with errors shows them in its header and doesn't dispatch.
| Code | Meaning |
|---|---|
missing_workflow_file | The file can't be read |
workflow_parse_error | Invalid YAML, or front matter opened with --- but never closed |
workflow_front_matter_not_a_map | The front matter isn't a YAML map |
missing_tracker_kind | tracker.kind is required |
unsupported_tracker_kind | Not one of local, clickup, github |
invalid_tracker_config | For example, tracker.active_states is empty |
invalid_hooks_timeout | hooks.timeout_ms must be positive |
invalid_max_turns | agent.max_turns must be positive |
invalid_max_concurrent_agents | agent.max_concurrent_agents must be 0 or more |
missing_claude_command / missing_codex_command | The runner's command is empty |
template_parse_error / template_render_error | The prompt template is invalid, or uses an unknown variable or filter |
Problems in links: (an invalid URL, a reserved token name, a project that doesn't share back) also block dispatch and show under Links in the inspector.
Secrets in WORKFLOW.md#
Never put a secret value in the file: it's committed, and agents can read it in their workspace. Reference variables instead:
tracker:
kind: clickup
provider:
api_token: $CLICKUP_API_TOKEN
gateway:
api_key: $LITELLM_API_KEY$NAME and ${NAME} are resolved on the host, from the project environment, then the global environment. See Environment and variables.