Companion draft, separately versioned. Version: draft 0.1, for ASR format version 0.1, Agent Trace 0.1.0 and the Git AI Standard v3.0.0. Not part of core conformance.
Attribution answers the question, “Which session wrote these lines of this commit?” ASR records the facts used to derive the answer: which tool call changed which file and how (file_change), and which commits the session made (vcs). It does not record the attribution itself. This companion defines how to compute attribution from those facts and write it in two existing formats.

Principles

  1. Evidence. A match in object IDs or added lines is strong evidence that a session wrote code in a commit, though it does not prove authorship (ASR-462).
  2. Untracked lines. Lines that cannot be attributed to a session are untracked. They are attributed to a person only when there is evidence that a person wrote them (for example an edit the source observed as human, cause.actor: human). An edit the source observed without knowing who made it (cause.actor: external) stays untracked.
  3. Originality. Copied records receive no attribution (ASR-417), and neither do records whose originality is unknown (ASR-416).
  4. Commit records. A session that mentions a commit did not necessarily produce it; only vcs records count (ASR-464).

Derivation

For a commit C (a full commit ID) in a repository, given the streams of candidate sessions:
  1. Candidate sessions. A session is a candidate when it has a vcs record for C (commit equal to C, or a commitPrefix that matches C and no other commit in the repository). A session is also a candidate when an original file_change in it has an after.objectId equal to the object ID of the blob at the same path in C’s tree (with a matching objectIdType, and representation: git_object, or worktree when the repository applies no clean filters to that path).
  2. Changed lines. Compute the lines that C adds relative to its first parent, per file.
  3. Matching. For each changed file and each candidate session, take the session’s original file_change records for that path (by root-relative path, with the root resolved to the repository) whose precision is exact or hunks, in seq order. A line added by C is attributed to the session when the same line text was added by one of those changes and survives to C (the after state matches, or the hunk’s added lines appear in C’s added lines in the same order). When several sessions match the same line, attribute it to the latest change by observation order and record the others as alternatives.
  4. Precision. snippet and unknown changes do not attribute lines. They may attribute the file as a whole with lower confidence. A change with basis: net spans other changes (ASR-474): use it only for paths that have no per-operation change in the same span.
  5. Model. For each attributed range, the model is the served model (inference.model.id) of the inference that produced the tool call responsible for the change, when recorded. A model taken from the turn’s configuration (model.requestedSource: turn_config) is the requested model, and is reported as such. For a session run by another session (executedBy), the model comes from the backing session’s records.
  6. Everything else is untracked.
A derivation is reproducible: the same streams and repository yield the same ranges.

Agent Trace

An exporter writes one Agent Trace record per commit:
  • id is a version 8 UUID built from the first 16 bytes of SHA-256 over the commit ID and the sorted canonical session references, so that repeated exports agree.
  • timestamp is the ts of the vcs record, when recorded.
  • files[].path is relative to the repository root.
  • contributor.type is ai for attributed ranges; untracked lines are not listed. model_id uses Agent Trace’s provider/model form.
  • conversations[].url and related[].url hold an Agent-Session-Url locator when one exists (see the integration companion). Agent Trace expects URLs; the session reference itself goes in metadata under the org.agent-session key.
  • ranges[].content_hash MAY be added to follow code that moves.
  • Agent Trace’s published examples use "0.1.0" while its schema’s version pattern expects two components; exporters write "0.1.0", as the specification text does, until Agent Trace resolves this.

Git AI

An exporter writes one Git AI authorship note per commit under refs/notes/ai, in Git AI’s format: per-file attestation lines, a line containing exactly ---, then JSON metadata.
(The key values above are illustrative.)

The session join key

Git AI identifies a session by s_ followed by the first 14 lowercase hex digits of SHA-256 over the UTF-8 bytes of <tool>:<conversation_id>:
  • <tool> is Git AI’s tool name for the agent;
  • <conversation_id> is the ASR sessionId, verbatim.
Because the key is computed from the agent’s own session ID, a Git AI note resolves to the same ASR session without a lookup table. Git AI’s tool names are not ASR short names; an exporter needs a mapping from ASR agent names to Git AI tool names, which this companion will publish once confirmed against Git AI’s implementation. Git AI’s key has no namespace. For agents whose session IDs need one (Goose, Cline, Kiro legacy), keys from different installations can collide. Readers MUST treat a match based on such a key as ambiguous unless other evidence distinguishes the sessions.

The trace key

Git AI’s t_<14hex> trace key is random per checkpoint and opaque to Git AI readers. An ASR exporter computes it as the first 14 lowercase hex digits of SHA-256 over the canonical session reference, #, and the file_change record ID, so that repeated exports agree.

Rules

  • Session entries MUST NOT contain messages, messages_url or statistics fields, as Git AI v3.0.0 requires: notes never carry transcripts.
  • Git AI specifies how notes follow rebases, squashes, cherry-picks, resets and stashes through its own hooks. An exporter writes notes for commits as they exist and leaves rewriting to Git AI’s implementation; the Git AI specification recommends integrating with that implementation rather than reimplementing it.
  • Pushing notes requires the user’s consent (see the integration companion).