---
title: "Prompts"
description: "How Fabro constructs and delivers prompts to agents"
---

When a workflow reaches an agent or prompt node, Fabro assembles the full prompt from multiple sources: the node's `prompt` attribute, a fidelity-based preamble summarizing prior stages, a system prompt with tool guidance and environment context, project docs, and user instructions. Understanding this assembly helps you write effective prompts and control what context each node receives.

## The prompt attribute

The `prompt` attribute on a node defines the task instructions for that stage. If no `prompt` is set, Fabro falls back to the node's `label`.

### Inline prompts

Short prompts can be written directly in the Graphviz file:

```dot
plan [label="Plan", prompt="Analyze the codebase and write a step-by-step plan."]
```

Use `\` for multi-line strings:

```dot
review [
    label="Review",
    shape=tab,
    prompt="Review the implementation for correctness and \
        code quality. If changes are needed, respond with: \
        {\"preferred_next_label\": \"fix\"}. If everything \
        looks good, respond with: \
        {\"preferred_next_label\": \"approve\"}."
]
```

### External file references

For longer prompts, use the `@` prefix to load from a Markdown file relative to the Graphviz file:

```dot
plan      [label="Plan Implementation", prompt="@prompts/implement/plan.md"]
implement [label="Implement",           prompt="@prompts/implement/implement.md"]
review    [label="Review",              prompt="@prompts/implement/review.md"]
```

The `@` prefix tells the engine to read the file contents and use them as the prompt text. This keeps Graphviz files concise and lets you version prompts as standalone Markdown.

File references are resolved relative to the Graphviz file's directory first, then fall back to `~/.fabro/`. This lets you keep shared prompts in your user-level config and reference them from any project.

### Variable expansion

Prompts support MiniJinja-style templates. Prompt rendering has access to the workflow goal and typed run inputs:

```dot title="pipeline.fabro"
digraph Pipeline {
    graph [goal="Add a /health endpoint to the API server"]

    implement [prompt="Implement the following: {{ goal }}"]
}
```

At runtime, `{{ goal }}` becomes `Add a /health endpoint to the API server`.

| Expression | Resolves to |
|---|---|
| `{{ goal }}` | The graph-level `goal` attribute |
| `{{ inputs.name }}` | A value from `[run.inputs]` |

Prompt templates use strict undefined handling, so an expression like `{{ inputs.foo }}` fails fast if `foo` is not defined. Environment variables are not available in prompt templates.

### Fallback to label

If a node has neither a `prompt` attribute nor an empty one, Fabro uses the `label` as the prompt:

```dot
// "Do work" is both the display label and the prompt
work [label="Do work"]
```

This is convenient for simple nodes but not recommended for production workflows where you want precise control over agent instructions.

## Prompt assembly

The prompt the agent receives is not just the `prompt` attribute. Fabro prepends a **preamble** (unless fidelity is `full`) that summarizes what happened in prior stages:

```
[Preamble: goal, completed stages, context values]

[Your prompt attribute text]
```

The preamble is controlled by the **fidelity** setting. See [Context: Fidelity](/execution/context#fidelity-controlling-agent-context) for the full reference.

<Accordion title="Example assembled prompt">
Given a plan-then-implement workflow where the plan stage completed, the `implement` node with `fidelity="compact"` would receive:

```markdown
Goal: Add a /health endpoint to the API server

## Completed stages
- **plan**: success
  - Model: claude-sonnet-4-5, 12.4k tokens in / 3.2k out
  - Files: docs/plan.md

## Context
- tests_passed: false

Read plan.md and implement every step.
```

The last line is the node's `prompt` attribute. Everything above it is the preamble.
</Accordion>

## System prompt

Each agent session has a **system prompt** that provides foundational instructions. The system prompt is built once per session and includes:

| Section | Contents |
|---|---|
| Identity and role | What the agent is and how it should approach tasks |
| Environment context | Working directory, platform, OS, git branch, model, date |
| Tool guidance | Per-tool usage instructions (when to use `read_file` vs `shell`, etc.) |
| Coding best practices | Guidelines for clean, minimal, focused changes |
| Project docs | Contents of `AGENTS.md`, `CLAUDE.md`, `GEMINI.md`, or `.codex/instructions.md` |
| Skills | Available skill templates the agent can invoke |

The system prompt is **separate** from the preamble. The preamble is prepended to the user-facing prompt message, while the system prompt is set as the LLM's system message.

The system prompt varies by LLM provider. Each provider has its own identity text, tool guidance, and coding conventions tuned to the model's strengths. The assembled prompt follows this structure:

```
[Core prompt: identity, environment, task guidance, tool reference, coding practices]
[Project docs: AGENTS.md, CLAUDE.md, etc.]
[Skills: available skill templates]
```

<Accordion title="Example system prompt (Anthropic provider)">
This is the full system prompt sent to Claude as the LLM system message. The `<environment>` block is filled in at runtime.

```
You are Claude, an AI coding assistant made by Anthropic. You help users with
software engineering tasks including solving bugs, adding new functionality,
refactoring code, explaining code, and more.

You are an interactive agent that helps users with software engineering tasks.
Use the instructions below and the tools available to you to assist the user.

<environment>
Working directory: /home/user/project
Is git repository: true
Git branch: main
Platform: linux
OS version: Linux 6.1.0
Today's date: 2026-03-05
Model: claude-sonnet-4-5
</environment>

