## 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

# Pretorin Developer & Agent Docs

> Beta — Pretorin is currently in closed beta. Framework and control browsing works for authenticated users. Platform write features (evidence, narratives, monitoring) require a beta code. Sign up for early access.

Pretorin gives developers and AI agents direct access to compliance data, implementation context, and evidence workflows. The primary surfaces are the CLI, the MCP server, and skill-driven agent workflows. It supports 30+ compliance frameworks and profiles, including NIST 800-53, NIST 800-171, FedRAMP, and CMMC.

The CLI and MCP tooling in this repository are open source under Apache-2.0. Access to Pretorin-hosted platform services, APIs, and account-scoped data is authenticated and governed separately by the applicable platform terms.

## Start Here

Choose the path that matches how you work:

- CLI-first — Use the Pretorin CLI directly for framework browsing, evidence workflows, reviews, scans, and agent execution.
- AI-agent-first — Connect `pretorin mcp-serve` to Claude Code, Codex CLI, Cursor, or another MCP-compatible tool.
- Hosted agent runtime — Use `pretorin agent run` when you want Pretorin-managed model execution with built-in skills.

Start with Installation, Authentication, and Quick Start if this is your first time here.

## Core Paths

Pretorin is usually used in one of these modes:

1. Bring-your-own-agent mode — Run `pretorin mcp-serve` and connect the MCP server to your existing AI tool (Claude Code, Codex CLI, Cursor, Windsurf, etc.). Your agent gets compliance tools without changing your workflow. Pair with `pretorin skill install` to give your agent explicit guidance for using pretorin (see “Bundled skill” below).

2. Pretorin-hosted agent mode — Run `pretorin agent run` to use Pretorin’s built-in agent runtime when you don’t have your own local agent. Pretorin manages the AI runtime; you supply prompts.

3. Direct CLI mode — Use `pretorin` subcommands directly for browsing frameworks, managing context, authoring evidence, updating narratives, and running scans. No agent involved.

Important architectural detail. The vast majority of the CLI and the entire MCP server are thin wrappers over the platform API — no LLM runs in pretorin in those paths. When you use mode 1 (your own agent via MCP), your local agent does all the reasoning. Pretorin is the tool surface. The only place pretorin runs its own LLM is `pretorin agent run` (mode 2), provided as a fallback for users without a local agent.

### Bundled skill (`pretorin skill install`)

`pretorin skill install` copies a bundled skill into `~/.claude/skills/pretorin/` (or `~/.codex/skills/pretorin/`). The skill is markdown + scripts that tell your agent how to use pretorin’s MCP tools effectively — which to call first, how to scope by system + framework, how to handle evidence and narrative writes. Highly recommended when using mode 1.

## What You Can Do

- Browse compliance frameworks — Query controls, families, and document requirements from authoritative sources
- Manage implementation context — Set an active system and framework, then track progress across controls
- Create and manage evidence — Generate local evidence files, push them to the platform, and link them to controls
- Write implementation narratives — Draft and push auditor-ready narratives for each control
- Run AI-powered compliance tasks — Use the built-in Codex agent with bundled skills (gap-analysis, narrative-generation, evidence-collection, security-review, stig-scan, cci-assessment)
- Run compliance recipes — Author or invoke recipe playbooks (markdown + scripts) that the calling agent executes for evidence capture, baseline scanning, and other procedures
- Review code against controls — Analyze your codebase for control coverage
- Track monitoring events — Record security scans, access reviews, configuration changes, and compliance checks
- Generate compliance artifacts — Produce structured JSON artifacts documenting control implementations
- Browse STIGs and CCIs — Look up STIG benchmarks, rules, and trace CCIs through the full control hierarchy
- Manage vendors — Track third-party vendors, link evidence to vendor assessments, and upload vendor documents
- Complete policy and scope questionnaires — Answer org-policy and scope questions through a guided workflow with AI-assisted generation and review

## Recommended Sections

- Quick Start for first commands and setup
- MCP Integration for Claude, Codex, Cursor, and other agent tools
- Agent Overview for Pretorin-hosted runtime usage
- CLI Reference for command-level detail
- Workflows for end-to-end compliance tasks
- Authoring Recipes for writing or invoking compliance playbooks

## Architecture

Pretorin CLI is three things, with one shared API client at the bottom:

    ┌──────────────────────────────────────────────────────────────────────┐
    │                            Developer                                  │
    │                                                                       │
    │   ┌──────────┐       ┌──────────────────┐      ┌──────────────────┐  │
    │   │ Terminal │       │ Local AI Agent   │      │ pretorin agent   │  │
    │   │ (direct  │       │ (Claude Code,    │      │ run              │  │
    │   │  pretorin│       │  Codex CLI,      │      │ (Pretorin's own  │  │
    │   │  cmds)   │       │  Cursor, ...)    │      │  CodexAgent — for│  │
    │   │          │       │                  │      │  users w/o local │  │
    │   │          │       │ + bundled        │      │  agent)          │  │
    │   │          │       │   pretorin       │      │                  │  │
    │   │          │       │   skill          │      │                  │  │
    │   │          │       │   (optional)     │      │                  │  │
    │   └────┬─────┘       └────────┬─────────┘      └────────┬─────────┘  │
    │        │                      │                          │            │
    │        │                      │ stdio                    │            │
    │        │             ┌────────┴─────────┐                │            │
    │        │             │  MCP Server      │                │            │
    │        │             │  pretorin        │                │            │
    │        │             │  mcp-serve       │                │            │
    │        │             │                  │                │            │
    │        │             │  Tool surface    │                │            │
    │        │             │  (no LLM here)   │                │            │
    │        │             └────────┬─────────┘                │            │
    │        │                      │                          │            │
    │        └──────────────┬───────┴──────────┬───────────────┘            │
    │                       │                  │                            │
    │              ┌────────┴──────────────────┴─────────┐                  │
    │              │  Pretorin API Client                │                  │
    │              │  (shared — only place that talks    │                  │
    │              │   to the platform)                  │                  │
    │              └────────────────┬────────────────────┘                  │
    └───────────────────────────────┼───────────────────────────────────────┘
                                    │
                           ┌────────┴─────────┐
                           │  Pretorin        │
                           │  Platform API    │
                           └──────────────────┘

Three things, one client:

1. Direct CLI — `pretorin <command>` runs synchronously, talks to the platform via the shared client. No LLM.
2. MCP server — `pretorin mcp-serve` exposes the same platform features as MCP tools. The local agent does all the reasoning; pretorin is the tool surface. No LLM runs in pretorin in this path.
3. `pretorin agent run` — Pretorin’s own LLM, used when the user doesn’t have a local agent. Calls the same platform-API tools as the MCP server, just over a Python in-process boundary.

The bundled skill (`pretorin skill install`) is content delivered to the local agent’s skill directory; it’s not a fourth path, it’s a way to give path 2 better instructions.

## Links

- GitHub Repository
- Platform
- PyPI Package
- Issue Tracker
- API Documentation

# Installation

Pretorin CLI requires Python 3.10 or later.

## Recommended: uv

uv installs the CLI as an isolated tool with its own dependencies:

    uv tool install pretorin

## pip

    pip install pretorin

## pipx

pipx provides isolated installation similar to uv:

    pipx install pretorin

## Docker

A Dockerfile and Docker Compose configuration are included in the repository:

    git clone https://github.com/pretorin-ai/pretorin-cli.git
    cd pretorin-cli
    docker compose up

## Verify Installation

    pretorin version

Expected output:

    pretorin version 0.17.4

## Updating

Check for and install the latest version:

    pretorin update

The CLI also checks for updates automatically on startup and notifies you when a new version is available. To disable passive update notifications:

    export PRETORIN_DISABLE_UPDATE_CHECK=1
    # or
    pretorin config set disable_update_check true

## Development Installation

For contributing to Pretorin CLI:

    git clone https://github.com/pretorin-ai/pretorin-cli.git
    cd pretorin-cli
    uv pip install -e ".[dev]"

This installs the package in editable mode with development dependencies (pytest, ruff, mypy, etc.).

# Authentication

## Getting an API Key

Get your API key from platform.pretorin.com.

> Beta Note: Framework and control browsing works for authenticated users. Platform write features (evidence, narratives, monitoring) require a beta code. Systems can only be created on the platform, not through the CLI or MCP. Sign up for early access.

All hosted API access is account-scoped and authenticated. Access to Pretorin-hosted services and any returned account-scoped data is governed by the applicable platform terms in addition to the open-source license for this repository.

## Login

    pretorin login

Options:

Flag | Description
--- | ---
`--api-key`, `-k` | API key (will prompt if not provided)
`--api-url` | Custom API base URL (for self-hosted instances)

You’ll be prompted to enter your API key. Credentials are stored in `~/.pretorin/config.json`.

If you’re already authenticated, `pretorin login` validates your existing key against the API and skips the prompt. To re-authenticate with a different key, pass it explicitly:

    pretorin login --api-key <new-key>

If you log into a different API endpoint or switch API keys, Pretorin clears the stored active `system + framework` context so stale scope does not bleed into the new environment.

## Verify Authentication

    $ pretorin whoami
    ╭──────────────────────────────── Your Session ────────────────────────────────╮
    │ Status: Authenticated                                                        │
    │ API Key: 4MAS****...9v7o                                                     │
    │ API URL: https://platform.pretorin.com/api/v1/public                         │
    │ Frameworks Available: 8                                                      │
    ╰──────────────────────────────────────────────────────────────────────────────╯

For machine-readable output, use the global `--json` flag:

    pretorin --json whoami

## Logout

Clear stored credentials:

    pretorin logout

## API Key via Environment Variable

You can set your API key via environment variable instead of `pretorin login`. The environment variable takes precedence over stored config:

    export PRETORIN_API_KEY=pretorin_your_key_here

This is useful for CI/CD pipelines and containerized environments.

# Quick Start

After installing and authenticating, here are some common first steps.

## Browse Frameworks

List all available compliance frameworks:

    pretorin frameworks list

Get details on a specific control:

    pretorin frameworks control nist-800-53-r5 ac-02

## Set Up Context

Set your active system and framework for platform operations:

    # Interactive selection
    pretorin context set

    # Or explicit
    pretorin context set --system "My Application" --framework fedramp-moderate

## Create Evidence

Create a local evidence file:

    pretorin evidence create ac-02 fedramp-moderate \
      --description "Role-based access control in Azure AD" \
      --type configuration \
      --name "RBAC Configuration"

Push evidence to the platform:

    pretorin evidence push

## Run an Agent Task

Use the Codex agent for compliance analysis:

    pretorin agent run "Assess AC-02 implementation gaps for my system"

Or use a predefined skill:

    pretorin agent run --skill gap-analysis "Analyze my system compliance gaps"

## Connect Your AI Tool

If you use Claude Code, Codex CLI, or another MCP-compatible AI tool:

    # Install the skill (teaches your agent how to use Pretorin tools)
    pretorin skill install

    # Add the MCP server (Claude Code example)
    claude mcp add --transport stdio pretorin -- pretorin mcp-serve

    # Then ask your AI agent about compliance
    # "What controls are in the Access Control family for FedRAMP Moderate?"

Check install status with `pretorin skill status`. See the MCP Setup Guides for other tools.

## Run a Recipe

Recipes are markdown-plus-scripts playbooks that the calling agent invokes through MCP for evidence capture, baseline scanning, and other procedures:

    # List available recipes (built-in + user + project)
    pretorin recipe list

    # Show one recipe's manifest and prose body
    pretorin recipe show inspec-baseline

    # Scaffold a new recipe in ~/.pretorin/recipes/<id>/
    pretorin recipe new my-first-recipe

See Authoring Recipes for the full guide.

## Browse STIGs and CCIs

Look up STIG benchmarks, rules, and CCI traceability:

    # List available STIG benchmarks
    pretorin stig list

    # View STIG benchmark details
    pretorin stig show <stig_id>

    # Trace a CCI to its parent controls
    pretorin cci chain <cci-id>

## Run the Demo Walkthrough

An interactive demo script is included in the repository:

    bash tools/demo-walkthrough.sh

# Framework Browsing

The `frameworks` command group lets you browse compliance frameworks, control families, and individual controls. The browsing commands documented on this page are read-only and available to all authenticated users.

The same command group also exposes write-side commands for authoring, validating, uploading, forking, and rebasing custom frameworks — see Custom Framework Authoring at the bottom of this page for a quick index, or jump directly to the Custom Frameworks guide for the end-to-end workflow.

## List All Frameworks

    $ pretorin frameworks list
    [°~°] Consulting the compliance archives...
                            Available Compliance Frameworks
    ┏━━━━━━━━━━━━━┳━━━━━━━━━━━━━┳━━━━━━━━━━━━━┳━━━━━━━━━━━━━━┳━━━━━━━━━━┳━━━━━━━━━━┓
    ┃ ID          ┃ Title       ┃ Version     ┃ Tier         ┃ Families ┃ Controls ┃
    ┡━━━━━━━━━━━━━╇━━━━━━━━━━━━━╇━━━━━━━━━━━━━╇━━━━━━━━━━━━━━╇━━━━━━━━━━╇━━━━━━━━━━┩
    │ cmmc-l1     │ CMMC 2.0    │ 2.0         │ tier1_essen… │        6 │       17 │
    │             │ Level 1     │             │              │          │          │
    │ cmmc-l2     │ CMMC 2.0    │ 2.0         │ tier1_essen… │       14 │      110 │
    │             │ Level 2     │             │              │          │          │
    │ ...         │             │             │              │          │          │
    └─────────────┴─────────────┴─────────────┴──────────────┴──────────┴──────────┘

    Total: 30+ framework(s)

The ID column is what you use in all other commands.

The exact total and available framework set can vary as the platform catalog expands. Use `pretorin frameworks list` to see the live catalog available to your account.

## Get Framework Details

    $ pretorin frameworks get fedramp-moderate
    [°~°] Gathering framework details...
    ╭───────────────── Framework: FedRAMP Rev 5 Moderate Baseline ─────────────────╮
    │ ID: fedramp-moderate                                                         │
    │ Title: FedRAMP Rev 5 Moderate Baseline                                       │
    │ Version: fedramp2.1.0-oscal1.0.4                                             │
    │ OSCAL Version: 1.0.4                                                         │
    │ Tier: tier1_essential                                                        │
    │ Category: government                                                         │
    │ Published: 2024-09-24T02:24:00Z                                              │
    ╰──────────────────────────────────────────────────────────────────────────────╯

## List Control Families

    $ pretorin frameworks families nist-800-53-r5
    [°~°] Gathering control families...
                           Control Families - nist-800-53-r5
    ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━┳━━━━━━━━━━┓
    ┃ ID                          ┃ Title                       ┃ Class ┃ Controls ┃
    ┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━╇━━━━━━━━━━┩
    │ access-control              │ Access Control              │ ac    │       25 │
    │ audit-and-accountability    │ Audit and Accountability    │ au    │       16 │
    │ awareness-and-training      │ Awareness and Training      │ at    │        6 │
    │ configuration-management    │ Configuration Management    │ cm    │       14 │
    │ ...                         │                             │       │          │
    └─────────────────────────────┴─────────────────────────────┴───────┴──────────┘

> Important: Family IDs are slugs like `access-control`, not short codes like `ac`. The short code is shown in the Class column for reference, but commands require the full slug ID.

### CMMC Family IDs

CMMC frameworks use level-specific family slugs:

    $ pretorin frameworks families cmmc-l2

CMMC family IDs include the level suffix, e.g., `access-control-level-2` instead of `access-control`.

## Get Family Details

    pretorin frameworks family nist-800-53-r5 access-control

## List Controls

    # Family filter via positional argument
    pretorin frameworks controls nist-800-53-r5 access-control

    # Family filter via flag (equivalent)
    pretorin frameworks controls nist-800-53-r5 --family access-control --limit 10

    # All controls in the framework (no family filter)
    pretorin frameworks controls fedramp-moderate

The family filter is optional and may be passed as a positional argument or with `--family`/`-f`. Without `--limit` (default `0`), all matching controls are shown.

> Important: Control IDs are zero-padded — use `ac-01`, not `ac-1`. See Control ID Formats for details.

## Get Control Details

    $ pretorin frameworks control nist-800-53-r5 ac-02
    [°~°] Looking up control details...
    ╭─────────────────────────────── Control: AC-02 ───────────────────────────────╮
    │ ID: ac-02                                                                    │
    │ Title: Account Management                                                    │
    │ Class: SP800-53                                                              │
    │ Type: organizational                                                         │
    │                                                                              │
    │ AI Guidance: Available                                                       │
    ╰──────────────────────────────────────────────────────────────────────────────╯

    Parameters:
      - ac-02_odp.01: prerequisites and criteria
      - ac-02_odp.02: attributes (as required)
      - ac-02_odp.03: personnel or roles
      - ac-02_odp.04: policy, procedures, prerequisites, and criteria
      - ac-02_odp.05: personnel or roles

### Brief Mode

By default, the full control is shown including statement, guidance, and references. Use `--brief` to show only the basic info panel:

    $ pretorin frameworks control nist-800-53-r5 ac-02 --brief

The default (no flag) includes:

- Statement — the formal control requirement text
- Guidance — implementation guidance from the framework
- Related Controls — other controls that relate to this one

### Common Mistakes

Using the wrong ID format produces an error:

    $ pretorin frameworks control nist-800-53-r5 ac-1
    [°~°] Looking up control details...
    [°︵°] Couldn't find control ac-1 in nist-800-53-r5
    Try pretorin frameworks controls nist-800-53-r5 to see available controls.

Use zero-padded IDs: `ac-01`, not `ac-1`.

## Document Requirements

    pretorin frameworks documents fedramp-moderate

Returns explicit and implicit document requirements for the framework.

## Framework Metadata

Get per-control metadata for a framework:

    pretorin frameworks metadata nist-800-53-r5

## Submit Artifacts

Submit a compliance artifact JSON file:

    pretorin frameworks submit-artifact artifact.json

See Artifact Generation for the artifact schema.

## JSON Output

All framework commands support JSON output for scripting and AI agents:

    pretorin --json frameworks list
    pretorin --json frameworks control nist-800-53-r5 ac-02

## Custom Framework Authoring

In addition to the read-only browsing commands above, the `frameworks` group exposes the write-side commands that drive the custom-framework revision lifecycle. These let you author your own catalog, validate it locally, upload it as a draft revision, and fork or rebase against an upstream Pretorin-managed framework.

Command | Description
--- | ---
`pretorin frameworks init-custom <id>` | Scaffold a minimal valid `unified.json` for a new custom framework.
`pretorin frameworks validate-custom <unified.json>` | Validate a `unified.json` artifact against the bundled JSON Schema.
`pretorin frameworks build-custom <input> -f <id>` | Normalize an OSCAL or custom catalog into uploadable `unified.json`.
`pretorin frameworks upload-custom <unified.json> [--publish]` | Upload as a draft revision, optionally publishing in one step.
`pretorin frameworks fork-framework <upstream-id>` | Create a linked-fork draft from an upstream framework.
`pretorin frameworks rebase-fork <fork-id>` | Create a rebase draft for a fork against the latest upstream revision.
`pretorin frameworks revisions <framework-id>` | List all draft and published revisions for a framework.
`pretorin frameworks export-oscal <unified.json>` | Regenerate an OSCAL catalog from a `unified.json` artifact.

See the Custom Frameworks guide for the full end-to-end workflow, the supported input shapes recognized by `build-custom`, and the linked-fork / rebase model.

# Context Management

The `context` command group manages your active system and framework scope. Platform-backed compliance operations (evidence, narratives, notes, monitoring, control status) run inside exactly one `system + framework` pair at a time.

This works similarly to `kubectl config use-context` — set your scope once, then run commands within it.

## List Available Systems

    $ pretorin context list
    [°~°] Fetching your systems...
                                  Your Systems
    ┏━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━━━━━━┓
    ┃ System           ┃ Framework ID       ┃ Progress % ┃ Status    ┃
    ┡━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━━━━━━┩
    │ My Application   │ nist-800-53-r5     │        42% │ in_progress │
    │ My Application   │ fedramp-moderate   │        28% │ in_progress │
    │ Internal Tool    │ cmmc-l2            │        75% │ implemented │
    └──────────────────┴────────────────────┴────────────┴───────────┘

## Set Active Context

    # Interactive — prompts for system and framework selection
    pretorin context set

    # Explicit
    pretorin context set --system "My Application" --framework nist-800-53-r5

    # Skip automatic source verification after setting context
    pretorin context set --system "My Application" --framework nist-800-53-r5 --no-verify

Option | Description
--- | ---
`--system` / `-s` | System name or ID
`--framework` / `-f` | Framework ID (e.g., `fedramp-moderate`)
`--no-verify` | Skip source verification after setting context

Pretorin stores the canonical system ID for stability and also caches the last known system name for display. After setting context, source verification runs automatically unless `--no-verify` is passed. If you change API keys or platform endpoints with `pretorin login`, the stored active context is cleared automatically so old scope does not leak into the new environment.

## Show Current Context

    $ pretorin context show
    ╭──────────────────────── Active Context ─────────────────────────╮
    │ System: My Application (sys-1234...)                           │
    │ Framework: nist-800-53-r5                                      │
    │ Progress: 42%                                                  │
    │ Status: in_progress                                            │
    ╰─────────────────────────────────────────────────────────────────╯

    # Compact summary for shell use
    pretorin context show --quiet

    # Fail fast if the stored context is missing, stale, or cannot be verified
    pretorin context show --quiet --check

`context show` validates the stored system and framework against the platform when credentials are available. If the system has been deleted or the framework is no longer attached, the command reports that state explicitly instead of silently showing a stale context.

## Verify Context

Verify the active context against source attestation:

    # Full output
    pretorin context verify

    # Compact output with custom TTL
    pretorin context verify --ttl 7200 --quiet

Option | Description
--- | ---
`--ttl` | Verification TTL in seconds (default: 3600)
`--quiet` / `-q` | Compact output

## Source Manifest

Show the resolved source manifest and evaluate it against detected sources:

    pretorin context manifest
    pretorin context manifest --quiet

## Clear Context

    pretorin context clear

## Single-Scope Enforcement

All platform write operations must target exactly one system + framework pair. This includes:

- Evidence creation and push
- Narrative updates
- Control notes
- Monitoring events
- Control status updates

If you need to work across multiple frameworks (e.g., `fedramp-low` and `fedramp-moderate`), run them as separate operations:

    # Work on FedRAMP Moderate
    pretorin context set --system "My App" --framework fedramp-moderate
    pretorin evidence push

    # Switch to FedRAMP Low
    pretorin context set --system "My App" --framework fedramp-low
    pretorin evidence push

Some commands also accept explicit `--system` and `--framework` flags, which override the stored context for that invocation.

# Evidence Commands

The `evidence` command group manages local evidence files and syncs them to the Pretorin platform.

## Create Local Evidence

    pretorin evidence create ac-02 fedramp-moderate \
      --name "RBAC Configuration" \
      --description "Role-based access control in Azure AD" \
      --type configuration

Creates a markdown file under `evidence/<framework>/<control>/` with YAML frontmatter containing metadata (control ID, framework, name, type, status).

`--type / -t` is required — the CLI no longer defaults to `policy_document`. See Evidence Types below for the 13 canonical values.

## List Local Evidence

    # List all local evidence
    pretorin evidence list

    # Filter by framework
    pretorin evidence list --framework fedramp-moderate

## Push Evidence to Platform

    pretorin evidence push

Pushes local evidence files to the platform using find-or-create upsert logic. Exact matches are reused and reported separately.

Requires an active single scope from `pretorin context set`, unless both `--system` and `--framework` are provided explicitly.

## Search Platform Evidence

    # Search by control
    pretorin evidence search --control-id ac-02 --framework-id fedramp-moderate

    # Search by system
    pretorin evidence search --system "My Application" --framework-id fedramp-moderate --limit 100

## Upload Evidence File

Upload a file directly as evidence:

    pretorin evidence upload screenshot.png ac-02 fedramp-moderate \
      --name "MFA Screenshot" --type screenshot

    pretorin evidence upload config.yaml ac-06 fedramp-moderate \
      --name "Auth Config" --type configuration --description "IdP auth config"

Creates an evidence record with the uploaded file and links it to the specified control. The file’s SHA-256 checksum is computed locally and verified server-side for integrity.

Option | Description
--- | ---
`--name` / `-n` | Evidence name (required)
`--type` / `-t` | Evidence type (default: `other`)
`--description` / `-d` | Evidence description
`--system` / `-s` | System name or ID (uses active context if omitted)

## Upsert Evidence

Find-or-create evidence and link it to a control:

    pretorin evidence upsert ac-02 fedramp-moderate \
      --name "RBAC Configuration" \
      --description "Role mapping in IdP" \
      --type configuration

This searches for an exact match on (name + description + type + control + framework) within the active system scope. If found, it reuses the existing item; otherwise, it creates a new one. It then ensures the evidence is linked to the specified control.

### Code Context Options

When upserting evidence, you can attach source code context:

Option | Description
--- | ---
`--code-file` | Path to source file
`--code-lines` | Line range (e.g., `10-25`)
`--code-repo` | Git repository URL
`--code-commit` | Git commit hash

If `--code-repo` or `--code-commit` are not provided, the CLI auto-populates them from the attested source verification snapshot when available.

## Link Evidence to a Control

Link an existing platform evidence item to a control:

    pretorin evidence link ev-abc123 ac-02
    pretorin evidence link ev-abc123 ac-02 --framework-id fedramp-moderate --system "My System"

Options:

- `--framework-id / -f` — Framework ID (uses active context if omitted)
- `--system / -s` — System name or ID (uses active context if omitted)

## Link Evidence to a CCI Implementation

Attach evidence to a per-system CCI implementation row:

    pretorin evidence link-cci ev-abc123 <cci_implementation_id>
    pretorin evidence link-cci ev-abc123 <cci_implementation_id> --system "My System"

Options:

- `--system / -s` — System name or ID (uses active context if omitted)
- `--override-system-mismatch` — Permit cross-system attachment (must be paired with `--override-reason`)
- `--override-reason TEXT` — Justification recorded with the override

The CCI implementation UUID can be obtained from `pretorin cci impl` or from the CCI status rollup.

## Link Evidence to a STIG Rule Workflow

Attach remediation proof, mitigating-control documentation, or waiver-justification artifacts to a STIG rule:

    pretorin evidence link-stig ev-abc123 <stig_rule_id>
    pretorin evidence link-stig ev-abc123 <stig_rule_id> --system "My System"

The first link to a given `(system, stig_rule)` pair lazy-creates the workflow row on the platform.

Options match `link-cci`: `--system`, `--override-system-mismatch`, `--override-reason`.

## Delete Evidence

    # Delete with confirmation prompt
    pretorin evidence delete ev-abc123

    # Skip confirmation (for automation)
    pretorin evidence delete ev-abc123 --yes

    # Explicit system scope
    pretorin evidence delete ev-abc123 --system "My Application" --framework-id fedramp-moderate --yes

Permanently deletes an evidence item from the platform. This is system-scoped and requires `WRITE` access. Associated evidence embeddings are removed as part of the delete lifecycle.

Option | Description
--- | ---
`--system` / `-s` | System name or ID (uses active context if omitted)
`--framework-id` / `-f` | Framework ID (uses active context if omitted)
`--yes` / `-y` | Skip confirmation prompt

## Evidence Types

Valid evidence types:

Type | Description
--- | ---
`policy_document` | Policy or procedure document
`screenshot` | Screenshot evidence
`screen_recording` | Screen recording
`log_file` | Log file extract
`configuration` | Configuration file or setting
`test_result` | Test output or report
`certificate` | Certificate or attestation document
`attestation` | Signed attestation
`code_snippet` | Code excerpt
`repository_link` | Link to source repository
`scan_result` | Security scan output
`interview_notes` | Interview or assessment notes
`other` | Other evidence type

### AI-Drift Normalization

Non-CLI write paths (MCP handlers, agent tools, `upsert_evidence` workflow, campaign apply) run a client-side normalizer before submitting evidence to the platform. It maps known AI-drift aliases to canonical types (e.g. `audit_log` → `log_file`, plural `test_results` → `test_result`, `screenshoot` → `screenshot`) and uses `difflib` fuzzy matching for novel typos before falling back to `other`. The CLI itself does not run the normalizer; users get a hard error listing all 13 canonical types and can self-correct.

## Markdown Quality Requirements

Evidence descriptions must be auditor-ready markdown:

- No markdown headings (`#`, `##`, etc.)
- At least one rich markdown element (fenced code block, table, list, or link)
- No markdown images (temporarily disabled pending platform image upload support)

These requirements are validated before push/upsert operations.

# Narrative Commands

The `narrative` command group manages implementation narratives for controls. Narratives describe how a specific control is implemented within your system.

## Create Local Narrative

    pretorin narrative create ac-02 fedramp-moderate \
      -c "- RBAC enforced via IdP\n\n\`\`\`yaml\nroles:\n  admin: ...\n\`\`\`"

Creates a local markdown file at `narratives/<framework>/<control>/<slug>.md` with YAML frontmatter. Markdown is validated at create time (same rules as push).

Options:

- `--content / -c` — Narrative content (required)
- `--name / -n` — Custom name (defaults to `<control>-<framework>`)
- `--ai-generated` — Mark the narrative as AI-generated

