All lessons › Function F — AI Governance for Agentic Systems › Building the Governance Framework — Risk and Control
F1.2

Building the AI and agent inventory

F1
▶
Lightboard recording

Not recorded yet. The lesson below is complete and runnable today — the video is added later without touching this page.

Video placeholderpublishes automatically from recordings/F1.2.mp4
Risk

Shadow AI and shadow agents — the inventory is the control most orgs still lack.

Control

Discovery, registration, ownership, risk tiering.

The lab

Discover agents from gateway and identity telemetry; build the register.

◉Use case relevance

Nobody can govern what nobody has listed. The inventory is the least interesting artefact in this function and the one everything else depends on — and the hard part is not building it, it is keeping it true next quarter.

At CyberTravels. inventory() comes from registry.all_workloads(), not a survey — and the reason is exact: a survey returns the agents somebody declared, which is the complement of the set you are looking for. shadow() wraps B2.5's orphans() and answers both directions, because teams check one.

◴What this lesson is — Day 0, Day 1, Day 2

Why an AI inventory built by survey returns the complement of the set you want, and how to derive one instead — plus the two directions of orphan, because teams check one.

Day 0 — why

You cannot govern, tier, test or revoke what you cannot list, and most of it is already in production.

Day 1 — how

Reconcile three sources — model registry, procurement, and egress logs to provider domains.

Day 2 — measure

The denominator for every other indicator. A share whose denominator is unknown is a count wearing a percentage sign.

Measure this

what to measuredenominator or target
the inventorythe denominator for every other indicator; a share whose denominator is unknown is a count wearing a percentage sign

▦The framework, and how it works

   what has to be in the register

   model . agent . integration . MCP server . eval corpus
        |
   +----v-------------------------------------------+
   | owner . purpose . data classes . autonomy level|
   | tools it holds . environment . last verified   |
   +------------------------------------------------+

   building it is a project. keeping it true is the control.

You cannot govern, tier, test or revoke what you cannot list. The AI inventory is therefore the first control, not a documentation exercise.

The honest finding of every first inventory is the same: most of it was already in production. Not because anyone was reckless, but because AI features arrive inside products you already bought, and agents get created programmatically by other agents.

Three sources, and the third finds what the first two miss:

  1. the model registry — what your ML team registered,
  2. procurement and expense — what someone bought,
  3. egress logs to model-provider domains — what is actually being used.

Source 3 is the one that discovers the department using a frontier API on a personal card, and the SaaS product that quietly added an AI feature.

Anchor → F1.1. The inventory is the denominator. Every indicator is a share — of agents, of tool calls, of handlers — and a share whose denominator is unknown is a count wearing a percentage sign. An incomplete inventory does not make your indicators wrong; it makes them unfalsifiable.

2 · Demo — build the inventory from three sources

3 · Where it breaks — the gap distribution is always like this

4 · The control — a discovery query you can re-run

5 · Resolving one row into a deployment, as a skill

Discovery finds that a thing exists. Governing it needs the row resolved into artefacts: which repository at which commit, which image digest rather than which tag, which IAM role and SPIFFE ID, which gateway and guardrail, and every downstream its tools call. Every other attestation skill consumes this graph, which is why it runs first. This is the file in this repository:

▶Real time execution as skill

The skill — skills/attestation/deployment-inventory-resolver/SKILL.md

name: deployment-inventory-resolver
description: >-
  Resolve a deployment_id into the artifacts that make up one agentic
  deployment and build the evidence graph every other attestation skill
  consumes. Use first, before any control is evaluated, or when asked which
  repo, image, role, workload identity, gateway or guardrail a deployment
  actually consists of.
allowed-tools: Bash, Read, Grep, Glob

Deployment Inventory Resolver

Controls: All — this skill produces the join key

Why this runs first

deployment_id is the primary key for every other skill in this set. Without it, an IAM finding, a SPIFFE entry and a gateway policy are three unrelated facts about three things that may or may not be the same system.

This skill turns that ID into a manifest of content-addressed artifacts, so every later finding resolves to one evaluable unit and cannot be silently confused with a neighbouring deployment.

When to use this

First, before any control is evaluated. Nothing else in this chain can run without a resolved deployment_id: an IAM finding, an identity entry and a gateway policy are three unrelated facts until something establishes they describe the same system. Also run it whenever anyone asks which repo, image, role, workload identity, gateway or guardrail a deployment actually consists of — that question is usually answered from memory and usually wrongly.

Procedure

  1. Resolve the code. Repository URL plus the exact commit SHA that was built. Not a branch — a branch moves.
  2. Resolve the image. Registry digest (sha256:…), not a tag. Confirm the digest matches the revision actually deployed, not the newest build.
  3. Resolve the runtime identity. The IAM role ARN, the SPIFFE ID, and — on platforms that auto-create one — the workload identity ARN exposed by the runtime/gateway description API.
  4. Resolve the traffic path. Gateway or route ARN, and the guardrail ID attached to it.
  5. Resolve the downstreams. Every service the deployment's tools call. These become the input to the risk-registry skill.
  6. Cross-link and check completeness. Every artifact must reference the others. Report orphans rather than omitting them.

