<?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: Daniel Paiva</title>
    <description>The latest articles on DEV Community by Daniel Paiva (@danhpaiva).</description>
    <link>https://dev.to/danhpaiva</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%2F536929%2Fb4249d89-56f7-451e-8d80-80ff03c350ec.jpeg</url>
      <title>DEV Community: Daniel Paiva</title>
      <link>https://dev.to/danhpaiva</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/danhpaiva"/>
    <language>en</language>
    <item>
      <title>I built a PR reviewer that survived its own model being deprecated</title>
      <dc:creator>Daniel Paiva</dc:creator>
      <pubDate>Fri, 02 Oct 2026 16:59:59 +0000</pubDate>
      <link>https://dev.to/danhpaiva/i-built-a-pr-reviewer-that-survived-its-own-model-being-deprecated-2p1k</link>
      <guid>https://dev.to/danhpaiva/i-built-a-pr-reviewer-that-survived-its-own-model-being-deprecated-2p1k</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for the &lt;a href="https://dev.to/challenges/hacktoberfest-weekend-2026-10-01"&gt;Hacktoberfest Weekend Challenge: Build for a Friend&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What I Built
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;groq-pr-reviewer-net&lt;/strong&gt; — a .NET 10 CLI that reads your &lt;code&gt;git diff&lt;/code&gt; and returns a structured code review in your terminal, before you ever open the pull request.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dotnet run &lt;span class="nt"&gt;--&lt;/span&gt; &lt;span class="nt"&gt;--staged&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;That's it. It reviews bugs and correctness, security, performance, and readability.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The friend I built it for is the developer who codes alone.&lt;/strong&gt; No teammate to tag for a second opinion, no budget for a paid review bot — the solo maintainer, the student, the person shipping a side project at 1am. I've been that person on every side project I've ever started.&lt;/p&gt;

&lt;p&gt;The problem it solves is specific: the moment right before you commit, when you know you should have someone look at this, and there is nobody to ask. Paid AI review tools answer that with a seat license and a company card. This answers it with a terminal and a free API key.&lt;/p&gt;
&lt;h2&gt;
  
  
  Demo
&lt;/h2&gt;

&lt;p&gt;A CLI has no deployed link, so here is the whole thing end to end.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Setup you can verify without leaking anything&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fww13qyry5pdlh1pitemn.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%2Fww13qyry5pdlh1pitemn.png" alt="Terminal running dotnet run --check, showing key source, length, gsk_ prefix ok, and the model name" width="800" height="308"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;--check&lt;/code&gt; reports where the key was loaded from, how long it is, and whether it has the right shape — &lt;strong&gt;without ever printing the key&lt;/strong&gt;. One command tells you if you are ready, before you spend a single request.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. The open-weight catalogue&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fkty0z1wzx3tjhzbbkgtx.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%2Fkty0z1wzx3tjhzbbkgtx.png" alt="Terminal listing the models available on Groq, with no Llama chat model present" width="800" height="347"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Every model my key can actually reach. Pay attention to what is &lt;em&gt;missing&lt;/em&gt; from that list — it matters in a moment.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. The model I built the whole thing on, dead&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fd6mezjtr3k86tb53c90w.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%2Fd6mezjtr3k86tb53c90w.png" alt="Terminal showing a 404 error: the model llama-3.3-70b-versatile does not exist, with a hint to use --model" width="798" height="197"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;No Llama chat model is left in the catalogue. Notice that the failure &lt;strong&gt;explains itself&lt;/strong&gt;: it names the likely cause, links the live model list, and tells you which flag fixes it. That was the one thing I most wanted to get right, because I'd just lost an hour to an error message that told me nothing.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Same diff, one flag different, working review&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fkcs7pz0my9hkf880wgc1.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%2Fkcs7pz0my9hkf880wgc1.png" alt="Terminal showing a full code review with findings grouped into bugs, security, performance and best practices" width="800" height="366"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Identical command minus one argument. That gap between screenshot 3 and screenshot 4 is the entire argument of this post, and it cost me about forty seconds.&lt;/p&gt;
&lt;h3&gt;
  
  
  Dogfooding: the tool reviewing its own source
&lt;/h3&gt;

&lt;p&gt;Screenshot 4 is the tool reviewing the commit that implements it. Earlier passes, against earlier versions of this code, caught three things worth showing — all since fixed, which is why you won't find them in the screenshot above.&lt;/p&gt;

&lt;p&gt;It found a real bug I had introduced minutes earlier — I'd changed the default model constant and the README, but forgot the &lt;code&gt;--help&lt;/code&gt; text:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Inconsistent default model description&lt;/strong&gt; – &lt;code&gt;PrintUsage()&lt;/code&gt; documents the default model as &lt;code&gt;llama-3.3-70b-versatile&lt;/code&gt;, while the code constant &lt;code&gt;DefaultModel&lt;/code&gt; is &lt;code&gt;openai/gpt-oss-120b&lt;/code&gt;. Users may be confused about which model is actually used.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It caught an unguarded JSON access that would throw if the API schema ever shifted:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Potential JSON-parsing failure in &lt;code&gt;FetchModelIds&lt;/code&gt;&lt;/strong&gt; – the method assumes the response contains a top-level &lt;code&gt;"data"&lt;/code&gt; array. If Groq changes the schema or returns an error payload without that property, &lt;code&gt;GetProperty("data")&lt;/code&gt; will throw an unhandled &lt;code&gt;KeyNotFoundException&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;And it flagged something missing from my own threat model entirely:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Potential secret leakage&lt;/strong&gt; – the diff itself is sent to Groq unchanged; if the diff contains passwords, tokens, or other secrets they will be transmitted to an external service.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That last one became a warning section in the README. &lt;strong&gt;The tool wrote its own security disclaimer.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;And it keeps earning its keep: the run in screenshot 4 flagged that I never dispose the &lt;code&gt;HttpResponseMessage&lt;/code&gt; in either API call — a socket leak I had walked straight past.&lt;/p&gt;
&lt;h3&gt;
  
  
  Being honest about the failure modes
&lt;/h3&gt;

&lt;p&gt;It is not magic. Across two earlier review passes it insisted, both times, that &lt;code&gt;net10.0&lt;/code&gt; was not a valid target framework and that I was missing a &lt;code&gt;using System.Linq;&lt;/code&gt;. Both wrong — the project builds clean with zero warnings. The model's knowledge cutoff predates .NET 10, and &lt;code&gt;ImplicitUsings&lt;/code&gt; already covers LINQ.&lt;/p&gt;

&lt;p&gt;Screenshot 4 has its own share: it claims the diff is "printed to stdout before being sent," which simply never happens. Roughly one in four findings is noise.&lt;/p&gt;

&lt;p&gt;I put that in the README verbatim, because a tool that oversells itself is worse than no tool:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Treat the output as a fast second opinion, not as truth. Read it the way you would read a well-meaning junior reviewer.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;A junior reviewer who is occasionally confidently wrong but catches things you missed is still worth having. Pretending otherwise would be the actual failure.&lt;/p&gt;
&lt;h2&gt;
  
  
  Code
&lt;/h2&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://assets.dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/danhpaiva" rel="noopener noreferrer"&gt;
        danhpaiva
      &lt;/a&gt; / &lt;a href="https://github.com/danhpaiva/groq-pr-reviewer-net" rel="noopener noreferrer"&gt;
        groq-pr-reviewer-net
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      Terminal code review for your git diff, powered by an open-weight model on Groq. One C# file, no SDK, no cost.
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;groq-pr-reviewer-net&lt;/h1&gt;
&lt;/div&gt;

&lt;p&gt;&lt;a href="https://dotnet.microsoft.com" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/7df3fe0cdbc824173ecb870f9de6f7e14bb18fa64d8a2c2969c22884df3159f4/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f2e4e45542d31302d3531324244343f6c6f676f3d646f746e6574266c6f676f436f6c6f723d7768697465" alt=".NET"&gt;&lt;/a&gt;
&lt;a href="https://huggingface.co/openai/gpt-oss-120b" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/fc57b31828939d68e0a204b9e22f89ef59c28028ae7c78224d11871f233d386f/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f6d6f64656c2d6770742d2d6f73732d2d313230622d343132393931" alt="Model"&gt;&lt;/a&gt;
&lt;a href="https://huggingface.co/openai/gpt-oss-120b" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/f42c23860dce262abb821b2889d767eb7ad6ad6a89b1bf451de7a4eeb25fbab4/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f776569676874732d4170616368652d2d322e302d73756363657373" alt="Weights"&gt;&lt;/a&gt;
&lt;a href="https://groq.com" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/d5e9d112dea78e32fa842bb32b3ff97b597a6ee797cc441acd87cbe25c0460ef/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f696e666572656e63652d47726f712d463535303336" alt="Inference"&gt;&lt;/a&gt;
&lt;a href="https://github.com/danhpaiva/groq-pr-reviewer-net/LICENSE" rel="noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/b8cadaa967891081f8f165695470689986c028821dd8a040132f6e661795dc0d/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f6c6963656e73652d4d49542d626c7565" alt="License"&gt;&lt;/a&gt;
&lt;a href="https://hacktoberfest.com" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/a2b318140d58f56862ecd5581d006b961f44164e891dbd5fd6834b93f824756f/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f4861636b746f626572666573742d323032362d626c756576696f6c6574" alt="Hacktoberfest"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;A C# (.NET 10) CLI that reviews your &lt;code&gt;git diff&lt;/code&gt; using an open-weight model
(&lt;code&gt;openai/gpt-oss-120b&lt;/code&gt;, Apache 2.0) running on Groq's ultra-fast inference.&lt;/p&gt;
&lt;p&gt;Point it at any git repository and it prints a structured code review in your
terminal — before you open the pull request.&lt;/p&gt;
&lt;div class="highlight highlight-source-shell notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;dotnet run -- --staged&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;&lt;a rel="noopener noreferrer" href="https://github.com/danhpaiva/groq-pr-reviewer-net/img/04.png"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fdanhpaiva%2Fgroq-pr-reviewer-net%2FHEAD%2Fimg%2F04.png" alt="A terminal showing a full code review with findings grouped into bugs and correctness, security, performance, and best practices"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Above: the tool reviewing its own source code.&lt;/em&gt;&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Why open-source AI here?&lt;/h2&gt;
&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Open weights.&lt;/strong&gt; The default model ships under Apache 2.0: you can swap it for
another one at any time, or run it elsewhere. No vendor lock-in — and when a
model is retired, &lt;code&gt;--list-models&lt;/code&gt; plus &lt;code&gt;--model&lt;/code&gt; gets you moving again without
touching the code.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Zero cost.&lt;/strong&gt; Groq's free tier comfortably covers an individual developer's usage.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No dependency on paid review tooling.&lt;/strong&gt; Anyone in the community can clone
this and use it today.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Your diff stays in your control.&lt;/strong&gt; You choose the provider and the model, instead…&lt;/li&gt;
&lt;/ul&gt;&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/danhpaiva/groq-pr-reviewer-net" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;The whole thing is under 400 lines across five small files — deliberately small enough to read in one sitting and fork.&lt;/p&gt;

