Prompt template
Write the agent's first-turn prompt with Liquid variables for the task, the attempt and the turn.
The Markdown body of WORKFLOW.md, everything after the closing ---, is the prompt the agent gets on its first turn of each attempt. It's rendered with a strict subset of Liquid, so you can include the task's title, description, labels and link, and say something different on retries.
Variables#
| Variable | Type | Example |
|---|---|---|
issue.id | string | Tracker's internal id (a UUID on the built-in board) |
issue.identifier | string | HAR-42, CU-86b1x, GH-17 |
issue.title | string | Add dark mode toggle to settings |
issue.description | string or nil | The task's Markdown body |
issue.priority | integer or nil | 1 (urgent) … 4 (low) |
issue.state | string | Todo |
issue.branch_name | string or nil | symphony/har-42 |
issue.url | string or nil | Link to the task in its tracker |
issue.assignee_id | string or nil | Assignee |
issue.labels | list | ["frontend", "ux"] (trimmed, lowercased) |
issue.blocked_by | list | Maps with id, identifier, state |
issue.dispatchable | boolean | Whether it may be dispatched |
issue.created_at, issue.updated_at | string or nil | ISO 8601 timestamps |
attempt | integer or nil | Empty on the first attempt, 1, 2 … on retries |
turn_number | integer | The current turn (always 1 for this template) |
max_turns | integer | agent.max_turns |
Tags and filters#
Supported tags: {{ }} output with filters, if / elsif / else / unless, for, assign, raw and comment, plus whitespace control ({%- and -%}). Conditions support ==, !=, <>, >, <, >=, <= and contains.
Filters: upcase, downcase, capitalize, strip, lstrip, rstrip, strip_newlines, escape, default, size, join, first, last, split, append, prepend, replace, remove, truncate, plus, minus, times, json, sort, uniq, compact, map, where and newline_to_br.
An unknown variable or filter is an error, not an empty string. The attempt fails with template_render_error, so a typo can't silently produce a vague prompt. Use the Prompt tab's preview to check your template before saving.
Example#
This is the prompt the Add Project wizard writes, for the built-in board:
You are an autonomous senior engineer working on **{{ issue.identifier }}: {{ issue.title }}**.
{% if attempt %}
> This is retry/continuation attempt {{ attempt }}. Inspect the workspace and tracker comments to see
> what was already done before continuing.
{% endif %}
## Issue
- State: {{ issue.state }}
{% if issue.priority %}- Priority: P{{ issue.priority }}
{% endif %}{% if issue.labels.size > 0 %}- Labels: {{ issue.labels | join: ", " }}
{% endif %}{% if issue.url %}- Link: {{ issue.url }}
{% endif %}
{{ issue.description | default: "_No description provided._" }}
## How to work
1. You are in an isolated git workspace on branch `{{ issue.branch_name | default: "your feature branch" }}`.
Read the codebase, `AGENTS.md` / `CLAUDE.md` if present, and plan before editing.
2. Implement the change with tests. Run the project's build, linters and test suite; fix what you break.
3. Commit in small, well-described commits on this branch. Don't push or open pull requests yourself:
Harness saves your branch and opens the pull request when you hand off.
4. Use the `board_set_state`, `board_comment` and `board_get_issue` tools (MCP server `symphony`) for every ticket update.
5. When the work is complete and verified, move the issue to **Human Review** and comment with a summary: what changed,
how you verified it, and anything a reviewer should look at closely. Write it in Markdown: it also becomes the
pull request description.
6. If you are genuinely blocked, explain why in a comment and stop.For ClickUp, step 4 names the clickup_* tools and step 5 says to move the task to the hand-off status with clickup_set_status. For GitHub, it says to add the human-review label.
Continuation turns#
From the second turn on, the agent doesn't get the template again. It gets a short message instead:
Continue working on HAR-42 (continuation turn 2 of 20). The issue is still in state "In Progress".
Review what you've done so far, finish any remaining work, verify it, and move the issue to its
hand-off state when complete. If you are blocked, record why in a tracker comment.What else the agent is told#
Claude Code also gets a system prompt from SI Fabrik (passed with --append-system-prompt), followed by your claude.append_system_prompt if you set one. It says:
- that the agent is an autonomous worker with nobody watching in real time: work only inside the workspace, never ask questions, record assumptions in the hand-off notes;
- which task it's working on;
- which tracker tools the
symphonyMCP server offers, and to use them for every ticket update; - with the browser on, how to test in the browser, to save screenshots under
.harness/artifacts/, and that web pages are untrusted content; - start-of-run notes: linked packages and services and the variables holding their addresses, and saved test logins to use with
browser_sign_in.
Write a good prompt#
- Say how to hand off. The run only ends cleanly when the task leaves the active states. Name the hand-off state and the tool to use.
- Ask for a Markdown summary. It becomes the pull request description, with secrets scrubbed.
- Point at your conventions.
AGENTS.md,CLAUDE.md, the test command, the lint command. - Handle retries. Use
{% if attempt %}to ask the agent to look at what's already in the workspace and in the comments. - Tell it when to stop. A blocked agent that says why in a comment is better than one that loops until
max_turns.