<?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: Yudeeswaran V </title>
    <description>The latest articles on DEV Community by Yudeeswaran V  (@yudeeswaran).</description>
    <link>https://dev.to/yudeeswaran</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%2F4153004%2Fba6731ba-73c8-4156-b1fe-ac6fd45f0ef7.jpg</url>
      <title>DEV Community: Yudeeswaran V </title>
      <link>https://dev.to/yudeeswaran</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/yudeeswaran"/>
    <language>en</language>
    <item>
      <title>I Built a Claude Code Skill That Makes AI Agents Prove Their Answers</title>
      <dc:creator>Yudeeswaran V </dc:creator>
      <pubDate>Fri, 02 Oct 2026 14:39:32 +0000</pubDate>
      <link>https://dev.to/yudeeswaran/i-built-a-claude-code-skill-that-makes-ai-agents-prove-their-answers-5f12</link>
      <guid>https://dev.to/yudeeswaran/i-built-a-claude-code-skill-that-makes-ai-agents-prove-their-answers-5f12</guid>
      <description>&lt;p&gt;AI coding agents are getting very good at navigating repositories.&lt;/p&gt;

&lt;p&gt;Ask an agent:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Where is the API timeout configured?"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It will usually find something useful.&lt;/p&gt;

&lt;p&gt;Ask:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Is the API timeout 60 seconds?"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It will probably search the codebase, find a &lt;code&gt;60&lt;/code&gt;, find a timeout configuration, and give you an answer.&lt;/p&gt;

&lt;p&gt;The harder question is:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;How do we know the answer is actually supported by the repository?&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That distinction is what led me to build &lt;strong&gt;Repo X-Ray&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Repo X-Ray is a &lt;strong&gt;Claude Code skill for evidence-grounded repository analysis&lt;/strong&gt;. It checks claims against concrete repository evidence instead of treating the agent's interpretation as the evidence itself.&lt;/p&gt;

&lt;p&gt;The underlying engine and evidence protocol are not tied to Claude. The same approach can be adapted to other coding agents and IDE environments.&lt;/p&gt;

&lt;p&gt;GitHub: &lt;a href="https://github.com/Yudeeswaran/repo-xray" rel="noopener noreferrer"&gt;https://github.com/Yudeeswaran/repo-xray&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  The problem isn't finding code
&lt;/h2&gt;

&lt;p&gt;One of the things coding agents are already good at is finding relevant code.&lt;/p&gt;

&lt;p&gt;The problem starts after the search.&lt;/p&gt;

&lt;p&gt;Suppose a repository contains:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docs/api.md
    timeout: 60 seconds

src/config.py
    API_TIMEOUT = 30

tests/test_api.py
    assert timeout == 30
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;An agent can find all three.&lt;/p&gt;

&lt;p&gt;But what should the answer be?&lt;/p&gt;

&lt;p&gt;The repository doesn't have one single source of truth.&lt;/p&gt;

&lt;p&gt;There are at least three different statements:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the documentation says 60 seconds&lt;/li&gt;
&lt;li&gt;the implementation says 30 seconds&lt;/li&gt;
&lt;li&gt;the tests expect 30 seconds&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A normal repository search gives you the files.&lt;/p&gt;

&lt;p&gt;It doesn't necessarily give you the contradiction.&lt;/p&gt;

&lt;p&gt;And this becomes more important when the repository also contains Git history, deployment configuration, pull requests, issues, and old implementations.&lt;/p&gt;

&lt;p&gt;The question changes from:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Can the agent find something relevant?"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;to:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;"Can the agent distinguish evidence from interpretation?"&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That's the problem I wanted to work on.&lt;/p&gt;




&lt;h2&gt;
  
  
  Repo X-Ray
&lt;/h2&gt;

&lt;p&gt;Repo X-Ray turns repository analysis into an evidence workflow.&lt;/p&gt;

&lt;p&gt;The basic pipeline is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Requirement / Claim
        │
        ▼
   Scope + Plan
        │
        ▼
Deterministic discovery
        │
        ├── source code
        ├── configuration
        ├── tests
        ├── documentation
        ├── Git history
        └── GitHub
        │
        ▼
   Evidence ledger
        │
        ▼
 Targeted reasoning
        │
        ▼
Deterministic validation
        │
        ▼
VERIFIED / CONTRADICTED /
UNVERIFIED / UNCHECKABLE
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The important part is the ordering.&lt;/p&gt;

&lt;p&gt;I don't want the model to invent an explanation first and then search for something that supports it.&lt;/p&gt;

&lt;p&gt;The repository gets searched first.&lt;/p&gt;

&lt;p&gt;Reasoning happens over the collected evidence.&lt;/p&gt;

&lt;p&gt;Then the result is validated against that evidence.&lt;/p&gt;




&lt;h2&gt;
  
  
  Four outcomes instead of "probably"
&lt;/h2&gt;

&lt;p&gt;Repo X-Ray uses four classifications:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Classification&lt;/th&gt;
&lt;th&gt;Meaning&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;VERIFIED&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Evidence supports the claim within the recorded scope&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;CONTRADICTED&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Evidence contains incompatible behavior, values, scope, or semantics&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;UNVERIFIED&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The claim wasn't established within the searched scope&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;UNCHECKABLE&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Verification requires unavailable runtime, external, private, or operational evidence&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;There is intentionally no &lt;code&gt;ASSUMED&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;That was an important design decision.&lt;/p&gt;

&lt;p&gt;An agent saying:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"I couldn't find evidence of this."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;is not the same thing as:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"This does not exist."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If I search only:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;src/orders/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and don't find something, I can say:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;No matching evidence was found within &lt;code&gt;src/orders/&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I cannot honestly conclude:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The repository doesn't implement it.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That distinction sounds small, but it matters a lot when an agent is being used for engineering decisions.&lt;/p&gt;




&lt;h2&gt;
  
  
  The evidence ledger
&lt;/h2&gt;

&lt;p&gt;The central artifact is an evidence ledger.&lt;/p&gt;

&lt;p&gt;Instead of returning only prose, Repo X-Ray records structured information about each claim.&lt;/p&gt;

&lt;p&gt;A simplified example:&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;"claim"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"API timeout is 60 seconds"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"classification"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"CONTRADICTED"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"search_scope"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"paths"&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;"src/"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="s2"&gt;"tests/"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="s2"&gt;"docs/"&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;"patterns"&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;"timeout"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="s2"&gt;"60"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="s2"&gt;"30"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"risk"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"HIGH"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"confidence"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;0.95&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 actual ledger also preserves evidence and provenance.&lt;/p&gt;

&lt;p&gt;For repository evidence, that means things like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;src/config.py:18
tests/test_api.py:91
docs/api.md:42
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For Git:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;commit abc123
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For GitHub:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;issue #118
PR #201
review/comment reference
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This matters because an agent's answer should be reproducible.&lt;/p&gt;

&lt;p&gt;If someone asks:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Why did you say this was contradicted?"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;there should be an answer other than:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"The model thought so."&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h1&gt;
  
  
  Documentation isn't automatically truth
&lt;/h1&gt;

&lt;p&gt;This is one of the more interesting parts of the system.&lt;/p&gt;

&lt;p&gt;Suppose the documentation says:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Maximum upload size: 25 MB
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;but the application contains:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;MAX_BODY_BYTES&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;1024&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;1024&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and the tests enforce 10 MB.&lt;/p&gt;

&lt;p&gt;The correct result isn't to blindly trust the documentation.&lt;/p&gt;

&lt;p&gt;It's also not to blindly trust the implementation and throw the documentation away.&lt;/p&gt;

&lt;p&gt;The useful result is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;CONTRADICTED

Documentation:
    25 MB

Runtime:
    10 MB

Tests:
    10 MB
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The sources are preserved separately.&lt;/p&gt;

&lt;p&gt;That lets the engineer decide what the intended behavior actually should be.&lt;/p&gt;

&lt;p&gt;Git history can add another layer.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;60 seconds introduced
        ↓
implementation changed
        ↓
change reverted
        ↓
documentation still says 60 seconds
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now the contradiction has a historical explanation.&lt;/p&gt;




&lt;h1&gt;
  
  
  GitHub discussions are evidence, not truth
&lt;/h1&gt;

&lt;p&gt;Repo X-Ray can also use GitHub issues and pull requests when available.&lt;/p&gt;

&lt;p&gt;But an issue saying:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"We support 25 MB uploads"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;doesn't prove that the running code supports 25 MB.&lt;/p&gt;

&lt;p&gt;It proves that somebody wrote that statement in an issue.&lt;/p&gt;

&lt;p&gt;That's still useful evidence.&lt;/p&gt;

&lt;p&gt;But it has different provenance.&lt;/p&gt;

&lt;p&gt;I therefore treat sources differently rather than flattening everything into one giant context window.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Current code/config/tests
        ↓
implementation evidence

Merged commits
        ↓
historical implementation evidence

Documentation
        ↓
declared behavior

Issues / PRs / reviews
        ↓
engineering discussion

External runtime behavior
        ↓
requires reproduction
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is particularly useful when a repository has accumulated years of documentation and implementation changes.&lt;/p&gt;




&lt;h1&gt;
  
  
  The uncomfortable case: UNVERIFIED
&lt;/h1&gt;

&lt;p&gt;One of the things I deliberately wanted to avoid was fake certainty.&lt;/p&gt;

&lt;p&gt;Imagine an issue says:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Requests larger than 10 MB cause the service to run out of memory."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Static repository analysis may find the 10 MB limit.&lt;/p&gt;

&lt;p&gt;It may find tests.&lt;/p&gt;

&lt;p&gt;It may find configuration.&lt;/p&gt;

&lt;p&gt;But it cannot necessarily prove the runtime memory behavior.&lt;/p&gt;