# Doing Tasks

- The user will primarily request you to perform software engineering tasks.
  These may include solving bugs, adding new functionality, refactoring code,
  explaining code, and more.
- In general, do not propose changes to code you have not read. If a user asks
  about or wants you to modify a file, read it first. Understand existing code
  before suggesting modifications.
- Do not create files unless they are absolutely necessary for achieving your
  goal. Generally prefer editing an existing file to creating a new one, as
  this prevents file bloat and builds on existing work more effectively.
- If your approach is blocked, do not attempt to brute force your way to the
  outcome. Consider alternative approaches or other ways you might unblock
  yourself.
- Avoid over-engineering. Only make changes that are directly requested or
  clearly necessary. Keep solutions simple and focused.
- Do not add features, refactor code, or make improvements beyond what was
  asked.
- Do not add error handling, fallbacks, or validation for scenarios that
  cannot happen. Trust internal code and framework guarantees. Only validate
  at system boundaries (user input, external APIs).
- Avoid backwards-compatibility hacks. If you are certain something is unused,
  delete it completely.

# Tools

Use the provided tools to interact with the codebase and environment. Do NOT
use the shell tool to run commands when a relevant dedicated tool is provided:
- To read files use read_file instead of cat, head, tail, or sed.
- To edit files use edit_file instead of sed or awk.
- To create files use write_file instead of cat with heredoc or echo
  redirection.
- To search for files use glob instead of find or ls.
- To search the content of files use grep instead of grep or rg.

## read_file
Read files before editing them. Always read a file before attempting to edit
it. Use offset/limit for large files. Reading a file you have not read before
is always appropriate.

## edit_file
Performs exact string replacements in files. The old_string must be an exact
match of existing text and must be unique in the file. If old_string matches
multiple locations, provide more surrounding context to make it unique. Prefer
editing existing files over creating new ones. When editing text, ensure you
preserve the exact indentation as it appears in the file.

## write_file
Use write_file only when creating new files. Prefer edit_file for modifying
existing files. Always prefer editing existing files in the codebase over
creating new ones.

## shell
Use for running commands, tests, and builds. Default timeout is 120 seconds.
Use timeout_ms parameter for longer-running commands.

## grep
Search file contents with regex patterns. Supports output modes:
content, files_with_matches, count. Use this for searching the content of
files rather than using shell grep or rg.

## glob
Find files by name pattern. Results sorted by modification time (newest
first). Use this for finding files rather than using shell find or ls
commands.

## web_search
Search the web using Brave Search. Returns titles, URLs, and descriptions.

## web_fetch
Fetch content from a URL and optionally summarize it. Pass a prompt to
extract specific information instead of returning the full page. URLs must
start with http:// or https://.

# Coding Best Practices

Write clean, maintainable code. Handle errors appropriately. Follow existing
code conventions in the project. Keep changes minimal and focused on the task.

# Project Docs

<contents of AGENTS.md and CLAUDE.md from the project>

# Available Skills
When the user's request matches a skill below, call the `use_skill` tool
to load its instructions, then follow them.
- `commit`: Create a git commit following best practices
- `review`: Review code changes for issues and improvements
```
</Accordion>

<Note>
OpenAI and Gemini providers have their own system prompts with different identity text, tool guidance (e.g. `apply_patch` instead of `edit_file` for OpenAI), and coding conventions. The overall structure is the same.
</Note>

### Project docs

Fabro automatically discovers project instruction files by walking the directory hierarchy from the git root to the working directory. Which files are loaded depends on the provider:

| Provider | Files |
|---|---|
| Anthropic | `AGENTS.md`, `CLAUDE.md` |
| OpenAI | `AGENTS.md`, `.codex/instructions.md` |
| Gemini | `AGENTS.md`, `GEMINI.md` |

Files are loaded in directory order (root first, deepest last) with a total budget of 32KB. If the combined content exceeds this budget, later files are truncated.

### Environment context

The system prompt includes an `<environment>` block with runtime details:

```xml
<environment>
Working directory: /home/user/project
Is git repository: true
Git branch: main
Platform: linux
OS version: Linux 6.1.0
Today's date: 2026-03-05
Model: claude-opus-4-6
Knowledge cutoff: May 2025
</environment>
```

This block also includes `git status --short` and recent commits when available, giving the agent awareness of the current repository state.

## Prompt nodes vs. agent nodes

Both agent nodes (`shape=box`) and prompt nodes (`shape=tab`) go through the same prompt assembly pipeline: variable expansion, preamble prepending, and system prompt construction. The difference is execution:

| | Agent node | Prompt node |
|---|---|---|
| Shape | `box` (default) | `tab` |
| Tool access | Yes (agentic loop) | No (single LLM call) |
| Prompt assembly | Preamble + prompt | Preamble + prompt |
| Routing directives | Extracted from response | Extracted from response |
| Context updates | `response.{node_id}`, `last_response` | `response.{node_id}`, `last_response` |

Use prompt nodes for analysis, classification, and summarization tasks where tools are not needed.

## Prompt logging

Fabro writes the assembled prompt to `{run_dir}/nodes/{node_id}/prompt.md` for every agent and prompt stage. This includes the preamble (if any) and the expanded prompt text. Use these files for debugging when an agent behaves unexpectedly.