## List Local Narratives

    pretorin narrative list
    pretorin narrative list --framework fedramp-moderate

Displays a table of local narrative files: Control, Framework, Name, AI Generated, Synced.

## Push Narratives

    pretorin narrative push --dry-run
    pretorin narrative push

Batch-pushes all unsynced local narratives to the platform. After a successful push, the local file’s `platform_synced` frontmatter is set to `true`.

## Get Current Narrative

    pretorin narrative get ac-02 fedramp-moderate --system "My System"

Returns the current narrative text, status, and AI confidence metadata when present.

## Push a Single File (Legacy)

    pretorin narrative push-file ac-02 fedramp-moderate "My System" narrative-ac02.md

Reads a markdown or text file and submits it as the implementation narrative for the specified control.

## Markdown Quality Requirements

Narratives must be auditor-ready markdown:

- No markdown headings (`#`, `##`, etc.)
- At least two rich markdown elements (fenced code blocks, tables, lists, or links)
- At least one structural element (code block, table, or list)
- No markdown images (temporarily disabled pending platform image upload support)

These requirements are validated at create time and before push.

## Generating Narratives with AI

To generate narratives using the agent runtime:

    pretorin agent run --skill narrative-generation "Generate narrative for AC-02"

Or use the MCP server’s `pretorin_generate_control_artifacts` tool for read-only drafts through your AI agent.

See Skills for more on agent-powered narrative generation.

## No-Hallucination Requirements

Generated narratives must only document observable facts. For missing information, use TODO placeholder blocks:

- Treat existing Pretorin narratives, notes, and status fields as a starting point, not proof that a control gap exists.
- Before writing a narrative update or gap note, inspect the relevant implementation in the workspace and connected systems.
- If observed implementation is stronger than the current platform record, update the narrative to match the observed implementation and record any remaining evidence gap separately.

    [[PRETORIN_TODO]]
    missing_item: <what is missing>
    reason: Not observable from current workspace and connected MCP systems
    required_manual_action: <what user must do on platform/integrations>
    suggested_evidence_type: <policy_document|configuration|...>
    [[/PRETORIN_TODO]]

# Notes Commands

The `notes` command group manages control implementation notes. Notes are used to track gaps, manual follow-up items, and implementation context for specific controls.

## Create Local Note

    pretorin notes create ac-02 fedramp-moderate \
      -c "Gap: Missing SSO evidence. Manual next step: collect IdP configuration screenshots."

Creates a local markdown file at `notes/<framework>/<control>/<slug>.md` with YAML frontmatter. No markdown validation is applied to notes.

Options:

- `--content / -c` — Note content (required)
- `--name / -n` — Custom name (defaults to content summary)

## Push Notes

    pretorin notes push --dry-run
    pretorin notes push

Batch-pushes all unsynced local notes to the platform. Notes are append-only on the platform. After a successful push, the local file’s `platform_synced` frontmatter is set to `true`.

## List Local Notes

    pretorin notes list --local
    pretorin notes list --local --framework fedramp-moderate

Use the `--local` flag to list local note files instead of platform notes.

## List Platform Notes

    pretorin notes list ac-02 fedramp-moderate --system "My System"

## Add a Note (Direct to Platform)

    pretorin notes add ac-02 fedramp-moderate \
      --content "Gap: Missing SSO evidence. Manual next step: collect IdP configuration screenshots."

    pretorin notes add ac-02 fedramp-moderate \
      --content "MFA verified" --system "My System"

Options:

- `--content / -c` — Note content (required)
- `--system / -s` — System name or ID (uses active context if omitted)

## Resolve or Reopen a Note

    # Resolve a note
    pretorin notes resolve ac-02 fedramp-moderate <note_id>

    # Resolve with updated content
    pretorin notes resolve ac-02 fedramp-moderate <note_id> --content "Resolved: SSO config verified."

    # Reopen a resolved note
    pretorin notes resolve ac-02 fedramp-moderate <note_id> --reopen

Options:

- `--system / -s` — System name or ID
- `--reopen` — Reopen a resolved note instead of resolving it
- `--content / -c` — Optional updated note content
- `--pinned` — Optional pinned state

## Gap Note Format

When adding notes for unresolved gaps, use this structured format:

    Gap: <short title>
    Observed: <what was verifiably found>
    Missing: <what could not be verified>
    Why missing: <access/system limitation>
    Manual next step: <explicit user/platform action>

This format ensures consistency across CLI, MCP, and agent workflows when documenting compliance gaps.

# Monitoring Commands

The `monitoring` command group records security and compliance events against a system.

## Push a Monitoring Event

    pretorin monitoring push --system "My System" --title "Quarterly Access Review" \
      --event-type access_review --severity info

## Event Types

Type | Description
--- | ---
`security_scan` | Automated security scan result
`configuration_change` | Infrastructure or application configuration change
`access_review` | Periodic access review or audit
`compliance_check` | Compliance posture check or assessment

## Severity Levels

Severity | Description
--- | ---
`critical` | Requires immediate attention
`high` | Significant finding
`medium` | Moderate finding
`low` | Minor finding
`info` | Informational event

## Options

Option | Description
--- | ---
`--system` / `-s` | System name or ID (uses active context if omitted)
`--framework` / `-f` | Framework ID (uses active context if omitted)
`--title` / `-t` | Event title (required)
`--severity` | Event severity (default: `high`)
`--control` / `-c` | Control ID (e.g., `sc-07`, `ac-02`)
`--description` / `-d` | Detailed event description
`--event-type` | Event type (default: `security_scan`)
`--update-control-status` | Also update the control status to `in_progress`

## Context Requirement

The `monitoring push` command requires an active system context. Set it with `pretorin context set` or pass `--system` explicitly.

# 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

Domain | Mode | Description
--- | --- | ---
`controls` | `initial` | Draft new narratives and evidence for controls
`controls` | `notes-fix` | Address platform notes on existing controls
`controls` | `review-fix` | Fix findings from a family review job
`policy` | `answer` | Generate answers for policy questions
`policy` | `review-fix` | Fix findings from a policy review
`scope` | `answer` | Generate answers for scope questions
`scope` | `review-fix` | Fix 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 Notes

    pretorin campaign controls --mode notes-fix --family AC

### Fix Controls after Family Review

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

### Options

Option | Description
--- | ---
`--system` | Target system ID or name
`--framework-id` | Target framework ID
`--family` | Control family to target (e.g., `AC`, `AU`)
`--controls` | Specific control IDs (comma-separated)
`--all-controls` | Target all controls in the framework
`--mode` | Campaign mode: `initial`, `notes-fix`, `review-fix`
`--artifacts` | Artifact types to generate: `narratives`, `evidence`, or `both` (default: `both`)
`--review-job` | Review job ID (required for `review-fix` mode)
`--concurrency` | Number of parallel workers
`--max-retries` | Maximum retry attempts per item
`--checkpoint` | Path to checkpoint file for resume
`--apply` | Apply proposals to platform after completion
`--output` | Output 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

Option | Description
--- | ---
`--policies` | Specific policy IDs (comma-separated)
`--all-incomplete` | Target all incomplete policies
`--mode` | Campaign mode: `answer`, `review-fix`
`--system` | Optional system context passthrough
`--concurrency` | Number of parallel workers (default: 4)
`--max-retries` | Maximum retry attempts per item (default: 2)
`--checkpoint` | Path to checkpoint file for resume
`--apply` | Apply proposals to platform after completion
`--output` | Output mode: `auto`, `live`, `compact`, `json`

## Scope Campaigns

### Answer Scope Questions

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

### Options

Option | Description
--- | ---
`--system` | Target system ID or name (required)
`--framework-id` | Target framework ID (required)
`--mode` | Campaign mode: `answer`, `review-fix`
`--concurrency` | Number of parallel workers (default: 4)
`--max-retries` | Maximum retry attempts per question (default: 2)
`--checkpoint` | Path to checkpoint file for resume
`--apply` | Apply proposals to platform after completion
`--output` | Output 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.

# Vendor Management

The `vendor` command group manages vendor entities and their evidence documents. Vendors represent external providers (CSPs, SaaS, managed services) or internal teams whose controls your system inherits.

## List Vendors

    pretorin vendor list

## Create a Vendor

    pretorin vendor create "AWS" --type csp --description "Primary cloud provider" \
      --authorization-level "FedRAMP High P-ATO"

### Vendor Types

Type | Description
--- | ---
`csp` | Cloud Service Provider
`saas` | Software as a Service
`managed_service` | Managed service provider
`internal` | Internal team or shared service

## Get Vendor Details

    pretorin vendor get <vendor_id>

## Update a Vendor

    pretorin vendor update <vendor_id> --name "AWS GovCloud" --authorization-level "FedRAMP High"

## Delete a Vendor

    pretorin vendor delete <vendor_id>
    pretorin vendor delete <vendor_id> --force  # skip confirmation

## Upload Vendor Documents

Upload SOC 2 reports, Customer Responsibility Matrices (CRMs), FedRAMP packages, or other vendor evidence:

    pretorin vendor upload-doc <vendor_id> ./aws-soc2-report.pdf \
      --name "AWS SOC 2 Type II" \
      --description "Annual SOC 2 report covering 2025" \
      --attestation-type third_party_attestation

### Attestation Types

Type | Description
--- | ---
`self_attested` | Vendor’s own assertion
`third_party_attestation` | Independent auditor report (SOC 2, FedRAMP)
`vendor_provided` | Documentation provided by vendor

## List Vendor Documents

    pretorin vendor list-docs <vendor_id>

## Related: Control Inheritance

Once vendors are created and documents uploaded, use the MCP tools or platform to set control responsibility edges:

- `pretorin_set_control_responsibility` — Mark controls as inherited/shared
- `pretorin_generate_inheritance_narrative` — AI-draft inheritance narratives from vendor docs
- `pretorin_get_stale_edges` / `pretorin_sync_stale_edges` — Monitor and sync inheritance

# STIG & CCI Browsing

The `stig` and `cci` command groups let you browse STIG benchmarks, rules, and CCIs with full traceability from NIST 800-53 controls down to individual STIG check rules.

## STIG Commands

### List STIG Benchmarks

    pretorin stig list
    pretorin stig list --technology-area "Network"
    pretorin stig list --product "Windows" --limit 10

### Show STIG Details

    pretorin stig show <stig_id>

Shows benchmark metadata including title, version, release info, and severity breakdown of rules.

### List Rules for a STIG

    pretorin stig rules <stig_id>
    pretorin stig rules <stig_id> --severity cat_i
    pretorin stig rules <stig_id> --cci CCI-000015 --limit 20

### Show Applicable STIGs

    # Uses active system context
    pretorin stig applicable

    # Explicit system
    pretorin stig applicable --system "My System"

### AI-Infer Applicable STIGs

    pretorin stig infer
    pretorin stig infer --system "My System"

Uses the system’s profile to recommend which STIG benchmarks should apply.

## CCI Commands

CCIs (Control Correlation Identifiers) bridge NIST 800-53 controls to specific STIG rules via SRGs (Security Requirements Guides).

### List CCIs

    pretorin cci list
    pretorin cci list --control ac-2
    pretorin cci list --status draft --limit 50

### Show CCI Details

    pretorin cci show CCI-000015

Shows the CCI definition, linked SRGs, and linked STIG rules.

### Full Traceability Chain

    pretorin cci chain ac-2
    pretorin cci chain ac-2 --system "My System"

Shows the complete chain: NIST 800-53 Control -> CCIs -> SRGs -> STIG rules (and test results when `--system` is provided).

This is useful for understanding exactly which technical checks validate a given control requirement.

### Per-System CCI Implementation

    pretorin cci impl <cci_uuid> --system "My System"

Reads the per-system CCI implementation row by `(system, cci_uuid)`. Returns the live impl detail — status, status source, narrative (operator-authored or AI-generated draft), evidence count, conflict flag, and eMASS fields. A 404 means the impl row hasn’t been initialized yet for this system.

Use this when you already have the CCI catalog UUID (from `cci show` or upstream tooling) and want the system-specific compliance state without walking the full rollup.

> STIG-to-CCI assignment is catalog-level. DISA defines the STIG-rule → CCI relationship in the catalog. There is no “assign STIG X to CCI Y on this system” operation — per-system applicability and per-system test results combine with the catalog mapping to produce the rollup. Use `cci chain --system` for the full picture.

# STIG Scanning

> Note: The legacy `pretorin scan` command was removed when the recipes system landed. Scanning now happens through recipes: each scanner ships as a built-in recipe that the calling AI agent (Claude Code, Codex CLI, custom MCP client, or `pretorin agent`) invokes through MCP.

> If you have local automation that called `pretorin scan run`, switch it to invoke the recipe directly via the agent or use `pretorin recipe list` to discover the equivalent recipe.

## Available Scanner Recipes

Recipe ID | Wraps | CLI requirement
--- | --- | ---
`inspec-baseline` | Chef InSpec | `inspec`
`openscap-baseline` | OpenSCAP | `oscap`
`cloud-aws-baseline` | AWS APIs (boto3) | `aws`
`cloud-azure-baseline` | Azure APIs | `az`
`manual-attestation` | Human attestation (no external tool) | —

List them locally:

    pretorin recipe list
    pretorin recipe show inspec-baseline

## How a Calling Agent Runs a Scan

The agent (running in your IDE or via `pretorin agent run`) opens a recipe context, calls the recipe’s `run_scan` script with a STIG id, and submits the returned per-rule results to the platform via `pretorin_submit_test_results`.

The recipe body (the markdown under the frontmatter in `recipe.md`) is the prompt the agent reads to know what to do. You don’t run the recipe by hand; you ask the agent something like:

> “Run an inspec-baseline scan against `RHEL_9_STIG` on this system.”

Behind the scenes the agent:

1. Calls `pretorin_start_recipe(id="inspec-baseline", system_id=...)`.
2. Calls the recipe’s `run_scan` tool with `stig_id="RHEL_9_STIG"`.
3. Reads the returned summary (per-rule pass/fail/error/not_applicable counts).
4. Submits results via `pretorin_submit_test_results`.
5. Calls `pretorin_end_recipe(...)`.

## Test Manifest

Browse what’s testable for a system without running anything:

    pretorin stig applicable --system "My System"
    pretorin cci chain ac-2 --system "My System"

The MCP equivalent is `pretorin_get_test_manifest` — the calling agent uses this to figure out which rules apply before running a scan.

## Authoring Your Own Scanner Recipe

Scanner recipes are just recipes. If you have an internal tool that produces STIG-style results, scaffold a recipe and drop it in `~/.pretorin/recipes/<id>/` (user) or `<repo>/.pretorin/recipes/<id>/` (team):

    pretorin recipe new my-scanner --location user

See the Authoring recipes docs for the full contract.

## Submitting Results Manually

If you have raw scan output and want to push it without running the recipe flow, the platform endpoint is exposed directly:

    pretorin_submit_test_results(system_id, results)

via MCP. Each result needs `rule_id`, `benchmark_id`, `status`, and tool metadata — see the STIG / CCI workflow for schema details.

# Review Commands

The `review` command group helps you review local code against framework controls.

## Run a Review

    # Uses active context for system/framework
    pretorin review run --control-id ac-02 --path ./src

    # Explicit system/framework override
    pretorin review run --control-id ac-02 --framework-id nist-800-53-r5 --system "My System" --path ./src

    # Local-only mode — saves control context as markdown, no system required
    pretorin review run --control-id ac-02 --framework-id fedramp-moderate --local

    # Custom output directory for local artifacts
    pretorin review run --control-id ac-02 --framework-id fedramp-moderate --local --output-dir ./compliance-notes

`pretorin review run` does not push narratives or evidence to the platform. In normal mode, it fetches control requirements and current implementation details for comparison. In `--local` mode, it writes a markdown review artifact under `.pretorin/reviews/` or the path specified with `--output-dir`.

### Options

Option | Description
--- | ---
`--control-id` / `-c` | Control ID to review against (required)
`--framework-id` / `-f` | Framework ID (uses active context if omitted)
`--system` / `-s` | System name or ID (uses active context if omitted)
`--path` / `-p` | Path to files to review (default: `.`)
`--local` | Force local-only output (no API calls for implementation data)
`--output-dir` / `-o` | Output directory for local review artifacts (default: `.pretorin/reviews`)

## Check Implementation Status

    pretorin review status --control-id ac-02
    pretorin review status --control-id sc-07 --framework-id fedramp-moderate --system my-system

Option | Description
--- | ---
`--control-id` / `-c` | Control ID (required)
`--system` / `-s` | System name or ID (uses active context if omitted)
`--framework-id` / `-f` | Framework ID (uses active context if omitted)

# Configuration

The `config` command group manages CLI configuration stored at `~/.pretorin/config.json`.

## List Configuration

    $ pretorin config list
              Pretorin Configuration
    ┏━━━━━━━━━┳━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━┓
    ┃ Key     ┃ Value           ┃ Source      ┃
    ┡━━━━━━━━━╇━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━┩
    │ api_key │ 4MAS****...9v7o │ config file │
    └─────────┴─────────────────┴─────────────┘

    Config file: /home/user/.pretorin/config.json

## Get a Config Value

    pretorin config get api_key

## Set a Config Value

    pretorin config set api_base_url https://custom-api.example.com/api/v1

## Show Config File Path

    $ pretorin config path
    /home/user/.pretorin/config.json

## Config File Format

The config file is JSON:

    {
      "api_key": "pretorin_...",
      "api_base_url": "https://platform.pretorin.com/api/v1/public",
      "platform_api_base_url": "https://platform.pretorin.com/api/v1/public",
      "model_api_base_url": "https://platform.pretorin.com/api/v1/public/model",
      "active_system_id": "sys-abc123",
      "active_system_name": "My Application",
      "active_framework_id": "nist-800-53-r5",
      "disable_update_check": false
    }

## Configuration Keys

Key | Description
--- | ---
`api_key` | Pretorin API key
`api_base_url` | Platform REST API URL
`platform_api_base_url` | Platform API base URL
`model_api_base_url` | Model API URL for agent runtime
`openai_api_key` | Optional model key for agent runtime
`active_system_id` | Currently active system ID
`active_system_name` | Cached display name for the active system
`active_framework_id` | Currently active framework ID
`disable_update_check` | Disable passive update notifications

## Environment Variable Overrides

Environment variables take precedence over config file values. See Environment Variables for the full list.

# Complete Command Reference

## Global Options

Option | Description
--- | ---
`--json` | JSON output mode for scripting and AI agents
`--version`, `-V` | Show version and exit
`--help` | Show command help

## Root Commands

Command | Description
--- | ---
`pretorin login` | Authenticate with the Pretorin API (`--api-key/-k`, `--api-url`)
`pretorin logout` | Clear stored credentials
`pretorin whoami` | Display authentication status
`pretorin version` | Show CLI version
`pretorin update [VERSION]` | Update to latest version, or a specific version
`pretorin mcp-serve` | Start the MCP server (stdio transport)

## Framework Commands

Command | Description
--- | ---
`pretorin frameworks list` | List all frameworks
`pretorin frameworks get <id>` | Get framework details
`pretorin frameworks families <id>` | List control families
`pretorin frameworks family <fw> <family>` | Get control family details
`pretorin frameworks controls <id> [FAMILY_ID]` | List controls (`--family/-f`, `--limit/-n`)
`pretorin frameworks control <fw> <ctrl>` | Get control details (`--brief/-b`)
`pretorin frameworks documents <id>` | Get document requirements
`pretorin frameworks metadata <id>` | Get per-control framework metadata
`pretorin frameworks submit-artifact <file>` | Submit a compliance artifact JSON file

### Custom Frameworks

Subcommands of `pretorin frameworks` for authoring, validating, and uploading custom or forked frameworks. See Custom Frameworks for the full authoring workflow.

Command | Description
--- | ---
`pretorin frameworks init-custom <framework_id>` | Scaffold a minimal valid `unified.json` (`--title/-t`, `--output/-o`, `--force/-f`)
`pretorin frameworks validate-custom <file>` | Validate a `unified.json` artifact against the bundled JSON Schema
`pretorin frameworks build-custom <input>` | Normalize a source catalog (unified, OSCAL, or known custom) into uploadable `unified.json` (`--framework-id/-f` required, `--output/-o`, `--force`)
`pretorin frameworks upload-custom <file>` | Upload a `unified.json` artifact as a draft revision (`--framework-id/-f`, `--version-label/-v`, `--publish`)
`pretorin frameworks fork-framework <source_id> <new_id>` | Create a linked-fork draft from an upstream framework (`--version-label/-v`)
`pretorin frameworks rebase-fork <framework_id>` | Create a rebase draft for a fork against the latest upstream revision (`--version-label/-v`)
`pretorin frameworks revisions <framework_id>` | List all draft and published revisions for a framework
`pretorin frameworks export-oscal <file>` | Regenerate an OSCAL catalog from a `unified.json` artifact (`--output/-o`, `--force`)

## Context Commands

Command | Description
--- | ---
`pretorin context list` | List systems and frameworks with progress
`pretorin context set` | Set active system/framework context (`--system/-s`, `--framework/-f`, `--no-verify`)
`pretorin context show` | Display and validate current active context (`--quiet/-q`, `--check`)
`pretorin context clear` | Clear active context
`pretorin context verify` | Verify active context with source attestation (`--ttl`, `--quiet/-q`)
`pretorin context manifest` | Show resolved source manifest and evaluate against detected sources (`--quiet/-q`)

## Control Commands

Command | Description
--- | ---
`pretorin control status <ctrl> <status>` | Update control implementation status (`--framework-id/-f`, `--system/-s`)
`pretorin control context <ctrl>` | Get rich control context with AI guidance (`--framework-id/-f`, `--system/-s`)

## Evidence Commands

Command | Description
--- | ---
`pretorin evidence create <ctrl> <fw>` | Create a local evidence file (`--name/-n`, `--description/-d`, `--type/-t`)
`pretorin evidence list` | List local evidence files (`--framework/-f`)
`pretorin evidence push` | Push local evidence to the platform (`--dry-run`)
`pretorin evidence search` | Search platform evidence (`--control-id/-c`, `--framework-id/-f`, `--system/-s`, `--limit/-n`)
`pretorin evidence upsert <ctrl> <fw>` | Find-or-create evidence and link it (`--name/-n`, `--description/-d`, `--type/-t`, `--system/-s`, `--code-file`, `--code-lines`, `--code-repo`, `--code-commit`)
`pretorin evidence upload <file> <ctrl> <fw>` | Upload a file as evidence (`--name/-n`, `--type/-t`, `--description/-d`, `--system/-s`)
`pretorin evidence link <evidence_id> <ctrl>` | Link evidence to a control (`--framework-id/-f`, `--system/-s`)
`pretorin evidence delete <evidence_id>` | Delete an evidence item (`--system/-s`, `--framework-id/-f`, `--yes/-y`)

## Narrative Commands

Command | Description
--- | ---
`pretorin narrative create <ctrl> <fw>` | Create a local narrative file (`--content/-c`, `--name/-n`, `--ai-generated`)
`pretorin narrative list` | List local narrative files (`--framework/-f`)
`pretorin narrative push` | Push local narratives to the platform (`--dry-run`)
`pretorin narrative push-file <ctrl> <fw> <sys> <file>` | Push a single narrative file to the platform
`pretorin narrative get <ctrl> <fw>` | Get current control narrative (`--system/-s`)

## Notes Commands

Command | Description
--- | ---
`pretorin notes create <ctrl> <fw>` | Create a local note file (`--content/-c`, `--name/-n`)
`pretorin notes list [ctrl] [fw]` | List notes — platform (`--system/-s`) or local (`--local`, `--framework/-f`)
`pretorin notes push` | Push local notes to the platform (`--dry-run`)
`pretorin notes add <ctrl> <fw>` | Add a note directly on the platform (`--content/-c`, `--system/-s`)
`pretorin notes resolve <ctrl> <fw> <note_id>` | Resolve or reopen a control note (`--system/-s`, `--reopen`, `--content/-c`, `--pinned`)

## Monitoring Commands

Command | Description
--- | ---
`pretorin monitoring push` | Push a monitoring event (`--system/-s`, `--framework/-f`, `--title/-t`, `--event-type`, `--severity`, `--control/-c`, `--description/-d`, `--update-control-status`)

## Policy Commands

Command | Description
--- | ---
`pretorin policy list` | List org policies available for questionnaire work
`pretorin policy show` | Show persisted policy questionnaire state (`--policy`)
`pretorin policy populate` | Draft policy questionnaire updates from the current workspace (`--policy`, `--path/-p`, `--apply`)

## Scope Commands

Command | Description
--- | ---
`pretorin scope show` | Show scope questionnaire state and review findings (`--system/-s`, `--framework-id/-f`)
`pretorin scope populate` | Draft scope questionnaire updates from the current workspace (`--system/-s`, `--framework-id/-f`, `--path/-p`, `--apply`)

## Agent Commands

Command | Description
--- | ---
`pretorin agent run "<task>"` | Run a compliance task (`--skill/-s`, `--model/-m`, `--base-url`, `--working-dir/-w`, `--no-stream`, `--legacy`, `--max-turns`, `--no-mcp`)
`pretorin agent doctor` | Validate Codex runtime setup
`pretorin agent install` | Download the pinned Codex binary
`pretorin agent version` | Show pinned Codex version and install status
`pretorin agent skills` | List available agent skills
`pretorin agent mcp-list` | List configured MCP servers for the agent
`pretorin agent mcp-add <name> <transport> <cmd>` | Add an MCP server configuration (`--arg/-a`, `--scope`)
`pretorin agent mcp-remove <name>` | Remove an MCP server configuration

## Skill Commands

Command | Description
--- | ---
`pretorin skill install` | Install the Pretorin skill for AI coding agents (`--agent/-a`, `--path/-p`, `--force/-f`)
`pretorin skill uninstall` | Uninstall the Pretorin skill (`--agent/-a`, `--path/-p`)
`pretorin skill status` | Show installation status of the Pretorin skill
`pretorin skill list-agents` | List all known agents and their skill directories

## Review Commands

Command | Description
--- | ---
`pretorin review run` | Review code against a control (`--control-id/-c`, `--framework-id/-f`, `--system/-s`, `--path/-p`, `--local`, `--output-dir/-o`)
`pretorin review status` | Check implementation status for a control (`--control-id/-c`, `--framework-id/-f`, `--system/-s`)

## Config Commands

Command | Description
--- | ---
`pretorin config list` | List all configuration
`pretorin config get <key>` | Get a config value
`pretorin config set <key> <value>` | Set a config value
`pretorin config path` | Show config file path

## Campaign Commands

Command | Description
--- | ---
`pretorin campaign controls` | Run bulk control narrative/evidence campaign (`--system`, `--framework-id`, `--mode`, `--family`, `--controls`, `--all-controls`, `--artifacts`, `--review-job`, `--concurrency`, `--max-retries`, `--checkpoint`, `--apply`, `--output`)
`pretorin campaign policy` | Run bulk policy questionnaire campaign (`--mode`, `--policies`, `--all-incomplete`, `--system`, `--concurrency`, `--max-retries`, `--checkpoint`, `--apply`, `--output`)
`pretorin campaign scope` | Run bulk scope questionnaire campaign (`--system`, `--framework-id`, `--mode`, `--concurrency`, `--max-retries`, `--checkpoint`, `--apply`, `--output`)
`pretorin campaign status` | Show campaign progress from a checkpoint file (`--checkpoint`, `--output`)

### Campaign Modes

Domain | Mode | Description
--- | --- | ---
controls | `initial` | Draft new narratives and evidence for controls
controls | `notes-fix` | Address platform notes on existing controls
controls | `review-fix` | Fix findings from a family review job
policy | `answer` | Generate answers for policy questions
policy | `review-fix` | Fix findings from a policy review
scope | `answer` | Generate answers for scope questions
scope | `review-fix` | Fix findings from a scope review

## Vendor Commands

Command | Description
--- | ---
`pretorin vendor list` | List all vendors in the organization
`pretorin vendor create <name>` | Create a vendor (`--type/-t`, `--description/-d`, `--authorization-level/-a`)
`pretorin vendor get <vendor_id>` | Get vendor details
`pretorin vendor update <vendor_id>` | Update vendor fields (`--name`, `--description/-d`, `--type/-t`, `--authorization-level/-a`)
`pretorin vendor delete <vendor_id>` | Delete a vendor (`--force/-f`)
`pretorin vendor upload-doc <vendor_id> <file>` | Upload a vendor evidence document (`--name/-n`, `--description/-d`, `--attestation-type`)
`pretorin vendor list-docs <vendor_id>` | List documents linked to a vendor

### Vendor Types

`csp`, `saas`, `managed_service`, `internal`

## STIG Commands

Command | Description
--- | ---
`pretorin stig list` | List STIG benchmarks (`--technology-area/-t`, `--product/-p`, `--limit/-l`)
`pretorin stig show <stig_id>` | Show STIG benchmark detail with severity breakdown
`pretorin stig rules <stig_id>` | List rules for a benchmark (`--severity/-s`, `--cci`, `--limit/-l`)
`pretorin stig applicable` | Show applicable STIGs for the active system (`--system/-s`)
`pretorin stig infer` | AI-infer applicable STIGs from system profile (`--system/-s`)

## CCI Commands

