<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE rfc [
  <!ENTITY nbsp    "&#160;">
  <!ENTITY zwsp   "&#8203;">
  <!ENTITY nbhy   "&#8209;">
  <!ENTITY wj     "&#8288;">
]>
<rfc xmlns:xi="http://www.w3.org/2001/XInclude"
     ipr="trust200902"
     docName="draft-arsentev-agent-run-metrics-00"
     category="info"
     submissionType="IETF"
     consensus="false"
     tocInclude="true"
     tocDepth="3"
     sortRefs="true"
     symRefs="true"
     version="3">

  <front>
    <title abbrev="Agent Run Metrics">Agent Run Metrics: A JSON Interchange Format for Resource Accounting of Language-Model Agent Runs</title>
    <seriesInfo name="Internet-Draft" value="draft-arsentev-agent-run-metrics-00" stream="IETF"/>

    <author fullname="Evgenii Arsentev" initials="E." surname="Arsentev">
      <organization>Independent</organization>
      <address>
        <email>hello@arsentev.ai</email>
        <uri>https://arsentev.ai/</uri>
      </address>
    </author>

    <date/>

    <keyword>agent</keyword>
    <keyword>metrics</keyword>
    <keyword>accounting</keyword>
    <keyword>large language model</keyword>
    <keyword>observability</keyword>

    <abstract>
      <t>Autonomous software agents driven by large language models execute
      multi-step runs in which the same conversational context is retransmitted
      to a model on every step. The resulting resource consumption is dominated
      by repeated input rather than by generated output, and it is reported today
      in mutually incompatible, vendor-specific shapes. This document defines
      Agent Run Metrics, a JSON interchange format that describes the resource
      consumption of a single agent run and of its constituent steps, together
      with a small set of derived quantities whose computation is specified
      exactly. It states normatively that one reported step corresponds to one
      completed model invocation, so that the several records a runtime may
      write while a single response is produced are not mistaken for several
      invocations, and it defines how the consumption of delegated sub-runs is
      attributed without being counted twice. The format carries counters,
      timing and cost attribution only; it deliberately excludes prompt and
      completion content. This document also registers the associated media
      type and creates IANA registries for
      extensible enumerations.</t>
    </abstract>
  </front>

  <middle>

    <section anchor="introduction" numbered="true">
      <name>Introduction</name>

      <t>A growing class of software systems operates as an "agent": a program
      that pursues a goal over a sequence of steps, at each step submitting an
      accumulated context to a large language model, receiving a response, and
      optionally invoking external tools whose results are appended to that
      context. The unit of work in such a system is not a single request but a
      <em>run</em>: a bounded sequence of steps sharing one accumulating
      context.</t>

      <t>Runs have a cost structure that differs qualitatively from that of the
      request/response interactions the surrounding infrastructure was designed
      to meter. Because the context accumulates, the input submitted at step
      <em>n</em> generally contains the input submitted at step <em>n-1</em>.
      Over a run of many steps, total input volume therefore grows super-linearly
      in the number of steps even when each individual step adds little. Content
      admitted into the context early is re-submitted on every subsequent step,
      so the position at which content enters a run affects total consumption as
      much as its size does. Practitioners consequently need per-run and per-step
      accounting, not merely per-request accounting, and they need it in a form
      that can be compared across implementations.</t>

      <t>Such accounting exists today, but only in vendor-specific shapes.
      Different implementations disagree on questions that are not matters of
      taste: whether tokens served from a provider-side cache are included in the
      reported input count or reported beside it; whether internal reasoning
      tokens that are never returned to the caller are part of the output count;
      whether the several records a runtime writes while one response is
      produced describe one invocation or several; whether the tokens of a
      delegated sub-run belong to that sub-run, to the run that started it, or
      to both; whether a retried step is one step or two; whether the totals for a run are
      the sum of its steps. Two reports that use the same field names can
      therefore describe incompatible quantities, and a consumer cannot detect
      the difference from the data alone.</t>

      <t>These are not questions of presentation. An accounting rule chosen
      wrongly changes a reported figure by a factor rather than by a percentage:
      a run whose journal records the progress of a response, rather than only
      its completion, will be reported with several times its true input if
      those records are summed, and a delegation tree whose parent and children
      both claim the children's tokens will be reported with roughly twice its
      true consumption. Errors of this class are silent, because the resulting
      report is internally consistent and no field states which rule was
      applied. <xref target="conclusion"/> records where such errors were
      observed.</t>

      <t>This document defines a JSON format, <em>Agent Run Metrics</em>, in
      which those questions are answered normatively. The format is small,
      content-free, and intended to be produced by an agent runtime and consumed
      by cost-management, capacity-planning, research and audit tooling.</t>

      <section anchor="scope" numbered="true">
        <name>Scope and Non-Goals</name>
        <t>This document specifies:</t>
        <ul>
          <li>a data model for a run, its steps, and their resource usage
          (<xref target="datamodel"/>);</li>
          <li>a JSON serialization of that data model
          (<xref target="serialization"/>);</li>
          <li>exact definitions for a small set of derived quantities
          (<xref target="derived"/>);</li>
          <li>an incremental reporting profile for runs observed while in
          progress (<xref target="incremental"/>);</li>
          <li>an OPTIONAL binding to HTTP for delivering reports to a collector
          (<xref target="transport"/>);</li>
          <li>the media type and IANA registries required by the above
          (<xref target="iana"/>).</li>
        </ul>

        <t>This document does not specify:</t>
        <ul>
          <li>any protocol between an agent and a language model, or between an
          agent and its tools;</li>
          <li>any pricing, billing or settlement mechanism; cost fields in this
          format are attributions supplied by the reporter, not invoices;</li>
          <li>the semantic content of a run: prompts, completions, tool
          arguments and tool results are out of scope and, by
          <xref target="content"/>, MUST NOT appear in a report;</li>
          <li>a distributed tracing system. Where a tracing system is in use,
          <xref target="correlation"/> defines how to correlate with it.</li>
        </ul>
      </section>

      <section anchor="relationship" numbered="true">
        <name>Relationship to Existing Work</name>
        <t>Distributed tracing conventions such as <xref target="TRACECONTEXT"/>
        and the semantic conventions maintained by the OpenTelemetry project
        <xref target="OTEL-GENAI"/> describe how to represent spans of execution,
        including spans that invoke a language model. Those conventions address
        the shape of a trace and the propagation of trace identity; they do not
        define an interchange document for a completed run, and they do not
        normatively resolve the counting questions listed in
        <xref target="introduction"/>. The format defined here is complementary:
        it is a self-contained report about one run, it can be produced without a
        tracing pipeline, and <xref target="correlation"/> specifies how a report
        references trace identifiers when a tracing pipeline is present.</t>

        <t>This format is not a metrics protocol in the sense of a time-series
        exposition format. A report describes one identified run; aggregation
        across runs is a function of the consumer.</t>
      </section>
    </section>

    <section anchor="conventions" numbered="true">
      <name>Conventions and Definitions</name>

      <t>The key words "<bcp14>MUST</bcp14>", "<bcp14>MUST NOT</bcp14>",
      "<bcp14>REQUIRED</bcp14>", "<bcp14>SHALL</bcp14>",
      "<bcp14>SHALL NOT</bcp14>", "<bcp14>SHOULD</bcp14>",
      "<bcp14>SHOULD NOT</bcp14>", "<bcp14>RECOMMENDED</bcp14>",
      "<bcp14>NOT RECOMMENDED</bcp14>", "<bcp14>MAY</bcp14>", and
      "<bcp14>OPTIONAL</bcp14>" in this document are to be interpreted as
      described in BCP&nbsp;14 <xref target="RFC2119"/> <xref target="RFC8174"/>
      when, and only when, they appear in all capitals, as shown here.</t>

      <dl newline="false" spacing="normal">
        <dt>Run:</dt>
        <dd>A bounded sequence of Steps executed by one Agent in pursuit of one
        goal, sharing a single accumulating Context. A Run is the unit described
        by a Report.</dd>

        <dt>Step:</dt>
        <dd>One indivisible unit of work within a Run: a single Model
        Invocation, a single Tool Call, or another unit whose kind is registered
        per <xref target="iana-kinds"/>.</dd>

        <dt>Model Invocation:</dt>
        <dd>A single request to a language model and the corresponding response,
        taken as one unit from the submission of the request to the completion
        of the response. A response delivered incrementally, or composed of
        several content blocks, is one Model Invocation and not several; see
        <xref target="dm-invocation"/>.</dd>

        <dt>Context:</dt>
        <dd>The sequence of tokens submitted as input to a Model Invocation.</dd>

        <dt>Token:</dt>
        <dd>The unit in which a model counts input and output. Token counts are
        model-specific; a Report does not assert that counts from different
        models are comparable.</dd>

        <dt>Delegated Run:</dt>
        <dd>A Run started by an Agent in the course of another Run, in pursuit
        of a subordinate goal and with its own Context. Such Runs are also
        called sub-agent Runs.</dd>

        <dt>Delegation Tree:</dt>
        <dd>A Run together with every Run reachable from it by delegation.</dd>

        <dt>Cached Prefix:</dt>
        <dd>A portion of a Context that a model provider serves from
        provider-side state established by an earlier Model Invocation, rather
        than processing afresh.</dd>

        <dt>Cache Lifetime:</dt>
        <dd>The period for which a provider undertakes to retain the state
        underlying a Cached Prefix.</dd>

        <dt>Reporter:</dt>
        <dd>The party that produces a Report. Typically the agent runtime.</dd>

        <dt>Collector:</dt>
        <dd>The party that receives and processes a Report.</dd>

        <dt>Report:</dt>
        <dd>One JSON document conforming to this specification.</dd>
      </dl>
    </section>

    <section anchor="datamodel" numbered="true">
      <name>Data Model</name>

      <t>A Report is a Run object. A Run object contains a Totals object, zero or
      more Step objects, and OPTIONAL descriptive and cost members. A Step object
      contains a Usage object when the Step consumed model resources.</t>

      <figure anchor="fig-model">
        <name>Structure of a Report</name>
        <artwork type="ascii-art"><![CDATA[
  Run
   +- run_id, start, end, status, termination_reason
   +- agent { name, version }
   +- models [ { id, provider, context_window } ]
   +- totals   -> Usage
   +- cost     -> Cost
   +- derived  -> Derived
   +- steps [ Step ]
                +- index, kind, start, end
                +- usage -> Usage        (model_invocation)
                +- tool  -> Tool         (tool_call)
                +- error -> Error
]]></artwork>
      </figure>

      <section anchor="dm-run" numbered="true">
        <name>The Run Object</name>
        <t>A Run object has the members defined in
        <xref target="tbl-run"/>.</t>

        <table anchor="tbl-run">
          <name>Members of the Run object</name>
          <thead>
            <tr><th>Member</th><th>Type</th><th>Presence</th><th>Description</th></tr>
          </thead>
          <tbody>
            <tr><td>version</td><td>string</td><td>REQUIRED</td><td>Format version; "1" for this specification.</td></tr>
            <tr><td>run_id</td><td>string</td><td>REQUIRED</td><td>Identifier of the Run, unique within the Reporter's scope.</td></tr>
            <tr><td>parent_run_id</td><td>string</td><td>OPTIONAL</td><td>run_id of the Run that delegated this Run; see <xref target="dm-hierarchy"/>.</td></tr>
            <tr><td>root_run_id</td><td>string</td><td>OPTIONAL</td><td>run_id of the Run at the root of the Delegation Tree; see <xref target="dm-hierarchy"/>.</td></tr>
            <tr><td>start</td><td>timestamp</td><td>REQUIRED</td><td>Instant at which the Run began.</td></tr>
            <tr><td>end</td><td>timestamp</td><td>see text</td><td>Instant at which the Run reached a terminal status.</td></tr>
            <tr><td>status</td><td>string</td><td>REQUIRED</td><td>One of "running", "completed", "failed", "aborted".</td></tr>
            <tr><td>termination_reason</td><td>string</td><td>OPTIONAL</td><td>A registered reason; see <xref target="iana-reasons"/>.</td></tr>
            <tr><td>agent</td><td>object</td><td>OPTIONAL</td><td>Descriptor of the producing agent; see <xref target="dm-agent"/>.</td></tr>
            <tr><td>models</td><td>array</td><td>OPTIONAL</td><td>Descriptors of the models used; see <xref target="dm-model"/>.</td></tr>
            <tr><td>step_count</td><td>integer</td><td>REQUIRED</td><td>Number of Steps in the Run, including Steps omitted from "steps".</td></tr>
            <tr><td>steps</td><td>array</td><td>OPTIONAL</td><td>Step objects; see <xref target="dm-step"/>.</td></tr>
            <tr><td>totals</td><td>object</td><td>REQUIRED</td><td>Usage object aggregating this Run alone.</td></tr>
            <tr><td>subtree_totals</td><td>object</td><td>OPTIONAL</td><td>Usage object aggregating this Run and its Delegated Runs; see <xref target="dm-hierarchy"/>.</td></tr>
            <tr><td>cost</td><td>object</td><td>OPTIONAL</td><td>Cost object for the whole Run; see <xref target="dm-cost"/>.</td></tr>
            <tr><td>derived</td><td>object</td><td>OPTIONAL</td><td>Derived quantities; see <xref target="derived"/>.</td></tr>
            <tr><td>trace_id</td><td>string</td><td>OPTIONAL</td><td>Correlation identifier; see <xref target="correlation"/>.</td></tr>
            <tr><td>labels</td><td>object</td><td>OPTIONAL</td><td>Reporter-defined string-to-string labels; see <xref target="dm-labels"/>.</td></tr>
          </tbody>
        </table>

        <t>The "end" member <bcp14>MUST</bcp14> be present when "status" is
        "completed", "failed" or "aborted", and <bcp14>MUST NOT</bcp14> be
        present when "status" is "running". When present, "end"
        <bcp14>MUST NOT</bcp14> be earlier than "start".</t>

        <t>The "status" member is a closed enumeration; a Reporter
        <bcp14>MUST NOT</bcp14> emit a value other than the four listed, and a
        Collector that encounters an unrecognised value <bcp14>MUST</bcp14>
        treat the Report as malformed. Distinctions finer than these four values
        are expressed through "termination_reason", which is extensible.</t>

        <t>The "step_count" member <bcp14>MUST</bcp14> equal the total number of
        Steps executed in the Run. When the "steps" array is present,
        "step_count" <bcp14>MUST</bcp14> be greater than or equal to the length
        of that array; a Reporter <bcp14>MAY</bcp14> omit individual Steps from
        the array, for example to bound Report size, while still accounting for
        them in "step_count" and in "totals".</t>
      </section>

      <section anchor="dm-step" numbered="true">
        <name>The Step Object</name>

        <table anchor="tbl-step">
          <name>Members of the Step object</name>
          <thead>
            <tr><th>Member</th><th>Type</th><th>Presence</th><th>Description</th></tr>
          </thead>
          <tbody>
            <tr><td>index</td><td>integer</td><td>REQUIRED</td><td>Zero-based position of the Step within the Run.</td></tr>
            <tr><td>kind</td><td>string</td><td>REQUIRED</td><td>A registered Step kind; see <xref target="iana-kinds"/>.</td></tr>
            <tr><td>start</td><td>timestamp</td><td>REQUIRED</td><td>Instant at which the Step began.</td></tr>
            <tr><td>end</td><td>timestamp</td><td>OPTIONAL</td><td>Instant at which the Step ended.</td></tr>
            <tr><td>model</td><td>string</td><td>see text</td><td>Identifier of the model invoked.</td></tr>
            <tr><td>invocation_id</td><td>string</td><td>see text</td><td>Identifier of the Model Invocation; see <xref target="dm-invocation"/>.</td></tr>
            <tr><td>usage</td><td>object</td><td>see text</td><td>Usage object for this Step.</td></tr>
            <tr><td>cost</td><td>object</td><td>OPTIONAL</td><td>Cost object for this Step.</td></tr>
            <tr><td>tool</td><td>object</td><td>see text</td><td>Tool object; see <xref target="dm-tool"/>.</td></tr>
            <tr><td>error</td><td>object</td><td>OPTIONAL</td><td>Error object; see <xref target="dm-error"/>.</td></tr>
            <tr><td>child_run_id</td><td>string</td><td>see text</td><td>run_id of the Run executed by a Step of kind "delegation"; see <xref target="dm-hierarchy"/>.</td></tr>
            <tr><td>retry_of</td><td>integer</td><td>OPTIONAL</td><td>index of the Step this Step retries.</td></tr>
            <tr><td>span_id</td><td>string</td><td>OPTIONAL</td><td>Correlation identifier; see <xref target="correlation"/>.</td></tr>
          </tbody>
        </table>

        <t>Within one Run, "index" values <bcp14>MUST</bcp14> be unique and
        <bcp14>MUST</bcp14> be assigned in the order in which Steps began, with
        no requirement that the values present in a Report be contiguous. Steps
        that overlap in time are permitted; "index" then reflects start order
        only.</t>

        <t>When "kind" is "model_invocation", the "usage" and "model" members
        <bcp14>MUST</bcp14> be present. When "kind" is "tool_call", the "tool"
        member <bcp14>MUST</bcp14> be present and the "usage" member
        <bcp14>MUST NOT</bcp14> be present; resources consumed by placing the
        tool result into the Context are attributed to the subsequent Model
        Invocation, where they are actually submitted.</t>

        <t>A retried attempt <bcp14>MUST</bcp14> be reported as a distinct Step
        with its own "index" and its own "usage", and <bcp14>SHOULD</bcp14>
        carry "retry_of" identifying the first attempt. Reporting a set of
        attempts as one Step with summed counters loses the distinction between
        work that advanced the Run and work that did not, and
        <bcp14>MUST NOT</bcp14> be done.</t>
      </section>

      <section anchor="dm-invocation" numbered="true">
        <name>One Step per Completed Model Invocation</name>

        <t>A Step of kind "model_invocation" <bcp14>MUST</bcp14> describe exactly
        one completed Model Invocation, and a Report <bcp14>MUST NOT</bcp14>
        contain more than one Step describing the same Model Invocation.</t>

        <t>This requirement is stated separately, and at length, because the
        journals from which Reporters are commonly built do not satisfy it. A
        model response is typically composed of several content blocks: internal
        reasoning, text addressed to the caller, and requests to invoke tools. A
        runtime frequently appends one journal record per block, or one record
        per streaming update, and each such record carries a usage structure of
        its own. Those records are not independent measurements of independent
        work:</t>

        <ul>
          <li>the input counters ("input_tokens", "cache_read_tokens",
          "cache_write_tokens") are identical in every record of the group,
          because the input was submitted once;</li>
          <li>the output counter is a cumulative snapshot of one response as it
          is produced, so the records of a group form a non-decreasing sequence
          that ends at the final count.</li>
        </ul>

        <t>Two naive treatments of such a journal therefore fail in opposite
        directions. Summing the records multiplies the input of the invocation by
        the number of records in its group and counts most of its output more
        than once. Retaining only the first record of each group understates the
        output by the ratio of the earliest snapshot to the final count, which
        may be an order of magnitude. Both errors are silent: the Report that
        results satisfies every other invariant in this document, and a Collector
        cannot detect either from the data. In the corpus described in
        <xref target="conclusion"/>, the first of these errors overstated total
        consumption by a factor of about 1.90 and the second halved the reported
        output.</t>

        <t>Accordingly:</t>

        <ol>
          <li>A partial or intermediate usage structure, observed while a
          response is still being produced, is not a metric. A Reporter
          <bcp14>MUST NOT</bcp14> emit a Step for it and
          <bcp14>MUST NOT</bcp14> include its counters in "totals".</li>

          <li>The "output_tokens" of a Step of kind "model_invocation"
          <bcp14>MUST</bcp14> be the count for the whole completed response. It
          <bcp14>MUST NOT</bcp14> be a snapshot taken before the response ended,
          and <bcp14>MUST NOT</bcp14> be the sum of the snapshots observed while
          it was produced.</li>

          <li>A response composed of several content blocks
          <bcp14>MUST</bcp14> be reported as one Step whose counters cover all of
          its blocks. Reasoning blocks are accounted for within that Step by
          "reasoning_tokens", and requests to invoke tools are part of its
          "output_tokens"; the Tool Calls to which those requests give rise are
          separate Steps of kind "tool_call".</li>

          <li>A Reporter deriving Reports from a source that emits several
          records per Model Invocation <bcp14>MUST</bcp14> group those records by
          an identifier of the invocation and emit one Step per group. It
          <bcp14>MUST NOT</bcp14> group by timestamp, by model identifier or by
          adjacency in the source, because concurrent invocations interleave and
          such grouping silently merges or splits them.</li>
        </ol>

        <t>A Step of kind "model_invocation" <bcp14>SHOULD</bcp14> carry
        "invocation_id", a string identifying the Model Invocation and unique
        within the Run. Where the source of a Report distinguishes invocations by
        such an identifier, the Reporter <bcp14>MUST</bcp14> use it both as the
        grouping key required above and as the value of this member, so that a
        Collector can verify the grouping instead of trusting it. A Reporter
        <bcp14>MUST NOT</bcp14> emit two Steps of one Run with the same
        "invocation_id". A Collector that receives a Report containing two such
        Steps <bcp14>MUST</bcp14> treat the Report as malformed rather than
        silently discard one of them, since it cannot know which carries the
        final counters.</t>

        <t>An invocation that never completed, because it failed, was cancelled
        or was abandoned, <bcp14>MAY</bcp14> be reported as a Step carrying an
        Error object together with whatever counters the provider metered for it.
        Such a Step is a completed unit of accounting even though it is not a
        completed response. The prohibition above concerns snapshots of an
        invocation that is still producing output, not invocations that ended
        early.</t>
      </section>

      <section anchor="dm-usage" numbered="true">
        <name>The Usage Object</name>

        <t>The Usage object is the core of this format, and the definitions in
        this section are the ones on which interoperability depends.</t>

        <table anchor="tbl-usage">
          <name>Members of the Usage object</name>
          <thead>
            <tr><th>Member</th><th>Type</th><th>Presence</th><th>Description</th></tr>
          </thead>
          <tbody>
            <tr><td>input_tokens</td><td>integer</td><td>REQUIRED</td><td>Total tokens submitted as input.</td></tr>
            <tr><td>output_tokens</td><td>integer</td><td>REQUIRED</td><td>Total tokens produced by the model.</td></tr>
            <tr><td>cache_read_tokens</td><td>integer</td><td>OPTIONAL</td><td>Input tokens served from a Cached Prefix.</td></tr>
            <tr><td>cache_write_tokens</td><td>integer</td><td>OPTIONAL</td><td>Input tokens stored into provider-side cache state.</td></tr>
            <tr><td>cache_writes</td><td>array</td><td>OPTIONAL</td><td>Breakdown of "cache_write_tokens" by Cache Lifetime; see <xref target="dm-cachelife"/>.</td></tr>
            <tr><td>reasoning_tokens</td><td>integer</td><td>OPTIONAL</td><td>Output tokens not returned to the caller.</td></tr>
            <tr><td>billable_input_tokens</td><td>integer</td><td>OPTIONAL</td><td>Input tokens the provider metered.</td></tr>
            <tr><td>billable_output_tokens</td><td>integer</td><td>OPTIONAL</td><td>Output tokens the provider metered.</td></tr>
          </tbody>
        </table>

        <t>All members of a Usage object <bcp14>MUST</bcp14> be non-negative
        integers.</t>

        <t>The following invariants are normative. A Reporter
        <bcp14>MUST NOT</bcp14> emit a Usage object that violates them, and a
        Collector <bcp14>MUST</bcp14> treat a Report containing such an object
        as malformed:</t>

        <ol>
          <li>"input_tokens" is the count of <em>all</em> tokens submitted as
          input, whether or not they were served from a Cached Prefix.
          "cache_read_tokens" is a subset of "input_tokens" and therefore
          <bcp14>MUST</bcp14> be less than or equal to it. A Reporter
          <bcp14>MUST NOT</bcp14> report cached tokens beside "input_tokens" as
          a disjoint quantity.</li>

          <li>"output_tokens" is the count of <em>all</em> tokens produced,
          including tokens consumed internally by the model and never returned
          to the caller. "reasoning_tokens" is a subset of "output_tokens" and
          therefore <bcp14>MUST</bcp14> be less than or equal to it.</li>

          <li>"cache_write_tokens" counts tokens that were submitted as input
          <em>and</em>, in the course of the same Model Invocation, stored into
          provider-side cache state for reuse by later invocations; it is
          likewise a subset of "input_tokens". A token that was served from a
          Cached Prefix was not processed and stored afresh in the same
          invocation, so "cache_read_tokens" and "cache_write_tokens" denote
          disjoint subsets of "input_tokens" and their sum
          <bcp14>MUST</bcp14> be less than or equal to "input_tokens". A
          Reporter <bcp14>MUST NOT</bcp14> report tokens stored into cache state
          beside "input_tokens" as a disjoint quantity, and
          <bcp14>MUST NOT</bcp14> report them a second time under
          "cache_read_tokens" merely because a later invocation read them.</li>

          <li>The "billable_" members carry the counts a provider used for
          metering, which may differ from the physical counts because of
          discounts applied to cached tokens, rounding, or minimum charges.
          They are unconstrained relative to the physical counts. A Reporter
          that has no metering information <bcp14>MUST</bcp14> omit them rather
          than copy the physical counts, so that a Collector can distinguish
          "metered and equal" from "not known".</li>
        </ol>

        <t>The distinction between the three ways in which an input token can be
        handled, namely processed afresh, served from a Cached Prefix, or
        processed and stored into cache state, is not a refinement of interest
        only to implementers. Providers price the three differently, commonly
        charging a premium for tokens stored and a discount for tokens served
        from store, so two Runs with identical "input_tokens" can differ in cost
        by a large factor. In the corpus described in
        <xref target="conclusion"/>, tokens served from a Cached Prefix
        accounted for about 56 percent of attributed cost and tokens stored into
        cache state for about 27 percent, leaving less than a fifth for input
        processed afresh and for all output together; storing into cache was thus
        the second largest component of cost rather than a marginal one. A
        Reporter that omits "cache_read_tokens" and "cache_write_tokens" leaves
        the greater part of the cost of a Run unattributable, and
        <bcp14>SHOULD</bcp14> emit both whenever the provider reports them.</t>

        <t>When a Run's "steps" array contains every Step of the Run, the
        "totals" object <bcp14>MUST</bcp14> be, member by member, the sum of the
        corresponding members of every Step's Usage object, with the exception of
        "cache_writes", whose aggregation is defined in
        <xref target="dm-cachelife"/>, and a member
        <bcp14>MUST</bcp14> be omitted from "totals" if it is absent from every
        Step. When the "steps" array is incomplete, "totals"
        <bcp14>MUST</bcp14> still account for the whole Run, including omitted
        Steps.</t>
      </section>

      <section anchor="dm-cachelife" numbered="true">
        <name>Cache Lifetime</name>

        <t>Provider-side cache state is retained for a bounded period, and a
        provider may offer several such periods at different prices. Two Model
        Invocations that stored the same number of tokens may therefore have been
        charged differently, and a single "cache_write_tokens" figure cannot
        distinguish them. A Usage object <bcp14>MAY</bcp14> for that reason carry
        "cache_writes", an array of objects each having the REQUIRED members
        "lifetime" and "tokens".</t>

        <t>The "lifetime" member <bcp14>MUST</bcp14> be a string conforming to the
        "duration" rule of Appendix A of <xref target="RFC3339"/>, giving the
        nominal retention period that the Reporter requested or that the provider
        applied, for example "PT5M" or "PT1H". The value states a nominal period
        and not an observed one; whether the state in fact survived that long is
        not reported. The "tokens" member <bcp14>MUST</bcp14> be a non-negative
        integer. One "lifetime" value <bcp14>MUST NOT</bcp14> appear in more than
        one element of the same array.</t>

        <t>The sum of the "tokens" members of "cache_writes"
        <bcp14>MUST</bcp14> equal the "cache_write_tokens" member of the same
        Usage object. This does not always hold in the sources from which
        Reporters are built: in the corpus described in
        <xref target="conclusion"/>, a small number of records carried
        per-lifetime counters whose sum differed from the aggregate counter in
        the same record. A Reporter whose source does not reconcile
        <bcp14>MUST</bcp14> omit "cache_writes" for the affected Step. It
        <bcp14>MUST NOT</bcp14> adjust either figure so that they agree, and
        <bcp14>MUST NOT</bcp14> substitute the sum of the per-lifetime counters
        for "cache_write_tokens", because a discrepancy is evidence that at least
        one of the two figures is wrong, and either repair would assert an
        agreement that was never observed.</t>

        <t>A Collector <bcp14>MUST</bcp14> treat as malformed a Report in which
        "cache_writes" is present and does not sum to "cache_write_tokens". Where
        "cache_writes" is absent, a Collector <bcp14>MUST NOT</bcp14> assume any
        particular lifetime.</t>

        <t>Where the Usage objects of Steps carry "cache_writes", a "totals" or
        "subtree_totals" object that carries it <bcp14>MUST</bcp14> contain one
        element for each distinct "lifetime" that appears, whose "tokens" is the
        sum of the "tokens" reported for that lifetime; this is the aggregation
        rule referred to in <xref target="dm-usage"/>.</t>

        <t>There is deliberately no corresponding breakdown of
        "cache_read_tokens". A read is served from whatever state exists at the
        moment of the read, and attributing it to the lifetime with which that
        state was created would require knowledge the reader does not have.</t>
      </section>

      <section anchor="dm-hierarchy" numbered="true">
        <name>Delegated Runs and Aggregation</name>

        <t>An Agent commonly pursues part of its goal by starting a subordinate
        Run with its own Context, and that Run may itself delegate further. A
        Delegated Run is a Run in the sense of this document: it has its own
        "run_id", its own Steps and its own "totals", and it is described by its
        own Report. In the corpus described in <xref target="conclusion"/>,
        slightly more than half of all Model Invocations occurred in Delegated
        Runs rather than in the Runs that started them, so a treatment that
        ignored delegation would describe a minority of the work.</t>

        <t>The relationship is expressed in three places, and a Reporter that
        observes both sides <bcp14>SHOULD</bcp14> populate all three:</t>

        <ul>
          <li>the child Report's "parent_run_id", naming the Run that delegated
          it;</li>

          <li>the child Report's "root_run_id", naming the Run at the root of the
          Delegation Tree. A Run that emits "root_run_id" and has no parent
          <bcp14>MUST</bcp14> set it equal to its own "run_id"; a Run that emits
          it and has a parent <bcp14>MUST</bcp14> set it to the "root_run_id" of
          that parent. The member exists so that a Collector can select a whole
          Tree without walking it edge by edge;</li>

          <li>in the parent, a Step of kind "delegation" spanning the interval
          during which the child ran, carrying "child_run_id" where the parent
          knows the child's identifier.</li>
        </ul>

        <t>The counting rule is that "totals" is per-Run and is not transitive.
        The "totals" of a Run <bcp14>MUST</bcp14> account for the Steps of that
        Run alone and <bcp14>MUST NOT</bcp14> include any resource consumed by a
        Delegated Run, whether or not that Run is separately reported. A Step of
        kind "delegation" accordingly carries no Usage object, as
        <xref target="iana-kinds"/> requires: the delegating Run submits no
        Context of its own while the child runs. The resources consumed in
        placing the child's result into the parent's Context are attributed, like
        any other addition to a Context, to the parent's next Model
        Invocation.</t>

        <t>A Run <bcp14>MAY</bcp14> additionally carry "subtree_totals", a Usage
        object accounting for that Run together with every Run reachable from it
        by delegation. When present, each member of "subtree_totals"
        <bcp14>MUST</bcp14> be greater than or equal to the corresponding member
        of "totals". A Reporter that cannot observe the whole Tree
        <bcp14>MUST</bcp14> omit "subtree_totals" rather than emit a partial
        figure under that name.</t>

        <t>A Collector computing the consumption of a Delegation Tree
        <bcp14>MUST</bcp14> use exactly one of two methods: sum the "totals" of
        every Run of the Tree, or take the "subtree_totals" of its root. It
        <bcp14>MUST NOT</bcp14> combine them, and in particular
        <bcp14>MUST NOT</bcp14> add a Run's "subtree_totals" to the "totals" of
        that Run's descendants; either mistake counts the delegated work twice.
        Where Reports arrive independently, a Collector <bcp14>SHOULD</bcp14>
        prefer summing "totals", which remains well defined when the Tree is
        incomplete, and <bcp14>SHOULD</bcp14> treat "subtree_totals" as an
        assertion by the Reporter to be checked once the Tree is complete.</t>

        <t>Derived quantities are per-Run in the same sense. In particular
        "input_amplification" (<xref target="derived-amp"/>) is computed over the
        Steps of one Run; the value reported for a parent says nothing about its
        children, and the values of a Tree cannot be combined into a value for the
        Tree.</t>
      </section>

      <section anchor="dm-cost" numbered="true">
        <name>The Cost Object</name>

        <table anchor="tbl-cost">
          <name>Members of the Cost object</name>
          <thead>
            <tr><th>Member</th><th>Type</th><th>Presence</th><th>Description</th></tr>
          </thead>
          <tbody>
            <tr><td>currency</td><td>string</td><td>REQUIRED</td><td>Three-letter alphabetic currency code <xref target="ISO4217"/>.</td></tr>
            <tr><td>amount</td><td>string</td><td>REQUIRED</td><td>Decimal amount; see below.</td></tr>
            <tr><td>basis</td><td>string</td><td>REQUIRED</td><td>One of "metered", "list_price", "estimated".</td></tr>
            <tr><td>pricing_ref</td><td>string</td><td>OPTIONAL</td><td>URI identifying the price schedule used.</td></tr>
          </tbody>
        </table>

        <t>The "amount" member <bcp14>MUST</bcp14> be a JSON string matching the
        ABNF <xref target="RFC5234"/> rule:</t>

        <sourcecode type="abnf"><![CDATA[
amount = [ "-" ] 1*DIGIT [ "." 1*DIGIT ]
]]></sourcecode>

        <t>A string is used rather than a JSON number so that the value survives
        parsers that map numbers to binary floating point. A Collector
        <bcp14>MUST NOT</bcp14> convert an "amount" to a binary floating point
        value before comparison or summation.</t>

        <t>The "basis" member states the provenance of the figure.
        "metered" means the amount was obtained from the provider's metering of
        this Run or Step. "list_price" means it was computed by the Reporter from
        a published price schedule. "estimated" means it was computed by any
        other means. A Reporter <bcp14>MUST NOT</bcp14> report "metered" for a
        figure it computed itself.</t>

        <t>Cost figures are attributions made by the Reporter. They are not
        invoices and <bcp14>MUST NOT</bcp14> be treated as authoritative for
        settlement.</t>
      </section>

      <section anchor="dm-tool" numbered="true">
        <name>The Tool Object</name>
        <t>A Tool object describes a Step of kind "tool_call". It has the
        REQUIRED member "name", a string naming the tool within the Reporter's
        scope, and the REQUIRED member "outcome", whose value is one of "ok",
        "error" or "timeout". It <bcp14>MAY</bcp14> carry "bytes_in" and
        "bytes_out", non-negative integers giving the octet length of the
        arguments passed to and the result returned from the tool.</t>

        <t>The "name" member identifies a class of tool, not an instance, and
        <bcp14>MUST NOT</bcp14> encode arguments, targets, identifiers or any
        other content of the call; see <xref target="content"/>.</t>
      </section>

      <section anchor="dm-error" numbered="true">
        <name>The Error Object</name>
        <t>An Error object has the REQUIRED member "type", a short lowercase
        token classifying the failure, and the OPTIONAL boolean member
        "retryable". The "type" member <bcp14>MUST NOT</bcp14> carry a
        provider's free-text error message, which may quote the Context; see
        <xref target="content"/>.</t>
      </section>

      <section anchor="dm-agent" numbered="true">
        <name>The Agent Object</name>
        <t>An Agent object has the OPTIONAL string members "name" and "version",
        identifying the software that executed the Run. These members describe
        software, not the party operating it; see
        <xref target="privacy"/>.</t>
      </section>

      <section anchor="dm-model" numbered="true">
        <name>The Model Object</name>
        <t>A Model object has the REQUIRED string member "id" and the OPTIONAL
        string member "provider". It <bcp14>MAY</bcp14> carry
        "context_window", a positive integer giving the maximum number of input
        tokens the model accepts. Where reported, "context_window" allows a
        Collector to relate a Step's "input_tokens" to the limit that Step was
        approaching, which is the quantity of operational interest.</t>
      </section>

      <section anchor="dm-labels" numbered="true">
        <name>The Labels Object</name>
        <t>The "labels" member carries Reporter-defined dimensions along which a
        Collector may group Runs, such as a deployment environment or a workload
        class. Its value <bcp14>MUST</bcp14> be a JSON object whose members all
        have string values. Label keys and values <bcp14>MUST NOT</bcp14> carry
        content from the Run and <bcp14>SHOULD NOT</bcp14> carry identifiers of
        natural persons; see <xref target="privacy"/>.</t>
      </section>
    </section>

    <section anchor="derived" numbered="true">
      <name>Derived Quantities</name>

      <t>The quantities in this section are computable from the members already
      defined. They are specified here because they are the quantities in which
      the behaviour of a Run is usually discussed, because they are easy to
      compute in mutually incompatible ways, and because a Collector that
      receives an incomplete "steps" array cannot recompute them itself. A
      Reporter <bcp14>MAY</bcp14> omit the "derived" object entirely; if it
      includes any member of that object, the member
      <bcp14>MUST</bcp14> be computed exactly as specified below.</t>

      <section anchor="derived-amp" numbered="true">
        <name>Input Amplification</name>
        <t>Let <em>S</em> be the set of Steps of kind "model_invocation" in the
        Run, and let <em>i(s)</em> be the "input_tokens" of Step <em>s</em>.
        The member "input_amplification" is defined as:</t>

        <sourcecode type="abnf"><![CDATA[
input_amplification = ( sum over s in S of i(s) )
                      / ( max over s in S of i(s) )
]]></sourcecode>

        <t>It is a JSON number, and it <bcp14>MUST</bcp14> be omitted when
        <em>S</em> is empty or when the denominator is zero. Its value is at
        least 1. It expresses how many times the largest Context reached in the
        Run was, in effect, paid for. A Run of many Steps whose Context grows
        steadily has an input amplification of roughly half its Step count; a Run
        whose Context is fully rebuilt at each Step has one close to its Step
        count; a Run that does its work in a single Step has exactly 1.</t>

        <t>This quantity deliberately uses the largest Context reached, not the
        final Context, so that Runs that compact or truncate their Context remain
        comparable with Runs that do not.</t>

        <t>Observed values span orders of magnitude, which is the reason the
        quantity is worth reporting at all: in the corpus described in
        <xref target="conclusion"/>, the median Run had an input amplification of
        about 24, the ninetieth percentile about 125, and the largest value
        observed exceeded 4000. A single aggregate token count does not separate
        such Runs from one another. Because the quantity is computed over the
        Steps of one Run, a delegating Run and each of its Delegated Runs yield
        separate observations; see <xref target="dm-hierarchy"/>.</t>
      </section>

      <section anchor="derived-cache" numbered="true">
        <name>Cache Hit Ratio</name>
        <t>The member "cache_hit_ratio" is defined as the "cache_read_tokens" of
        the Run's "totals" divided by the "input_tokens" of the Run's "totals".
        It is a JSON number in the interval from 0 to 1 inclusive, and it
        <bcp14>MUST</bcp14> be omitted when "cache_read_tokens" is absent from
        "totals" or when "input_tokens" is zero.</t>
      </section>

      <section anchor="derived-cachewrite" numbered="true">
        <name>Cache Write Ratio</name>
        <t>The member "cache_write_ratio" is defined as the "cache_write_tokens"
        of the Run's "totals" divided by the "input_tokens" of the Run's
        "totals". It is a JSON number in the interval from 0 to 1 inclusive, and
        it <bcp14>MUST</bcp14> be omitted when "cache_write_tokens" is absent
        from "totals" or when "input_tokens" is zero.</t>

        <t>It is specified alongside "cache_hit_ratio" because the two answer
        different questions and neither can be inferred from the other. A high
        "cache_hit_ratio" says that the Run re-submitted a Context that was
        already stored; a high "cache_write_ratio" says that the Run kept
        establishing new cache state, which a provider commonly charges at a
        premium. A Run can exhibit both. By the disjointness required in
        <xref target="dm-usage"/>, the two ratios cannot sum to more than 1.</t>
      </section>

      <section anchor="derived-output" numbered="true">
        <name>Output Share</name>
        <t>The member "output_share" is defined as the "output_tokens" of the
        Run's "totals" divided by the sum of "input_tokens" and "output_tokens"
        of the Run's "totals". It is a JSON number in the interval from 0 to 1
        inclusive, and it <bcp14>MUST</bcp14> be omitted when that sum is
        zero.</t>
      </section>

      <section anchor="derived-density" numbered="true">
        <name>Step Efficiency</name>
        <t>The member "advancing_step_ratio" is defined as the number of Steps
        that do not carry a "retry_of" member and whose "error" member is absent,
        divided by "step_count". It is a JSON number in the interval from 0 to 1
        inclusive. A Reporter <bcp14>MUST NOT</bcp14> emit this member when its
        "steps" array is incomplete, because the numerator is then not
        determinable from the Report.</t>
      </section>

      <section anchor="derived-precision" numbered="true">
        <name>Numeric Precision</name>
        <t>The derived members are JSON numbers and are inherently approximate.
        A Reporter <bcp14>SHOULD NOT</bcp14> emit more than six significant
        digits. A Collector <bcp14>MUST NOT</bcp14> rely on a derived member
        being reproducible bit-for-bit from the Report's own counters, and
        <bcp14>SHOULD</bcp14> recompute derived quantities from counters when
        the "steps" array is complete.</t>
      </section>
    </section>

    <section anchor="serialization" numbered="true">
      <name>Serialization</name>

      <t>A Report <bcp14>MUST</bcp14> be a JSON <xref target="RFC8259"/> object
      conforming to the restricted profile of <xref target="RFC7493"/>. In
      particular, a Report <bcp14>MUST</bcp14> be encoded in UTF-8, its objects
      <bcp14>MUST NOT</bcp14> contain duplicate member names, and integers
      <bcp14>MUST</bcp14> be within the range that can be represented exactly,
      that is from -(2^53)+1 to (2^53)-1 inclusive.</t>

      <t>Timestamps <bcp14>MUST</bcp14> be strings conforming to the
      "date-time" production of <xref target="RFC3339"/>. They
      <bcp14>MUST</bcp14> use the "Z" time offset and <bcp14>SHOULD</bcp14>
      include at least milliseconds of fractional seconds. Local offsets are
      excluded because they disclose the operator's location without carrying
      information the format needs.</t>

      <t>Identifier members, namely "run_id", "parent_run_id", "root_run_id",
      "child_run_id", "invocation_id" and the "name" members of the Agent and
      Tool objects, <bcp14>MUST</bcp14> be non-empty
      strings of at most 128 characters. A Collector <bcp14>MAY</bcp14> reject a
      Report whose identifiers exceed that length.</t>

      <t>A machine-readable schema for the format is expected to accompany a
      future revision of this document. Where such a schema and the text of this
      document disagree, the text governs; the subset invariants of
      <xref target="dm-usage"/> and the computation rules of
      <xref target="derived"/> are not expressible in schema languages in common
      use.</t>

      <section anchor="extensibility" numbered="true">
        <name>Extensibility</name>
        <t>A Collector <bcp14>MUST</bcp14> ignore any object member it does not
        recognise, and <bcp14>MUST NOT</bcp14> treat its presence as an error,
        except where this document specifies otherwise for closed
        enumerations.</t>

        <t>A Reporter that adds members not defined in this document or in a
        registry established by it <bcp14>MUST</bcp14> name them with a prefix of
        the form "x-" followed by a label that is unlikely to collide, such as a
        reversed domain name. A Reporter <bcp14>MUST NOT</bcp14> use an
        unprefixed member name for a purpose other than the one specified
        here.</t>

        <t>The "version" member identifies the format, not the Reporter. A
        Reporter <bcp14>MUST</bcp14> emit "1" while conforming to this
        specification. A Collector that receives a "version" it does not
        recognise <bcp14>SHOULD</bcp14> reject the Report rather than interpret
        members whose meaning may have changed.</t>
      </section>

      <section anchor="correlation" numbered="true">
        <name>Correlation with Tracing Systems</name>
        <t>When the Reporter participates in a tracing system that uses the
        identifiers of <xref target="TRACECONTEXT"/>, it <bcp14>SHOULD</bcp14>
        populate the Run's "trace_id" member with the lowercase hexadecimal
        encoding of the 16-octet trace identifier, and each Step's "span_id"
        member with the lowercase hexadecimal encoding of the 8-octet span
        identifier of the span covering that Step. These members exist to permit
        a Collector to join a Report to a trace; this document does not otherwise
        depend on any tracing system, and a Reporter that does not participate in
        one <bcp14>MUST</bcp14> omit them rather than invent values.</t>
      </section>
    </section>

    <section anchor="incremental" numbered="true">
      <name>Incremental Reporting</name>

      <t>A Run may be long-lived, and a Collector may need to observe it before
      it terminates. A Reporter <bcp14>MAY</bcp14> therefore emit more than one
      Report for the same "run_id".</t>

      <t>Every Report for a Run <bcp14>MUST</bcp14> be self-contained: its
      "totals" <bcp14>MUST</bcp14> account for the Run from its "start" up to the
      moment of the Report, not for the interval since the previous Report. A
      Collector therefore replaces rather than accumulates.</t>

      <t>To let a Collector order Reports that arrive out of order, a Reporter
      emitting more than one Report for a Run <bcp14>MUST</bcp14> include the
      member "revision", a non-negative integer that starts at 0 and increases by
      1 for each subsequent Report about the same "run_id". A Collector
      <bcp14>MUST</bcp14> discard a Report whose "revision" is not greater than
      that of a Report it has already accepted for the same "run_id", and
      <bcp14>MUST</bcp14> treat this discard as normal operation rather than as
      an error.</t>

      <t>At most one Report for a given "run_id" may have a terminal "status". A
      Collector that receives a second Report with a terminal status for a
      "run_id" for which it has already accepted one <bcp14>MUST</bcp14> discard
      the later Report, whatever its "revision".</t>

      <t>A sequence of Reports <bcp14>MAY</bcp14> be serialized as JSON text
      sequences <xref target="RFC7464"/>, one Report per element. Whole-Run
      Reports for distinct Runs <bcp14>MAY</bcp14> be batched the same way. A
      batch <bcp14>MUST NOT</bcp14> be represented as a JSON array of Reports,
      because a consumer cannot then process the batch incrementally.</t>
    </section>

    <section anchor="transport" numbered="true">
      <name>HTTP Binding</name>

      <t>This section defines an OPTIONAL binding for delivering Reports to a
      Collector over HTTP <xref target="RFC9110"/>. Implementations
      <bcp14>MAY</bcp14> convey Reports by other means; the format does not
      depend on this binding.</t>

      <t>A Reporter delivers a Report by issuing a POST request to a Collector
      URI configured out of band, with a "Content-Type" header field of
      "application/agent-run-metrics+json". A batch is delivered with a
      "Content-Type" of "application/agent-run-metrics-seq+json".</t>

      <t>The request <bcp14>MUST</bcp14> be sent over a connection providing
      confidentiality and integrity; in practice this means TLS
      <xref target="RFC9846"/> or a protocol offering equivalent protection. A
      Collector <bcp14>MUST NOT</bcp14> accept Reports over an unprotected
      connection.</t>

      <t>A Collector that has accepted a Report for processing
      <bcp14>SHOULD</bcp14> respond with 202 (Accepted) and an empty body. A
      Collector that rejects a Report <bcp14>SHOULD</bcp14> respond with a 4xx
      status code and a problem details document
      <xref target="RFC9457"/> describing the reason.</t>

      <t>Delivery is idempotent by construction: the pair of "run_id" and
      "revision" identifies a Report, and a Collector that receives the same pair
      twice <bcp14>MUST</bcp14> treat the second delivery as a duplicate and
      respond as it did to the first. A Reporter <bcp14>MAY</bcp14> therefore
      retry a request whose outcome it does not know, and <bcp14>SHOULD</bcp14>
      space retries using exponential backoff with jitter.</t>

      <t>A Collector <bcp14>MUST</bcp14> impose a limit on the size of a Report
      it will accept and <bcp14>SHOULD</bcp14> respond with 413 (Content Too
      Large) when that limit is exceeded. A Reporter that receives 413
      <bcp14>SHOULD</bcp14> retry with an abbreviated "steps" array as permitted
      by <xref target="dm-run"/>, rather than dropping the Report.</t>
    </section>

    <section anchor="privacy" numbered="true">
      <name>Privacy Considerations</name>

      <section anchor="content" numbered="true">
        <name>Exclusion of Content</name>
        <t>A Report <bcp14>MUST NOT</bcp14> contain any part of the Context, of a
        model response, of a tool's arguments, or of a tool's result. This
        prohibition is absolute and applies to every member of every object
        defined here, including free-form members such as "labels", the Agent
        object's "name", the Tool object's "name" and the Error object's "type".
        A Reporter that wishes to convey content operates outside this format.</t>

        <t>The prohibition exists because a metrics pipeline typically has a
        wider audience, a longer retention period and weaker access control than
        the system that produced the Run. Content that leaks into a Report
        inherits the pipeline's exposure, not the Run's.</t>
      </section>

      <section anchor="inference" numbered="true">
        <name>Inference from Counters</name>
        <t>Counters are not content, but they are not free of information about
        content either. The length of a document supplied to a Run is closely
        related to the increase in "input_tokens" at the Step that first
        submitted it; the presence of a Step of kind "tool_call" with a
        particular "name" discloses that the Run used that class of tool; timing
        members disclose when a Run was executed and, in aggregate, an operator's
        working hours. Where a Run's inputs are drawn from a small or guessable
        set, an observer holding candidate inputs can compare their token lengths
        against a Report and identify which was used.</t>

        <t>A Reporter <bcp14>SHOULD</bcp14> therefore treat Reports as
        confidential to the same degree as the workload they describe, and
        <bcp14>SHOULD NOT</bcp14> publish per-Step Reports for Runs whose inputs
        are sensitive. Publishing only Run-level "totals" and "derived" members
        substantially reduces, but does not eliminate, this exposure.</t>
      </section>

      <section anchor="identifiers" numbered="true">
        <name>Identifiers</name>
        <t>The "run_id" member is opaque to this format and
        <bcp14>MUST NOT</bcp14> be derived from an identifier of a natural
        person, a session, an account or a request that the Collector could
        correlate with one. A Reporter <bcp14>SHOULD</bcp14> generate "run_id"
        values that carry no structure, for example as UUIDs
        <xref target="RFC9562"/>.</t>

        <t>A Report describes a Run, and a Run is usually initiated on behalf of
        a person. Retaining Reports therefore constitutes processing of data
        about that person's activity even though no personal data appears in the
        Report itself. Operators <bcp14>SHOULD</bcp14> apply retention limits to
        Reports and <bcp14>SHOULD</bcp14> aggregate them once the individual Runs
        are no longer operationally relevant.</t>
      </section>
    </section>

    <section anchor="security" numbered="true">
      <name>Security Considerations</name>

      <t>The privacy considerations in <xref target="privacy"/> are also security
      considerations and are not repeated here.</t>

      <section anchor="sec-trust" numbered="true">
        <name>Trust in the Reporter</name>
        <t>Every value in a Report is asserted by the Reporter. This format
        provides no means for a Collector to verify a count, a timestamp or a
        cost against the provider that actually served the Run. A Collector
        <bcp14>MUST NOT</bcp14> treat a Report as evidence of what a Run
        consumed; it is a statement by the Reporter about what the Run consumed.
        Where an independent guarantee is required, it must come from a
        signature over the Report or from reconciliation against the provider's
        own metering, neither of which is specified here.</t>

        <t>Where Reports drive chargeback, quota enforcement or capacity
        decisions, a Reporter has an incentive to under-report and, in
        adversarial multi-tenant settings, a neighbour has an incentive to
        over-report on another tenant's behalf. A Collector
        <bcp14>MUST</bcp14> authenticate Reporters and
        <bcp14>MUST</bcp14> reject a Report that claims a "run_id" outside the
        authenticated Reporter's namespace. Mechanisms for authentication are out
        of scope; the HTTP binding of <xref target="transport"/> can carry any
        of the standard HTTP authentication schemes.</t>
      </section>

      <section anchor="sec-resource" numbered="true">
        <name>Resource Exhaustion at the Collector</name>
        <t>A Report's "steps" array is unbounded in the format, and a Run may
        legitimately contain a very large number of Steps. A Collector that
        parses Reports of unbounded size, or that retains one entry per
        "run_id" without limit, can be exhausted by a Reporter that emits many
        Runs or one very large Run. A Collector <bcp14>MUST</bcp14> enforce
        limits on Report size, on the number of Steps it will process, and on the
        number of distinct "run_id" values it will track per Reporter, and
        <bcp14>SHOULD</bcp14> apply rate limits per authenticated Reporter.</t>

        <t>The incremental profile of <xref target="incremental"/> permits an
        unbounded number of Reports per "run_id". A Collector
        <bcp14>SHOULD</bcp14> bound the "revision" values it will accept for a
        single Run and <bcp14>SHOULD</bcp14> expire tracking state for Runs that
        have neither terminated nor been updated within an operator-configured
        interval.</t>
      </section>

      <section anchor="sec-parsing" numbered="true">
        <name>Parsing</name>
        <t>Reports arrive from parties that may be compromised even when they are
        authenticated. A Collector <bcp14>MUST</bcp14> validate a Report against
        the constraints of <xref target="serialization"/> before acting on it,
        including the integer range restriction, which exists to prevent silent
        truncation in implementations that map JSON numbers to binary floating
        point. Because the "amount" member is a string, a Collector
        <bcp14>MUST</bcp14> bound its length before parsing it as a decimal, so
        that an arbitrarily long digit string cannot drive unbounded work in an
        arbitrary-precision library.</t>
      </section>

      <section anchor="sec-crossagent" numbered="true">
        <name>Delegation</name>
        <t>The "parent_run_id" member permits a Reporter to attribute a Run to
        another Run, potentially one reported by a different Reporter. A
        Collector <bcp14>MUST NOT</bcp14> allow a Reporter to attach a Run to a
        parent outside that Reporter's namespace without an authorization
        decision of its own, because doing so would let one party inflate
        another's accounted consumption.</t>

        <t>A Report may carry "subtree_totals" as well as "totals"
        (<xref target="dm-hierarchy"/>), and the two are aggregated by mutually
        exclusive methods. A Collector that combines them counts delegated
        consumption twice. Where accounted consumption allocates cost or enforces
        a budget, a Reporter that can influence which method a Collector applies
        can inflate or deflate the accounted figure without emitting any value
        that an invariant of this document forbids. A Collector
        <bcp14>SHOULD</bcp14> therefore fix its aggregation method by policy and
        <bcp14>SHOULD NOT</bcp14> infer it from the members a Report happens to
        contain.</t>
      </section>
    </section>

    <section anchor="iana" numbered="true">
      <name>IANA Considerations</name>

      <section anchor="iana-mt1" numbered="true">
        <name>Media Type application/agent-run-metrics+json</name>
        <t>IANA is requested to register the following media type in the "Media
        Types" registry, per <xref target="RFC6838"/>.</t>

        <dl newline="false" spacing="compact">
          <dt>Type name:</dt><dd>application</dd>
          <dt>Subtype name:</dt><dd>agent-run-metrics+json</dd>
          <dt>Required parameters:</dt><dd>N/A</dd>
          <dt>Optional parameters:</dt><dd>N/A</dd>
          <dt>Encoding considerations:</dt><dd>binary; the content is JSON text encoded in UTF-8</dd>
          <dt>Security considerations:</dt><dd>See <xref target="security"/> of RFC&nbsp;XXXX</dd>
          <dt>Interoperability considerations:</dt><dd>See <xref target="serialization"/> of RFC&nbsp;XXXX</dd>
          <dt>Published specification:</dt><dd>RFC&nbsp;XXXX</dd>
          <dt>Applications that use this media type:</dt><dd>Agent runtimes, cost-management, capacity-planning and audit tooling</dd>
          <dt>Fragment identifier considerations:</dt><dd>As specified for "application/json" in <xref target="RFC6839"/></dd>
          <dt>Additional information:</dt>
          <dd>
            <t>Deprecated alias names for this type: N/A</t>
            <t>Magic number(s): N/A</t>
            <t>File extension(s): .json</t>
            <t>Macintosh file type code(s): TEXT</t>
          </dd>
          <dt>Person and email address to contact for further information:</dt><dd>IETF (iesg@ietf.org)</dd>
          <dt>Intended usage:</dt><dd>COMMON</dd>
          <dt>Restrictions on usage:</dt><dd>None</dd>
          <dt>Author:</dt><dd>See the "Authors' Addresses" section of RFC&nbsp;XXXX</dd>
          <dt>Change controller:</dt><dd>IETF</dd>
        </dl>
      </section>

      <section anchor="iana-mt2" numbered="true">
        <name>Media Type application/agent-run-metrics-seq+json</name>
        <t>IANA is requested to register a second media type, identical to that
        of <xref target="iana-mt1"/> except as follows.</t>

        <dl newline="false" spacing="compact">
          <dt>Subtype name:</dt><dd>agent-run-metrics-seq+json</dd>
          <dt>Encoding considerations:</dt><dd>binary; the content is a JSON text sequence as specified in <xref target="RFC7464"/>, each element of which is a Report</dd>
          <dt>Interoperability considerations:</dt><dd>See <xref target="incremental"/> of RFC&nbsp;XXXX</dd>
          <dt>File extension(s):</dt><dd>.jsonseq</dd>
        </dl>
      </section>

      <section anchor="iana-reasons" numbered="true">
        <name>Agent Run Metrics Termination Reasons Registry</name>
        <t>IANA is requested to create a new registry group named "Agent Run
        Metrics", and within it a registry named "Agent Run Metrics Termination
        Reasons".</t>

        <t>The registration policy is Specification Required
        <xref target="RFC8126"/>. The designated expert is directed to confirm
        that a proposed reason denotes a cause of termination that is
        distinguishable from every registered reason, that its name uses only
        lowercase ASCII letters, digits and the underscore character, and that
        the specification defines the reason without reference to any single
        implementation.</t>

        <t>Each entry has the fields: Reason (the string value), Description,
        Terminal Statuses (the subset of "completed", "failed" and "aborted" with
        which the reason may appear), Reference, and Change Controller.</t>

        <t>The registry is to be populated with the initial entries in
        <xref target="tbl-reasons"/>. The Reference for each is RFC&nbsp;XXXX and
        the Change Controller for each is IETF.</t>

        <table anchor="tbl-reasons">
          <name>Initial Termination Reasons</name>
          <thead>
            <tr><th>Reason</th><th>Description</th><th>Terminal Statuses</th></tr>
          </thead>
          <tbody>
            <tr><td>goal_reached</td><td>The Agent determined the goal was satisfied.</td><td>completed</td></tr>
            <tr><td>step_limit</td><td>A configured limit on the number of Steps was reached.</td><td>completed, aborted</td></tr>
            <tr><td>budget_exhausted</td><td>A configured limit on tokens or cost was reached.</td><td>completed, aborted</td></tr>
            <tr><td>context_limit</td><td>The Context reached the model's context window.</td><td>failed, aborted</td></tr>
            <tr><td>time_limit</td><td>A configured wall-clock limit was reached.</td><td>completed, aborted</td></tr>
            <tr><td>tool_failure</td><td>A tool failed in a way the Agent could not recover from.</td><td>failed</td></tr>
            <tr><td>model_error</td><td>A model invocation failed and could not be retried successfully.</td><td>failed</td></tr>
            <tr><td>policy_refusal</td><td>The Run stopped because a policy declined to proceed.</td><td>completed, aborted</td></tr>
            <tr><td>operator_abort</td><td>An operator stopped the Run.</td><td>aborted</td></tr>
            <tr><td>internal_error</td><td>The Agent failed for a reason not covered above.</td><td>failed</td></tr>
          </tbody>
        </table>
      </section>

      <section anchor="iana-kinds" numbered="true">
        <name>Agent Run Metrics Step Kinds Registry</name>
        <t>IANA is requested to create, within the "Agent Run Metrics" registry
        group, a registry named "Agent Run Metrics Step Kinds".</t>

        <t>The registration policy is Specification Required
        <xref target="RFC8126"/>. The designated expert is directed to confirm
        that a proposed kind describes a unit of work that consumes resources
        distinguishable from those of every registered kind, and to reject kinds
        that merely subdivide an existing kind along a dimension better expressed
        through "labels".</t>

        <t>Each entry has the fields: Kind (the string value), Description,
        Required Members (members of the Step object that <bcp14>MUST</bcp14> be
        present for this kind), Prohibited Members, Reference, and Change
        Controller.</t>

        <t>The registry is to be populated with the initial entries in
        <xref target="tbl-kinds"/>. The Reference for each is RFC&nbsp;XXXX and
        the Change Controller for each is IETF.</t>

        <table anchor="tbl-kinds">
          <name>Initial Step Kinds</name>
          <thead>
            <tr><th>Kind</th><th>Description</th><th>Required</th><th>Prohibited</th></tr>
          </thead>
          <tbody>
            <tr><td>model_invocation</td><td>A single request to a language model and its response.</td><td>usage, model</td><td>tool</td></tr>
            <tr><td>tool_call</td><td>A single invocation of a tool external to the model.</td><td>tool</td><td>usage</td></tr>
            <tr><td>retrieval</td><td>A search over a corpus whose results are placed into the Context.</td><td>tool</td><td>usage</td></tr>
            <tr><td>delegation</td><td>Execution of a child Run, reported separately.</td><td>(none)</td><td>usage</td></tr>
            <tr><td>human_input</td><td>A pause awaiting input from a person.</td><td>(none)</td><td>usage, tool</td></tr>
            <tr><td>compaction</td><td>A transformation that reduces the Context in place.</td><td>(none)</td><td>tool</td></tr>
          </tbody>
        </table>

        <t>The "delegation" kind marks the interval during which a subordinate
        Run executed. It prohibits "usage" because the delegating Run submits no
        Context of its own while the child runs; the child's consumption is
        reported by the child, as <xref target="dm-hierarchy"/> requires. A Step
        of this kind <bcp14>SHOULD</bcp14> carry "child_run_id".</t>

        <t>The "compaction" kind permits "usage", because a Context is commonly
        compacted by invoking a model on it; the resources so consumed are
        attributed to the compaction rather than to the Step that follows it.</t>
      </section>
    </section>

    <section anchor="conclusion" numbered="true">
      <name>Implementation Status and Empirical Basis</name>

      <t>At the time of writing this document describes a proposed format, and
      the author is aware of no implementation of it. The examples in
      <xref target="examples"/> are illustrative constructions; none of the
      values in them was measured from any system.</t>

      <t>The requirements in <xref target="dm-invocation"/>,
      <xref target="dm-hierarchy"/> and <xref target="dm-cachelife"/> were not
      arrived at from the shape of the format alone. They follow from an analysis
      of the execution journals of one agent runtime, covering 722 Runs, some
      150,000 Model Invocations and roughly 34 billion tokens, collected by a
      single practitioner. That corpus, its collection method and its analysis
      are published separately; this document does not restate them. It is a
      single-practitioner corpus from one runtime and one provider, and no claim
      is made that its magnitudes generalise to other deployments. What it
      establishes is not the values but the existence of the failure modes the
      values illustrate:</t>

      <ul>
        <li>the runtime wrote several journal records per Model Invocation, one
        per content block of the response, whose input counters repeated and
        whose output counter was a cumulative snapshot. Summing them overstated
        consumption by a factor of about 1.90; taking the first record of each
        group understated output by about half
        (<xref target="dm-invocation"/>);</li>

        <li>slightly more than half of all Model Invocations occurred in
        Delegated Runs rather than in the Runs that started them, so the rule
        against attributing a child's tokens to its parent as well as to itself
        governs the majority of the work (<xref target="dm-hierarchy"/>);</li>

        <li>tokens stored into provider-side cache state accounted for about 27
        percent of attributed cost and tokens served from that state for about 56
        percent, so cache accounting rather than fresh input dominated the cost
        of a Run (<xref target="dm-usage"/>);</li>

        <li>per-lifetime cache counters and the aggregate cache counter of the
        same record disagreed in a small number of records, which is why
        <xref target="dm-cachelife"/> requires the breakdown to be omitted rather
        than reconciled (<xref target="dm-cachelife"/>);</li>

        <li>input amplification had a median of about 24, a ninetieth percentile
        of about 125 and a maximum above 4000
        (<xref target="derived-amp"/>).</li>
      </ul>

      <t>These figures are recorded here as observations of one corpus, to
      explain why the corresponding requirements exist. They are not normative,
      and a Collector <bcp14>MUST NOT</bcp14> use them as expected values against
      which to validate a Report.</t>
    </section>

  </middle>

  <back>

    <references>
      <name>References</name>

      <references>
        <name>Normative References</name>
        <xi:include href="https://bib.ietf.org/public/rfc/bibxml/reference.RFC.2119.xml"/>
        <xi:include href="https://bib.ietf.org/public/rfc/bibxml/reference.RFC.3339.xml"/>
        <xi:include href="https://bib.ietf.org/public/rfc/bibxml/reference.RFC.5234.xml"/>
        <xi:include href="https://bib.ietf.org/public/rfc/bibxml/reference.RFC.6838.xml"/>
        <xi:include href="https://bib.ietf.org/public/rfc/bibxml/reference.RFC.6839.xml"/>
        <xi:include href="https://bib.ietf.org/public/rfc/bibxml/reference.RFC.7464.xml"/>
        <xi:include href="https://bib.ietf.org/public/rfc/bibxml/reference.RFC.7493.xml"/>
        <xi:include href="https://bib.ietf.org/public/rfc/bibxml/reference.RFC.8126.xml"/>
        <xi:include href="https://bib.ietf.org/public/rfc/bibxml/reference.RFC.8174.xml"/>
        <xi:include href="https://bib.ietf.org/public/rfc/bibxml/reference.RFC.8259.xml"/>
        <xi:include href="https://bib.ietf.org/public/rfc/bibxml/reference.RFC.9846.xml"/>
        <xi:include href="https://bib.ietf.org/public/rfc/bibxml/reference.RFC.9110.xml"/>
        <xi:include href="https://bib.ietf.org/public/rfc/bibxml/reference.RFC.9457.xml"/>
        <xi:include href="https://bib.ietf.org/public/rfc/bibxml/reference.RFC.9562.xml"/>

        <reference anchor="ISO4217" target="https://www.iso.org/iso-4217-currency-codes.html">
          <front>
            <title>Codes for the representation of currencies</title>
            <author>
              <organization>International Organization for Standardization</organization>
            </author>
            <date year="2015"/>
          </front>
          <seriesInfo name="ISO" value="4217"/>
        </reference>
      </references>

      <references>
        <name>Informative References</name>

        <reference anchor="TRACECONTEXT" target="https://www.w3.org/TR/trace-context/">
          <front>
            <title>Trace Context</title>
            <author>
              <organization>World Wide Web Consortium</organization>
            </author>
            <date year="2021"/>
          </front>
          <refcontent>W3C Recommendation</refcontent>
        </reference>

        <reference anchor="OTEL-GENAI" target="https://opentelemetry.io/docs/specs/semconv/gen-ai/">
          <front>
            <title>Semantic Conventions for Generative AI Systems</title>
            <author>
              <organization>OpenTelemetry Authors</organization>
            </author>
            <date/>
          </front>
        </reference>
      </references>
    </references>

    <section anchor="examples" numbered="true">
      <name>Examples</name>

      <t>The Reports in this appendix are illustrative. They were constructed to
      demonstrate the format and the invariants of
      <xref target="dm-usage"/>; the values in them are not measurements of any
      system, and no conclusion should be drawn from their magnitudes.</t>

      <section anchor="ex-minimal" numbered="true">
        <name>Minimal Report</name>
        <t>The smallest conforming Report omits every OPTIONAL member.</t>
        <sourcecode type="json"><![CDATA[
{
  "version": "1",
  "run_id": "0193f4d1-3c9e-7b2a-9f01-6c2d5a8e4b77",
  "start": "2026-03-04T09:12:44.118Z",
  "end": "2026-03-04T09:13:57.902Z",
  "status": "completed",
  "step_count": 3,
  "totals": {
    "input_tokens": 41200,
    "output_tokens": 1860
  }
}
]]></sourcecode>
      </section>

      <section anchor="ex-full" numbered="true">
        <name>Report with Steps, Cache and Derived Quantities</name>
        <t>The following illustrative Report shows a Run of four Steps in which
        the Context accumulates across three Model Invocations separated by one
        Tool Call. Note that "cache_read_tokens" and "cache_write_tokens" are in
        every case disjoint subsets of "input_tokens", that each Model Invocation
        is one Step bearing one "invocation_id" however many content blocks its
        response contained, and that "totals" is the member-by-member sum of the
        Steps that carry a Usage object, "cache_writes" being aggregated per
        lifetime.</t>

        <sourcecode type="json"><![CDATA[
{
  "version": "1",
  "run_id": "0193f4d1-4a02-7c31-b0d4-1e7f9a3c2255",
  "start": "2026-03-04T09:20:00.000Z",
  "end": "2026-03-04T09:21:16.415Z",
  "status": "completed",
  "termination_reason": "goal_reached",
  "agent": { "name": "example-agent", "version": "0.4.1" },
  "models": [
    {
      "id": "example-model-large",
      "provider": "example-provider",
      "context_window": 200000
    }
  ],
  "labels": { "environment": "staging", "workload": "batch" },
  "step_count": 4,
  "steps": [
    {
      "index": 0,
      "kind": "model_invocation",
      "start": "2026-03-04T09:20:00.010Z",
      "end": "2026-03-04T09:20:11.284Z",
      "model": "example-model-large",
      "invocation_id": "inv-7f21a0",
      "usage": {
        "input_tokens": 8400,
        "output_tokens": 310,
        "cache_read_tokens": 0,
        "cache_write_tokens": 8300,
        "cache_writes": [
          { "lifetime": "PT1H", "tokens": 8300 }
        ]
      }
    },
    {
      "index": 1,
      "kind": "tool_call",
      "start": "2026-03-04T09:20:11.300Z",
      "end": "2026-03-04T09:20:13.771Z",
      "tool": {
        "name": "corpus_search",
        "outcome": "ok",
        "bytes_in": 214,
        "bytes_out": 18944
      }
    },
    {
      "index": 2,
      "kind": "model_invocation",
      "start": "2026-03-04T09:20:13.790Z",
      "end": "2026-03-04T09:20:41.002Z",
      "model": "example-model-large",
      "invocation_id": "inv-7f21a1",
      "usage": {
        "input_tokens": 13950,
        "output_tokens": 640,
        "cache_read_tokens": 8300,
        "cache_write_tokens": 5600,
        "cache_writes": [
          { "lifetime": "PT5M", "tokens": 4200 },
          { "lifetime": "PT1H", "tokens": 1400 }
        ]
      }
    },
    {
      "index": 3,
      "kind": "model_invocation",
      "start": "2026-03-04T09:20:41.020Z",
      "end": "2026-03-04T09:21:16.400Z",
      "model": "example-model-large",
      "invocation_id": "inv-7f21a2",
      "usage": {
        "input_tokens": 15300,
        "output_tokens": 905,
        "reasoning_tokens": 520,
        "cache_read_tokens": 13900
      }
    }
  ],
  "totals": {
    "input_tokens": 37650,
    "output_tokens": 1855,
    "cache_read_tokens": 22200,
    "cache_write_tokens": 13900,
    "cache_writes": [
      { "lifetime": "PT5M", "tokens": 4200 },
      { "lifetime": "PT1H", "tokens": 9700 }
    ],
    "reasoning_tokens": 520
  },
  "cost": {
    "currency": "USD",
    "amount": "0.0000",
    "basis": "estimated",
    "pricing_ref": "https://example.com/pricing/2026-03"
  },
  "derived": {
    "input_amplification": 2.46078,
    "cache_hit_ratio": 0.589641,
    "cache_write_ratio": 0.369190,
    "output_share": 0.0469561,
    "advancing_step_ratio": 1.0
  }
}
]]></sourcecode>

        <t>The "cost" object above carries a zero "amount" with a basis of
        "estimated" solely to exhibit the object's shape; it is not a price.</t>

        <t>The third Model Invocation illustrates the rule of
        <xref target="dm-invocation"/>. Its response consisted of a reasoning
        block and a text block, and the runtime that produced it wrote a record
        for each. The Report contains one Step whose "output_tokens" is 905, the
        count for the whole response, of which 520 are attributed to reasoning.
        A Reporter that had emitted one Step per record would have reported 30600
        input tokens for this invocation instead of 15300.</t>
      </section>

      <section anchor="ex-delegation" numbered="true">
        <name>A Run and the Run It Delegates</name>
        <t>The two Reports below describe one Delegation Tree. The parent's
        Step of index 1 spans the interval during which the child ran and carries
        no Usage object; the child reports its own consumption. The parent also
        asserts "subtree_totals", which equals the member-by-member sum of the two
        "totals" objects. A Collector that added "subtree_totals" to the child's
        "totals" would count the child twice; see
        <xref target="dm-hierarchy"/>.</t>

        <sourcecode type="json"><![CDATA[
{
  "version": "1",
  "run_id": "0193f4d1-6c24-7e51-9bf3-3a91bc5e4477",
  "root_run_id": "0193f4d1-6c24-7e51-9bf3-3a91bc5e4477",
  "start": "2026-03-04T09:40:00.000Z",
  "end": "2026-03-04T09:41:52.700Z",
  "status": "completed",
  "termination_reason": "goal_reached",
  "step_count": 3,
  "steps": [
    {
      "index": 0,
      "kind": "model_invocation",
      "start": "2026-03-04T09:40:00.010Z",
      "end": "2026-03-04T09:40:07.220Z",
      "model": "example-model-large",
      "invocation_id": "inv-b30c10",
      "usage": {
        "input_tokens": 6100,
        "output_tokens": 240,
        "cache_read_tokens": 0
      }
    },
    {
      "index": 1,
      "kind": "delegation",
      "start": "2026-03-04T09:40:07.240Z",
      "end": "2026-03-04T09:41:39.880Z",
      "child_run_id": "0193f4d1-7d35-7f62-a0e4-4b02cd6f5588"
    },
    {
      "index": 2,
      "kind": "model_invocation",
      "start": "2026-03-04T09:41:39.900Z",
      "end": "2026-03-04T09:41:52.610Z",
      "model": "example-model-large",
      "invocation_id": "inv-b30c11",
      "usage": {
        "input_tokens": 9400,
        "output_tokens": 410,
        "cache_read_tokens": 6000
      }
    }
  ],
  "totals": {
    "input_tokens": 15500,
    "output_tokens": 650,
    "cache_read_tokens": 6000
  },
  "subtree_totals": {
    "input_tokens": 63200,
    "output_tokens": 2130,
    "cache_read_tokens": 34100
  }
}
]]></sourcecode>

        <t>The child reports itself independently. It omits its "steps" array,
        which "step_count" and "totals" still account for.</t>

        <sourcecode type="json"><![CDATA[
{
  "version": "1",
  "run_id": "0193f4d1-7d35-7f62-a0e4-4b02cd6f5588",
  "parent_run_id": "0193f4d1-6c24-7e51-9bf3-3a91bc5e4477",
  "root_run_id": "0193f4d1-6c24-7e51-9bf3-3a91bc5e4477",
  "start": "2026-03-04T09:40:07.300Z",
  "end": "2026-03-04T09:41:39.800Z",
  "status": "completed",
  "termination_reason": "goal_reached",
  "step_count": 5,
  "totals": {
    "input_tokens": 47700,
    "output_tokens": 1480,
    "cache_read_tokens": 28100
  }
}
]]></sourcecode>
      </section>

      <section anchor="ex-incremental" numbered="true">
        <name>Incremental Report</name>
        <t>A Reporter observing a Run in progress emits a self-contained Report
        with a non-terminal "status", no "end", and a "revision".</t>
        <sourcecode type="json"><![CDATA[
{
  "version": "1",
  "run_id": "0193f4d1-5b13-7d40-8ae2-2f80ab4d3366",
  "revision": 2,
  "start": "2026-03-04T09:30:00.000Z",
  "status": "running",
  "step_count": 11,
  "totals": {
    "input_tokens": 302400,
    "output_tokens": 7210
  }
}
]]></sourcecode>
      </section>
    </section>

    <section anchor="acks" numbered="true" removeInRFC="false">
      <name>Acknowledgements</name>
      <t>This document has not yet benefited from review. The author welcomes
      comment.</t>
    </section>

  </back>
</rfc>
