<?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: Praveen</title>
    <description>The latest articles on DEV Community by Praveen (@pn_28428886923dfc665).</description>
    <link>https://dev.to/pn_28428886923dfc665</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%2F3940098%2F8a7a4942-5b0d-4847-9a7a-2eaf76d0ce30.png</url>
      <title>DEV Community: Praveen</title>
      <link>https://dev.to/pn_28428886923dfc665</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/pn_28428886923dfc665"/>
    <language>en</language>
    <item>
      <title>81% of Engineering Teams Don't Know Where Their AI-Generated Code Lives in Production</title>
      <dc:creator>Praveen</dc:creator>
      <pubDate>Sat, 20 Jun 2026 05:22:19 +0000</pubDate>
      <link>https://dev.to/pn_28428886923dfc665/81-of-engineering-teams-dont-know-where-their-ai-generated-code-lives-in-production-56ec</link>
      <guid>https://dev.to/pn_28428886923dfc665/81-of-engineering-teams-dont-know-where-their-ai-generated-code-lives-in-production-56ec</guid>
      <description>&lt;p&gt;The Cloud Security Alliance published a research note this month with a number that should make every engineering manager uncomfortable: 81% of organizations surveyed had no complete visibility into where AI-generated code lives in their production systems.&lt;/p&gt;

&lt;p&gt;Not "limited visibility." Complete absence.&lt;/p&gt;

&lt;p&gt;Every one of those teams uses Copilot, Cursor, Claude Code, or one of a dozen other AI coding tools. Code is being generated at speed. It's landing in commits, getting merged, and running in production. But the teams cannot answer a basic audit question: which lines in &lt;code&gt;src/routes/auth.py&lt;/code&gt; were written by an AI?&lt;/p&gt;

&lt;p&gt;This is not a hypothetical governance problem. It's a production security problem.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why git blame doesn't solve this
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;git blame&lt;/code&gt; returns a username and a commit SHA. It was designed to answer "who wrote this line" in a world where humans wrote all code.&lt;/p&gt;

&lt;p&gt;AI coding tools broke that assumption without replacing the tooling. When a developer accepts a Copilot suggestion, the commit author is the developer. When Claude Code generates a 60-line authentication function in an agentic session, the commit author is still the developer. The AI is invisible in the commit history.&lt;/p&gt;

&lt;p&gt;The provenance metadata -- which model, which prompt, what the AI was asked to do -- exists for exactly one window: the moment of generation. The AI tool has it. The moment the developer moves on, it's gone. Commit by commit, sprint by sprint, that metadata accumulates into an unattributed window that grows backward through your git history.&lt;/p&gt;

&lt;h3&gt;
  
  
  What March 2026 showed us
&lt;/h3&gt;

&lt;p&gt;Georgia Tech's Vibe Security Radar tracked 35 CVEs in March 2026 alone that were directly attributable to AI-generated code. Researchers estimate the true count is 5 to 10 times higher, because most vulnerabilities are never traced back to an AI tool -- you need the generation record to do that, and almost no team has one.&lt;/p&gt;

&lt;p&gt;Separately, Pillar Security disclosed a "Rules File Backdoor" class of attack: hidden unicode characters in Cursor &lt;code&gt;.cursorrules&lt;/code&gt; files and Copilot config files that silently steer the model to inject malicious code during generation. The generated code looks normal. The review sees nothing unusual. The AI did exactly what it was secretly instructed to do.&lt;/p&gt;

&lt;p&gt;If you can't distinguish AI-generated lines from human-written lines, you can't contain this attack class. The blast radius is "everything an AI tool touched" -- and without provenance records, you don't know what that is.&lt;/p&gt;

&lt;h3&gt;
  
  
  How LineageLens closes the gap
&lt;/h3&gt;

&lt;p&gt;LineageLens sits as a proxy between your AI coding tool and the upstream model API. Every edit event -- &lt;code&gt;tool_use&lt;/code&gt; blocks from Claude, &lt;code&gt;apply_patch&lt;/code&gt; DSL from Codex CLI, &lt;code&gt;functionCall&lt;/code&gt; from Gemini -- gets captured as a provenance record with model, prompt, file path, timestamp, risk category, and a confidence score.&lt;/p&gt;

&lt;p&gt;Those records get mapped back onto current file contents by the blame engine. The matching algorithm is whitespace-normalized contiguous block matching (newest record wins, exactly like &lt;code&gt;git blame&lt;/code&gt;), with a fuzzy per-line fallback for blocks that were edited after insertion.&lt;/p&gt;

&lt;p&gt;The output for a single file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;-- lineagelens blame - src/routes/auth.py

AI   claude-opus-4-8  2026-06-10   42 | def authenticate_user(token: str):
AI   claude-opus-4-8  2026-06-10   43 |     payload = jwt.decode(token, SECRET, algorithms=["HS256"])
AI?  claude-opus-4-8  2026-06-10   44 |     return db.query(User).filter_by(id=payload["sub"]).first()
                                    45 |
                                    46 | def logout(session_id: str):
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;AI&lt;/code&gt; means exact contiguous match (high confidence). &lt;code&gt;AI?&lt;/code&gt; means the line was in the original AI insertion but has been edited since. Lines with no marker are attributed to human authorship.&lt;/p&gt;

&lt;p&gt;For a whole repository, the Risk Discovery command is one line:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;lineagelens report &lt;span class="nb"&gt;.&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--url&lt;/span&gt; https://lineagelens.internal &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--token&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$JWT&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--workspace&lt;/span&gt; my-team &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--review-status&lt;/span&gt; unreviewed &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--category&lt;/span&gt; auth
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This walks every non-binary file in the repo, matches live content against provenance records, filters to records with no associated human review, and filters further to the &lt;code&gt;auth&lt;/code&gt; risk category. The output is a ranked table: which files have the most unreviewed AI-generated auth code, right now, in the live tree.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;-- lineagelens report - my-repo -- filter: unreviewed, auth

  src/routes/auth.py     ████████████████░░░░  80.5%  53/66 lines
  src/middleware/jwt.py  ██████████░░░░░░░░░░  51.2%  23/45 lines
  src/utils/tokens.py    ████░░░░░░░░░░░░░░░░  18.3%   9/49 lines

  Repo total: 85/160 lines AI-attributed (53.1%) across 3 of 89 scanned -- filter: unreviewed, auth
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The "currently running" half is verified by the blame engine running against live file contents, not git history. The "never reviewed" half is the &lt;code&gt;reviewStatus&lt;/code&gt; filter against the provenance record store.&lt;/p&gt;

&lt;h3&gt;
  
  
  The --json flag for CI
&lt;/h3&gt;

