Not recorded yet. The lesson below is complete and runnable today — the video is added later without touching this page.
recordings/F1.4.mp4Inventing new controls where an existing one applied to a new principal type.
Map identity, secrets, sandbox, eval and telemetry onto the existing library.
Map the B2/B3 controls onto your control library.
Most agentic risks map onto controls you already have. Building a second, parallel control estate for AI is the most common and most expensive mistake in this function — the work is finding the genuine gaps, not restating the overlap.
map_outward() answers the question the framework was for: which clauses nothing covers. And needs_a_new_control() checks the cheaper thing first — five of the controls you already operate apply to an agent principal unchanged, and a new control is a new thing to maintain and evidence forever.Mapping controls outward to frameworks rather than inward from them, so the output is which clauses nothing covers — and the prior question, which is whether an existing control already applies to a new principal type.
A framework-first mapping produces a complete checklist that defends nothing, because a clause with no operating control behind it evidences nothing.
Map control to framework, never the reverse, and let coverage fall out as an output.
Mapped controls with an indicator behind them. Those without are unevidenced, and most tooling renders them green.
Measure this
| what to measure | denominator or target |
|---|---|
| mapped controls with an indicator behind them | share of mapped controls; those without are unevidenced and most tooling renders them green |
agentic risk existing control gap? +-------------------+ +------------------+ +-----+ | prompt injection | | input validation | | yes | | over-privilege | | least privilege | | no | | no attribution | | audit logging | | yes | +-------------------+ +------------------+ +-----+ the work is finding the "yes" rows, not restating the "no" ones
Control mapping runs one way: control → framework.
Starting from the framework produces a checklist that is complete, satisfies an assessor, and defends nothing — because it enumerates clauses rather than capabilities, and a clause with no operating control behind it evidences nothing.
Starting from controls produces the opposite: a smaller list of things you actually do, each of which happens to satisfy several framework clauses. The framework coverage is an output, and that is the only mapping that survives a supervisor asking "show me".
Anchor → F1.1. Control mapping is where an indicator gets its subject. A mapped control with no indicator behind it is the state F1.1 calls unevidenced: it is neither passing nor failing, and most GRC tooling will render it green.
The skill starts from eight controls you actually operate, and maps outward to the clauses they satisfy across five frameworks. That direction matters — starting from the frameworks and working back produces a wish list.
For each control it attaches the evidence artefact you would be asked to show.
Then it works out what a critical tier requires. Coverage comes out as a result you computed, rather than a claim you made.
The skill — skills/grc/control-to-framework-mapping/SKILL.md
name: control-to-framework-mapping
description: >-
Map a control catalogue outward to framework clauses — never the reverse — and
assemble the evidence pack a given risk tier requires. Use when a framework
checklist is being turned into a programme, or when coverage is claimed
without artefacts.
allowed-tools: Read, Grep, GlobStarting from the framework produces a checklist that is complete, satisfies an assessor, and defends nothing: it enumerates clauses rather than capabilities, and a clause with no operating control behind it evidences nothing. Starting from controls produces a smaller list of things you actually do, each of which happens to satisfy several clauses.
Building a control catalogue, responding to a framework mapping request, or auditing a coverage claim.
1 — Write the catalogue first, as operating controls. Each one a mechanism somebody runs, with an owner. If a row cannot be described as something that runs, it is a policy statement and belongs elsewhere.
2 — Map each control outward. One control to many clauses across every framework you report against. The one-to-many direction is what makes this cheaper than it looks.
3 — Attach the evidence artefact per control. A log, an export, a test result, a signed attestation — the thing an assessor would be handed. A control with no artefact is unevidenced whatever its status says.
4 — Derive requirements per tier. Which controls a critical-tier system must have, which a medium one. Then count the clauses that follow, rather than promising clause coverage directly.
5 — Report coverage as an output. "These 8 controls satisfy 12 clauses" is defensible; "we cover 12 clauses" invites the follow-up you cannot answer.
Input — the fixture committed at the top of scripts/control_to_framework_mapping.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:
control kind satisfies
------------------------------------------------------------------------------------
AC-1 preventive 3 clause(s): NIST AI RMF: GOVERN-1.2
ISO 42001: 6.1
EU AI Act: Art.14
AC-2 preventive 2 clause(s): NIST AI RMF: MANAGE-2.2
ISO 42001: 8.1
SB-1 preventive 2 clause(s): NIST AI RMF: MANAGE-2.1
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.
{
"catalogue": [{"id": "str", "control": "str", "owner": "str", "artefact": "str"}],
"mapping": [{"control": "str", "framework": "str", "clauses": ["str"]}],
"tiers": [{"tier": "str", "requires": ["str"], "clauses_satisfied": 0}],
"coverage": {"controls": 0, "clauses": 0, "unevidenced": ["str"]}
}
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.
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.4Then restart your agent and open it in that folder. On Windows, if python3 says Python was not found, use python instead.
Then pick f1-4-control-mapping-for-agents:
| agent | install just for this agent | start it |
|---|---|---|
| Claude Code | python3 scripts/install_skills.py --tool claude --lessons F1.4 | type /f1-4-control-mapping-for-agents |
| GitHub Copilot (VS Code) | python3 scripts/install_skills.py --tool copilot --lessons F1.4 | type /f1-4-control-mapping-for-agents in Chat |
| Cursor | python3 scripts/install_skills.py --tool cursor --lessons F1.4 | type / in Agent chat and search for it |
| Codex | python3 scripts/install_skills.py --tool agents --lessons F1.4 | type $f1-4-control-mapping-for-agents, or run /skills |
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.4Neither 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.4 --out work/cybertravels
python3 scripts/checkpoint.py --at F1.4 --diffOn 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.
The catalogue's 8 controls map to framework clauses across NIST AI RMF, ISO 42001, ISO 27001, the EU AI Act and DORA. Critical tier requires all 8 and satisfies 12 clauses; medium requires 5. Working framework-first leaves 3 of 7 clauses with no operating control. The evidence pack shows one artefact satisfying several clauses.
Take one framework clause your programme claims to satisfy and ask which operating control produces its evidence. If the answer is a policy document, the clause is ticked and undefended.
Tools used
Skill source: skills/grc/control-to-framework-mapping