dnr-analyze · diff
git:20261024.3c920f9 to git:20261104.3c4a546
4 added, 4 removed. Audit A to A.
---
name: dnr-analyze
- description: "One analysis phase of /auditor:hunt or /auditor:respond, run in the incident-responder agent over incident_<n>/ only: profiles and queries the incident corpus with the bundled offline tools, keeps LEDGER.md, and writes the incident files there. Invoke it only where those commands say to, with the phase brief they describe as its arguments."
+ description: "One analysis phase of /auditor:hunt or /auditor:respond, run in the auditor:incident-responder agent over incident_<n>/ only: profiles and queries the incident corpus with the bundled offline tools, keeps LEDGER.md, and writes the incident files there. Invoke it only where those commands say to, with the phase brief they describe as its arguments."
context: fork
agent: auditor:incident-responder
background: false
user-invocable: false
allowed-tools:
- Read
- Grep
- Glob
- Edit(incident_*/**)
- 'Bash("${CLAUDE_PLUGIN_ROOT}/scripts/dnr/profile.sh" *)'
- 'Bash("${CLAUDE_PLUGIN_ROOT}/scripts/dnr/frames.py" *)'
- 'Bash("${CLAUDE_PLUGIN_ROOT}/scripts/dnr/deltas.py" *)'
- 'Bash("${CLAUDE_PLUGIN_ROOT}/scripts/dnr/jq-ro.sh" *)'
- 'Bash("${CLAUDE_PLUGIN_ROOT}/tools/auditor-tools/target/release/audit-logs" *)'
---
# auditor-skill — D&R analysis phase
*Method credited to anthropics/defending-code-reference-harness (dnr-hunt, dnr-respond); see ATTRIBUTION.md.*
**Phase brief:** $ARGUMENTS
`/auditor:hunt` or `/auditor:respond` started you for one analysis phase. You run
- as `incident-responder`, in a context of your own: you see this brief, not the
+ as `auditor:incident-responder`, in a context of your own: you see this brief, not the
conversation, and you cannot ask the human anything. The brief starts with the
mode, the phase and the run directory (`hunt baseline incident_<n>`), then names
everything you work from: the corpus paths, the snapshot lines, the provider
domain, the window and caps of each collection and whether it ended with its
`collection gaps: N` line, the execution mode and PoC build cache once recorded,
- and the results the command brings back from `context-builder` and
- `poc-engineer`. Work from exactly those. A value the brief leaves out is a gap to
+ and the results the command brings back from `auditor:context-builder` and
+ `auditor:poc-engineer`. Work from exactly those. A value the brief leaves out is a gap to
report, never one to guess.
Read `${CLAUDE_PLUGIN_ROOT}/references/detection-response/README.md` and
`${CLAUDE_PLUGIN_ROOT}/references/detection-response/log-sources.md` first, as
your instructions say.
## The phase
| Brief | Do | Report back |
|---|---|---|
| `hunt baseline` | Start `LEDGER.md` from `${CLAUDE_PLUGIN_ROOT}/templates/hunt-ledger.md`, copy the collection gaps into it, and profile the corpus. Then run the seed sweeps of the log-sources reference, one ledger row per query, pivoting from every hit along the campaign chain; stop after two rounds with no new entity | the suspects (entities, the instruction each touched, the ledger rows behind them), the paths written, any wider collection |
| `hunt emit` | Write `INCIDENTS.json` (`${CLAUDE_PLUGIN_ROOT}/references/detection-response/incidents-schema.md`) and `INCIDENT_REPORT.md` (`${CLAUDE_PLUGIN_ROOT}/templates/incident-report.md`) from the ledger and the source and PoC results in the brief | a summary and the paths written |
| `respond seed` | Write the question this run answers as one sentence in `LEDGER.md` (from the hunt-ledger template when the run directory has none), and turn the lead into seed entities | the seed entities, the one ring to widen to, and the addresses, window and caps a collection of it needs |
| `respond outcome` | Copy the collection gaps into the ledger and merge one timeline ordered by `(slot, transactionIndex)`. Then Leg 1 and the damage, from queries: distinct victims, raw units out per mint, the window, and whether activity is still under way | the verdict so far, the figures with their ledger rows, the implicated handler |
| `respond plan` | Write `RESPONSE-INC-<n>.<k>.md` from `${CLAUDE_PLUGIN_ROOT}/templates/response-plan.md`, never over a plan for another lead, then update `INCIDENTS.json` | a summary and the paths written |
## Where you read and write
- **Inside `incident_<n>/` only.** That is the run directory the brief names,
which the command made or checked with `rundir.sh`, spelled exactly as the brief
spells it: never another `incident_*`, and never the same name in another letter
case. The corpus is `incident_<n>/corpus*/` and, for the user's own export, the
copy the command made in `incident_<n>/logs/`; the bundled tools refuse a file
outside a run directory. Reference files come from `${CLAUDE_PLUGIN_ROOT}`. Never
read or write the client's tree.
- **Writes** go into `incident_<n>/` through the Write and Edit tools, or a `>`
into a new name there: `LEDGER.md`, a saved query result, `INCIDENTS.json`,
`INCIDENT_REPORT.md`, a response plan. Nothing else writes.
## The tools that run without a prompt
These forms, and only these, are pre-approved for this phase:
```bash
"${CLAUDE_PLUGIN_ROOT}/tools/auditor-tools/target/release/audit-logs" profile incident_<n>/corpus*/transactions.jsonl
"${CLAUDE_PLUGIN_ROOT}/scripts/dnr/profile.sh" incident_<n>/corpus*/transactions.jsonl
"${CLAUDE_PLUGIN_ROOT}/scripts/dnr/frames.py" [--instructions] incident_<n>/corpus*/transactions.jsonl
"${CLAUDE_PLUGIN_ROOT}/scripts/dnr/deltas.py" tokens|lamports [--by account|owner,mint|mint] incident_<n>/corpus*/transactions.jsonl
"${CLAUDE_PLUGIN_ROOT}/scripts/dnr/jq-ro.sh" [-c] [-r] [-s] [-e] [-n] [--arg <name> <value>]... [--argjson <name> <json>]... <filter> incident_<n>/<file>...
```
Give each the corpus paths the brief names in place of
`incident_<n>/corpus*/transactions.jsonl`: that glob once a collection ran, and
each `--logs` copy in `incident_<n>/logs/`. Name only paths that exist: with no
collection there is no `corpus*/`, and the line then fails.
- **As written.** The quoted absolute path comes first, with nothing in front: no
`bash` or `python3` (the shebang runs each one), no `cd`, no variable
assignment. One tool per call, with no `&&`, `;` or `|` between commands, and no
`$` or backtick anywhere in the line, inside quotes too: Claude Code asks about
any command that holds one. Keep an output with `> incident_<n>/<new name>` at
the end, or a recipe's own `--out incident_<n>/<name>.jsonl` (or `.tsv`, `.txt`:
a new name directly in the run directory, of at most 128 bytes of letters,
digits, `.`, `_` and `-`, starting with a letter or digit), which never leaves a
partial file.
These forms replace the `bash …` and `python3 -I …` spellings in the references.
- **audit-logs first.** It runs the three recipes as subcommands (`profile`,
`frames [--instructions]`, `deltas tokens|lamports [--by …]`) with the same arguments
after the subcommand, and prints what they print. Run the script with those
arguments instead when audit-logs exits 126 or 127 (not built, or not
executable), dies by a signal (a status above 128), or exits 4 as a stale build
(its stderr starts `audit-logs: stale build:`, and it writes nothing). Any other
result stands: a refusal (exit 2) means the scripts would refuse the same input.
Each ledger row names the tool that produced it.
- **jq only through `jq-ro.sh`.** It takes `-c -r -s -e -n`, each its own word, and
`--arg` / `--argjson`, then the filter and files inside `incident_<n>/`. A jq
variable (`$name`, `$ENV`) puts a `$` in the line, so write values into the filter
as literals instead (`select(.slot == 301)`). Never sum u64 amounts in it
(doubles; use `deltas.py --by`: `tokens --by owner,mint` for an owner's net per
mint, `tokens --by mint` for a mint's loss and how many lost,
`lamports --by account` for an account's net), and never flatten log arrays with
it (use `frames.py`).
- **Anything else** prompts the human or is denied: `jq` itself, a bare
`python3`, `solana`, `curl`, a bundled script not listed above. A read outside
these forms that the work needs, such as an `rpc-read.sh` read from the
log-sources reference, is attempted once as written there; when it is
denied, record the gap in the ledger and in your report, and never work around
it.
## Report back
Your last message is the report the command receives. Give, in this order:
1. the paths you wrote;
2. what the phase reports back (the table above);
3. a short ledger summary: rounds run, suspects, verdicts, collection gaps, and
every recipe note of skipped malformed lines, each also in `LEDGER.md` and in
`collection_gaps`;
4. any collection you propose: exact addresses, window and caps;
5. every call that was denied or could not run, with the gap it leaves.
Quote no corpus text beyond the values a finding needs; memos and log text stay
in files.