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

Campaign Workflows

The campaign command group runs bulk compliance operations across multiple controls, policies, or scope questions in a single coordinated run. Campaigns support an external-agent-first pattern with checkpoint persistence and lease-based concurrency for safe fan-out to multiple agents.

Campaign Domains and Modes

DomainModeDescription
controlsinitialDraft new narratives and evidence for controls
controlsissues-fixAddress platform issues on existing controls
controlsnotes-fixDeprecated alias for issues-fix
controlsreview-fixFix findings from a family review job
policyanswerGenerate answers for policy questions
policyreview-fixFix findings from a policy review
scopeanswerGenerate answers for scope questions
scopereview-fixFix findings from a scope review

Control Campaigns

Draft New Narratives for a Family

pretorin campaign controls --mode initial --family AC \
  --system "My System" --framework-id fedramp-moderate

Fix Controls with Platform Issues

pretorin campaign controls --mode issues-fix --all-open-issues

Fix Controls after Family Review

pretorin campaign controls --mode review-fix --family AC --review-job <job-id>

Options

OptionDescription
--systemTarget system ID or name
--framework-idTarget framework ID
--familyControl family to target (e.g., AC, AU)
--controlsSpecific control IDs (comma-separated)
--all-controlsTarget all controls in the framework
--modeCampaign mode: initial, issues-fix, notes-fix, review-fix
--all-open-issuesTarget controls with open issues in the system/framework context
--issue-sourceOptional issue source filter
--issue-controlOptional comma-separated issue control filter
--issue-familyOptional issue family filter
--include-resolvedInclude resolved issues in issue discovery
--artifactsArtifact types to generate: narratives, evidence, or both (default: both). Apply is evidence-first: narratives cite the created evidence ids, so narratives alone is refused for a control with no citable evidence — use both.
--review-jobReview job ID (required for review-fix mode)
--concurrencyNumber of parallel workers
--max-retriesMaximum retry attempts per item
--checkpointPath to checkpoint file for resume
--applyApply proposals to platform after completion
--outputOutput mode: auto, live, compact, json

Policy Campaigns

Answer All Incomplete Policy Questions

pretorin campaign policy --mode answer --all-incomplete

Fix Policy Review Findings

pretorin campaign policy --mode review-fix --policies <policy-id>

Options

OptionDescription
--policiesSpecific policy IDs (comma-separated)
--all-incompleteTarget all incomplete policies
--modeCampaign mode: answer, review-fix
--systemOptional system context passthrough
--concurrencyNumber of parallel workers (default: 4)
--max-retriesMaximum retry attempts per item (default: 2)
--checkpointPath to checkpoint file for resume
--applyApply proposals to platform after completion
--outputOutput mode: auto, live, compact, json

Scope Campaigns

Answer Scope Questions

pretorin campaign scope --mode answer \
  --system "My System" --framework-id fedramp-moderate

Options

OptionDescription
--systemTarget system ID or name (required)
--framework-idTarget framework ID (required)
--modeCampaign mode: answer, review-fix
--concurrencyNumber of parallel workers (default: 4)
--max-retriesMaximum retry attempts per question (default: 2)
--checkpointPath to checkpoint file for resume
--applyApply proposals to platform after completion
--outputOutput mode: auto, live, compact, json

Checking Campaign Status

pretorin campaign status --checkpoint .pretorin/campaign-checkpoint.json

Campaign Lifecycle

  1. Prepare — The campaign snapshots platform state and creates a local checkpoint file
  2. Claim — Items are claimed with TTL-based leases (safe for multiple agents)
  3. Draft — Each item gets full context and drafting instructions
  4. Propose — Proposals are submitted without writing to the platform
  5. Apply — All accepted proposals are pushed to the platform in one operation

Use --apply to automatically apply after completion, or run campaign status to review before applying.

Idempotency and Replay

Apply is safe to retry. Every campaign create write carries an idempotency key the platform uses to recognize a repeat of a write it already committed, so a crash or a lost response leads to a replay rather than a duplicate.

