# Token Run Usage 证据

> Canonical path：`specs/daemon/token-run-evidence.md`
> 范围：以最小 Run/usage correlation 解释累计 Token 来自单次输入还是 model invocation 数。
> 状态：已实现 launch-level usage 最小证据；任务级 Run lifecycle、outcome 和 parent edge 仍不支持。上位规范为 [AgentTeam Token 消耗诊断](./token-optimization.md)。

## 1. AnalysisRun

目标 `AnalysisRun` 包含：

- owning Agent；
- runtime/exact model；
- start/end；
- `root`；
- outcome：`success`、`failure`、`unknown`。

当前最小实现没有可信的任务级 lifecycle，先以 runtime launch 聚合 usage，并只输出：

- owning Agent；
- runtime/exact model；
- `root`；
- launch-level opaque Run ref。

当前输出由 Process 在 launch 时直接记录的 `startedAt`、报告的 `snapshotAt`，以及
daemon 在同一快照读取到的 `activeAtSnapshot`。`activeAtSnapshot=false` 只表示该
launch 在快照时不再存活，不合成精确 end time。当前仍不输出 outcome 或 parent root。
不得用 timestamp proximity、shared Session 或 inactivity 猜测这些证据。

## 2. UsageObservation

每条 observation 包含：

- owning launch-level Run；
- optional unique model invocation ID；
- runtime；
- input；
- optional cache read；
- output。

新增或改变 runtime usage adapter 时必须明确：

- input 是否 inclusive；
- cache read 是否为 input subset；
- counter scope；
- delta/cumulative；
- reset/replacement；
- unique model invocation identity 能力。

Cumulative counter 只有在同 scope、同 generation 有 earlier snapshot 时才能转换为 delta。Ambiguous reset、duplicate 或 unknown version 使 observation 为 `limited`。

当前 Token 诊断消费 driver 已归一化的 delta，公共口径为：

- `input` 是 inclusive total input，包含 non-cached input、cache read 和
  cache creation/write；
- `cached input` 是 input 中的 cache-read 子集；
- `output` 是 adapter 可验证的 total output；OpenCode 会把 reasoning 纳入 output；
- total Token 为 `input + output`，不能再次加 cached input。

普通低基数 per-Agent 总量不得因 exact model 暂时不可归因而丢弃 usage；此时使用
code-controlled `unknown/<runtime>` model bucket。该 bucket 只保全总量，不把未知
model 伪装成 configured model，也不改变详细 observation 的原始 model capability。

Invocation identity capability 为：

- OpenCode 使用每次 `step-finish` 的稳定 part ID；其 current-Runner root invocation
  同时声明 root Context exposure 可关联，内部 compaction summary invocation 不声明；
- Kimi ACP 的 `usage_update` 仅记录 owning launch 的会话当前 Context 占用量和
  窗口大小；它不提供 invocation identity，不创建 child Run，也不套用 root
  Context exposure；
- Codex 的 `turnId` 只标识 runtime turn，不能作为 provider model-call identity；
- Claude 当前没有可用于去重的 stable invocation ID。

受控 comparison 还需要独立的 task-payload identity。Prompt compiler 在正文仍在
内存时先计算 ephemeral digest；Desktop 用 machine-persisted key 再做 keyed hash
后，才把 fingerprint 写入详细 attribution。原文、未加 key 的 digest 和
message/run/turn/delivery/attachment ID 都不进入 TSDB 或 portable artifact。
同一 launch 若没有有效 task-payload fingerprint、存在非法 fingerprint，或观察
到多个不同 fingerprint，不得宣称该 launch 是单任务受控样本。附件当前没有
content identity，因此带附件的 payload 标记为 identity unavailable 并 fail
closed。无 task payload 的 compiler block 继续使用独立状态，不能与 unavailable
混为同一个空 fingerprint。

## 3. 聚合

每个 launch-level Run 计算：

- cumulative input；
- optional uncached input；
- cache read；
- output；
- usage observation count；
- unique model invocation count；
- average input per invocation；
- 该 launch 的 runtime usage；
- 该 launch 的 materialized compiled、controllable 和 required-payload Context。
- runtime capability 完整时，该 launch 的 controllable exposure。

