Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Recipe Activation (the Active Set)

Recipes are a two-layer system, the same way installed skills relate to a marketplace:

  • The cookbook is every recipe Pretorin can load — built-in, your own (~/.pretorin/recipes/), your team’s (./.pretorin/recipes/), and explicit paths. It grows as you author and clone recipes. See Overview and Publishing.
  • The active set is the curated subset of the cookbook provisioned for one compliance effort — one (system, framework) scope. It is stable and confirmed: established once, edited deliberately, not re-derived on every interaction.

Provisioning answers which recipes are in the room for this effort. It does not change how a recipe is chosen for a given task — that is still the agent reasoning over descriptions per task (a menu, not a binding). Once a scope is provisioned, the active set is the confirmed menu everything downstream draws from.

Where the active set lives

On the scope’s preflight artifact, keyed by (system, framework) under ~/.pretorin/preflight/. Availability is already host-local and lives there; the active set is a decision layered on the same facts. Each active recipe records the version, content digest, and loader source pinned at activation, the ready source kinds it covers, and who activated it.

Provisioning during preflight

The active set is seeded at the end of the preflight workflow (Step 8), from what the host can actually do:

# Direct CLI: seed the active set with every official-tier recipe runnable on
# this host (community/project recipes need --include-unofficial), then trim
# to the logical subset.
pretorin preflight provision            # show candidates + coverage gaps
pretorin preflight provision --apply    # seed the active set from candidates
pretorin recipe deactivate openscap-baseline   # trim what you don't run

An agent does the same over MCP: get_active_recipes returns the candidates runnable on the host (source-less recipes plus those whose required source kinds are all ready), any coverage gaps (ready sources that no cookbook recipe consumes — author or clone one), and any version drift. It then commits a logical subset with set_active_recipes.

Managing the active set

pretorin recipe active                       # show the active set + proposal
pretorin recipe activate code-evidence-capture workspace-capture
pretorin recipe deactivate manual-attestation
pretorin recipe list --active                # list only the active subset

Over MCP:

  • get_active_recipes — read the set + proposal (candidates, coverage_gaps, drift).
  • set_active_recipes(recipe_ids=[...], mode="replace" | "add" | "remove") — edit it.
  • list_recipes(system_id=..., active_only=true) — list the active subset; every recipe also carries an active flag when a system scope is supplied.

What changes once a scope is provisioned

  • Capture plan / check_sources draw candidate recipes from the active set. A recipe_gap no longer means “impossible” — it means no active recipe covers this expectation, and it names the host’s ready source kinds and the active recipes that can substitute. Activate or add a recipe, or accept the gap.
  • start_recipe refuses a recipe that is not in the active set. Activate it first (pretorin recipe activate <id>), or pass force=true for a genuine one-off run. It also refuses active recipes whose pinned version, content digest, or loader source has drifted; review and re-activate, or force the current recipe ad hoc.

Migration

A scope that has never been provisioned behaves exactly as before: downstream tools fall back to the whole cookbook (filtered by preflight availability) and nudge you to provision. Provisioning is opt-in and non-breaking — establish an active set when you want the stability and the confirmed menu.

Version drift

Activation pins the recipe’s version and a content digest (a hash of recipe.md plus its scripts) and its loader source. Every get_active_recipes / pretorin recipe active compares them against the cookbook and flags:

  • version moved — the recipe was upgraded/downgraded since activation;
  • content changed — a same-version body/script swap (e.g. a shadowing recipe at a higher-precedence loader path replacing the code behind the id);
  • source changed — the id now resolves from a different loader path.

Drift is a visible signal, not a silent swap under your audit. On a provisioned scope it is also an execution gate: start_recipe refuses the drifted recipe until you re-activate it, or pass force=true for an audited ad-hoc run.

Security & integrity

  • The gate fails closed. If a scope’s preflight artifact is present but unreadable (corrupt, symlinked, or a tampered/unknown schema_version), start_recipe refuses rather than silently treating the scope as unprovisioned. Rebuild it with pretorin preflight verify, or pass force=true for an audited ad-hoc run (recorded as forced on the execution).
  • Untrusted recipes are activated deliberately. pretorin preflight provision --apply seeds official-tier only; community/project recipes must be activated explicitly (pretorin recipe activate <id>) or with --include-unofficial.
  • Provisioning is scope-bound. set_active_recipes enforces the active CLI context — an agent can’t provision a scope other than the active one.
  • The artifact is local, chmod 600, redacted before write, and rejects path-separator scope keys and symlinks. An attacker with write access to your ~/.pretorin/preflight/ can still deny service (as they could by deleting any config); cryptographic artifact integrity is future work.