Example

Input — the fixture committed at the top of scripts/deployment_inventory_resolver.py. Edit it and re-run: the buckets, counts and verdicts below are derived from it, not hard-coded.

Output — the opening lines of a real run:

asset                             kind              autonomy  owner         found via
--------------------------------------------------------------------------------------------
fraud-scoring-model               model             L1        risk-eng      registry
support-summariser                copilot           L1        support-eng   registry
vendor-contract-analyser          embedded-feature  L1        —             expense report
unknown-openai-usage-marketing    copilot           L1        —             egress logs
pr-remediation-agent              agent             L2.5      —             egress logs
agent-worker-7f3c                 agent             L2.5      —             egress logs

The run continues past this. test_skills.py executes the script on every build, with no model configured — so what CI proves is that it runs and refuses legibly, not that it prints these lines. The output above is a recorded run against a real model, and nothing re-diffs it: a model's answer is not reproducible, and this repository does not claim otherwise anywhere it can be checked.

Output contract

{
  "deployment_id": "str",
  "resolved_at": "str",
  "artifacts": {
    "repo": {"url": "str", "commit": "str"},
    "image": {"registry": "str", "digest": "str", "matches_deployed": true},
    "identity": {"role_arn": "str", "spiffe_id": "str", "workload_identity_arn": "str"},
    "traffic": {"gateway_arn": "str", "guardrail_id": "str"},
    "downstreams": ["str"]
  },
  "missing": ["str"],
  "orphans": ["str"],
  "verdict": "PASS|PARTIAL|FAIL"
}

A manifest with entries in missing is PARTIAL at best. Every downstream skill inherits that ceiling — you cannot attest a control on an artifact you could not resolve.

Failure modes

  • Resolving a tag instead of a digest. Tags are mutable; the thing you attested is not necessarily the thing running.
  • Treating an unresolvable artifact as absent. "No gateway configured" and "I could not read the gateway API" are different findings.
  • Reusing a manifest across runs. Re-resolve. Drift is the point.

Three ways to do this lesson. Pick one. They run the same procedure against the same code — what changes is who drives it, and how much you see.

1In your AI agent — you are taught

The agent reads the lesson, explains the idea, fetches the code, runs the check and answers your questions as you go. This is the one to take if you are here to learn.

Install it, once. From the folder you cloned. --all does every agent it finds on your machine:

python3 scripts/install_skills.py --all --lessons F1.2

Then restart your agent and open it in that folder. On Windows, if python3 says Python was not found, use python instead.

Then pick f1-2-building-the-ai-and-agent-inventory:

agentinstall just for this agentstart it
Claude Codepython3 scripts/install_skills.py --tool claude --lessons F1.2type /f1-2-building-the-ai-and-agent-inventory
GitHub Copilot (VS Code)python3 scripts/install_skills.py --tool copilot --lessons F1.2type /f1-2-building-the-ai-and-agent-inventory in Chat
Cursorpython3 scripts/install_skills.py --tool cursor --lessons F1.2type / in Agent chat and search for it
Codexpython3 scripts/install_skills.py --tool agents --lessons F1.2type $f1-2-building-the-ai-and-agent-inventory, or run /skills

2As one Python command — you just run it

No agent, no conversation. The same lesson executes and prints the same readback. Take this one if you have no assistant, if you are reviewing rather than learning, or if you want the result without the teaching around it.

python3 scripts/lesson.py F1.2

3Read the code, and the difference — nothing runs

Neither of the above, and no model needed. The first command writes CyberTravels exactly as it stood at the end of this lesson; the second prints the diff — the lines this lesson added or changed, and nothing from any later one.

python3 scripts/checkpoint.py --at F1.2 --out work/cybertravels
python3 scripts/checkpoint.py --at F1.2 --diff

On Windows, type python wherever these say python3 — and use \ instead of / in the paths. Everything else is identical. A0.0 step 2 explains why.

Ways 1 and 2 need a model; way 3 does not. A signed-in Claude Code CLI needs no API key, and any OpenAI-compatible endpoint works too. With neither, the audit exits 2 and says so rather than inventing an answer. A0.0 sets this up.

Ways 1 and 2 both end with the same readback: What I did, What changed, The number and Read this next. A step that was skipped or refused is reported as exactly that, never as a pass.

If you did the last lesson’s challenge, expect this to stop. Most challenges ask you to edit the code, and ways 1 and 2 will not overwrite your edits: you get stopped, not a failure, with two options — --out for a fresh folder that keeps your work, or --force to discard it and start this lesson clean.

✓What you just proved

The skill loads and reports its shape. Two of its rules are what make an inventory hold: resolve digests rather than tags, because a tag is mutable and the thing you attested is not the thing running; and never record an unresolvable artefact as absent — "no gateway configured" and "could not read the gateway" are different findings with different owners.

✎Your turn

Run the egress query for real: one week of traffic to model-provider domains, joined against your inventory. It takes an hour and it always finds something.

Tools used

agentgatewaySPIRE

Skill source: skills/attestation/deployment-inventory-resolver

← PreviousF1.1 · From framework control to key control indicator