&lt;p&gt;That should become:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;UNVERIFIED
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;with something like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;No matching runtime evidence found within the searched scope.

Searched:
- source files
- configuration
- tests
- Git history
- GitHub issue references

Runtime memory behavior was not reproducibly established.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the evidence genuinely requires a production runtime, external service, or private system, the result can instead be:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;UNCHECKABLE
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That is preferable to inventing a conclusion.&lt;/p&gt;




&lt;h1&gt;
  
  
  Requirement changes are another problem
&lt;/h1&gt;

&lt;p&gt;I didn't want Repo X-Ray to stop at:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"The repository is inconsistent."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;A more practical question is:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"What happens if the requirement changes?"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Suppose the requirement changes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;10 MB → 50 MB
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A naive search might find every occurrence of:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;10
MB
50
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That produces a lot of noise.&lt;/p&gt;

&lt;p&gt;Instead, Repo X-Ray tries to follow the requirement through concrete repository concepts:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Requirement
    │
    ├── identifiers
    ├── numeric encodings
    ├── configuration
    ├── environment variables
    ├── validation
    ├── application code
    ├── tests
    ├── deployment
    ├── documentation
    ├── Git history
    └── GitHub discussion
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The output distinguishes between two important things.&lt;/p&gt;

&lt;h3&gt;
  
  
  Direct value
&lt;/h3&gt;

&lt;p&gt;The requested value or a concrete encoding of it is actually present.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;MAX_BODY_BYTES&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;1024&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;1024&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's strong evidence.&lt;/p&gt;

&lt;h3&gt;
  
  
  Semantic anchor
&lt;/h3&gt;

&lt;p&gt;A location is strongly related to the requirement, but the exact control point isn't proven.&lt;/p&gt;

&lt;p&gt;For example:&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;validate_request_body&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="bp"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This function is clearly relevant to request-size validation, but static evidence alone doesn't prove that this is where the 10 MB limit originates.&lt;/p&gt;

&lt;p&gt;That distinction is important.&lt;/p&gt;

&lt;p&gt;I don't want a tool that says:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"I found 17 places that must be changed."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;when what it really found is:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"I found 17 places that might be related."&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h1&gt;
  
  
  Mutation is read-only
&lt;/h1&gt;

&lt;p&gt;Another deliberate decision: Repo X-Ray doesn't automatically rewrite the repository when analyzing a requirement change.&lt;/p&gt;

&lt;p&gt;Mutation analysis answers:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"What evidence suggests would be affected?"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It does not mean:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Go modify all of these files."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;For a change such as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;30 seconds → 60 seconds
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;the system can trace:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;runtime configuration
tests
documentation
deployment configuration
Git history
related GitHub discussions
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and classify the resulting impact.&lt;/p&gt;

&lt;p&gt;This makes mutation analysis useful before an engineer or coding agent actually makes the change.&lt;/p&gt;




&lt;h1&gt;
  
  
  Risk is separate from truth
&lt;/h1&gt;

&lt;p&gt;Another thing I didn't want to collapse into one number is truth and risk.&lt;/p&gt;

&lt;p&gt;A claim can be:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;VERIFIED
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and still be high risk.&lt;/p&gt;

&lt;p&gt;For example:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Production authentication is enabled."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That might be completely supported by the repository.&lt;/p&gt;

&lt;p&gt;But if there is a separate internal route that bypasses authentication, the security implications may still be significant.&lt;/p&gt;

&lt;p&gt;Likewise, a small numerical change isn't automatically low risk.&lt;/p&gt;

&lt;p&gt;Risk considers things such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;severity&lt;/li&gt;
&lt;li&gt;blast radius&lt;/li&gt;
&lt;li&gt;production relevance&lt;/li&gt;
&lt;li&gt;security implications&lt;/li&gt;
&lt;li&gt;configuration impact&lt;/li&gt;
&lt;li&gt;test coverage&lt;/li&gt;
&lt;li&gt;confidence&lt;/li&gt;
&lt;li&gt;dependency surface&lt;/li&gt;
&lt;li&gt;semantic scope&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So classification answers:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;What does the evidence say?&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Risk answers:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;How much should we care about it?&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h1&gt;
  
  
  Why deterministic first?
&lt;/h1&gt;

&lt;p&gt;This is probably the most important architectural decision in the project.&lt;/p&gt;

&lt;p&gt;It's tempting to solve repository analysis by launching many agents:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;repository
    │
    ├── agent 1
    ├── agent 2
    ├── agent 3
    ├── agent 4
    ├── ...
    └── agent N
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That can work.&lt;/p&gt;

&lt;p&gt;It can also become expensive very quickly.&lt;/p&gt;

&lt;p&gt;More importantly, adding more agents doesn't automatically give you better evidence.&lt;/p&gt;

&lt;p&gt;Repo X-Ray instead starts with deterministic work:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;files
patterns
identifiers
values
Git
GitHub
        ↓
concrete evidence
        ↓
