# SSP CSV manifest schemas — column contracts for `ssp_compile`

Locked column contracts for the flat CSV **structural manifests** that author and review an SSP
before compilation. These manifests are the human authoring/review projection over the
[[halo-action-pipeline]]: `ssp_compile.py` reads them, validates each against the contracts below
(`--check` gate), and emits the executable `actions.jsonl`. **The JSONL stays the source of truth
for execution** — the CSVs are the reviewable projection, regenerable and diffable.

**Row key across every manifest = AFUUID.** One row per asset (or per relationship pair, for the
rel manifest). The compiler is pure and deterministic: it never calls MCP. It consumes these
manifests plus an agent-produced tenant snapshot CSV and a library-lookup CSV.

## Scope — what is and isn't a CSV cell

- **Structural data → CSV.** Field values, enums, framework attribution, AFUUIDs, flags.
- **Narratives → companion markdown, never a CSV cell.** The IS `AFImplementation Statement`
  payload, the SP long `AFParameter Value` prose, `AFValidation Process`, and the Site
  `system_information` blocks all live in AFUUID-keyed companion `.md` files. A CSV cell holds at
  most a short one-liner (`description`, `purpose`), never a narrative surface.
- Field-level dictionary for every asset type referenced here is [[halo-ssp-asset-model]] /
  `reference-docs/halo-ssp-asset-schemas.md`. Write discipline is
  [[halo-ssp-asset-write-conventions]]. Import-sheet mechanics are [[halo-asset-import-spreadsheet]].

## AFUUID conventions

All AFUUIDs follow [[halo-ssp-asset-write-conventions]] verbatim:

| Asset | AFUUID pattern |
|---|---|
| Component (173) | `CMPN-<CFComponentAcronym>` (library) / `CMPN-<NAME>` (off-library) |
| Implementation Statement (191) | `IS-<CFISAcronym>` (library) / `IS-<CMPN>-<ACRONYM>` (off-library) |
| Set Parameter (193) | `SP-<SystemName>-<CATALOGSHORT>-<CONTROL>[-<REQSUFFIX>]-<IDX>` |
| Control Implementation (192) | `CI-<SystemName>-<CATALOGSHORT>` |
| Implemented Requirement (190) | `IR-<SystemName>-<CATALOGSHORT>-<CONTROL>` |

`<CATALOGSHORT>` resolves from the baseline's short code (`FR5CC`, `FR20XCC`, `CMMCL2`).
`<SystemName>` comes from `system_information.System Name`.

## library vs off-library semantics

Every Component and Implementation Statement row carries a `library_flag` of `library` or
`off-library`:

- **`library`** — the asset instantiates a marketplace/library definition. The row must supply the
  library key (`library_component_key` / `library_is_key`), and the compiler resolves the
  authoritative title, description, and (for IS) the narrative from the library at compile time. The
  AFUUID uses the library acronym (`CMPN-<CFComponentAcronym>`, `IS-<CFISAcronym>`).
- **`off-library`** — a tenant-specific asset with no library parent. The library key is empty; the
  narrative must come from a companion file.

### IS narrative model

The IS narrative (`AFImplementation Statement`, a long payload) is never a CSV column. Each IS row
declares how its narrative resolves:

| Column | Values / rule |
|---|---|
| `narrative_status` | `library` \| `agent-draft` \| `human-reviewed` |
| `narrative_sha` | content hash of the companion file captured at review time; **empty for `library` rows** |
| `companion_file` | rel path to the AFUUID-keyed companion `.md` holding the narrative; **empty for `library` rows** |

- `library` rows resolve the narrative from `lookup_implementation_statement_definition` — no
  companion file, no sha.
- `agent-draft` and `human-reviewed` rows read the narrative from `companion_file`; the compiler
  compares `narrative_sha` against the live companion hash and routes any mismatch to the review
  gate (see Fail-loud conditions).

---

## 1. components.csv — Component (asset type 173, tag `CMPN`)

