---
title: "Keep model usage as a Bot-owned retained statistic"
version: "en"
---

> Documentation Index
> Fetch the complete documentation index at: https://botharness.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Keep model usage as a Bot-owned retained statistic

Status: accepted

DSH SessionEvents are the canonical facts for each actual provider/model attempt and its provider-reported token usage, including reported usage from failed or retried attempts. BotHarness records a daily PersonaBot × execution-role × exact provider/model aggregate of uncached input, output, cache-read, and cache-write tokens, plus unknown-usage attempts. It attributes DSH Subagent work to its owning PersonaBot and distinguishes Orchestrator, Assignment, and Subagent activity. A Turn that calls several models contributes to each route separately rather than to a `mixed` bucket.

The aggregate is an application-owned retained statistic: ordinary Session deletion does not erase it, so rebuilding from the remaining Session logs must neither clear nor double-count historical values. Incremental folding must be idempotent per durable attempt, and reconciliation must replace or correct only buckets for which complete source evidence remains; it cannot truncate retained history. It contains no prompt, response, credential, or deleted Session identity. A PersonaBot's thorough purge removes its identifiable usage; archiving leaves it available for inspection. The Profile presents actual observed routes, not merely allowed routes, and marks missing provider usage as unknown instead of zero. Its activity chart initially shows the latest seven days, with a selectable range; a separate all-time total uses the same route/role filters while ignoring the chart date range (#507).

This deliberately separates replayable execution evidence from the bounded statistic retained after its source Session is gone. It extends the existing `usage_daily` projection, whose whole-table rebuild and `mixed` route bucket cannot satisfy that retention or attribution contract.

## Retention implementation and upgrade boundary (#502)

The Usage owner commits a daily increment and a deduplication receipt in one Operational Database transaction. Receipts contain only the Bot slug and an HMAC fingerprint of native Session identity plus event sequence, using a random profile-local key; they retain neither the Session identifier nor its event payload, route, timestamp or per-attempt token detail. Missing Session histories never delete either the daily totals or their receipts. Returning evidence can seed previously unseen attempts once. Archive does not change these records; Purge deletes the Bot's aggregates, receipts and migration baseline. Purge also retires owned roots using anonymous HMAC-only tombstones with no Bot identifier or usage, preventing late or restored evidence from repopulating a purged or recreated Bot even when creation timestamps coincide. Trusted ownership and the current Bot incarnation provide the live admission boundary.

Generation 40 preserves older aggregates as a Bot-level baseline through the upgrade time. Older deployments did not retain durable attempt receipts, so available pre-upgrade events seed receipts without adding their tokens again; attempts after the cutoff increment normally. A missing old history cannot prove which of its attempts were already counted, and we do not silently replace the retained baseline or claim exact historical backfill. The Host reports this upgrade limitation as a bounded reconciliation diagnostic. This limitation does not apply to new profiles or post-upgrade attempts, whose receipts survive restart and source loss.

Native DSH history deletion is not a prerequisite for this statistic contract. Verification can make a real isolated Session's source log unavailable while the Host is stopped, then compare the public Profile query after restart; this is a source-unavailability experiment, not a new native Session deletion command.

## Filtered public query (#507)

Usage owns `profileUsage` query calculations through the existing Typert API Gateway. A Human chooses model **or** provider grouping, an observed route and optionally an execution role (in collapsed details). The default chart is seven Host-local days; each public request requires real, non-future dates spanning at most 182 days. Parameterized SQL applies the same route/role predicates to both bounded daily rows and retained all-time aggregates; only the latter omit date predicates. Missing reported totals stay null, empty selections are zero with zero records, and absent cache buckets remain unknown even when a total is known.

Responses expose read/reconciliation timestamps and ready, reconciling or degraded reconciliation state. Failed source reads never erase retained totals. The Client queries on opening or changing filters and on explicit refresh; it does not poll or expire results on a timer; a failed refresh may show the previous result only for the exact same filter, clearly marked stale. A new filter never inherits another filter's counts or late response. Public detail rows are capped at 2,000 and observed model/provider choices at 1,000 each; truncation is explicit, exact aggregate totals remain complete and incomplete charts are hidden with a request to narrow the query. No schema migration or private Session-log query is introduced.

Source: https://botharness.ai/dev/adr/0094-retain-per-model-usage-after-session-deletion/index.mdx
