Projects

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#

VariableTypeExample
issue.idstringTracker's internal id (a UUID on the built-in board)
issue.identifierstringHAR-42, CU-86b1x, GH-17
issue.titlestringAdd dark mode toggle to settings
issue.descriptionstring or nilThe task's Markdown body
issue.priorityinteger or nil1 (urgent) … 4 (low)
issue.statestringTodo
issue.branch_namestring or nilsymphony/har-42
issue.urlstring or nilLink to the task in its tracker
issue.assignee_idstring or nilAssignee
issue.labelslist["frontend", "ux"] (trimmed, lowercased)
issue.blocked_bylistMaps with id, identifier, state
issue.dispatchablebooleanWhether it may be dispatched
issue.created_at, issue.updated_atstring or nilISO 8601 timestamps
attemptinteger or nilEmpty on the first attempt, 1, 2 … on retries
turn_numberintegerThe current turn (always 1 for this template)
max_turnsintegeragent.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.

Strict rendering

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:

WORKFLOW.md (body)
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:

TEXT
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 symphony MCP 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.

See also#