| column | maps to (asset field / CF) | required | notes |
|---|---|---|---|
| `afuuid` (PK) | `AFUUID` | yes | `CMPN-<CFComponentAcronym>` (library) / `CMPN-<NAME>` (off-library) |
| `library_flag` | — | yes | `library` \| `off-library` |
| `library_component_key` | `update_component_from_library` key | if library | empty off-library |
| `title` | `AFTitle` | yes | |
| `component_type` | `AFComponent Type` | yes | `System`\|`Interconnection`\|`Software`\|`Hardware`\|`Service`\|`Policy`\|`Physical`\|`Process/Procedure`\|`Plan`\|`Guidance`\|`Standard`\|`Validation`\|`Network`\|`Document` |
| `description` | `AFDescription` | no | short one-liner (NOT a narrative surface) |
| `purpose` | `AFPurpose` | no | short |
| `cmmc_asset_type` | `AFCMMC Asset Type` | no | `SPA`\|`CUI`\|`SPD`\|`CRMA`\|`OOS` |
| `provider` | `AFProvider` | no | |
| `fedramp_controls` / `ksi_families` / `cmmc_controls` | CF 382 / 384 / 383 | no | usually EMPTY on a Component (attribution lives on the IS) |
| `status` | asset status | no | default `Active` |
| `notes` | — | no | reviewer-only, compiler ignores |

## 2. cap-component-rel6.csv — Capability↔Component, rel 6 (VALIDATED, NOT EMITTED)

| column | maps to (asset field / CF) | required | notes |
|---|---|---|---|
| `capability_afuuid` (FK) | rel 6 parent | yes | references an EXISTING reconciler-owned Capability (validated against snapshot) |
| `component_afuuid` (FK) | rel 6 child | yes | → `components.csv` |
| `notes` | — | no | reviewer-only |

The compiler groups by `capability_afuuid` and emits **one** `set_asset_relationships` per
Capability carrying the FULL child list — rel 6 is full-replace on the parent's child set. Never
emit per-pair, or you orphan the Capability's other children.

## 3. odp-parameters.csv — Set Parameter (asset type 193, tag `SP`)

| column | maps to (asset field / CF) | required | notes |
|---|---|---|---|
| `afuuid` (PK) | `AFUUID` (209) | yes | `SP-<SystemName>-<CATALOGSHORT>-<CONTROL>[-<REQSUFFIX>]-<IDX>` |
| `title` | `AFTitle` (213) | yes | value-FREE handle, e.g. `AU-11 audit-record retention period` |
| `parameter_index` | `AFParameter Index` (237) | yes | integer, 1-based |
| `parameter_label` | `AFParameter Label` (238) | yes | |
| `parameter_scope` | `AFParameter Scope` (239) | yes | `Control` (parent IR) \| `System` (parent CI) |
| `parameter_value` | `AFParameter Value` (236) | yes | the chosen value (data, not narrative); long prose → companion |
| `baseline` | `CFBaseline` (503) | yes | |
| `fedramp_controls` / `ksi_families` / `fedramp_rules` / `cmmc_controls` | CF 382 / 384 / 597 / 383 | controls tier (≥1) | the control/rule whose ODP this fills; 20x SPs attribute by `fedramp_rules` (597) |
| `fedramp_requirements` / `cmmc_requirements` | CF (Requirements) | requirements tier | third CF phase, after the controls tier |
| `parent_afuuid` | — | yes | IR (`Control`) or CI (`System`) — **REVIEW/VALIDATION ONLY**; compiler validates it resolves but emits NO rel action (rel 12/11 are reconciler-owned) |
| `status` | asset status | no | |
| `notes` | — | no | reviewer-only |

Per SP the compiler emits: create (guard on AFUUID) → update AFs → three-phase CF write (`CFBaseline`
alone, then the controls tier, then the requirements tier). **No parent rel action.**

## 4. is-manifest.csv — Implementation Statement (asset type 191, tag `IS`)

One row per Component × control.