reasoning
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The model is useful for interpreting relationships and ambiguous cases.&lt;/p&gt;

&lt;p&gt;It shouldn't have to rediscover the same repository facts independently.&lt;/p&gt;




&lt;h1&gt;
  
  
  Cost is part of the design
&lt;/h1&gt;

&lt;p&gt;This came from another problem I ran into while looking at agent-based workflows.&lt;/p&gt;

&lt;p&gt;If every request turns into a large number of sub-agent calls, the system can become expensive before the user realizes what is happening.&lt;/p&gt;

&lt;p&gt;Repo X-Ray therefore has a planner.&lt;/p&gt;

&lt;p&gt;Before expensive work, it can estimate:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Files in scope
Claims
Evidence items
Git lookups
GitHub lookups
Expected operations
Reasoning calls
Token range
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The intended execution model is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;cheap discovery
      ↓
find ambiguity
      ↓
escalate selectively
      ↓
reason
      ↓
validate
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Not:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;launch 50 agents
      ↓
hope one of them finds the answer
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h1&gt;
  
  
  I tested it against deliberately inconsistent repositories
&lt;/h1&gt;

&lt;p&gt;I didn't want the benchmark to be:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"I ran it on my own repository and it looked good."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;So I built adversarial fixtures containing contradictions such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;documentation says 25 MB, runtime says 10 MB&lt;/li&gt;
&lt;li&gt;documentation says 60-second timeout, runtime says 30 seconds&lt;/li&gt;
&lt;li&gt;retry policy differs between configuration and API behavior&lt;/li&gt;
&lt;li&gt;documentation says idempotency is optional, implementation requires it&lt;/li&gt;
&lt;li&gt;authentication is described as universal while a specific internal route bypasses it&lt;/li&gt;
&lt;li&gt;queue visibility timeout differs from the worker configuration&lt;/li&gt;
&lt;li&gt;PII is documented as being redacted before persistence while code persists the raw object first&lt;/li&gt;
&lt;li&gt;rate limits differ by scope&lt;/li&gt;
&lt;li&gt;documentation describes inventory reservation as atomic while implementation performs separate operations&lt;/li&gt;
&lt;li&gt;currency precision differs between documentation and storage behavior&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The benchmark also included decoy information.&lt;/p&gt;

&lt;p&gt;For example, an architectural document saying that some internal endpoints &lt;em&gt;may&lt;/em&gt; bypass authentication should not cause the system to claim that every internal endpoint is insecure.&lt;/p&gt;

&lt;p&gt;That distinction matters.&lt;/p&gt;

&lt;p&gt;The final adversarial benchmark detected all 10 expected contradiction domains.&lt;/p&gt;

&lt;p&gt;I also tested mutation analysis across several public-source stacks, including Flask, FastAPI, Django, Kubernetes, and Go.&lt;/p&gt;

&lt;p&gt;Those tests are useful as generalization checks, but they obviously aren't evidence that the tool works perfectly on every repository.&lt;/p&gt;




&lt;h1&gt;
  
  
  Repo X-Ray is a Claude Code skill
&lt;/h1&gt;

&lt;p&gt;The project is packaged as a &lt;strong&gt;Claude Code plugin/skill&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The Claude-facing instructions live in:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;skills/xray/SKILL.md
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That skill tells the agent:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;when X-Ray should be used&lt;/li&gt;
&lt;li&gt;how to establish scope&lt;/li&gt;
&lt;li&gt;how to gather evidence&lt;/li&gt;
&lt;li&gt;how to handle uncertainty&lt;/li&gt;
&lt;li&gt;how to interpret Git/GitHub evidence&lt;/li&gt;
&lt;li&gt;how to reason about contradictions&lt;/li&gt;
&lt;li&gt;how to perform requirement mutation&lt;/li&gt;
&lt;li&gt;how to report the evidence ledger&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The deterministic implementation sits alongside it.&lt;/p&gt;

&lt;p&gt;So there are really two layers:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Claude Code
    │
    ▼
Repo X-Ray Skill
    │
    ▼
Deterministic engine
    │
    ├── repository
    ├── Git
    └── GitHub
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The skill is the &lt;strong&gt;agent interface&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The Python engine is the &lt;strong&gt;deterministic execution layer&lt;/strong&gt;.&lt;/p&gt;




&lt;h1&gt;
  
  
  It isn't limited to Claude
&lt;/h1&gt;

&lt;p&gt;Although I built Repo X-Ray first as a &lt;strong&gt;Claude Code skill&lt;/strong&gt;, the underlying idea isn't specific to Claude.&lt;/p&gt;

&lt;p&gt;The important pieces are:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;evidence model
scope model
classification model
provenance
risk model
mutation protocol
JSON schemas
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Those can be consumed by other coding agents and IDE environments.&lt;/p&gt;

&lt;p&gt;That's why the repository also contains an agent-oriented protocol and adapter documentation.&lt;/p&gt;

&lt;p&gt;The goal is not to create another Claude-only prompt.&lt;/p&gt;