The run id

When a campaign checkpoint is created, the CLI mints a per-run UUID and stores it in the checkpoint file as run_id:

{
  "version": 2,
  "identity": { "domain": "controls", "mode": "initial", "...": "..." },
  "run_id": "3f6c2a1e-9d84-4c17-8f0b-1a2b3c4d5e6f",
  "idempotency_support": "supported",
  "items": { "...": "..." }
}

run_id is run state, not campaign identity — it sits beside identity, never inside it, so resuming a checkpoint still passes identity validation.

  • Resuming or retrying the same checkpoint reuses the same run_id, so keys match the earlier attempt and the platform replays it.
  • A deliberate fresh campaign (a new checkpoint file, or a deleted one) mints a new run_id, so its writes create new resources.
  • Checkpoints written before this feature have no run_id; apply mints one and flushes it to disk before its first platform write.

Do not hand-edit or copy run_id between checkpoints. Copying it into an unrelated campaign makes that campaign’s writes collide with the original run’s keys.

Key scheme

Keys are derived per artifact and sent on each evidence batch item and on control-issue creation:

{run_id}:{item_id}:{artifact_type}:{revision}:{index}
  • artifact_type is evidence or issue.
  • index is the artifact’s position in the stored proposal — not its offset in the request being sent, so a resume that re-sends only the un-receipted subset reuses each artifact’s original key.
  • revision is the item’s proposal revision. Submitting a new proposal for an item that already has apply receipts bumps it, so the new content writes under fresh keys. A key must map to exactly one payload for the resource’s lifetime; without this, re-drafting a failed item would reuse a key the platform had already bound to the previous draft and fail permanently.

The format is internal to the CLI; the platform treats the key as an opaque string and never parses it.

Checking whether the platform enforces keys

Apply probes GET /api/v1/public/capabilities once at the start of each run and records the answer in the checkpoint as idempotency_support:

ValueMeaning
supportedThe platform enforces client keys — retries are protected
unsupportedThe platform answered but ignores keys — protection falls back to local receipts
unknownThe probe could not get an answer (auth, rate limit, outage, network)

unknown is deliberately distinct from unsupported: an error response says nothing about the deployed build, so it is never read as “the platform doesn’t support this.” The probe decides only what a run may claim about duplicate protection. Keys are always sent regardless of the result — a platform version that predates the feature ignores the field, so sending is free, and backing off on a flaky probe would drop the protection exactly when the network is unreliable.

The value is re-probed every run and never trusted from a previous run’s checkpoint: a server that started mid-migration can legitimately flip. The per-write proof is always the item’s own replayed status, not the capability flag.

Replay semantics

Server responseMeaningWhat the CLI does
created / okNew resource writtenReceipt records ok; downstream steps run
replayedKey matched an earlier writeReceipt records replayed with the original resource ids; treated as applied, but no downstream step re-fires
idempotency_key_conflictSame key reused with different contentHard per-item failure; the platform’s remediation message is surfaced

A replayed item is a success: its original evidence ids are still cited by the narrative, and a resume will not re-send it. Control campaigns do not post a separate completion note: the legacy notes endpoint has no idempotency key, so no client can guarantee exactly-once notification after a committed response is lost. The durable artifact receipts are the completion record.

An idempotency_key_conflict is not retryable — re-running apply on the same checkpoint derives the same key and conflicts again. It means a key was reused for different content, which normally indicates a hand-edited checkpoint. To get new keys, either submit a fresh proposal for the item (which bumps its revision and supersedes the conflicting content) or re-prepare the campaign into a new checkpoint file so it mints a new run_id. The error message names both.

Keys are unique within your organization and honored for the lifetime of the resource. Platform versions that predate idempotency-key support ignore the field, so sending it is always safe; against those versions, retry protection falls back to the CLI’s local receipts, which narrow the crash/retry duplication window rather than closing it.