Skip to main content

Module render

Module render 

Source
Expand description

system_prompt template rendering for the Operator path.

Lets the agent.md body (AgentDef.profile.system_prompt) carry Jinja2-compatible syntax ({{ directive }} / {% if intent %} / {{ x | upper }} and so on). The caller passes TaskSpec.initial_directive as JSON and its fields expand into the template slots.

§Engine choice

minijinja (maintained by Armin Ronacher, the Jinja2 author — a light dependency). Two defaults are forced up front to avoid classic traps:

  • auto_escape = None. HTML auto-escape is off; for LLM prompts, turning < / > into &lt; / &gt; corrupts the prompt.
  • UndefinedBehavior::Strict. A typo’d variable would otherwise silently render as an empty string; in a production prompt template we want that to fail loud.

§Syntax available inside the agent.md body (Jinja2-compatible, per

minijinja v2)

Variables:  {{ directive }} / {{ slot.nested }} / {{ items[0] }}
Filters:    {{ name | upper }} / {{ x | default("fallback") }} / {{ s | length }}
Branch:     {% if intent %}...{% elif other %}...{% else %}...{% endif %}
Loop:       {% for x in items %}{{ x }},{% endfor %}
Comment:    {# note #}
Raw:        {% raw %}{{ literal }}{% endraw %}

Macros, include, and inheritance are not available here — this layer performs a flat render over one string handed in by the caller, and does not support multi-template composition. If we ever need that, adding a source loader to Environment is a carry.

§Slot names (the variables the caller can reference in the

template)

slots_from_prompt(prompt: &str) builds the slot map:

  • When prompt is a JSON object, the object’s top-level keys become the slot names — for example r#"{"directive":"X","intent":"fix"}"# exposes {{ directive }} and {{ intent }}.
  • When prompt is anything else (a plain string, a number, an array, an already-stringified JSON), it is wrapped as {"directive": <the prompt itself>}; only {{ directive }} is available.

To expose additional slots, the caller (whoever assembles TaskSpec.initial_directive) passes a JSON object. Conventions:

  • directive (effectively required) — the main task instruction; the plain-prompt fallback also lives here.
  • intent — task kind / classification hint (optional; used in if branches).
  • context — additional context (optional).
  • Beyond those, agent.md authors are free to add whatever slots the template needs.

§Errors

  • Undefined variable → RenderError::Template (strict mode).
  • Syntax error → RenderError::Template.
  • On the OperatorSpawner path this is wrapped in SpawnError::Internal("render system_prompt: ...") and propagated — no silent fallback, fail loud.

Enums§

RenderError
Render errors. Anything from minijinja is wrapped as Template.

Functions§

render_system
Render a system_prompt template in strict mode with auto-escape disabled.
slots_from_prompt
If prompt parses as a JSON object, treat it as the slot map; otherwise wrap it as {"directive": prompt}. Corresponds to the initial_directive intake convention on the caller side (OperatorSpawner).