&lt;p&gt;The goal is to define a reasonably strict way for coding agents to reason about repository evidence.&lt;/p&gt;




&lt;h1&gt;
  
  
  What Repo X-Ray does not try to do
&lt;/h1&gt;

&lt;p&gt;There are some boundaries I think are important.&lt;/p&gt;

&lt;p&gt;It isn't a compiler.&lt;/p&gt;

&lt;p&gt;It isn't a complete dependency graph.&lt;/p&gt;

&lt;p&gt;It doesn't prove runtime behavior that can't be observed from the available evidence.&lt;/p&gt;

&lt;p&gt;It doesn't magically know which architectural component will be affected by every requirement change.&lt;/p&gt;

&lt;p&gt;And a semantic mutation anchor is not the same thing as a confirmed dependency.&lt;/p&gt;

&lt;p&gt;Those limitations are intentional.&lt;/p&gt;

&lt;p&gt;A tool that says "I don't know" when the evidence isn't sufficient is more useful to me than a tool that produces a confident architecture diagram from weak signals.&lt;/p&gt;




&lt;h1&gt;
  
  
  The bigger idea
&lt;/h1&gt;

&lt;p&gt;I think we're entering an interesting phase of coding agents.&lt;/p&gt;

&lt;p&gt;The first problem was:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Can the agent write code?&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Then:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Can the agent understand a codebase?&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Now there's another question:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Can the agent distinguish what it knows from what it inferred?&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That's the problem Repo X-Ray is trying to address.&lt;/p&gt;

&lt;p&gt;The interesting part isn't the phrase "AI repository analysis."&lt;/p&gt;

&lt;p&gt;It's the evidence boundary.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Repository
    │
    ▼
What can we actually observe?
    │
    ▼
What does that evidence imply?
    │
    ▼
What remains uncertain?
    │
    ▼
What should the agent do next?
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That separation becomes increasingly important as coding agents move from answering questions to making changes.&lt;/p&gt;

&lt;p&gt;If an agent is going to modify a production repository, "I found something that looks relevant" isn't quite enough.&lt;/p&gt;

&lt;p&gt;I'd rather have:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;I found this.
Here's where I found it.
Here's what conflicts with it.
Here's what I couldn't verify.
Here's what Git history says.
Here's what would potentially change.
And here's how confident I am.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's what I wanted Repo X-Ray to provide.&lt;/p&gt;




&lt;h2&gt;
  
  
  Try it
&lt;/h2&gt;

&lt;p&gt;Repo X-Ray is open source and available here:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://github.com/Yudeeswaran/repo-xray" rel="noopener noreferrer"&gt;https://github.com/Yudeeswaran/repo-xray&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The core workflow is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;repo-xray plan &lt;span class="nb"&gt;.&lt;/span&gt;
repo-xray scan &lt;span class="nb"&gt;.&lt;/span&gt;
repo-xray claim &lt;span class="nb"&gt;.&lt;/span&gt;
repo-xray mutate &lt;span class="nb"&gt;.&lt;/span&gt;
repo-xray validate &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you're building Claude Code skills, coding-agent workflows, or repository-analysis systems, I'd be particularly interested in feedback on the evidence model and the boundary between deterministic evidence and agent reasoning.&lt;/p&gt;

</description>
      <category>claude</category>
      <category>ai</category>
      <category>coding</category>
      <category>opensource</category>
    </item>
    <item>
      <title>I Tried to Make Natural Language a SQL Predicate. The Numbers Said No.</title>
      <dc:creator>Yudeeswaran V </dc:creator>
      <pubDate>Wed, 30 Sep 2026 19:56:53 +0000</pubDate>
      <link>https://dev.to/yudeeswaran/i-tried-to-make-natural-language-a-sql-predicate-the-numbers-said-no-16l3</link>
      <guid>https://dev.to/yudeeswaran/i-tried-to-make-natural-language-a-sql-predicate-the-numbers-said-no-16l3</guid>
      <description>&lt;p&gt;A few weeks ago I got stuck on a question: what if part of a &lt;code&gt;WHERE&lt;/code&gt; clause could be written in plain English?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;support_tickets&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;SEM_PREDICT&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;ticket_text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'customer is asking for a refund'&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I didn't want an LLM writing SQL for me, and I didn't want a chatbot. I also didn't want to send every row to an API. I wanted the natural-language condition to behave like any other predicate the engine can evaluate.&lt;/p&gt;

&lt;p&gt;I built a prototype called &lt;strong&gt;SemPred&lt;/strong&gt;, benchmarked it, and stopped when it missed the bar I'd set beforehand. The idea holds up. My first model doesn't. This post covers both.&lt;/p&gt;

&lt;p&gt;Code: &lt;a href="https://github.com/Yudeeswaran/SemPred" rel="noopener noreferrer"&gt;github.com/Yudeeswaran/SemPred&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The problem
&lt;/h2&gt;