完整 block ledger 同时在报告 scope 聚合；launch-level Run 与 Context 已按 launch ID
关联，但尚未形成 task-level Run join。

如果 input inclusive 且 cache read 是 subset：

```text
uncachedInput = input - cacheRead
```

Cache read 不得再次加到 input。

Human owner 的 Agent/window summary 另外计算：

- root runtime input；
- 无法关联到支持范围内 Run 的 `unattributedUsage`。

一条 usage observation 只能贡献给一个 Run。无法关联到支持范围内 Run 的 usage 必须单独展示，不得隐式并入 root。

总观测 input 的 partition 是：

```text
totalObservedInput
  = rootRuntimeInput
    + unattributedUsage
```

只有 unique model invocation ID 可用时才输出 invocation count 和 average input per invocation。Usage observation count 只用于 data quality。
一个 launch 中只要有 usage observation 缺少 identity，该 launch 的 invocation
count 和 average input per invocation 就是 `unsupported`，不能只计算有 ID 的子集。
报告 scope 的 invocation count 和 average input 专门解释 root runtime input。
缺少 invocation identity 的 observation 不得混入可信的调用次数或平均值。

Exposure coverage 与 invocation count 分开判定。每个 stable invocation identity
最多贡献一次 coverage；同一 invocation/block 因 runtime replay 再次出现时必须去重。
一个 launch 只要存在 invocation identity 但 root Context 归属未知，其 exposure 为
`limited`；完全没有支持 exposure 的 invocation evidence 时为 `unsupported`。

## 4. Data Quality

以下情况必须显式展示：

- `no_data`：支持但窗口内没有 observation；
- `unsupported`：runtime/version 不提供所需语义；
- `limited`：adapter 标记的 counter ambiguity、missing correlation 或 truncation；
- `disabled`：产品专用 correlation 已关闭。

Missing field 不能编码为零。

详细诊断采集默认关闭，由 Human `agentteam token analyze` 自动开启，或由
`agentteam token ui` 中的开关控制；`agentteam token analyze --stop` 在输出后关闭且不会
先开启已经关闭的采集。
Agent caller 只能读取，不能改变 machine-wide 采集状态。普通低基数 per-Agent
usage 不受该开关影响。

无法关联的 runtime-native subagent、server/MaaS、retrieval 或 summarization usage 不强行归入 root，统一作为 `unattributedUsage` 或 `unsupported`。

## 5. 隐私和保留

Internal store 可以保留 bounded correlation ID，但不保存 Prompt、message、task、tool 或 model output 正文。

Machine-local report 使用 daemon-lifetime opaque Agent/Run ref；portable report 删除
Agent/Run 明细，也不包含 raw Session、launch、turn、request ID。

Run-scoped export 是 `snapshotAt` 上界内的不可变 point-in-time artifact；同一
`runRef` 后续刷新可以增长，但不会修改已经导出的报告。报告必须标记 Run 在
`snapshotAt` 时为 `active` 或 `ended`。对于常驻 interactive launch，操作者在目标
任务 outcome 已确定后导出即冻结本次测量边界；`active` 只提示后续刷新可能增长，
不会让已经保存的 artifact 可变。

Raw high-cardinality Run evidence 最多保留七天，并对 query sample、report 和
portable artifact 设硬限制。第一版复用已有 Desktop metrics TSDB，不增加独立
store 或磁盘配额。Persistence failure 不能阻塞 runtime。

## 6. 验证要求

- launch-level root 与 unattributed usage 分栏；
- 可信 launch startedAt、snapshotAt 与 active-at-snapshot；不伪造 task-level
  end、outcome 或 parent edge；
- 拒绝 heuristic correlation；
- 已支持 runtime 的 counter semantic；
- cumulative-to-delta、reset 和 dedup；
- input/cache-read 不重复计算；
- root 和 unattributed usage 分栏；
- invocation identity 与 observation count 分离；
- missing/unsupported/limited/disabled；
- bounded retention/query/report 和 privacy；
- 相同 payload 跨 daemon lifetime 保持 fingerprint 稳定，不同 payload 拒绝受控比较；
- 缺失、非法或多 payload launch fail closed，portable artifact 不含 raw body/ID。