Command | Description
--- | ---
`pretorin cci list` | List CCIs (`--control/-c`, `--status`, `--limit/-l`)
`pretorin cci show <cci_id>` | Show CCI detail with linked SRGs and STIG rules (e.g., `CCI-000015`)
`pretorin cci chain <control_id>` | Full traceability chain: Control -> CCIs -> SRGs -> STIG rules (`--system/-s`)

## Recipe Commands

Recipes are markdown + script playbooks the calling AI agent executes. See Recipes for authoring guidance.

Command | Description
--- | ---
`pretorin recipe list` | List all loaded recipes with id, name, tier, author, and source path (`--tier`, `--source`)
`pretorin recipe show <recipe_id>` | Display a recipe’s manifest, body, and (with `--sources`) all loader paths
`pretorin recipe new <recipe_id>` | Scaffold a new recipe directory (`--location` user/project/builtin, `--author`, `--name`)
`pretorin recipe validate <recipe_id>` | Validate a recipe’s manifest, scripts, and description quality (`--path` for path-based override)
`pretorin recipe run <recipe_id>` | Run a recipe’s script locally for testing (`--script/-s`, `--param/-p` repeatable, `--path`, `--system`, `--framework`, `--no-context`)

## Scanning

The legacy `pretorin scan` command was removed when the recipes system landed. Scanning now happens through built-in recipes that the calling AI agent invokes via MCP. See STIG Scanning for the recipe-based workflow.

Recipe ID | Wraps | CLI requirement
--- | --- | ---
`inspec-baseline` | Chef InSpec | `inspec`
`openscap-baseline` | OpenSCAP | `oscap`
`cloud-aws-baseline` | AWS APIs | `aws`
`cloud-azure-baseline` | Azure APIs | `az`
`manual-attestation` | Human attestation | —

## Deprecated Commands

Command | Description
--- | ---
`pretorin harness init` | Deprecated: initialize harness config
`pretorin harness doctor` | Deprecated: validate harness setup
`pretorin harness run "<task>"` | Deprecated: run task through harness backend

# MCP Integration Overview

The Pretorin CLI includes a built-in Model Context Protocol (MCP) server that enables AI assistants to access compliance framework data directly during conversations.

## Why MCP?

The Model Context Protocol allows AI assistants to:

- Access real-time data — Query the latest compliance frameworks, controls, and requirements
- Understand context — Get detailed control guidance and related controls for better recommendations
- Reduce hallucination — Work with authoritative compliance data instead of training knowledge
- Streamline workflows — No need to copy-paste control requirements or switch between tools

## How It Works

The MCP server communicates via stdio (standard input/output) using JSON-RPC messages. When you start it with `pretorin mcp-serve`, your AI tool connects and gains access to 94 compliance tools.

    ┌──────────────┐     stdio      ┌──────────────┐     HTTPS     ┌──────────────┐
    │   AI Agent   │◄──────────────►│   Pretorin   │◄─────────────►│   Pretorin   │
    │ (Claude,     │   JSON-RPC    │   MCP Server  │              │   Platform   │
    │  Cursor,     │               │               │              │              │
    │  Codex)      │               │               │              │              │
    └──────────────┘               └──────────────┘              └──────────────┘

## Scope

Scoped compliance execution tools on the MCP server run inside exactly one `system + framework` pair at a time. Set the active scope with `pretorin context set`, or pass both values explicitly. If a request spans multiple frameworks or systems, split it into separate runs.

Before running write-heavy MCP workflows from a shell or GUI wrapper, prefer validating the stored scope with:

    pretorin context show --quiet --check

## Tool Categories

The 94 MCP tools are organized into categories:

Category | Tools | Access
--- | --- | ---
Task Routing | 1 | Read-only, all users
Framework & Control Reference | 8 | Read-only, all users
Systems | 5 | Read-only
Evidence Management | 7 | Read/Write, requires beta
Implementation Context | 11 | Read/Write, requires beta
Compliance Updates | 3 | Write, requires beta
Workflow State & Analytics | 4 | Read-only
Family Operations | 4 | Read/Write, requires beta
Scope Workflow | 6 | Read/Write, requires beta
Policy Workflow | 7 | Read/Write, requires beta
Campaign Operations | 6 | Read/Write, requires beta
Vendor Management | 8 | Read/Write, requires beta
Inheritance & Responsibility | 6 | Read/Write, requires beta
STIG & CCI | 12 | Read-only / Write mix
Recipes & Workflows | 6 | Read-only

See Tool Reference for the complete list.

## Quick Setup

    # 1. Install
    uv tool install pretorin

    # 2. Authenticate
    pretorin login

    # 3. Add to your AI tool (example: Claude Code)
    claude mcp add --transport stdio pretorin -- pretorin mcp-serve

See Setup Guides for other AI tools.

## Example Conversations

### Getting Started with a Framework

> You: What compliance frameworks are available for government systems?

> Claude: Uses pretorin_list_frameworks — I can see several frameworks available including NIST 800-53 Rev 5, NIST 800-171, and FedRAMP at various impact levels…

### Understanding a Control

> You: I need to implement Account Management for our FedRAMP Moderate system. What does it require?

> Claude: Uses pretorin_get_control and pretorin_get_control_references — Account Management requires organizations to manage system accounts including identifying account types, establishing conditions for membership, and specifying authorized users…

### Control Family Overview

> You: Give me an overview of the Audit controls in NIST 800-53

> Claude: Uses pretorin_list_controls with family filter — The Audit and Accountability family contains controls for audit events, content, storage, review, and reporting…

# MCP Setup Guides

## Prerequisites

Install and authenticate the Pretorin CLI:

    uv tool install pretorin
    pretorin login

## Install the Pretorin Skill

The skill teaches your AI agent how to use MCP tools for compliance workflows — control ID formats, narrative authoring rules, gap analysis methodology, and more. Install it before setting up MCP:

    pretorin skill install                # both Claude Code and Codex CLI
    pretorin skill install --agent claude # Claude Code only
    pretorin skill install --agent codex  # Codex CLI only
    pretorin skill status                 # check what's installed

The skill is copied to `~/.claude/skills/pretorin/` and/or `~/.codex/skills/pretorin/` and auto-discovered by each agent.

## Claude Code

Quick setup — run a single command:

    claude mcp add --transport stdio pretorin -- pretorin mcp-serve

This registers the server for your current project. To make it available across all your projects, add `--scope user`.

Team setup — add a `.mcp.json` file to your project root so every team member gets the server automatically:

    {
      "mcpServers": {
        "pretorin": {
          "type": "stdio",
          "command": "pretorin",
          "args": ["mcp-serve"]
        }
      }
    }

Claude Code detects the file automatically.

## Claude Desktop

Add to your Claude Desktop configuration file:

- macOS: `~/Library/Application Support/Claude/claude_desktop_config.json`
- Windows: `%APPDATA%\Claude\claude_desktop_config.json`
- Linux: `~/.config/Claude/claude_desktop_config.json`

    {
      "mcpServers": {
        "pretorin": {
          "command": "pretorin",
          "args": ["mcp-serve"]
        }
      }
    }

Restart Claude Desktop after saving.

## Cursor

Add to `~/.cursor/mcp.json`:

    {
      "mcpServers": {
        "pretorin": {
          "command": "pretorin",
          "args": ["mcp-serve"]
        }
      }
    }

Restart Cursor after saving.

## OpenAI Codex CLI

Add to `~/.codex/config.toml`:

    [mcp_servers.pretorin]
    command = "pretorin"
    args = ["mcp-serve"]

If you installed Pretorin with `uv tool install` or `pipx`, prefer pinning the absolute path from `command -v pretorin` to avoid PATH drift between shells and GUI apps.

## Windsurf

Add to `~/.codeium/windsurf/mcp_config.json`:

    {
      "mcpServers": {
        "pretorin": {
          "command": "pretorin",
          "args": ["mcp-serve"]
        }
      }
    }

Restart Windsurf after saving.

## Other MCP Clients

The Pretorin MCP server follows the standard Model Context Protocol and works with any MCP-compatible client. The server communicates via stdio.

To test the server manually:

    pretorin mcp-serve

The server accepts JSON-RPC messages on stdin and responds on stdout.

## PATH Considerations

If your AI tool can’t find the `pretorin` command, use the full path:

    # Find the full path
    command -v pretorin

Then use that path in your configuration:

    {
      "mcpServers": {
        "pretorin": {
          "command": "/home/user/.local/bin/pretorin",
          "args": ["mcp-serve"]
        }
      }
    }

This is especially important for `uv tool` and `pipx` installations where the binary may not be on the PATH available to GUI applications.

Before debugging scoped MCP write failures, validate the active CLI scope:

    pretorin context show --quiet --check

# MCP Tool Reference

The MCP server provides 94 tools organized by category.

## Task Routing

### pretorin_start_task

Route a user prompt to the right workflow. Call this FIRST whenever the user references compliance work (a control, system, framework, questionnaire, or campaign). The calling agent extracts entities from the user prompt and supplies them as structured args; pretorin applies deterministic rules to pick a workflow and bundles the platform read-state (workflow_state, compliance_status, pending items) into the response. The agent then reads the selected workflow’s body via `pretorin_get_workflow` and follows it.

The one exception is pure reference questions (“show me AC-2”, “list frameworks”) — those go directly to the read-side tools without `start_task`.

Parameters:

- `entities` (required) — Structured entities extracted from the user prompt. Required sub-fields: `intent_verb` (one of `work_on`, `collect_evidence`, `draft_narrative`, `answer`, `campaign`, `inspect_status`) and `raw_prompt` (original verbatim text). Optional: `system_id`, `framework_id`, `control_ids`, `scope_question_ids`, `policy_question_ids`.
- `active_system_id` (optional) — The user’s active CLI context system_id, if any. Used to detect cross-system writes.
- `skip_inspect` (optional) — Skip the server-side platform reads when the calling agent already has fresh state. Default: `false`

Returns: Selected workflow id, resolved scope (system/framework/items), and a platform-state bundle for the agent to act on.

## Framework & Control Reference

These tools are read-only and available to all authenticated users.

### pretorin_list_frameworks

List all available compliance frameworks.

Parameters: None

Returns: List of frameworks with ID, title, version, tier, and control counts.

### pretorin_get_framework

Get detailed metadata about a specific framework including AI context (purpose, target audience, regulatory context).

Parameters:

- `framework_id` (required) — e.g., `nist-800-53-r5`, `fedramp-moderate`

Returns: Framework details including description, version, OSCAL version, and dates.

### pretorin_list_control_families

List control families for a framework with AI context (domain summary, risk context, implementation priority).

Parameters:

- `framework_id` (required)

Returns: List of control families with ID, title, class, and control count.

### pretorin_list_controls

List controls for a framework, optionally filtered by family.

Parameters:

- `framework_id` (required)
- `family_id` (optional) — Family IDs are slugs like `access-control`, not short codes. CMMC families include a level suffix (e.g., `access-control-level-2`).

Returns: List of controls with ID, title, and family.

### pretorin_get_control

Get detailed control information including AI guidance (summary, control intent, evidence expectations, implementation considerations, common failures, complexity).

Parameters:

- `framework_id` (required)
- `control_id` (required) — NIST/FedRAMP: zero-padded (`ac-01`). CMMC: dotted (`AC.L2-3.1.1`).

Returns: Control details including parameters, parts, and enhancement count.

### pretorin_get_controls_batch

Get detailed control data for many controls in one framework-scoped request.

Parameters:

- `framework_id` (required)
- `control_ids` (optional) — List of canonical control IDs; omit to retrieve all controls in the framework

Returns: Full control detail records for the requested controls.

### pretorin_get_control_references

Get reference information including statement, guidance, objectives, and related controls.

Parameters:

- `framework_id` (required)
- `control_id` (required)

Returns: Statement, guidance, objectives, parameters, and related controls.

### pretorin_get_document_requirements

Get document requirements for a framework.

Parameters:

- `framework_id` (required)

Returns: List of explicit and implicit document requirements with control references.

## Systems

### pretorin_list_systems

List systems in the current organization.

Parameters: None

Returns: System IDs, names, and summary metadata.

### pretorin_get_cli_status

Return the local Pretorin CLI version status, including update availability and upgrade guidance for MCP hosts and agents.

Parameters:

- `force` (optional) — Bypass local cache and re-check PyPI for the latest version. Default: `false`

Returns: Current version, latest version, update available flag, and upgrade instructions.

### pretorin_get_source_manifest

Get the resolved source manifest for a system and evaluate it against currently detected sources. Shows which external sources (git, cloud, HRIS, etc.) are required, recommended, or optional, and whether each is currently satisfied.

Parameters:

- `system_id` (optional) — System ID or name

Returns: Source manifest with per-source satisfaction status. Returns null manifest if none is configured.

### pretorin_get_system

Get system metadata including attached frameworks and security impact level.

Parameters:

- `system_id` (required) — System ID or name

Returns: System metadata.

### pretorin_get_compliance_status

Get framework progress and implementation posture for a system.

Parameters:

- `system_id` (required) — System ID or friendly system name

Returns: Framework status summaries and progress metrics.

## Evidence Management

### pretorin_search_evidence

Search current evidence items.

Parameters:

- `system_id` (optional) — System ID or friendly system name. When omitted, the active CLI scope is used if available.
- `control_id` (optional) — Filter by control
- `framework_id` (optional) — Filter by framework
- `limit` (optional) — Maximum number of results. Default: `20`

Returns: Matching evidence items. The server uses the same compatibility search path as the CLI for deployments that only expose system-scoped evidence routes.

### pretorin_create_evidence

Upsert evidence on the platform (find-or-create by default). If `dedupe` is true, exact matching evidence in the active system/framework scope is reused; otherwise a new record is created.

Parameters:

- `name` (required)
- `description` (required) — Must be auditor-ready markdown (no headings, at least one rich element, no images)
- `evidence_type` (required) — Must be one of the canonical evidence types
- `system_id` (optional) — Defaults to active scope
- `control_id` (optional)
- `framework_id` (optional)
- `dedupe` (optional) — Default: `true`

Returns:

- `evidence_id`
- `created` — true if new, false if reused
- `linked` — whether control/system link succeeded
- `match_basis` — `exact_name_desc_type_control_framework` or `none`

### pretorin_create_evidence_batch

Create and link multiple evidence items within one system/framework scope in a single request.

Parameters:

- `items` (required) — Array of evidence payloads with `name`, `description`, and `control_id`; may also include `evidence_type` and `relevance_notes`
- `system_id` (optional) — Defaults to active scope
- `framework_id` (optional) — Defaults to active scope

Returns: Batch creation summary with per-item results and created evidence IDs.

### pretorin_link_evidence

Link an existing evidence item to a control.

Parameters:

- `evidence_id` (required)
- `control_id` (required)
- `system_id` (optional) — Defaults to active scope
- `framework_id` (optional)

Returns: Link confirmation.

### pretorin_link_evidence_to_cci_implementation

Link an existing evidence item to a per-system CCI implementation row. Use when you already have the CCI implementation UUID (from `pretorin_get_cci_implementation` or `pretorin_get_cci_status`).

Parameters:

- `evidence_id` (required)
- `cci_implementation_id` (required)
- `system_id` (optional) — Defaults to active scope
- `override_system_mismatch` (optional, default `false`) — Permit cross-system attachment
- `override_reason` (optional) — Required when `override_system_mismatch` is true

Returns: Link confirmation with `link_summary.cci_implementation_id`.

### pretorin_link_evidence_to_stig_rule_workflow

Link an existing evidence item to a STIG rule workflow. Lazy-creates the workflow row if none exists yet for `(system, stig_rule)`. Use to attach remediation proof, mitigating-control documentation, or waiver-justification artifacts to a failing rule.

Parameters:

- `evidence_id` (required)
- `stig_rule_id` (required) — STIG catalog rule UUID
- `system_id` (optional) — Defaults to active scope
- `override_system_mismatch` (optional, default `false`)
- `override_reason` (optional) — Required when `override_system_mismatch` is true

Returns: Link confirmation with `link_summary.stig_rule_workflow_id`.

### pretorin_upload_evidence

Upload a file as evidence to the platform (system-scoped, requires WRITE access).

Parameters:

- `file_path` (required) — Absolute path to the file to upload
- `name` (required) — Evidence name
- `system_id` (optional) — Defaults to active scope
- `evidence_type` (optional) — Default: `other`
- `description` (optional) — Evidence description
- `control_id` (optional)
- `framework_id` (optional)

Returns: Uploaded evidence record.

### pretorin_delete_evidence

Delete an evidence item from the platform (system-scoped, requires WRITE access).

Parameters:

- `evidence_id` (required) — The evidence item ID to delete
- `system_id` (optional) — Defaults to active scope
- `framework_id` (optional) — Defaults to active scope

Returns: Deletion confirmation.

### pretorin_get_narrative

Get the current narrative record for a control.

Parameters:

- `control_id` (required)
- `system_id` (optional) — Defaults to active scope
- `framework_id` (optional) — Defaults to active scope

Returns: Narrative text, status, and AI confidence metadata.

## Implementation Context

### pretorin_get_control_context

Get rich context for a control including AI guidance, statement, objectives, scope status, and current implementation details.

Parameters:

- `control_id` (required)
- `system_id` (optional) — Defaults to active scope
- `framework_id` (optional) — Defaults to active scope

Returns: Combined control metadata and implementation details.

### pretorin_get_scope

Get system scope and policy information including excluded controls and Q&A responses.

Parameters:

- `system_id` (required)
- `framework_id` (required)

Returns: Scope narrative, excluded controls, Q&A responses, and scope status.

### pretorin_patch_scope_qa

Apply partial scope questionnaire updates for a system/framework.

Parameters:

- `system_id` (required)
- `framework_id` (required)
- `updates` (required) — Non-empty list of `{question_id, answer}` objects

Returns: Updated scope questionnaire state, including the saved responses.

### pretorin_list_org_policies

List organization policies available for questionnaire work.

Parameters: None

Returns: Policy summaries including `id`, `name`, template linkage, and questionnaire status.

### pretorin_get_org_policy_questionnaire

Get the canonical questionnaire state for one organization policy.

Parameters:

- `policy_id` (required)

Returns: Policy metadata, saved answers, and the merged template/question set when available.

### pretorin_patch_org_policy_qa

Apply partial questionnaire updates for one organization policy.

Parameters:

- `policy_id` (required)
- `updates` (required) — Non-empty list of `{question_id, answer}` objects

Returns: Updated organization policy questionnaire state.

### pretorin_get_control_implementation

Get implementation details for a control in a system.

Parameters:

- `control_id` (required)
- `system_id` (optional) — Defaults to active scope
- `framework_id` (optional) — Defaults to active scope

Returns: Current status, narrative, evidence count, and notes metadata.

### pretorin_get_control_notes

Get notes for a control implementation.

Parameters:

- `control_id` (required)
- `system_id` (optional) — Defaults to active scope
- `framework_id` (optional)

Returns: Note list with `total` count.

### pretorin_update_narrative

Push a narrative text update for a control implementation.

Parameters:

- `control_id` (required)
- `narrative` (required) — Must be auditor-ready markdown (no headings, 2+ rich elements, 1+ structural element, no images)
- `system_id` (optional) — Defaults to active scope
- `framework_id` (optional) — Defaults to active scope
- `is_ai_generated` (optional) — Default: `false`

Returns: Update confirmation.

### pretorin_add_control_note

Add a note for unresolved gaps or manual follow-up actions. Content is plain text (no markdown validation required).

Parameters:

- `control_id` (required)
- `content` (required)
- `system_id` (optional) — Defaults to active scope
- `framework_id` (optional) — Defaults to active scope

Returns: The created note record.

### pretorin_resolve_control_note

Resolve, unresolve, or update an existing control note. Use this to clear blocking notes so control status can advance.

Parameters:

- `control_id` (required)
- `note_id` (required)
- `system_id` (optional) — Defaults to active scope
- `framework_id` (optional) — Defaults to active scope
- `is_resolved` (optional) — Default: `true`
- `content` (optional) — Updated note content
- `is_pinned` (optional)

Returns: The updated note record.

## Compliance Updates

### pretorin_update_control_status

Update the implementation status for a control.

Parameters:

- `control_id` (required)
- `status` (required) — `implemented`, `in_progress`, `inherited`, `not_applicable`, `not_started`, `partially_implemented`, `planned`, `ready_to_approve`
- `system_id` (optional) — Defaults to active scope
- `framework_id` (optional)

Returns: Status update confirmation.

### pretorin_push_monitoring_event

Create a monitoring event for a system.

Parameters:

- `title` (required)
- `system_id` (optional) — Defaults to active scope
- `framework_id` (optional) — Defaults to active scope
- `severity` (optional) — `critical`, `high`, `medium`, `low`, `info`. Default: `medium`
- `event_type` (optional) — `security_scan`, `configuration_change`, `access_review`, `compliance_check`. Default: `security_scan`
- `control_id` (optional)
- `description` (optional)

Returns: The created monitoring event.

### pretorin_generate_control_artifacts

Generate read-only AI drafts for a control narrative and evidence-gap assessment.

Parameters:

- `system_id` (required)
- `control_id` (required)
- `framework_id` (required)
- `working_directory` (optional) — Local workspace path for code-aware drafting
- `model` (optional) — Model override

Returns: Draft narrative text plus evidence-gap analysis. Does not write to the platform.

Use `pretorin_update_narrative`, `pretorin_create_evidence`, and `pretorin_add_control_note` to persist approved changes.

## Workflow State & Analytics

### pretorin_get_workflow_state

Get the lifecycle state for a system+framework, showing which stage needs work next (scope, policies, controls, evidence).

Parameters:

- `system_id` (required)
- `framework_id` (required)

Returns: Current workflow stage, completion percentages, and next recommended action.

### pretorin_get_analytics_summary

Get a lightweight system progress snapshot.

Parameters:

- `system_id` (required)
- `framework_id` (required)

Returns: Scope completion, policy completion, control coverage, and evidence gaps.

### pretorin_get_family_analytics

Get per-family breakdown with narrative coverage, evidence coverage, and status distribution.

Parameters:

- `system_id` (required)
- `framework_id` (required)

Returns: Per-family metrics.

### pretorin_get_policy_analytics

Get per-policy breakdown with answer completion and review status.

Parameters:

- `policy_id` (required)

Returns: Per-policy completion metrics.

## Family Operations

### pretorin_get_pending_families

Identify which control families need work.

Parameters:

- `system_id` (required)
- `framework_id` (required)

Returns: Families with counts of pending vs total controls.

### pretorin_get_family_bundle

Get all controls in one family with status, narrative presence, evidence presence, and note counts.

Parameters:

- `system_id` (required)
- `family_id` (required)
- `framework_id` (required)

Returns: Complete family bundle with per-control details.

### pretorin_trigger_family_review

Trigger AI review of all controls in a family. Takes 2-4 minutes for large families.

Parameters:

- `system_id` (required)
- `family_id` (required)
- `framework_id` (required)

Returns: Review job ID for polling.

### pretorin_get_family_review_results

Poll family review results.

Parameters:

- `system_id` (required)
- `job_id` (required)

Returns: Aggregated findings with severity, affected control IDs, and recommended fixes.

## Scope Workflow

### pretorin_get_pending_scope_questions

Get only unanswered scope questions (lightweight).

Parameters:

- `system_id` (required)
- `framework_id` (required)

Returns: List of unanswered questions with IDs.

### pretorin_get_scope_question_detail

Get guidance, tips, and example responses for a specific scope question.

Parameters:

- `system_id` (required)
- `question_id` (required)
- `framework_id` (required)

Returns: Question text, guidance, tips, and example answers.

### pretorin_answer_scope_question

Answer one scope question.

Parameters:

- `system_id` (required)
- `question_id` (required)
- `answer` (required)
- `framework_id` (required)

Returns: Updated question state.

### pretorin_trigger_scope_generation

Trigger AI generation of scope document from answered questions.

Parameters:

- `system_id` (required)
- `framework_id` (required)

Returns: Generation job ID for polling.

### pretorin_trigger_scope_review

Trigger AI review of scope answers.

Parameters:

- `system_id` (required)
- `framework_id` (required)

Returns: Review job ID for polling.

### pretorin_get_scope_review_results

Poll for structured scope review findings.

Parameters:

- `system_id` (required)
- `job_id` (required)

Returns: Findings with severity levels and recommended fixes.

## Policy Workflow

### pretorin_get_pending_policy_questions

Get only unanswered policy questions.

Parameters:

- `policy_id` (required)

Returns: List of unanswered questions.

### pretorin_get_policy_question_detail

Get guidance, tips, and examples for a specific policy question.

Parameters:

- `policy_id` (required)
- `question_id` (required)

Returns: Question text, guidance, and example answers.

### pretorin_answer_policy_question

Answer one policy question.

Parameters:

- `policy_id` (required)
- `question_id` (required)
- `answer` (required)

Returns: Updated question state.

### pretorin_get_policy_workflow_state

Get per-policy workflow state including completion, generation, and review status.

Parameters:

- `policy_id` (required)

Returns: Policy workflow state.

### pretorin_trigger_policy_generation

Trigger AI generation of policy document from answered questions.

Parameters:

- `policy_id` (required)
- `system_id` (optional) — System ID for scope context

Returns: Generation job status.

### pretorin_trigger_policy_review

Trigger AI review of policy answers/document.

Parameters:

- `policy_id` (required)

Returns: Review job ID for polling.

### pretorin_get_policy_review_results

Poll for structured policy review findings.

Parameters:

- `policy_id` (required)
- `job_id` (required)

Returns: Findings with severity levels and recommended fixes.

## Campaign Operations

Campaigns enable bulk compliance operations with checkpoint persistence and lease-based concurrency.

### pretorin_prepare_campaign

Prepare a workflow-aligned campaign run with a platform state snapshot.

Parameters:

- `domain` (required) — `controls`, `policy`, or `scope`
- `mode` (required) — Campaign mode for the selected domain
- `system_id` (optional) — Defaults to active scope
- `framework_id` (optional) — Defaults to active scope
- `family_id` (optional) — Target family for control campaigns
- `control_ids` (optional) — Explicit control IDs to include
- `all_controls` (optional) — Include all controls. Default: `false`
- `artifacts` (optional) — Artifact type: `narratives`, `evidence`, or `both`. Default: `both`
- `review_job` (optional) — Family review job ID for `review-fix` mode
- `policy_ids` (optional) — Explicit policy IDs to include
- `all_incomplete` (optional) — Include all incomplete items. Default: `false`
- `apply` (optional) — Apply proposals immediately. Default: `false`
- `output` (optional) — Output format: `auto`, `live`, `compact`, `json`. Default: `json`
- `checkpoint_path` (optional) — Local checkpoint file path
- `working_directory` (optional) — Working directory for executors
- `concurrency` (optional) — Parallel execution limit. Default: `4`
- `max_retries` (optional) — Retry limit per item. Default: `2`

Returns: Campaign checkpoint with item list and metadata.

### pretorin_claim_campaign_items

Claim items for drafting with TTL-based leases. Safe for fan-out to multiple agents.

Parameters:

- `checkpoint_path` (required) — Local campaign checkpoint path
- `lease_owner` (optional) — Stable identifier for the claiming agent
- `max_items` (optional) — Number of items to claim. Default: `1`
- `lease_ttl_seconds` (optional) — Lease time-to-live. Default: `300`

Returns: Claimed items with lease metadata.

### pretorin_get_campaign_item_context

Get full item context plus drafting instructions for a claimed item.

Parameters:

- `checkpoint_path` (required)
- `item_id` (required)

Returns: Control/policy/scope context, current state, and drafting guidance.

### pretorin_submit_campaign_proposal

Submit an external agent’s proposal without applying it to the platform.

Parameters:

- `checkpoint_path` (required)
- `item_id` (required)
- `proposal` (required) — Campaign proposal payload object

Returns: Proposal acceptance confirmation.

### pretorin_apply_campaign

Push stored proposals to the platform.

Parameters:

- `checkpoint_path` (required)
- `item_ids` (optional) — Subset of item IDs to apply; omit to apply all

Returns: Apply results with per-item status.

### pretorin_get_campaign_status

Get structured campaign status with a stable transcript snapshot.

Parameters:

- `checkpoint_path` (required)

Returns: Campaign progress, item states, and transcript.

## Vendor Management

### pretorin_list_vendors

List all vendor entities in the organization.

Parameters: None

Returns: Vendor list with IDs, names, types, and authorization levels.

### pretorin_create_vendor

Create a new vendor entity.

Parameters:

- `name` (required)
- `provider_type` (required) — `csp`, `saas`, `managed_service`, `internal`
- `description` (optional)
- `authorization_level` (optional)

Returns: Created vendor record.

### pretorin_get_vendor

Get vendor details.

Parameters:

- `vendor_id` (required)

Returns: Vendor metadata and linked documents.

### pretorin_update_vendor

Update vendor fields.

Parameters:

- `vendor_id` (required)
- `name` (optional)
- `description` (optional)
- `provider_type` (optional) — `csp`, `saas`, `managed_service`, `internal`
- `authorization_level` (optional)

Returns: Updated vendor record.

### pretorin_delete_vendor

Delete a vendor entity.

Parameters:

- `vendor_id` (required)

Returns: Deletion confirmation.

### pretorin_upload_vendor_document

Upload vendor evidence documents (SOC 2 reports, CRMs, FedRAMP packages).

Parameters:

- `vendor_id` (required)
- `file_path` (required)
- `name` (optional)
- `description` (optional)
- `attestation_type` (optional) — `self_attested`, `third_party_attestation`, `vendor_provided`. Default: `vendor_provided`

Returns: Uploaded document record.

### pretorin_list_vendor_documents

List documents linked to a vendor.

Parameters:

- `vendor_id` (required)

Returns: Document list with metadata.

### pretorin_link_evidence_to_vendor

Link an evidence item to a vendor with attestation type. Set `vendor_id` to null to unlink.

Parameters:

- `evidence_id` (required)
- `vendor_id` (optional) — Vendor ID; null to unlink
- `attestation_type` (optional) — `self_attested`, `third_party_attestation`, `vendor_provided`

Returns: Link confirmation.

## Inheritance & Responsibility

### pretorin_set_control_responsibility

Create or update an inheritance edge for a control.

Parameters:

- `system_id` (required)
- `control_id` (required)
- `framework_id` (required)
- `responsibility_mode` (required) — `inherited` or `shared`
- `source_type` (optional) — `provider`, `internal`, or `hybrid`
- `vendor_id` (optional) — Vendor providing the inherited control

Returns: Created responsibility edge.

### pretorin_get_control_responsibility

Check if a control is inherited and from where.

Parameters:

- `system_id` (required)
- `control_id` (required)
- `framework_id` (required)

Returns: Responsibility edge details or null.

### pretorin_remove_control_responsibility

Convert an inherited control back to system-specific.

Parameters:

- `system_id` (required)
- `control_id` (required)
- `framework_id` (required)

Returns: Removal confirmation.

### pretorin_generate_inheritance_narrative

AI-generate an inheritance narrative grounded in vendor documentation.

Parameters:

- `system_id` (required)
- `control_id` (required)
- `framework_id` (required)

Returns: Draft inheritance narrative text.

### pretorin_get_stale_edges

Identify controls where the source narrative changed but the inherited control hasn’t been updated.

Parameters:

- `system_id` (required)

Returns: List of stale inheritance edges with source change timestamps.

### pretorin_sync_stale_edges

Bulk update inherited controls by regenerating narratives from latest source.

Parameters:

- `system_id` (required)

Returns: Sync results with per-control status.

## STIG & CCI

### pretorin_list_stigs

List STIG benchmarks with optional filters.

Parameters:

- `technology_area` (optional) — Filter by technology area
- `product` (optional) — Filter by product name
- `limit` (optional) — Default: `100`
- `offset` (optional) — Pagination offset. Default: `0`

Returns: STIG benchmark list with IDs, titles, and rule counts.

### pretorin_get_stig

Get STIG benchmark detail.

Parameters:

- `stig_id` (required)

Returns: Benchmark metadata including title, version, release info, and severity breakdown.

### pretorin_list_stig_rules

List rules for a STIG benchmark.

Parameters:

- `stig_id` (required)
- `severity` (optional) — Filter by severity (`high`, `medium`, `low`)
- `cci_id` (optional) — Filter by CCI identifier
- `limit` (optional) — Default: `100`
- `offset` (optional) — Pagination offset. Default: `0`

Returns: Rule list with IDs, titles, severities, and linked CCIs.

### pretorin_get_stig_rule

Get full STIG rule detail.

Parameters:

- `stig_id` (required)
- `rule_id` (required)

Returns: Check text, fix text, discussion, and linked CCIs.

### pretorin_list_ccis

List CCIs with optional filters.

Parameters:

- `nist_control_id` (optional) — Filter by NIST 800-53 control ID (e.g., `AC-2`)
- `status` (optional)
- `limit` (optional) — Default: `100`
- `offset` (optional) — Pagination offset. Default: `0`

Returns: CCI list with definitions and linked controls.

### pretorin_get_cci

Get CCI detail with linked SRGs and STIG rules.

Parameters:

- `cci_id` (required) — e.g., `CCI-000015`

Returns: CCI definition, linked SRGs, and linked STIG rules.

### pretorin_get_cci_chain

Get the full traceability chain: Control -> CCIs -> SRGs -> STIG rules.

Parameters:

- `nist_control_id` (required) — NIST 800-53 control ID (e.g., `AC-2`)

Returns: Complete traceability from control requirements to technical checks.

### pretorin_get_cci_status

Get CCI-level compliance rollup for a system.

Parameters:

- `system_id` (required)
- `nist_control_id` (optional) — Filter by NIST control ID (e.g., `AC-2`)

Returns: Per-CCI pass/fail status.

### pretorin_get_cci_implementation

Read a single per-system CCI implementation row by `(system_id, cci_uuid)`. Returns the live impl detail (status, status_source, narrative, ai_generated_narrative, evidence_ids, eMASS fields, has_status_conflict). 404 means the row hasn’t been initialized for this system yet.

Parameters:

- `system_id` (required)
- `cci_uuid` (required) — The CCI catalog UUID (the unique catalog row id, not the `CCI-000XXX` label)

Returns: Full CCI implementation detail.

### pretorin_get_stig_applicability

Get which STIGs apply to a system based on its profile.

Parameters:

- `system_id` (required)

Returns: List of applicable STIG benchmarks.

### pretorin_infer_stigs

AI-infer applicable STIGs from the system’s profile.

Parameters:

- `system_id` (required)

Returns: Recommended STIG benchmarks with reasoning.

### pretorin_get_test_manifest

Fetch the test manifest (applicable STIGs + rules) for a system.

Parameters:

- `system_id` (required)
- `stig_id` (optional) — Scope manifest to a specific STIG benchmark

Returns: Test manifest with applicable rules and scanner assignments.

### pretorin_submit_test_results

Upload STIG scan results to the platform.

Parameters:

- `system_id` (required)
- `cli_run_id` (required) — CLI scan run identifier
- `results` (required) — Array of test result objects
- `cli_version` (optional) — CLI version string

Returns: Submission confirmation with per-result status.

## Recipes & Workflows

Recipes are markdown playbooks the calling agent reads and executes; workflows describe how to iterate items in a domain and which recipes to pick per item. See RFC 0001 for the contract spec and `docs/src/recipes/` for authoring guides.

### pretorin_list_recipes

List loaded recipes with their summary metadata (id, name, tier, description, use_when, produces). Use this to discover which recipes are available, then call `pretorin_get_recipe(id)` to read the full body.

Parameters:

- `tier` (optional) — Filter to one tier: `official`, `partner`, or `community`
- `produces` (optional) — Filter by what the recipe produces: `evidence`, `narrative`, `both`, or `answers`

Returns: Recipe summaries with manifest metadata.

### pretorin_get_recipe

Return one recipe’s full manifest and body. The body is the markdown playbook the calling agent reads to understand the procedure.

Parameters:

- `recipe_id` (required) — Recipe id to fetch

Returns: Recipe manifest plus the markdown body.

### pretorin_start_recipe

Open a recipe execution context. Returns a `context_id` the caller passes on subsequent platform-API write tool calls so audit metadata is stamped with `producer_kind='recipe'` automatically. One recipe per session at a time (nesting forbidden in v1). Contexts auto-expire after 1 hour of inactivity.

Parameters:

- `recipe_id` (required) — Recipe id (must be loadable from the registry)
- `recipe_version` (required) — Recipe version the caller intends to run. Cross-checked against the loaded recipe; mismatch is an error.
- `params` (optional) — Inputs the calling agent supplies, validated against the recipe’s params schema
- `selection` (optional) — Structured `RecipeSelection` record from the engagement layer, stored on the context for the eventual `RecipeResult`

Returns: Context id and the resolved recipe manifest snapshot.

### pretorin_end_recipe

Close a recipe execution context and return the `RecipeResult` summary (status, evidence and narrative counts, errors, elapsed time). Must be called once the recipe’s work is complete; failure to call leaves the context in place until the 1-hour expiry sweep.

Parameters:

- `context_id` (required) — Context id returned by `pretorin_start_recipe`
- `status` (optional) — Caller-supplied disposition: `pass`, `fail`, or `needs_input`. Default: `pass`

Returns: `RecipeResult` summary.

### pretorin_list_workflows

List loaded workflow playbooks (single-control, scope-question, policy-question, campaign). Each workflow describes how to iterate items in its domain and which recipes to pick per item. Use this before picking a recipe so the agent works at the right granularity.

Parameters:

- `iterates_over` (optional) — Filter to one item-iteration shape: `single_control`, `scope_questions`, `policy_questions`, or `campaign_items`

Returns: Workflow summaries with manifest metadata.

### pretorin_get_workflow

Return one workflow’s full manifest and body. The body is the markdown playbook the calling agent reads to know how to iterate items and pick recipes per item in this workflow’s domain.

Parameters:

- `workflow_id` (required) — Workflow id to fetch

Returns: Workflow manifest plus the markdown body.

# MCP Resources

The MCP server exposes read-only resources across three URI schemes: `analysis://`, `status://`, and `workflow://`.

## Available Resources

Resource URI | Description
--- | ---
`analysis://schema` | JSON schema for compliance artifacts
`analysis://guide/{framework_id}` | Analysis guide for a specific framework
`analysis://control/{framework_id}/{control_id}` | Analysis guidance for a specific control within one framework scope
`status://cli` | Current CLI version, update availability, and upgrade guidance
`workflow://recipe/{recipe_id}` | Step-by-step workflow recipe for common compliance tasks

## Usage

Access these resources via `ReadMcpResourceTool` with `server: "pretorin"` in your MCP client.

### Analysis Schema

`analysis://schema`

Returns the JSON schema for structured compliance artifacts. Use this when generating artifact JSON to ensure correct structure. See Artifact Schema for documentation.

### Framework Analysis Guide

`analysis://guide/{framework_id}`

Available framework guides:

- `analysis://guide/fedramp-moderate`
- `analysis://guide/nist-800-53-r5`
- `analysis://guide/nist-800-171-r3`

Returns framework-specific analysis guidance including purpose, target audience, scope, and assessment methodology.

### Control Analysis Guidance

`analysis://control/{framework_id}/{control_id}`

Example: `analysis://control/fedramp-moderate/ac-02`

Returns control-specific analysis guidance including search patterns, evidence examples, and assessment criteria for one framework scope.

### CLI Status

`status://cli`

Returns the current CLI version, latest available version, whether an update is available, passive notification status, check state (verified/unverified), and the upgrade command. MCP hosts can inspect this resource to surface update guidance.

### Workflow Recipes

`workflow://recipe/{recipe_id}`

Returns a step-by-step workflow recipe for common compliance tasks. Available recipes are listed dynamically via the MCP `list_resources` method.

# MCP Troubleshooting

## “Not authenticated” Error

Ensure you’ve logged in:

    pretorin login
    pretorin whoami  # Verify authentication

## MCP Server Not Found

1. Verify pretorin is installed and in your PATH:

       which pretorin
       pretorin --version

2. Try using the full path in your configuration:

       {
         "mcpServers": {
           "pretorin": {
             "command": "/path/to/pretorin",
             "args": ["mcp-serve"]
           }
         }
       }

3. For `uv tool` or `pipx` installations, find the path:

       command -v pretorin

4. If the MCP client can talk to Pretorin but scoped write tools behave strangely, validate the stored CLI context:

       pretorin context show --quiet --check

   This catches deleted systems, detached frameworks, and other stale local scope before you debug the MCP client itself.

## Server Crashes or Hangs

Check the MCP server logs:

    pretorin mcp-serve 2>&1 | tee mcp-debug.log

Ensure your API key is valid:

    pretorin whoami

## Framework or Control Not Found

- Verify the framework ID exists: `pretorin frameworks list`
- Verify the control ID exists: `pretorin frameworks controls <framework_id>`
- Check Control ID Formats for correct formatting

## Common ID Mistakes

Error | Fix
--- | ---
`ac-1` not found | Use zero-padded: `ac-01`
`ac` family not found | Use slug: `access-control`
`AC.l2-3.1.1` not found | CMMC is case-sensitive: `AC.L2-3.1.1`
`3.1.1` control not found | 800-171 needs leading zeros: `03.01.01`

## No Systems Found

If `pretorin_list_systems` returns no systems, you need a beta code to create one on the Pretorin platform. Systems cannot be created through the CLI or MCP. Sign up for early access.

## Rate Limiting

The API uses rate limiting. If you receive `429 Too Many Requests` errors, the client automatically retries with exponential backoff. For persistent issues, reduce request frequency.

## Support

- Documentation: platform.pretorin.com/api/docs
- Issues: github.com/pretorin-ai/pretorin-cli/issues
- Platform: platform.pretorin.com

# Agent Overview

The `agent` command group runs autonomous compliance tasks using the Codex agent runtime. This is the Pretorin-hosted model mode — Pretorin manages the AI runtime and routes model calls through its `/v1` endpoints.

If you already use another AI agent (Claude Code, Cursor, etc.), use the MCP mode instead (`pretorin mcp-serve`) and connect Pretorin tools to your existing agent.

## Running a Compliance Task

    # Free-form task
    pretorin agent run "Assess AC-02 implementation gaps for my system"

    # Use a predefined skill
    pretorin agent run --skill gap-analysis "Analyze my system compliance gaps"

## Options

Option | Description
--- | ---
`--skill/-s <name>` | Use a predefined skill template
`--model/-m <model>` | Model override
`--base-url <url>` | Custom model API endpoint
`--working-dir/-w <path>` | Working directory for code analysis
`--no-stream` | Disable streaming output
`--legacy` | Use legacy OpenAI Agents SDK (deprecated)
`--max-turns <n>` | Maximum agent turns (legacy mode only, default 15)
`--no-mcp` | Disable external MCP servers (legacy mode only)

## Hosted Model Setup

Use this setup when you want `pretorin agent run` to call Pretorin-hosted model endpoints.

    # 1. Login with your Pretorin API key
    pretorin login

    # 2. Optional: override the default model proxy endpoint
    #    (default: https://platform.pretorin.com/api/v1/public/model)
    pretorin config set model_api_base_url https://your-proxy.example.com/v1

    # 3. Validate runtime
    pretorin agent doctor
    pretorin agent install

    # 4. Run a task
    pretorin agent run "Assess AC-02 implementation gaps for my system"

## Model Key Precedence

The Codex agent resolves API keys in this order:

1. `config.api_key` (from `pretorin login`) — used as bearer key for the platform model proxy
2. `OPENAI_API_KEY` environment variable
3. `config.openai_api_key`

When `--base-url` is explicitly provided (non-platform endpoint), the order changes to prefer `OPENAI_API_KEY` first, then falls back to config keys.

## Custom Model Endpoints

The agent supports any OpenAI-spec LLM endpoint, including:

- Azure OpenAI
- vLLM
- LiteLLM
- Ollama

Configure via `--base-url` flag or the `model_api_base_url` config key.

## How It Works

The agent runtime uses the Codex SDK with a pinned binary in `~/.pretorin/bin/` and an isolated `CODEX_HOME` at `~/.pretorin/codex/`. The agent:

1. Downloads and pins a specific Codex binary version
2. Runs in an isolated `CODEX_HOME` environment (never touches `~/.codex/`)
3. Automatically injects the Pretorin MCP server for compliance tool access
4. Streams events and output in real-time (unless `--no-stream` is passed)

See Agent Runtime Management for the full set of `pretorin agent` lifecycle commands.

# Agent Skills

Skills are predefined task templates that guide the agent through specific compliance workflows.

## Available Skills

Skill | Description
--- | ---
`gap-analysis` | Assess codebase against framework controls, identify gaps, and prioritize remediation
`narrative-generation` | Generate auditor-ready implementation narratives for controls
`evidence-collection` | Collect and map evidence from codebase to controls
`security-review` | Review codebase for security controls and compliance posture
`stig-scan` | Run STIG compliance scans against a system
`cci-assessment` | Assess CCI compliance for a specific control

## Using Skills

    # Gap analysis
    pretorin agent run --skill gap-analysis "Analyze my system compliance gaps"

    # Narrative generation
    pretorin agent run --skill narrative-generation "Generate narratives for all AC controls"

    # Evidence collection
    pretorin agent run --skill evidence-collection "Collect evidence for AC-02 in this repo"

    # Security review
    pretorin agent run --skill security-review "Review this codebase for AC-02 coverage"

    # STIG scan
    pretorin agent run --skill stig-scan "Check STIG applicability for my system"

    # CCI assessment
    pretorin agent run --skill cci-assessment "Assess CCI compliance for AC-02"

## List Skills

    pretorin agent skills

## Skill Details

### Gap Analysis

Systematically assesses a codebase against a framework’s controls. The agent:

1. Scopes the assessment to relevant control families
2. Prioritizes families with code evidence (Access Control, Audit, IA, SC, CM)
3. Searches the codebase for evidence matching AI guidance expectations
4. Assigns implementation status per control
5. Produces a report with findings and priority remediation items

See Gap Analysis Workflow for the detailed methodology.

### Narrative Generation

Generates control implementation narratives that meet auditor-readiness requirements:

- No markdown headings
- At least two rich markdown elements (code blocks, tables, lists, links)
- At least one structural element (code block, table, or list)
- TODO placeholders for missing information
- Only documents observable facts (no hallucination)

### Evidence Collection

Searches the codebase for evidence that maps to specific controls:

- Identifies relevant files and code patterns
- Creates evidence items with auditor-ready descriptions
- Links evidence to controls via the platform
- Flags gaps where evidence is missing

### Security Review

Reviews the codebase against specific controls:

- Analyzes code for control coverage
- Identifies implementation strengths and weaknesses
- Documents findings with file paths and line numbers
- Pushes monitoring events for critical or high-severity findings
- Produces remediation recommendations

### STIG Scan

Runs STIG compliance scans against a system:

- Checks which STIGs apply to the system (applicability)
- Gets the test manifest (rules to evaluate)
- Reports available scanners and rule coverage
- Summarizes the scan plan and gaps in automated coverage

### CCI Assessment

Assesses CCI-level compliance for a specific control:

- Gets control context and implementation status
- Lists CCIs for the target control
- Checks CCI-level test results (pass/fail/not tested)
- Identifies CCIs with no test coverage
- Presents results as a traceability chain: Control -> CCIs -> SRGs -> STIG rules

# Agent Runtime Management

The agent runtime uses a managed Codex binary with isolated configuration.

## Check Runtime Health

    pretorin agent doctor

Validates that the Codex runtime is properly installed and configured.

## Install Codex Binary

    pretorin agent install

Downloads the pinned Codex binary to `~/.pretorin/bin/`. The version is pinned by the CLI to ensure compatibility.

## Check Version

    pretorin agent version

Shows the pinned Codex version and whether it’s currently installed.

## Manage MCP Servers

The agent can connect to additional MCP servers beyond Pretorin. This lets the agent access other tools (filesystem, databases, etc.) during compliance tasks.

### List Configured Servers

    pretorin agent mcp-list

### Add a Server

    # stdio transport
    pretorin agent mcp-add <name> stdio <command> --arg <arg1> --arg <arg2>

    # http transport
    pretorin agent mcp-add <name> http <url>

Options:

Option | Description
--- | ---
`--arg/-a <arg>` | Additional args for stdio transport (repeatable)
`--scope <scope>` | Config scope: `project` (default, `.pretorin-mcp.json`) or `global` (`~/.pretorin/mcp.json`)

Examples:

    pretorin agent mcp-add github stdio uvx --arg mcp-server-github
    pretorin agent mcp-add aws http https://mcp.example.com/aws
    pretorin agent mcp-add tools stdio node --arg /path/to/server --scope global

### Remove a Server

    pretorin agent mcp-remove <name>

## Runtime Architecture

The Codex runtime is fully isolated:

- Binary location: `~/.pretorin/bin/`
- Configuration: `~/.pretorin/codex/` (`CODEX_HOME`)
- Version pinning: The CLI pins a specific Codex version for compatibility
- MCP injection: Pretorin MCP server is automatically available to the agent

This isolation ensures the agent runtime never interferes with any user-installed Codex instances.

# Authoring Recipes

Recipes are markdown-plus-Python playbooks that the calling AI agent invokes through MCP. Each recipe is a directory with a `recipe.md` (frontmatter + prose body) and one or more script files. The agent reads the body to understand what the recipe does, picks one when its `use_when` matches the task, and calls the recipe’s scripts as MCP tools.

If you’ve ever written a Claude Code skill, the shape will feel familiar — recipes are the same idea, scoped to compliance work and stamped with audit metadata automatically.

## Why You Might Write One

Three concrete reasons:

- Your team has a non-obvious procedure for capturing a particular kind of evidence (e.g., “pull the latest IAM policy from the prod account, redact customer ARNs, attach as a configuration record”). Encoding it as a recipe means every teammate’s agent does the same steps the same way.
- You wrap an internal scanner that produces STIG-style results. A recipe exposes it next to the built-in `inspec-baseline` / `openscap-baseline` so the calling agent can pick it for the rules it covers.
- You’re contributing back upstream. First-party scanner recipes (`inspec-baseline`, `openscap-baseline`, etc.) live under `src/pretorin/recipes/_recipes/`. New contributions follow the same shape.

## Write Your First Recipe in 10 Minutes

    # 1. Scaffold a fresh recipe in your user folder.
    pretorin recipe new my-first-recipe

    # 2. The scaffolder drops a directory at ~/.pretorin/recipes/my-first-recipe/
    #    with recipe.md, scripts/main.py, README.md, tests/test_recipe.py.

    # 3. Edit the description, use_when, and the body of recipe.md.
    $EDITOR ~/.pretorin/recipes/my-first-recipe/recipe.md

    # 4. Edit scripts/main.py — implement `async def run(ctx, **params)`.
    $EDITOR ~/.pretorin/recipes/my-first-recipe/scripts/main.py

    # 5. Validate.
    pretorin recipe validate my-first-recipe

    # 6. Run it locally to test (no agent / MCP boundary involved).
    pretorin recipe run my-first-recipe --param key=value

If validate passes and run prints the result you expect, the recipe is in the registry. Restart your MCP client and the agent can use it on the next task.

`pretorin recipe run` is a local-testing path: writes go through your authenticated `PretorianClient` directly, so audit-metadata stamping requires explicit `audit_metadata` on each `create_evidence` call (see Writer tools). The agent path through MCP stamps automatically.

## What Ships in v1

- Four loader paths with clear precedence: explicit > project > user > built-in. See Loader paths below.
- `pretorin recipe list / show / new / validate / run` CLI commands.
- `pretorin_list_recipes` / `pretorin_get_recipe` MCP discovery tools.
- Per-script MCP tools auto-registered as `pretorin_recipe_<safe_id>__<script_name>`.
- Audit-metadata stamping: the calling agent opens a recipe execution context with `pretorin_start_recipe(...)`; every platform write inside the context is stamped with `producer_kind="recipe"`, the recipe id, and the recipe version. The platform records the full chain.
- Workflow playbooks (`pretorin_list_workflows` / `pretorin_get_workflow`) for the four item-iteration shapes: single-control, scope-question, policy-question, campaign. Workflows describe how to iterate; recipes describe what to do per item.

## Loader Paths

Source | Path | Use it when
--- | --- | ---
Built-in | `src/pretorin/recipes/_recipes/<id>/` | First-party recipes shipped with pretorin-cli.
User folder | `~/.pretorin/recipes/<id>/` | Your local recipes — survives across projects.
Project folder | `<repo>/.pretorin/recipes/<id>/` | Team-shared recipes checked into the compliance repo.
Explicit path | `pretorin recipe show --path /abs/...` | Testing a recipe while authoring.

If the same id appears in two paths, the higher-precedence one wins. `pretorin recipe show <id> --sources` lists every location and marks which is active.

## Tier

Each loaded recipe gets a `tier` set by the loader from its source path:

- `official` — built-in, shipped with pretorin-cli (forced regardless of what the manifest says).
- `community` — anything from the user/project folders or explicit paths.
- `partner` — reserved for installed packages (v1.5).

The calling agent sees the tier in `pretorin_list_recipes` output and can factor it in when picking. Community recipes are first-class — the tier is signal, not a permission gate.

## Where to Read Next

- Manifest reference — every frontmatter field with examples.
- Script contract — the `async def run(ctx, **params) -> dict` signature.
- Writer tools — the platform-API tools your scripts call, and how audit metadata gets stamped.
- Testing — pytest fixtures and patterns.
- Publishing — how to share a community recipe or PR an official one.
- Workflows — the layer above recipes: how the calling agent picks a workflow, then picks recipes per item.
- Worked example — a community recipe walked through end-to-end.

# Manifest Reference

The manifest is the YAML frontmatter at the top of `recipe.md`. It’s the public contract between recipe authors and pretorin. The schema is defined by the pydantic models in `src/pretorin/recipes/manifest.py` and frozen per `contract_version`.

## Minimal Example

    ---
    id: my-first-recipe
    version: 0.1.0
    name: "My First Recipe"
    description: "Capture the active IAM role's trust policy and attach it as a configuration record."
    use_when: "The agent needs evidence that an IAM role's trust policy meets least-privilege requirements."
    produces: evidence
    author: "Jane Doe"
    license: Apache-2.0
    scripts:
      capture:
        path: scripts/capture.py
        description: "Pull the trust policy from AWS and return it as a redacted dict."
    ---

    # My First Recipe Body

    The agent reads everything below the closing `---` to understand what to do.

## Required Fields

Field | Type | Notes
--- | --- | ---
`id` | string | Kebab-case (`^[a-z][a-z0-9-]*$`). Globally unique across loader paths.
`version` | string | SemVer-ish. Bumped when behavior changes. Stamped on evidence.
`name` | string | Display name shown in `pretorin recipe list`.
`description` | string | ≥ 50 chars. The text the agent reads to decide if this recipe fits.
`use_when` | string | ≥ 30 chars. Explicit “when the agent has X and needs Y” guidance.
`produces` | enum | `evidence` / `narrative` / `both`. What the recipe writes back to the platform.
`author` | string | Attribution. Stamped in evidence provenance.

## Optional Fields

Field | Type | Default | Notes
--- | --- | --- | ---
`tier` | enum | `community` | `official` / `partner` / `community`. Loader overrides from source path.
`license` | string | `Apache-2.0` | SPDX identifier. Required for any recipe shared publicly.
`params` | map | `{}` | Recipe-level inputs. See Params.
`requires` | object | `{}` | CLI binaries / env vars the recipe needs. See Requires.
`attests` | list | `[]` | `[{control, framework}]` hints. Filter, not binding.
`scripts` | map | `{}` | `tool_name → ScriptDecl`. Each becomes an MCP tool. See Scripts.
`contract_version` | int | `1` | Frontmatter shape version. Bumps only on breaking changes.
`recipe_schema_version` | string | `"1.0"` | Schema this recipe is written against.
`min_pretorin_version` | string | `null` | Loader refuses to load if running pretorin is older.

## Tier

`tier` is set by the loader from the recipe’s source path, overriding whatever the manifest declares:

- Built-in path → `official`
- User folder, project folder, explicit path → `community`
- `partner` → reserved for installed packages (v1.5)

Authors should still declare a tier — it documents intent, and any manifest-internal validation runs against the declared value.

## Params

Recipe-level params are inputs the calling agent supplies via `pretorin_start_recipe(...)`. They flow through to scripts as kwargs.

    params:
      stig_id:
        type: string
        description: "STIG benchmark id targeting Linux baseline controls"
        required: true
      target:
        type: string
        description: "Optional connection string (e.g., 'ssh://host')"
        default: "local"

Supported types: `string`, `integer`, `number`, `boolean`, `array`. For `array`, declare `items: { type: ... }` so the MCP renderer can emit a real JSON Schema.

## Requires

Document the runtime environment the recipe needs. v1 captures the requirements but does not run probes — the v1.5 `pretorin recipe check` will. Failures surface at runtime when scripts try to invoke missing tools.

    requires:
      cli:
        - { name: inspec, probe: "inspec --version" }
        - { name: jq, probe: "jq --version" }
      env:
        - AWS_PROFILE
        - INSPEC_TARGET

## Attests

`attests` is a list of `{control, framework}` entries the recipe is likely relevant to. It’s a hint for filtering, never a binding — the agent picks recipes by reading `description` and `use_when`, not by joining on `attests`.

    attests:
      - { control: AC-2, framework: nist-800-53-r5 }
      - { control: AC-6, framework: nist-800-53-r5 }

## Scripts

Each `ScriptDecl` becomes an MCP tool:

    scripts:
      run_scan:
        path: scripts/run_scan.py
        description: "Pull the manifest, run the scan, return per-rule results."
        params:
          stig_id:
            type: string
            description: "STIG benchmark id"
            required: true
          target:
            type: string
            description: "Connection string"
        timeout_seconds: 600
        writes_evidence: false

Field | Notes
--- | ---
`path` | Relative to the recipe directory. The validator checks the file exists and contains `async def run`.
`description` | One-liner the agent sees on the tool. Be specific — this is the tool’s “tooltip”.
`params` | Per-script JSON Schema input. Independent of recipe-level `params`.
`timeout_seconds` | Wall-clock cap for one invocation. Default 300.
`writes_evidence` | Declared intent. The trust gate for community recipes considers it.

The MCP tool name for this script is `pretorin_recipe_<safe_id>__run_scan`, where `safe_id` is the recipe id with hyphens converted to underscores (MCP tool names can’t contain hyphens). The recipe author doesn’t construct this name — pretorin does.