&lt;p&gt;Databases are great at structured predicates. &lt;code&gt;amount &amp;gt; 1000 AND currency = 'USD'&lt;/code&gt; is deterministic, cheap, indexable, and easy to reason about.&lt;/p&gt;

&lt;p&gt;Plenty of real data doesn't fit that shape. Take a ticket table:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;id&lt;/th&gt;
&lt;th&gt;ticket_text&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;The ATM kept my card&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;I don't recognize this transaction&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;Can I get my money back for this charge?&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;My card hasn't arrived yet&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;"Find the tickets where the customer wants a refund" has no SQL operator. You can write keyword rules, pre-classify everything, or call an LLM per row. I wanted to see if there was a fourth option, where the semantic condition is just another function in the query.&lt;/p&gt;

&lt;h2&gt;
  
  
  The abstraction
&lt;/h2&gt;

&lt;p&gt;Two functions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;SEM_SCORE(text, predicate)&lt;/code&gt; returns a score.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;SEM_PREDICT(text, predicate)&lt;/code&gt; returns &lt;code&gt;TRUE&lt;/code&gt;, &lt;code&gt;FALSE&lt;/code&gt;, or &lt;code&gt;UNKNOWN&lt;/code&gt;.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt;
    &lt;span class="n"&gt;ticket_text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;SEM_SCORE&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ticket_text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'customer is asking for a refund'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;   &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;score&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;SEM_PREDICT&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ticket_text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'customer is asking for a refund'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;decision&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;tickets&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The third state matters most. In DuckDB, &lt;code&gt;UNKNOWN&lt;/code&gt; maps to SQL &lt;code&gt;NULL&lt;/code&gt;, so the engine can tell "the model thinks this is false" apart from "the model has no idea". I'd rather get a &lt;code&gt;NULL&lt;/code&gt; than a confident wrong answer, and three-valued logic already gives SQL a place to put it.&lt;/p&gt;

&lt;p&gt;The SQL stays SQL. The semantic part is one more predicate next to your joins and filters.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F0p9epcoq93xzmkg5v6h5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F0p9epcoq93xzmkg5v6h5.png" alt=" " width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  How the first version works
&lt;/h2&gt;

&lt;p&gt;The constraints I set up front:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;no external API call per row&lt;/li&gt;
&lt;li&gt;batched inference&lt;/li&gt;
&lt;li&gt;reusable embeddings&lt;/li&gt;
&lt;li&gt;deterministic output&lt;/li&gt;
&lt;li&gt;model serialization&lt;/li&gt;
&lt;li&gt;explicit abstention&lt;/li&gt;
&lt;li&gt;native DuckDB integration&lt;/li&gt;
&lt;li&gt;a reproducible benchmark&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;The model.&lt;/strong&gt; A frozen &lt;code&gt;all-MiniLM-L6-v2&lt;/code&gt; encoder turns text into an embedding. Each predicate gets its own small logistic-regression head on top of it. The encoder isn't trained at all.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Embed once, score many.&lt;/strong&gt; If one ticket needs checking against 50 predicates, the encoder runs once and the 50 heads run on the same vector. Encoding is the expensive part, and the heads are nearly free by comparison.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A bounded embedding cache.&lt;/strong&gt; Repeated text skips the encoder. This is a small detail in a notebook, but over millions of rows it's an architecture decision, and it was the point where the project started to feel like a data-systems problem.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;DuckDB integration.&lt;/strong&gt; Where PyArrow is available, the functions use DuckDB's Arrow UDF path, so values arrive in batches. The naive alternative (DuckDB → Python → model → DuckDB, once per row) looks fine at 100 rows and falls over at scale.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Abstention.&lt;/strong&gt; The decision isn't &lt;code&gt;p &amp;gt;= 0.5&lt;/code&gt;. There's a margin around the threshold. With a threshold of 0.5 and a margin of 0.1:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;score&lt;/th&gt;
&lt;th&gt;result&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&amp;lt; 0.4&lt;/td&gt;
&lt;td&gt;FALSE&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;0.4 to 0.6&lt;/td&gt;
&lt;td&gt;UNKNOWN&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;≥ 0.6&lt;/td&gt;
&lt;td&gt;TRUE&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Both numbers are configurable.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F0k50c7z8l5iw15olvzea.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F0k50c7z8l5iw15olvzea.png" alt=" " width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The benchmark
&lt;/h2&gt;

&lt;p&gt;I used Banking77: 10,003 training examples, 3,080 test examples, 77 intents, official split. I framed it as a few-shot predicate problem. Given a handful of labeled examples per predicate, can the system decide whether unseen text satisfies it?&lt;/p&gt;

