---
name: agent_bill_of_materials
description: Build an agent bill of materials for a deployed agent: inventory every component that can change its behaviour (base model and version, system prompts, skills with source and version, tools and connectors with their permissions, memory stores, sandbox and runtime, model router and fallback rules), record provenance and owner for each, diff the inventory when anything changes, and flag unpinned or unowned components before they cause an incident. Use when documenting a deployed agent, preparing an agent for review or handover, investigating why an agent's behaviour changed, or setting up drift checks across agent releases.
---

# Agent Bill of Materials

## Purpose

A software bill of materials lists packages. That is not enough for an agent. An agent's behaviour also changes when a system prompt is edited, a skill is bumped, a router sends more traffic to a fallback model, a memory store fills with stale summaries, or a connector quietly gains a new permission. None of those show up in a package list, and two of them have no component type at all in CycloneDX or SPDX, which is why prompts and tool graphs fall out of standard AIBOM work.

An agent bill of materials (ABOM) fixes that. It is a deployed-state inventory, in the CISA sense of a deployed or runtime BOM rather than a design BOM: what is actually running, pinned as it actually is, with a source, provenance evidence, and an owner for every row. It follows the NTIA minimum fields for a BOM (supplier, component name, version, unique identifiers, dependency relationships, author, timestamp) and the NIST SSDF practice PS.3.2 on collecting and sharing provenance data, and it adds three fields software BOMs do not carry: how the component can change behaviour, what permissions it holds, and what is not known about it.

## Workflow

1. **Fix the boundary.** One ABOM covers one agent in one environment at one deployment version. Write the agent id, version, environment, owner, and generation date first. If two deployments differ in any component, they get separate BOMs that can be diffed, not one averaged BOM.

2. **Collect from the live deployment, not from the design.** Work through `references/collector-checklist.md` layer by layer: model, prompts, skills, tools and connectors, memory and data, routing, guardrails, runtime, and sandbox. Take versions from lockfiles, config exports, registry entries, and deployed file hashes. A component someone remembers installing is a lead, not a row, until the deployment itself confirms it.

3. **Write each component with the full field set.** Use the schema in `references/inventory-schema.md`. Every component gets an id, type, name, deployed version, a pin, a source with supplier and locator, provenance evidence with who asserted it, an owner, and one plain sentence on how it can change behaviour. Tools and connectors also get their granted permissions, scope by scope, with the approval rule for every write. Routers get their fallback written out. Memory stores get retention and access. Never record a secret value anywhere in the file; secret names and locations only.

4. **Pin honestly.** Pin strength runs from a floating label (latest, main, current, which is no pin at all and fails validation), through a supplier version string and an immutable registry version or lockfile, to a git commit and a content hash you computed yourself. Prompts are pinned by hash or commit, never by version label alone. Record the pin you have, not the pin you wish you had.

5. **State the gaps.** For a closed model called over an API, record what you control (model id, endpoint, settings, your own evaluations), label provider-asserted facts as provider-asserted, and put the rest in `known_unknowns`. Do the same for a continuously updating knowledge corpus (snapshot date plus a drift note) and for any component you found but could not identify. A stated gap is a finding. A filled-in guess is a defect in the BOM.

6. **Validate.** Run `python3 bin/abom.py validate <abom.json>`. Errors mean the BOM is not done: unowned components, unpinned components, missing sources, tools with no permissions recorded, routers with no fallback. Treat each error as a fact about the deployment, not as paperwork. Read warnings to the owner: broad permission grants, write access with no approval rule, missing provenance evidence with no gap recorded. Use `--strict` in a release gate to fail on warnings too.

7. **Diff on every change, and re-collect on a schedule.** When a new deployment ships, collect a fresh BOM, validate it, and run `python3 bin/abom.py diff <old.json> <new.json>`. Act on the risk flags the diff raises: a model change means rerun the eval set before calling the agent unchanged, a new permission means a reviewer looks at the grant, a source change means provenance gets checked again, and a prompt hash change means someone diffs the prompt text. When behaviour changes and nobody knows why, the ABOM diff between the last good version and now is the first place to look. The worked example in `references/worked-example.md` shows that investigation end to end.

## Output Contract

A finished ABOM delivery contains:

- One JSON inventory per agent deployment, following `references/inventory-schema.md`, passing `bin/abom.py validate` with zero errors.
- A validation report: component count, errors, warnings, and the list of unpinned, unowned, and broad-permission findings with the owner assigned to each.
- A `known_unknowns` record, either inside components or as a short companion list, covering every fact that could not be verified.
- On updates, a diff report against the previous deployment's BOM, with risk flags and the action each flag triggered (eval rerun, permission review, provenance recheck, prompt text diff).
- Optionally, a CycloneDX or SPDX export for the components those formats cover (models, datasets, packages), using the mapping in `references/inventory-schema.md`, with a note that prompts, permissions, memory, and routing stay authoritative in the ABOM because the standard formats have no fields for them.

## Operating Rules

- Inventory what is deployed, not what was designed, planned, or intended. If collection finds a component nobody knew was running, that is the most valuable finding in the pass. Record it and assign it an owner in the same pass.
- No row without an owner, a source, and a pin. If one of the three cannot be produced, the component goes in with the gap written in `known_unknowns` and the validation finding stays visible until it is resolved. Do not delete the row to make the report clean.
- Never put a credential, token, key, or any secret value in an ABOM. Record that a credential exists, its kind, and where it is stored, by name only.
- Permissions are recorded as granted, not as offered. A connector that could delete records but is granted read-only access is a read-only row. A grant that grew since the last BOM is a permission widening and gets reviewed as one.
- A BOM is evidence only to the level of its pins and provenance. A hash you recomputed is a check. A version string a supplier told you is a claim. Keep the two apart in the provenance fields, the way SLSA provenance keeps builder-asserted facts apart from verified ones.
- One deployment version, one BOM, stored with the release record. Editing an old BOM after the fact destroys the diff. Corrections ship as a new BOM version with a new timestamp.
- Start a new BOM with `python3 bin/abom.py init --out abom.json --agent-id <id>`. The template fails validation on purpose until the empty fields are filled from the real deployment.