## Contract and Schema Versioning

Two version fields exist for forward compatibility:

- `contract_version` — bumps only on backwards-incompatible shape changes to the frontmatter itself. Most recipes pin to `1`.
- `recipe_schema_version` — the schema this specific recipe is written against. The loader refuses to load a recipe whose schema is newer than what the running pretorin supports, with a hint to upgrade.

`min_pretorin_version` lets a recipe author require a specific runtime version (e.g., when a recipe uses a writer tool that only exists in pretorin ≥ 0.18).

## What the Loader Does to Your Manifest

1. Parses the YAML frontmatter.
2. Validates against the pydantic schema. A failure raises `RecipeManifestError` for that recipe — the registry keeps loading other recipes.
3. Overrides `tier` from the source path (built-in → official, otherwise community).
4. Caches the parsed manifest by `(path, mtime)`. If you edit the file, the next load re-parses it.

# Script Contract

Every script declared under `scripts:` in `recipe.md` must export a single async function:

    async def run(ctx, **params) -> dict:
        ...

That’s the entire contract. The recipe runner imports the module, calls `run`, awaits the result, and hands it back to the calling agent as the MCP tool response.

## The Signature

    from typing import Any

    async def run(ctx: Any, *, stig_id: str, target: str = "local") -> dict[str, Any]:
        """Run a baseline scan and return per-rule results."""
        ...

Argument | Type | What it is
--- | --- | ---
`ctx` | `RecipeScriptContext` | Per-invocation execution context (see below).
`**params` | varies | The keyword args the agent supplied, validated against `scripts.<name>.params` in the manifest.

The function must be `async`. Use `await` for I/O. Pretorin’s writer tools are async; using sync HTTP or sync subprocess for slow operations will block the recipe runner’s event loop.

The return value must be a JSON-serializable `dict`. Anything with `tuple`, `set`, `datetime`, or custom classes will fail to serialize back through MCP.

## The `ctx` Argument

`RecipeScriptContext` (defined in `src/pretorin/recipes/runner.py`):

    @dataclass
    class RecipeScriptContext:
        system_id: str | None
        framework_id: str | None
        api_client: Any           # PretorianClient — see writer-tools.md
        logger: logging.Logger
        recipe_id: str
        recipe_version: str
        recipe_context_id: str | None

The two you’ll use most:

- `ctx.api_client` — the authenticated `PretorianClient`. Use this to call platform-API methods (`ctx.api_client.create_evidence(...)`, `ctx.api_client.get_test_manifest(...)`, etc.). See Writer tools for the full surface.
- `ctx.logger` — a `logging.Logger` named for the recipe. Prefer this over `print` so the calling agent’s logs stay structured.

`ctx.system_id` is set when the calling agent specified a system at `pretorin_start_recipe` time. Use it for any per-system platform call. If your recipe doesn’t make sense without a system, raise early with a clear error.

`ctx.recipe_context_id` is the active execution context id. The MCP write handlers read it from the session automatically — you only need to pass it explicitly if your script makes a platform write outside the MCP boundary (e.g., a direct `httpx` call against a custom internal endpoint).

## Returning Results

Whatever your script returns becomes the MCP tool response the calling agent reads. Keep it structured: nested dicts the agent can inspect, with clear keys.

    async def run(ctx, *, stig_id: str) -> dict[str, Any]:
        rules = await fetch_rules(ctx.api_client, ctx.system_id, stig_id)
        results = await scan(rules)
        return {
            "stig_id": stig_id,
            "summary": {
                "total": len(results),
                "passed": sum(1 for r in results if r.status == "pass"),
                "failed": sum(1 for r in results if r.status == "fail"),
            },
            "rules": [r.to_dict() for r in results],
        }

Return shapes the agent can pattern-match are easier to act on than freeform prose. Save the prose for the recipe body — let the script return data.

## Imports Inside `scripts/`

The runner adds the recipe’s `scripts/` directory to `sys.path` for the duration of the call, so a sibling module is reachable as a top-level import:

    # scripts/run_scan.py
    from helpers import normalize_results   # reaches scripts/helpers.py

The path is removed after the call returns. You don’t have to `__init__.py`-decorate the directory.

## Error Handling

Don’t swallow exceptions inside `run`. Let them propagate — the runner catches them, logs them, and returns a structured error to the calling agent. Catching and returning a string error makes the agent think the call succeeded.

    # Bad
    async def run(ctx, *, stig_id: str) -> dict[str, Any]:
        try:
            return await fetch(ctx, stig_id)
        except Exception as e:
            return {"error": str(e)}        # agent sees a "successful" call

    # Good
    async def run(ctx, *, stig_id: str) -> dict[str, Any]:
        return await fetch(ctx, stig_id)    # exceptions surface as tool errors

## Patterns

Three shapes cover most recipes:

### Capture-from-source

The recipe pulls a thing (a config file, a snippet of code, a query result), redacts it, and registers an evidence record.

    async def run(ctx, *, file_path: str, line_range: str | None = None) -> dict[str, Any]:
        text = (Path(file_path).read_text()).split("\n")
        snippet = _slice(text, line_range)
        redacted, summary = redact_secrets(snippet)
        composed = compose_audit_markdown(redacted, file_path=file_path, line_range=line_range)
        evidence_id = await ctx.api_client.create_evidence(
            system_id=ctx.system_id,
            ...
        )
        return {"evidence_id": evidence_id, "redaction_summary": summary.to_dict()}

### Wrap-a-scanner

The recipe wraps an external tool (oscap, inspec, az, aws), runs it against the platform’s test manifest, and returns per-rule results.

    async def run(ctx, *, stig_id: str, target: str = "local") -> dict[str, Any]:
        manifest = await fetch_test_manifest(ctx.api_client, ctx.system_id, stig_id=stig_id)
        rules = rules_for_stig(manifest, stig_id)
        scanner = InSpecScanner()
        results = await scanner.execute(rules, config={"target": target})
        return {
            "stig_id": stig_id,
            "summary": summarize_results(results),
        }

The five built-in scanner recipes are exactly this shape — each is a thin adapter over a `pretorin.scanners.*` class.

### Q-and-A attestation

The recipe is the agent collecting human attestations interactively, with no external tool involved. Inputs are structured answers; the recipe just records them.

    async def run(ctx, *, stig_id: str, attestations: list[dict]) -> dict[str, Any]:
        scanner = ManualScanner()
        results = await scanner.execute(rules, config={"attestations": attestations})
        return {"stig_id": stig_id, "summary": summarize_results(results)}

## When to Write Multiple Scripts

If your recipe has steps that the calling agent might want to interleave with reasoning (e.g., “redact, show me, then compose”), expose each step as its own script. The agent can then call them as separate MCP tools and inspect the intermediate output.

The `code-evidence-capture` recipe ships two scripts (`redact_secrets` and `compose_snippet`) for exactly this reason.

# Writer Tools

Recipe scripts call the platform API through `ctx.api_client`, which is the same `PretorianClient` the rest of pretorin uses. This page covers the common write paths and how audit metadata gets stamped automatically when your recipe runs inside an execution context.

## How Audit Metadata Gets Stamped

The calling agent opens an execution context with `pretorin_start_recipe(recipe_id, recipe_version, params)` and gets back a `context_id`. Subsequent platform writes from that MCP session pick up the context automatically and stamp:

    {
      "producer_kind": "recipe",
      "producer_id": "<recipe_id>",
      "producer_version": "<recipe_version>",
      "recipe_context_id": "<context_id>"
    }

When your script makes an MCP-routed write — i.e., the calling agent’s write goes through `pretorin_create_evidence` or similar — the stamping is automatic. You don’t pass `producer_kind` yourself; the MCP handler reads the context from the session and builds the metadata.

When your script makes a write directly through `ctx.api_client` (skipping MCP), you have to pass `audit_metadata` yourself. The helper to build it lives at `pretorin.evidence.audit_metadata`:

    from pretorin.evidence.audit_metadata import build_recipe_metadata

    audit_metadata = build_recipe_metadata(
        recipe_id=ctx.recipe_id,
        recipe_version=ctx.recipe_version,
        recipe_context_id=ctx.recipe_context_id,
        source_type="configuration",
        content_hash=...,
        redaction_summary=...,
    )

    evidence = EvidenceCreate(
        control_id="ac-2",
        framework_id="nist-800-53-r5",
        name="...",
        evidence_type="configuration",
        description=composed_markdown,
        audit_metadata=audit_metadata,
    )
    await ctx.api_client.create_evidence(ctx.system_id, evidence)

Most recipes don’t need the direct path — write through MCP and let the handler stamp.

## Read-Side Helpers

You’ll often need to read platform state before writing. The most useful methods on `ctx.api_client`:

Method | Returns | Use
--- | --- | ---
`list_systems()` | `list[dict]` | Find a system id when one wasn’t passed in `ctx.system_id`.
`get_test_manifest(system_id, stig_id=None)` | `dict` | The applicable rules for a system, optionally narrowed to one STIG.
`get_control(framework_id, control_id)` | `ControlDetail` | Full control text + family + status.
`get_controls_batch(framework_id, control_ids)` | dict | Batch fetch — cheaper than N calls.
`get_control_implementation(...)` | dict | Current narrative + status for one control.
`get_stig_applicability(system_id)` | dict | Which STIGs apply to a system.
`get_compliance_status(system_id, framework_id)` | dict | High-level coverage rollup.
`get_source_manifest(system_id)` | dict | Verified-sources state for the system.

### Manifest helpers for scanner recipes

If you’re building a scanner recipe, three helpers in `pretorin.scanners.manifest` cover the common shape:

    from pretorin.scanners.manifest import (
        fetch_test_manifest,
        rules_for_stig,
        summarize_results,
    )

    manifest = await fetch_test_manifest(ctx.api_client, ctx.system_id, stig_id="RHEL_9_STIG")
    rules = rules_for_stig(manifest, "RHEL_9_STIG")
    results = await my_scanner.execute(rules, config={"target": "local"})
    summary = summarize_results(results)

Every built-in scanner recipe uses exactly this pattern. If you’re wrapping a new scanner, copy `inspec-baseline/scripts/run_scan.py` as a starting point.

## Write-Side: Evidence

    from pretorin.client.models import EvidenceCreate

    evidence = EvidenceCreate(
        control_id="ac-2",
        framework_id="nist-800-53-r5",
        name="RBAC configuration excerpt",
        evidence_type="configuration",
        description=composed_markdown,
        status="approved",
    )
    result = await ctx.api_client.create_evidence(ctx.system_id, evidence)

For batched writes (much faster when you have ≥ 10):

    from pretorin.client.models import EvidenceBatchItemCreate

    items = [
        EvidenceBatchItemCreate(control_id="ac-2", name="...", evidence_type="...", ...)
        for _ in batch
    ]
    response = await ctx.api_client.create_evidence_batch(
        ctx.system_id,
        framework_id="nist-800-53-r5",
        items=items,
    )

For binary/unstructured artifacts (a screenshot, a PDF, an exported report), upload the file:

    result = await ctx.api_client.upload_evidence(
        system_id=ctx.system_id,
        file_path="/tmp/my-screenshot.png",
        name="Console screenshot — IAM users page",
        evidence_type="screenshot",
        control_id="ac-2",
    )

## Write-Side: Narratives

    await ctx.api_client.update_narrative(
        system_id=ctx.system_id,
        framework_id="nist-800-53-r5",
        control_id="ac-2",
        narrative=composed_text,
    )

Narratives are stamped with the same audit metadata when written through the MCP write handler.

## Write-Side: Test Results

For scanner recipes:

    await ctx.api_client.submit_test_results(
        system_id=ctx.system_id,
        benchmark_id="RHEL_9_STIG",
        results=[...],   # list of TestResult dicts
    )

## Redaction and Markdown Composition

Two helpers from `pretorin.evidence`:

    from pretorin.evidence.redact import redact_secrets
    from pretorin.evidence.markdown import compose

    redacted_text, redaction = redact_secrets(raw_text)
    markdown = compose(
        prose="The IAM trust policy is configured to require MFA for assume-role.",
        snippet=redacted_text,
        snippet_lang="json",
        file_path="iam/trust-policy.json",
    )

`redact_secrets` returns a `RedactionResult` with counts per secret type (AWS access keys, GitHub tokens, JWTs, etc.). `compose` turns prose + snippet into the audit-grade markdown body the platform expects on evidence records.

These two are the building blocks of the `code-evidence-capture` built-in recipe — read its source under `src/pretorin/recipes/_recipes/code-evidence-capture/` for a real-world usage pattern.

## What Recipe Scripts Should Not Do

- Don’t bypass `ctx.api_client` to talk to the platform via raw HTTP. You’ll skip the auth, retry, and error-handling logic the client provides.
- Don’t construct `audit_metadata` from scratch. Use `build_recipe_metadata` / `build_recipe_metadata_from_context` so the shape stays consistent.
- Don’t call `pretorin_start_recipe` from inside a script. The recipe context is already open — that’s how the script got invoked. Nesting is forbidden.
- Don’t assume `ctx.system_id` is set. If your recipe requires a system, raise early with a clear message rather than passing `None` through to the API.

# Testing Recipes

Recipes are real Python modules — test them like any other Python code. The scaffolder drops a `tests/test_recipe.py` stub when you run `pretorin recipe new <id>`; this page covers what to put in it.

## Three Layers Worth Testing

A recipe has three layers and each rewards a different test style:

1. Pure helpers inside `scripts/` — redaction, normalization, parsers. Plain unit tests with no fixtures. Fastest feedback loop; most coverage per line of test code.
2. `run` against a fake `ctx` — the script’s main entry point. Mock `ctx.api_client` so you don’t hit the network.
3. End-to-end through the recipe runner — load the recipe, call its script through the runner, assert the result. Slower but proves the manifest, the importlib-based dispatch, and the script all line up.

## Unit-Testing Helpers

If you’ve factored out helpers into `scripts/redact.py` or `scripts/normalize.py`, import them directly:

    # tests/test_helpers.py
    from scripts.redact import redact_aws_keys

    def test_redact_aws_keys_replaces_full_key() -> None:
        text = "AKIAIOSFODNN7EXAMPLE"
        redacted = redact_aws_keys(text)
        assert "AKIA" not in redacted
        assert "[REDACTED:AWS_KEY]" in redacted

The recipe runner adds the `scripts/` directory to `sys.path`. In tests, make sure your `pytest.ini` or `pyproject.toml` does the same:

    [tool.pytest.ini_options]
    pythonpath = ["scripts"]

## Testing `run` with a Fake `ctx`

The script’s `run` function takes a `ctx` argument typed as `Any` (loose intentionally — see Script contract). A `MagicMock` with `AsyncMock` for the I/O methods is enough:

    # tests/test_run.py
    from unittest.mock import AsyncMock, MagicMock
    import pytest

    from scripts.run_scan import run

    @pytest.mark.asyncio
    async def test_run_returns_summary_for_no_rules() -> None:
        ctx = MagicMock()
        ctx.system_id = "sys-1"
        ctx.api_client = MagicMock()
        ctx.api_client.get_test_manifest = AsyncMock(
            return_value={"applicable_stigs": []}
        )

        result = await run(ctx, stig_id="EMPTY_STIG")

        assert result["stig_id"] == "EMPTY_STIG"
        assert result["summary"]["total"] == 0

The four scanner recipe tests in pretorin-cli’s test suite use exactly this shape — patch `get_test_manifest`, call `run`, assert on the returned summary.

## End-to-End Through the Runner

The strongest test exercises the full path: registry loads the manifest, runner imports the script, script runs against a fake API client. This is what `tests/recipes/test_code_evidence_capture.py` does for the `code-evidence-capture` recipe and it’s the regression-test pattern to copy.

Sketch:

    import pytest
    from pretorin.recipes import loader as loader_module
    from pretorin.recipes.loader import clear_cache
    from pretorin.recipes.registry import RecipeRegistry
    from pretorin.recipes.runner import RecipeScriptContext, run_script
    from unittest.mock import AsyncMock, MagicMock

    @pytest.fixture(autouse=True)
    def _isolate(tmp_path, monkeypatch):
        clear_cache()
        monkeypatch.setattr(loader_module, "_user_recipes_root", lambda: tmp_path / "u")
        monkeypatch.setattr(loader_module, "_project_recipes_root", lambda start=None: None)

    @pytest.mark.asyncio
    async def test_my_recipe_end_to_end() -> None:
        registry = RecipeRegistry()
        entry = registry.get("my-recipe")
        assert entry is not None

        api_client = MagicMock()
        api_client.create_evidence = AsyncMock(return_value={"id": "ev-1"})
        ctx = RecipeScriptContext(
            system_id="sys-1",
            framework_id="nist-800-53-r5",
            api_client=api_client,
            logger=MagicMock(),
            recipe_id="my-recipe",
            recipe_version="0.1.0",
            recipe_context_id=None,
        )

        result = await run_script(
            recipe=entry.active,
            script_name="capture",
            ctx=ctx,
            params={"control_id": "ac-2"},
        )

        assert result["evidence_id"] == "ev-1"

For a community recipe outside the pretorin source tree, point the loader at your recipe’s directory:

    monkeypatch.setattr(loader_module, "_user_recipes_root", lambda: my_recipe_parent)

## Run It Locally

Before wiring the recipe into an agent, exercise it directly:

    pretorin recipe run my-recipe --param key=value --param limit=20

`pretorin recipe run` loads the recipe through the registry (or `--path` for a not-yet-registered directory), opens a recipe execution context, calls the script, prints the return value, and closes the context. It bypasses the MCP boundary, so:

- Use it for fast iteration on the script itself.
- Pure transformation recipes (return data, don’t write to the platform) work end-to-end.
- Recipes that do write through `ctx.api_client` need explicit `audit_metadata` on each call — the MCP boundary stamps automatically; this command does not. See Writer tools.

`--no-context` skips opening the execution context for recipes that don’t need it.

## Validate as a Smoke Test

`pretorin recipe validate <id>` runs the manifest schema check, the script existence check, and the description-quality check. Add it to your CI as a shell-out smoke test:

    - name: Validate recipes
      run: |
        pretorin recipe validate my-recipe
        pretorin recipe validate my-other-recipe

This catches “you renamed the script and forgot to update the manifest” faster than any pytest assertion will.

## What Not to Test

- Don’t test the platform API. Your recipe is an adapter; testing what `create_evidence` does is pretorin’s job, not yours. Mock the client.
- Don’t test pydantic validation of the manifest. That’s already covered by pretorin’s loader tests. If your manifest is malformed, `pretorin recipe validate` will tell you.
- Don’t test redaction patterns. Use `pretorin.evidence.redact`’s helpers and trust them.

# Publishing Recipes

Recipes can ship at three tiers. The tier is set by the loader from the recipe’s source path, not by what the manifest declares. Where you put the recipe directory determines who can run it and how.

## Three Distribution Models

### 1. Personal — User Folder

    ~/.pretorin/recipes/<id>/

Drop a recipe directory there and `pretorin recipe list` shows it immediately. Loaded as `tier: community`. No further setup.

Use this when:

- You’re prototyping a recipe.
- The recipe encodes a personal workflow no one else needs.
- You’re working through the 10-minute walkthrough.

The user folder isn’t synced anywhere — it lives on your machine.

### 2. Team — Project Folder

    <repo>/.pretorin/recipes/<id>/

Checked into your team’s compliance repo (the same repo with `pretorin.yaml` or your team’s CLAUDE.md). The loader walks up from CWD looking for a `.pretorin/recipes/` directory, so any teammate working in the repo gets the recipes automatically.

Use this when:

- The recipe encodes a team-specific procedure (e.g., “pull our internal IAM audit log endpoint”).
- You want your CI to validate the recipes alongside the rest of the compliance repo.
- The recipe is too domain-specific to belong upstream but everyone on the team needs it.

Loaded as `tier: community`. The project folder has higher precedence than the user folder, so a team-shared recipe shadows a teammate’s personal copy with the same id.

### 3. Upstream — First-Party in pretorin-cli

    src/pretorin/recipes/_recipes/<id>/

Forced to `tier: official` by the loader. Distributed with every pretorin-cli install.

Use this when:

- The recipe is broadly useful to anyone running pretorin (a new scanner wrapper, a generic capture pattern).
- You’re willing to maintain it — official recipes get tested in CI and block releases on regressions.
- The procedure is stable enough that a SemVer bump with backwards- incompatible changes would be unusual.

## How to Submit a First-Party Recipe

1. Open an issue first. Describe what the recipe does and why it belongs in the upstream set rather than as a community recipe. The first-party set is intentionally small — it’s a curation surface, not a catch-all.

2. Scaffold under `_recipes/` — clone pretorin-cli and create the recipe directory:

       pretorin recipe new my-new-scanner --location builtin

   This drops the recipe under `src/pretorin/recipes/_recipes/<id>/` with the right structure.

3. Set `tier: official` in the manifest. The loader will override from the source path anyway, but it documents intent.

4. Add a smoke test. Every built-in recipe has at least one test under `tests/recipes/` that loads it through the registry and verifies the basic shape. Copy the pattern from `tests/recipes/test_builtin_scanner_recipes.py`.

5. Run quality gates locally:

       pretorin recipe validate my-new-scanner
       pytest tests/recipes/
       ./tools/check.sh quick

6. PR the recipe. Include in the description: what the recipe does, when an agent should pick it (your `use_when` text is a good start), and what scanners or platform APIs it depends on. Reference the issue from step 1.

7. Maintenance commitment. Once merged, the recipe ships with every pretorin-cli release. Be prepared to respond to issues against it.

## How to Share a Community Recipe

There’s no central registry yet (pretorin’s `recipe install <pkg>` is v1.5). For now, share a community recipe by:

- Posting the recipe directory in a gist or repo. Anyone can clone it into their `~/.pretorin/recipes/` or `<repo>/.pretorin/recipes/` and use it.
- Opening a PR against your team’s compliance repo. The recipe lands under `<repo>/.pretorin/recipes/<id>/` and is loaded automatically by every teammate.

When you publish a community recipe, fill out:

- `license` — required for anything you share publicly. SPDX identifier (`Apache-2.0`, `MIT`, etc.).
- `author` — your name or your team’s name. Stamped in the audit metadata of every evidence record the recipe writes.
- `description` — clear enough that someone else’s agent can decide whether to pick it without needing to read your team’s wiki.

## Tier and Trust

The calling agent sees `tier` on every recipe in `pretorin_list_recipes` output. v1 doesn’t gate execution by tier — a community recipe runs just like an official one. What tier does is:

- Audit signal. Every evidence record stamped by a community recipe carries the recipe id, version, and author. A reviewer can trace which recipes contributed to a system’s evidence set.
- Selection signal. When two recipes both fit a task, the agent prefers the official one unless the community one’s `description` makes a stronger case.

`partner` tier is reserved for recipes shipped via installed Python packages (deferred to v1.5). The shape is: a Python package declares an entry point pointing at a recipe directory inside the package; the loader picks it up at install time.

## Versioning Your Recipe

Bump `version` in the manifest when:

- The recipe’s behavior changes in a way that would surprise a previous consumer (different output shape, different side effects, different redaction rules).
- A bug fix changes results materially.

Don’t bump for cosmetic edits to the body or doc-only changes. Recipe version stamps every evidence record, so a version bump means the audit trail reflects “results from a different procedure.”

`recipe_schema_version` is independent — it tracks the frontmatter shape, not the recipe’s behavior. Most recipes pin this to `"1.0"` and never touch it.

# Workflows

Workflows sit one layer above recipes. A workflow is a playbook the calling agent reads to learn how to iterate items in a domain (one control, all pending scope questions, the entire campaign). Recipes describe what to do per item; workflows describe how to walk the items.

Three-layer routing model:

    engagement (deterministic Python rules)
      → workflow (markdown playbook the calling agent reads)
        → recipe (calling agent picks per item from the menu)

The engagement layer (`pretorin_start_task`) lands later — see the roadmap. v1 ships the workflow registry and the per-workflow playbook markdown so the agent can navigate manually.

## What Ships in v1

Four built-in workflows:

ID | Iterates over | Pick when
--- | --- | ---
`single-control` | one control | The user names exactly one control id and the work fits in a single focused pass.
`scope-question` | scope questionnaire items | The user references the scope questionnaire or scope is the active workflow-state blocker.
`policy-question` | policy questionnaire items | The user references an org policy questionnaire or policy is the active blocker.
`campaign` | many controls (server-side) | Bulk control work — drafting narratives or capturing evidence for a family or framework.

Browse them:

    pretorin recipe list  # for recipes (CLI)

There’s no `pretorin workflow list` CLI yet — workflows are discovered through MCP only:

- `pretorin_list_workflows` — summary metadata for every loaded workflow.
- `pretorin_get_workflow(workflow_id)` — full manifest plus the markdown body.

## How a Workflow’s Body Looks

Every workflow body has the same shape: a brief intent statement, a description of the iteration shape, a step-by-step block, and a “what to avoid” closing section. Read one of the built-ins as a template:

    cat src/pretorin/workflows_lib/_workflows/single-control/workflow.md

The frontmatter declares:

Field | Notes
--- | ---
`id` | kebab-case, globally unique
`version` | SemVer-ish
`name` | display name
`description` | ≥ 50 chars, what the engagement layer matches against
`use_when` | ≥ 30 chars, explicit trigger guidance
`produces` | `evidence` / `narrative` / `answers` / `mixed`
`iterates_over` | `single_control` / `scope_questions` / `policy_questions` / `campaign_items`
`recipes_commonly_used` | hint list of recipe ids the agent often picks

## Why Workflows Matter

Without workflows, the calling agent would freelance the iteration pattern for every task. That’s drift-prone — different agents hit the same questionnaire and follow different orders, producing inconsistent audit trails. The workflow body fixes the pattern: load pending items, filter, iterate, pick a recipe per item, submit through the audit boundary, optionally trigger review.

`recipes_commonly_used` is a hint, not a binding. The agent reads `pretorin_list_recipes` at runtime and picks per-item by matching `use_when` strings, then falls back to freelance only when no recipe fits.

## Server-Side vs Calling-Agent Iteration

Three of the four workflows are calling-agent iteration: the agent loops items in its own context window, calling MCP tools per item. Fine for small sets (one control, ~50 questionnaire items).

`campaign` is server-side iteration: pretorin’s own CodexAgent walks items inside pretorin, calling the same recipe surface. The calling agent kicks off the campaign and observes status — it doesn’t iterate items in its own context. This is what makes thousand-control campaigns tractable without overwhelming the calling agent’s context window.

## Authoring a New Workflow

v1 doesn’t ship a workflow scaffolder — workflows are first-party only. If you need a new iteration shape, open an issue describing:

- What domain it iterates (controls? questions? something else?).
- Why the existing four don’t fit.
- The recipes the workflow would commonly use.

Community workflows land in v1.5 once the engagement layer is in place and the routing rules can include community contributions safely.

## What’s Already Wired

- Engagement layer (`pretorin_start_task`) — picks the workflow from the user’s prompt entities. See Engagement Layer.
- Recipe selection in the campaign hot site — `draft_control_artifacts` consults `pretorin.recipes.selection.select_recipe_for_drafting` before falling through to freelance prose. The decision is recorded as a `RecipeSelection` record on the response and written to the audit trail. v1’s built-in recipes don’t cover narrative drafting, so the current path is “log selection → freelance”; community recipes that attest the matching `(control, framework)` plug in automatically.

## Roadmap

- In-process recipe execution — when the selector finds a recipe match, dispatch to `run_script` in-process instead of falling through to freelance. v1.5; v1 records the would-have-picked decision so the audit trail is forward-compatible.
- Community workflows — third loader path, scaffolder, validator. v1.5.

# Engagement Layer

The engagement layer is pretorin’s routing boundary. When the user says “draft AC-2 for system X” or “answer my scope questionnaire” or “work through the AC family”, the calling agent’s first move is `pretorin_start_task`. Pretorin picks the workflow; the agent then loads the workflow body and follows it.

This is the third layer in the routing model:

    engagement      ← pretorin_start_task (deterministic Python rules)
      workflow      ← pretorin_get_workflow(selected) → markdown playbook
        recipe      ← pretorin_list_recipes / pretorin_start_recipe

Recipes are the leaf — what to do per item. Workflows are the trunk — how to iterate items. Engagement is the root — what kind of work we’re doing in the first place.

## Why a Routing Layer

Without engagement, the calling agent guesses. Pattern-matching on nouns sends the agent into evidence/narrative write tools the moment the user says “AC-2”, which produces wrong-framework writes when the user wasn’t explicit and silently-cross-system writes when the active context shifted. The audit chain breaks.

The engagement layer fixes this with deterministic rules that run in pretorin (no LLM here). The calling agent extracts entities; the rules pick a workflow; the response carries the platform read-state the workflow needs. One round-trip, one routing decision, no drift.

## What `pretorin_start_task` Does

Three things:

1. Validates the entities the calling agent extracted. Hallucinated control ids, unknown frameworks, unresolvable system names — these fail loud as MCP errors. The calling agent surfaces the error and asks the user to clarify.
2. Cross-checks coherence. If the named control exists in some framework but not the one the user named, that’s an ambiguity. If the resolved system doesn’t match the active CLI context, that’s ambiguity too. The response is `ambiguous: true` with a reason — the calling agent surfaces it before any writes.
3. Picks a workflow. The rule cascade (in priority order):
   - `intent_verb == "inspect_status"` → no workflow, just inspect output.
   - `intent_verb == "campaign"` → campaign.
   - scope question ids OR (intent=answer AND pending scope) → scope-question.
   - policy question ids OR (intent=answer AND pending policy) → policy-question.
   - exactly one control id → single-control.
   - multiple control ids → campaign with control filter.
   - framework set, no controls → campaign over the framework.
   - else → ambiguous; ask the user.

