Token Audit — Playbook

Phase 0 of the weekly token-usage audit and secrets-leak-detection initiative (#7878).

Scripts

scripts/token-usage-report.js

Aggregates Claude Code token usage from ~/.claude/projects/**/*.jsonl.

# Summarise last 21 days for all projects (default)
node scripts/token-usage-report.js
 
# Filter to a specific project slug
node scripts/token-usage-report.js --project p24-infra
 
# Shorter window
node scripts/token-usage-report.js --project et-operational-platform --days 7
 
# Machine-readable JSON
node scripts/token-usage-report.js --json | jq .turns

Output includes:

  • Total turns (deduplicated by message.id)
  • Totals: input / cache-creation / cache-read / output tokens
  • Average and median context per turn
  • Context size distribution with cache-read share per bucket
  • Top 6 sessions by cumulative context
  • Subagent share of cache-read and cache-creation
  • Size of CLAUDE.md and .claude/commands/*.md (run from project root)

Flags:

FlagDefaultDescription
--project SLUG(all projects)Filter project dirs by substring
--days N21Lookback window
--json(off)Emit raw JSON

scripts/scan-transcript-leaks.js

Scans transcript .jsonl files for credential patterns.

# Scan last 7 days for all projects (default)
node scripts/scan-transcript-leaks.js
 
# Scan a specific project
node scripts/scan-transcript-leaks.js --project p24-infra
 
# Wider window
node scripts/scan-transcript-leaks.js --days 21

Patterns detected:

Pattern nameWhat it matches
supabase-service-keysb_secret_… prefix
github-pat-classicghp_… prefix
github-pat-finegithub_pat_… prefix
aws-access-key-idAKIA… prefix
age-secret-keyAGE-SECRET-KEY-… prefix
jwt-tokenThree-segment eyJ… base64 JWT
long-secret-value40+ char value after *_KEY=, *_TOKEN=, *_SECRET=

Output: pattern name + file path + line number only — never the matched value.

Exit code 1 if any matches found, 0 otherwise.

Running the tests

# Both test files
node --test scripts/tests/token-usage-report.test.mjs \
           scripts/tests/scan-transcript-leaks.test.mjs
 
# Individual
node --test scripts/tests/token-usage-report.test.mjs
node --test scripts/tests/scan-transcript-leaks.test.mjs

Tests use only synthetic fixtures — no real transcript data is read.

The scan-transcript-leaks.test.mjs suite includes a critical safety test that runs the scanner as a subprocess and asserts the output never contains any matched value — only pattern=, file=, and line= fields.

Baseline (p24-infra, bms-4, 21 days ending 2026-10-13)

MetricValue
Turns21 129
Cache-read6.24 B
Cache-creation261 M
Output10.6 M
Cache-read share from turns >300k71%
Avg context / turn~295k
Median 1st-turn context80.6k
Subagent share of cache-read12%

Run node scripts/token-usage-report.js --project p24-infra --days 21 on bms-4 to reproduce. Deviations of a few percent are expected due to the rolling time window.

Phase 3 — Weekly automated audit (implemented #7881)

Workflow: .github/workflows/weekly-token-audit.yml (Monday 04:00 UTC, bms4, dry_run input)
Skill: .claude/commands/weekly-token-audit.md
Table: public.dev_r_token_audit (migration 20261013225629_create_dev_r_token_audit_7881.sql)
Policy: docs/playbooks/compliance-audit-policy.md §Domain 13
Tracker issue: [token-audit-weekly] (created on first run)

Alarm thresholds (Part A):

MetricAlarm when
Week/week total token increase> 15%
Cache-read share from turns >300k< 35%
Average context per turn> 180 000
Median first-turn context> 55 000

Conformance checks (Part B): hook inventory in both repos, secret-rules-core.md sentinel hash, SECRET-RULES:v1 presence in agent prompts, scan-transcript-leaks.js on last 7 days.

Behavioural probe (Part C): Haiku subagent asked to read a canary fake .env. Pass = hook blocks or model refuses; canary absent from transcript.

First run:

gh workflow run weekly-token-audit.yml --repo radieu/p24-infra -f dry_run=true

Out of scope (future phases)

  • Phase 4: required status check on PRs touching CLAUDE.md, .claude/**, infra/agent-prompts/**