&lt;p&gt;Before any neural model, I ran TF-IDF. If a lexical baseline gets most of the way there, embeddings aren't earning their cost.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Examples per predicate&lt;/th&gt;
&lt;th&gt;TF-IDF&lt;/th&gt;
&lt;th&gt;MiniLM&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;8&lt;/td&gt;
&lt;td&gt;47.90%&lt;/td&gt;
&lt;td&gt;62.70%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;16&lt;/td&gt;
&lt;td&gt;60.25%&lt;/td&gt;
&lt;td&gt;72.16%&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;At 16 examples, MiniLM beats the matched TF-IDF setup by about 11.9 points. That looked like a win until I trained TF-IDF on all the training data:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Setup&lt;/th&gt;
&lt;th&gt;Accuracy&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Full-data TF-IDF&lt;/td&gt;
&lt;td&gt;85.45%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;16-shot MiniLM&lt;/td&gt;
&lt;td&gt;72.16%&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;"Improves accuracy by 12 points" was true and also misleading. The useful question is always "compared to what?"&lt;/p&gt;

&lt;h2&gt;
  
  
  A bigger encoder didn't fix it
&lt;/h2&gt;

&lt;p&gt;I swapped in MPNet with the same frozen-encoder setup. Its best 16-shot result was &lt;strong&gt;74.93% ± 1.77 pp&lt;/strong&gt;. That's better than MiniLM, but below the 80% continuation threshold I'd defined before running it.&lt;/p&gt;

&lt;p&gt;It was also much slower. Encoding 13,072 unique texts:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Encoder&lt;/th&gt;
&lt;th&gt;Time&lt;/th&gt;
&lt;th&gt;Throughput&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;MiniLM&lt;/td&gt;
&lt;td&gt;18.24 s&lt;/td&gt;
&lt;td&gt;~717 texts/s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;MPNet&lt;/td&gt;
&lt;td&gt;133.06 s&lt;/td&gt;
&lt;td&gt;~98 texts/s&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;That's about 7× slower. A slower model can be worth it if quality jumps, but here it didn't.&lt;/p&gt;

&lt;h2&gt;
  
  
  Abstention: trading coverage for accuracy
&lt;/h2&gt;

&lt;p&gt;Accuracy alone wasn't the real problem. The real problem was how accuracy trades against coverage.&lt;/p&gt;

&lt;p&gt;At an abstention margin of 0.10, the 16-shot system committed to a decision on about &lt;strong&gt;44.1%&lt;/strong&gt; of ticket-predicate pairs. Of those committed decisions, about &lt;strong&gt;96.3%&lt;/strong&gt; matched the binary labels. That looks strong until you notice it's answering less than half the questions.&lt;/p&gt;

&lt;p&gt;Widening the margin pushes accuracy up and coverage down. At one operating point I got roughly 90% ticket accuracy, but coverage fell to about 21%.&lt;/p&gt;

&lt;p&gt;Before running anything, I'd written a gate:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;≥ 90% committed-ticket accuracy AND ≥ 50% ticket coverage&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;No margin I evaluated met both. You can make almost any model look accurate by letting it answer fewer questions, so every abstention number needs its coverage figure next to it.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fp7iropfar89hpcrcmrxp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fp7iropfar89hpcrcmrxp.png" alt=" " width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Why I stopped
&lt;/h2&gt;

&lt;p&gt;This is where I could have kept trying encoders and margins until something looked good. I didn't, because the gate existed to prevent exactly that. The gate wasn't met, so the conclusion is that this architecture isn't production-ready.&lt;/p&gt;

&lt;p&gt;Having a written stopping condition changed how I ran everything. "Is this model better?" became "Did it meet the requirement?", and the second question is much harder to fudge.&lt;/p&gt;

&lt;h2&gt;
  
  
  Some predicates are easier than others
&lt;/h2&gt;

&lt;p&gt;Averages hide a lot. The &lt;em&gt;card swallowed&lt;/em&gt; predicate did far better than the overall numbers, with the 16-shot model at the selected abstention setting:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;precision 59.5%&lt;/li&gt;
&lt;li&gt;recall 81.0%&lt;/li&gt;
&lt;li&gt;F1 67.1%&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Matched low-shot TF-IDF scored around 35.7% F1 on the same predicate. Some predicates have distinctive language, while others overlap heavily with neighboring intents. A real system probably shouldn't treat every predicate as equally hard.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I think the architecture got wrong
&lt;/h2&gt;

&lt;p&gt;In the current design, the predicate is the classifier head. The text goes through the encoder, and a per-predicate model decides. But the task is really pairwise: &lt;em&gt;does this text satisfy this predicate?&lt;/em&gt; The predicate's wording carries information the head never sees.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Text:      "My account was charged twice."
Predicate: "customer is reporting a duplicate charge"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The relationship between those two strings is the signal. So the next hypothesis is to make the predicate an input to the model, not the name of a head.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F2zo07gq6aluiwo8y3nhd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F2zo07gq6aluiwo8y3nhd.png" alt=" " width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is a hypothesis to test, not a result. I haven't claimed it works.&lt;/p&gt;

&lt;h2&gt;
  
  
  What about just using an LLM?
&lt;/h2&gt;