The `rule_matched` field on the response records which rule fired so the audit trail captures the routing decision.

## Entity Shape

The calling agent’s LLM extracts:

    {
        "intent_verb": "work_on" | "collect_evidence" | "draft_narrative"
                     | "answer" | "campaign" | "inspect_status",
        "raw_prompt": "<user's verbatim prompt>",
        "system_id": "<id or name, or None>",
        "framework_id": "<id, or None>",
        "control_ids": ["ac-2", "ac-3"],
        "scope_question_ids": [],
        "policy_question_ids": [],
    }

`raw_prompt` is audit-only — pretorin doesn’t parse it. Everything else either resolves cleanly or fails the cross-check.

## Inspect Bundle

When the call succeeds, the response carries a bundled snapshot of the platform state the workflow will need:

- `workflow_state` (per-system stage rollup)
- `compliance_status` (overall posture)
- `pending_families` (controls that still need work)
- `pending_scope_questions` / `pending_policy_questions`
- `org_policies`

The calling agent doesn’t have to issue separate reads for each — one round-trip yields the routing decision plus the context the workflow will reference.

Inspect is best-effort: if any one platform call fails, that section carries an `error` field but the rest of the payload still populates. Pass `skip_inspect: true` when you already have fresh state.

## Three Response Shapes

1. MCP error — entity validation or cross-check hard failure. The calling agent shows the error and stops.
2. `selected_workflow` set, `ambiguous: false` — routed. Calling agent reads the workflow body and follows it.
3. `ambiguous: true` with `ambiguity_reason` — coherence problem. Calling agent surfaces the reason to the user, gets clarification, and retries with disambiguated entities.

There’s no fourth shape. The router never produces a confidence score or alternatives — the rule either matched or didn’t.

## Active System Context

Pass `active_system_id` (the user’s CLI context system) so the cross- check catches cross-system writes. When the resolved system doesn’t match, the response is ambiguous regardless of what the rules would say. This is the small extra friction that eliminates the silent wrong-system-write class of error.

## Where the Code Lives

- `src/pretorin/engagement/entities.py` — `EngagementEntities` pydantic model.
- `src/pretorin/engagement/selection.py` — `EngagementSelection` response model.
- `src/pretorin/engagement/rules.py` — pure-function rule cascade.
- `src/pretorin/engagement/cross_check.py` — platform-state coherence checks.
- `src/pretorin/engagement/inspect.py` — bundles platform reads into the response.
- `src/pretorin/mcp/handlers/engagement.py` — the `pretorin_start_task` MCP handler.

The rule cascade is testable in pure isolation — same inputs always produce the same output. Drift impossible by construction.

# Worked Example: a Community Recipe

This walkthrough builds a community recipe end-to-end, the same way you would. The recipe captures the most recent N entries from a structured audit log file as evidence for an access-control review.

The recipe doesn’t ship with pretorin-cli — it’s a teaching artifact. Drop it under `~/.pretorin/recipes/audit-log-capture/` to actually run it; the files are reproduced below for reference.

## What This Recipe Does

The audit team needs evidence that an admin’s recent actions are being logged. They have a structured log file (one JSON event per line). The recipe:

1. Reads the last N entries from the log.
2. Filters to events matching a username.
3. Composes a markdown evidence body with the events as a code block.
4. Returns the composed text to the calling agent so the agent can hand it to `pretorin_create_evidence`.

The recipe doesn’t write evidence itself — it returns structured data the agent submits through the MCP write boundary. That’s where audit metadata gets stamped automatically.

## Directory Layout

    ~/.pretorin/recipes/audit-log-capture/
    ├── recipe.md
    ├── README.md
    └── scripts/
        └── capture.py

## `recipe.md`

    ---
    id: audit-log-capture
    version: 0.1.0
    name: "Audit Log Capture"
    description: "Capture the most recent admin events from a JSONL audit log and return a formatted markdown body for evidence submission."
    use_when: "The auditor needs evidence that admin actions are logged. You have a JSONL audit log file path and a username to filter on."
    produces: evidence
    author: "Example Team"
    license: Apache-2.0
    attests:
      - { control: AU-2, framework: nist-800-53-r5 }
      - { control: AU-3, framework: nist-800-53-r5 }
    params:
      log_path:
        type: string
        description: "Absolute path to the JSONL audit log file"
        required: true
      username:
        type: string
        description: "Admin username to filter events for"
        required: true
      limit:
        type: integer
        description: "Maximum number of events to include"
        default: 20
    scripts:
      capture:
        path: scripts/capture.py
        description: "Read the audit log, filter by username, return composed markdown."
        params:
          log_path:
            type: string
            description: "Absolute path to JSONL log"
            required: true
          username:
            type: string
            description: "Admin username"
            required: true
          limit:
            type: integer
            description: "Max events"
            default: 20
    ---

    # Audit Log Capture

    Reads the tail of a JSONL audit log, filters to events for one admin
    user, and returns a composed markdown body the calling agent can submit
    as a configuration evidence record.

    The agent should attach the result to the relevant `AU-2` / `AU-3`
    implementation narrative for the system.

## `scripts/capture.py`

    """Capture recent audit events for a specific admin user."""

    from __future__ import annotations

    import json
    from pathlib import Path
    from typing import Any

    from pretorin.evidence.markdown import compose

    async def run(
        ctx: Any,
        *,
        log_path: str,
        username: str,
        limit: int = 20,
    ) -> dict[str, Any]:
        """Read tail of JSONL log, filter to one user, compose evidence body."""
        path = Path(log_path)
        if not path.is_file():
            raise FileNotFoundError(f"audit log not found: {log_path}")

        matched: list[dict[str, Any]] = []
        for raw in path.read_text(encoding="utf-8").splitlines():
            if not raw.strip():
                continue
            try:
                event = json.loads(raw)
            except json.JSONDecodeError:
                continue
            if event.get("user") == username:
                matched.append(event)
            if len(matched) >= limit:
                break

        snippet = "\n".join(json.dumps(e, sort_keys=True) for e in matched)
        body = compose(
            prose=(
                f"The {len(matched)} most recent audit events for user "
                f"{username!r} from {path.name}. Each line is one structured "
                "event (timestamp, action, target, source IP)."
            ),
            snippet=snippet,
            snippet_lang="json",
            file_path=str(path),
        )
        return {
            "username": username,
            "event_count": len(matched),
            "evidence_body": body,
        }

## `README.md`

    # audit-log-capture

    A community recipe that captures admin audit events as markdown evidence.

    ## Try it

    1. Drop this directory under ~/.pretorin/recipes/audit-log-capture/
    2. Run `pretorin recipe validate audit-log-capture`
    3. From your AI agent (Claude Code, Codex CLI), ask:
       > "Capture the last 20 audit events for admin alice from /var/log/audit.jsonl
       > as evidence for AU-2."

## Walking Through It

### Why split into manifest + script

Everything inside the frontmatter is what the calling agent reads to decide whether to use the recipe. Everything in `scripts/capture.py` is how the work happens. Keep the description sharp — the agent picks based on it.

### Why use `compose` from `pretorin.evidence.markdown`

`compose` produces an audit-grade markdown body: prose explaining the context, a fenced code block with the snippet, an italic provenance footer with the file path and timestamp. Doing this by hand drifts; using the helper keeps every evidence record consistent.

### Why no `create_evidence` call

The recipe returns the composed body and lets the calling agent submit it via `pretorin_create_evidence`. This is the right shape for two reasons:

1. Audit metadata gets stamped at the MCP write boundary. The recipe context is open in the calling agent’s session; routing through MCP means the handler reads the context and stamps `producer_kind="recipe"` automatically.
2. The agent stays in the loop. The agent can review the composed body, ask the user to confirm before submitting, or attach extra metadata it pulled from elsewhere.

A recipe that takes raw input and produces structured output the agent hands to a writer tool is the most useful shape. Recipes that perform their own writes are sometimes appropriate (the scanner recipes do, via `submit_test_results`) but the default should be: return data, let the agent submit.

### How the agent invokes it

The MCP tool name is `pretorin_recipe_audit_log_capture__capture` (the hyphens in the recipe id become underscores; the script name is the suffix after `__`).

The agent’s call sequence:

    pretorin_start_recipe(id="audit-log-capture", version="0.1.0",
                          params={"log_path": "/var/log/audit.jsonl",
                                  "username": "alice", "limit": 20})
      → returns context_id

    pretorin_recipe_audit_log_capture__capture(
        log_path="/var/log/audit.jsonl",
        username="alice",
        limit=20,
    )
      → returns {"username": "alice", "event_count": 20, "evidence_body": "..."}

    pretorin_create_evidence(
        system_id="...",
        control_id="au-2",
        framework_id="nist-800-53-r5",
        name="Recent admin audit events for alice",
        evidence_type="configuration",
        description=<the evidence_body from the previous call>,
        recipe_context_id=<context_id>,
    )
      → platform stamps producer_kind="recipe", producer_id="audit-log-capture",
                        producer_version="0.1.0"

    pretorin_end_recipe(context_id=<context_id>, status="pass")

### What the agent should not do

The recipe doesn’t include a `submit` script that calls `create_evidence` directly. Why: every evidence record submitted from inside the recipe context picks up audit metadata at the MCP boundary; moving the write into the script would require the script to build metadata itself, which is one more place for the audit trail to drift.

When in doubt, return data, let the agent write.

# Supported Frameworks

Pretorin provides access to 30+ compliance frameworks and profiles spanning federal, contractor, defense industrial base, and related compliance requirements.

## Representative Frameworks

The table below highlights a representative subset of commonly used frameworks in Pretorin. Always call `pretorin frameworks list` to get the current catalog from the API for your environment.

ID | Title | Version | Tier | Families | Controls
--- | --- | --- | --- | --- | ---
`nist-800-53-r5` | NIST SP 800-53 Rev 5 | 5.2.0 | foundational | 20 | 324
`nist-800-171-r3` | NIST SP 800-171 Revision 3 | 1.0.0 | foundational | 17 | 130
`fedramp-low` | FedRAMP Rev 5 Low Baseline | fedramp2.1.0 | operational | 18 | 135
`fedramp-moderate` | FedRAMP Rev 5 Moderate Baseline | fedramp2.1.0 | operational | 18 | 181
`fedramp-high` | FedRAMP Rev 5 High Baseline | fedramp2.1.0 | operational | 18 | 191
`cmmc-l1` | CMMC 2.0 Level 1 (Foundational) | 2.0 | operational | 6 | 17
`cmmc-l2` | CMMC 2.0 Level 2 (Advanced) | 2.0 | operational | 14 | 110
`cmmc-l3` | CMMC 2.0 Level 3 (Expert) | 2.0 | strategic | 10 | 24

### Framework Tiers

Each framework has a tier classification displayed in the `pretorin frameworks list` output:

Tier | Description
--- | ---
foundational | Base catalogs that other frameworks derive from (e.g., NIST 800-53, 800-171)
operational | Baselines and profiles used for day-to-day compliance (e.g., FedRAMP, CMMC L1/L2)
strategic | Advanced or specialized frameworks for high-assurance environments (e.g., CMMC L3)

## Framework Relationships

Understanding how frameworks relate helps with cross-compliance:

    NIST 800-53 Rev 5 (full catalog, 324 controls)
    ├── FedRAMP Low/Moderate/High (800-53 subset + cloud requirements)
    ├── NIST 800-171 Rev 3 (800-53 subset for CUI in non-federal systems)
    │   └── CMMC Level 2 (maps to 800-171 requirements)
    └── CMMC Level 3 (advanced controls beyond 800-171)

If an organization is already compliant with a parent framework, many child framework controls are already satisfied.

## NIST SP 800-53 Rev 5

The foundational catalog for federal information systems. Contains 324 controls across 20 families covering all aspects of information security. All other US government frameworks derive from it.

Target audience: Federal agencies

## NIST SP 800-171 Rev 3

Protects Controlled Unclassified Information (CUI) in non-federal systems. A focused subset of 800-53 with 130 requirements.

Target audience: Federal contractors, universities, and other non-federal entities handling CUI under DFARS 252.204-7012 or similar requirements.

## FedRAMP

Based on NIST 800-53 with additional cloud-specific requirements. Required for cloud services used by federal agencies.

Impact levels:

Level | ID | Controls | Use When
--- | --- | --- | ---
Low | `fedramp-low` | 135 | Public, non-sensitive data. Limited adverse effect from loss.
Moderate | `fedramp-moderate` | 181 | CUI, PII, sensitive data. Serious adverse effect from loss. Most common level.
High | `fedramp-high` | 191 | Life-safety, financial, law enforcement data. Severe/catastrophic effect from loss.

Target audience: Cloud service providers to government

## CMMC 2.0

Cybersecurity Maturity Model Certification for defense contractors. Required by DoD contracts.

Level | ID | Controls | Use When
--- | --- | --- | ---
Level 1 | `cmmc-l1` | 17 | Handles only Federal Contract Information (FCI). Basic cyber hygiene.
Level 2 | `cmmc-l2` | 110 | Handles CUI. Aligns with NIST 800-171. Most defense contractors need this.
Level 3 | `cmmc-l3` | 24 | Highest sensitivity CUI. Advanced practices on top of Level 2.

Target audience: Defense industrial base organizations

> Note: CMMC Level 3 controls are in addition to Level 2. An organization at Level 3 must also satisfy all Level 2 controls.

## Custom and Forked Frameworks

If your organization needs to track a framework that isn’t in the built-in catalog (e.g., an internal control set, a tailored ISO/SOC 2 mapping, an industry-specific regulation), you can author one yourself or fork an existing Pretorin-managed framework. The `pretorin frameworks` group exposes the full revision lifecycle:

- Author from scratch — `init-custom`, `validate-custom`, `upload-custom`
- Convert from OSCAL or 12 known custom catalog shapes — `build-custom`
- Fork an existing framework — `fork-framework`, `rebase-fork`
- Inspect drafts — `revisions`
- Round-trip back to OSCAL — `export-oscal`

See the Custom Frameworks guide for the end-to-end workflow.

See Framework Selection Guide for help choosing the right framework.

# Control ID Formats

Correct ID formatting is critical. The Pretorin API returns errors on malformed IDs. When unsure, discover IDs first with `pretorin frameworks families <id>` or `pretorin frameworks controls <id>`.

## NIST 800-53 Rev 5 / FedRAMP

Framework IDs: `nist-800-53-r5`, `fedramp-low`, `fedramp-moderate`, `fedramp-high`

### Family IDs

Family IDs are lowercase slugs, not short codes:

Correct | Incorrect
--- | ---
`access-control` | `ac`
`audit-and-accountability` | `au`
`identification-and-authentication` | `ia`
`system-and-communications-protection` | `sc`
`configuration-management` | `cm`
`incident-response` | `ir`
`risk-assessment` | `ra`

### Control IDs

Control IDs are zero-padded with a hyphen:

Correct | Incorrect
--- | ---
`ac-01` | `ac-1`, `AC-1`, `ac1`
`ac-02` | `ac-2`, `AC-2`, `ac2`
`au-02` | `au-2`, `AU-2`
`sc-07` | `sc-7`, `SC-7`

Enhancement IDs append a dot-suffix or parenthetical suffix:

Format | Example
--- | ---
Dot notation | `ac-02.1`
Parenthetical | `ac-02(1)`

## CMMC 2.0

Framework IDs: `cmmc-l1`, `cmmc-l2`, `cmmc-l3`

### Family IDs

CMMC family IDs include a level suffix:

Correct | Incorrect
--- | ---
`access-control-level-1` | `access-control`, `ac`
`access-control-level-2` | `access-control`, `ac-l2`
`incident-response-level-2` | `incident-response`, `ir`
`system-and-communications-protection-level-3` | `sc`, `sc-l3`

### Control IDs

CMMC control IDs use dotted notation with a level prefix and are case-sensitive:

Correct | Incorrect
--- | ---
`AC.L2-3.1.1` | `ac-01`, `3.1.1`
`SC.L3-3.13.2` | `sc-07`, `3.13.2`
`AC.L1-3.1.22` | `ac.l1-3.1.22`

Use uppercase for the family prefix (e.g., `AC`, not `ac`).

## NIST 800-171 Rev 3

Framework ID: `nist-800-171-r3`

### Family IDs

Family IDs use the same lowercase slug convention as NIST 800-53:

Correct | Incorrect
--- | ---
`access-control` | `ac`, `3.1`
`incident-response` | `ir`, `3.6`
`identification-and-authentication` | `ia`, `3.5`

### Control IDs

Control IDs use dotted notation with leading zeros:

Correct | Incorrect
--- | ---
`03.01.01` | `3.1.1`, `ac-01`
`03.01.02` | `3.1.2`, `ac-02`
`03.13.01` | `3.13.1`, `sc-01`

## Auto-Normalization

The CLI and MCP tools automatically normalize NIST 800-53 and FedRAMP control IDs: uppercase is lowered and single-digit numbers are zero-padded. For example, `AC-2` becomes `ac-02` and `SC-7.1` becomes `sc-07.1`. CMMC and NIST 800-171 IDs are passed through unchanged — use the exact format shown above.

## Discovery Workflow

When a user provides an informal control reference (e.g., “AC-2” or “access control”):

1. Call `pretorin frameworks families <framework_id>` to find the correct family slug
2. Call `pretorin frameworks controls <framework_id> --family <family_slug>` to find the correct control ID
3. Use the discovered ID in subsequent calls

## Quick Reference

Framework | Family Format | Control Format | Example
--- | --- | --- | ---
NIST 800-53 | `access-control` | `ac-01` | `pretorin frameworks control nist-800-53-r5 ac-02`
FedRAMP | `access-control` | `ac-01` | `pretorin frameworks control fedramp-moderate ac-02`
CMMC | `access-control-level-2` | `AC.L2-3.1.1` | `pretorin frameworks control cmmc-l2 AC.L2-3.1.1`
800-171 | `access-control` | `03.01.01` | `pretorin frameworks control nist-800-171-r3 03.01.01`

# Framework Selection Guide

Use this decision tree to identify the right compliance framework for your situation.

## Decision Tree

### 1. Federal Agency (US Government)

Use: NIST 800-53 Rev 5 (`nist-800-53-r5`)

The foundational catalog for federal information systems. All other US government frameworks derive from it. Contains 324 controls across 20 families. Use this when the organization IS a federal agency and needs the full control catalog.

### 2. Federal Contractor Handling CUI

Use: NIST 800-171 Rev 3 (`nist-800-171-r3`)

Protects Controlled Unclassified Information (CUI) in non-federal systems. A focused subset of 800-53 with 130 requirements. Use this when the organization is a contractor, university, or other non-federal entity that handles CUI under DFARS 252.204-7012 or similar requirements.

### 3. Cloud Service Provider to Government

Use: FedRAMP (`fedramp-low`, `fedramp-moderate`, `fedramp-high`)

Based on NIST 800-53 with additional cloud-specific requirements. Required for cloud services used by federal agencies.

Level | ID | Controls | Use When
--- | --- | --- | ---
Low | `fedramp-low` | 135 | Public, non-sensitive data. Loss would have limited adverse effect.
Moderate | `fedramp-moderate` | 181 | CUI, PII, sensitive but not critical data. Loss would have serious adverse effect. Most common level.
High | `fedramp-high` | 191 | Life-safety, financial, law enforcement, or emergency services data. Loss would have severe or catastrophic effect.

When unsure, FedRAMP Moderate is the most common starting point for cloud services handling government data.

### 4. Defense Industrial Base (DIB)

Use: CMMC (`cmmc-l1`, `cmmc-l2`, `cmmc-l3`)

Cybersecurity Maturity Model Certification for defense contractors. Required by DoD contracts.

Level | ID | Controls | Use When
--- | --- | --- | ---
Level 1 | `cmmc-l1` | 17 | Handles only Federal Contract Information (FCI). Basic cyber hygiene.
Level 2 | `cmmc-l2` | 110 | Handles CUI. Aligns with NIST 800-171. Most defense contractors need this.
Level 3 | `cmmc-l3` | 24 | Highest sensitivity CUI. Advanced/progressive practices on top of Level 2.

Note: CMMC Level 3 controls are in addition to Level 2.

### 5. None of the above fits — bring your own framework

If the built-in catalog doesn’t cover your obligation (e.g., an internal control set, a tailored mapping, an industry-specific regulation), you can author a custom framework or fork an existing one. See the Custom Frameworks guide for the `pretorin frameworks init-custom` / `build-custom` / `upload-custom` / `fork-framework` workflow.

## Quick Reference

Situation | Framework | ID
--- | --- | ---
We’re a federal agency | NIST 800-53 | `nist-800-53-r5`
We handle CUI as a contractor | NIST 800-171 | `nist-800-171-r3`
We’re a cloud service for government | FedRAMP | `fedramp-moderate`
We have a DoD contract | CMMC | `cmmc-l2`
We need to handle both CUI and cloud | FedRAMP + 800-171 | Start with `fedramp-moderate`
We’re not sure yet | Start with NIST 800-53 | `nist-800-53-r5`
Our framework isn’t in the catalog | Author or fork your own | See Custom Frameworks

## Using AI Context for Selection

Call `pretorin_get_framework` (MCP) or `pretorin frameworks get <id>` (CLI) to get AI context including purpose, target audience, regulatory context, scope, and key concepts. This helps confirm whether a framework is the right fit.

# Custom Frameworks

The `pretorin frameworks` command group supports authoring, validating, and uploading custom and forked compliance frameworks through the platform’s revision-lifecycle endpoints.

The canonical artifact is `unified.json`. `_index.json` is not an upload format — it’s an internal index file used elsewhere in the data pipeline, but the platform’s revision lifecycle expects the full `unified.json`.

## End-to-end workflow

    init-custom ──► (edit) ──► validate-custom ──► upload-custom [--publish] ──► revisions
                                                              │
    build-custom ◄────── (OSCAL or custom catalog as input) ──┘

## Starting from scratch

Scaffold a minimal valid `unified.json`:

    pretorin frameworks init-custom acme-soc2-tailored
    # → writes unified.json with one sample family + one sample control

Edit the file, fill in your metadata, families, and controls, then run a local pre-flight check:

    pretorin frameworks validate-custom unified.json

The bundled JSON Schema validator catches structural errors fast. The platform runs the authoritative validator on upload — additional issues may surface there.

## Starting from OSCAL

Already have an OSCAL catalog? Convert it to `unified.json`:

    pretorin frameworks build-custom my-oscal-catalog.json -f acme-iso27001 -o unified.json

The CLI auto-detects the input shape. OSCAL → unified preserves the `_oscal` blocks for lossless regeneration; you can round-trip back via `export-oscal`.

## Starting from a custom catalog

Many compliance catalogs ship in custom (non-OSCAL) JSON shapes — `control_families + controls`, CIS-style nested safeguards, ISO `control_themes`, PCI-DSS, CSA-CCM domains, NIST AI RMF governance requirements, FIPS 140-3, DISA STIG wrappers, MITRE ATLAS, and more.

`build-custom` recognizes 12 known custom shapes and normalizes them all to `unified.json`:

    pretorin frameworks build-custom catalog.json -f acme-soc2 -o unified.json

If the input shape isn’t recognized, the CLI tells you which shapes are supported. Use `init-custom` to scaffold instead and copy your data over manually.

## Uploading

Upload a draft revision to the platform:

    pretorin frameworks upload-custom unified.json

The platform validates synchronously. On a validation failure (HTTP 400) the CLI renders the platform’s structured `validation_report` as a readable table — you’ll see exact paths and messages for what to fix.

To upload and publish in one step:

    pretorin frameworks upload-custom unified.json --publish

You can override the framework ID baked into the artifact and add a version label:

    pretorin frameworks upload-custom unified.json -f acme-soc2-v2 -v "2026-Q1"

## Linked forks

Fork a Pretorin-managed framework into your own draft. The platform records the lineage so you can rebase later when upstream advances.

    pretorin frameworks fork-framework nist-800-53-r5 acme-nist-tailored
    pretorin frameworks fork-framework fedramp-moderate acme-fedramp-mod -v initial

The fork starts as a draft anchored on the upstream’s current revision. Edit the resulting unified.json (export it from the platform UI or use `revisions` to find the draft), then re-upload via `upload-custom`.

### Rebasing a fork

When upstream has advanced and you want to bring your fork forward:

    pretorin frameworks rebase-fork acme-nist-tailored

The platform creates a fresh draft anchored on the latest upstream. You resolve any divergence locally and re-upload.

## Listing revisions

See all drafts and published revisions for a framework:

    pretorin frameworks revisions acme-nist-tailored

## Exporting OSCAL

Regenerate an OSCAL catalog from a unified artifact:

    pretorin frameworks export-oscal unified.json -o catalog.json

When the unified artifact retains the `_oscal` blocks (i.e., it was originally converted from OSCAL via `build-custom`), regeneration is lossless — props, parts, links, and back-matter are restored verbatim.

## Command reference

Command | Talks to platform? | Purpose
--- | --- | ---
`init-custom <id> [-t title] [-o path] [--force]` | No | Scaffold a minimal valid `unified.json`
`validate-custom <path>` | No | Local JSON Schema pre-flight
`build-custom <input> -f <id> [-o path] [--force]` | No | Normalize OSCAL / custom catalog → `unified.json`
`upload-custom <path> [-f id] [-v label] [--publish]` | Yes | Upload draft revision; optionally publish
`fork-framework <upstream_id> <new_id> [-v label]` | Yes | Create linked-fork draft
`rebase-fork <id> [-v label]` | Yes | Create rebase draft against latest upstream
`revisions <id>` | Yes | List drafts + published revisions
`export-oscal <path> [-o path] [--force]` | No | Regenerate OSCAL catalog from `unified.json`

All commands respect the global `--json` flag for machine-readable output.

## Notes for tool authors

The vendored conversion and validation primitives are exposed at `pretorin.frameworks` and can be imported directly:

    from pretorin.frameworks import (
        custom_to_unified,
        oscal_to_unified,
        unified_to_oscal,
    )
    from pretorin.frameworks.validate import validate_unified
    from pretorin.frameworks.templates import minimal_unified

These are pure-data functions — no I/O, no platform calls. Good for embedding in CI pipelines, agent tools, or your own automation.

# Narrative & Evidence Workflow

This is the core workflow for updating control implementations on the platform. Follow this sequence for any control update.

## Workflow Steps

### 1. Resolve the Target

Identify the `system_id`, `control_id`, and `framework_id` for your update. Set the active context:

    pretorin context set --system "My Application" --framework fedramp-moderate

### 2. Read Current State

Before making changes, understand what’s already there:

    # Get full control context (requirements + current implementation)
    pretorin control context ac-02 --framework-id fedramp-moderate
    # Via MCP: pretorin_get_control_context

    # Get current narrative
    pretorin narrative get ac-02 fedramp-moderate

    # Search existing evidence
    pretorin evidence search --control-id ac-02 --framework-id fedramp-moderate

    # List existing notes
    pretorin notes list ac-02 fedramp-moderate

### 3. Collect Observable Facts

Search your codebase and connected systems for evidence. Only document what is directly observable — never assume or fabricate implementation details.

Treat existing Pretorin narratives, notes, and status fields as a starting point, not proof that a control gap exists. Before writing a narrative update or gap note, inspect the relevant implementation in the workspace and connected systems. If those sources show stronger implementation than the current platform record, update the narrative to reflect the observed implementation and record any remaining evidence gap separately.

### 4. Draft Updates

Prepare three types of updates:

Narrative — How the control is implemented. Include TODO placeholders for unknowns:

    [[PRETORIN_TODO]]
    missing_item: SSO configuration details
    reason: Not observable from current workspace and connected MCP systems
    required_manual_action: Export IdP SAML configuration
    suggested_evidence_type: configuration
    [[/PRETORIN_TODO]]

Evidence — Specific artifacts demonstrating implementation (config files, code, policies).

Gap Notes — Unresolved items requiring manual follow-up:

    Gap: Missing MFA enforcement evidence
    Observed: TOTP library imported in auth module
    Missing: MFA policy enforcement configuration
    Why missing: IdP admin console not accessible via codebase
    Manual next step: Screenshot MFA policy from Azure AD admin portal

### 5. Push Updates

    # Push a single narrative file
    pretorin narrative push-file ac-02 fedramp-moderate "My Application" narrative-ac02.md

    # Upsert evidence (finds or creates, then links)
    pretorin evidence upsert ac-02 fedramp-moderate \
      --name "RBAC Configuration" \
      --description "Role mapping in IdP" \
      --type configuration

    # Add gap notes
    pretorin notes add ac-02 fedramp-moderate \
      --content "Gap: Missing MFA evidence..."

    # Update implementation status
    pretorin control status ac-02 partially_implemented \
      --framework-id fedramp-moderate

Status values: `implemented`, `partially_implemented`, `planned`, `in_progress`, `ready_to_approve`, `not_started`, `not_applicable`, `inherited`.

## Read-Only Draft Workflow