| column | maps to (asset field / CF) | required | notes |
|---|---|---|---|
| `afuuid` (PK) | `AFUUID` | yes | `IS-<CFISAcronym>` (library) / `IS-<CMPN>-<ACRONYM>` (off-library) |
| `component_afuuid` (FK) | rel 8 source | yes | → `components.csv`; rel 8 derived from AFUUID stem, this column validates it |
| `library_flag` | — | yes | `library` \| `off-library` |
| `library_is_key` | `lookup_implementation_statement_definition` key | if library | empty off-library |
| `narrative_status` | — | yes | `library` \| `agent-draft` \| `human-reviewed` |
| `narrative_sha` | — | if not library | content hash of companion file at review time; empty for library rows |
| `companion_file` | — | if not library | rel path to AFUUID-keyed companion `.md` holding the `AFImplementation Statement` narrative |
| `title` | `AFTitle` | yes | `{Component} – {phrase}` en-dash, no control/KSI IDs |
| `implementation_state` | `AFImplementation State` | yes | `Implemented`\|`Partial`\|`Planned`\|`Alternative`\|`Not Applicable`\|`Not Implemented` |
| `control_origination` | `AFControl Origination` | no | multi: `Organization`\|`System Specific`\|`Customer Configured`\|`Customer Provided`\|`Inherited` |
| `responsible_role` | `AFResponsible Role` | no | `Self`\|`Third Party`\|`Managed Service Provider`\|`Cloud Service Provider` |
| `baseline` | `CFBaseline` (503) | yes | the instance's baseline envelope; always manifest-authored (not library-sourced) |
| `fedramp_controls` / `ksi_families` / `fedramp_rules` / `cmmc_controls` | CF 382 / 384 / 597 / 383 | controls tier | framework attribution (≥1 for off-library). Drives IR→IS reconciler match + parent IR sync. **20x sites attribute by `fedramp_rules` (597).** **Library rows leave all six blank — the compiler resolves them library-verbatim from the library-lookup CSV.** |
| `fedramp_requirements` / `cmmc_requirements` | CF (Requirements) | requirements tier | written in a THIRD CF phase AFTER the controls tier — Halo filters the Requirements lookup by the asset's current Controls value, so Requirements must land last. Library rows: library-sourced. |
| `validation_name` | `AFValidation Name` | no | short label |
| `validation_method` | `AFValidation Method` | no | `Machine Based`\|`Non-Machine Based` |
| `validation_pass_fail` | `AFValidation Pass/Fail Criteria` | no | short; long `AFValidation Process` prose → companion |
| `status` | asset status | no | |
| `notes` | — | no | reviewer-only |

The IS narrative (`AFImplementation Statement`, the long payload) is NEVER a CSV column: library
rows resolve it at compile from `lookup_implementation_statement_definition`; off-library and
authored rows read it from `companion_file`.

## 5. plan-review.csv — reverse projection (one flat row per emitted action)

The compiler's human-readable emission plan. Row count == emitted action count.

| column | maps to (asset field / CF) | required | notes |
|---|---|---|---|
| `id` | — | yes | action ordinal / id |
| `tool` | — | yes | pipeline tool name for the action |
| `asset_type` | — | yes | 173 / 190 / 191 / 192 / 193 |
| `afuuid` | `AFUUID` | yes | subject asset |
| `title` | `AFTitle` | yes | |
| `key_cfs` | CF summary | no | the framework CFs the action writes |
| `off_library_flag` | — | no | mirrors the source row's `library_flag` |
| `needs` | — | no | dependency / gate notes (e.g. companion sha, review gate) |

This manifest is emitted by the compiler for review; it is not authored by hand.

---

## 6. tenant-snapshot.csv — compile-time read-only tenant state (agent-produced)

The compiler is pure and offline — it never calls MCP. The tenant snapshot is produced UPSTREAM (an
agent runs small-scope read-only MCP reads, or the user exports a bulk CSV, e.g. a Halo report) and
passed in via `--snapshot`. It lets the compiler skip re-creates (idempotency), suppress unchanged
UPDATEs (changed-only emit), report drift (tenant value ≠ manifest value) as overwrite decisions, and
report orphans. **Wide shape: one row per existing SSP asset, keyed by AFUUID, with manifest-owned
field columns mirrored from the manifests.**

| column | required | notes |
|---|---|---|
| `afuuid` (PK) | yes | the existing asset's AFUUID |
| `asset_type` | recommended | 173 / 190 / 191 / 192 / 193 / 178 — drives orphan classification |
| `asset_id` | no | Halo numeric id, if the agent read it |
| *(manifest field columns)* | no | any comparable columns mirrored from the manifests — `title`, `component_type`, `description`, `purpose`, `cmmc_asset_type`, `provider`, `implementation_state`, `control_origination`, `responsible_role`, `parameter_index`, `parameter_label`, `parameter_scope`, `parameter_value`, `baseline`, `fedramp_controls`, `ksi_families`, `cmmc_controls`, `validation_name`, `validation_method`, `validation_pass_fail` |