&lt;p&gt;The core is unremarkable on purpose, which is the point: an open-weight model behind an OpenAI-compatible endpoint means no SDK, no framework, no abstraction layer to learn.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="c1"&gt;// GroqClient.cs&lt;/span&gt;
&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;ChatCompletionsUrl&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"https://api.groq.com/openai/v1/chat/completions"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;ModelsUrl&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"https://api.groq.com/openai/v1/models"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// CliOptions.cs&lt;/span&gt;
&lt;span class="c1"&gt;// Open-weight (Apache 2.0) and currently the strongest chat model on Groq.&lt;/span&gt;
&lt;span class="c1"&gt;// Run --list-models if this one is ever retired, then pass --model.&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;DefaultModel&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"openai/gpt-oss-120b"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The system prompt is plain text, not a framework construct — which is exactly why swapping models costs nothing:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;SystemPrompt&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"""
&lt;/span&gt;    &lt;span class="n"&gt;You&lt;/span&gt; &lt;span class="n"&gt;are&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="n"&gt;senior&lt;/span&gt; &lt;span class="n"&gt;code&lt;/span&gt; &lt;span class="n"&gt;reviewer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt; &lt;span class="n"&gt;Analyse&lt;/span&gt; &lt;span class="n"&gt;the&lt;/span&gt; &lt;span class="n"&gt;pull&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt; &lt;span class="n"&gt;diff&lt;/span&gt; &lt;span class="n"&gt;below&lt;/span&gt; &lt;span class="k"&gt;and&lt;/span&gt; &lt;span class="n"&gt;reply&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;English&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;bullet&lt;/span&gt; &lt;span class="n"&gt;points&lt;/span&gt; &lt;span class="n"&gt;organised&lt;/span&gt; &lt;span class="k"&gt;into&lt;/span&gt; &lt;span class="n"&gt;these&lt;/span&gt; &lt;span class="n"&gt;sections&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="n"&gt;Bugs&lt;/span&gt; &lt;span class="k"&gt;and&lt;/span&gt; &lt;span class="n"&gt;correctness&lt;/span&gt;
    &lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="n"&gt;Security&lt;/span&gt;
    &lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="n"&gt;Performance&lt;/span&gt;
    &lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="n"&gt;Best&lt;/span&gt; &lt;span class="n"&gt;practices&lt;/span&gt; &lt;span class="p"&gt;/&lt;/span&gt; &lt;span class="n"&gt;readability&lt;/span&gt;

    &lt;span class="n"&gt;Be&lt;/span&gt; &lt;span class="n"&gt;concise&lt;/span&gt; &lt;span class="k"&gt;and&lt;/span&gt; &lt;span class="n"&gt;specific&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt; &lt;span class="n"&gt;If&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="n"&gt;section&lt;/span&gt; &lt;span class="n"&gt;has&lt;/span&gt; &lt;span class="n"&gt;nothing&lt;/span&gt; &lt;span class="n"&gt;worth&lt;/span&gt; &lt;span class="n"&gt;raising&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;write&lt;/span&gt; &lt;span class="s"&gt;"Nothing to flag"&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
    &lt;span class="n"&gt;Ignore&lt;/span&gt; &lt;span class="n"&gt;trivial&lt;/span&gt; &lt;span class="n"&gt;formatting&lt;/span&gt; &lt;span class="n"&gt;changes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
    &lt;span class="s"&gt;""";
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;MIT licensed.&lt;/p&gt;

&lt;h2&gt;
  
  
  How I Built It
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;The open-source AI:&lt;/strong&gt; &lt;a href="https://huggingface.co/openai/gpt-oss-120b" rel="noopener noreferrer"&gt;&lt;code&gt;openai/gpt-oss-120b&lt;/code&gt;&lt;/a&gt;, an &lt;strong&gt;open-weight model released under Apache 2.0&lt;/strong&gt;, served on &lt;a href="https://groq.com" rel="noopener noreferrer"&gt;Groq&lt;/a&gt; for inference. No proprietary model touches this project.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The stack:&lt;/strong&gt; .NET 10, five small files, and &lt;code&gt;HttpClient&lt;/code&gt;. No agent framework, no orchestration library, no vector database. The entire dependency list is the .NET base class library.&lt;/p&gt;

&lt;p&gt;That minimalism is a design choice tied directly to the open-weight decision. Because open models are served behind the same OpenAI-compatible &lt;code&gt;/chat/completions&lt;/code&gt; contract, I didn't need an abstraction layer to stay portable — &lt;strong&gt;the HTTP contract &lt;em&gt;is&lt;/em&gt; the abstraction layer.&lt;/strong&gt; A 30-line &lt;code&gt;HttpClient&lt;/code&gt; call is already provider-agnostic. Anything heavier would have added lock-in rather than removing it, and would have been one more thing for a forker to learn.&lt;/p&gt;

&lt;p&gt;The flow is four steps: shell out to &lt;code&gt;git diff&lt;/code&gt;, truncate at 60k characters to respect the context window, POST it with the system prompt, print the response.&lt;/p&gt;

&lt;p&gt;Two things took longer than the happy path:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Reading streams without deadlocking.&lt;/strong&gt; Draining &lt;code&gt;git&lt;/code&gt;'s stdout and stderr sequentially will hang the process if stderr fills its pipe buffer while you're still reading stdout. Both reads have to be in flight before you wait:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;outputTask&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;StandardOutput&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ReadToEndAsync&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;errorTask&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;StandardError&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ReadToEndAsync&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WhenAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;outputTask&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;errorTask&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WaitForExitAsync&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Making the setup failure legible.&lt;/strong&gt; Which brings me to the hour I lost.&lt;/p&gt;

&lt;h3&gt;
  
  
  Groq is not Grok
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://groq.com" rel="noopener noreferrer"&gt;Groq&lt;/a&gt; is an inference provider for open models; keys start with &lt;code&gt;gsk_&lt;/code&gt;. &lt;a href="https://x.ai" rel="noopener noreferrer"&gt;xAI's Grok&lt;/a&gt; is an unrelated company with proprietary models; keys start with &lt;code&gt;xai-&lt;/code&gt;. The names differ by one letter.&lt;/p&gt;

&lt;p&gt;I generated a key on the wrong console and spent an hour staring at &lt;code&gt;401 Invalid API Key&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;So I built the diagnostic I wished I'd had:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ dotnet run -- --check
Key source  : /path/to/groq-pr-reviewer-net/.env
Length      : 56 characters
gsk_ prefix : ok
Model       : openai/gpt-oss-120b
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It reports where the key was loaded from, its length, and whether it has the right shape — &lt;strong&gt;without ever printing the key itself.&lt;/strong&gt; The 401 message now names the &lt;code&gt;gsk_&lt;/code&gt; vs &lt;code&gt;xai-&lt;/code&gt; mix-up outright, and the README carries the warning up front.&lt;/p&gt;

&lt;p&gt;Small thing. But "build for a friend" means thinking about the person hitting your error message at midnight, and that person has no idea the two companies exist.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Does Open Innovation Matter?
&lt;/h2&gt;

&lt;p&gt;I was going to write the usual paragraph about open weights and avoiding vendor lock-in. Then the argument proved itself while I was still building.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Halfway through, my model was deprecated.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I'd built the whole thing on Llama 3.3 70B. When I ran the first real end-to-end test against the API:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Groq API error (404 NotFound): The model `llama-3.3-70b-versatile`
does not exist or you do not have access to it.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Not a single Llama chat model was left in the catalogue.&lt;/p&gt;

&lt;p&gt;Here's what a closed API would have cost me: a new SDK, a new request shape, a new auth flow, a rewritten prompt, and a wait on somebody's migration guide. Possibly a dead weekend project.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What it actually cost me was a flag.&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dotnet run &lt;span class="nt"&gt;--&lt;/span&gt; &lt;span class="nt"&gt;--list-models&lt;/span&gt;      &lt;span class="c"&gt;# what can I actually reach right now?&lt;/span&gt;
dotnet run &lt;span class="nt"&gt;--&lt;/span&gt; &lt;span class="nt"&gt;--model&lt;/span&gt; qwen/qwen3.8-27b
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I moved to &lt;code&gt;openai/gpt-oss-120b&lt;/code&gt;, re-ran the review, and shipped. Same endpoint, same request body, same prompt. I added &lt;code&gt;--list-models&lt;/code&gt; during the recovery so the next person hitting a dead model can see their options in one command instead of digging through changelogs.&lt;/p&gt;

&lt;p&gt;That is what open innovation made possible here, concretely: &lt;strong&gt;the difference between a flag change and a rewrite.&lt;/strong&gt; Interchangeable models behind a common contract mean no single vendor's roadmap can end your project.&lt;/p&gt;

&lt;p&gt;The rest of the case holds too, and matters specifically for the friend I built this for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Zero cost.&lt;/strong&gt; Groq's free tier covers an individual developer comfortably. No card, no trial clock — the barrier for a student or a solo maintainer is actually zero, not "zero for 14 days."&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A real escape hatch.&lt;/strong&gt; Apache 2.0 weights can be run elsewhere, including on your own hardware. If every hosted option vanished tomorrow, the model would still exist. You cannot say that about a closed endpoint.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;You choose where your code goes.&lt;/strong&gt; Point it at a different provider, or at a local inference server, and your diff never leaves your machine. With a closed tool you get whatever data policy the vendor ships that quarter.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For someone with no team and no budget, "I can swap the engine myself" isn't an abstract principle. It's whether the tool still works next year.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Built in a weekend with .NET 10 and an open-weight model that costs nothing to run.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>devchallenge</category>
      <category>weekendchallenge</category>
      <category>hf26challenge</category>
      <category>opensource</category>
    </item>
  </channel>
</rss>
