Campaign workflow
A Campaign is local orchestration around the existing Pretorin workflow, Plan, MCP, and platform surfaces. Use it when a bounded set is too large for one agent session—for example an entire control family or framework.
CLI campaign start --dry-run OR MCP check_campaign_runtime
-> local execution-dependency gate (before external reads or local writes)
-> for MCP, launch_ready=true -> preview_campaign
-> ordinary Pretorin source preflight (once for the target workspace and scope)
-> frozen resolver verdicts + bounded reader grants + active recipe pins
-> local draft parent Campaign Plan + deterministic child assignments
-> CLI confirmation OR MCP start_campaign(plan ID + digest + confirmed=true)
-> recheck execution dependencies
-> embedded Hatchet dispatch
-> one specific work_on task per item
-> selected workflow phases + child-authored actions and recipes
-> read-only preflight snapshot + bounded Pretorin readers + configured external MCP servers
-> existing typed MCP platform writes
-> existing platform review only when explicitly configured
-> deterministic local aggregate + platform review handoff
check_campaign_runtime reports execution and observer dependencies, Codex
and embedded Hatchet runtimes, blocking failures, and native setup that
may be deferred until confirmed start. It never contacts the platform,
downloads or installs software, reserves an idempotency key, or writes a Plan.
For a configured or PATH agent executable, the check freezes its resolved path
and content identity without launching it; version and capability validation
are reported separately as deferred_validation and occur only after explicit
start confirmation.
When it returns launch_ready=false, stop. Apply its remediation outside MCP,
restart the server, and check again before preview.
Pretorin does not run a Campaign-specific LLM. Each isolated child session uses the Codex binary and shared agent configuration. Recipes and workflows are markdown playbooks that agent reads; they are not server-side runtimes.
Authority boundaries
The shared transport-neutral coordinator creates the local parent Plan and freezes Campaign type, selector, ordered item/objective roster, Codex model/provider/reasoning effort, the Codex model endpoint, exact executable source, managed status, and SHA-256 identity, plus the maintained version when known, Hatchet engine/SDK version, concurrency and retry settings, publication policy, and review policy. The CLI and MCP adapters both ask the coordinator to prepare the ordinary Pretorin preflight once and freeze its resolver verdicts, active recipe pins, and content digest on the Campaign. Before dispatch the coordinator creates or strictly reconciles one deterministic child identity per item and binds its exact scope, objective roster, required outcomes, publication policy, safety boundary, and replay-safe logical write identities. It does not supply a Campaign-specific execution sequence. The selected workflow supplies semantic Plan phases; the child adds concrete actions and recipe selections, then activates the result as the stable retry contract. Bounded Campaign state tracks attempts, Hatchet correlation IDs, process facts, resource receipts, and aggregate progress.
CLI/MCP adapter fields are excluded from that semantic definition. In
particular, MCP’s durable preview idempotency key lives only in a sidecar
reservation; it cannot change Campaign identity, Plans, effects, or the
confirmation digest. All new parent/child creation and transitions use the
surface-neutral campaign-coordinator actor and v2 launch contract.
The portable reasoning-effort values are low, medium, and high. An
explicit campaign start --reasoning-effort wins over the saved Codex setting;
if neither is present, the provider and Codex defaults are used.
Once prepared, the effective optional value is part of the frozen assignment,
so retry and resume cannot follow a later configuration change. Codex consumes
the setting natively.
The existing platform remains authoritative for evidence, mappings, narratives, objective implementations, Issues, review jobs/findings, and human approval. The local store never copies those artifact bodies and cannot approve a control. No platform Campaign API, Campaign database model, shared Plan repository, or Campaign-specific UI is required.
Execution contract
Hatchet assigns one frozen item to an isolated Codex process. The worker prompt
is deliberately small: it states the specific task,
provides start_task entities with intent_verb="work_on" and the exact item
scope, then directs the child to fetch the compact selected workflow without
repeating tool schemas already registered by its MCP client. The resulting
guidance payload stays under 800 words. The trusted runtime injects the parent
Plan and Campaign context before profile validation; those bookkeeping fields
are not instructions the child must reproduce.
The selected MCP workflow defines domain guidance and the semantic Plan phase
order. The child owns concrete actions within those phases, reads, tools, and
recipe choices, then advances that Plan and publishes through existing typed
Plan-scoped writers. Every selected recipe must earn its exact lifecycle
receipt before its step can complete; no minimum artifact count is imposed.
A pass requires an observed recipe script or producer write, while an
unavailable action can end needs_input without synthetic evidence.
For a control pass, the required result includes an authoritative evidence
mapping, one truthful disposition and working narrative for every frozen
assessment objective, and any explicitly authorized review handoff. The worker
never approves an assessment-objective leaf or its parent control. Before an
objective narrative update or evidence link, the CLI re-reads the live
objective and requires its exact frozen UUID, system, framework, control, and
unarchived state. Missing scope fields fail closed; the frozen roster alone
cannot authorize a rehomed objective.
The child uses
ordinary evidence tools to establish the relationships; at complete_plan,
the server re-reads authoritative coverage and atomically stores the verified
mapping as a top-level Plan outcome. The workflow mapping phase remains named
and ordered, but the Campaign does not trust a child-supplied mapping payload.
Campaign completion validates the frozen scope, terminal child Plan, required persisted outcomes, idempotent/reconciled writes, and publication policy. It does not require a coordinator-authored milestone shell or compare the run to a prescribed MCP method. This keeps retry and audit guarantees without turning the Campaign into a second workflow engine.
The worker MCP profile exposes ordinary platform reads across Campaign types and scopes under the platform credential’s authorization, including reusable evidence search. It validates the current attempt and keeps child Plan, recipe execution, Campaign process records, and typed writes bound to the assignment. Campaign-only Issue and evidence inspectors still address the assigned item. It withholds Campaign finalization, generic proposal/apply, destructive evidence operations, objective/control approval, and human review decisions. Workflow-owned completion criteria remain authoritative, while the child authors the Plan that satisfies them. For newly frozen initial/interim control Campaigns, those criteria include both assessment-objective working narratives and a parent control narrative. The finalizer re-reads the exact objective scope and persisted narrative text and requires a matching current-attempt typed-write publication receipt for each required objective and parent narrative; pre-existing or reason-only text does not silently satisfy publication. The parent must be a grounded roll-up of supported leaf outcomes, or the server-rendered limitation-only narrative when every objective needs input and none has evidence. No-review runs still publish this parent; frozen older runs retain the output contract they were confirmed under. An existing explicitly human-authored parent is not overwritten. For a new-contract Campaign the child may record a non-mutating, exact-hash preservation decision with a reason; completion rechecks that same human text, and readiness remains pending human reconciliation. Conflicting human prose instead requires human action. Codex receives one ephemeral home/config, the assigned Pretorin MCP server, the configured user MCP servers, and the assigned workspace context. It otherwise behaves like an ordinary Codex workspace session: project instructions, native shell and workspace tools, writable workspace access, and the caller’s source environment remain available. Pretorin’s MCP server stays bound to the frozen assignment.
Those guarantees cover the CLI-owned Pretorin MCP and worker runtime surfaces. An operator-configured external MCP server remains a separate trust boundary and can expose its own credentials or side effects. Configure only servers whose authority is intended for the Campaign.
Model-provider credentials, the Pretorin platform key, and evidence-source credentials serve different purposes. The Codex process receives the same source environment as an interactive workspace session, while the assigned Pretorin MCP child receives the source variables authorized by preflight. Preflight scopes bounded Pretorin MCP readers; it is not a sandbox around native Codex or configured external MCP access. Credential values are not copied into Plans, Campaign records, Codex config, or observer events.
Preflight is one shared input to the work, not a per-child Campaign phase. The
coordinator prepares it through the same implementation used before an
interactive agent task. Each child and retry receives a private read-only copy;
it cannot verify, refresh, mutate, reprovision, or record preflight again.
Campaign preview and the child’s get_preflight distinguish host readiness from
bounded MCP acquisition for each source kind. Codex may also use native tools
and explicitly configured external MCP servers; those reads do not count as
Pretorin reader receipts or establish evidence provenance by themselves.
Concrete grounding-usable Pretorin source-reader results carry a CLI-local
source_read_receipt_id and a copy-ready create_evidence_fields projection.
The projection includes an artifact body that already embeds the verbatim
redacted excerpt. The child passes those exact fields to create_evidence; the server still
validates the receipt, excerpt, and body, then stamps the source URI,
locator, version, capture time, and method. Non-grounding responses withhold
the projection. This binds a reader result to a write, not the truth of the
objective conclusion, and it is not an operating-system tamper boundary.
For a verified workspace directory binding, the child can page safe directory
entries with list_preflight_workspace_entries, traverse nested directories,
then read an exact file with read_preflight_workspace_source. Listing names
does not earn a source-read receipt or prove the file content; the typed file
read is the provenance-bearing step.
An existing EvidenceItem PDF can be frozen as an optional per-control read
grant at preview; the bounded read_campaign_evidence_content returns a
receipt only for valid, nonempty, grounding-usable exact page text or a
contiguous bounded slice. The ordinary
read_evidence_content remains a platform-authorized inspection read and
does not create this receipt. Neither a grant nor PDF metadata proves an
assessment objective.
Host CLI probes and platform feature bindings do not guarantee a usable
record; a pinned source recipe is a candidate until its exact resolver
coverage is known. Platform evidence search is a separate path.
For a repository-scoped GitHub pull-request/review binding, a fresh preflight
can freeze a fixed PR index and a separately probed exact-PR detail grant.
The child may read only that repository, with the live repository identity
rechecked for every read. A PR detail can supply a source receipt, but a
review approval is a code-change record, not automatically an authorized
external connection or a periodic external-system review. The worker must
verify the exact external system, approval scope, approver, and date before
citing it for AC.L1-3.1.20. Historical snapshots have no new detail grant.
These availability verdicts inform the best-effort assessment but do not act as
an objective readiness gate. An operator may separately request a launch gate
for named source kinds (--require-source-kinds in CLI or
required_source_kinds in MCP preview). Each named kind must be ready in the
frozen preflight; a blocked gate prevents launch and calls for a new preview
after access is restored. This launch gate still does not prove that an
objective has qualifying evidence. The preview also reports available bindings
inside required kinds that have no confirmed bounded path for each child;
for newly prepared gated Campaigns these are launch-blocking under the frozen
all_connected_bindings_bounded policy. Historical gated Campaigns without
that policy retain their original minimum-per-kind semantics and display the
same gaps as diagnostics only. A metadata-only policy route can show that an
exact binding is intentionally ineligible for the assigned control, but it is
not a source read and cannot by itself satisfy the per-kind readable-path gate.
Codex workers then perform a just-in-time, metadata-only attestation through
their exact finalized Pretorin MCP command before the model
process starts. The attestation compares the complete tool catalog and every
paginated frozen grant, checks snapshot identity and required source-key
forwarding, and fails closed on drift without reading source content or creating
receipts. This attestation scopes Pretorin MCP access; it does not narrow the
ordinary Codex workspace tool surface.
Recipe IDs must be trusted package built-ins, and
a selected recipe must match the exact active content pin. No source is a valid
process condition. The child uses normal MCP guidance to decide which available
pinned recipes fit the assignment; the coordinator does not mandate a
particular fallback. An attempt may truthfully produce no evidence, and no
runnable source remains a valid specific limitation:
groundedrequires persisted platform evidence;gaprequires an authoritative exact-scope, nonterminalcontrol_gapIssue plus either pre-existing scoped evidence or a server-verified workspace path, bounded line range, and whole-file SHA-256; free-form observation prose is insufficient. An existing Issue that already represents the deficiency is reused, and no same-child Issue-writer receipt is required;needs_inputrequires a coverage limitation or evidence request and is not itself an execution failure; and- an evidence request remains a next action, never an evidence artifact.
Durability and replay
Embedded Hatchet owns dispatch, per-Campaign concurrency, retry/backoff, cancellation, and run IDs. Local code does not add another queue or concurrency limiter. Duplicate delivery of a succeeded item returns its saved state without starting an agent.
Parent and child Plan IDs are the recovery identities. Retry and restart reuse
the same deterministic child Plan. Resume adopts a persisted non-terminal
Hatchet run when one exists and submits only work without a live run; an
explicit resume from completed_with_errors reopens failed children only.
The frozen Codex model and exact runtime source/version/identity are
revalidated on each attempt, so configuration or PATH changes cannot redirect
an existing Campaign. The preflight snapshot is reused verbatim across retry
and restart; later canonical preflight changes apply only to newly prepared
work. Resume authenticates the frozen Campaign and complete parent/child Plan
hierarchy before it reopens Plans, reconciles orphan attempts, installs a
runtime, or dispatches anything.
Logical platform-write identities make a committed response loss replay-safe,
and local finalization is atomic and exactly once. A cancellation command first
persists the request; a live worker may acknowledge the terminal state only
after Hatchet stops outstanding runs. Recovery is supported on the same host;
multi-host execution is deferred until Plans are shared.
Campaign state remains on schema v2, while new preparation journals use schema
v3 and new Campaigns use launch contract v2. Launch contract v2 is a deliberate
execution clean break from coordinator-authored child step shells and
step-scoped typed-write identities: launch-contract-v1 Campaigns remain
readable history (including status and terminal cancellation) but cannot be
resumed. Start a new Campaign to use the child-owned Plan contract. State
schema v2 freezes resolved system/framework IDs, coordinator and launch-contract
versions, roster, hierarchy, template and effect-policy hashes, exact authorized
effects, the launch-definition SHA-256, and durable confirmation time. CLI and MCP reject
v0.29.3 Plan-backed state with campaign_state_version_unsupported; listing
skips and counts it. This does not affect the separate deprecated checkpoint
records and compatibility tools.
Observe and hand off
campaign status <campaign-plan-id> reports persisted execution, assessment,
evidence-requirement coverage from each child Plan’s server-verified completion
outcome, and last-recorded review aggregates without querying the platform.
Coverage reports active requirements as covered or uncovered and counts
intentionally unbound evidence separately; it never implies objective or
control approval. Partial
aggregates are labeled as known coverage. Pending, loading, or unavailable
mappings are explicit instead of appearing as zero requirements.
Legacy mapping records without the current authority-verification marker remain
readable, but their coverage is reported as unavailable rather than trusted.
campaign watch <campaign-plan-id> is a read-only projection that additionally
refreshes recorded queued/running review jobs and overlays their live state
without mutating the Campaign record. It keeps execution, assessment,
evidence-requirement coverage, and review separate while showing Plan steps,
attempt and Hatchet IDs, recent retained MCP calls, objective-disposition
progress, shared preflight context, recipes, safe resource IDs, and platform
review identity. Press q to detach without cancellation.
The tool_activity observer field uses retained child Plan MCP signals. Native
Codex activity is not retained in that projection and cannot, by itself,
establish which source record was read, that evidence was persisted, or that an
objective was satisfied.
The paginated get_campaign and campaign status --output json item projection
also reports source_read_activity from the CLI-local successful-read receipt
ledger. It separates no_ledger, verified, and corrupt states, with
per-attempt receipt counts, grounding-usable counts, and source kinds only.
It never returns receipt IDs, source content, locators, or credentials. A
source-read receipt confirms a bounded reader response was returned under the
local MCP trust boundary; it does not prove objective coverage, evidence
creation, or a read by an external MCP server. A corrupt ledger has no trusted
count.
The separate source_attempt_activity field summarizes granted typed-reader
outcomes (nonempty, empty, partial, safe_failure) by attempt and source
kind without exposing record IDs or content. A zero count is an observable
quality gap, not proof that no configured external server inspected a source;
neither a witness nor a source-read receipt establishes objective coverage.
The child prompt lists host-available source kind IDs only when its frozen
preflight snapshot matches the assignment. These are routing hints, not child
query grants or evidence.
For assessable controls, get_campaign_objective_source_routes additionally
joins the child’s frozen objective UUID roster to current catalog expectation
hints and exact frozen child-reader grants. It fails closed if the roster,
taxonomy, or snapshot no longer matches. Its per-objective route is advisory:
the child still needs a relevant source record, persisted provenance, and exact
control/objective links before claiming grounded work. It only routes
catalog-hinted kinds and cannot rule out other ready preflight sources or
reads by configured external MCP servers; the current reference revision is
not frozen.
get_campaign returns the same persisted projection in an MCP-safe bounded
form: immutable item pages default to 10 and cap at 20; Campaign-list pages
default to 20 and cap at 50. Its execution, assessment, verified-coverage, and
review fields match the overlapping CLI fields exactly. Local get/list/cancel
continue to work during a platform auth or consent outage; preview/start/resume
still require current platform and frozen-authority validation.
Every MCP response for one Campaign includes the exact same-host watch command and a version-matched registry fallback:
pretorin campaign watch <campaign-plan-id>
uvx --from "pretorin==0.29.21" pretorin campaign watch <campaign-plan-id>
The MCP server never runs these strings or streams a terminal over JSON-RPC.
The operator launches one on the MCP server’s host; q safely detaches.
After automated work finishes, continue review in the existing Pretorin control and review experience. A completed Campaign says that its declared process ran; it does not say a human approved the controls.
See Campaign CLI for commands, runtime management, and the legacy checkpoint migration.