**Coverage semantics (deterministic, graceful degradation):**

- A **minimal** snapshot (`afuuid` + `asset_type` only) still works: it enables create-skip + orphan
  detection, but NOT changed-only/drift — every existing row re-emits its UPDATE (the pre-increment
  behavior).
- A **wide** snapshot (manifest field columns present) enables no-op skipping + drift reporting. A row
  is a **no-op** (its UPDATE + CF actions are skipped) only when it exists AND every field the compiler
  would write is present in the snapshot AND matches. If the compiler writes a field the snapshot omits,
  the UPDATE is emitted (conservative — never assume unchanged).
- **Drift** = a comparable field present in the snapshot whose value differs from the manifest. Drift
  rows are REPORTED (what will be overwritten) and the UPDATE is emitted (manifest wins), but the run is
  approval-gated — the user may exclude any AFUUID via `--exclude-file`. Silent overwrite never happens.
  *Adopt-into-manifest (writing the tenant value back to the CSV) needs fold-back; deferred.*
- Multi-value framework CFs and `control_origination` are compared as sorted, comma-joined sets;
  whitespace-trimmed; empty == absent.
- **Narrative changes are NOT compared here** — they ride the is-manifest `narrative_sha` review gate.
  The snapshot carries structural fields only, never the long IS narrative.

**Producing the snapshot from `get_asset` (agent guidance).** `get_asset` returns Asset Fields under
`fields[]` keyed by `name` **without the `AF` prefix** (`"Component Type"`, `"Provider"`, `"Title"`, …)
with the human value in `.display`; Custom Fields are under `customfields[]` (`CFBaseline`,
`CFAFedRAMPRules`, …), also with `.display`. Map those to the snapshot columns above, and key each row on
the asset's **`inventory_number`** (the AFUUID surfaced by `asset_summary`), not the field-209 `UUID`
(which can differ). A component referenced by a rel-6 or IS row resolves if it is in `components.csv`
**OR** already present in this snapshot — partial manifests need not re-declare existing components.

## 7. library-lookup.csv — compile-time library resolution (agent-produced)

The resolution source for library-flagged Components + Implementation Statements, produced upstream by
an agent's read-only MCP reads (`lookup_implementation_statement_definition` / component library) and
passed in via `--library`. The compiler resolves library rows against this file and **fails loud on any
missing key** (naming the AFUUID).

| column | required | notes |
|---|---|---|
| `library_key` (PK) | yes | matches `library_component_key` / `library_is_key` in the manifests |
| `kind` | yes | `component` \| `is` |
| `title` | no | authoritative library title |
| `narrative` | IS only | the `AFImplementation Statement` payload for a library IS row |
| `fedramp_controls` / `ksi_families` / `fedramp_rules` / `cmmc_controls` | IS | the controls-tier framework lists (the compiler writes these for library rows) |
| `fedramp_requirements` / `cmmc_requirements` | IS | the requirements-tier lists (third CF phase) |
| `baseline` | no | usually empty — the definition is baseline-agnostic; the instance's baseline comes from the manifest row |

`lookup_implementation_statement_definition` returns all six pre-split framework lists (`FedRAMP Controls`,
`FedRAMP KSIs`, `FedRAMP Rules`, `CMMC Controls`, `FedRAMP Requirements`, `CMMC Requirements`) plus the
`Statement` narrative — map those columns straight into this CSV. For a **library-flagged** manifest row the
compiler sources the narrative AND all six framework CFs from here, not from the manifest.

---

## Reconciler-owned — NOT a manifest

The compiler owns **structural SSP assets and two relationships only**. Everything below is owned by
the reconciler (`reconcile_ssp_assets` / `reconcile_ssp_relationships_*`) and has NO manifest and NO
compile-emitted action:

- **Assets:** Capabilities (178), Control Implementations (192), Implemented Requirements (190).
- **Links:** CI→IR (rel 10), IR→IS (rel 7), IR→SP (rel 12), CI→SP (rel 11).

Compile-emitted relationships are **only**:

- **rel 6** — Capability↔Component: NOT emitted (reconciler-owned; `--emit-rel6-fallback` to override).
- **rel 8** — Component↔IS (derived from the IS AFUUID stem / `component_afuuid`).

