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