Architecture
Architecture
mentu-recipes is a single Swift package with two targets. MentuRecipesCore
is a library that holds everything that decides or does something.
mentu-recipes is a command-line target of a few hundred lines that parses
arguments, calls the core, and prints. Everything below is in the public
source at mentu-ai/mentu-recipes.
There is no daemon, no background service, and no network call unless a step
uses a network backend or you turn on the cloud hooks.
The core modules
Loading. RecipePaths decides where files may come from: recipes under
<workspace>/.mentu/recipes then ~/.mentu/recipes, prompts under the
matching prompts directories. A path that resolves outside those roots after
symlinks are followed is refused. RecipeStore decodes the JSON, validates
names, labels, prompts, and dependencies, and produces a topological order.
PromptRenderer reads an inline prompt or a prompt file and substitutes
$KEY and ${KEY} from the run's variables.
Backends. AdapterRegistry holds seven built-in adapters: shell,
openai, openai-chat, deepseek, ollama, claude, and codex. Each
adapter reports a capability record (execution kind, stream format, completion
policy, whether it needs a credential or the network, which step options it
supports) that doctor and the runner consult. A recipe can add custom
OpenAI-compatible providers with a base_url and its own credential name.
CredentialResolver turns credential names into values from the environment
or the macOS Keychain, and the vault command is a thin wrapper over it.
ProviderLogSanitizer strips terminal escape sequences from output lines
before the progress view shows them.
Running. RecipeRunner is the scheduler. For a sequence it computes
dependency layers and runs each layer's steps concurrently, capped by
--max-parallel or the recipe's max_parallel. For pipeline, parallel, and
compound recipes it runs child recipes as nodes with the same layer logic.
ProcessRunner and OutputBuffer execute the step and capture its output
with a timeout. HookRunner runs the recipe's before_run, before_step,
after_step, on_error, and after_run shell hooks and records their exit
codes.
Checking a step. After a step exits, three things happen in order.
Verification runs the step's verify block: grep_present, grep_absent,
file_absent, git_clean_outside, and arbitrary commands. GitWorkspace
compares the working tree against the baseline captured at run start,
commits the paths the step declared in expected_changes, and writes any
undeclared change out as a patch under the run's quarantine/ directory
rather than committing it. WorkspaceBaseline is what makes that comparison
possible: it records which paths were already dirty before the run so that
pre-existing changes are not blamed on a step.
Recording. RunStateStore keeps state.json, the per-step state machine
(pending, running, success, warn_bookkeeping, failed, skipped,
cancelled) that resume and retry-step read. RunEventWriter appends
sequence-numbered events to events.jsonl. The run record itself, run.json,
is rewritten after every step. RunReporter renders it as markdown, JSON, or
CSV for report; RunAnalyzer aggregates many records for analyze-runs.
Quality and hygiene. RecipeDoctor scores a recipe without running it.
ReleaseScanner is the scan command: it looks for secret-shaped tokens,
local user paths, and internal names before a release is published, and the
project runs it on itself.
Cloud hooks. MentuCloudClient is the only module that talks to a Mentu
server. It is constructed only when --cloud is passed or the recipe sets
cloud.enabled, and only if a MENTU_API_KEY is present in the environment
or the Keychain. Without one, every run is local-only, which is what the
run record says.
What a run leaves on disk
Each run writes one directory, .mentu/runs/<run-id>/, holding run.json,
events.jsonl, baseline.json, state.json, one <step>.stdout and
<step>.stderr per executed step, and a quarantine/ directory when a step
touched undeclared paths. Steps with expected_changes also create a git
commit in your repository, so the audit trail is your git log plus those
files. The CLI reference describes each file.
These records are plain JSON. Nothing in the runner chains them, signs them, or verifies them after the fact.
Relationship to mentu-execution-graph-core
mentu-ai/mentu-execution-graph-core is a separate public Swift library, MIT-licensed, that defines a canonical form for contract-bearing execution graphs: RFC 8785 canonical JSON, lowering of a candidate graph into a hashed executable artifact, qualification and admission checks, and frontier scheduling with hash-linked outcomes. It performs no I/O and calls no model. It is the reference implementation behind the Agent Graph Runtime preprint at mentu-ai/agent-graph-runtime.
mentu-recipes does not depend on that package and does not import it. The
runner's scheduler, records, and verification are implemented in
MentuRecipesCore on their own. The two projects describe adjacent layers,
recipes as files on one side and hashed graph artifacts on the other, but
today they share no code.
What is not here
The runner does not ship a hash-chained ledger, trust scoring, hash-gated recipe commitment, per-step worktree isolation, or VM isolation. The public repositories are mentu-recipes, mentu-execution-graph-core, and the Homebrew tap; nothing else is documented on this site.