**SP parent links (rel 12 `Control`-scope, rel 11 `System`-scope) are reconciler-owned.**
`odp-parameters.csv.parent_afuuid` exists for review and validation only: the
compiler confirms it resolves against the snapshot, then emits the SP with no parent rel action.

## Fail-loud conditions (`--check` gate)

Non-zero exit, no partial `actions.jsonl`, on any of:

- schema violation — unknown column, bad enum value, or missing required field;
- unknown AFUUID;
- dangling FK — rel6 capability/component, IS `component_afuuid`, or SP `parent_afuuid`;
- `library_flag=library` but no matching row in the library lookup CSV.

Two conditions route to a **gate** rather than a hard fail:

- `narrative_sha` mismatch on a non-library row → route the batch to the SSP review CHANGE-PLAN gate.
- orphan (a manifest row removed vs the snapshot) → REPORT row, never a delete action.
- drift on a manifest-owned row → decision item (adopt vs overwrite); the action file is not
  finalized until decided.

## Worked example

**components.csv**

```
afuuid,library_flag,library_component_key,title,component_type,description,provider
CMPN-ENTRAID,library,entra-id,Entra ID,Service,Cloud identity provider,Microsoft
CMPN-KMS,library,aws-kms,AWS KMS,Service,Managed key store,AWS
CMPN-IRPLAN,off-library,,Incident Response Plan,Plan,Documented IR process,
```

**cap-component-rel6.csv**

```
capability_afuuid,component_afuuid,notes
CAP-ACME-IAM,CMPN-ENTRAID,
CAP-ACME-CRYPTO,CMPN-KMS,
```

**is-manifest.csv**

```
afuuid,component_afuuid,library_flag,library_is_key,narrative_status,narrative_sha,companion_file,title,implementation_state,baseline,fedramp_controls
IS-KMS-ROT,CMPN-KMS,off-library,,human-reviewed,3f9a...,companions/IS-KMS-ROT.md,AWS KMS – key rotation,Implemented,FedRAMP Rev5 Class C,SC-12
IS-ENTRA-MFA,CMPN-ENTRAID,library,is-entra-mfa,library,,,Entra ID – multi-factor authentication,Implemented,FedRAMP Rev5 Class C,IA-02 (01)
```

**odp-parameters.csv**

```
afuuid,title,parameter_index,parameter_label,parameter_scope,parameter_value,baseline,fedramp_controls,parent_afuuid
SP-ACME-FR5CC-AU-11-1,AU-11 audit-record retention period,1,time period,Control,90 days,FedRAMP Rev5 Class C,AU-11,IR-ACME-FR5CC-AU-11
SP-ACME-FR5CC-AC-02-1,AC-02 inactivity disable window,1,time period,Control,30 days,FedRAMP Rev5 Class C,AC-02,IR-ACME-FR5CC-AC-02
```

## Compile emission order (write-conventions Rule 5, reconciler steps excluded)

1. `system_information` (from companion md, if present)
2. *(reconcile_ssp_assets — NOT emitted; run separately)*
3. Components: create (guard AFUUID) → update AFs (+ `update_component_from_library` if library) → three-phase CF
4. rel 6 Capability↔Component: SKIPPED (reconciler-owned); under `--emit-rel6-fallback`, one `set_asset_relationships` per Capability, full child list
5. Implementation Statements: create (guard) → update AFs (narrative from library \| companion) → three-phase CF
6. rel 8 Component↔IS: `set_asset_relationships` (derived from IS AFUUID stem / `component_afuuid`)
7. Set Parameters: create (guard) → update AFs → three-phase CF *(no rel 12/11 — reconciler-owned)*
8. *(reconcile_ssp_relationships_ci_ir / _ir_is / _ir_sp / _ci_sp — NOT emitted)*

## Related references

- [[halo-ssp-asset-model]] — SSP asset spine overview, relationship semantics, read-side audit.
- [[halo-ssp-asset-write-conventions]] — AFUUID rules, framework-CF write phases, Title Rule 4.
- [[halo-action-pipeline]] — the executable pipeline these manifests compile into.
- [[halo-asset-import-spreadsheet]] — import-sheet mechanics for asset writes.
