This starts chapter A2. Its course guide is A2 in the Function A course guide →
Not recorded yet. The lesson below is complete and runnable today — the video is added later without touching this page.
recordings/A2.0.mp4A demo promoted to production carries none of the machinery an incident needs, and the first investigation discovers that at the worst moment.
Spans, an audit trail, an evaluation, and a stop — built before they are needed.
List what your run currently cannot tell an operator.
The prototype is promoted with nobody changing a line of it. Three weeks later a traveller disputes a cancellation and the question is what the agent actually did. The answer is in a log line that says run completed. Everything needed to answer it was cheap to add and is now expensive, and none of it was ever a feature request.
What separates a demo from a system: a trace, an audit trail and an evaluation. The lesson measures the gap before building any of them, because the list is shorter and more useful than "add logging".
A demo promoted to production carries none of the machinery an incident needs, and the first investigation finds that out at the worst moment.
Establish the gap first: put an investigation's questions to what your run currently emits.
Questions the current record cannot answer. Expect most of them, which is the point of measuring before building.
Measure this
| what to measure | denominator or target |
|---|---|
| investigation questions the current record cannot answer | count out of three — which human, what motivated it, which hop; expect all three before you build |
DEMO ──────────────────────► SYSTEM what did it do? answer only ─► a trace who caused it? one identity ─► an audit trail is it still right? checked once ─► an evaluation all three are cheaper now than after the first incident
You have a working agent. It is not yet a system, and the difference is not features — it is the machinery that lets somebody who is not you operate it.
Three capabilities separate the two, and a demo has none of them:
| capability | what it needs | where it goes wrong | built in |
|---|---|---|---|
| what did it do? | a trace | the run emitted only its answer | A2.1 |
| who caused it? | an audit trail | one shared identity in every row | A2.2 |
| is it still right? | an evaluation | it was checked by hand, once | A2.3 and A2.4 |
Those are the three things you build next. The skill in this lesson scores something different and narrower — the three questions an investigation asks of a record that already exists: which human, what motivated it, which hop originated it. Keep the two triples apart: the first is the chapter's plan, the second is the number you are about to measure, and it is a number out of three.
Every one of those is cheaper to build now than after the first incident, and all three are load-bearing for security later. A detection rule needs something to read. An investigation needs a record that can answer a question nobody asked in advance. A red-team finding needs a regression case to become a control rather than a memory.
That is the argument for this chapter sitting before Function B rather than inside it: the security work in the rest of the commons assumes these exist. Build them while the system is small enough that adding them is an afternoon.
Nothing. The notebook worked, somebody wrapped it in a service, and the first investigation discovers at the worst possible moment that the run left no record. It is the most common failure in this whole subject and it has nothing to do with models.
Before building any of it, establish the gap. The skill below puts an investigation's questions to an existing record and reports which ones it cannot answer — which is a shorter and more useful list than "add logging".
The skill — skills/threats/audit-answerability-check/SKILL.md
name: audit-answerability-check
description: >-
Put an investigation's three questions to an existing agent log — which human,
what motivated the action, which hop originated it — and record which of them
the record cannot answer. Use when reviewing agent telemetry before an
incident rather than during one.
allowed-tools: Read, Grep, GlobAgent logs are usually complete in the sense that every tool call is present. They are useless in the sense that the three questions an investigation opens with have no fields behind them. The check is not "are we logging" — it is "which question dies here".
Before an incident. Run it against a real log line from production, not against the logging design.
1 — Take one real record. A single tool-call row, with every field it actually carries. Not the schema — the row.
2 — Ask: which human? Is there a field naming the principal on whose behalf this ran? An agent identity is not an answer; it is the thing that ran.
3 — Ask: what motivated it? Which input caused this call — the user's request, a retrieved document, a tool result, a memory record. Without it you cannot tell an instructed action from an injected one, and that is the distinction the whole investigation turns on.
4 — Ask: which hop originated it? In any multi-agent or delegated flow, which agent started the chain. A chain reconstructed by correlating timestamps across four services is a chain you will not reconstruct at 2am.
5 — Report per question, with the field that would answer it. Three rows. Each says: answerable yes/no, and the field to add. Anything vaguer produces a logging project rather than a fix.
Input — the fixture committed at the top of scripts/audit_answerability_check.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:
the log you have:
09:14:02 agent-svc search {'q': 'invoice 8812'}
09:14:07 agent-svc fetch_doc {'id': 'wiki/473'}
09:14:11 agent-svc run_query {'sql': 'DELETE FROM invoices WHERE id=8812'}
09:14:12 agent-svc send_email {'to': 'ops@corp.example'}
question field needed present?
which user caused the deletion? principal NO
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.
{
"record": {"fields": ["str"]},
"questions": [{"question": "which human|what motivated|which hop", "answerable": false, "missing_field": "str"}],
"answerable_count": 0,
"reconstruction": {"possible": false, "requires_correlating": ["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 A2.0Then restart your agent and open it in that folder. On Windows, if python3 says Python was not found, use python instead.
Then pick a2-0-why-a-demo-is-not-a-system:
| agent | install just for this agent | start it |
|---|---|---|
| Claude Code | python3 scripts/install_skills.py --tool claude --lessons A2.0 | type /a2-0-why-a-demo-is-not-a-system |
| GitHub Copilot (VS Code) | python3 scripts/install_skills.py --tool copilot --lessons A2.0 | type /a2-0-why-a-demo-is-not-a-system in Chat |
| Cursor | python3 scripts/install_skills.py --tool cursor --lessons A2.0 | type / in Agent chat and search for it |
| Codex | python3 scripts/install_skills.py --tool agents --lessons A2.0 | type $a2-0-why-a-demo-is-not-a-system, 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 A2.0Neither 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 A2.0 --out work/cybertravels
python3 scripts/checkpoint.py --at A2.0 --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 questions — which human, what motivated it, which hop — checked against what the record currently holds, each with the field that would answer it and whether that field is present. Expect all three to come back unanswerable: the log has an actor of agent-svc on every row and no motivating input at all. That is the point of running this before you build. A2.2 then designs the record that answers these three and a fourth — which call, with its audience and scope — and scores four out of four on the same incident.
Answer the same questions about a system you actually work on. The gap is usually wider than for the agent you just built, because nobody chose it.
Tools used
Skill source: skills/threats/audit-answerability-check