When you want AI drafts before any platform writes:

1. Resolve scope (system, control, framework)
2. Read current state (context, narrative, evidence, notes)
3. Generate drafts via `pretorin agent run --skill narrative-generation` or the MCP `pretorin_generate_control_artifacts` tool
4. Review the draft — clearly separate candidate narrative, evidence gaps, and manual follow-up actions
5. Only push to the platform after explicit approval

## Markdown Quality Rules

All narratives and evidence must pass markdown quality validation:

### Narratives

- No markdown headings (`#`, `##`, etc.)
- At least 2 rich markdown elements (code blocks, tables, lists, links)
- At least 1 structural element (code block, table, or list)
- No markdown images

### Evidence

- No markdown headings
- At least 1 rich markdown element
- No markdown images

## Evidence Deduplication

`pretorin evidence upsert` and the MCP `pretorin_create_evidence` tool use find-or-create logic by default (`dedupe: true`):

1. Search for an exact match on (name + description + type + control + framework) within the active system scope
2. If found, reuse the existing evidence item
3. If not found, create a new one
4. Ensure the evidence is linked to the specified control

The response indicates whether the evidence was `created` (new) or reused, along with the `match_basis`.

# Campaign Workflows

Campaigns are the recommended way to run bulk compliance operations across multiple controls, policies, or scope questions. They replace manual one-at-a-time updates with a coordinated prepare-claim-propose-apply lifecycle.

## When to Use Campaigns

- Initial control implementation — Draft narratives and evidence for an entire control family
- Fixing review findings — Address issues flagged by family or policy reviews
- Answering questionnaires — Bulk-answer policy or scope questions
- Notes remediation — Fix controls flagged by platform notes

## Campaign Lifecycle

    Prepare → Claim → Draft → Propose → Apply

1. Prepare — Snapshot platform state and create a checkpoint file. This captures the current state of all target items so the campaign works from a consistent baseline.

2. Claim — Lease items for drafting. TTL-based leases prevent concurrent editing when multiple agents are working in parallel.

3. Draft — For each claimed item, get full context (control requirements, current state, guidance) and produce a draft.

4. Propose — Submit drafts as proposals without writing to the platform. This provides a review opportunity before any changes are persisted.

5. Apply — Push all accepted proposals to the platform as a single operation.

## External Agent Pattern

Campaigns are designed for external agents (Claude Code, Codex, Cursor, etc.) operating through MCP:

    Agent A: prepare_campaign → claim_campaign_items → get_campaign_item_context → submit_campaign_proposal
    Agent B: claim_campaign_items → get_campaign_item_context → submit_campaign_proposal
    ...
    Coordinator: get_campaign_status → apply_campaign

The checkpoint file enables independent agent execution. Agents can claim non-overlapping items and work in parallel.

## CLI Usage

### Control Campaign

    # Draft narratives for the Access Control family
    pretorin campaign controls --mode initial --family AC \
      --system "My System" --framework-id fedramp-moderate

    # Fix controls flagged by notes
    pretorin campaign controls --mode notes-fix --family AC \
      --system "My System" --framework-id fedramp-moderate

    # Fix controls flagged by review
    pretorin campaign controls --mode review-fix --family AC --review-job <job-id> \
      --system "My System" --framework-id fedramp-moderate

    # Auto-apply after completion
    pretorin campaign controls --mode initial --family AC --apply \
      --system "My System" --framework-id fedramp-moderate

### Policy Campaign

    # Answer all incomplete policy questions
    pretorin campaign policy --mode answer --all-incomplete

    # Fix review findings for a specific policy
    pretorin campaign policy --mode review-fix --policies <policy-id>

### Scope Campaign

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

### Check Status

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

## MCP Tool Sequence

For AI agents working through MCP:

1. `pretorin_get_workflow_state` — Understand what needs work
2. `pretorin_get_pending_families` — Identify target families
3. `pretorin_prepare_campaign` — Create the campaign
4. `pretorin_claim_campaign_items` — Claim items
5. `pretorin_get_campaign_item_context` — Get context per item
6. `pretorin_submit_campaign_proposal` — Submit drafts
7. `pretorin_get_campaign_status` — Review progress
8. `pretorin_apply_campaign` — Push to platform

# Policy & Scope Questionnaires

Pretorin uses questionnaire workflows to capture organizational policy information and system scope details. Both follow a similar lifecycle: answer questions, generate documents, review, and iterate.

## Policy Questionnaire Workflow

Organization policies (e.g., Access Control Policy, Incident Response Policy) are defined at the org level and apply across systems.

### 1. List Available Policies

    pretorin policy list

Or via MCP: `pretorin_list_org_policies`

### 2. View Current State

    # Show questionnaire state and saved review findings
    pretorin policy show --policy <policy-id-or-name>

Or via MCP:

    pretorin_get_pending_policy_questions  # lightweight — only unanswered
    pretorin_get_policy_question_detail    # guidance and examples per question

### 3. Answer Questions

Via CLI — Draft answers from your workspace:

    # Preview proposed answers
    pretorin policy populate --policy <policy-id>

    # Apply answers to the platform
    pretorin policy populate --policy <policy-id> --apply

Via MCP — Answer individually for precise control:

    pretorin_answer_policy_question(policy_id, question_id, answer)

Or batch-update multiple answers:

    pretorin_patch_org_policy_qa(policy_id, updates=[{question_id, answer}, ...])

### 4. Generate Policy Document

Once questions are answered, trigger AI document generation:

    pretorin_trigger_policy_generation(policy_id)

### 5. Review

Trigger an AI review of the policy:

    pretorin_trigger_policy_review(policy_id)
    pretorin_get_policy_review_results(policy_id)  # poll for results

Review results include findings with severity levels, affected sections, and recommended fixes.

### 6. Track Status

    pretorin_get_policy_workflow_state(policy_id)
    pretorin_get_policy_analytics()

## Scope Questionnaire Workflow

Scope questionnaires are system+framework specific. They define what’s in scope, what’s excluded, and system boundary details.

### 1. View Current State

    # Show scope questionnaire state and review findings
    pretorin scope show --system "My System" --framework-id fedramp-moderate

Or via MCP:

    pretorin_get_pending_scope_questions(system_id, framework_id)
    pretorin_get_scope_question_detail(system_id, framework_id, question_id)

### 2. Answer Questions

Via CLI — Draft answers from your workspace:

    # Preview proposed answers
    pretorin scope populate --system "My System" --framework-id fedramp-moderate

    # Apply answers to the platform
    pretorin scope populate --system "My System" --framework-id fedramp-moderate --apply

Via MCP — Answer individually:

    pretorin_answer_scope_question(system_id, framework_id, question_id, answer)

Or batch-update:

    pretorin_patch_scope_qa(system_id, framework_id, updates=[{question_id, answer}, ...])

### 3. Generate Scope Document

    pretorin_trigger_scope_generation(system_id, framework_id)

### 4. Review

    pretorin_trigger_scope_review(system_id, framework_id)
    pretorin_get_scope_review_results(system_id, framework_id)

### 5. View Full Scope

    pretorin_get_scope(system_id, framework_id)

Returns scope narrative, excluded controls, and Q&A responses.

## Bulk Questionnaire Campaigns

For answering many questions at once, use campaigns:

    # Answer all incomplete policy questions
    pretorin campaign policy --mode answer --all-incomplete

    # Answer scope questions
    pretorin campaign scope --mode answer --system "My System" --framework-id fedramp-moderate

    # Fix review findings
    pretorin campaign policy --mode review-fix --policies <policy-id>

See Campaign Workflows for details on the campaign lifecycle.

# Vendor Inheritance

Many compliance controls are partially or fully satisfied by external providers (cloud platforms, SaaS tools, managed services). Pretorin tracks these inheritance relationships and keeps inherited narratives in sync with vendor documentation.

## Concepts

- Vendor — An external provider or internal shared service (CSP, SaaS, managed service, internal)
- Responsibility edge — A link between a control and a vendor indicating the control is inherited or shared
- Stale edge — A responsibility edge where the source narrative has changed but the inherited control hasn’t been updated

## Workflow

### 1. Create Vendor Entities

    pretorin vendor create "AWS GovCloud" --type csp \
      --description "Primary cloud infrastructure" \
      --authorization-level "FedRAMP High P-ATO"

    pretorin vendor create "Okta" --type saas \
      --description "Identity and access management"

### 2. Upload Vendor Documentation

    pretorin vendor upload-doc <vendor_id> ./aws-crm.pdf \
      --name "AWS Customer Responsibility Matrix" \
      --attestation-type vendor_provided

    pretorin vendor upload-doc <vendor_id> ./okta-soc2.pdf \
      --name "Okta SOC 2 Type II Report" \
      --attestation-type third_party_attestation

### 3. Set Control Responsibility

Via MCP tools:

    pretorin_set_control_responsibility(system_id, control_id, framework_id, vendor_id, responsibility_type)

Responsibility types:

- inherited — Fully satisfied by the vendor
- shared — Partially satisfied; your system handles the remainder

### 4. Generate Inheritance Narratives

    pretorin_generate_inheritance_narrative(system_id, control_id, framework_id, vendor_id)

AI generates a narrative grounded in the vendor’s uploaded documentation, explaining how the vendor satisfies the control requirements.

### 5. Monitor Staleness

Over time, vendor documentation or source narratives may be updated. Check for stale inheritance:

    pretorin_get_stale_edges(system_id, framework_id)

Returns controls where the source has changed but the inherited narrative hasn’t been refreshed.

### 6. Sync Stale Edges

    pretorin_sync_stale_edges(system_id, framework_id)

Bulk updates inherited controls by regenerating narratives from the latest source.

## Linking Evidence to Vendors

    pretorin_link_evidence_to_vendor(evidence_id, vendor_id, attestation_type)

Attestation types: `self_attested`, `third_party_attestation`, `vendor_provided`

# Gap Analysis Workflow

A systematic approach to assessing a codebase against a compliance framework’s controls.

## Step 1: Scope the Assessment

Determine which framework and control families to assess.

    # List frameworks if not specified
    pretorin frameworks list

    # List control families for the chosen framework
    pretorin frameworks families fedramp-moderate

Not all families will have code evidence. Prioritize based on evidence likelihood.

## Step 2: Prioritize Control Families

### High Priority (Direct Code Evidence)

These families typically have strong evidence in source code:

Family | What to Search For
--- | ---
Access Control (AC) | Authentication systems, RBAC/ABAC, session management, user provisioning
Audit & Accountability (AU) | Logging frameworks, audit trails, log retention, structured logging
Identification & Authentication (IA) | Login flows, MFA, password hashing, credential storage, OAuth/SAML
System & Communications Protection (SC) | TLS config, encryption, network boundaries, CORS, API security
Configuration Management (CM) | Config files, env handling, version pinning, baseline settings, IaC

### Medium Priority (Mixed Code/Policy)

Family | What to Search For
--- | ---
System Acquisition (SA) | Secure development practices, dependency management, SAST/DAST configs
System Integrity (SI) | Input validation, error handling, malware protection configs
Assessment (CA) | Security testing configs, vulnerability scanning, CI/CD security gates

### Lower Priority (Mostly Policy)

Primarily documentation-based, unlikely to have code evidence:

- Awareness & Training (AT)
- Planning (PL)
- Personnel Security (PS)
- Physical Protection (PE)
- Program Management (PM)

## Step 3: Collect Evidence

For each high-priority family:

1. List controls filtered by family:

       pretorin frameworks controls fedramp-moderate --family access-control

2. For each relevant control, get AI guidance:

       pretorin frameworks control fedramp-moderate ac-02

   References and AI guidance are shown by default. The `ai_guidance` field provides evidence expectations, implementation considerations, and common failures. Use `--brief` to show only the basic info panel.

3. Search the codebase using guidance-informed patterns:

File patterns:

    **/auth/**          **/users/**         **/accounts/**
    **/logging/**       **/audit/**         **/security/**
    **/config/**        **/settings/**      **/crypto/**
    **/identity/**      **/iam/**           **/rbac/**
    **/middleware/**     **/terraform/**     **/k8s/**

Keyword patterns:

    authenticate, authorize, permission, role, session
    log, audit, event, trace, record
    encrypt, tls, ssl, https, certificate
    config, setting, baseline, default
    password, credential, hash, mfa, token

4. For each piece of evidence, note the file path, line numbers, and what it demonstrates.

## Step 4: Assess Implementation Status

For each control, assign a status:

Status | Criteria
--- | ---
Implemented | Full requirements met with clear code evidence
Partial | Some requirements met, others missing or incomplete
Planned | Architecture supports it but feature not built yet
Not Applicable | Control doesn’t apply to this component
Gap | Control requirements not addressed at all

Use `ai_guidance.common_failures` to calibrate your assessment — if the codebase exhibits a known failure pattern, it’s likely a gap or partial implementation.

## Step 5: Produce the Report

Structure the gap analysis output as:

### Summary

- Framework assessed and total controls in scope
- Counts by status (implemented, partial, planned, not applicable, gap)
- Overall compliance posture assessment

### Family-by-Family Findings

For each assessed family:

- Family name and total controls
- Status breakdown
- Key findings with evidence references
- Gaps with remediation recommendations

### Priority Remediation Items

Rank gaps by:

1. Controls with the highest security impact
2. Controls that are prerequisites for other controls (check related controls)
3. Controls that are easiest to implement (quick wins)

### Evidence Summary

For each assessed control:

- Control ID and title
- Implementation status
- Evidence file paths and descriptions
- Recommendations if partial or gap

## Example Output

See the example gap analysis for a complete sample report.

## Tips

- Start broad (family level) and drill into specific controls where evidence exists
- Use `pretorin frameworks control <fw> <ctrl>` for AI guidance — it provides the richest context (references are included by default; use `--brief` to skip them)
- Check related controls to identify dependencies
- For infrastructure evidence, look at Terraform, CloudFormation, Dockerfiles, Helm charts, and CI/CD configs
- For application evidence, focus on auth, logging, crypto, and configuration code

# Example: Gap Analysis Report

This example shows a gap analysis for a hypothetical web application assessed against FedRAMP Moderate.

## Gap Analysis: Acme Web Platform — FedRAMP Moderate

### Summary

Metric | Value
--- | ---
Framework | FedRAMP Rev 5 Moderate (`fedramp-moderate`)
Component | Acme Web Platform
Families Assessed | 5 of 18 (high-priority code-evidenced families)
Controls in Scope | 47
Implemented | 18 (38%)
Partial | 14 (30%)
Planned | 3 (6%)
Not Applicable | 4 (9%)
Gap | 8 (17%)

Overall Posture: Partial compliance. Strong authentication and logging foundations, but gaps in account lifecycle management, boundary protection, and baseline configuration documentation.

### Access Control (AC) — 12 controls assessed

Status: 5 implemented, 4 partial, 1 planned, 2 gap

Key Findings:

- AC-02 (Account Management) — Partial. User creation with role assignment exists in `src/auth/users.py:45-72`, but no account expiration, dormant account handling, or manager approval workflow.
- AC-03 (Access Enforcement) — Implemented. RBAC middleware in `src/middleware/auth.py:12-38` enforces role-based access on all API routes.
- AC-07 (Unsuccessful Logon Attempts) — Implemented. Account lockout after 5 failed attempts in `src/auth/login.py:89-105`.
- AC-17 (Remote Access) — Gap. No VPN or remote access controls documented.

Recommendations:

1. Add account expiration and dormant account cleanup (addresses AC-02 gaps)
2. Implement remote access policy and controls for administrative access (addresses AC-17)

### Audit & Accountability (AU) — 8 controls assessed

Status: 5 implemented, 2 partial, 1 gap

Key Findings:

- AU-02 (Audit Events) — Implemented. Structured JSON logging for auth events, data access, and admin actions.
- AU-03 (Content of Audit Records) — Implemented. Logs include timestamp, user ID, action, outcome, and source IP.
- AU-06 (Audit Record Review) — Gap. No automated log review or alerting configured.

Recommendations:

1. Configure CloudWatch alarms for security events (addresses AU-06)
2. Add log review procedures and alerting rules

### System & Communications Protection (SC) — 10 controls assessed

Status: 2 implemented, 4 partial, 2 planned, 2 not applicable

Key Findings:

- SC-07 (Boundary Protection) — Partial. TLS 1.3 and CORS configured, but security groups allow broad ingress.
- SC-08 (Transmission Confidentiality) — Implemented. All traffic encrypted via TLS 1.3 with HSTS.
- SC-28 (Protection of Information at Rest) — Planned. Database encryption not yet enabled.

### Priority Remediation

Priority | Control | Gap | Effort
--- | --- | --- | ---
1 | SC-28 | Enable RDS encryption at rest | Low — Terraform change
2 | AU-06 | Add CloudWatch alerting for security events | Medium — alerting rules
3 | AC-02 | Account lifecycle management | Medium — new feature
4 | CM-02/CM-06 | Baseline configuration documentation | Medium — documentation
5 | AC-17 | Remote access controls for admin access | High — new infrastructure

# Artifact Generation

Compliance artifacts are structured JSON documents that describe how a specific control is implemented within a component.

## Generating Artifacts

### Via Agent

    pretorin agent run --skill evidence-collection "Generate artifact for AC-02 in my system"

### Via MCP

Use the `pretorin_generate_control_artifacts` tool for read-only AI drafts.

### Submit to Platform

    pretorin frameworks submit-artifact artifact.json

## Artifact Schema

    {
      "framework_id": "fedramp-moderate",
      "control_id": "ac-02",
      "component": {
        "component_id": "my-application",
        "title": "My Application",
        "description": "A web application that handles user data",
        "type": "software",
        "control_implementations": [
          {
            "control_id": "ac-02",
            "description": "2-3 sentence narrative explaining HOW the control is implemented",
            "implementation_status": "implemented",
            "responsible_roles": ["System Administrator", "Security Team"],
            "evidence": [
              {
                "description": "What this evidence demonstrates",
                "file_path": "src/auth/users.py",
                "line_numbers": "45-72",
                "code_snippet": "def create_user(username, role):\n    ..."
              }
            ],
            "remarks": "Optional additional context"
          }
        ]
      },
      "confidence": "high"
    }

See Artifact Schema Reference for the full field documentation.

## Implementation Status Values

Status | Criteria
--- | ---
`implemented` | Fully implemented and operational. Clear, direct code evidence.
`partial` | Some aspects implemented, others pending.
`planned` | Not yet implemented but scheduled. Architecture supports it.
`not-applicable` | Control doesn’t apply to this component.

## Confidence Levels

Level | Criteria
--- | ---
`high` | Clear, direct evidence in code. Specific file paths and line numbers.
`medium` | Reasonable evidence with some inference required.
`low` | Limited evidence. Significant assumptions made.

## Evidence Quality

Good evidence shows HOW a control is implemented with specifics. Weak evidence merely shows that relevant code exists.

Good:

> User creation requires role assignment and manager approval via the `create_user()` function which validates roles against an allowlist and triggers an approval workflow.

Weak:

> Has a User class in the models file.

### Guidelines

- Call `pretorin frameworks control <fw> <ctrl>` first — the AI guidance describes exactly what evidence assessors expect
- Include specific file paths and line numbers
- Keep code snippets brief (under 10 lines)
- Focus on the most relevant evidence, not exhaustive listing
- Describe what the evidence demonstrates in relation to the control requirement

## Example: Good Artifact

    {
      "framework_id": "fedramp-moderate",
      "control_id": "ac-02",
      "component": {
        "component_id": "acme-web-platform",
        "title": "Acme Web Platform",
        "description": "A web application with multi-tenant user management",
        "type": "software",
        "control_implementations": [
          {
            "control_id": "ac-02",
            "description": "The application implements account management through a provisioning system that requires role assignment during user creation, enforces manager approval for elevated roles, and automatically disables accounts after 90 days of inactivity.",
            "implementation_status": "implemented",
            "responsible_roles": ["System Administrator", "Security Team", "Team Managers"],
            "evidence": [
              {
                "description": "User creation requires role assignment and manager approval for admin roles",
                "file_path": "src/users/provisioning.py",
                "line_numbers": "45-72",
                "code_snippet": "def create_user(username, role, manager_id):\n    validate_role(role)\n    if role in ELEVATED_ROLES:\n        require_approval(manager_id)\n    user = User.create(username=username, role=role)"
              },
              {
                "description": "Automated dormant account detection and deactivation after 90 days",
                "file_path": "src/users/lifecycle.py",
                "line_numbers": "120-145",
                "code_snippet": "def check_dormant_accounts():\n    threshold = datetime.utcnow() - timedelta(days=90)\n    dormant = User.query.filter(User.last_login < threshold)"
              }
            ],
            "remarks": "Account removal via soft delete to maintain audit trail."
          }
        ]
      },
      "confidence": "high"
    }

## Example: Partial Implementation

    {
      "framework_id": "fedramp-moderate",
      "control_id": "sc-07",
      "component": {
        "component_id": "acme-web-platform",
        "title": "Acme Web Platform",
        "description": "A web application with multi-tenant user management",
        "type": "software",
        "control_implementations": [
          {
            "control_id": "sc-07",
            "description": "TLS 1.3 enforced and CORS restricted to specific origins. However, security group ingress allows broad access from 0.0.0.0/0 on port 443, and no WAF is configured.",
            "implementation_status": "partial",
            "responsible_roles": ["System Administrator", "DevOps Team"],
            "evidence": [
              {
                "description": "CORS restricted to application origins only",
                "file_path": "src/api/middleware.py",
                "line_numbers": "8-15",
                "code_snippet": "app.add_middleware(\n    CORSMiddleware,\n    allow_origins=['https://app.acme.com'])"
              },
              {
                "description": "Security group allows unrestricted ingress — overly permissive",
                "file_path": "terraform/security.tf",
                "line_numbers": "12-25",
                "code_snippet": "ingress {\n    from_port = 443\n    cidr_blocks = [\"0.0.0.0/0\"]\n}"
              }
            ],
            "remarks": "Recommend restricting security group ingress and adding WAF."
          }
        ]
      },
      "confidence": "medium"
    }

# Cross-Framework Mapping

Map controls across related frameworks to identify overlaps, reduce duplicate work, and understand framework relationships.

## When to Use Cross-Framework Mapping

- Dual compliance — Organization needs FedRAMP + CMMC. Map overlapping controls to avoid duplicate work.
- Framework migration — Moving from 800-171 to FedRAMP. Identify which controls already satisfy FedRAMP requirements.
- Gap identification — Already compliant with 800-53 and need CMMC. Find the delta.
- Audit preparation — Show auditors how controls in one framework map to another.

## Workflow

### Step 1: Start with the Source Control

Query the control with references to discover relationships:

    pretorin frameworks control nist-800-53-r5 ac-02

References are shown by default. The Related Controls field reveals connections to other controls and frameworks.

### Step 2: Build the Mapping

Look up the equivalent control in each target framework:

Framework | Control ID | Title
--- | --- | ---
NIST 800-53 Rev 5 | `ac-02` | Account Management
FedRAMP Moderate | `ac-02` | Account Management
NIST 800-171 Rev 3 | `03.01.01` | Account Management
CMMC Level 2 | `AC.L2-3.1.1` | Authorized Access Control

### Step 3: Compare Requirements

Get details for each framework’s version of the control:

    pretorin frameworks control nist-800-53-r5 ac-02
    pretorin frameworks control fedramp-moderate ac-02
    pretorin frameworks control nist-800-171-r3 03.01.01
    pretorin frameworks control cmmc-l2 AC.L2-3.1.1

Compare what each framework emphasizes. For Account Management:

- NIST 800-53 — Full control with 13 enhancements. Covers account types, conditions, authorized users, managers, CRUD, monitoring, and atypical usage.
- FedRAMP Moderate — Same base control with FedRAMP-specific parameter values (e.g., specific timeframes for disabling inactive accounts).
- NIST 800-171 — Streamlined from 800-53. Core requirements: defining types, assigning managers, establishing conditions, authorizing access, monitoring.
- CMMC Level 2 — Maps directly to 800-171 03.01.01. Same core requirements framed as maturity practices.

### Step 4: Identify Gaps and Overlaps

    NIST 800-53 AC-02 (most comprehensive)
      ├── Includes all FedRAMP Moderate AC-02 requirements ✓
      ├── Includes all NIST 800-171 03.01.01 requirements ✓
      └── Includes all CMMC L2 AC.L2-3.1.1 requirements ✓

    FedRAMP Moderate AC-02
      ├── Satisfies NIST 800-171 03.01.01 ✓
      └── Satisfies CMMC L2 AC.L2-3.1.1 ✓

    NIST 800-171 03.01.01
      └── Satisfies CMMC L2 AC.L2-3.1.1 ✓

Key insight: Compliance with a parent framework generally satisfies the child framework’s corresponding control. Always verify with `pretorin frameworks control <fw> <ctrl>` to check for framework-specific parameters or additional requirements (references are included by default).

## Using MCP for Cross-Framework Mapping

With an MCP-connected AI agent, ask questions like:

> “Map Account Management controls across NIST 800-53, FedRAMP Moderate, and CMMC Level 2. Show me the overlaps and any unique requirements.”

The agent will use `pretorin_get_control` and `pretorin_get_control_references` to discover and compare related controls across frameworks.

# STIG Compliance Scanning

Pretorin integrates STIG (Security Technical Implementation Guide) scanning to verify technical control implementations. The scanning workflow connects NIST 800-53 controls to specific technical checks via the CCI (Control Correlation Identifier) chain.

## Traceability Chain

    NIST 800-53 Control → CCIs → SRGs → STIG Rules → Scanner Results

- CCI — Control Correlation Identifier: bridges a control requirement to testable items
- SRG — Security Requirements Guide: technology-neutral security requirements
- STIG Rule — Technology-specific check with detailed test and fix procedures

## Browse the Chain

### Find Applicable STIGs

    # Show STIGs applicable to your system
    pretorin stig applicable --system "My System"

    # AI-infer STIGs from system profile
    pretorin stig infer --system "My System"

### Explore the Traceability

    # Full chain from a NIST control to STIG rules
    pretorin cci chain ac-2 --system "My System"

    # Browse CCIs for a control
    pretorin cci list --control ac-2

    # See what a specific CCI requires
    pretorin cci show CCI-000015

    # Browse STIG rules
    pretorin stig rules <stig_id> --severity cat_i

## Scanning Workflow

Scanning is driven by recipes that the calling AI agent invokes through MCP. Each scanner ships as a built-in recipe (`inspec-baseline`, `openscap-baseline`, `cloud-aws-baseline`, `cloud-azure-baseline`, `manual-attestation`).

### 1. Discover Available Recipes

    pretorin recipe list
    pretorin recipe show inspec-baseline

### 2. Review Test Manifest

The agent uses `pretorin_get_test_manifest` (MCP) to see which STIGs and rules apply to a system before running a scan. From the CLI you can browse the relationships directly:

    pretorin stig applicable --system "My System"
    pretorin cci chain ac-2 --system "My System"

### 3. Ask the Agent to Run the Scan

Inside Claude Code, Codex CLI, or `pretorin agent run`, ask:

> “Run an inspec-baseline scan against `RHEL_9_STIG` on this system.”

The agent will open a recipe context, call the recipe’s `run_scan` script, and submit results through `pretorin_submit_test_results`. There is no direct CLI command for executing a scan — the recipe layer is the contract surface.

### 4. Submit Results Manually

If you have raw scanner output and want to upload it without running through a recipe, push it directly via MCP:

    pretorin_submit_test_results(system_id, results)

## MCP Tools for STIG/CCI

Tool | Description
--- | ---
`pretorin_list_stigs` | List benchmarks with filters
`pretorin_get_stig` | Benchmark detail
`pretorin_list_stig_rules` | Rules with severity/CCI filters
`pretorin_get_stig_rule` | Full rule: check text, fix text, CCIs
`pretorin_list_ccis` | CCIs with control filter
`pretorin_get_cci` | CCI detail with linked rules
`pretorin_get_cci_chain` | Full traceability chain
`pretorin_get_cci_status` | CCI compliance rollup
`pretorin_get_stig_applicability` | Applicable STIGs for a system
`pretorin_infer_stigs` | AI-infer applicable STIGs
`pretorin_get_test_manifest` | Test manifest for a system
`pretorin_submit_test_results` | Upload scan results

# Environment Variables

Environment variables override stored configuration values.

## Authentication & API

Variable | Description | Default
--- | --- | ---
`PRETORIN_API_KEY` | API key for platform access. Overrides `api_key` in config file. | —
`PRETORIN_PLATFORM_API_BASE_URL` | Platform REST API base URL | `https://platform.pretorin.com/api/v1/public`
`PRETORIN_API_BASE_URL` | Backward-compatible alias for `PRETORIN_PLATFORM_API_BASE_URL` | —
`PRETORIN_MODEL_API_BASE_URL` | Model API URL for agent runtime | `https://platform.pretorin.com/api/v1/public/model`

## Context

Variable | Description | Default
--- | --- | ---
`PRETORIN_SYSTEM_ID` | Active system ID. Overrides the system set via `pretorin context set`. | —
`PRETORIN_FRAMEWORK_ID` | Active framework ID. Overrides the framework set via `pretorin context set`. | —

## Agent Runtime

Variable | Description | Default
--- | --- | ---
`OPENAI_API_KEY` | Model key override for agent runtime. Takes precedence over stored Pretorin login key. | —
`OPENAI_BASE_URL` | Base URL for the model API endpoint. Overrides `openai_base_url` in config file. | —
`OPENAI_MODEL` | Model name for the agent runtime. | `gpt-4o`

## Source Attestation

Variable | Description | Default
--- | --- | ---
`PRETORIN_SOURCE_PROVIDERS` | JSON array of source provider configurations. Overrides `source_providers` in config file. | —
`PRETORIN_SOURCE_MANIFEST` | JSON string or file path to a source manifest. Falls back to `.pretorin/source-manifest.json` or `~/.pretorin/source-manifest-{system_id}.json`. | —

## Behavior

Variable | Description | Default
--- | --- | ---
`PRETORIN_DISABLE_UPDATE_CHECK` | Set to `1` to disable passive update notifications | —
`PRETORIN_LOG_LEVEL` | Logging level (`DEBUG`, `INFO`, `WARNING`, `ERROR`) | `WARNING`

## Precedence

For the API key:

1. `PRETORIN_API_KEY` environment variable (highest)
2. `api_key` in `~/.pretorin/config.json`

For the model key (agent runtime):

1. `OPENAI_API_KEY` environment variable (highest)
2. `config.api_key` (from `pretorin login`)
3. `config.openai_api_key`

For the model name:

1. `OPENAI_MODEL` environment variable (highest)
2. `openai_model` in `~/.pretorin/config.json`
3. Org AI settings from the platform (cached)
4. `gpt-4o` default

## CI/CD Example

    export PRETORIN_API_KEY=pretorin_your_key_here
    export PRETORIN_DISABLE_UPDATE_CHECK=1
    export PRETORIN_SYSTEM_ID=your_system_id

    pretorin frameworks list
    pretorin evidence push

# Artifact Schema Reference

Complete field reference for compliance artifact JSON documents.

## Top-Level Fields

Field | Type | Required | Description
--- | --- | --- | ---
`framework_id` | string | Yes | The compliance framework (e.g., `fedramp-moderate`, `nist-800-53-r5`)
`control_id` | string | Yes | The control being addressed (e.g., `ac-02`, `au-02`)
`component` | object | Yes | The system component being assessed
`confidence` | string | Yes | Confidence in the analysis: `high`, `medium`, or `low`

## Component Fields

Field | Type | Required | Description
--- | --- | --- | ---
`component_id` | string | Yes | Source identifier (repository name, package name)
`title` | string | Yes | Human-readable component name
`description` | string | Yes | Brief description of what the component does
`type` | string | Yes | One of: `software`, `hardware`, `service`, `policy`, `process`
`control_implementations` | array | Yes | How the control is implemented

## Control Implementation Fields

Field | Type | Required | Description
--- | --- | --- | ---
`control_id` | string | Yes | Must match parent `control_id`
`description` | string | Yes | 2-3 sentence narrative of HOW the control is implemented
`implementation_status` | string | Yes | `implemented`, `partial`, `planned`, or `not-applicable`
`responsible_roles` | array | No | Roles responsible (default: `["System Administrator"]`)
`evidence` | array | No | Supporting evidence items
`remarks` | string | No | Additional notes or caveats

## Evidence Fields

Field | Type | Required | Description
--- | --- | --- | ---
`description` | string | Yes | Narrative of what this evidence shows
`file_path` | string | No | Path to the source file
`line_numbers` | string | No | Line range (e.g., `"10-25"`)
`code_snippet` | string | No | Relevant code excerpt (keep under 10 lines)

## Implementation Status Definitions

Status | Definition
--- | ---
`implemented` | Control is fully implemented and operational. Clear, direct evidence exists in the codebase.
`partial` | Some aspects are implemented, others are pending. Example: user CRUD exists but no account expiration or manager approval.
`planned` | Not yet implemented but scheduled. The architecture supports it but the feature isn’t built.
`not-applicable` | Control doesn’t apply to this component. Example: a pure API service with no user accounts doesn’t need account management controls.

## Confidence Levels

Level | Definition
--- | ---
`high` | Clear, direct evidence in code. Well-documented implementations with specific file paths and line numbers.
`medium` | Reasonable evidence but some inference required. The implementation likely satisfies the control but some aspects aren’t explicitly documented.
`low` | Limited evidence. Significant assumptions made. The codebase has relevant code but the connection to the control requirement is indirect.

# Contributing

Thank you for your interest in contributing to the Pretorin CLI!

We welcome contributions to the CLI, MCP server, docs, scanners, developer workflows, and local tooling. This repository is open source under Apache-2.0, while Pretorin-hosted platform services, authenticated API access, and account-scoped data are governed separately by the applicable platform terms.

## Scope

Good fits for this repository:

- CLI commands and output improvements
- MCP tools, prompts, and local agent integrations
- Scanner integrations and developer workflow automation
- Documentation, examples, and tests

Out of scope for public contributions:

- Customer data, exported platform data, or private operational runbooks
- Secrets, internal credentials, or private environment details
- Changes that imply trademark rights or suggest an unofficial fork is an official Pretorin service

For brand usage guidance, see Trademarks and Service Terms.

## Getting Started

1. Fork the repository
2. Clone your fork:

       git clone https://github.com/YOUR_USERNAME/pretorin-cli.git
       cd pretorin-cli

3. Install development dependencies:

       uv pip install -e ".[dev]"

## Development Workflow

### Running Tests

    pytest

Integration tests require an API key and are marked with `@pytest.mark.integration`:

    pytest -m integration

Integration tests require a valid API key tied to an account that has accepted the platform terms.

### Type Checking

    mypy src/pretorin

### Linting

    ruff check src/pretorin
    ruff format src/pretorin

### Full CI Check

Run the same checks as the CI pipeline:

    ruff check src/pretorin && ruff format --check src/pretorin && mypy src/pretorin && pytest

## Submitting Changes

1. Create a feature branch from `main`
2. Make your changes
3. Ensure tests pass and code is properly formatted
4. Add a sign-off to each commit with `git commit -s`
5. Submit a pull request

By submitting a contribution, you certify that:

- You have the right to submit the code, docs, or other materials.
- Your contribution may be distributed under the Apache License, Version 2.0.
- You are not including confidential information, customer data, or material that is governed by separate platform terms.

## Code Style

- Follow PEP 8 guidelines
- Use type hints for all function signatures
- Write docstrings for public functions and classes
- Keep functions focused and small

## CI Pipeline

The CI pipeline runs on Python 3.10, 3.11, and 3.12:

- Lint — Ruff check + format
- Audit — pip-audit (dependency vulnerability scan)
- Type check — mypy strict mode
- Test — pytest

## Legal and Platform Boundaries

- The source code in this repository is licensed under Apache-2.0.
- The Pretorin name, logos, and other brand assets remain subject to trademark rights and are not licensed for reuse except for nominative/reference use. See Trademarks and Service Terms.
- Access to Pretorin-hosted APIs, services, and account-scoped data is authenticated and governed by separate platform terms.

## Reporting Issues

Use GitHub Issues to report bugs or request features. Include:

- Clear description of the issue
- Steps to reproduce (for bugs)
- Expected vs actual behavior
- CLI version (`pretorin version`)

## Questions?

- API documentation: platform.pretorin.com/api/docs
- Platform: platform.pretorin.com

# Trademarks and Service Terms

Pretorin, the Pretorin logo, and related brand assets are trademarks or registered trademarks of Pretorin, Inc.

The Apache-2.0 license for this repository covers the source code and documentation in this repo. It does not grant permission to use Pretorin trademarks, logos, or branding for derivative products or services in a way that suggests sponsorship, endorsement, or official status.

Permitted uses generally include truthful, referential statements such as:

- Saying that your project is based on or compatible with Pretorin CLI
- Linking to this repository or to Pretorin documentation
- Describing changes you made in a fork, as long as you do not imply the fork is an official Pretorin release or hosted service

Not permitted without separate permission:

- Shipping a fork under the Pretorin name as if it were the official product
- Reusing Pretorin logos, trade dress, or marketing assets for another hosted service
- Suggesting endorsement, partnership, certification, or official support where none exists

Access to Pretorin-hosted platform services, APIs, and any account-scoped data returned by those services is governed by the applicable platform terms and account agreements, which are separate from the open-source license for this repository.

# Changelog

All notable changes to the Pretorin CLI are documented here. The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.

## [0.17.4] - 2026-05-06

### Added

- CCI implementation read endpoint (#97): `pretorin cci impl <cci_uuid>` and MCP tool `pretorin_get_cci_implementation` wrap the new platform `GET /systems/{system_id}/cci-implementations/{cci_uuid}` endpoint, returning the live per-system impl row.
- Evidence link target-type extensions (#97): new sibling commands `pretorin evidence link-cci` and `pretorin evidence link-stig` plus MCP tools `pretorin_link_evidence_to_cci_implementation` and `pretorin_link_evidence_to_stig_rule_workflow`. Both honor the platform’s `override_system_mismatch` + `override_reason` gate; the STIG variant lazy-creates the workflow row when none exists.
- Agent guidance on STIG-to-CCI traceability: SKILL.md and the `single-control` workflow playbook clarify that the STIG-rule → CCI relationship is catalog-level (DISA-defined). Use `pretorin_get_cci_chain(nist_control_id, system_id)` for “what tests this CCI on this system.”

## [0.17.3] - 2026-05-05

### Fixed

- Scope and policy generation MCP tools now request AI review in the same durable generation job by default, matching the platform workflow while preserving an `include_review=false` opt-out.

## [0.17.2] - 2026-05-02

### Documentation

- Repository-wide documentation sync to current v0.17 surfaces: README recipes table, getting-started, CLI/MCP reference, frameworks selection + custom-framework authoring, recipes/workflows, agent overview, env-vars reference, llms.txt manifests, and a fresh mdBook rebuild.

### Fixed

- Test isolation: `test_install_default_writes_to_all_known_agents` now performs filesystem assertions inside the `Path.home()` patch context so CI runs do not depend on the runner’s real home directory.

## 0.17.1 - 2026-04-30

### Added

- Custom framework authoring CLI (#90): end-to-end build / validate / upload workflow around the platform’s `unified.json` revision-lifecycle endpoints. New `pretorin frameworks` commands: `init-custom`, `validate-custom`, `build-custom`, `upload-custom` (`--publish` to ship immediately), `fork-framework`, `rebase-fork`, `revisions`, `export-oscal`.
- Vendored unified-framework toolchain at `pretorin.frameworks`: bundled JSON Schema validator, OSCAL ↔ unified converters with lossless round-trip, and the 12-format custom-catalog converter ported from the monorepo `data/tools/`.
- Framework revision lifecycle client methods on `PretorianClient`: `create_custom_draft`, `publish_draft`, `fork_framework`, `create_rebase_draft`, `list_revisions`. Structured `validation_report` is preserved through `PretorianClientError.details` on 400.
- `jsonschema>=4.0.0` added as a runtime dependency.

### Documentation

- New page covering the full custom-framework workflow: Custom frameworks.

## 0.17.0 - 2026-04-30

### Added

- Recipe extensibility system (RFC 0001): full implementation of the three-layer routing model — engagement → workflow → recipe. Calling AI agents now route through deterministic Python rules to a workflow playbook, then pick recipes per item from a discoverable menu instead of freelancing.
- `pretorin_start_task` MCP tool: pure-function rule cascade over agent-extracted entities. Cross-checks against platform state (hallucinated control ids → hard error; wrong-framework / cross-system writes → ambiguous response). Bundles inspect summary into the response.
- Workflow registry + 4 built-in playbooks: `single-control`, `scope-question`, `policy-question`, `campaign`. `pretorin_list_workflows` and `pretorin_get_workflow` MCP tools.
- Recipe registry + 8 built-in recipes: `code-evidence-capture`, `inspec-baseline`, `openscap-baseline`, `cloud-aws-baseline`, `cloud-azure-baseline`, `manual-attestation`, `scope-q-answer`, `policy-q-answer`.
- Recipe authoring surface: `pretorin recipe list / show / new / validate / run` CLI commands. Four loader paths with clear precedence: explicit > project > user > built-in. Per-script MCP tools auto-registered as `pretorin_recipe_<safe_id>__<script>`.
- Recipe execution context: `pretorin_start_recipe` / `pretorin_end_recipe`; every platform write inside the context auto-stamps `producer_kind="recipe"`, recipe id, and recipe version.
- Audit-trail metadata: `EvidenceAuditMetadata` is stamped on every CLI / agent / MCP / campaign-apply evidence write. Build helpers at `pretorin.evidence.audit_metadata` are the single construction surface.
- Recipe selection on every drafting call: `draft_control_artifacts` consults the recipe registry before falling through to freelance. The decision is recorded as `RecipeSelection` on the response.
- `pretorin.evidence.redact` + `pretorin.evidence.markdown`: shared primitives for secret redaction and audit-grade markdown composition.
- Bundled `pretorin` skill v0.17.0: teaches the calling agent about the routing model. New “Engagement (Routing)” section flags `pretorin_start_task` as the FIRST call.
- Authoring docs at `docs/src/recipes/`: index, manifest reference, script contract, writer tools, testing, publishing, workflows, engagement, worked example.

### Changed (BREAKING)

- `pretorin scan` CLI command removed. All scanner functionality moved to recipes. Migrate to `pretorin recipe run <recipe-id>` (e.g., `pretorin recipe run inspec-baseline --param stig_id=RHEL_9_STIG`) or invoke via MCP.
- `ScanOrchestrator` removed. Manifest fetch + rule filter + summary helpers extracted to `pretorin.scanners.manifest` and shared across scanner recipes.

### Removed

- `src/pretorin/cli/scan.py` and `src/pretorin/scanners/orchestrator.py`.
- The deprecated `rejected_invalid_type` campaign-apply telemetry counter (deprecated in 0.16.0).

## 0.16.3 - 2026-04-26

### Fixed

- CCI chain test fix: `test_cci_chain_with_system_status` now correctly mocks `resolve_execution_context` so CCI status rendering is exercised. No production code changes.

## 0.16.2 - 2026-04-21

### Fixed

- `pretorin campaign controls --family` case-insensitive resolution (#84): `--family cc6` now resolves to canonical `CC6` before hitting the backend. Unknown families raise a structured error listing available families and pointing at `pretorin frameworks families <framework-id>`. Same fix applied to `pretorin_prepare_campaign` MCP handler.

## 0.16.1 - 2026-04-21

### Added

- Gap questions for policy and scope Q&A: MCP tool descriptions guide agents through answer-first workflow with structured gap questions for organizational knowledge gaps.

## 0.16.0 - 2026-04-21

### Changed (BREAKING)

- `evidence_type` is now required on every CLI, MCP, agent, and workflow write path (#79). CLI paths hard-error when the user omits `-t/--type`; every other path runs a client-side normalizer before submission.

### Added

- Evidence provenance fields: CLI sends `code_file_path`, `code_line_numbers`, `code_snippet`, `code_repository`, `code_commit_hash` on all evidence creation paths. Auditors can trace evidence to source files and commits.
- Source verification mapping: Attested source identities mapped to platform’s `SourceVerificationPayload` with `source_type` and `source_role`.
- `pretorin evidence upload`: Upload files (screenshots, PDFs, configs) as evidence with SHA-256 integrity verification.
- `pretorin_upload_evidence` MCP tool: Agents and recipes can upload evidence files via MCP.
- File reference validation: Campaign apply reads actual file content as canonical snippet, validates paths and line ranges.
- Code provenance on local evidence: Frontmatter supports code_* fields for local evidence create and push.
- `pretorin.evidence.types` module: canonical 13-type enum, AI-drift alias map, and `normalize_evidence_type()` with fuzzy matching.

### Changed

- Evidence models include code provenance fields. Campaign extracts `code_*` and `relevance_notes` from AI recommendations.
- `upsert_evidence()` creates enriched evidence as new record when provenance fields are provided.
- AI generation prompt requests code file paths and line numbers in evidence recommendations.

### Fixed

- SOC2 campaign batches with non-canonical `evidence_type` strings now succeed end-to-end via the normalizer.
- Non-campaign write paths can no longer silently tag missing-type evidence as `policy_document`.

## 0.15.5 - 2026-04-20

### Fixed

- Campaign `--apply` runs no longer flood the evidence locker with AI-authored summaries typed as `policy_document` (issue #77). The pipeline now wires `recommended_notes` through to the platform as real gap notes, rejects evidence recommendations with missing or invalid `evidence_type` (turning them into synthesized gap notes), and emits a structured `campaign.apply.control` telemetry line for post-ship measurement.
- Partial failures in the per-control notes write now raise `PretorianClientError` with the failing indexes, mirroring the existing evidence-batch behavior so checkpoint resumes are idempotent.
- Evidence batch result mapping now aligns offsets to the original recommendation index via the accepted-items list and asserts length match, fixing a latent index-drift bug that appeared once any recommendation was rejected mid-loop.
- Completion note now fires when all pending work has landed across runs, not only when something new was written in the current run.

### Changed

- `evidence_type` is now required on `EvidenceBatchItemCreate`. The campaign batch write path no longer silently tags missing types as `policy_document`; pydantic validation raises instead. Other evidence write paths (CLI, MCP, direct API) keep their existing defaults.
- Agent drafting prompts (`_build_generation_task`, `_draft_control_fix`, `_WORKFLOW_GUARDRAILS`, codex system prompt, `[[PRETORIN_TODO]]` template) now list all 13 valid evidence types verbatim and state that an empty `evidence_recommendations` list is a valid result — gaps belong in `recommended_notes`.
- `_WORKFLOW_GUARDRAILS` merged in the evidence-collection skill’s “concrete, auditable artifacts” language so narrative-generation skill callers inherit the same rules.

## 0.15.4 - 2026-04-18

### Changed

- Updated 6 dependencies to resolve 7 known vulnerabilities (cryptography, pygments, pyjwt, pytest, python-multipart, requests)
- Added CLAUDE.md and AGENTS.md for AI agent context

## 0.15.3 - 2026-04-18

### Fixed

- `pretorin update` now checks PyPI before running pip, skipping reinstall when already current
- `pretorin update` verifies the installed version after pip runs, detecting silent failures in pipx/uv-managed environments

### Added

- `pretorin update [VERSION]` accepts an optional version argument to install a specific release

## 0.15.2 - 2026-04-18

### Changed

- Documentation sync: rebuilt all docs to match current codebase

## 0.15.1 - 2026-04-17

### Added

- `pretorin evidence delete <evidence-id>` command with `--yes` flag for non-interactive workflows
- MCP tool `pretorin_delete_evidence` for programmatic evidence deletion within system scope
- API client method `delete_evidence` for the public DELETE endpoint

## 0.15.0 - 2026-04-16

### Added

- Source manifest requirement policy: declare which external sources a system expects and gate compliance writes on their presence
- `pretorin context manifest` command for viewing the resolved manifest and evaluating it against detected sources
- Manifest loading from four layered sources: env var, repo-local `.pretorin/source-manifest.json`, per-system user config, or inline config key
- Family-level source requirements with three requirement levels (required/recommended/optional) and write blocking on missing required sources
- Manifest evaluation results in write provenance (`manifest_status` and `missing_required_sources` fields)

### Changed

- `_enforce_source_attestation` now evaluates manifest requirements after the existing MISMATCH check
- `resolve_execution_context` and `build_write_provenance` accept optional `control_id` for family-level manifest enforcement

## 0.14.0 - 2026-04-10

### Changed

- MCP and agent write workflows now treat the active CLI context as a strict execution boundary by default, with an explicit `allow_scope_override` escape hatch for intentional cross-scope writes
- Control-scoped MCP and agent workflows now route through one shared scope-validation path so exact control lookup happens in the resolved framework before any write proceeds
- Agent guidance now tells built-in workflows to resolve an exact user-supplied control in the active framework before doing broader discovery
- `pretorin mcp-serve` now emits a non-blocking stderr update prompt when a newer CLI release is available, so MCP-only users can discover upgrades without interrupting active tool calls

### Fixed

- `apply_campaign` now reports `apply: true` after a successful apply run and persists that state back to the checkpoint summary
- Stored active context and campaign checkpoints are now validated against the current API environment before campaign reads or writes proceed
- Control-scoped MCP and agent updates now refuse silent remaps like `cm-04.02` to a different control when the exact control does not resolve in the active framework

### Added

- `pretorin_get_cli_status` and the `status://cli` MCP resource expose local CLI version, update availability, and upgrade guidance to MCP hosts and agents

## 0.13.1 - 2026-04-07

### Added

- `pretorin_get_stig` MCP tool for STIG benchmark detail
- `pretorin_get_cci_chain` MCP tool for full Control → CCI → SRG → STIG rule traceability

## 0.13.0 - 2026-04-07

### Added

- Complete STIG/CCI MCP tools: `list_stigs`, `get_stig`, `list_stig_rules`, `get_stig_rule`, `list_ccis`, `get_cci`, `get_cci_chain`, `get_cci_status`, `get_stig_applicability`, `infer_stigs`, `get_test_manifest`, `submit_test_results`
- STIG/CCI agent tools for OpenAI Agents SDK
- `pretorin stig` CLI group: `list`, `show`, `rules`, `applicable`, `infer`
- `pretorin cci` CLI group: `list`, `show`, `chain`
- `pretorin scan` CLI group: `doctor`, `manifest`, `run`, `results`
- Scanner orchestration module with support for OpenSCAP, InSpec, AWS/Azure Cloud Scanners, and Manual review

## 0.12.0 - 2026-04-04

### Added

- Vendor management CLI: `pretorin vendor list/create/get/update/delete/upload-doc/list-docs`
- MCP vendor tools: `list_vendors`, `create_vendor`, `get_vendor`, `update_vendor`, `delete_vendor`, `upload_vendor_document`, `list_vendor_documents`, `link_evidence_to_vendor`
- Inheritance/responsibility MCP tools: `set_control_responsibility`, `get_control_responsibility`, `remove_control_responsibility`, `generate_inheritance_narrative`, `get_stale_edges`, `sync_stale_edges`

## 0.11.0 - 2026-04-02

### Added

- Campaign CLI: `pretorin campaign controls/policy/scope/status`
- Campaign MCP tools: `prepare_campaign`, `claim_campaign_items`, `get_campaign_item_context`, `submit_campaign_proposal`, `apply_campaign`, `get_campaign_status`
- External-agent-first campaign pattern with checkpoint persistence and lease-based concurrency
- Campaign builtin executor for local execution

## 0.10.0 - 2026-03-28

### Added

- Workflow state and analytics MCP tools: `get_workflow_state`, `get_analytics_summary`, `get_family_analytics`, `get_policy_analytics`
- Family operations MCP tools: `get_pending_families`, `get_family_bundle`, `trigger_family_review`, `get_family_review_results`
- Policy workflow MCP tools: `get_pending_policy_questions`, `get_policy_question_detail`, `answer_policy_question`, `get_policy_workflow_state`, `trigger_policy_generation`, `trigger_policy_review`, `get_policy_review_results`
- Scope workflow MCP tools: `get_pending_scope_questions`, `get_scope_question_detail`, `answer_scope_question`, `trigger_scope_generation`, `trigger_scope_review`, `get_scope_review_results`
- ExecutionScope for thread-safe parallel agent execution

## 0.9.7 - 2026-03-25

### Fixed

- Aligned CLI control status validation with the live platform status enum set, including `partially_implemented`
- Aligned MCP control status validation with the live platform status enum set to match public API behavior
- Synced package version metadata and release notes so PyPI builds publish a consistent CLI version

### Changed

- Updated CLI and MCP coverage tests to reflect the platform control status contract used by public control workflows

## 0.8.7 - 2026-03-23

### Added

- MCP questionnaire tooling for scope and organization policy workflows

### Changed

- MCP documentation now reflects the full 29-tool surface, including batch evidence support

## 0.8.6 - 2026-03-23

### Added

- `pretorin context show --quiet` for compact shell-friendly context checks
- `pretorin context show --check` to fail fast when stored scope is missing, stale, or unverified

### Changed

- `context show` caches the last known system name so offline and stale context output stays human-friendly

### Fixed

- `context show` validates stored context against the platform instead of silently treating deleted systems as active

## 0.8.5 - 2026-03-23

### Fixed

- Reset active system/framework context when logging into a different API endpoint or with a different API key
- Model API base URL now follows the configured platform public API endpoint during login
- `scope populate --json --apply` and `policy populate --json --apply` now persist questionnaire updates
- Larger Codex subprocess line buffer for policy questionnaire responses

## 0.8.0 - 2026-03-07

### Added

- MCP `pretorin_generate_control_artifacts` for read-only AI drafting of control narratives and evidence-gap assessments
- Shared AI drafting workflow helper for structured MCP/CLI parity

### Changed

- MCP system-scoped tools now resolve friendly system names the same way the CLI does
- Codex Desktop MCP configuration can be pinned to the UV-managed Pretorin wrapper

## 0.7.0 - 2026-03-07

### Fixed

- Control implementation parsing tolerant of `notes: null` deployments
- Compatibility fallback for control note reads when `/notes` endpoint returns `405`
- Compatibility fallback for evidence search on system-scoped evidence routes
- Agent `--no-stream` crash on literal `[[PRETORIN_TODO]]` blocks

### Changed

- MCP and legacy agent evidence search tools accept optional `system_id` context

## 0.6.1 - 2026-03-05

### Fixed

- Added required MCP registry ownership marker for PyPI validation

## 0.6.0 - 2026-03-05

### Added

- Shared markdown quality validator for auditor-readable artifacts
- Dedicated tests for markdown quality guardrails
- CLI/MCP/agent parity for reading notes via dedicated endpoint

### Changed

- Narrative and evidence update flows enforce markdown quality checks before push/upsert
- Agent prompts require auditor-ready markdown (lists/tables/code/links)
- Source tagging normalized to `cli` across write paths

### Removed

- Markdown image usage from narrative/evidence authoring contract (temporarily)

## 0.5.4 - 2026-03-05

### Added

- `pretorin narrative get` to read current control narratives
- `pretorin notes list` and `pretorin notes add` for control-note management
- `pretorin evidence search` for platform evidence visibility
- `pretorin evidence upsert` for find-or-create evidence with control linking
- Shared compliance workflow helpers (system resolution, evidence dedupe/upsert, TODO blocks, gap notes)
- MCP `pretorin_get_control_notes` tool

### Changed

- `pretorin_create_evidence` now upserts by default (`dedupe: true`)
- `pretorin evidence push` uses find-or-create upsert logic
- Agent skill prompts include no-hallucination guidance and gap note format

### Removed

- Automatic control status updates from CLI evidence push workflow

## 0.5.3 - 2026-03-02

### Fixed

- CI lint failure formatting
- CLI model key precedence: `OPENAI_API_KEY` → `config.api_key` → `config.openai_api_key`

## 0.5.2 - 2026-02-27

### Fixed

- Rich markup `MarkupError` crash in login flow
- Evidence type mismatch (`documentation` → `policy_document`)
- CMMC control ID casing preserved (no longer incorrectly lowercased)
- `monitoring push` checks active context before requiring `--system`
- `pretorin login` skips prompt when already authenticated
- Demo script `--json` flag position and stdin handling

### Changed

- Default evidence type changed to `policy_document`
- Valid evidence types aligned with API
- Added `.pretorin/` and `evidence/` to `.gitignore`

## 0.5.0 - 2026-02-27

### Added

- Context management (`context list/set/show/clear`)
- Evidence commands (`evidence create/list/push/search/upsert`)
- Narrative push (`narrative push`)
- Monitoring events (`monitoring push`)
- Codex agent runtime (`agent run` with skills, `agent doctor/install/version/skills`)
- Agent MCP management (`agent mcp-list/mcp-add/mcp-remove`)
- Code review (`review run/status`)
- 14 new MCP tools for system, evidence, narrative, monitoring, notes, and control operations
- Control ID normalization (zero-padding)
- Interactive demo walkthrough script
- Beta messaging across CLI, MCP, and README

### Changed

- Platform API base URL changed to `/api/v1/public`
- Evidence and linking scoped to system
- `update_control_status()` changed from PATCH to POST

### Removed

- `pretorin narrative generate` — use `pretorin agent run --skill narrative-generation`
- `pretorin_generate_narrative` MCP tool

### Security

- MCP mutation handler parameter validation
- Client-side enum validation
- Path traversal protection in evidence writer
- TOML injection prevention in Codex config writer
- Connection error URL display

## 0.2.0 - 2026-02-06

### Added

- `--json` flag for machine-readable output
- `pretorin frameworks family/metadata/submit-artifact` commands
- Full AI Guidance rendering on control detail view
- `.mcp.json` for Claude Code auto-discovery
- Usage examples in command docstrings

### Changed

- Control references shown by default (replaced `--references` with `--brief`)
- Default controls limit changed to 0 (show all)

## 0.1.0 - 2025-02-03

### Added

- Initial public release
- CLI commands for browsing compliance frameworks
- Authentication commands (login, logout, whoami)
- Configuration management
- MCP server with 7 tools and analysis resources
- Self-update functionality
- Rich terminal output with Rome-bot mascot
- Docker support
- GitHub Actions CI/CD
- Integration test suite

### Supported Frameworks

- NIST SP 800-53 Rev 5
- NIST SP 800-171 Rev 2/3
- FedRAMP (Low, Moderate, High)
- CMMC Level 1, 2, and 3