<p>There is a question every developer asks a codebase and almost never gets a good answer to: why is this line here?</p><p><code>git blame</code> tells you who and when. The commit message tells you what they were trying to do, if they wrote one, and if it was honest. But the reasoning — the conversation that led to that particular <code>if</code>, the alternative that was tried first and rejected, the constraint nobody wrote down — that lived in someone's head, or in a chat window that scrolled away, or in a code review thread on a platform you no longer use.</p><p>When an agent writes the code, that reasoning is right there. It's a prompt, a model's reply, a tool call, a diff. It's the most complete record of intent a codebase has ever had, and it sits in a session file that nobody connects to the line it produced.</p><p>Elyra Desktop 0.3 connects them. That's the release: line provenance, plus two ways of getting around a project faster. Here's how each works and why it's built the way it is.</p><h2>Line provenance</h2><p>Open a file from the Files tab — any file the agent has touched — and press the history button. You get the file with a gutter, and every line in it is attributed:</p><pre><code class="language-text">   12  a3f9c21  2026-09-05  kh        pub fn resolve(&amp;self, path: &amp;Path) -&gt; Result&lt;Resolved&gt; {
   13  a3f9c21  2026-09-05  kh            let cleaned = path.components()
   14  7be0d44  2026-09-12  ⚡ turn 14        .filter(|c| !matches!(c, Component::CurDir))
   15  7be0d44  2026-09-12  ⚡ turn 14        .collect::&lt;PathBuf&gt;();
   16  a3f9c21  2026-09-05  kh            self.index.get(&amp;cleaned).ok_or(...)
</code></pre><p>Lines 12, 13 and 16 came from a human commit — shown as exactly that. Lines 14 and 15 came from Elyra, and the gutter says which turn. Click it and you're looking at the prompt that produced the change:</p><pre><code class="language-text">│ You, 2026-09-12 14:02 — resolve() fails on paths like ./src/./main.rs. Normalise . components before the lookup, but don't touch .. — that
│ has to stay an error.
│
│ claude-sonnet-5 · 1,840 in / 312 out · $0.011
</code></pre><p>So now you know. Not that a filter was added, but why it filters <code>CurDir</code> and not <code>ParentDir</code> — because someone said, in one sentence, that <code>..</code> has to stay an error. That sentence would otherwise be gone.</p><h3>How it's built, and what it deliberately doesn't store</h3><p>The obvious design is to record line ranges: "turn 14 wrote lines 14–15 of <code>resolve.rs</code>." That design is wrong, and it's wrong the moment anyone adds a line above 14. Every stored range in the file is now off by one, forever, and the tool that promised to tell you the truth is confidently pointing at the wrong line.</p><p>So Elyra stores no line numbers at all. Three pieces do the work, and each one resolves on demand:</p><ol><li><p><code>git blame --porcelain -C</code> resolves a line to the commit that last touched it — following moves between files, so a function you relocated keeps its history. Git already recomputes line ancestry for a living; we let it.</p></li><li><p><strong>An </strong><code>Elyra-Turns:</code><strong> commit trailer.</strong> Every commit the agent makes carries <code>Elyra-Turns: &lt;sessionId&gt; &lt;entryId&gt;,&lt;entryId&gt;</code> in its message. That resolves the commit to a session and the turns within it. Errored turns are left out — they changed nothing a line could descend from.</p></li><li><p><strong>The session archive</strong> (from 0.2.0) resolves those turns to the prompt, the model and the cost.</p></li></ol><p>Why a trailer, and not <code>git notes</code>? Because a trailer is part of the commit message. It survives a rebase, a cherry-pick and a push to any remote, and it needs no per-clone <code>notes.rewriteRef</code> configuration. Anyone who clones the repository can read it with <code>git log</code>. The provenance is in the repository, not in a sidecar that stays behind on one machine.</p><p>You can turn the trailer off (<code>gitTurnsTrailer</code>), if you'd rather commits didn't say. But the default is on, because a codebase that remembers why is worth a line in a commit message.</p><h2>Project switcher</h2><p><code>cmd+p</code>. Every folder you've ever had a session in, most recently used first, fuzzy-searchable. Enter continues that folder's most recent session.</p><pre><code class="language-text">  cmd+p
    › eterm
      ▸ ~/code/eterm              2 hours ago     14 sessions
      ▸ ~/code/eterm-docs         3 days ago       1 session
      ▸ ~/old/eterm-prototype     missing          6 sessions
</code></pre><p>Two decisions worth naming. First, the list is derived from the sessions on disk, not kept as a list of its own. That means a folder you only ever opened from the terminal edition is there too, because both editions write to the same <code>~/.elyra/agent/sessions/</code>. And a folder that has been moved, deleted, or lives on a disk that isn't mounted right now is shown as missing — with its sessions still countable and searchable — rather than either vanishing from the list or failing when you pick it.</p><p>Second, <code>ctrl+p</code> pins a project to the top. Pins are the one piece of state here that is stored, because they're a preference, not a fact about the disk.</p><h2>File explorer</h2><p><code>cmd+1</code> opens the project tree on the right. It honours <code>.gitignore</code>, and it lists one directory level at a time as you expand folders — so a monorepo with forty thousand files costs nothing until you open the folder you want.</p><p>Click a file and <code>@path</code> is appended to the editor. That's the whole interaction, and it's deliberate: <code>@path</code> is already how you point the agent at a file in Elyra, in both editions. The explorer doesn't introduce a second way; it makes the existing way clickable. Each file also carries the provenance button, so "why is this line here" is one click from the tree too.</p><p>It's off by default and remembers whether you turned it on. A pane you didn't ask for is a pane in the way.</p><h2>Two small things</h2><p>The transcript's width is yours now. It has always been capped at 880 pixels and centred — a fixed measure keeps prose and code readable, and that stays the default. But on a very wide window that leaves a gap, so <code>desktop.transcriptWidth</code> sets another cap, or <code>0</code> to fill whatever the sidebar and the explorer leave.</p><p><code>/about</code>, or click the version in the title bar: the version, the website, and who made it. That's all. No repository link, because there isn't a public one to give you, and a link that 404s is worse than none.</p><h2>Why this, now</h2><p>The 0.2 release gave the desktop edition an archive — a search over every session you've ever had. 0.3 is what that archive is for. A search box is useful; a gutter that turns every agent-written line into a link back to its own reasoning is a different kind of thing. It means the question <em>why is this here?</em> finally has a place to go, and it means a codebase built with an agent carries its own explanation, forever, in the repository, in a form <code>git log</code> can read.</p><p>We think that matters more than any single feature in the window. Code has always been able to tell you <em>what</em>. It can now tell you <em>why</em>.</p><h2>Get it</h2><p>Elyra Desktop 0.3 is at <a target="_blank" rel="noopener noreferrer nofollow" href="https://elyracode.com/elyra">elyracode.com/elyra</a> — the Desktop tab. macOS on Apple silicon; Linux on x86_64 and ARM64; free to use, every feature. Already installed? <code>/update</code>. The <a target="_blank" rel="noopener noreferrer nofollow" href="https://elyracode.com/docs/desktop">desktop guide</a> has a section on provenance with the three pieces spelled out.</p><p>Open a file the agent touched. Press the history button. Read why.</p>