&lt;p&gt;An LLM given &lt;code&gt;(ticket, predicate)&lt;/code&gt; would likely reason better. It also brings cost, latency, throughput, hosting, batching, privacy, and determinism concerns. At a million rows, a million LLM calls is hard to justify.&lt;/p&gt;

&lt;p&gt;I see it more as a teacher or an escalation path. A compact model handles the bulk, and only the &lt;code&gt;UNKNOWN&lt;/code&gt; cases go to a bigger model.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F0oqearuo38iglx5a9xkr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F0oqearuo38iglx5a9xkr.png" alt=" " width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;That's future work as well.&lt;/p&gt;

&lt;h2&gt;
  
  
  Throughput inside DuckDB
&lt;/h2&gt;

&lt;p&gt;The systems side held up better than the quality side. In the benchmark environment, the DuckDB path scored &lt;strong&gt;237,160 predicate pairs in 5.57 seconds&lt;/strong&gt;, about &lt;strong&gt;42,582 pairs/sec&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;That's possible because unique texts are embedded once and the embeddings are reused across predicates. With &lt;code&gt;N&lt;/code&gt; unique texts and &lt;code&gt;P&lt;/code&gt; predicates, the cost is one encoder pass over &lt;code&gt;N&lt;/code&gt; texts plus &lt;code&gt;N × P&lt;/code&gt; cheap head evaluations, instead of &lt;code&gt;N × P&lt;/code&gt; encoder runs.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why this feels more like a data-systems problem
&lt;/h2&gt;

&lt;p&gt;I started out thinking the hard part was classifying text. After the prototype I'm less sure. The harder part may be running uncertain predicates safely over large tables:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Semantics:&lt;/strong&gt; what does &lt;code&gt;UNKNOWN&lt;/code&gt; mean for joins, aggregates, and &lt;code&gt;NOT&lt;/code&gt;?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cost:&lt;/strong&gt; what's the budget per million rows?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Caching:&lt;/strong&gt; which embeddings or decisions can be reused?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pruning:&lt;/strong&gt; can lexical or metadata filters cut down the semantic evaluations?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Confidence:&lt;/strong&gt; how do you decide when to abstain?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Escalation:&lt;/strong&gt; when is an expensive model worth calling?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reproducibility:&lt;/strong&gt; can you recreate the exact model and config later?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Integration:&lt;/strong&gt; can this work in DuckDB, Polars, Spark, and warehouses?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;None of those are NLP questions.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I'd do differently
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Write the gate before touching a model.&lt;/strong&gt; It turns evaluation into a pass/fail check.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Report accuracy, coverage, cost, and latency together.&lt;/strong&gt; 98% accuracy on 2% of rows isn't useful, and neither is 80% accuracy at a cost nobody can pay.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Treat abstention as a dial.&lt;/strong&gt; It can always inflate accuracy, so never quote one number alone.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Don't buy quality with model size by default.&lt;/strong&gt; MPNet improved the score and missed the gate anyway.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Keep the negative result.&lt;/strong&gt; "This architecture doesn't meet the requirement" is a useful finding, because now I know what not to build again.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Where it goes next
&lt;/h2&gt;

&lt;p&gt;I'm not going to try five more embedding models, because that's benchmark roulette. The next experiment changes the architecture (pairwise scoring with the predicate as input), judged against the same gate and the same evaluation setup. After that, in rough order:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;calibration and better abstention strategies&lt;/li&gt;
&lt;li&gt;candidate pruning and lexical + semantic cascades&lt;/li&gt;
&lt;li&gt;teacher escalation&lt;/li&gt;
&lt;li&gt;ONNX inference&lt;/li&gt;
&lt;li&gt;larger datasets and real workloads&lt;/li&gt;
&lt;li&gt;cost per million rows&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Nothing on that list counts as progress until it beats a benchmark defined in advance.&lt;/p&gt;

&lt;h2&gt;
  
  
  So, is it useful?
&lt;/h2&gt;

&lt;p&gt;As a product, no. The model I evaluated misses its own quality and coverage bar.&lt;/p&gt;

&lt;p&gt;As an experiment, yes. It turned "can SQL take a natural-language condition?" into a more specific question: what would it take for semantic reasoning to be a measurable, cacheable, safely executable primitive inside a data system? I don't have the full answer, but I now have a prototype, a benchmark, performance numbers, and a clear picture of where the design breaks.&lt;/p&gt;

&lt;p&gt;The repo is open if you want to reproduce the benchmark or tell me where the architecture is wrong: &lt;strong&gt;&lt;a href="https://github.com/Yudeeswaran/SemPred" rel="noopener noreferrer"&gt;github.com/Yudeeswaran/SemPred&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If you work on semantic query execution, database-native ML, DuckDB extensions, or cheap inference at scale, I'd like to hear from you.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;SemPred is a research prototype. It hasn't been validated as a general-purpose classifier or a production decision system, and the current benchmark results don't support using it for high-impact decisions.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>machinelearning</category>
      <category>ai</category>
      <category>database</category>
      <category>python</category>
    </item>
  </channel>
</rss>
