* Convert audit-context-building to a dynamic workflow The skill held a fixed plan that ran the same steps over many functions and spawned subagents from prose. That is a workflow, so it is one now. workflows/audit-context.js orients, analyzes each function in its own subagent, and synthesizes a dossier. Each subagent writes its prose to audit-context/functions/ and returns a schema-validated record, so only the records reach the calling session. The skill routes to the workflow rather than analyzing inline. Also in this change: - Resolve a contradiction between SKILL.md and OUTPUT_REQUIREMENTS.md, which gave different minimum assumption counts for the same section while the agent was pointed at both. - Collapse OUTPUT_REQUIREMENTS.md and COMPLETENESS_CHECKLIST.md into one ANALYSIS_FORMAT.md. The per-function checklist existed in four places. - Drop the numeric quotas. Minimum counts of invariants, assumptions, and applications of a technique produce padding rather than analysis. - Delete commands/audit-context.md. Its --focus flag reached a skill that never accepted one, and its command name collided with the workflow's. - Add DOMAIN_NOTES.md mapping the format across smart contracts, C and C++, decompiled firmware, and web services. - Rework the worked example to cover C and Solidity. - Fix two README links to plugins that do not exist. - Rewrite user-facing text in plainer language. Add four eval cases under evals/, covering C source, Solidity, and Ghidra output. dispatches-not-inlines checks that the skill routes instead of analyzing in the caller's context; the other three check that analysis follows a call into the function being called and walks every path through it, not only the one that succeeds. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * Raise dispatches-not-inlines turn limit; note measurement gaps The plugin arm was hitting max_turns 20 and being truncated before it answered, which the graders scored as a failure to route. SKILL.md points at three reference files that are read before any work starts, so it needs roughly twice the turns a bare agent uses on the same prompt. Raised to 40, and the timeout to 900s after a run hit the old 600s ceiling. Restore the imperative wording in the routing section. The plainer phrasing was not the cause of the truncation, but this is the revision that was measured, so keep it. Record in the README what is and is not known: dispatches-not-inlines has not been re-measured since the turn limit changed, the Solidity and Ghidra cases score 1.00 with no plugin loaded, and the plugin arm costs about twice the turns of a bare agent. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com> Co-authored-by: kz-tob <kara.zaffarano@trailofbits.com>
Audit Context Building
Understand a codebase before you go looking for bugs in it.
Author: Omar Inuwa
What it does
It reads the code function by function and writes up three things about each one: what it assumes is already true, what it promises to whatever calls it, and what it depends on elsewhere. Those write-ups go into files. You get back a short summary and a list of the spots worth a closer look.
It does not report vulnerabilities. That is the next job, and it goes much better once this one is done.
Why this exists
Claude can already read a function and explain it well. This plugin is not here to teach it that. It is here for three things Claude cannot do for itself:
You don't have to remember it exists. A workflow only runs when someone types its name. This skill notices the situation instead — you are starting an audit, or opening a codebase nobody on the team knows — and starts the right one for you.
It splits the work up. One helper runs per function, all at the same time, each writing its notes straight to a file. Ask Claude to do the same job directly and it works through the functions one at a time, filling its working memory with the notes until there is no room left to actually use them.
Every write-up comes out the same shape. That matters most in one specific case: when the code counts on
something being true and nothing anywhere checks it. That always gets recorded the same way, with the words
nothing found. So you can search a whole codebase for that one phrase and get every such spot in a list.
Claude finds those spots on its own just fine — but describes each one differently, and forty differently
worded notes do not add up to a list.
If you are changing this plugin, keep that order in mind. The guidance in SKILL.md and resources/ is
sound practice and worth keeping, but it is not what makes the plugin useful. The value is in the workflow,
the fixed write-up format, and the fact that it starts itself.
When to use it
At the start of an audit, a threat model, or an architecture review — any time the code is unfamiliar and somebody is about to go looking for problems in it.
Also useful when an earlier review turned up issues nobody could judge, because no one had mapped out how the system fits together.
How to run it
/audit-context-building:audit-context <path> [--focus <module>]
That runs the workflow, in three steps:
- Get oriented. Map out the pieces of the system, the ways in from outside, who can reach them, and the data that sticks around between calls. Then pick the functions that carry the most weight.
- Analyze. One helper per function. Each writes its full notes to
audit-context/functions/and hands back only a short record. - Pull it together. Work out the rules that span several functions — the ones no single write-up could
state on its own — and save the result to
audit-context/DOSSIER.md.
For a single function, you can run the audit-context-building:function-analyzer helper on its own. Either
way the reading happens in a helper, not in your own session.
Why it works this way
Analyzing a function properly takes a lot of words. If those words come back into the conversation, they crowd out the very understanding they were meant to build.
Asking politely does not fix this. A skill can say "save it to a file and just summarize" and still get the whole thing back in the reply. The workflow fixes it because each helper is only allowed to hand back a fixed set of fields. There is no slot for a wall of text, so none comes back.
The skill points you at the workflow. The workflow is what actually holds the line.
What you get back
Understanding, not verdicts. The write-ups cover how the code is put together, what must always be true for it to work, and what it takes on faith. They do not name vulnerabilities, suggest fixes, write exploits, or rate severity — that is the next phase's job, done with the whole picture in hand.
The most useful thing in the output is the list of things the code counts on that nothing actually checks, each with the line where the check should have been.
The second most useful thing is the open questions. An honest list of what is still unclear beats a confident answer that turns out to be wrong.
Installation
/plugin install trailofbits/skills/plugins/audit-context-building
Tests
claude plugin eval plugins/audit-context-building --judge-model sonnet
Run it against the plugin by name. That way the tests run twice — once with the plugin and once without — so you can see what it actually adds. Pointing at a folder path instead only runs it once.
Four tests, covering the three kinds of target this plugin gets used on.
dispatches-not-inlines — asks for audit context on a small C codebase with no way to hand the work off,
so the only correct move is to say what to run and stop. It fails if the reply contains the analysis instead.
This is the test aimed at what the plugin actually changes, rather than at what the model can already do.
Its max_turns was raised from 20 to 40 after the plugin arm was being truncated before it could answer,
which scored as a routing failure. It has not been re-measured since. Run it before relying on its number.
contract-continuity — Solidity. release() looks like require(_charge(...)) confirms the buyer had
enough credit. It doesn't: for whitelisted accounts, _charge subtracts and returns true without ever
comparing. The test also checks that feeSink.notify() gets treated as untrusted, since nothing in the file
says what it does.
decompiled-continuity — Ghidra output from a firmware image. The length used to copy data into a
512-byte buffer comes from a function that isn't in the file at all, so nothing visible limits it. A check
near the top looks like it bounds the copy but only bounds the header. Claiming that an unnamed function
"is memcpy" fails the test — it looks like memcpy, and the listing never confirms it.
continuity-across-calls — C. The check that keeps a copy in bounds sits two calls away, on only one of
two branches, while the header file promises a limit that branch never applies.
The last three confirm the behavior still works rather than proving the plugin causes it — a capable model scores 1.00 on the Solidity and Ghidra cases with no plugin loaded. Worth remembering before reading a passing run as proof.
One measured cost worth knowing: the plugin arm uses roughly twice the turns of a bare agent on the same
prompt, because SKILL.md points at three reference files that get read before any work starts.
Related Skills
entry-point-analyzer— lists the functions that change state; a good input to step 1spec-to-code-compliance— checks what the code does against what the spec says it shouldtrailmark— call graphs and data-flow tracing, for the structural picture this builds on