Skip to content
Prompting Press v0.2

Template features

Prompting Press uses MiniJinja as its template engine — a strict, sandboxed subset of the Jinja2 syntax. The feature set is deliberately narrow to preserve the sound agreement check.

These features are fully supported in prompt templates:

Variable substitution uses {{ }} delimiters:

You are a support assistant for {{ company }}. Keep your replies under {{ max_words }} words.

Variables referenced in the template must be declared in the prompt’s variables map — the agreement check catches any undeclared reference at construction time, not at render time.

{% if %} / {% elif %} / {% else %} / {% endif %}:

{% if count == 1 %}
You have 1 message.
{% elif count > 1 %}
You have {{ count }} messages.
{% else %}
You have no messages.
{% endif %}

{% for item in items %} / {% endfor %}:

Your items:
{% for item in items %}
- {{ item }}
{% endfor %}

Loop variables (the iteration variable, e.g. item above) are loop-local — the agreement check correctly excludes them from the “undeclared variable” analysis.

Standard Jinja2 filters (| upper, | lower, | join(", "), etc.) and tests (is defined, is none, etc.) are supported. Filter names are part of the engine’s built-in globals and are excluded from the agreement-check’s “required roots” set.

{%- ... -%} strips surrounding whitespace, as in standard Jinja2.


The following features are explicitly disabled and are rejected at construction with a ConsumerError::Kernel / PromptRenderError carrying code: "excluded_feature":

FeatureTag(s)Reason
Template inclusion{% include "…" %}Requires a cross-template graph walk for the agreement check, which would force the unstable AST API.
Template inheritance{% extends "…" %}Same: block resolution requires full cross-template analysis.
Import{% import "…" as … %}, {% from "…" import … %}Cross-template dependency.
Macros{% macro … %} / {% call … %}Macro calls introduce their own variable scope, breaking the single-template agreement analysis.
Block{% block … %}Only meaningful with extends; disabled alongside it.

The enforcement is structural: the Rust engine is compiled with MiniJinja’s macros and multi_template features disabled. The excluded tags are therefore unrecognised at parse time — they cannot be accidentally enabled by configuration.


The engine runs with UndefinedBehavior::Strict. A template that references a variable that was not supplied in vars at render time produces a loud KernelError::UndefinedVariable (code "undefined_variable"), never a silent empty string. This is independent of the agreement check: the agreement check catches undeclared variables at construction; strict-undefined catches any gap that slips through at render time.


The agreement check is the construction-time enforcement of the subset invariant:

template references ⊆ declared variables (in the prompt's variables map)

If a template references a variable name that is not declared in variables, construction fails immediately — Prompt::new, from_yaml, from_json, from_toml all return an error (or raise in Python / throw in TypeScript). A constructed Prompt is always agreement-sound.

prompt.check() is a pure advisory lint for the remaining live finding: an untrusted (trusted: false) variable without a declared guard. See Lint prompts in CI.

docs current as of 0.2.0