&lt;p&gt;Scriptable output with &lt;code&gt;--json&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;PERCENT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;lineagelens &lt;span class="nt"&gt;--json&lt;/span&gt; report &lt;span class="nb"&gt;.&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--review-status&lt;/span&gt; unreviewed &lt;span class="nt"&gt;--category&lt;/span&gt; auth | jq &lt;span class="s1"&gt;'.stats.percent'&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;((&lt;/span&gt; &lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$PERCENT&lt;/span&gt;&lt;span class="s2"&gt; &amp;gt; 50"&lt;/span&gt; | bc &lt;span class="nt"&gt;-l&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt; &lt;span class="o"&gt;))&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
  &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"::error::Repo has &lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;PERCENT&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;% unreviewed AI auth code -- block merge"&lt;/span&gt;
  &lt;span class="nb"&gt;exit &lt;/span&gt;1
&lt;span class="k"&gt;fi&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  The install path
&lt;/h3&gt;

&lt;p&gt;The CLI works on every tier, including the Base tier (free VS Code extension, no backend required). For the &lt;code&gt;--review-status&lt;/code&gt; and &lt;code&gt;--category&lt;/code&gt; filters, you need backend mode.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;lineagelens-cli

&lt;span class="c"&gt;# Base mode: export from the VS Code extension first&lt;/span&gt;
lineagelens report &lt;span class="nb"&gt;.&lt;/span&gt; &lt;span class="nt"&gt;--input&lt;/span&gt; captures.json

&lt;span class="c"&gt;# Backend mode: Risk Discovery with filters&lt;/span&gt;
lineagelens report &lt;span class="nb"&gt;.&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--url&lt;/span&gt; https://your-lineagelens-instance &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--token&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$JWT&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--workspace&lt;/span&gt; your-workspace &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--review-status&lt;/span&gt; unreviewed &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--category&lt;/span&gt; auth
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  The practical question
&lt;/h3&gt;

&lt;p&gt;81% of teams don't have this answer. Most won't know until an incident forces the archaeology. The archaeology is slow, incomplete, and almost always comes back inconclusive -- because the generation records are gone.&lt;/p&gt;

&lt;p&gt;If a CVE was traced to an AI-generated line in your auth path tomorrow, how would you reconstruct what was generated, by which model, with what prompt context, and whether any human reviewed it before it shipped? If the answer is "we'd look at git blame and the PR comments," you're working with evidence that was never designed to answer that question.&lt;/p&gt;

&lt;p&gt;Star us on GitHub: github.com/lineagelens&lt;/p&gt;

&lt;p&gt;Try the CLI: &lt;code&gt;pip install lineagelens-cli&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;More on the architecture: lineage-website.vercel.app&lt;/p&gt;

&lt;p&gt;What's the highest-risk unreviewed AI code you've found in your own codebase? Auth paths, payment handlers, or something weirder -- drop it in the comments.&lt;/p&gt;

</description>
      <category>developers</category>
      <category>buildinpublic</category>
      <category>cli</category>
      <category>dataengineering</category>
    </item>
    <item>
      <title>think about this !!</title>
      <dc:creator>Praveen</dc:creator>
      <pubDate>Wed, 17 Jun 2026 04:06:23 +0000</pubDate>
      <link>https://dev.to/pn_28428886923dfc665/think-about-this--5239</link>
      <guid>https://dev.to/pn_28428886923dfc665/think-about-this--5239</guid>
      <description>&lt;div class="ltag__link--embedded"&gt;
  &lt;div class="crayons-story "&gt;
  &lt;a href="https://dev.to/pn_28428886923dfc665/approved-is-not-evidence-of-a-review-here-is-what-evidence-actually-looks-like-3h3m" class="crayons-story__hidden-navigation-link"&gt;"Approved" Is Not Evidence of a Review — Here Is What Evidence Actually Looks Like&lt;/a&gt;


  &lt;div class="crayons-story__body crayons-story__body-full_post"&gt;
    &lt;div class="crayons-story__top"&gt;
      &lt;div class="crayons-story__meta"&gt;
        &lt;div class="crayons-story__author-pic"&gt;

          &lt;a href="/pn_28428886923dfc665" class="crayons-avatar  crayons-avatar--l  "&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.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3940098%2F8a7a4942-5b0d-4847-9a7a-2eaf76d0ce30.png" alt="pn_28428886923dfc665 profile" class="crayons-avatar__image" width="96" height="96"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
        &lt;div&gt;
          &lt;div&gt;
            &lt;a href="/pn_28428886923dfc665" class="crayons-story__secondary fw-medium m:hidden"&gt;
              Praveen
            &lt;/a&gt;
            &lt;div class="profile-preview-card relative mb-4 s:mb-0 fw-medium hidden m:inline-block"&gt;
              
                Praveen
                
              
              &lt;div id="story-author-preview-content-3920374" class="profile-preview-card__content crayons-dropdown branded-7 p-4 pt-0"&gt;
                &lt;div class="gap-4 grid"&gt;
                  &lt;div class="-mt-4"&gt;
                    &lt;a href="/pn_28428886923dfc665" class="flex"&gt;
                      &lt;span class="crayons-avatar crayons-avatar--xl mr-2 shrink-0"&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.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3940098%2F8a7a4942-5b0d-4847-9a7a-2eaf76d0ce30.png" class="crayons-avatar__image" alt="" width="96" height="96"&gt;
                      &lt;/span&gt;
                      &lt;span class="crayons-link crayons-subtitle-2 mt-5"&gt;Praveen&lt;/span&gt;
                    &lt;/a&gt;
                  &lt;/div&gt;
                  &lt;div class="print-hidden"&gt;
                    
                      Follow
                    
                  &lt;/div&gt;
                  &lt;div class="author-preview-metadata-container"&gt;&lt;/div&gt;
                &lt;/div&gt;
              &lt;/div&gt;
            &lt;/div&gt;

          &lt;/div&gt;
          &lt;a href="https://dev.to/pn_28428886923dfc665/approved-is-not-evidence-of-a-review-here-is-what-evidence-actually-looks-like-3h3m" class="crayons-story__tertiary fs-xs"&gt;&lt;time&gt;Jun 17&lt;/time&gt;&lt;span class="time-ago-indicator-initial-placeholder"&gt;&lt;/span&gt;&lt;/a&gt;
        &lt;/div&gt;
      &lt;/div&gt;

    &lt;/div&gt;

    &lt;div class="crayons-story__indention"&gt;
      &lt;h2 class="crayons-story__title crayons-story__title-full_post"&gt;
        &lt;a href="https://dev.to/pn_28428886923dfc665/approved-is-not-evidence-of-a-review-here-is-what-evidence-actually-looks-like-3h3m" id="article-link-3920374"&gt;
          "Approved" Is Not Evidence of a Review — Here Is What Evidence Actually Looks Like
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;div class="crayons-story__tags"&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/ai"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;ai&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/mcp"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;mcp&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/developers"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;developers&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/cli"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;cli&lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="crayons-story__bottom"&gt;
        &lt;div class="crayons-story__details"&gt;
          &lt;a href="https://dev.to/pn_28428886923dfc665/approved-is-not-evidence-of-a-review-here-is-what-evidence-actually-looks-like-3h3m" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left"&gt;
            &lt;div class="multiple_reactions_aggregate"&gt;
              &lt;span class="multiple_reactions_icons_container"&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/sparkle-heart-5f9bee3767e18deb1bb725290cb151c25234768a0e9a2bd39370c382d02920cf.svg" width="24" height="24"&gt;
                  &lt;/span&gt;
              &lt;/span&gt;
              &lt;span class="aggregate_reactions_counter"&gt;1&lt;span class="hidden s:inline"&gt;&amp;nbsp;reaction&lt;/span&gt;&lt;/span&gt;
            &lt;/div&gt;
          &lt;/a&gt;
            &lt;a href="https://dev.to/pn_28428886923dfc665/approved-is-not-evidence-of-a-review-here-is-what-evidence-actually-looks-like-3h3m#comments" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left flex items-center"&gt;
              

              &lt;span class="hidden s:inline"&gt;Add&amp;nbsp;Comment&lt;/span&gt;
            &lt;/a&gt;
        &lt;/div&gt;
        &lt;div class="crayons-story__save"&gt;
          &lt;small class="crayons-story__tertiary fs-xs mr-2"&gt;
            6 min read
          &lt;/small&gt;
            
              &lt;span class="bm-initial crayons-icon c-btn__icon"&gt;
                

              &lt;/span&gt;
              &lt;span class="bm-success crayons-icon c-btn__icon"&gt;
                

              &lt;/span&gt;
            
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;


</description>
    </item>
    <item>
      <title>"Approved" Is Not Evidence of a Review — Here Is What Evidence Actually Looks Like</title>
      <dc:creator>Praveen</dc:creator>
      <pubDate>Wed, 17 Jun 2026 04:06:07 +0000</pubDate>
      <link>https://dev.to/pn_28428886923dfc665/approved-is-not-evidence-of-a-review-here-is-what-evidence-actually-looks-like-3h3m</link>
      <guid>https://dev.to/pn_28428886923dfc665/approved-is-not-evidence-of-a-review-here-is-what-evidence-actually-looks-like-3h3m</guid>
      <description>&lt;p&gt;Here is a scenario that happens constantly on teams using AI coding tools.&lt;/p&gt;

&lt;p&gt;A developer is wrapping up before a standup. GitHub shows a PR open for 18 hours — 340 lines added to &lt;code&gt;src/routes/auth.py&lt;/code&gt;, mostly AI-generated by Copilot. They open it on their second monitor, scroll through to the bottom in about four seconds, and click Approve. "Looks fine." PR merges. Status: &lt;code&gt;approved&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Two weeks later someone asks: was this auth refactor actually reviewed?&lt;/p&gt;

&lt;p&gt;The answer stored in every system in your stack is: yes.&lt;/p&gt;

&lt;p&gt;The real answer is: not in any meaningful sense.&lt;/p&gt;




&lt;h2&gt;
  
  
  The gap between "approved" and "reviewed"
&lt;/h2&gt;

&lt;p&gt;The problem is structural, not behavioral. Most teams do not have negligent developers — they have too many PRs, too many AI-generated diffs that look syntactically correct at a glance, and no system that distinguishes a 30-second rubber-stamp from a 20-minute line-by-line review. Both produce the same &lt;code&gt;approved&lt;/code&gt; status.&lt;/p&gt;

&lt;p&gt;For non-AI-generated code, this has always been a problem. For AI-generated code, it is a materially different one. Human-written code embeds the author's context — you can often infer the intent from the surrounding code, the variable names, the structure. AI-generated code can look completely idiomatic while doing something the developer who triggered it did not fully anticipate. The code is a downstream artifact of a prompt. Without the prompt context and a real review of what the model produced, "approved" is a label on a box you did not open.&lt;/p&gt;




&lt;h2&gt;
  
  
  What LineageLens captures when a review happens
&lt;/h2&gt;

&lt;p&gt;LineageLens's backend has a &lt;code&gt;POST /review/attest&lt;/code&gt; endpoint that records a human review as a signed attestation. The payload includes four fields that go beyond a simple verdict:&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;"scopeRef"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"pr/12345"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"linesReviewed"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;340&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"secondsOnDiff"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;42&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"commentCount"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"verdict"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"approved"&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;&lt;code&gt;linesReviewed&lt;/code&gt;, &lt;code&gt;secondsOnDiff&lt;/code&gt;, and &lt;code&gt;commentCount&lt;/code&gt; are the raw behavioral signals from the code review session. The endpoint then computes a &lt;code&gt;depth_signal&lt;/code&gt; from these — not a replacement for the verdict, but an additional classification layer that runs on top of it.&lt;/p&gt;




&lt;h2&gt;
  
  
  The depth_signal formula
&lt;/h2&gt;

&lt;p&gt;The full formula is documented in &lt;code&gt;lineagelens-backend/app/services/human_review_service.py&lt;/code&gt;. It is intentionally transparent — if you are going to use a score to block merges, the thresholds should be auditable:&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="c1"&gt;# Input signals
&lt;/span&gt;&lt;span class="n"&gt;time_per_line&lt;/span&gt;  &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;seconds_on_diff&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="nf"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;lines_reviewed&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;comment_count&lt;/span&gt;  &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;inline&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="n"&gt;PR&lt;/span&gt; &lt;span class="n"&gt;comments&lt;/span&gt; &lt;span class="n"&gt;left&lt;/span&gt; &lt;span class="n"&gt;by&lt;/span&gt; &lt;span class="n"&gt;the&lt;/span&gt; &lt;span class="n"&gt;reviewer&lt;/span&gt;
&lt;span class="n"&gt;lines_reviewed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;AI&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;flagged&lt;/span&gt; &lt;span class="n"&gt;lines&lt;/span&gt; &lt;span class="n"&gt;the&lt;/span&gt; &lt;span class="n"&gt;reviewer&lt;/span&gt; &lt;span class="n"&gt;claims&lt;/span&gt; &lt;span class="n"&gt;to&lt;/span&gt; &lt;span class="n"&gt;have&lt;/span&gt; &lt;span class="n"&gt;seen&lt;/span&gt;

&lt;span class="c1"&gt;# Scoring (0–100):
&lt;/span&gt;&lt;span class="n"&gt;time_score&lt;/span&gt;     &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;min&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;time_per_line&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mf"&gt;5.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;1.0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="err"&gt;×&lt;/span&gt; &lt;span class="mi"&gt;40&lt;/span&gt;   &lt;span class="err"&gt;→&lt;/span&gt; &lt;span class="nb"&gt;max&lt;/span&gt; &lt;span class="mi"&gt;40&lt;/span&gt; &lt;span class="n"&gt;pts&lt;/span&gt; &lt;span class="n"&gt;at&lt;/span&gt; &lt;span class="err"&gt;≥&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;line&lt;/span&gt;
&lt;span class="n"&gt;comment_score&lt;/span&gt;  &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;min&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;comment_count&lt;/span&gt;  &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mf"&gt;3.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;1.0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="err"&gt;×&lt;/span&gt; &lt;span class="mi"&gt;30&lt;/span&gt;   &lt;span class="err"&gt;→&lt;/span&gt; &lt;span class="nb"&gt;max&lt;/span&gt; &lt;span class="mi"&gt;30&lt;/span&gt; &lt;span class="n"&gt;pts&lt;/span&gt; &lt;span class="n"&gt;at&lt;/span&gt; &lt;span class="err"&gt;≥&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt; &lt;span class="n"&gt;comments&lt;/span&gt;
&lt;span class="n"&gt;coverage_score&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;min&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;lines_reviewed&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mf"&gt;50.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;1.0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="err"&gt;×&lt;/span&gt; &lt;span class="mi"&gt;30&lt;/span&gt;  &lt;span class="err"&gt;→&lt;/span&gt; &lt;span class="nb"&gt;max&lt;/span&gt; &lt;span class="mi"&gt;30&lt;/span&gt; &lt;span class="n"&gt;pts&lt;/span&gt; &lt;span class="n"&gt;at&lt;/span&gt; &lt;span class="err"&gt;≥&lt;/span&gt; &lt;span class="mi"&gt;50&lt;/span&gt; &lt;span class="n"&gt;lines&lt;/span&gt;

&lt;span class="c1"&gt;# Bands:
#   shallow   raw_score &amp;lt; 35
#   adequate  35 ≤ raw_score &amp;lt; 70
#   deep      raw_score ≥ 70
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The time signal gets the most weight (40 points) because time-per-line is the hardest signal to fake without actually reading. A developer who spent 30 seconds on a 340-line diff clocked 0.088 seconds per line. To score maximum time points on that diff, they would need to spend 28 minutes on it — roughly 5 seconds per line.&lt;/p&gt;

&lt;p&gt;The comment signal (30 points) captures engagement. A reviewer who left three or more inline comments clearly engaged with the diff content. Zero comments on 340 AI-generated lines in an auth file is a meaningful absence.&lt;/p&gt;

&lt;p&gt;The coverage signal (30 points) is the least informative in isolation — &lt;code&gt;linesReviewed&lt;/code&gt; is self-reported. But combined with the time signal, it creates a consistency check: if you claim to have reviewed 340 lines in 42 seconds, the time_per_line is 0.12 seconds. That floors the time_score and caps the achievable total regardless of coverage.&lt;/p&gt;




&lt;h2&gt;
  
  
  The rubber-stamp override
&lt;/h2&gt;

&lt;p&gt;The most important rule in the formula is not the scoring — it is this:&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="c1"&gt;# Implausibly-fast override: time_per_line &amp;lt; 1.0 s → always "shallow"
# (flags rubber-stamp approvals of large diffs, e.g. 3 s for 400 lines).
&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;time_per_line&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mf"&gt;1.0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;shallow&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.0&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the reviewer spent less than one second per line — regardless of comment count, regardless of lines_reviewed, regardless of verdict — the result is &lt;code&gt;shallow&lt;/code&gt; with a raw score of zero.&lt;/p&gt;

&lt;p&gt;This catches the scenario at the top of this article. 4 seconds on 340 lines = 0.012 seconds per line. The gate returns &lt;code&gt;shallow&lt;/code&gt;. The signed attestation records it. A merge gate configured to require &lt;code&gt;adequate&lt;/code&gt; or higher rejects the PR.&lt;/p&gt;

&lt;p&gt;The threshold of 1.0 second per line is a judgment call, not a scientific constant. A genuinely fast reader who knows the codebase well might clock 2 seconds per line. A reviewer skimming for obvious errors might spend 1.5 seconds. The override is calibrated to catch clearly impossible review speeds — not to penalize fast reviewers who know what they are looking at.&lt;/p&gt;




&lt;h2&gt;
  
  
  What the attestation record looks like
&lt;/h2&gt;

&lt;p&gt;After &lt;code&gt;compute_depth_signal()&lt;/code&gt; runs, &lt;code&gt;record_review()&lt;/code&gt; signs the result and persists two rows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;Attestation row&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;subject_type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;review"&lt;/span&gt;
  &lt;span class="na"&gt;subject_id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;pr/12345"&lt;/span&gt;
  &lt;span class="na"&gt;statement_json&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;{&lt;/span&gt;
    &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;reviewer"&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;user-uuid"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt;
    &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;lines_reviewed"&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;340&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt;
    &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;seconds_on_diff"&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;42&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt;
    &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;comment_count"&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;0&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt;
    &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;depth_signal"&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;shallow"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt;
    &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;depth_score"&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;0.0&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt;
    &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;verdict"&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;approved"&lt;/span&gt;
  &lt;span class="pi"&gt;}&lt;/span&gt;
  &lt;span class="na"&gt;signature&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;lt;Ed25519 signature over canonical JSON&amp;gt;&lt;/span&gt;
  &lt;span class="na"&gt;public_key_id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;lt;16-hex fingerprint&amp;gt;&lt;/span&gt;

&lt;span class="na"&gt;HumanReviewAttestation row&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;scope_ref&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;pr/12345"&lt;/span&gt;
  &lt;span class="na"&gt;depth_signal&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;shallow"&lt;/span&gt;
  &lt;span class="na"&gt;verdict&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;approved"&lt;/span&gt;
  &lt;span class="na"&gt;attestation_id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;lt;FK to Attestation&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The signed statement means the depth classification cannot be retroactively altered without invalidating the signature. The audit trail is not just what someone claimed — it is what the behavioral signals said, cryptographically bound to the review event.&lt;/p&gt;

&lt;p&gt;This is a different kind of evidence than a closed PR. A closed PR tells you someone clicked approve. The attestation tells you how long they spent, what depth that corresponded to, and whether the classification was &lt;code&gt;shallow&lt;/code&gt;, &lt;code&gt;adequate&lt;/code&gt;, or &lt;code&gt;deep&lt;/code&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  The CI merge gate
&lt;/h2&gt;

&lt;p&gt;The gate endpoint sits at &lt;code&gt;POST /review/gate/{pr_ref}?min_depth=adequate&lt;/code&gt;. It is designed to be called by CI (X-API-Key auth, not JWT) at the point a PR is ready to merge:&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="err"&gt;GET&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;/review/gate/pr/&lt;/span&gt;&lt;span class="mi"&gt;12345&lt;/span&gt;&lt;span class="err"&gt;?min_depth=adequate&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="err"&gt;→&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;OK&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;"prRef"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"pr/12345"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"passed"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"reason"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Review depth 'shallow' is below minimum required 'adequate'."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"depthSignal"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"shallow"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"verdict"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"approved"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"minDepthRequired"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"adequate"&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;The gate passes only when &lt;code&gt;depth_signal &amp;gt;= min_depth&lt;/code&gt; AND &lt;code&gt;verdict == "approved"&lt;/code&gt;. Three valid values for &lt;code&gt;min_depth&lt;/code&gt;: &lt;code&gt;shallow&lt;/code&gt;, &lt;code&gt;adequate&lt;/code&gt;, &lt;code&gt;deep&lt;/code&gt;. The depth ranking is ordinal: &lt;code&gt;shallow=0&lt;/code&gt;, &lt;code&gt;adequate=1&lt;/code&gt;, &lt;code&gt;deep=2&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The practical effect: for high-sensitivity paths (auth, payments, security), you set &lt;code&gt;min_depth=deep&lt;/code&gt; and require at least 70 points — which means roughly 5 seconds per line minimum, 3+ comments, and 50+ lines reviewed. That is a real review. For standard paths, &lt;code&gt;min_depth=adequate&lt;/code&gt; at 35+ points is a reasonable floor that blocks 3-second approvals while allowing fast but engaged reviewers through.&lt;/p&gt;




&lt;h2&gt;
  
  
  ASCII flow diagram
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Developer reviews AI-generated PR
          │
          ▼
  POST /review/attest
  { linesReviewed, secondsOnDiff, commentCount, verdict }
          │
          ▼
  compute_depth_signal()
  ┌─────────────────────────────────────────────────────┐
  │  time_per_line = seconds / lines                    │
  │  if time_per_line &amp;lt; 1.0 → return ("shallow", 0.0)  │
  │  time_score     = min(tpl/5.0, 1.0) × 40           │
  │  comment_score  = min(cc/3.0, 1.0) × 30            │
  │  coverage_score = min(lr/50.0, 1.0) × 30           │
  │  raw = sum → band: shallow / adequate / deep        │
  └─────────────────────────────────────────────────────┘
          │
          ▼
  sign_attestation() [Ed25519]
  persist Attestation + HumanReviewAttestation
          │
          ▼
  CI calls POST /review/gate/{pr_ref}?min_depth=adequate
          │
     ┌────┴─────┐
     │          │
  passed     blocked
  (merge)    (reason: "Review depth 'shallow' is below 'adequate'")
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  What this does not solve
&lt;/h2&gt;

&lt;p&gt;The depth signal is a behavioral proxy, not a comprehension test. A developer who knows how to game the system can sit on a diff for 30 minutes, leave three generic comments, and achieve &lt;code&gt;deep&lt;/code&gt; classification without actually understanding what the AI produced. This is not a flaw unique to this design — any measurable signal can be gamed. The value is not that the system is ungameable; it is that it sets a minimum bar that catches the most common failure mode (pure rubber-stamping) and creates an auditable record of the behavioral signals.&lt;/p&gt;

&lt;p&gt;The formula is also calibrated for AI-generated code review, not general PR review. The 5-seconds-per-line maximum for time_score reflects the assumption that AI-generated code may require more careful reading than human-written code where you know the author's patterns. Teams reviewing dense algorithm implementations might reasonably argue the bar should be higher. The comment threshold of 3 might be too low for a 340-line diff. These are parameters worth arguing about — and they should be.&lt;/p&gt;




&lt;h2&gt;
  
  
  The connection to Risk Discovery
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;depth_signal&lt;/code&gt; system exists because &lt;code&gt;reviewStatus&lt;/code&gt; alone is not a sufficient filter. The full Risk Discovery query in LineageLens is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;lineagelens report &lt;span class="nb"&gt;.&lt;/span&gt; &lt;span class="nt"&gt;--unreviewed&lt;/span&gt; &lt;span class="nt"&gt;--category&lt;/span&gt; auth
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Without the depth classification, "unreviewed" only catches code with no review record at all. With it, you can extend the definition: code where the only review on record is &lt;code&gt;shallow&lt;/code&gt; is operationally closer to unreviewed than to reviewed. The filter becomes meaningfully stricter.&lt;/p&gt;

&lt;p&gt;More at &lt;a href="https://lineage-website.vercel.app" rel="noopener noreferrer"&gt;lineage-website.vercel.app&lt;/a&gt;. The Hashnode post goes deeper on the design tradeoffs I considered before settling on this formula.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;One question for the comments:&lt;/strong&gt; Is 1 second per line the right floor for the rubber-stamp override? What threshold would you set for auth-path code — and what signals am I missing that would make this more reliable?&lt;/p&gt;

</description>
      <category>ai</category>
      <category>mcp</category>
      <category>developers</category>
      <category>cli</category>
    </item>
    <item>
      <title>discuss about it !!</title>
      <dc:creator>Praveen</dc:creator>
      <pubDate>Mon, 15 Jun 2026 05:49:20 +0000</pubDate>
      <link>https://dev.to/pn_28428886923dfc665/discuss-about-it--a17</link>
      <guid>https://dev.to/pn_28428886923dfc665/discuss-about-it--a17</guid>
      <description>&lt;div class="ltag__link--embedded"&gt;
  &lt;div class="crayons-story "&gt;
  &lt;a href="https://dev.to/pn_28428886923dfc665/one-openai-compatible-adapter-seven-ai-coding-tools-three-very-different-wire-formats-17fp" class="crayons-story__hidden-navigation-link"&gt;One OpenAI-Compatible Adapter. Seven AI Coding Tools. Three Very Different Wire Formats.&lt;/a&gt;


  &lt;div class="crayons-story__body crayons-story__body-full_post"&gt;
    &lt;div class="crayons-story__top"&gt;
      &lt;div class="crayons-story__meta"&gt;
        &lt;div class="crayons-story__author-pic"&gt;

          &lt;a href="/pn_28428886923dfc665" class="crayons-avatar  crayons-avatar--l  "&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.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3940098%2F8a7a4942-5b0d-4847-9a7a-2eaf76d0ce30.png" alt="pn_28428886923dfc665 profile" class="crayons-avatar__image"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
        &lt;div&gt;
          &lt;div&gt;
            &lt;a href="/pn_28428886923dfc665" class="crayons-story__secondary fw-medium m:hidden"&gt;
              Praveen
            &lt;/a&gt;
            &lt;div class="profile-preview-card relative mb-4 s:mb-0 fw-medium hidden m:inline-block"&gt;
              
                Praveen
                
              
              &lt;div id="story-author-preview-content-3903523" class="profile-preview-card__content crayons-dropdown branded-7 p-4 pt-0"&gt;
                &lt;div class="gap-4 grid"&gt;
                  &lt;div class="-mt-4"&gt;
                    &lt;a href="/pn_28428886923dfc665" class="flex"&gt;
                      &lt;span class="crayons-avatar crayons-avatar--xl mr-2 shrink-0"&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.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3940098%2F8a7a4942-5b0d-4847-9a7a-2eaf76d0ce30.png" class="crayons-avatar__image" alt=""&gt;
                      &lt;/span&gt;
                      &lt;span class="crayons-link crayons-subtitle-2 mt-5"&gt;Praveen&lt;/span&gt;
                    &lt;/a&gt;
                  &lt;/div&gt;
                  &lt;div class="print-hidden"&gt;
                    
                      Follow
                    
                  &lt;/div&gt;
                  &lt;div class="author-preview-metadata-container"&gt;&lt;/div&gt;
                &lt;/div&gt;
              &lt;/div&gt;
            &lt;/div&gt;

          &lt;/div&gt;
          &lt;a href="https://dev.to/pn_28428886923dfc665/one-openai-compatible-adapter-seven-ai-coding-tools-three-very-different-wire-formats-17fp" class="crayons-story__tertiary fs-xs"&gt;&lt;time&gt;Jun 15&lt;/time&gt;&lt;span class="time-ago-indicator-initial-placeholder"&gt;&lt;/span&gt;&lt;/a&gt;
        &lt;/div&gt;
      &lt;/div&gt;

    &lt;/div&gt;

    &lt;div class="crayons-story__indention"&gt;
      &lt;h2 class="crayons-story__title crayons-story__title-full_post"&gt;
        &lt;a href="https://dev.to/pn_28428886923dfc665/one-openai-compatible-adapter-seven-ai-coding-tools-three-very-different-wire-formats-17fp" id="article-link-3903523"&gt;
          One OpenAI-Compatible Adapter. Seven AI Coding Tools. Three Very Different Wire Formats.
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;div class="crayons-story__tags"&gt;
            &lt;a class="crayons-tag crayons-tag--filled  " href="/t/discuss"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;discuss&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/security"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;security&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/showdev"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;showdev&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/startup"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;startup&lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="crayons-story__bottom"&gt;
        &lt;div class="crayons-story__details"&gt;
          &lt;a href="https://dev.to/pn_28428886923dfc665/one-openai-compatible-adapter-seven-ai-coding-tools-three-very-different-wire-formats-17fp" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left"&gt;
            &lt;div class="multiple_reactions_aggregate"&gt;
              &lt;span class="multiple_reactions_icons_container"&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/sparkle-heart-5f9bee3767e18deb1bb725290cb151c25234768a0e9a2bd39370c382d02920cf.svg" width="18" height="18"&gt;
                  &lt;/span&gt;
              &lt;/span&gt;
              &lt;span class="aggregate_reactions_counter"&gt;1&lt;span class="hidden s:inline"&gt;&amp;nbsp;reaction&lt;/span&gt;&lt;/span&gt;
            &lt;/div&gt;
          &lt;/a&gt;
            &lt;a href="https://dev.to/pn_28428886923dfc665/one-openai-compatible-adapter-seven-ai-coding-tools-three-very-different-wire-formats-17fp#comments" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left flex items-center"&gt;
              

              &lt;span class="hidden s:inline"&gt;Add&amp;nbsp;Comment&lt;/span&gt;
            &lt;/a&gt;
        &lt;/div&gt;
        &lt;div class="crayons-story__save"&gt;
          &lt;small class="crayons-story__tertiary fs-xs mr-2"&gt;
            4 min read
          &lt;/small&gt;
            
              &lt;span class="bm-initial crayons-icon c-btn__icon"&gt;
                

              &lt;/span&gt;
              &lt;span class="bm-success crayons-icon c-btn__icon"&gt;
                

              &lt;/span&gt;
            
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;


</description>
    </item>
    <item>
      <title>One OpenAI-Compatible Adapter. Seven AI Coding Tools. Three Very Different Wire Formats.</title>
      <dc:creator>Praveen</dc:creator>
      <pubDate>Mon, 15 Jun 2026 05:49:05 +0000</pubDate>
      <link>https://dev.to/pn_28428886923dfc665/one-openai-compatible-adapter-seven-ai-coding-tools-three-very-different-wire-formats-17fp</link>
      <guid>https://dev.to/pn_28428886923dfc665/one-openai-compatible-adapter-seven-ai-coding-tools-three-very-different-wire-formats-17fp</guid>
      <description>&lt;p&gt;If your team uses Aider, Cline, Continue, Copilot CLI, Goose, or Windsurf — plus any OpenAI-compatible backend like Azure, groq, fireworks, mistral, or together.ai — you now have a single provenance capture layer for all of them. It shipped this week in &lt;code&gt;lineagelens-proxy/adapters/openai_chat.py&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;But writing that adapter revealed something worth documenting: "speaks the Chat Completions format" does not mean "expresses code edits the same way." There are three distinct patterns in the wild, and missing any one of them produces silent capture gaps.&lt;/p&gt;

&lt;h3&gt;
  
  
  The three edit expression patterns
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Pattern A: Tool-call edits&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The canonical OpenAI way. The model emits &lt;code&gt;choices[].message.tool_calls[]&lt;/code&gt;, each with a function name and JSON-serialized arguments. File edits arrive as structured arguments to tools like &lt;code&gt;write_file&lt;/code&gt;, &lt;code&gt;str_replace_editor&lt;/code&gt;, or &lt;code&gt;apply_patch&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;In streaming mode this means assembling argument JSON fragments across SSE chunks — each &lt;code&gt;tool_calls[].function.arguments&lt;/code&gt; delta is a partial string that only makes sense when concatenated with every prior delta for the same &lt;code&gt;tool_calls[].index&lt;/code&gt;. The adapter accumulates these per &lt;code&gt;(choice_index, tool_call_index)&lt;/code&gt; before parsing.&lt;/p&gt;

&lt;p&gt;The adapter also handles the legacy singular &lt;code&gt;function_call&lt;/code&gt; field, which OpenAI deprecated but older tool integrations still emit.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pattern B: Text-content edits&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This is where it gets interesting. Several major tools — Aider being the most prominent — do not use tool calls at all. They send edits as structured text inside the assistant message content. Three sub-formats exist:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Aider SEARCH/REPLACE blocks&lt;/strong&gt;: &lt;code&gt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt; SEARCH&lt;/code&gt;, &lt;code&gt;=====&lt;/code&gt;, &lt;code&gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt; REPLACE&lt;/code&gt; delimiters. The filename is on the line immediately before the opening fence, not inside the block itself. The adapter has to look backwards in the preceding text to find it.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Unified diffs&lt;/strong&gt;: Standard &lt;code&gt;--- a/file&lt;/code&gt; / &lt;code&gt;+++ b/file&lt;/code&gt; / &lt;code&gt;@@&lt;/code&gt; format. The adapter parses these into per-file edit records.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Fenced code blocks&lt;/strong&gt;: The fallback. A block with a &lt;code&gt;path=&lt;/code&gt; hint in the info string, or a &lt;code&gt;# file: path/to/file&lt;/code&gt; comment on the first line of the code. If neither is present, the block is still captured as &lt;code&gt;file_path="proxy-capture"&lt;/code&gt; — never silently dropped.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The priority order matters: apply-patch DSL is tried first, then Aider SEARCH/REPLACE, then unified diff. Fenced code blocks only run when none of the structured formats matched. This ensures a single edit is never recorded twice.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pattern C: Mixed responses&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A single response can carry both tool calls and structured text content. This is more common than you might expect — some tools emit a tool call for the primary edit and then add context or diff output as text. The adapter runs both paths against every response.&lt;/p&gt;

&lt;h3&gt;
  
  
  The session key problem
&lt;/h3&gt;

&lt;p&gt;When two developers are using the same proxy simultaneously and happen to send requests that produce overlapping &lt;code&gt;tool_call_id&lt;/code&gt; values, you get an aliasing bug: two unrelated edits get cross-attributed.&lt;/p&gt;

&lt;p&gt;The fix is a session fingerprint derived from the request, not a counter or timestamp:&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="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;_openai_chat_session_key&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;body_dict&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;system&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;""&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;body_dict&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;messages&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="p"&gt;[]):&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nf"&gt;isinstance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;role&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;system&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;developer&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="n"&gt;system&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;_content_to_text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;content&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
            &lt;span class="k"&gt;break&lt;/span&gt;

    &lt;span class="n"&gt;auth&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;""&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;header_name&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;authorization&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;x-api-key&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Authorization&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;X-Api-Key&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;v&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;header_name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;auth&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="p"&gt;[:&lt;/span&gt;&lt;span class="mi"&gt;24&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
            &lt;span class="k"&gt;break&lt;/span&gt;

    &lt;span class="n"&gt;raw&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;openai-chat|&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;system&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;4096&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;|&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;auth&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;hashlib&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sha256&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;raw&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-8&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;errors&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;replace&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nf"&gt;hexdigest&lt;/span&gt;&lt;span class="p"&gt;()[:&lt;/span&gt;&lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;SHA-256 over the system message prefix and the first 24 characters of the auth header. The result is a 16-hex-char fingerprint. The pending-edits store keys on &lt;code&gt;(session_key, tool_call_id)&lt;/code&gt; tuples — making aliasing across concurrent sessions geometrically unlikely (~1 in 10^19).&lt;/p&gt;

&lt;h3&gt;
  
  
  The fail-open rule
&lt;/h3&gt;

&lt;p&gt;Every parse path in the adapter is wrapped in exception handling that fails open: a JSON decode error, a malformed SSE chunk, a tool-call argument that does not map to any known edit shape — none of these surface as errors to the forwarding path. The response always passes through to the tool.&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="c1"&gt;# Everything is fail-open: a parse error never raises into the forwarding path.
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is not a compromise. It is the design requirement. A governance layer that crashes the developer's tool gets disabled within hours of installation. The only viable architecture for a capture layer that developers leave running is one that can never interrupt the thing they care about — their coding session.&lt;/p&gt;

&lt;h3&gt;
  
  
  What this enables for Risk Discovery
&lt;/h3&gt;

&lt;p&gt;The concrete payoff is in the &lt;code&gt;lineagelens report&lt;/code&gt; CLI. With full coverage across Aider, Cline, Windsurf, and every groq/mistral/Azure backend, you can now run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;lineagelens report &lt;span class="nb"&gt;.&lt;/span&gt; &lt;span class="nt"&gt;--unreviewed&lt;/span&gt; &lt;span class="nt"&gt;--category&lt;/span&gt; auth
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And get back every AI-written line in your auth paths — regardless of which tool wrote it — that has never been reviewed. This query is only meaningful when your capture layer actually covers your whole stack. An adapter that misses Aider's text-content edits silently excludes every Aider-generated file from that result.&lt;/p&gt;

&lt;p&gt;The path annotation on every captured record looks like this:&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="err"&gt;tool_name:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"aider_search_replace"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"text_codeblock"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"str_replace_editor"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="err"&gt;file_path:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"src/routes/auth.py"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="err"&gt;verb:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"replace"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"write"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"add"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="err"&gt;prompt_context:&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="err"&gt;model:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"gpt-4o"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;system:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"..."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;messages:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="err"&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;Three fields that a git blame will never give you: which tool made this change, what the model was, and what the developer asked for.&lt;/p&gt;

&lt;h3&gt;
  
  
  What is not yet covered
&lt;/h3&gt;

&lt;p&gt;The adapter comment is explicit about the gaps:&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="c1"&gt;# TODO(bedrock/vertex): Claude-via-Bedrock uses /model/{id}/invoke[-with-response-stream]
# on *.bedrock-runtime.*.amazonaws.com and Gemini-via-Vertex uses
# :generateContent / :streamGenerateContent on *-aiplatform.googleapis.com.
# Those are NOT chat/completions and are NOT captured here — they need their own adapters.
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If your team routes through Bedrock or Vertex, this adapter does not cover you. Separate adapters are needed. This is the right design — assuming coverage for endpoints you have not explicitly tested is worse than documenting the gap.&lt;/p&gt;

&lt;h3&gt;
  
  
  Getting started
&lt;/h3&gt;

&lt;p&gt;The adapter is part of the LineageLens proxy, which runs alongside your AI coding tool and intercepts traffic at the network layer. No tool modification required for tools that already target a configurable API endpoint.&lt;/p&gt;

&lt;p&gt;Cross-reference: for Hashnode readers, I wrote about the broader proxy modularization and what each adapter handles at lineage-website.vercel.app.&lt;/p&gt;

&lt;p&gt;Full source at &lt;a href="https://lineage-website.vercel.app" rel="noopener noreferrer"&gt;lineage-website.vercel.app&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Which tools in your stack are you most worried about provenance gaps for — the chat-completions tools, or the proprietary backends (Cursor, GitHub Copilot) that cannot be proxied at all?&lt;/p&gt;

</description>
      <category>security</category>
      <category>discuss</category>
      <category>showdev</category>
      <category>startup</category>
    </item>
    <item>
      <title>what's your opininon on this</title>
      <dc:creator>Praveen</dc:creator>
      <pubDate>Sun, 14 Jun 2026 05:14:12 +0000</pubDate>
      <link>https://dev.to/pn_28428886923dfc665/whats-your-opininon-on-this-27c0</link>
      <guid>https://dev.to/pn_28428886923dfc665/whats-your-opininon-on-this-27c0</guid>
      <description>&lt;div class="ltag__link--embedded"&gt;
  &lt;div class="crayons-story "&gt;
  &lt;a href="https://dev.to/pn_28428886923dfc665/your-code-review-process-is-verbal-heres-what-a-machine-verifiable-proof-of-ai-code-safety-looks-1j1n" class="crayons-story__hidden-navigation-link"&gt;Your Code Review Process Is Verbal. Here's What a Machine-Verifiable Proof of AI Code Safety Looks Like.&lt;/a&gt;


  &lt;div class="crayons-story__body crayons-story__body-full_post"&gt;
    &lt;div class="crayons-story__top"&gt;
      &lt;div class="crayons-story__meta"&gt;
        &lt;div class="crayons-story__author-pic"&gt;

          &lt;a href="/pn_28428886923dfc665" class="crayons-avatar  crayons-avatar--l  "&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.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3940098%2F8a7a4942-5b0d-4847-9a7a-2eaf76d0ce30.png" alt="pn_28428886923dfc665 profile" class="crayons-avatar__image" width="96" height="96"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
        &lt;div&gt;
          &lt;div&gt;
            &lt;a href="/pn_28428886923dfc665" class="crayons-story__secondary fw-medium m:hidden"&gt;
              Praveen
            &lt;/a&gt;
            &lt;div class="profile-preview-card relative mb-4 s:mb-0 fw-medium hidden m:inline-block"&gt;
              
                Praveen
                
              
              &lt;div id="story-author-preview-content-3895765" class="profile-preview-card__content crayons-dropdown branded-7 p-4 pt-0"&gt;
                &lt;div class="gap-4 grid"&gt;
                  &lt;div class="-mt-4"&gt;
                    &lt;a href="/pn_28428886923dfc665" class="flex"&gt;
                      &lt;span class="crayons-avatar crayons-avatar--xl mr-2 shrink-0"&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.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3940098%2F8a7a4942-5b0d-4847-9a7a-2eaf76d0ce30.png" class="crayons-avatar__image" alt="" width="96" height="96"&gt;
                      &lt;/span&gt;
                      &lt;span class="crayons-link crayons-subtitle-2 mt-5"&gt;Praveen&lt;/span&gt;
                    &lt;/a&gt;
                  &lt;/div&gt;
                  &lt;div class="print-hidden"&gt;
                    
                      Follow
                    
                  &lt;/div&gt;
                  &lt;div class="author-preview-metadata-container"&gt;&lt;/div&gt;
                &lt;/div&gt;
              &lt;/div&gt;
            &lt;/div&gt;

          &lt;/div&gt;
          &lt;a href="https://dev.to/pn_28428886923dfc665/your-code-review-process-is-verbal-heres-what-a-machine-verifiable-proof-of-ai-code-safety-looks-1j1n" class="crayons-story__tertiary fs-xs"&gt;&lt;time&gt;Jun 14&lt;/time&gt;&lt;span class="time-ago-indicator-initial-placeholder"&gt;&lt;/span&gt;&lt;/a&gt;
        &lt;/div&gt;
      &lt;/div&gt;

    &lt;/div&gt;

    &lt;div class="crayons-story__indention"&gt;
      &lt;h2 class="crayons-story__title crayons-story__title-full_post"&gt;
        &lt;a href="https://dev.to/pn_28428886923dfc665/your-code-review-process-is-verbal-heres-what-a-machine-verifiable-proof-of-ai-code-safety-looks-1j1n" id="article-link-3895765"&gt;
          Your Code Review Process Is Verbal. Here's What a Machine-Verifiable Proof of AI Code Safety Looks Like.
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;div class="crayons-story__tags"&gt;
            &lt;a class="crayons-tag crayons-tag--filled  " href="/t/showdev"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;showdev&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/ai"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;ai&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/security"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;security&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/news"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;news&lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="crayons-story__bottom"&gt;
        &lt;div class="crayons-story__details"&gt;
          &lt;a href="https://dev.to/pn_28428886923dfc665/your-code-review-process-is-verbal-heres-what-a-machine-verifiable-proof-of-ai-code-safety-looks-1j1n" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left"&gt;
            &lt;div class="multiple_reactions_aggregate"&gt;
              &lt;span class="multiple_reactions_icons_container"&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/sparkle-heart-5f9bee3767e18deb1bb725290cb151c25234768a0e9a2bd39370c382d02920cf.svg" width="24" height="24"&gt;
                  &lt;/span&gt;
              &lt;/span&gt;
              &lt;span class="aggregate_reactions_counter"&gt;1&lt;span class="hidden s:inline"&gt;&amp;nbsp;reaction&lt;/span&gt;&lt;/span&gt;
            &lt;/div&gt;
          &lt;/a&gt;
            &lt;a href="https://dev.to/pn_28428886923dfc665/your-code-review-process-is-verbal-heres-what-a-machine-verifiable-proof-of-ai-code-safety-looks-1j1n#comments" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left flex items-center"&gt;
              

              &lt;span class="hidden s:inline"&gt;Add&amp;nbsp;Comment&lt;/span&gt;
            &lt;/a&gt;
        &lt;/div&gt;
        &lt;div class="crayons-story__save"&gt;
          &lt;small class="crayons-story__tertiary fs-xs mr-2"&gt;
            4 min read
          &lt;/small&gt;
            
              &lt;span class="bm-initial crayons-icon c-btn__icon"&gt;
                

              &lt;/span&gt;
              &lt;span class="bm-success crayons-icon c-btn__icon"&gt;
                

              &lt;/span&gt;
            
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;


</description>
    </item>
    <item>
      <title>Your Code Review Process Is Verbal. Here's What a Machine-Verifiable Proof of AI Code Safety Looks Like.</title>
      <dc:creator>Praveen</dc:creator>
      <pubDate>Sun, 14 Jun 2026 05:13:51 +0000</pubDate>
      <link>https://dev.to/pn_28428886923dfc665/your-code-review-process-is-verbal-heres-what-a-machine-verifiable-proof-of-ai-code-safety-looks-1j1n</link>
      <guid>https://dev.to/pn_28428886923dfc665/your-code-review-process-is-verbal-heres-what-a-machine-verifiable-proof-of-ai-code-safety-looks-1j1n</guid>
      <description>&lt;p&gt;Most code review processes produce one artifact: a merged PR. Someone approved it. The review presumably happened. But if an auditor asks you to prove that the AI-generated function in your auth service passed your risk policy — that the model was on your allowlist, that the risk score was below threshold, that a human actually approved it — what do you hand them?&lt;/p&gt;

&lt;p&gt;A closed PR is not evidence of the above. It is evidence that someone clicked "Approve." The model identity, the risk state at merge time, whether the reviewer read the AI context or just the diff — none of that is in the PR.&lt;/p&gt;

&lt;p&gt;This is the gap that machine-verifiable AI code certificates close.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Problem Is Structural
&lt;/h2&gt;

&lt;p&gt;When you merge AI-generated code today, you lose the generation context permanently. The commit records the diff. Git blame records the author. Nothing records which model generated it, what the prompt was, what the risk score was at insertion, or whether the human reviewer actually engaged with the full AI context.&lt;/p&gt;

&lt;p&gt;Post-merge, you're reconstructing from memory and process documentation. That reconstruction will not survive a security audit. It certainly won't survive an EU AI Act compliance review, where Article 12 requires records of AI system outputs to be kept for a period appropriate to the purpose.&lt;/p&gt;

&lt;h2&gt;
  
  
  What an Indemnity Certificate Actually Contains
&lt;/h2&gt;

&lt;p&gt;LineageLens's indemnity system issues certificates at three scopes: per-record, per-PR, and per-release. The evaluation runs against your workspace's active &lt;code&gt;IndemnityPolicy&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;A policy has five configurable rules:&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="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PolicyRules&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BaseModel&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;max_risk_score&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Field&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;default&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;70&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ge&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;le&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;require_license_clean&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;bool&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Field&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;default&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;require_human_review&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;bool&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Field&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;default&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;allowed_models&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Field&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;default_factory&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;unknown_review_pass&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;bool&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Field&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;default&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;cert_ttl_days&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Field&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;default&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;90&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ge&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;le&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;3650&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When you call &lt;code&gt;POST /indemnity/certificate&lt;/code&gt; with &lt;code&gt;scope=pr&lt;/code&gt; and &lt;code&gt;scope_ref=PR-442&lt;/code&gt;, the service fetches every provenance record tagged &lt;code&gt;pr:PR-442&lt;/code&gt;, evaluates each one against all five rules, and either issues a certificate or returns a structured list of reasons why eligibility failed.&lt;/p&gt;

&lt;p&gt;The evaluation is explicit:&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="c1"&gt;# Risk check
&lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;record&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;risk_score&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;record&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;risk_score&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;max_risk&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;eligible&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;
    &lt;span class="n"&gt;reasons&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Record &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;uid&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;: risk score &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;record&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;risk_score&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; exceeds policy maximum &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;max_risk&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Model allowlist check
&lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;allowed_models&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;record&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;model_name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;record&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;model_name&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;allowed_models&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;eligible&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;
        &lt;span class="n"&gt;reasons&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Record &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;uid&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;: model &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;record&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;model_name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt; is not in the policy allowed-models list.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notice what this requires: the model name must be captured at generation time. The risk score must have been computed at insertion. Human review status must exist in &lt;code&gt;ReviewQueue&lt;/code&gt;. If any of these are missing, the certificate either fails or the &lt;code&gt;unknown_review_pass&lt;/code&gt; escape hatch applies — your choice, policy-level.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Cryptographic Layer
&lt;/h2&gt;

&lt;p&gt;When eligibility passes, the system builds a canonical attestation statement and signs it with Ed25519:&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="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;sign_attestation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;statement&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;SignedAttestation&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;private_key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;_load_private_key&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;canonical&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dumps&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;statement&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sort_keys&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;default&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;str&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="n"&gt;sig_bytes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;private_key&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sign&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;canonical&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;SignedAttestation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;statement&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;statement&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;signature&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;sig_bytes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;hex&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
        &lt;span class="n"&gt;public_key_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nf"&gt;_get_public_key_id&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;private_key&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;sort_keys=True&lt;/code&gt; ensures canonical key ordering regardless of Python dict insertion order — without this, semantically identical statements could produce different byte sequences and fail verification. The corresponding public key is available unauthenticated at &lt;code&gt;GET /attestations/{public_ref}/verify&lt;/code&gt;, so any third party can verify the certificate without workspace credentials.&lt;/p&gt;

&lt;p&gt;The attestation also includes &lt;code&gt;prev_hash&lt;/code&gt; — the &lt;code&gt;record_hash&lt;/code&gt; of the most recent hash-chained provenance record in the workspace. This anchors the certificate to the workspace's provenance history at the moment of issuance. You cannot retroactively alter the provenance records that supported it without breaking the chain.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Ineligibility Looks Like
&lt;/h2&gt;

&lt;p&gt;An ineligible evaluation is equally useful. The system issues an unsigned certificate with a structured reasons list:&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;"eligibility"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ineligible"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"reasons"&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="s2"&gt;"Record abc-123: risk score 84 exceeds policy maximum 70."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"Record def-456: model 'gpt-4o-mini' is not in the policy allowed-models list."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"Record ghi-789: human-review status is 'pending' — policy requires 'approved'."&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 is a machine-generated record of exactly which AI code insertions failed your policy and why — before the code shipped.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Connection to Capture Quality
&lt;/h2&gt;

&lt;p&gt;None of this works without the provenance capture layer. If a record has &lt;code&gt;model_name = null&lt;/code&gt; because the insertion went through a path LineageLens couldn't proxy, the model allowlist check cannot run. If &lt;code&gt;risk_score&lt;/code&gt; is null, the risk check cannot run. The certificate is only as strong as the capture underneath it.&lt;/p&gt;

&lt;p&gt;This is the compounding argument for installing early: every day of uncaptured AI code is a day of records that cannot support a certificate.&lt;/p&gt;

&lt;p&gt;LineageLens is open source and free to install. The indemnity endpoint is part of the Plus/Max tier backend. The deeper cryptographic design walkthrough covers why Ed25519 over HMAC, the key derivation fallback, and where the policy gate should actually live.&lt;/p&gt;

&lt;p&gt;What does your team produce as evidence when AI-generated code goes to production? And would it survive a structured audit?&lt;/p&gt;

</description>
      <category>ai</category>
      <category>security</category>
      <category>showdev</category>
      <category>news</category>
    </item>
    <item>
      <title>what do you think about it??</title>
      <dc:creator>Praveen</dc:creator>
      <pubDate>Sat, 13 Jun 2026 05:46:30 +0000</pubDate>
      <link>https://dev.to/pn_28428886923dfc665/what-do-you-think-about-it-31pb</link>
      <guid>https://dev.to/pn_28428886923dfc665/what-do-you-think-about-it-31pb</guid>
      <description>&lt;div class="ltag__link--embedded"&gt;
  &lt;div class="crayons-story "&gt;
  &lt;a href="https://dev.to/pn_28428886923dfc665/co-authored-by-copilot-is-not-an-audit-trail-heres-what-one-actually-looks-like-65a" class="crayons-story__hidden-navigation-link"&gt;"Co-authored-by: Copilot" Is Not an Audit Trail — Here's What One Actually Looks Like&lt;/a&gt;


  &lt;div class="crayons-story__body crayons-story__body-full_post"&gt;
    &lt;div class="crayons-story__top"&gt;
      &lt;div class="crayons-story__meta"&gt;
        &lt;div class="crayons-story__author-pic"&gt;

          &lt;a href="/pn_28428886923dfc665" class="crayons-avatar  crayons-avatar--l  "&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.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3940098%2F8a7a4942-5b0d-4847-9a7a-2eaf76d0ce30.png" alt="pn_28428886923dfc665 profile" class="crayons-avatar__image" width="96" height="96"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
        &lt;div&gt;
          &lt;div&gt;
            &lt;a href="/pn_28428886923dfc665" class="crayons-story__secondary fw-medium m:hidden"&gt;
              Praveen
            &lt;/a&gt;
            &lt;div class="profile-preview-card relative mb-4 s:mb-0 fw-medium hidden m:inline-block"&gt;
              
                Praveen
                
              
              &lt;div id="story-author-preview-content-3889306" class="profile-preview-card__content crayons-dropdown branded-7 p-4 pt-0"&gt;
                &lt;div class="gap-4 grid"&gt;
                  &lt;div class="-mt-4"&gt;
                    &lt;a href="/pn_28428886923dfc665" class="flex"&gt;
                      &lt;span class="crayons-avatar crayons-avatar--xl mr-2 shrink-0"&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.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3940098%2F8a7a4942-5b0d-4847-9a7a-2eaf76d0ce30.png" class="crayons-avatar__image" alt="" width="96" height="96"&gt;
                      &lt;/span&gt;
                      &lt;span class="crayons-link crayons-subtitle-2 mt-5"&gt;Praveen&lt;/span&gt;
                    &lt;/a&gt;
                  &lt;/div&gt;
                  &lt;div class="print-hidden"&gt;
                    
                      Follow
                    
                  &lt;/div&gt;
                  &lt;div class="author-preview-metadata-container"&gt;&lt;/div&gt;
                &lt;/div&gt;
              &lt;/div&gt;
            &lt;/div&gt;

          &lt;/div&gt;
          &lt;a href="https://dev.to/pn_28428886923dfc665/co-authored-by-copilot-is-not-an-audit-trail-heres-what-one-actually-looks-like-65a" class="crayons-story__tertiary fs-xs"&gt;&lt;time&gt;Jun 13&lt;/time&gt;&lt;span class="time-ago-indicator-initial-placeholder"&gt;&lt;/span&gt;&lt;/a&gt;
        &lt;/div&gt;
      &lt;/div&gt;

    &lt;/div&gt;

    &lt;div class="crayons-story__indention"&gt;
      &lt;h2 class="crayons-story__title crayons-story__title-full_post"&gt;
        &lt;a href="https://dev.to/pn_28428886923dfc665/co-authored-by-copilot-is-not-an-audit-trail-heres-what-one-actually-looks-like-65a" id="article-link-3889306"&gt;
          "Co-authored-by: Copilot" Is Not an Audit Trail — Here's What One Actually Looks Like
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;div class="crayons-story__tags"&gt;
            &lt;a class="crayons-tag crayons-tag--filled  " href="/t/discuss"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;discuss&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/ai"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;ai&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/programming"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;programming&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/security"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;security&lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="crayons-story__bottom"&gt;
        &lt;div class="crayons-story__details"&gt;
          &lt;a href="https://dev.to/pn_28428886923dfc665/co-authored-by-copilot-is-not-an-audit-trail-heres-what-one-actually-looks-like-65a" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left"&gt;
            &lt;div class="multiple_reactions_aggregate"&gt;
              &lt;span class="multiple_reactions_icons_container"&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/exploding-head-daceb38d627e6ae9b730f36a1e390fca556a4289d5a41abb2c35068ad3e2c4b5.svg" width="24" height="24"&gt;
                  &lt;/span&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/multi-unicorn-b44d6f8c23cdd00964192bedc38af3e82463978aa611b4365bd33a0f1f4f3e97.svg" width="24" height="24"&gt;
                  &lt;/span&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/sparkle-heart-5f9bee3767e18deb1bb725290cb151c25234768a0e9a2bd39370c382d02920cf.svg" width="24" height="24"&gt;
                  &lt;/span&gt;
              &lt;/span&gt;
              &lt;span class="aggregate_reactions_counter"&gt;6&lt;span class="hidden s:inline"&gt;&amp;nbsp;reactions&lt;/span&gt;&lt;/span&gt;
            &lt;/div&gt;
          &lt;/a&gt;
            &lt;a href="https://dev.to/pn_28428886923dfc665/co-authored-by-copilot-is-not-an-audit-trail-heres-what-one-actually-looks-like-65a#comments" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left flex items-center"&gt;
              

              3&lt;span class="hidden s:inline"&gt;&amp;nbsp;comments&lt;/span&gt;
            &lt;/a&gt;
        &lt;/div&gt;
        &lt;div class="crayons-story__save"&gt;
          &lt;small class="crayons-story__tertiary fs-xs mr-2"&gt;
            5 min read
          &lt;/small&gt;
            
              &lt;span class="bm-initial crayons-icon c-btn__icon"&gt;
                

              &lt;/span&gt;
              &lt;span class="bm-success crayons-icon c-btn__icon"&gt;
                

              &lt;/span&gt;
            
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;


</description>
    </item>
    <item>
      <title>"Co-authored-by: Copilot" Is Not an Audit Trail — Here's What One Actually Looks Like</title>
      <dc:creator>Praveen</dc:creator>
      <pubDate>Sat, 13 Jun 2026 05:45:34 +0000</pubDate>
      <link>https://dev.to/pn_28428886923dfc665/co-authored-by-copilot-is-not-an-audit-trail-heres-what-one-actually-looks-like-65a</link>
      <guid>https://dev.to/pn_28428886923dfc665/co-authored-by-copilot-is-not-an-audit-trail-heres-what-one-actually-looks-like-65a</guid>
      <description>&lt;p&gt;In late April 2026, Microsoft shipped VS Code 1.117. Buried in the release was a change: the &lt;code&gt;github.copilot.chat.generateCommitMessage.addCoAuthoring&lt;/code&gt; setting was flipped from &lt;code&gt;off&lt;/code&gt; to &lt;code&gt;all&lt;/code&gt; by default. That meant "Co-authored-by: Copilot &lt;a href="mailto:copilot@github.com"&gt;copilot@github.com&lt;/a&gt;" was now being appended to every commit message in the background — silently, without showing up in the commit message editor, and critically, without verifying that Copilot had generated any of the code.&lt;/p&gt;

&lt;p&gt;Developers noticed within days. The backlash was significant. VS Code 1.119 shipped May 3 with the default reverted and a consent requirement added. Microsoft apologized.&lt;/p&gt;

&lt;p&gt;The technical fix was straightforward. The governance question it exposed is not.&lt;/p&gt;

&lt;h3&gt;
  
  
  What the incident actually revealed
&lt;/h3&gt;

&lt;p&gt;The developer anger wasn't really about attribution credit. It was about consent and accuracy. The co-author trailer was added to commits where AI features were disabled. It was added when developers had manually written every line. It attributed work that wasn't done.&lt;/p&gt;

&lt;p&gt;But underneath that anger is a more important problem: even when Copilot does write code, a "Co-authored-by" git trailer tells you almost nothing useful from a governance or security standpoint.&lt;/p&gt;

&lt;p&gt;It tells you that a tool called Copilot existed somewhere in the developer's editor during some portion of the work that eventually became this commit. That's it.&lt;/p&gt;

&lt;p&gt;It doesn't tell you which model generated which lines. It doesn't tell you what the developer prompted for. It doesn't contain the raw model response. It doesn't tell you whether any of the AI-generated lines touched authentication paths, hardcoded credentials, or SQL construction. It says nothing about when generation happened relative to the commit. It carries no risk score.&lt;/p&gt;

&lt;p&gt;If you had to defend a specific commit in a security audit six months from now — "which parts of this function were AI-generated, under what prompt, using what model?" — a git trailer gets you nowhere.&lt;/p&gt;

&lt;h3&gt;
  
  
  What a real provenance record contains
&lt;/h3&gt;

&lt;p&gt;LineageLens captures provenance at insertion time, not commit time. Each AI code insertion generates a &lt;code&gt;ProviderAgnosticProvenanceEvent&lt;/code&gt; structured around schema version &lt;code&gt;lineagelens.provenance-event.v1&lt;/code&gt;. Here is what that record contains:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;ProviderAgnosticProvenanceEvent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;schemaVersion&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;lineagelens.provenance-event.v1&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;eventId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="nl"&gt;timestamps&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;observedAtIso&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;         &lt;span class="c1"&gt;// when the extension saw the insertion&lt;/span&gt;
    &lt;span class="nl"&gt;insertedAtIso&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;         &lt;span class="c1"&gt;// when the text hit the buffer&lt;/span&gt;
    &lt;span class="nl"&gt;requestAtIso&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;   &lt;span class="c1"&gt;// when the proxy saw the outbound request&lt;/span&gt;
    &lt;span class="nl"&gt;responseAtIso&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  &lt;span class="c1"&gt;// when the model responded&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;

  &lt;span class="nl"&gt;source&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;ide&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;           &lt;span class="c1"&gt;// 'vscode'&lt;/span&gt;
    &lt;span class="nl"&gt;shim&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;                 &lt;span class="c1"&gt;// which capture path fired&lt;/span&gt;
    &lt;span class="nl"&gt;toolName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;      &lt;span class="c1"&gt;// 'Edit', 'Write', 'apply_patch', etc.&lt;/span&gt;
    &lt;span class="nl"&gt;provider&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;      &lt;span class="c1"&gt;// 'anthropic', 'openai', 'google'&lt;/span&gt;
    &lt;span class="nl"&gt;adapterName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;   &lt;span class="c1"&gt;// 'claude-code', 'copilot', 'cursor', etc.&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;

  &lt;span class="nl"&gt;capture&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;level&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;CaptureStatus&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;         &lt;span class="c1"&gt;// 'full' | 'metadata_only' | 'tunnel_only' | 'file_diff'&lt;/span&gt;
    &lt;span class="nl"&gt;promptStatus&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;captured&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;not-captured&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;capabilities&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ProvenanceEventCapability&lt;/span&gt;&lt;span class="p"&gt;[];&lt;/span&gt;  &lt;span class="c1"&gt;// 10 named slots&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;

  &lt;span class="nl"&gt;model&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;unknown&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;parameters&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Record&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;unknown&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  &lt;span class="c1"&gt;// temperature, max_tokens, etc.&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;

  &lt;span class="nl"&gt;prompt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;unknown&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;    &lt;span class="c1"&gt;// the full prompt messages array&lt;/span&gt;
    &lt;span class="nl"&gt;system&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;unknown&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  &lt;span class="c1"&gt;// the system prompt&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;

  &lt;span class="nl"&gt;diff&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;insertedText&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;chunks&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ProvenanceInsertedChunk&lt;/span&gt;&lt;span class="p"&gt;[];&lt;/span&gt;
    &lt;span class="nl"&gt;netAddedLines&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;

  &lt;span class="nl"&gt;correlation&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;confidence&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;                     &lt;span class="c1"&gt;// 0.0–1.0&lt;/span&gt;
    &lt;span class="nl"&gt;timingDifferenceMs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;contentSimilarityScore&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;fileContextMatched&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;boolean&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Compare that to what a git trailer gives you: a tool name and an email address.&lt;/p&gt;

&lt;h3&gt;
  
  
  The 10 capability slots
&lt;/h3&gt;

&lt;p&gt;The most important part of the schema is the &lt;code&gt;capture.capabilities&lt;/code&gt; array. Every provenance event gets 10 named capability assessments:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="s"&gt;prompt-body       — was the full prompt captured?&lt;/span&gt;
&lt;span class="s"&gt;response-body     — was the raw model response captured?&lt;/span&gt;
&lt;span class="s"&gt;headers           — were the request headers available?&lt;/span&gt;
&lt;span class="s"&gt;request-id        — was a UUID present to link request to insertion?&lt;/span&gt;
&lt;span class="s"&gt;session-id        — was there session context?&lt;/span&gt;
&lt;span class="s"&gt;model             — was the model name captured?&lt;/span&gt;
&lt;span class="s"&gt;user-agent        — was the tool's user-agent available?&lt;/span&gt;
&lt;span class="s"&gt;file-diff         — was the inserted diff captured? (always 'provided')&lt;/span&gt;
&lt;span class="s"&gt;file-context      — did the file context match to the capture?&lt;/span&gt;
&lt;span class="s"&gt;workspace         — was workspace context available?&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each entry carries a status: &lt;code&gt;provided&lt;/code&gt;, &lt;code&gt;missing&lt;/code&gt;, or &lt;code&gt;unknown&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This matters because it tells you precisely what you know about a given insertion and what you don't. A record with &lt;code&gt;prompt-body: missing&lt;/code&gt; and &lt;code&gt;promptStatus: 'not-captured'&lt;/code&gt; is not the same as no record — it is an explicit declaration that the prompt gap exists. That gap is auditable. An audit trail with explicit gaps is categorically more useful than a label with no gaps declared.&lt;/p&gt;

&lt;p&gt;The VS Code co-author trailer has no gap declarations. It has no granularity at all — it just has nothing.&lt;/p&gt;

&lt;h3&gt;
  
  
  Capture time vs. commit time
&lt;/h3&gt;

&lt;p&gt;The harder architectural point: by the time you are in a git commit, you have already lost the evidence.&lt;/p&gt;

&lt;p&gt;The prompt body does not live anywhere post-generation. The model name was in the HTTP response header. The raw response body was discarded after the tool processed it. The timing data only exists in the milliseconds between request and file write.&lt;/p&gt;

&lt;p&gt;None of that is in the commit. None of it can be recovered retroactively.&lt;/p&gt;

&lt;p&gt;LineageLens captures the &lt;code&gt;ProviderAgnosticProvenanceEvent&lt;/code&gt; at the insertion event — before the diff even exists as a file change. The &lt;code&gt;observedAtIso&lt;/code&gt; timestamp records when the VS Code extension detected the text entering the buffer. The &lt;code&gt;requestAtIso&lt;/code&gt; and &lt;code&gt;responseAtIso&lt;/code&gt; timestamps come from the proxy intercept that happened seconds or minutes before. By the time you type a commit message, the provenance record has already been stored.&lt;/p&gt;

&lt;p&gt;A git trailer is a retroactive label. Provenance is an evidence chain that exists before the label does.&lt;/p&gt;

&lt;h3&gt;
  
  
  What actually changed after the VS Code incident
&lt;/h3&gt;

&lt;p&gt;Microsoft reverted the default. They added a consent gate. They clarified that &lt;code&gt;disableAIFeatures: true&lt;/code&gt; now also disables the co-authoring trailer.&lt;/p&gt;

&lt;p&gt;None of that gives you a provenance record. You still do not know which lines in a given commit were AI-generated. You still cannot answer "what did Copilot generate in auth.py last month" from git history alone.&lt;/p&gt;

&lt;p&gt;The incident forced consent around labeling. That is progress. It did not touch the underlying gap: labeling that something was AI-assisted is not the same as recording what the AI actually did.&lt;/p&gt;

&lt;h3&gt;
  
  
  The practical implication
&lt;/h3&gt;

&lt;p&gt;If you are on a team shipping AI-generated code — and 84% of development teams are, per the 2026 Stack Overflow Developer Survey — you are almost certainly making three implicit assumptions:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;That your CI pipeline or git history contains enough attribution information to answer an audit question.&lt;/li&gt;
&lt;li&gt;That "Co-authored-by" or an equivalent label satisfies your traceability obligations.&lt;/li&gt;
&lt;li&gt;That you could reconstruct the provenance of a specific function if you had to.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;All three assumptions are likely wrong for the same reason: commit-time labeling cannot carry insertion-time evidence.&lt;/p&gt;

&lt;p&gt;The EU AI Act Articles 11 and 12 enforcement window opens in August 2026. The question "which AI model generated this code, under what prompt, at what risk level?" is going to become a routine compliance requirement.&lt;/p&gt;

&lt;p&gt;When it does, a git trailer is not going to be a defensible answer.&lt;/p&gt;

&lt;h3&gt;
  
  
  Try it
&lt;/h3&gt;

&lt;p&gt;LineageLens Base is a free VS Code extension that starts capturing provenance events at insertion time today, even without proxy infrastructure. Lite, Plus, and Max tiers add proxy capture for full prompt, model, and response-body fields. The full architecture details are at lineage-website.vercel.app. The Hashnode post goes deeper on schema design tradeoffs.&lt;/p&gt;

&lt;p&gt;One question for the comments: &lt;strong&gt;what data would your team actually need to survive a security audit of your AI-generated code?&lt;/strong&gt; Not in theory — what specific fields would an auditor ask for?&lt;/p&gt;

</description>
      <category>ai</category>
      <category>programming</category>
      <category>security</category>
      <category>discuss</category>
    </item>
    <item>
      <title>what's yoiur opinion</title>
      <dc:creator>Praveen</dc:creator>
      <pubDate>Fri, 12 Jun 2026 09:19:33 +0000</pubDate>
      <link>https://dev.to/pn_28428886923dfc665/whats-yoiur-opinion-21li</link>
      <guid>https://dev.to/pn_28428886923dfc665/whats-yoiur-opinion-21li</guid>
      <description>&lt;div class="ltag__link--embedded"&gt;
  &lt;div class="crayons-story "&gt;
  &lt;a href="https://dev.to/pn_28428886923dfc665/why-your-ai-capture-store-needs-two-security-layers-not-one-271l" class="crayons-story__hidden-navigation-link"&gt;Why Your AI Capture Store Needs Two Security Layers (Not One)&lt;/a&gt;


  &lt;div class="crayons-story__body crayons-story__body-full_post"&gt;
    &lt;div class="crayons-story__top"&gt;
      &lt;div class="crayons-story__meta"&gt;
        &lt;div class="crayons-story__author-pic"&gt;

          &lt;a href="/pn_28428886923dfc665" class="crayons-avatar  crayons-avatar--l  "&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.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3940098%2F8a7a4942-5b0d-4847-9a7a-2eaf76d0ce30.png" alt="pn_28428886923dfc665 profile" class="crayons-avatar__image" width="96" height="96"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
        &lt;div&gt;
          &lt;div&gt;
            &lt;a href="/pn_28428886923dfc665" class="crayons-story__secondary fw-medium m:hidden"&gt;
              Praveen
            &lt;/a&gt;
            &lt;div class="profile-preview-card relative mb-4 s:mb-0 fw-medium hidden m:inline-block"&gt;
              
                Praveen
                
              
              &lt;div id="story-author-preview-content-3881107" class="profile-preview-card__content crayons-dropdown branded-7 p-4 pt-0"&gt;
                &lt;div class="gap-4 grid"&gt;
                  &lt;div class="-mt-4"&gt;
                    &lt;a href="/pn_28428886923dfc665" class="flex"&gt;
                      &lt;span class="crayons-avatar crayons-avatar--xl mr-2 shrink-0"&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.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3940098%2F8a7a4942-5b0d-4847-9a7a-2eaf76d0ce30.png" class="crayons-avatar__image" alt="" width="96" height="96"&gt;
                      &lt;/span&gt;
                      &lt;span class="crayons-link crayons-subtitle-2 mt-5"&gt;Praveen&lt;/span&gt;
                    &lt;/a&gt;
                  &lt;/div&gt;
                  &lt;div class="print-hidden"&gt;
                    
                      Follow
                    
                  &lt;/div&gt;
                  &lt;div class="author-preview-metadata-container"&gt;&lt;/div&gt;
                &lt;/div&gt;
              &lt;/div&gt;
            &lt;/div&gt;

          &lt;/div&gt;
          &lt;a href="https://dev.to/pn_28428886923dfc665/why-your-ai-capture-store-needs-two-security-layers-not-one-271l" class="crayons-story__tertiary fs-xs"&gt;&lt;time&gt;Jun 12&lt;/time&gt;&lt;span class="time-ago-indicator-initial-placeholder"&gt;&lt;/span&gt;&lt;/a&gt;
        &lt;/div&gt;
      &lt;/div&gt;

    &lt;/div&gt;

    &lt;div class="crayons-story__indention"&gt;
      &lt;h2 class="crayons-story__title crayons-story__title-full_post"&gt;
        &lt;a href="https://dev.to/pn_28428886923dfc665/why-your-ai-capture-store-needs-two-security-layers-not-one-271l" id="article-link-3881107"&gt;
          Why Your AI Capture Store Needs Two Security Layers (Not One)
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;div class="crayons-story__tags"&gt;
            &lt;a class="crayons-tag crayons-tag--filled  " href="/t/discuss"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;discuss&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/showdev"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;showdev&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/news"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;news&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/startup"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;startup&lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="crayons-story__bottom"&gt;
        &lt;div class="crayons-story__details"&gt;
          &lt;a href="https://dev.to/pn_28428886923dfc665/why-your-ai-capture-store-needs-two-security-layers-not-one-271l" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left"&gt;
            &lt;div class="multiple_reactions_aggregate"&gt;
              &lt;span class="multiple_reactions_icons_container"&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/sparkle-heart-5f9bee3767e18deb1bb725290cb151c25234768a0e9a2bd39370c382d02920cf.svg" width="24" height="24"&gt;
                  &lt;/span&gt;
              &lt;/span&gt;
              &lt;span class="aggregate_reactions_counter"&gt;1&lt;span class="hidden s:inline"&gt;&amp;nbsp;reaction&lt;/span&gt;&lt;/span&gt;
            &lt;/div&gt;
          &lt;/a&gt;
            &lt;a href="https://dev.to/pn_28428886923dfc665/why-your-ai-capture-store-needs-two-security-layers-not-one-271l#comments" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left flex items-center"&gt;
              

              1&lt;span class="hidden s:inline"&gt;&amp;nbsp;comment&lt;/span&gt;
            &lt;/a&gt;
        &lt;/div&gt;
        &lt;div class="crayons-story__save"&gt;
          &lt;small class="crayons-story__tertiary fs-xs mr-2"&gt;
            7 min read
          &lt;/small&gt;
            
              &lt;span class="bm-initial crayons-icon c-btn__icon"&gt;
                

              &lt;/span&gt;
              &lt;span class="bm-success crayons-icon c-btn__icon"&gt;
                

              &lt;/span&gt;
            
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;


</description>
    </item>
    <item>
      <title>Why Your AI Capture Store Needs Two Security Layers (Not One)</title>
      <dc:creator>Praveen</dc:creator>
      <pubDate>Fri, 12 Jun 2026 09:17:35 +0000</pubDate>
      <link>https://dev.to/pn_28428886923dfc665/why-your-ai-capture-store-needs-two-security-layers-not-one-271l</link>
      <guid>https://dev.to/pn_28428886923dfc665/why-your-ai-capture-store-needs-two-security-layers-not-one-271l</guid>
      <description>&lt;p&gt;Liquid syntax error: Unknown tag 'lineage'&lt;/p&gt;
</description>
      <category>discuss</category>
      <category>showdev</category>
      <category>news</category>
      <category>startup</category>
    </item>
    <item>
      <title>You Cannot Retroactively Capture AI Code Provenance. Here Is What You Lose Every Day You Wait.</title>
      <dc:creator>Praveen</dc:creator>
      <pubDate>Wed, 10 Jun 2026 07:05:15 +0000</pubDate>
      <link>https://dev.to/pn_28428886923dfc665/you-cannot-retroactively-capture-ai-code-provenance-here-is-what-you-lose-every-day-you-wait-5gmc</link>
      <guid>https://dev.to/pn_28428886923dfc665/you-cannot-retroactively-capture-ai-code-provenance-here-is-what-you-lose-every-day-you-wait-5gmc</guid>
      <description>&lt;p&gt;There is a failure mode in AI code governance that does not get enough attention because it is invisible until it isn't.&lt;/p&gt;

&lt;p&gt;It is not a vulnerability. It is not a misconfiguration. It is not something a security scan will catch.&lt;/p&gt;

&lt;p&gt;It is a gap in time: the period between "your team started using AI coding tools" and "your team started recording what those tools did." Every line of&lt;br&gt;
  code generated in that gap is permanently unattributable. The prompt is gone. The model is gone. Whether the suggestion was reviewed, modified, or&lt;br&gt;
  auto-accepted is gone.&lt;/p&gt;

&lt;p&gt;You cannot go back. Retroactive provenance capture does not exist.&lt;/p&gt;




&lt;p&gt;## The One-Way Door&lt;/p&gt;

&lt;p&gt;When a developer prompts Claude Code and accepts a suggestion, the following information exists briefly in memory and in transit:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The full prompt sent to the model&lt;/li&gt;
&lt;li&gt;The model identifier and version&lt;/li&gt;
&lt;li&gt;The generated code, before any human edits&lt;/li&gt;
&lt;li&gt;Whether the insertion was accepted, rejected, or applied with modifications&lt;/li&gt;
&lt;li&gt;The file path and surrounding context at the time of generation&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;At the moment the developer's editor applies the change and moves on, most of that information disappears. Git records the diff and the commit author.&lt;br&gt;
  Nothing else records provenance by default.&lt;/p&gt;

&lt;p&gt;The commit is not the generation event. These happen at different times with different context. Understanding this distinction is the precondition for any&lt;br&gt;
  serious AI governance posture.&lt;/p&gt;

&lt;p&gt;If you were not capturing at the moment of generation, you were not capturing. There is no reconstruct operation.&lt;/p&gt;




&lt;p&gt;## What You Actually Lose&lt;/p&gt;

&lt;p&gt;Let's be specific about what "unattributable" means in practice.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Scenario 1: The incident trace.&lt;/strong&gt;&lt;br&gt;
  A bug surfaces in src/payments/processor.py. You trace it to a block inserted six weeks ago. Git blame gives you a developer name and a commit hash. What&lt;br&gt;
  you cannot recover: the prompt that produced the block, the model that generated it, whether the developer reviewed it or accepted it from the first&lt;br&gt;
  suggestion, and what the risk patterns in that insertion looked like at generation time. You are debugging code whose origin is opaque.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Scenario 2: The compliance question.&lt;/strong&gt;&lt;br&gt;
  Your company is asked — by an enterprise customer, an auditor, or your own legal team — to document AI usage in the SDLC. The EU AI Act Articles 11, 12,&lt;br&gt;
  and 14 have enforcement teeth from August 2026. The question is: which code was AI-generated, which model produced it, and what review occurred?&lt;/p&gt;

&lt;p&gt;If you have been capturing since January, you have a full audit trail. If you started capturing this week because someone asked the question, you have a&lt;br&gt;
  full audit trail from this week forward and nothing before that.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Scenario 3: The departing engineer.&lt;/strong&gt;&lt;br&gt;
  A developer who used AI tools heavily leaves the team. Their code is in the codebase, some of it AI-generated. The team has no record of which blocks were&lt;br&gt;
  AI-generated, what was prompted, or what the risk posture of those blocks is. Onboarding the next developer is a code archaeology project that cannot be&lt;br&gt;
  fully resolved.&lt;/p&gt;




&lt;p&gt;## What Starting Looks Like — Zero Configuration Required&lt;/p&gt;

&lt;p&gt;The reason the irreversibility argument matters so much is that the cost of starting is zero.&lt;/p&gt;

&lt;p&gt;LineageLens Base installs in one command and starts capturing immediately:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;code --install-extension karnatipraveen.lineagelens-base&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;No backend. No proxy. No account. No configuration. No API key.&lt;/p&gt;

&lt;p&gt;The extension activates the moment it installs. It hooks into VS Code's onDidChangeTextDocument event and watches for insertions of 4 or more lines. When&lt;br&gt;
  a qualifying insertion occurs, it captures:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;File path and language&lt;/li&gt;
&lt;li&gt;Inserted code block&lt;/li&gt;
&lt;li&gt;Net lines added&lt;/li&gt;
&lt;li&gt;Confidence score (0.0–1.0)&lt;/li&gt;
&lt;li&gt;Source classification (cursor, copilot, unknown, etc.)&lt;/li&gt;
&lt;li&gt;UTC timestamp&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Records are stored in VS Code global state — a local JSON store on the developer's machine. No data leaves the machine. Status bar shows LL: Easy (local).&lt;/p&gt;

&lt;p&gt;From this moment forward, every AI insertion of 4+ lines has a record. The record is sparse (no prompt, no model name — those require the proxy) but it&lt;br&gt;
  exists, and its timestamp is authoritative.&lt;/p&gt;




&lt;p&gt;## Upgrading Record Quality Without Reinstalling&lt;/p&gt;

&lt;p&gt;When you want full prompt and model capture, add the Lite proxy alongside it:&lt;/p&gt;

&lt;p&gt;git clone &lt;a href="https://github.com/karnati-praveen/lineagelens" rel="noopener noreferrer"&gt;https://github.com/karnati-praveen/lineagelens&lt;/a&gt;&lt;br&gt;
  cd lineagelens&lt;br&gt;
  bash lineagelens-scripts/quickstart-lite.sh&lt;/p&gt;

&lt;p&gt;Open &lt;a href="http://localhost:8787/setup" rel="noopener noreferrer"&gt;http://localhost:8787/setup&lt;/a&gt;, create your admin account in three browser steps, then set one environment variable:&lt;/p&gt;

&lt;p&gt;export ANTHROPIC_BASE_URL=&lt;a href="http://localhost:8788" rel="noopener noreferrer"&gt;http://localhost:8788&lt;/a&gt;&lt;br&gt;
  export OPENAI_BASE_URL=&lt;a href="http://localhost:8788" rel="noopener noreferrer"&gt;http://localhost:8788&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The extension polls /proxy-health every 30 seconds. When it detects the proxy, the status bar switches from LL: Easy (local) to LL: Power automatically.&lt;br&gt;
  Records captured after that point include the full prompt, model identifier, and applied/rejected status.&lt;/p&gt;

&lt;p&gt;Records captured in Easy Mode before the proxy was running remain in the store with capture_status: file_diff and confidence ~0.35. They are not&lt;br&gt;
  retroactively enriched. But they exist. That is the point.&lt;/p&gt;




&lt;p&gt;## The Confidence Gradient&lt;/p&gt;

&lt;p&gt;Here is what record quality looks like across capture configurations:&lt;/p&gt;

&lt;p&gt;Capture mode         capture_status    Confidence     Prompt captured?&lt;br&gt;
  ---Proxy (Power Mode)   full              0.80 – 1.00    Yes&lt;br&gt;
  Proxy (tunneled)     tunnel_only       0.60 – 0.80    Partial&lt;br&gt;
  Extension only       file_diff         0.25 – 0.45    No&lt;br&gt;
  No capture           —                 —              —&lt;/p&gt;

&lt;p&gt;A file_diff record at confidence 0.35 is not a rich provenance record. But it is infinitely better than no record for a specific reason: it is timestamped&lt;br&gt;
  and file-attributed at generation time, not commit time.&lt;/p&gt;

&lt;p&gt;When an incident occurs six months later and you are tracing a bug to a specific block, a file_diff record tells you approximately when this code appeared&lt;br&gt;
  in the file, that it passed the threshold for "likely AI-generated," which AI tool extension was probably active, and what the file path was. That is&lt;br&gt;
  enough to narrow a 90-day window to a specific week. Without the record, the investigation starts from zero.&lt;/p&gt;




&lt;p&gt;## For Teams: The Asymmetry Compounds&lt;/p&gt;

&lt;p&gt;If your team has ten developers using a mix of Cursor, Copilot, and Claude Code, and none of them are running LineageLens, you have a growing body of&lt;br&gt;
  unattributable AI-generated code accumulating daily. Every sprint without capture is a sprint of records that cannot be recovered.&lt;/p&gt;

&lt;p&gt;LineageLens Lite adds a shared backend without requiring Postgres:&lt;/p&gt;

&lt;p&gt;bash lineagelens-scripts/quickstart-lite.sh&lt;/p&gt;

&lt;p&gt;Single Docker container. SQLite. Runs on a $5 VPS or a spare machine. The setup wizard creates the admin account and workspace in three browser steps.&lt;br&gt;
  Share the proxy URL and one environment variable with the team. From that point forward, every developer's AI tool traffic is captured and stored&lt;br&gt;
  centrally.&lt;/p&gt;




&lt;p&gt;## The Direct Argument&lt;/p&gt;

&lt;p&gt;LineageLens might be the right tool for your team or it might not be. That is worth evaluating. But the evaluation should happen today, not when you&lt;br&gt;
decide you need it — because the cost of deciding you need it after the fact is permanent.&lt;br&gt;
Base is free.Both are MIT-licensed and fully self-hosted.&lt;br&gt;
The cost of starting is 30 seconds and one command:&lt;/p&gt;

&lt;p&gt;*&lt;em&gt;code --install-extension karnatipraveen.lineagelens-base&lt;br&gt;
*&lt;/em&gt;&lt;br&gt;
  After you try it: what is the oldest piece of AI-generated code in your codebase that you cannot explain the origin of? How far back does your&lt;br&gt;
  unattributable window go?&lt;/p&gt;

</description>
      <category>ai</category>
      <category>discuss</category>
      <category>showdev</category>
      <category>startup</category>
    </item>
  </channel>
</rss>
