Audit harness usage
Treat every number as one of three different things:
- Trace usage — tokens recorded by a local harness.
- API-equivalent estimate — trace tokens multiplied by a versioned public rate card.
- Metered or invoiced cost — what a provider says it actually deducted.
Never collapse those labels. A $500 API-equivalent estimate can coexist with a fixed-price subscription and a $0 incremental invoice.
Quick start
Run the deterministic scanner before interpreting anything:
python3 scripts/audit_harness_usage.py --days 30
python3 scripts/audit_harness_usage.py --days 30 --format json
python3 scripts/audit_harness_usage.py --provider gemini --days 7 --format csv
python3 scripts/audit_harness_usage.py --provider cursor \
--path cursor=/path/to/cursor-usage-export.json --format json
python3 scripts/audit_harness_usage.py --provider antigravity --format json
python3 scripts/audit_harness_usage.py --provider antigravity \
--path antigravity=/path/to/quota-response.json --format json
python3 scripts/audit_harness_usage.py --days 30 --format html \
--output harness-usage-report.html
Default discovery is read-only:
- Codex:
$CODEX_HOME/{sessions,archived_sessions}or~/.codex/..., scanned by the bundled lineage engine; CodexBar is not required - Claude:
$CLAUDE_CONFIG_DIR/projects,~/.claude/projects, and~/.config/claude/projects - Gemini:
~/.gemini/tmp/**/chats/session-*.jsonplus newer JSONL chats - Cursor: no default. Supply an Admin API/dashboard export explicitly.
- Antigravity: probes an already-running app, IDE, or
agyCLI over its localhost language-server interface; it never starts a process or reads OAuth credentials. An explicit quota-response JSON is also accepted.
Use --path PROVIDER=PATH to replace one provider's discovery root. Repeat it
for several roots. Use --max-files N only for a quick diagnostic sample; it
is not a complete report.
Use --format html --output REPORT.html for a self-contained dashboard with
summary metrics, token composition, model concentration, quota windows,
diagnostics, insights, and evidence-backed improvement opportunities. Existing
files are never replaced unless --force is explicit; forced replacements are
atomic, and report files are owner-only (0600). The HTML has inline CSS, no
external assets or scripts, and escapes every dynamic field.
Procedure
1. Set the accounting question
Name the local-calendar-day window and whether the user wants trace volume, public API-equivalent cost, actual charged cost, or all three. If they say "cost" without qualification, report all available semantics and label each one.
2. Scan and inspect coverage
Prefer JSON when further analysis is required. Check, in order:
diagnostics.malformed_rowsdiagnostics.unattributed_tokensdiagnostics.codex_unresolved_forksdiagnostics.codex_unresolved_total_only_rowsdiagnostics.codex_ambiguous_copied_prefixesdiagnostics.codex_owned_suffixesdiagnostics.codex_interleaved_filesdiagnostics.backendsdiagnostics.quota_backendsoverall.pricing_coverage- model buckets whose
estimated_cost_usdisnull
null means unknown or partial. It never means zero. The field
estimated_cost_usd_priced_portion is a disclosed lower bound when pricing
coverage is below 100%.
Codex unattributed_tokens is a raw scanner diagnostic accumulated before
lineage deduplication. It can exceed normalized totals and must not be presented
as a normalized token bucket or used by the HTML opportunity rules.
3. Reconcile the hard provider edges
For Codex, the bundled Python lineage engine follows CodexBar v0.45's
accounting rules: index leaf sessions, resolve the parent snapshot at the fork
timestamp, subtract inherited totals component-wise, classify independent
versus copied-prefix subagent counters, recognize locally owned suffixes, and
contain interleaved cumulative counters with a monotonic watermark. Confirm
the receipt says diagnostics.backends.codex: native-lineage.
For Claude, deduplicate streaming/fork copies only when both message.id and
requestId are present. Rows missing either half stay distinct, and the nested
1-hour cache-write component is clamped to total cache creation. These details
are required for CodexBar v0.45 count parity on older traces.
An unresolved parent is never silently repaired. Rows with both cumulative and last-usage counters use contained growth; total-only rows remain uncounted because no safe cap exists. Ambiguous copied prefixes without a unique parent or owned-suffix boundary are suppressed to avoid double counting. Both cases are explicit diagnostics and headline warnings.
No CodexBar binary, subprocess, cache, or Swift runtime is required. Use an explicit path for a redacted fixture or alternate Codex home:
python3 scripts/audit_harness_usage.py --provider codex --days 30 \
--path codex=/path/to/codex-home --format json
CodexBar remains a development-time parity oracle only. Never merge its totals
with this scanner's totals; they measure the same work. See
references/log-formats.md.
For Cursor, prefer tokenUsage.totalCents for vendor list-price cost and
chargedCents for what the plan deducted. Never reconstruct charged cost from
tokens when the platform supplied it.
For Gemini, state the billing mode if known. Gemini CLI can run under free, Code Assist, API-key, or Vertex arrangements; local token counts alone do not identify the bill.
Treat Antigravity as a separate provider, not a Gemini CLI log source. Its
local language server exposes current 5-hour/weekly quota windows for Gemini
and Claude/GPT model families, with per-model quotas as a legacy fallback. It
does not expose supported trace-level token history or cost through this
interface. Read quota_windows[]; leave its contribution to overall and
by_model[] empty. Never convert a remaining percentage into tokens or USD.
4. Interpret, then recommend
Use the deterministic report as evidence and apply judgment only here. Good insights answer one of these questions:
- Concentration: Which provider/model owns most tokens and estimated cost?
- Cache economics: What share of normalized input is cache read? Is a low hit rate driving uncached input spend?
- Output pressure: Is output/reasoning unusually large relative to input?
- Pricing risk: How much usage is unpriced because the model is unknown or the rate card is stale?
- Accounting quality: Do unresolved fork baselines, interleaved-counter containment, malformed rows, or partial Cursor cost fields make the headline unsafe?
Recommend a change only if the report supports it. Examples: route routine tasks to a smaller model, shorten repeated uncached context, stabilize prompts to improve caching, update the price snapshot, or investigate one anomalous day. Do not infer productivity or code quality from token volume.
The HTML renderer turns a small, documented set of those evidence checks into “opportunities to review.” They are conditional signals, not causal findings: pricing coverage gaps, low cache-read share, model concentration, output-heavy volume, unresolved Codex lineage gaps, and low Antigravity remaining quota. Malformed rows remain scanner diagnostics. Validate the workload context before acting on any card.
5. Present the receipt
Always include:
- window and providers scanned;
- total tokens with component breakdown;
- API-equivalent estimate and pricing coverage;
- platform-reported/charged cost when present;
- Antigravity quota windows when requested, explicitly separate from tokens;
- top models;
- all material quality warnings;
- price-card date and source links.
Deterministic / latent split
The script owns: path discovery, boundary validation, JSON/JSONL parsing, provider-specific token normalization, Claude stream/fork deduplication, Codex parent-snapshot resolution, copied-prefix classification, locally owned suffixes, interleaved-counter watermarks, model matching, long-context thresholds, cost arithmetic, coverage, Antigravity localhost quota probing, CSV/JSON/text/HTML output, bounded opportunity signals, and content redaction by construction.
The agent owns: choosing the business-relevant window, explaining the difference between list price and a subscription bill, judging whether an accounting bound is decision-safe, identifying plausible causes of anomalies, and proposing optimizations. The agent must not repair a missing mechanical fact with a guess.
Safety and privacy
- Read only. Do not edit, compact, move, or delete harness traces.
- Never emit prompts, responses, tool arguments, file bodies, emails, or full project paths. The scanner only consumes usage metadata.
- Antigravity requests are POSTed only to constant paths on
127.0.0.1; CSRF tokens are used in memory and never included in output. The scanner does not launchagy, refresh OAuth, or create a persistent session. - Do not discover or reuse Cursor cookies. Ask for an explicit export/API response file.
- Do not refresh the bundled price card silently. Pricing changes are a source
update with a new
as_ofdate and tests. - Keep raw logs local unless the user explicitly authorizes sharing them.
--outputwrites only the rendered report to the exact chosen file. It does not create directories and refuses to replace an existing file without--force. New reports are owner-readable only (0600), and symlink targets are refused even with--force.
References
references/log-formats.md— provider schemas, formulas, CodexBar internals, deduplication limits, and primary sourcesreferences/report-schema.md— stable report fields and cost semanticsreferences/pricing.v1.json— offline rate snapshot with provenancereferences/CODEXBAR-NOTICE.md— pinned upstream attribution and MIT notice