Skip to content

Where people and agents ship software together. The open-source git platform for the whole job: issues, agents, checks and deploys to the edge.

188 linesCodeBlameRaw
1---
2title: Sessions and why-blame
3description: How g1t records the way a change was made, and how to find out why any line is the way it is.
4---
5
6Blame tells you who last changed a line. On g1t it also tells you why: the pull request the line arrived in, the issue that asked
7for it, and, when a coding agent wrote it, the agent's own account of what it did.
8That comes from **sessions**, the record of how each pull request was made.
9
10## Sessions
11
12A pull request's session records how the change was made, entry by entry,
13as it happens. Entries come from `record_session` on the pull request, and
14from any [harness session](/guides/sessions/#sessions) attached to it:
15
16| Kind | What it holds |
17| --- | --- |
18| `prompt` | What the agent was asked to do, and messages people sent it while it worked. |
19| `message` | The agent's own reasoning and explanation. |
20| `tool_call` | A tool the agent ran, and with what input. |
21| `tool_result` | What the tool returned. |
22| `note` | Anything else worth keeping, such as what the agent was told about other work in progress. |
23
24Each entry is stored with the head commit of the pull request at the time
25it was recorded. That link is what lets g1t show the reasoning behind a
26commit rather than only the commit.
27
28Read a session on the pull request's **Session** tab, with the
29`pull_request` tool's `read_session` action,
30or with `GET /repos/{owner}/{name}/pulls/{number}/session?after=`. A
31session is as visible as the repository, so do not put secrets in one.
32
33### Recording a session
34
35Your coding agent records its session in one of two ways.
36
37**With g1t's Claude Code plugin.** Every session is recorded without the
38agent having to remember:
39
40```sh
41claude plugin marketplace add https://g1t.sh/flagon-io/g1t-plugins.git
42claude plugin install g1t@g1t
43```
44
45See [Claude Code plugin](/guides/claude-code-plugin/) for what it records,
46what it never records and how to opt out.
47
48**With the `pull_request` tool's `record_session` action**, from any agent. It takes a list of entries, each
49with a `kind` from the table above and `text`, and `tool` for tool entries.
50The same is `POST /repos/{owner}/{name}/pulls/{number}/session`, with up to
51200 entries per request:
52
53```sh
54curl -X POST https://api.g1t.sh/repos/acme/web/pulls/14/session \
55 -H "Authorization: Bearer $G1T_TOKEN" \
56 -H "Content-Type: application/json" \
57 -d '{"entries": [
58 {"kind": "prompt", "text": "Make the greeting name the caller."},
59 {"kind": "tool_call", "tool": "Edit", "text": "src/main.rs"},
60 {"kind": "message", "text": "Took the name from the first argument, falling back to world."}
61 ]}'
62```
63
64Record as you work, not only at the end: an entry is tied to the commit
65that was the head when it was recorded, so recording before each push is
66what lets why-blame find the reasoning behind each commit.
67
68## Why a line is the way it is
69
70Every file can be shown with **Blame**: beside each run of lines, the commit
71that last changed it.
72
731. Open a file in the repository's **Code** tab.
742. Choose **Blame**.
753. Pick a line.
76
77g1t then shows why the line is the way it is:
78
79- the commit that last changed it;
80- the pull request it arrived in, and who or what wrote it and merged it;
81- the issue that asked for it, with its description;
82- how it was decided: the [decision records](#decision-records) on the
83 issue and every pull request for it, oldest first, each marked
84 **Claimed**, **Tested** or **Approved**. A combination that failed in
85 the merge queue is shown first, with the pull requests tested together,
86 the check that failed and the fix that answered it; then the
87 proposals, the fix, the approval and the merge;
88- when a coding agent wrote it, the agent's own account of the change and the
89 commands it ran, taken from its session. The steps shown are from the
90 work that produced that commit: the agent's messages, and the tool calls
91 that touched the file.
92
93Blame follows every parent of a merge, so a line that came into a pull
94request when it caught up with `main` is credited to whoever wrote it on
95`main`, not to the merge.
96
97Blame reads up to 400 commits back, for up to six seconds. A line older
98than that is credited to the oldest commit read that has it, and the view
99says the history is long. While it is worked out the file shows as it is,
100with "Working out who changed each line" above it, and each line's commit
101fills in when it is ready. A file's blame at a commit is kept once worked
102out, so opening it again, or from a commit's link, shows at once. Search
103engines and other crawlers are asked to leave blame pages alone
104(`Disallow: /*?blame=` in `/robots.txt`); the file itself stays open to
105them.
106
107### Why over the API
108
109The `repository` tool's `why` action, or
110`GET /repos/{owner}/{name}/why?path=&line=&ref=`, answers the same for one
111line, with the decision records too. `ref` is a branch, tag or commit; the
112default branch when left out.
113
114```sh
115curl "https://api.g1t.sh/repos/acme/web/why?path=src/config.rs&line=42" \
116 -H "Authorization: Bearer $G1T_TOKEN"
117```
118
119| Field | |
120| --- | --- |
121| `commit` | The commit that last changed the line: `hash`, `message`, `author` (`name` as git wrote it, and `username`, the g1t account the author's address belongs to, when one does; never the address), `parents`, `authored_at`. |
122| `pull` | The merged pull request it arrived in: `number`, `title`, `author`, `agent`, `merged_by`, `merged_at`. Absent when it did not arrive through one. |
123| `issue` | That pull request's issue: `number`, `title`, `state` and `body` (cut at 900 characters). |
124| `decisions` | The [decision records](#decision-records) on the pull request, oldest first. |
125| `sessions` | Up to eight entries of the pull request's session behind that commit: the messages, and the tool calls that touched the file. |
126
127A pull request is found for a commit as on the site: the one whose head it
128is, or the one whose stretch of the default branch's last 300 commits holds
129it. It is public for a public repository, and needs the `code:read` scope
130otherwise.
131
132## Decision records
133
134A decision record is a reason kept on an issue or pull request, with how
135sure it is. Sessions say what happened; decisions say why the result is
136the way it is, in a sentence, with what it rests on.
137
138| Confidence | Meaning |
139| --- | --- |
140| `claimed` | Someone said so: an agent or a person recorded it. |
141| `tested` | A check or the merge queue proved it. |
142| `approved` | A person approved it. |
143
144g1t keeps these itself:
145
146| Kind | When | Confidence |
147| --- | --- | --- |
148| `integration_failure` | The [merge queue](/guides/merge-queue/#when-an-entry-fails) found a combination of pull requests fails: which ones, and the check. | `tested` |
149| `integration_passed` | A combined state passed in the merge queue. | `tested` |
150| `approval` | Someone approved the pull request. | `approved` for a person, `claimed` for an agent's token |
151| `merge` | It was merged. | `tested` through the queue or a repository's own auto-merge, `approved` when a person merged it, `claimed` for an agent's token |
152
153Agents and people record their own, of kind `proposal` (why the change is
154made this way), `claim` (something true of it), `fix` (what fixed a
155failure) or `note`, always as `claimed`, with the `pull_request` tool's
156`record_decision` action or
157`POST /repos/{owner}/{name}/pulls/{number}/decisions`:
158
159```sh
160curl -X POST https://api.g1t.sh/repos/acme/web/pulls/14/decisions \
161 -H "Authorization: Bearer $G1T_TOKEN" \
162 -H "Content-Type: application/json" \
163 -d '{"kind": "fix",
164 "summary": "Renamed the config key #12 also introduced, so both read retry_limit.",
165 "evidence": {"commit": "7d3e9a1c"}}'
166```
167
168`summary` is at most 2,000 characters and `evidence` an object of at most
1698 KB. Whoever opened the pull request can record one, and so can anyone
170who may comment on it, once their email address is confirmed. Each is
171attributed to whoever recorded it, carries
172the issue the pull request is for, and is published as `decision.recorded`.
173
174A pull request's **Session** tab lists its decisions above what was
175recorded, with the harness sessions working on it; an issue lists its own
176and its pull requests' under them.
177
178Read them with the `pull_request` tool's `list_decisions` action, with
179`GET /repos/{owner}/{name}/pulls/{number}/decisions`, or for an issue and
180all its pull requests with
181`GET /repos/{owner}/{name}/issues/{number}/decisions`. A decision is as
182visible as the repository, so do not put secrets in one.
183
184## Commit pages
185
186Every commit has a page of its own, `g1t.sh/<workspace>/<repo>/commit/<hash>`,
187with its diff, its parents and the pull request it arrived in. The
188repository's **Commits** tab lists them.