Implementations lists where each common agent tool can be hooked, and what it can fill.
Map your records
1
Decide what a node and an attempt are
A node is a unit of work: a task, a subagent role, a step in a graph. An attempt is one try at a node: a session, a subagent run, one pass of a step. A retry is a new attempt at the same node, never an edit of the old one, and its
cause says why it happened.2
Choose stable ids
Ids are scoped to your producer. Derive them from your tool’s own ids, so writing the same run twice gives the same ids. A common pattern is
<node>.a<n> for attempts.3
Record the model and effort as your tool knows them
Always write
model.raw, the label exactly as your tool had it. Add model.id only when you know the provider’s canonical id. Reasoning effort goes in effort, never in the model name.4
Record cost per attempt, per stream
Write each token stream separately. OCP’s
input_tokens is uncached input: if your source counts cache reads inside input, as Codex, the OpenTelemetry GenAI conventions, LangChain and the OpenAI Agents SDK do, subtract them. Set basis to measured only when the counts come from usage records of that attempt; if you split a session or daily total across attempts, write allocated. Dollars are optional; if you write usd, write the tariff you priced it with.5
Record outcomes with their evidence
When an attempt ends, write its
status and an outcome whose result matches it. Say how the result is known with evidence: a check that ran is verified, a reviewer’s verdict reported. via names the node that judged, and receipt holds a short label. See Evidence.6
Add edges with their tier
If your tool knows the order or lineage of work, add edges:
dep for “must finish first”, spawn when one attempt started another, artifact when one read what another wrote. Every edge carries a tier.7
Declare capabilities
producer.capabilities says which elements you can write. A false promises the element is absent, so a reader can tell “not recorded” from “did not happen”.A minimal producer
Standard library only. It writes a two-node run with a review, and passesloopmath ocp validate with no findings.
emit.py
Checklist
-
privacy.profileis set, and undermetadata_onlyno field holds prompt, completion or transcript text. - Every free-text field is a short label. The schema caps label fields at 500 characters; strings it does not cap, such as
task.featuresvalues, settingoptionsandextpayloads, hold no transcript text either. - Timestamps carry an explicit offset.
- Your own data sits in
extunder a namespace you control, such ascom.example.. See Extensions. - Documents validate in CI. See Validate.
Going further: the observation
The 0.3 observation fields make a run comparable with other runs: the task, the configuration that ran with its content-derived id, the signals the result was judged on, and the acceptance rule. They are optional. Fillrun.task and run.signals when your tool knows the task and runs checks; fill run.configuration when your tool has a declared workflow, and compute its id with the canonical algorithm.