Not recorded yet. The lesson below is complete and runnable today — the video is added later without touching this page.
recordings/E3.2.mp4The investigation is itself the breach: an agent granted broad read to 'find the problem' exfiltrates more than the incident did.
A declared admission set per investigation class, enforced at the tool boundary and logged, with anything outside it requiring a human grant.
Run an investigation against an admission set and watch the out-of-scope queries get refused and recorded.
The investigating agent is granted broad read across production so it can find the problem. Broad read across production is frequently what the problem was. Admission rules are how the response avoids becoming the second incident.
Investigation declares its admission set per class, refuses anything outside it, and requires a named human and a reason to grant more. The grant list is the part an assessor reads.Scoping the investigation itself, because an agent granted broad read to find the problem moves more data than most incidents do — a declared admission set per class, enforced at the tool boundary, with named human grants for anything beyond it.
Incident response grants the broadest read in the organisation at the moment of least supervision — and that grant is often larger than the incident.
Bound the investigating agent per investigation class before it runs: sources, forbidden fields, volume cap.
This one spends time on purpose. Measure refusals logged with their query — the evidence you stayed on the right side of the line.
Measure this
| what to measure | denominator or target |
|---|---|
| refusals logged with their query | count; the evidence you stayed on the right side of the line |
| interval spent on purpose | minutes added to the clock, stated rather than hidden |
investigation class: agent-misuse
+--------------------------------------------------+
| sources agent.traces gateway.logs tool.audit | allowlist
| fields NOT payment_card, passport_no, | denied
| message_body |
| volume <= 5000 rows | cap
+--------------------------------------------------+
|
query --+--> admitted -> runs, logged
|
+--> refused -> logged WITH the query text
-> a human can grant it deliberately
the volume cap is the one people leave out: same source, same fields,
400 rows is an investigation and 90,000 is a copyAn investigating agent is handed broad read across the estate so it can find the problem. Broad read across the estate is frequently what the problem was.
Admission rules decide, per investigation class and before anything runs, which sources the agent may reach, which fields it may never see, and how much it may pull. The volume cap is the one people leave out: the same query over the same fields is an investigation at four hundred rows and a copy at ninety thousand.
Anchor → E1.0. This one spends the interval on purpose. Bounding the investigator before it runs is slower than handing it broad read, and broad read across the estate is frequently what the incident was — so the time buys not having a second one.
When a query is refused, the refusal keeps the exact text of the query. That means a human can look at it and grant it on purpose — and the granting is on the record too.
This matters twice over.
First, the investigator can ask for one specific thing instead of asking for "more access".
Second, the refusal log is what shows afterwards that the response did not turn into a second incident. A regulator will ask that question about any agent that read production during an outage.
Three are refused. Look at the third: same source and same fields as an allowed query, refused on volume alone.
The skill — skills/secops/investigation-admission-rules/SKILL.md
name: investigation-admission-rules
description: >-
Decide and enforce which sources, fields and volumes an investigating agent
may reach, before the investigation starts, and record every refusal. Use when
granting an agent read access for incident response, when investigation
queries touch personal data, or when an investigation's own access needs to be
auditable.
allowed-tools: Read, Grep, GlobAn investigating agent is handed broad read across the estate so it can find the problem. Broad read across the estate is frequently what the problem was.
Admission rules decide, per investigation class and before anything runs, which sources it may reach, which fields it may never see, and how much it may pull.
Before an agent-driven investigation touches production data, and whenever an investigation class is defined. Also after the fact: the refusal log is the evidence that the response did not become its own incident.
1 — Classify the investigation first. "Agent misuse" and "data exfiltration" need different sources. One admission set for everything is no admission set.
2 — Name the sources positively. An allowlist. A denylist of sources is a list of the ones somebody thought of.
3 — Deny fields, not just sources. The trace table is admissible; the message body inside it usually is not.
4 — Cap the volume. The same query over the same fields is an investigation at 400 rows and a copy at 90,000.
5 — Record refusals as evidence, not errors. A refusal carries the exact query, so a human can grant it deliberately.
Input — one admission set and six queries, in scripts/investigation_admission_rules.py.
Output — the refusals from a real run:
bookings.db owner_id,payment_card 8000 REFUSE — source not admitted for this class
agent.traces agent,message_body 900 REFUSE — denied field: message_body
gateway.logs src,route 90000 REFUSE — 90000 rows exceeds the 5000 cap
The third is refused on volume alone — same source, same fields as an allowed query.
{
"allowed": [{"source": "str", "fields": ["str"], "rows": 0, "why": null}],
"refused": [{"source": "str", "fields": ["str"], "rows": 0, "why": "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 E3.2Then restart your agent and open it in that folder. On Windows, if python3 says Python was not found, use python instead.
Then pick e3-2-admission-rules:
| agent | install just for this agent | start it |
|---|---|---|
| Claude Code | python3 scripts/install_skills.py --tool claude --lessons E3.2 | type /e3-2-admission-rules |
| GitHub Copilot (VS Code) | python3 scripts/install_skills.py --tool copilot --lessons E3.2 | type /e3-2-admission-rules in Chat |
| Cursor | python3 scripts/install_skills.py --tool cursor --lessons E3.2 | type / in Agent chat and search for it |
| Codex | python3 scripts/install_skills.py --tool agents --lessons E3.2 | type $e3-2-admission-rules, 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 E3.2Neither 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 E3.2 --out work/cybertravels
python3 scripts/checkpoint.py --at E3.2 --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.
Three queries allowed and three refused — one on an inadmissible source, one on a denied field, and one on volume alone.
Write the admission set for a data-exfiltration investigation. It is not the same set, and working out why is the exercise.
Tools used
Skill source: skills/secops/investigation-admission-rules