Skip to content

Anatomy of an agent prompt

Every pipeline stage talks to Claude through a prompt that worca assembles at runtime. That prompt is not a single block of text — it’s three layers of context, two of them built from composable templates and filled with per-run variables. This page explains every part: where the pieces live, how they’re merged, and exactly which variables each stage can use.

If you only want to customize a prompt, the practical how-to is Overriding agent prompts. This page is the model behind it.

Before a stage runs, the agent receives context from three distinct layers. Knowing which layer owns what is the key to putting information in the right place.

  1. CLAUDE.md — ambient & primary. Claude Code automatically reads CLAUDE.md from the project root and loads it for every agent, on every stage, with no templating involved. It’s the highest-traffic context layer and the one you most often control. This is where project-wide, stage-agnostic knowledge belongs: build and test commands, house style, architectural conventions, code-hosting quirks. If a fact is true regardless of which agent is running, it goes here — not in an agent prompt.
  2. The system prompt — per role. The resolved core agent template (planner.md, implementer.md, …) defines who the agent is: its role, process, output contract, rules, and governance constraints. Stable across iterations of a run.
  3. The user message — per iteration. The resolved stage block (plan.block.md, implement.block.md, …) carries the dynamic content: the work request, the assigned task, retry feedback, accumulated context. This is what changes between a first attempt and a retry.

The rest of this page covers layers 2 and 3 — the templated halves worca builds itself.

worca ships two kinds of template file, both under src/worca/agents/core/ (copied into your runtime as .claude/worca/agents/core/):

FileRoleBecomes
<agent>.mdCore agent template — role, rules, governanceThe system prompt
<block>.block.mdStage block — dynamic per-iteration contentThe user message

Each stage maps to one agent and one block:

StageAgent templateStage block
Planplanner.mdplan.block.md
Plan Reviewplan_reviewer.mdplan-review.block.md
Coordinatecoordinator.mdcoordinate.block.md
Implementimplementer.mdimplement.block.md
Testtester.mdtest.block.md
Reviewreviewer.mdreview.block.md
Guardian (PR)guardian.mdpr.block.md
Learnlearner.mdlearn.block.md

The Preflight stage runs no agent — it’s deterministic environment checking, so it has no prompt.

Neither file is read in isolation. Each is resolved through a three-tier overlay chain so a project (or a template) can customize a prompt without forking the shipped version:

  1. Core — the shipped base (.claude/worca/agents/core/<name>).
  2. Project override — your file in .claude/agents/<name>, layered on top.
  3. Template override — a pipeline template can ship its own layer, applied last.

Both agent templates and block files use the same chain and the same replace-vs-append merge rules, and governance-protected sections can never be overridden. Those mechanics — <!-- replace -->, <!-- append -->, ## Override: section merge, <!-- governance --> — are documented in full on Overriding agent prompts. What matters here is that by the time a prompt is rendered, the overlay chain has already produced one merged template per layer.

Once a template is merged, worca resolves its tokens against a per-stage context dictionary. Four token forms are supported:

TokenMeaning
{{name}}Substitute the value of name from the context.
{{name|default text}}Substitute name, or default text if it’s empty/unset.
{{#if name}}…{{else}}…{{/if}}Include a region only when name is truthy. Nestable.
{{block:name}}Insert another block file (resolved through the same overlay chain). Must sit on its own line.

Resolution runs in a fixed order: block insertions → conditionals (innermost first) → simple placeholders → cleanup. Resolving inside-out is what lets a block wrap its body in {{#if has_guide}}…{{/if}} and have the guide appear only when one was attached.

The context dictionary is assembled fresh for each stage. Some variables are present everywhere; most are stage-specific. Empty values simply render as nothing (and gate their {{#if}} regions off).

Available to every stage’s block:

VariableTypeWhat it carries
work_requesttextThe task: title + description (no heading — the block supplies it).
assigned_tasktextThe assigned bead’s ID, title, and description. Empty when no bead is assigned.
guide_contenttextThe body of the attached --guide document, if any.
has_guideboolTrue when a guide is attached — gates the normative-guide block.
accumulated_design_notestextAdvisory design notes recorded by sibling beads this run (capped, oldest dropped first).
has_design_notesboolTrue when sibling design notes exist.
has_graphifyboolTrue when a queryable code knowledge graph is ready for this run.

Plan

VariableTypeWhat feeds it
plan_revision_modeboolTrue when looping back from Plan Review — switches the block into revision mode.
plan_contenttextThe current MASTER_PLAN.md (revision mode only).
plan_review_issues_formattedtextPlan Reviewer issues to address (revision mode).
plan_review_history_formattedtextPrior plan-review attempts (revision mode).

Plan Review

VariableTypeWhat feeds it
plan_contenttextThe plan under review (from context, or read from MASTER_PLAN.md).
plan_review_history_formattedtextEarlier review attempts, on iterations after the first.

Coordinate

VariableTypeWhat feeds it
plan_summarytextApproach + task outline distilled from the Planner’s structured output.
unresolved_plan_issues_formattedtextPlan-review issues carried forward unresolved.

Implement

VariableTypeWhat feeds it
is_retryboolTrue on attempts after the first — switches the block into fix mode.
issue_typetextWhat triggered the retry: Test Failures, Review Issues, or Issues.
attempt_countnumberWhich attempt this is.
test_failures_formattedtextFailing tests and their errors to fix.
review_issues_formattedtextReviewer issues to fix (severity, file:line, description).
previous_attemptstextSummary of earlier failed attempts.

Test

VariableTypeWhat feeds it
implementation_summarytextFiles changed and tests added by the Implementer.

Review

VariableTypeWhat feeds it
test_resultstextPass/fail, coverage, and proof artifacts from the Tester.
files_changed_formattedtextBullet list of changed files.

Guardian (PR)

VariableTypeWhat feeds it
plan_approachtextThe plan’s approach line — gates the ## Approach section.
pr_title_prefixtextResolved at dispatch time to prefix the PR title.

Learn

VariableTypeWhat feeds it
termination_typetextHow the run ended.
run_idtextThe run identifier.
run_datatext (JSON)The full run status, truncated past 50 KB.
plan_contenttextThe plan the run executed.
files_changed_since_git_headtextgit diff --stat from the run’s starting commit to HEAD — ground truth for what the pipeline produced.

The dictionary isn’t static — each stage writes its structured output back into a shared context that downstream stages read. The Planner’s approach feeds the Coordinator’s plan_summary; the Tester’s results feed the Reviewer’s test_results; a Reviewer bounce feeds the Implementer’s review_issues_formatted on the next attempt. This accumulated context is persisted to prompt_context.json (capped at 100 KB, oldest keys dropped first) so a resumed run rebuilds the same picture. The retry loops are just this mechanism in a cycle: failure detail flows back into the next iteration’s block.

Authority & precedence of injected sources

Section titled “Authority & precedence of injected sources”

When several sources describe what to do, agents follow a fixed order of authority:

guide > plan > graph > description

The prompt encodes this. An attached guide is injected under a normative header that explicitly tells the agent to treat any guide-vs-description conflict as a bug in the description and surface it. The knowledge graph and accumulated design notes are framed as advisory orientation, below the plan. See Plans, work requests & guides for the concept, and Knowledge graph for the graph layer.

Nothing about this is hidden at runtime. For every iteration, worca writes the fully-resolved system prompt to agents/resolved/<stage>-<agent>-iter-N.md inside the run directory, and records the rendered user message in the run status. In the dashboard, the expanded Agent Instructions panel separates the resolved system prompt from the work-request message, so you can read exactly what an agent was told. See Monitoring a run.

The clearest way to see the engine is to watch one block render. Here’s the retry branch of implement.block.md (trimmed):

{{#if is_retry}}
## PRIORITY: Fix {{issue_type}} (attempt {{attempt_count}})
{{#if review_issues_formatted}}
### Issues to Fix
{{review_issues_formatted}}
{{/if}}
{{#if previous_attempts}}
### Previous Attempts (all failed to resolve)
{{previous_attempts}}
{{/if}}
---
### Reference: Task & Plan (already implemented)
{{#if assigned_task}}
{{assigned_task}}
{{/if}}
{{work_request}}
{{/if}}

Now suppose the Reviewer bounced the work on the first attempt. The Implement stage runs again with this context:

VariableValue
is_retrytrue
issue_typeReview Issues
attempt_count2
review_issues_formatted1. [high] src/auth/session.py:42“ … Token TTL not enforced.
previous_attempts(empty — only one prior attempt)
assigned_taskBead wc-12, “Add session expiry”, …
work_requestAdd session expiry …”

The engine resolves conditionals (dropping the empty previous_attempts region), substitutes the placeholders, and produces the user message:

## PRIORITY: Fix Review Issues (attempt 2)
### Issues to Fix
1. [high] `src/auth/session.py:42`
Token TTL not enforced — sessions never expire.
---
### Reference: Task & Plan (already implemented)
**Bead ID:** wc-12
**Title:** Add session expiry
**Description:** Enforce a 30-minute idle TTL on sessions.
**Add session expiry**
Sessions currently never expire…

That message is paired with the Implementer’s role/rules system prompt and the ambient CLAUDE.md, and sent to Claude — the full prompt the agent acts on.