| name | client-auditor |
| description | Use when auditing, reviewing, or finding vulnerabilities in a blockchain node, execution client, consensus client, or any Go/Rust/C++ codebase with P2P networking, consensus logic, RPC handlers, or bridge components.
|
| allowed-tools | Read, Grep, Glob, Bash, Agent, Write, Edit |
| metadata | {"argument-hint":"start [path] | verify [path] [deep] | report [path]"} |
Blockchain Client Auditor
You are the orchestrator for a lightweight but deep blockchain client security audit. Keep the main context small: coordinate agents, run cheap Bash gate snippets, and assemble decisions from disk. Subagents read source code and detailed references.
Commands
Use explicit phases. Do not silently continue into the next phase unless the user asks for it.
/client-auditor start [target-path]: setup, recon, hunt drafts, optional cross-subsystem pass, inventory promotion to canonical findings, and coverage. Stops after the inventory gate. Does not accept deep — depth lenses run only during verify (see below).
/client-auditor verify [target-path] [deep]: build audit/verification_queue.md from frontmatter of audit/findings/, verify queued findings (each verifier edits the finding file in place), and when deep is specified additionally run depth lenses + adversarial review. Stops after the verify gate.
/client-auditor report [target-path]: formatting only. Read audit/findings/, inventory view, queue, coverage, and adversarial review; write audit/report.md. Do not run tests, PoCs, benchmarks, harness checks, source validation, inventory reconciliation, verifier re-checks, or deep lenses.
If no command is supplied, treat it as start and state that assumption.
If the user invokes /client-auditor start ... deep, ignore the trailing deep token, run start normally, and warn: "deep has no effect on start; run /client-auditor verify {path} deep after start finishes to activate depth lenses and adversarial review."
Default mode is intentionally light: 1 recon agent, 2-5 hunt agents, and inventory. Verification is a separate command. deep on verify adds at most four focused depth lenses plus an adversarial review pass.
Skill Directory Resolution
Set {SKILL_DIR} to the first existing directory from:
./client-auditor
~/.codex/skills/client-auditor
~/.claude/skills/client-auditor
Use {REF_DIR} = {SKILL_DIR}/references. All paths below are relative to {REF_DIR} unless explicitly under audit/.
Version check: read {SKILL_DIR}/VERSION; optionally fetch the remote VERSION and warn if newer. If network fails, continue silently.
Context Discipline
The orchestrator must not read source files, references/patterns/*.md, references/lenses/*.md, references/analysis-checklist.md, references/heuristics.md, references/judging.md, references/adversarial-review.md, references/report-format.md, individual audit/findings/{...}.md body text, or audit/findings/_drafts/* body text. Those are subagent inputs.
The orchestrator may read:
- agent prompt files under
references/agents/
- specs under
references/specs/
- routing files under
references/routing/ when constructing recon prompts
audit/metadata.md, audit/manifest.md, audit/spawn_manifest.md, audit/findings_inventory.md (derived view), audit/verification_queue.md, audit/coverage.md, audit/adversarial_review.md, audit/report.md
- only the frontmatter of
audit/findings/*.md via awk / head / grep when building the verification queue or running a gate snippet (do not read full bodies)
If a specific finding needs targeted review, use /client-auditor verify rather than reading source in the orchestrator.
Artifact Spec
Read {REF_DIR}/specs/audit-artifacts.md and {REF_DIR}/specs/finding-format.md before starting. The audit state lives in:
audit/
metadata.md
manifest.md
spawn_manifest.md
progress/
findings/
_drafts/ # hunt/xsub/depth output
{C|H|M|L|I}-{NNN}-{slug}.md # canonical findings; PREFIX = current severity
_false-positives/FP-{NNN}-{slug}.md # REFUTED
findings_inventory.md # auto-generated derived view
verification_queue.md
depth/{lens}.md # depth scratch artifact
adversarial_review.md
coverage.md
report.md
Each finding is one file in findings/; verification fields live in that finding's frontmatter and a ## Verification body section. findings_inventory.md is a derived view, not authority — see specs/finding-format.md.
Observability
Every phase has a progress artifact under audit/progress/ following specs/progress-format.md. Agents create their own progress file as the very first action — the orchestrator does not pre-create skeletons. If an agent has not written its progress file, it has not started.
Required progress files per phase:
progress/recon.md
progress/hunt-{focus}.md for every required hunt row, or the exact Progress Output in spawn_manifest.md
progress/xsub.md during start, with Status: skipped if no cross-subsystem pass runs
progress/inventory.md
progress/verification_queue.md during /client-auditor verify
progress/verify-{ID}.md for every queued finding
progress/depth-{lens}.md for every triggered deep lens
progress/adversarial.md when adversarial review runs or is intentionally skipped in deep mode
progress/report.md during /client-auditor report
For phases that are intentionally skipped (e.g. cross-subsystem when no scoped hypotheses exist), the orchestrator writes the progress file directly with Status: skipped and a Blockers: reason. No agent needed.
To detect a stalled agent, the orchestrator polls audit/progress/ via ls, find -newer, or by reading Last Updated: fields. If an agent has not updated progress in N minutes, the orchestrator may checkpoint it with a narrow request (update progress, write any partial artifacts, record blockers, stop or continue within its assigned phase) or terminate and re-spawn.
Workspace Sidecar Files
Some hosts and filesystems create hidden metadata sidecars such as ._* next to files written under audit/. These files are benign environment artifacts, not audit artifacts.
- Do not pause the audit when
._* files appear; do not delete them; do not treat them as unexpected workspace mutations.
- Gate snippets either use literal prefix patterns (e.g.
findings/[CHMLI]-*.md, _drafts/${focus}-*.md) that naturally skip ._*, or pass ! -name '._*' to find when the glob would otherwise match them.
- Agents that list or glob a directory must apply the same exclusion (
find ... ! -name '._*' or equivalent filtering).
Agent Lifecycle
Use the host's default subagent facility. Some hosts limit concurrent worker threads or require completed workers to be explicitly released. At the end of each Stage, after the stage gate passes, release/close completed workers when the host exposes such a lifecycle operation before spawning workers for the next Stage.
Specifically, if the host exposes worker lifecycle controls:
- After Stage 3 (Hunt): release all hunt workers once their drafts and progress files pass the hunt gate.
- After Stage 4 (Cross-subsystem): release the xsub worker.
- After Stage 5 (Inventory): release the inventory worker.
- After Stage 7 (Verification): release all verifier workers in batches; do not let completed verifier slots block Stage 8 spawns.
- After Stage 8 (Depth): release all depth workers, then re-spawn the inventory worker for the re-inventory step, then release it.
- After Stage 9 (Adversarial): release the adversarial worker.
Worker state is on disk; releasing/closing a worker does not lose work.
Gates — Cardinal Rule
When a gate snippet reports an inconsistency, the orchestrator must never delete, move, or rewrite an already-written verifier section, finding file, or progress file just to make the gate pass. If a gate complains that an artifact is "extra" or "no longer required," the gate definition is wrong — fix the gate, not the artifact.
Schema Discipline
The audit state is markdown frontmatter + body sections grepped by Bash gates. This is intentional — every artifact is human-inspectable and machine-parseable with awk / grep / find. But text protocols are fragile: a stray |, an upper/lower case typo, an unfiltered ._* sidecar, or a column rename can break gates silently.
Every agent and gate snippet treats specs/finding-format.md as the schema authority:
- Frontmatter values that flow into markdown tables MUST NOT contain
|. Agents replace | with / before write; gates that build tables tr '|' '/' on field substitution.
- Enum values are case-sensitive.
severity: High (title-case) and verification_status: confirmed (lowercase) are the only valid spellings.
- Glob patterns that may match hidden sidecars MUST filter (
find ... ! -name '._*' or use a character-class prefix that excludes a leading dot).
spawn_manifest.md / verification_queue.md column orders are part of the contract. Inline comments in the gate snippets pin the column indices.
- Filename PREFIX MUST match the
severity field's first letter for canonical findings. Inventory enforces this; the inventory gate re-verifies it.
If a future change adds a column to any tabular artifact, update the matching gate snippet's column index AND its inline comment in the same edit.
Shell Binding
All gate snippets in this file are written for bash, not POSIX sh or other interactive shells. When a shell is available, run each snippet under bash explicitly:
bash -lc '<snippet>'
or save the snippet to a temp file and run it with bash. Do not paste snippets into another shell directly; shell-specific reserved variables and [[ ]] behavior can break otherwise valid gate snippets.
Gate snippets in this file avoid named variables that commonly collide with interactive-shell built-ins (e.g. status is renamed st), but the safest convention is still to invoke them via bash -lc or an equivalent bash runner.
Start Phase
Run for /client-auditor start [target-path].
Stage 1 — Setup
Create directories:
mkdir -p audit/progress audit/findings/_drafts audit/findings/_false-positives audit/depth
Write audit/metadata.md with target, date, mode, skill version, resolved {SKILL_DIR}, and any user-provided impact calibration.
Stage 2 — Recon Manifest and Spawn Manifest
Read:
agents/recon-agent.md
specs/progress-format.md
routing/entry-points.md
routing/trust-boundaries.md
routing/pattern-routing.md
routing/discovery-playbook.md
Spawn one recon agent with those texts plus target_path, audit_dir, and {REF_DIR}. Recon writes audit/manifest.md, audit/spawn_manifest.md, and audit/progress/recon.md. Recon's spawn_manifest.md uses an Expected Output Prefix column pointing under findings/_drafts/.
After it returns, read only audit/manifest.md and audit/spawn_manifest.md. If no subsystem groups or required spawn rows exist, halt with a clear scope/path error.
Recon gate:
test -f audit/manifest.md && test -f audit/spawn_manifest.md \
&& grep -qE '\| YES \| [A-Z_]+ \|[[:space:]]*$' audit/spawn_manifest.md \
&& awk -F': *' '/^- Status:/ {s=$2} END {exit (s ~ /^(complete|skipped|blocked)$/) ? 0 : 1}' audit/progress/recon.md \
|| { echo "FAIL: recon gate"; exit 1; }
echo "recon gate: OK"
Stage 3 — Hunt Drafts
Read agents/hunt-agent.md, specs/finding-format.md, specs/progress-format.md.
For each Required = YES row in audit/spawn_manifest.md, spawn one hunt agent with:
- exact
Focus, Trust Level, Entry Points, Pattern Files from the row
expected_output_prefix = findings/_drafts/{focus}- (from Expected Output Prefix column)
progress_output = exact Progress Output from the row (or progress/hunt-{focus}.md if missing)
{REF_DIR} and audit_dir
Prefer 2-5 total hunt agents. If recon recommends more, batch by priority; tell extra rows to merge by adjacent trust boundary or subsystem in manifest.md. Hunt agents write to findings/_drafts/{focus}-NN-{slug}.md (one file per draft, no aggregate index) and update their own progress.
Hunt gate:
fail=0
for focus in $(awk -F'|' '
{ for (i = 1; i <= NF; i++) gsub(/^[[:space:]]+|[[:space:]]+$/, "", $i) }
$9 == "YES" && $3 != "" && $3 != "Focus" { print $3 }
' audit/spawn_manifest.md); do
drafts=$(ls audit/findings/_drafts/${focus}-*.md 2>/dev/null | wc -l | tr -d ' ')
prog=audit/progress/hunt-${focus}.md
if [ ! -f "$prog" ]; then echo "FAIL hunt $focus: missing $prog"; fail=1; continue; fi
st=$(awk -F': *' '/^- Status:/ {print $2; exit}' "$prog")
case "$st" in
complete|blocked|skipped) ;;
*) echo "FAIL hunt $focus: progress not terminal ($st)"; fail=1 ;;
esac
echo "[hunt $focus] drafts=$drafts status=$st"
done
[ $fail -eq 0 ] && echo "hunt gate: OK" || exit 1
If a hunt failed terminally, the orchestrator may re-spawn just that focus (do not touch other agents' progress or drafts).
After the hunt gate passes, release/close every hunt worker spawned in this Stage if the host exposes worker lifecycle controls before proceeding to Stage 4. Their progress / draft files are on disk; releasing them does not lose work.
Stage 4 — Cross-Subsystem Drafts
If manifest.md or progress files list cross-boundary hypotheses, spawn agents/cross-subsystem-agent.md with scoped hypotheses and audit_dir. The agent writes only findings/_drafts/xsub-*.md and progress/xsub.md.
If skipped, the orchestrator writes the progress marker directly:
cat > audit/progress/xsub.md <<EOF
# Progress: cross-subsystem/xsub
- Phase: cross-subsystem
- Owner: orchestrator
- Status: skipped
- Started At: $(date -u +%Y-%m-%dT%H:%M:%SZ)
- Last Updated: $(date -u +%Y-%m-%dT%H:%M:%SZ)
- Assigned Output: progress/xsub.md
- Current Step: phase skipped by orchestrator
- Files Read: none
- Files Written: this progress marker
- Decisions Made: no scoped cross-subsystem hypotheses
- Findings Touched: 0
- Impact / Severity Notes: n/a
- Blockers: no cross-subsystem hypotheses identified
- Next Checkpoint: inventory consumes this skipped marker
EOF
After Stage 4, release/close the xsub worker if one was spawned and the host exposes worker lifecycle controls.
Stage 5 — Inventory and Coverage
Read agents/inventory-agent.md, specs/finding-format.md, specs/inventory-format.md, specs/progress-format.md.
Spawn the inventory agent with audit_dir and {REF_DIR}. It writes:
audit/findings/{C,H,M,L,I}-{NNN}-{slug}.md (promoted from drafts)
audit/findings/_false-positives/FP-{NNN}-{slug}.md (only on inventory re-runs that move a verifier-REFUTED finding here; first-run start phase produces none)
audit/findings_inventory.md (derived view; auto-generated)
audit/coverage.md
audit/progress/inventory.md
Inventory gate:
test -f audit/findings_inventory.md && test -f audit/coverage.md \
|| { echo "FAIL: inventory outputs missing"; exit 1; }
leftover=$(find audit/findings/_drafts -maxdepth 1 -type f -name '*.md' ! -name '._*' 2>/dev/null | wc -l | tr -d ' ')
if [ "$leftover" != "0" ]; then echo "FAIL: $leftover drafts unprocessed in _drafts/"; exit 1; fi
bad=0
for f in audit/findings/[CHMLI]-*.md; do
[ -f "$f" ] || continue
for field in 'id:' 'status:' 'severity:' 'confidence:'; do
grep -q "^${field}" "$f" || { echo "MISSING $field in $f"; bad=1; }
done
prefix=$(basename "$f" | cut -c1)
sev_letter=$(awk -F': *' '/^severity:/ {print toupper(substr($2,1,1)); exit}' "$f")
[ "$prefix" = "$sev_letter" ] || { echo "MISMATCH $f: filename prefix=$prefix vs severity[0]=$sev_letter"; bad=1; }
done
[ $bad -eq 0 ] && echo "inventory gate: OK" || exit 1
If inventory is missing or malformed, re-run the inventory agent. Stop here and summarize draft counts, reportable findings, coverage gaps, and what /client-auditor verify will need.
After the inventory gate passes, release/close the inventory worker if the host exposes worker lifecycle controls.
Verify Phase
Run for /client-auditor verify [target-path] [deep].
Stage 6 — Verification Queue
The orchestrator builds audit/verification_queue.md from the frontmatter of audit/findings/{C,H,M,L,I}-*.md. Verifier agents must not edit this file.
Build the queue with a Bash snippet:
mkdir -p audit
{
echo "# Verification Queue"
echo
echo "| ID | Severity | Required | Reason |"
echo "|----|----------|----------|--------|"
for f in audit/findings/[CHMLI]-*.md; do
[ -f "$f" ] || continue
id=$(awk -F': *' '/^id:/ {print $2; exit}' "$f")
sev=$(awk -F': *' '/^severity:/ {print $2; exit}' "$f")
req=$(awk -F': *' '/^verification_required:/ {print $2; exit}' "$f")
reason=$(awk -F': *' '/^verification_reason:/ {print $2; exit}' "$f" | tr '|' '/')
st=$(awk -F': *' '/^verification_status:/ {print $2; exit}' "$f")
if [ "$req" = "true" ] && [ "$st" != "confirmed" ] && [ "$st" != "contested" ] && [ "$st" != "refuted" ] && [ "$st" != "not_reproducible" ]; then
echo "| $id | $sev | true | needs verification |"
else
echo "| $id | $sev | false | ${reason:-already-verified-or-not-required} |"
fi
done
} > audit/verification_queue.md
Then write the orchestrator's progress file:
cat > audit/progress/verification_queue.md <<EOF
# Progress: verification/queue
- Phase: verification
- Owner: orchestrator
- Status: complete
- Started At: $(date -u +%Y-%m-%dT%H:%M:%SZ)
- Last Updated: $(date -u +%Y-%m-%dT%H:%M:%SZ)
- Assigned Output: verification_queue.md
- Current Step: queue built from findings frontmatter
- Files Read: audit/findings/[CHMLI]-*.md
- Files Written: audit/verification_queue.md
- Decisions Made: queued findings with verification_required=true and not already verified
- Findings Touched: $(grep -c '^| F-' audit/verification_queue.md)
- Impact / Severity Notes: n/a
- Blockers: none
- Next Checkpoint: per-finding verifier spawns
EOF
If no rows match (empty queue beyond the table header), skip the verifier phase.
Stage 7 — Per-Finding Verification
Read agents/verifier-agent.md, specs/finding-format.md, specs/progress-format.md.
For each queued ID (where Required = true in verification_queue.md):
- Look up the finding file:
f=$(ls audit/findings/[CHMLI]-${NNN}-*.md 2>/dev/null | head -1)
- Spawn verifier with
finding_id, finding_path: ${f}, audit_dir, {REF_DIR}
Verifier edits the finding file in place: updates verification_* frontmatter fields and appends a ## Verification section. Verifier writes progress/verify-{ID}.md. Verifier does not write verification_queue.md, findings_inventory.md, or any other finding.
Verify gate:
required_ids=$(awk -F'|' '
{ for (i = 1; i <= NF; i++) gsub(/^[[:space:]]+|[[:space:]]+$/, "", $i) }
$4 == "true" && $2 ~ /^F-[0-9]+$/ { print $2 }
' audit/verification_queue.md)
fail=0
for id in $required_ids; do
num=$(echo "$id" | sed 's/^F-//')
f=$(ls audit/findings/[CHMLI]-${num}-*.md 2>/dev/null | head -1)
if [ -z "$f" ]; then echo "FAIL $id: no matching finding file"; fail=1; continue; fi
st=$(awk -F': *' '/^verification_status:/ {print $2; exit}' "$f")
case "$st" in
confirmed|contested|refuted|not_reproducible) ;;
*) echo "FAIL $id: verification_status=${st:-<missing>} in $f"; fail=1 ;;
esac
done
[ $fail -eq 0 ] && echo "verify gate: OK" || exit 1
If the gate fails, re-spawn the missing verifier(s). Never delete verifier-written frontmatter or ## Verification sections to make the gate pass.
After the verify gate passes, release/close every verifier worker spawned in this Stage if the host exposes worker lifecycle controls before proceeding to Stage 8 (deep) or terminating the verify command.
Stage 8 — Deep Mode Lenses
Skip unless deep is specified. Read agents/depth-agent.md. Note: the orchestrator does NOT read the lens files themselves — depth agents read their assigned references/lenses/{lens}.md.
Trigger at most four lenses from manifest.md Deep Lens Triggers table (only those marked YES):
consensus-invariant
network-surface
state-resource
memory-concurrency
Execute in order:
8.1 Spawn depth agents. For each triggered lens, spawn one depth agent with lens, audit_dir, {REF_DIR}. Each agent writes audit/depth/{lens}.md plus promotion drafts at audit/findings/_drafts/depth-{lens}-NN-{slug}.md and audit/progress/depth-{lens}.md.
8.2 Wait + release. Wait for all depth workers using the host's subagent completion mechanism. When complete, release/close each one if the host exposes worker lifecycle controls before the inventory re-spawn.
8.3 Re-run inventory. Spawn the inventory agent again with audit_dir and {REF_DIR}. It promotes the new depth drafts (assigning new F-NNN from the existing ID pool, preserving every prior F-NNN), may move verifier-REFUTED findings to _false-positives/, may rename files via mv if severity changed. Wait for completion, then release/close the inventory worker if the host exposes worker lifecycle controls.
8.4 Re-run inventory gate (same Bash snippet as Stage 5 inventory gate).
8.5 Rebuild verification_queue.md (same Bash snippet as Stage 6 — it reads the now-updated finding frontmatter).
8.6 Spawn verifiers for newly queued IDs. Read the updated queue; for each row with Required = true that does NOT already have verification_status set in its finding, spawn a verifier per Stage 7. Wait + close.
8.7 Re-run verify gate (same Bash snippet as Stage 7).
Stage 9 — Adversarial Review (deep only)
Read agents/adversarial-agent.md. Select finding_ids whose frontmatter satisfies any of:
severity ∈ {Critical, High}, OR
severity: Medium AND confidence >= 70, OR
verification_evidence_tag: [UNVERIFIED] regardless of severity.
(Use the same Bash frontmatter parsing pattern as the verify queue builder.)
Spawn the adversarial agent with the chosen finding_ids. It writes only audit/adversarial_review.md and audit/progress/adversarial.md — it never edits findings. If it recommends severity changes, the orchestrator re-runs inventory once more; inventory reads adversarial_review.md and applies accepted recommendations (mv + frontmatter + severity_history) per its Step 5 adversarial parsing rule.
If no adversarial review is needed, the orchestrator writes the skipped marker:
cat > audit/progress/adversarial.md <<EOF
# Progress: adversarial/adversarial
- Phase: adversarial
- Owner: orchestrator
- Status: skipped
- Started At: $(date -u +%Y-%m-%dT%H:%M:%SZ)
- Last Updated: $(date -u +%Y-%m-%dT%H:%M:%SZ)
- Assigned Output: progress/adversarial.md
- Current Step: phase skipped by orchestrator
- Files Read: none
- Files Written: this progress marker
- Decisions Made: no findings met the adversarial review threshold
- Findings Touched: 0
- Impact / Severity Notes: n/a
- Blockers: no qualifying findings
- Next Checkpoint: report
EOF
After Stage 9, release/close the adversarial worker if one was spawned and the host exposes worker lifecycle controls. Also release any lingering inventory worker from Step 8.3 re-inventory or post-adversarial re-inventory. At this point every worker spawned during the verify command should be complete and releasable.
Stop here. Summarize verifier verdicts, tests attempted, blockers, and any report input gaps.
Report Phase
Run for /client-auditor report [target-path].
Read agents/report-agent.md, references/report-format.md, and specs/progress-format.md. Spawn the report agent with audit_dir and {REF_DIR}.
Report stage is formatting only. Do not run tests, PoCs, benchmarks, harness checks, source validation, inventory reconciliation, verifier re-checks, or deep lenses. If report inputs appear stale or inconsistent, the report agent discloses the inconsistency in audit/report.md under Report Input Gaps.
After the report gate passes, release/close the report worker if the host exposes worker lifecycle controls.
Report gate:
test -f audit/report.md && [ "$(wc -c < audit/report.md)" -gt 200 ] \
&& test -f audit/progress/report.md \
&& awk -F': *' '/^- Status:/ {print $2; exit}' audit/progress/report.md | grep -q '^complete$' \
|| { echo "FAIL: report gate"; exit 1; }
echo "report gate: OK"
Resume Protocol
Recover state from disk in this order: metadata.md, progress/, manifest.md, spawn_manifest.md, findings/[CHMLI]-*.md, findings/_false-positives/, verification_queue.md, coverage.md, report.md. The per-finding files in findings/[CHMLI]-*.md are the authoritative source; findings_inventory.md is auto-generated and can be re-derived. If findings/_drafts/ contains files on resume, run the inventory phase to flush them (drafts in a healthy state should be empty after start completes). Resume only within the requested command phase. Do not auto-advance from start to verify or from verify to report.
Completion Criteria
start is complete when the recon, hunt, xsub, and inventory gates all pass and findings/_drafts/ is empty.
verify is complete when the verify gate passes (every Required = true queue row has a finding file with verification_status set). In deep mode, also depth lens artifacts exist for every triggered lens and progress/adversarial.md exists.
report is complete when the report gate passes.