Preflight
Preflight is how Pretorin verifies that the host is connected to the sources a framework needs before evidence work begins. The platform recommends canonical source kinds per framework, but it cannot verify whether any of them are reachable from where you are working — only the CLI host can. Preflight is that CLI-local verification layer, and its verdict is the single source of truth for source availability (it replaces the platform connection registry).
The model
A recommended source kind (e.g. code_repository, cloud_control_plane)
maps to a collection of resolvers, not a single one. Each resolver is a
concrete, host-local way to reach part of the evidence story:
Resolver type | Verifies | Example params |
|---|---|---|
workspace_path | a path (and optional marker) exists | {path: /infra, marker: "*.tf"} |
cli_tool | a CLI is present/authenticated | {name: gh, probe: "gh auth status"} |
command | a generic probe exits 0 | {probe: "curl -fsS https://host/health"} |
manual / attested | nothing — user-asserted | {identity: "SOC2-2026-Q1"} |
mcp / connected_api / pretorin_feature | a declared probe, else unverified | {probe: "..."} |
Resolver type and params are open — custom types work, and an unknown
type with a declared probe is simply run (the registry fails open). A resolver
may also declare capabilities, matched against a recipe’s
requires.sources[].capabilities so the executor requires only the resolver(s)
a recipe touches.
Status vocabulary
Each resolver verifies to one of: connected (machine-verified), degraded
(reachable but stale/partial), missing (probe ran, not reachable), attested
(user-asserted, never silently “connected”), or unverified (no probe yet).
These roll up per kind:
ready— every bound resolver is up; the whole evidence story is reachable.degraded— some up, some not; partial story.missing— resolvers bound and verified, none reachable.unverified— resolvers bound but not probed yet.unmapped— a recommended kind with no resolvers bound.
Two grains of availability
- Control grain — the per-kind rollup answers “is the whole evidence story present?”
- Executor grain —
start_recipeonly requires the specific resolver(s) a recipe touches (kind + optional capability), so a degraded kind never blocks a recipe that doesn’t need the down resolver.
How availability is decided
The verdict drives every availability decision:
list_recipes/ the capture plan hide recipes whose required kinds are verified missing; unmapped/unverified kinds fail open (shown asunknown— a soft “verify before capture”, never a hard “not connected”).start_reciperefuses to open a context when a required kind is verified missing, unless you passforce=true.
Commands
pretorin preflight init # bind sensible local defaults, verify by default
pretorin preflight show # read the verdict for the active scope
pretorin preflight verify # probe every bound resolver, refresh status
pretorin preflight bind <kind> --type <type> [--param k=v ...] \
[--probe "<cmd>"] [--capability <cap> ...] [--recommended] \
[--constraint "<usage note>"] [--scope k=v ...]
pretorin preflight init is the low-friction setup path for a fresh machine:
it detects the current git root, common local tools (gh, az, aws,
kubectl), and local docs/policy folders, then binds those resolvers without
calling the platform. It skips existing mappings unless you pass --replace,
and it verifies by default (--no-verify just writes the mappings).
--system / --framework default to the active context
(pretorin context set). The agent-facing MCP tools are get_preflight,
verify_preflight, and update_preflight.
--constraint records a human/agent-readable usage note on the binding.
--scope k=v (repeatable) pins a structured usage scope — for example
--scope subscription=sub-prod --scope region=westus2. Scope entries become
recipe param defaults: when a recipe declares a param with the same name,
start_recipe and pretorin recipe run fill it from the matching binding
unless the caller passes an explicit value. The applied defaults are reported
back as source_params. Scope never crosses cloud providers — a binding
identifiable as Azure (e.g. the az CLI, or an explicit --scope provider=azure) will not feed defaults into an AWS-kind recipe.
The guided workflow
For an interactive, recommendation-aware setup, run the preflight
workflow: it pulls the framework’s recommended source kinds, diffs them
against what you have mapped, walks you through binding a resolver collection
for each gap, verifies them, and reports ready / degraded / missing. The
artifact persists locally per (system, framework) under
~/.pretorin/preflight/, so later evidence work reads the verdict directly —
re-run pretorin preflight verify if it has gone stale.
Task and Campaign preparation, along with CLI and MCP updates for the same
scope, share one transaction. Concurrent preparation therefore cannot discard
another workspace binding or an explicitly activated recipe.
Over MCP, enter this workflow through the normal routing boundary:
start_task with entities.intent_verb="preflight", the active system_id
and framework_id, and the user’s verbatim prompt. Then load the returned
preflight workflow with get_workflow before calling its preflight and
recipe-discovery tools.
Do not put literal credentials in resolver probes. Secret-shaped strings in resolver params and probe results are redacted before the local artifact is persisted, but probes should rely on the host’s normal auth stores whenever possible.