Skip to content

Report per-agent and per-engine token usage from a sealed run - #4

Merged
apresmoi merged 7 commits into
mainfrom
feat/operational-ecosystem
Aug 30, 2026
Merged

Report per-agent and per-engine token usage from a sealed run#4
apresmoi merged 7 commits into
mainfrom
feat/operational-ecosystem

Conversation

@apresmoi

Copy link
Copy Markdown
Collaborator

What

A sealed run now reports what it cost. observe derives per-agent and per-engine token usage from the exported Daimon ledger at raw/daimon/usage.jsonl, surfaced as usage[] and usage_summary on the observe report.

No lifecycle-contract change was needed. Simfile already reads exported raw artifacts directly — this follows exactly how memoryBanks.ts derives per-bank counts from raw/mneme/<bank>/. The pinned 43-row capability contract is untouched, so this needs no coordinated Spawnfile release.

Boundaries hold: no Spawnfile import, own zod schema with .strict() so an unknown key is rejected rather than stripped, file reads only, nothing selects or wakes cognition.

The lower-bound guarantee is structural

usage_summary.lower_bound is z.literal(true), not z.boolean(). A report asserting these counts are exact is not representable in simfile.observe.v1 — there is a test that a lower_bound: false report fails to parse.

This matters because the qualification is load-bearing. An all-zero usage block means unknown, not free; AGY reports no cost field at all; codex is unmetered by design. unknown_turns carries the producer's complete: false signal per agent and in aggregate. A report that quietly presented these as exact would recreate the exact failure this feature exists to prevent — a dead subscription that looked like zero cost.

Absent is not zero

collectUsage returns undefined when neither ledger generation exists, and the report omits usage entirely — following worldGrants.ts, which already establishes that convention and documents it. A present-but-empty ledger aggregates to [], an observed zero. The two are distinguishable.

Both rotation generations are read, oldest first. Engine attribution comes from an agent's earliest record, so an agent that switches engine mid-run reports the one it started on.

Verification

1687 tests passing. tsc --noEmit clean on both tsconfig.json and tsconfig.web.json.

Four mutations, each confirmed against the named subtest it kills rather than the failure count: reversing generation order, treating an absent ledger as empty, relaxing lower_bound to a boolean, and dropping the incomplete-turn signal.

Related

Depends on the export side in noopolis/spawnfile#7, which carries both ledger generations into exported run artifacts.

@apresmoi
apresmoi merged commit a83f196 into main Aug 30, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant