<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: YS Lee</title>
    <description>The latest articles on DEV Community by YS Lee (@devyuuun).</description>
    <link>https://dev.to/devyuuun</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4123896%2F66cc7313-3de8-4614-8c80-24ef838e2560.png</url>
      <title>DEV Community: YS Lee</title>
      <link>https://dev.to/devyuuun</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/devyuuun"/>
    <language>en</language>
    <item>
      <title>A PR walkthrough needs evidence between the steps</title>
      <dc:creator>YS Lee</dc:creator>
      <pubDate>Tue, 22 Sep 2026 13:47:52 +0000</pubDate>
      <link>https://dev.to/devyuuun/a-pr-walkthrough-needs-evidence-between-the-steps-3pfk</link>
      <guid>https://dev.to/devyuuun/a-pr-walkthrough-needs-evidence-between-the-steps-3pfk</guid>
      <description>&lt;p&gt;A suggestion on GitHub changed what I wanted to improve next in &lt;a href="https://github.com/YunSinLee/pr-tour" rel="noopener noreferrer"&gt;PR Tour&lt;/a&gt;, the open-source skill I’m building for Codex and Claude Code.&lt;/p&gt;

&lt;p&gt;The guide could show every changed file and cite valid source lines, yet still leave out the connection between two steps. An unchanged callback or queue might be the part a reader actually needs.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/anthropics/skills/discussions/1800#discussioncomment-18549931" rel="noopener noreferrer"&gt;imMamdouhaboammar suggested making the transition itself explicit&lt;/a&gt;. That led to the main change in v0.7.0: a reader can now inspect the source behind a step-to-step explanation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Give the next step a reason and some evidence
&lt;/h2&gt;

&lt;p&gt;The existing &lt;code&gt;next&lt;/code&gt; text still explains why the guide moves on. An optional &lt;code&gt;transition&lt;/code&gt; adds the destination and the basis of that explanation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"next"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"The response receives the WebSocket send method. Follow that callback to state handling."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"transition"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"to"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"response-state"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"basis"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"source"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"evidence"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"path"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"starlette/websockets.py"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"side"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"right"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"start"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;207&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"end"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;209&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"text"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"The response receives the current WebSocket send method."&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This fragment comes from the pinned Starlette example. The full example adds the callback implementation and destination branch as well.&lt;/p&gt;

&lt;p&gt;The builder reads the cited ranges from Git. &lt;code&gt;right&lt;/code&gt; means the pinned head; &lt;code&gt;left&lt;/code&gt; means the merge base. The source can come from an unchanged file, because the connection often lives outside the diff.&lt;/p&gt;

&lt;p&gt;In the HTML, &lt;strong&gt;View connection evidence&lt;/strong&gt; expands those excerpts with line numbers, syntax colors, and links to the pinned source. The excerpts are bundled into the same file and remain readable offline.&lt;/p&gt;

&lt;h2&gt;
  
  
  Show what the explanation has not established
&lt;/h2&gt;

&lt;p&gt;There are three authored categories:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Source cited:&lt;/strong&gt; the explanation includes source excerpts.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Includes inference:&lt;/strong&gt; the author must state what remains unverified.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reading order:&lt;/strong&gt; the move helps understanding, such as moving from implementation to tests.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I used “Source cited” because a valid source range does not prove a call relationship. The builder checks the destination step, source identity, and ranges. The agent still authors the explanation.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://yunsinlee.github.io/pr-tour/demo.en.html#finish-body" rel="noopener noreferrer"&gt;queue handoff in the demo&lt;/a&gt; is an example of that distinction. It shows the send callback, queue insertion, queue retrieval, and session entry. It also says that the full TestClient wrapping and session-creation path is not included, so the interpretation still contains an inference. It makes no claim about a real network server’s transport.&lt;/p&gt;

&lt;p&gt;The example has seven connections: three source-cited, one including inference, and three reading-order moves. Those counts describe the guide’s annotations; they are not an execution-path coverage score.&lt;/p&gt;

&lt;p&gt;Older manifests still build. Adding this information to an old guide requires authoring the transitions and regenerating the HTML; a rebuild alone cannot supply the missing reasoning.&lt;/p&gt;

&lt;h2&gt;
  
  
  The next piece of feedback was about space
&lt;/h2&gt;

&lt;p&gt;Once source excerpts were inside the explanation pane, the three-column layout felt cramped.&lt;/p&gt;

&lt;p&gt;The two desktop dividers are now draggable. Moving one resizes its adjacent panes, with minimum widths so a pane does not disappear. Widths follow the guide and code when their order is switched, and the browser remembers them when storage is available. Double-click a divider to reset; keyboard users can focus it and use arrow keys or Enter. Mobile keeps its Guide / Code tabs.&lt;/p&gt;

&lt;p&gt;I checked the update in Chromium and WebKit, including offline excerpts, pane resizing, mobile navigation, and existing review-comment behavior. Comment imports still require the same PR snapshot and exact source.&lt;/p&gt;

&lt;p&gt;The WebKit checks also caught a race when two tabs saved comments at once: serializing localStorage writes with Web Locks did not always make the next tab’s read current. Comment saves now read, merge, and write in one IndexedDB transaction. Existing localStorage comments are imported without deleting the old copy, and the JSON backup remains available when browser storage is blocked.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://yunsinlee.github.io/pr-tour/demo.en.html#finish-body" rel="noopener noreferrer"&gt;Try the English demo&lt;/a&gt; · &lt;a href="https://github.com/YunSinLee/pr-tour" rel="noopener noreferrer"&gt;Source and installation&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you try it, I’d like to know where the reading order still loses you. Does the cited code explain the jump, and is it clear which part remains an inference?&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Written with AI assistance from Codex.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>codereview</category>
      <category>opensource</category>
      <category>python</category>
    </item>
    <item>
      <title>Adding portable review comments to a single HTML file</title>
      <dc:creator>YS Lee</dc:creator>
      <pubDate>Mon, 21 Sep 2026 08:48:40 +0000</pubDate>
      <link>https://dev.to/devyuuun/adding-portable-review-comments-to-a-single-html-file-200f</link>
      <guid>https://dev.to/devyuuun/adding-portable-review-comments-to-a-single-html-file-200f</guid>
      <description>&lt;p&gt;Someone trying PR Tour, the pull request walkthrough tool I'm building, suggested a useful next step: let readers comment on the code, then copy the comments and their source lines into an AI coding tool.&lt;/p&gt;

&lt;p&gt;The guide already showed real diffs, explanations, and clickable definitions in one standalone HTML file. But when a reader had a question, they still had to carry it elsewhere and explain which part of the code they meant.&lt;/p&gt;

&lt;p&gt;PR Tour 0.6.0 now lets readers select a line or range, write a comment, and copy or download JSON containing the feedback and its source context. The same JSON can be imported back into a matching guide.&lt;/p&gt;

&lt;p&gt;Here is the comment editor at a mobile viewport size, using the public Starlette PR #2041 demo. The question is an example comment, not feedback submitted to Starlette.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ffjmksky6ozm5p3uydkf5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ffjmksky6ozm5p3uydkf5.png" alt="PR Tour at a mobile viewport: a bottom-sheet comment editor for starlette/websockets.py, head lines 207–213, with an example question about handling an unsupported extension." width="390" height="844"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  A line number needs a version
&lt;/h2&gt;

&lt;p&gt;“Check line 211” is incomplete context. It could refer to the old file, the new file, or a newer checkout where line 211 has moved.&lt;/p&gt;

&lt;p&gt;The export includes the PR and repository URLs, pinned head/base/merge-base commits, and a record for each comment:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A stable comment ID.&lt;/li&gt;
&lt;li&gt;The file path, diff side, and commit for that side.&lt;/li&gt;
&lt;li&gt;The inclusive start and end lines.&lt;/li&gt;
&lt;li&gt;The exact selected source text, without diff markers.&lt;/li&gt;
&lt;li&gt;The comment body and timestamps.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For a renamed file, a comment on the old side keeps the old path. Selected source is exported as text; syntax colors and clickable definition markup stay in the UI.&lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;Copy for AI&lt;/strong&gt; button copies this JSON. It does not call a model or publish a GitHub review. Whoever uses the export still needs to check the pinned commit against their checkout and evaluate the comment against the code.&lt;/p&gt;

&lt;h2&gt;
  
  
  Browser storage is convenient; JSON makes it portable
&lt;/h2&gt;

&lt;p&gt;Saved comments stay in browser storage, grouped by PR and source snapshot. Reloading the same guide can restore them, and changing the reading step or UI language does not create a different comment collection.&lt;/p&gt;

&lt;p&gt;That storage has limits. Clearing browser data removes it, another device does not have it, and a local-file or embedded viewer may restrict it. The interface shows storage failures and keeps current-session comments available for export.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Download JSON&lt;/strong&gt; provides a backup that the reader can carry to another browser. The HTML itself is unchanged, so sharing only the HTML does not share the comments.&lt;/p&gt;

&lt;p&gt;Import is deliberately strict. It checks the PR, repository, pinned commits, file side, line range, and exact selected source before applying anything. One invalid record rejects the whole file. There is no attempt to guess where an old comment belongs in a newer revision.&lt;/p&gt;

&lt;p&gt;Before import, a preview shows additions, duplicates, and conflicting edits. Existing comments win by default; replacing conflicts requires an explicit choice. Re-importing an unchanged comment with the same ID skips it. Different IDs remain separate, and imports do not propagate deletions.&lt;/p&gt;

&lt;h2&gt;
  
  
  The bug hiding behind a second tab
&lt;/h2&gt;

&lt;p&gt;During pre-release review, we caught a problem in the first storage implementation. Each tab loaded the comment collection once and later saved the whole collection back.&lt;/p&gt;

&lt;p&gt;Consider two tabs that both start with no comments:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Tab A adds a comment and saves &lt;code&gt;[A]&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Tab B, still holding its original empty collection, adds another and saves &lt;code&gt;[B]&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;A's comment disappears from storage.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;No network or database was involved. The stale copy in the second tab was enough.&lt;/p&gt;

&lt;p&gt;The fix uses Web Locks, when available, to serialize saves for that snapshot. Inside the lock, a save reads the latest stored collection and merges the current tab's additions, edits, and deletions by ID. Unrelated comments survive.&lt;/p&gt;

&lt;p&gt;If another tab has changed the same comment incompatibly, the save preserves the stored version, keeps the current tab's edits in memory, and asks the reader to download JSON before reloading.&lt;/p&gt;

&lt;p&gt;Without Web Locks, the fallback refuses a save when it detects that storage changed. That fallback does not guarantee atomic writes from simultaneously saving tabs. Open comment lists also do not live-sync across tabs.&lt;/p&gt;

&lt;h2&gt;
  
  
  Keeping the code reachable on a phone-sized screen
&lt;/h2&gt;

&lt;p&gt;The other recent feedback was about navigation: moving to the next explanation left too much scrolling to reach the relevant code.&lt;/p&gt;

&lt;p&gt;The mobile Code view now has previous/next controls that follow individual code notes across reading steps and scroll to their lines. It stays in the Code tab. An expanded reading view makes more room for the diff, and the comment editor opens as a bottom sheet with larger line-selection targets.&lt;/p&gt;

&lt;p&gt;I checked these flows at mobile viewport sizes in Chromium and WebKit, including export/import, offline files, reload persistence, and the two-tab save cases. I have not verified them on physical phones or inside Orca's artifact viewer yet.&lt;/p&gt;

&lt;p&gt;The biggest design change was deciding what travels with a comment: enough source context to identify what the reader saw, and a portable copy the reader can keep outside browser storage.&lt;/p&gt;

&lt;p&gt;You can try the &lt;a href="https://yunsinlee.github.io/pr-tour/demo.en.html#entry" rel="noopener noreferrer"&gt;English demo&lt;/a&gt;, browse the &lt;a href="https://github.com/YunSinLee/pr-tour" rel="noopener noreferrer"&gt;source and installation instructions&lt;/a&gt;, or read the &lt;a href="https://github.com/YunSinLee/pr-tour/blob/main/skills/pr-tour/references/review-comments.md" rel="noopener noreferrer"&gt;JSON format and storage behavior&lt;/a&gt;. I'm the creator of PR Tour. Existing guides need to be regenerated with the updated skill to get these features.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Written with AI assistance from Codex.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>codereview</category>
      <category>javascript</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Adding syntax colors without changing the diff</title>
      <dc:creator>YS Lee</dc:creator>
      <pubDate>Fri, 18 Sep 2026 07:28:48 +0000</pubDate>
      <link>https://dev.to/devyuuun/adding-syntax-colors-without-changing-the-diff-4na0</link>
      <guid>https://dev.to/devyuuun/adding-syntax-colors-without-changing-the-diff-4na0</guid>
      <description>&lt;p&gt;Someone trying the pull request reading guide I'm building pointed out that the code was hard to read because most of it appeared in one color.&lt;/p&gt;

&lt;p&gt;The diff already had green backgrounds for additions, red for deletions, line numbers, and links to selected definitions. Those cues showed where the changes were. Keywords, strings, and comments still looked much alike.&lt;/p&gt;

&lt;p&gt;I added syntax highlighting to the diff and the definition previews. Here is the same method before and after the change. The example code is from Starlette PR #2041.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Before&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fo8nz8kqf0udf3rqmxvqq.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fo8nz8kqf0udf3rqmxvqq.jpg" alt="Before: a Python diff with green added-line backgrounds and mostly uniform code text" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;After&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fift73e7196nj40hyb32j.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fift73e7196nj40hyb32j.jpg" alt="After: the same Python diff with separate syntax colors for keywords, strings, and function names" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The added-line backgrounds stay green. Syntax colors provide another set of cues within each line.&lt;/p&gt;

&lt;p&gt;Adding those colors also meant preserving the source and the clickable names. Three implementation details mattered:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Highlight the old and new files separately.&lt;/strong&gt; A diff mixes deleted and added lines, but those lines belong to different versions of the file. Parsing each complete snapshot, including collapsed context, keeps a multiline string on one side from affecting the other side.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Keep the original text.&lt;/strong&gt; The renderer uses highlight.js to obtain token ranges, checks that its decoded text matches the source, and applies those ranges to the original text. It does not insert the highlighter's HTML directly into the page.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Keep reading possible when highlighting fails.&lt;/strong&gt; Unknown file types, lexer errors, or a text mismatch fall back to plain code. A color feature should not prevent the diff from rendering.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Definition links use character offsets in the same source text, so preserving whitespace and Unicode positions matters too. The regression tests cover multiline strings, collapsed context, renamed files, and text containing emoji or HTML-like characters.&lt;/p&gt;

&lt;p&gt;The screenshots show a presentation change, not a measured improvement in review speed. What the feedback exposed was a gap in the original UI: marking changed lines did not give readers the familiar syntax cues they expected inside those lines.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Written with AI assistance from Codex.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>codereview</category>
      <category>javascript</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Why a PR walkthrough can pass validation and still be hard to read</title>
      <dc:creator>YS Lee</dc:creator>
      <pubDate>Mon, 14 Sep 2026 05:06:39 +0000</pubDate>
      <link>https://dev.to/devyuuun/why-a-pr-walkthrough-can-pass-validation-and-still-be-hard-to-read-3gmb</link>
      <guid>https://dev.to/devyuuun/why-a-pr-walkthrough-can-pass-validation-and-still-be-hard-to-read-3gmb</guid>
      <description>&lt;p&gt;I made an HTML guide for reading pull requests. When I shared it with coworkers, the visual feedback was encouraging, but I still felt that parts were hard to follow. One person wanted to see the explanation before the code pane, which led to a layout switch.&lt;/p&gt;

&lt;p&gt;That feedback points to a limit of validation: source references can all be valid while the reader still lacks the context needed to understand a change.&lt;/p&gt;

&lt;p&gt;The implementation discussed here is my project, PR Tour. A coding agent writes the reading order, explanations, and definition mappings into a manifest. A Python builder reads the repository and produces the HTML. The builder can check the source material much more directly than it can check the explanation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Make every excerpt belong to a snapshot
&lt;/h2&gt;

&lt;p&gt;The manifest records the PR URL and base and head commits. The builder resolves the commits, computes their merge base, and collects the changed files between that merge base and the head. The left and right sides of the guide come from those snapshots.&lt;/p&gt;

&lt;p&gt;This makes a reference reproducible. It also imposes a limit: if the PR changes afterward, the saved HTML still describes the old snapshot. A working link to a PR does not make the embedded code current.&lt;/p&gt;

&lt;p&gt;The useful invariant is that an excerpt and its line numbers refer to the same source revision. Whether the explanation correctly describes that revision remains a separate review question.&lt;/p&gt;

&lt;h2&gt;
  
  
  Reconstruct both sides of the diff
&lt;/h2&gt;

&lt;p&gt;A normal diff omits unchanged regions. For supported text files, the builder adds that context back as expandable rows, then checks the result against the source from Git.&lt;/p&gt;

&lt;p&gt;It checks two properties on each side:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The line numbers form the complete expected sequence.&lt;/li&gt;
&lt;li&gt;The reconstructed line text equals the original source text.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Checking the numbering alone would leave room for incorrect text at a valid position. Checking text alone would miss errors in the line labels that notes rely on. Both are needed for a trustworthy source view.&lt;/p&gt;

&lt;p&gt;The builder also requires every changed file to appear in a reading step, including files it cannot render as a text diff. Binary and non-UTF-8 files get a notice directing the reader to GitHub.&lt;/p&gt;

&lt;p&gt;That is file coverage, not explanation coverage. A step can mention a file and still omit an important failure path. The same file can also appear in multiple steps, because following a behavior may require returning to it later.&lt;/p&gt;

&lt;h2&gt;
  
  
  Validate link placement without claiming symbol resolution
&lt;/h2&gt;

&lt;p&gt;Definition previews use authored mappings. The builder verifies that the target exists, its source range is valid, and the clickable text matches the source. An explicit text link must be unambiguous or include its column. Overlapping links are rejected.&lt;/p&gt;

&lt;p&gt;There is a small cross-language detail here: Python string positions count Unicode code points, while JavaScript string slicing counts UTF-16 code units. The builder converts positions before rendering them in the browser:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;start&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="p"&gt;[:&lt;/span&gt;&lt;span class="n"&gt;column&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;utf-16-le&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="o"&gt;//&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;
&lt;span class="n"&gt;end&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;start&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;link_text&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;utf-16-le&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="o"&gt;//&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For example, in &lt;code&gt;"😀send"&lt;/code&gt;, Python places the &lt;code&gt;s&lt;/code&gt; at index 1. JavaScript places it at index 2. Sending the Python index directly to JavaScript would put the link at the wrong position. The conversion concerns string indexing, not visual character width.&lt;/p&gt;

&lt;p&gt;Even with correct offsets, the chosen definition can be conceptually wrong. Two objects can have the same name. A working preview proves that a source range exists; it does not prove that the author mapped the reference to the relevant object.&lt;/p&gt;

&lt;h2&gt;
  
  
  Keep evidence near the explanation
&lt;/h2&gt;

&lt;p&gt;The reader can open selected definitions and inspect the diff beside the explanation. Notes point to the lines being discussed. The layout switch lets readers choose which pane comes first.&lt;/p&gt;

&lt;p&gt;These are navigation choices, not evidence that the guide improves comprehension. The public Starlette example has five changed files, eight reading steps, and thirteen definition previews. Those counts describe its structure; they do not measure how well it teaches the change.&lt;/p&gt;

&lt;p&gt;The output is one HTML file containing the source excerpts, explanations, styles, and scripts. Sharing it therefore shares the included source code too. The public example uses public Starlette code and includes its attribution.&lt;/p&gt;

&lt;h2&gt;
  
  
  Test the explanation with a reader
&lt;/h2&gt;

&lt;p&gt;A next evaluation could ask a reviewer to explain a transition between two steps, identify the definition that justifies a claim, or point out a missing failure case. That would test something a line-range validator cannot.&lt;/p&gt;

&lt;p&gt;No review-time benchmark or controlled comprehension study has been performed for this release. The upstream Starlette test suite was not run as part of generating the example, either. Building the guide should not be confused with testing the underlying change.&lt;/p&gt;

&lt;p&gt;The unresolved question for me is how to find the missing connection in an otherwise complete-looking guide. Valid source references give the reader evidence to inspect. The explanation still has to earn their trust.&lt;/p&gt;

&lt;h2&gt;
  
  
  Implementation references
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/YunSinLee/pr-tour/blob/4f3218b5052e9dcc407f593b82bda8912ff536ed/skills/pr-tour/scripts/build_guide.py" rel="noopener noreferrer"&gt;Builder at the version discussed here&lt;/a&gt;: snapshot resolution in &lt;code&gt;build&lt;/code&gt;, source reconstruction in &lt;code&gt;changed_files&lt;/code&gt;, and link checks in &lt;code&gt;collect_links&lt;/code&gt; and &lt;code&gt;append_link&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/YunSinLee/pr-tour/blob/4f3218b5052e9dcc407f593b82bda8912ff536ed/examples/starlette-2041.en.json" rel="noopener noreferrer"&gt;Public example manifest&lt;/a&gt;: the authored reading steps and definition mappings.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;Writing disclosure: Codex drafted this article from my project, feedback I shared, and its source code. Codex checked the implementation descriptions against the pinned version linked above.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>codereview</category>
      <category>python</category>
      <category>testing</category>
    </item>
  </channel>
</rss>
